Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interaction with Casper smart-contract source code verification service #144

Open
wants to merge 33 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2b9d3e7
Added verify-contract command
kchudy Dec 22, 2023
b9149a9
Added verify contract to lib
kchudy Dec 22, 2023
f879378
Added function for creating archive
kchudy Jan 2, 2024
d0c6241
Added arg parsing
kchudy Jan 2, 2024
bf44ebb
Added verificator api client
kchudy Jan 2, 2024
a6e6360
Added waiting for verification to finish
kchudy Jan 3, 2024
ff261d5
Switched to deploy hash
kchudy Jan 3, 2024
e4c24de
Updated comments
kchudy Jan 3, 2024
ffb53e3
Added getting verification details
kchudy Jan 5, 2024
4d756f6
Imploved error handling
kchudy Jan 8, 2024
104d4cb
Improved logging
kchudy Jan 8, 2024
f07b7ec
Refactored verification function
kchudy Jan 8, 2024
d72f852
Initialized API client
kchudy Jan 8, 2024
4266492
Adopt new datatypes; use reqwest
moubctez Jan 22, 2024
979ed97
Cleanup
moubctez Jan 22, 2024
2392104
Fix result type
moubctez Jan 22, 2024
4f321b5
Fixes and cleanups
moubctez Jan 22, 2024
21012c4
Sync structs with verificator
moubctez Jan 23, 2024
5e43e05
More retries
moubctez Jan 24, 2024
5a3af7c
Increase number of retries
moubctez Jan 25, 2024
af1c39b
Shorten message
moubctez Jan 25, 2024
d9caa43
Resolve comments
moubctez Apr 10, 2024
3f944b1
Update src/verify_contract.rs
moubctez Apr 11, 2024
ad4bf50
cargo fmt
moubctez Apr 11, 2024
e395eae
Update vergen to v8. This resolves audit error.
moubctez Apr 19, 2024
b59f117
Merge branch 'dev' of github.com:casper-ecosystem/casper-client-rs in…
moubctez Jun 5, 2024
537347d
Resolve pull request comments
moubctez Jun 20, 2024
96c3013
Restore vergen
moubctez Jun 24, 2024
44fef2d
Use correct build.rs
moubctez Jun 24, 2024
258db32
Fix build without default features. Now tokio is required with "time"…
moubctez Jun 26, 2024
c047567
Fix wasm build
moubctez Jun 27, 2024
deaa529
Fix CI-CD tokio not optional
gRoussac Jun 27, 2024
f53e85d
Merge pull request #2 from gRoussac/contract-verification-command
moubctez Jun 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
[package]
name = "casper-client"
version = "2.0.0" # when updating, also update 'html_root_url' in lib.rs
authors = ["Marc Brinkmann <[email protected]>", "Fraser Hutchison <[email protected]>", "Zachary Showalter <[email protected]>"]
authors = [
"Marc Brinkmann <[email protected]>",
"Fraser Hutchison <[email protected]>",
"Zachary Showalter <[email protected]>",
]
edition = "2021"
description = "A client library and binary for interacting with the Casper network"
documentation = "https://docs.rs/casper-client"
Expand All @@ -19,32 +23,36 @@ path = "lib/lib.rs"
name = "casper-client"
path = "src/main.rs"
doc = false
required-features = ["async-trait", "clap", "clap_complete", "tokio", "std-fs-io"]
required-features = ["async-trait", "clap", "clap_complete", "std-fs-io"]

[features]
default = ["async-trait", "clap", "clap_complete", "tokio", "std-fs-io"]
default = ["async-trait", "clap", "clap_complete", "std-fs-io"]
std-fs-io = ["casper-types/std-fs-io"]

[dependencies]
async-trait = { version = "0.1.74", optional = true }
base16 = "0.2.1"
base64 = { version = "0.22.1", default-features = false }
bytes = { version = "1.6.0", default-features = false }
casper-hashing = "3.0.0"
casper-types = { version = "4.0.1", features = ["std"] }
clap = { version = "~4.4", optional = true, features = ["cargo", "deprecated", "wrap_help"] }
clap = { version = "~4.4", optional = true, features = ["cargo", "deprecated"] }
clap_complete = { version = "<4.5.0", optional = true }
flate2 = "1.0.30"
hex-buffer-serde = "0.4.0"
humantime = "2.1.0"
itertools = "0.12.0"
jsonrpc-lite = "0.6.0"
num-traits = "0.2.15"
once_cell = "1.18.0"
rand = "0.8.5"
reqwest = { version = "0.12.3", features = ["json"] }
reqwest = { version = "0.12.4", features = ["json"] }
schemars = "=0.8.5"
serde = { version = "1.0.193", default-features = false, features = ["derive"] }
serde_json = { version = "1.0.108", features = ["preserve_order"] }
thiserror = "1.0.50"
tokio = { version = "1.34.0", optional = true, features = ["macros", "rt", "sync", "time"] }
tar = { version = "0.4.41", default-features = false }
thiserror = "1"
tokio = { version = "1.38.0", features = ["macros", "rt", "sync", "time"] }
uint = "0.9.5"

[dev-dependencies]
Expand Down
20 changes: 20 additions & 0 deletions lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use crate::{
DictionaryItemIdentifier,
},
types::Deploy,
verification_types::VerificationDetails,
SuccessResponse,
};
#[cfg(doc)]
Expand Down Expand Up @@ -708,3 +709,22 @@ pub async fn get_era_info(
.await
.map_err(CliError::from)
}

/// Verifies the smart contract code againt the one deployed at address.
pub async fn verify_contract(
hash_str: &str,
verification_url_base_path: &str,
verification_project_path: Option<&str>,
verbosity_level: u64,
) -> Result<VerificationDetails, CliError> {
let key = parse::key_for_query(hash_str)?;
let verbosity = parse::verbosity(verbosity_level);
crate::verify_contract(
key,
verification_url_base_path,
verification_project_path,
verbosity,
)
.await
.map_err(CliError::from)
}
44 changes: 22 additions & 22 deletions lib/cli/json_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,59 +59,59 @@ fn write_json_to_bytesrepr(
.as_i64()
.and_then(|value| i32::try_from(value).ok())
.ok_or(ErrorDetails::CannotParseToI32)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::I64, Value::Number(number)) => {
let value = number.as_i64().ok_or(ErrorDetails::CannotParseToI64)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::U8, Value::Number(number)) => {
let value = number
.as_u64()
.and_then(|value| u8::try_from(value).ok())
.ok_or(ErrorDetails::CannotParseToU8)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::U32, Value::Number(number)) => {
let value = number
.as_u64()
.and_then(|value| u32::try_from(value).ok())
.ok_or(ErrorDetails::CannotParseToU32)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::U64, Value::Number(number)) => {
let value = number.as_u64().ok_or(ErrorDetails::CannotParseToU64)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::U128, Value::String(string)) => {
let value = U128::from_dec_str(string)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::U128, Value::Number(number)) => {
let value = number.as_u64().ok_or(ErrorDetails::CannotParseToU64)?;
U128::from(value).write_bytes(output)?
U128::from(value).write_bytes(output)?;
}
(&CLType::U256, Value::String(string)) => {
let value = U256::from_dec_str(string)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::U256, Value::Number(number)) => {
let value = number.as_u64().ok_or(ErrorDetails::CannotParseToU64)?;
U256::from(value).write_bytes(output)?
U256::from(value).write_bytes(output)?;
}
(&CLType::U512, Value::String(string)) => {
let value = U512::from_dec_str(string)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::U512, Value::Number(number)) => {
let value = number.as_u64().ok_or(ErrorDetails::CannotParseToU64)?;
U512::from(value).write_bytes(output)?
U512::from(value).write_bytes(output)?;
}
(&CLType::Unit, Value::Null) => (),
(&CLType::String, Value::String(string)) => string.write_bytes(output)?,
(&CLType::Key, Value::String(string)) => {
let value = Key::from_formatted_str(string)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::Key, Value::Object(map)) => {
// This is an alternative JSON representation of a `Key`, e.g. if calling
Expand Down Expand Up @@ -141,22 +141,22 @@ fn write_json_to_bytesrepr(
Key::ChainspecRegistry if mapped_variant == "ChainspecRegistry" => {}
_ => return Err(ErrorDetails::KeyObjectHasInvalidVariant),
}
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::URef, Value::String(string)) => {
let value = URef::from_formatted_str(string)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(&CLType::PublicKey, Value::String(string)) => {
let value = PublicKey::from_hex(string)?;
value.write_bytes(output)?
value.write_bytes(output)?;
}
(CLType::Option(ref _inner_cl_type), Value::Null) => {
output.push(OPTION_NONE_TAG);
}
(CLType::Option(ref inner_cl_type), _) => {
output.push(OPTION_SOME_TAG);
write_json_to_bytesrepr(inner_cl_type, json_value, output)?
write_json_to_bytesrepr(inner_cl_type, json_value, output)?;
}
(CLType::List(ref inner_cl_type), Value::Array(vec)) => {
(vec.len() as u32).write_bytes(output)?;
Expand Down Expand Up @@ -209,11 +209,11 @@ fn write_json_to_bytesrepr(
match map.iter().next() {
Some((key, value)) if key.to_ascii_lowercase() == "ok" => {
output.push(RESULT_OK_TAG);
write_json_to_bytesrepr(ok, value, output)?
write_json_to_bytesrepr(ok, value, output)?;
}
Some((key, value)) if key.to_ascii_lowercase() == "err" => {
output.push(RESULT_ERR_TAG);
write_json_to_bytesrepr(err, value, output)?
write_json_to_bytesrepr(err, value, output)?;
}
_ => return Err(ErrorDetails::ResultObjectHasInvalidVariant),
}
Expand Down Expand Up @@ -243,7 +243,7 @@ fn write_json_to_bytesrepr(
_ => return Err(ErrorDetails::MapTypeNotValidAsObject(*key_type.clone())),
};
(map.len() as u32).write_bytes(output)?;
for (key_as_str, value) in map.iter() {
for (key_as_str, value) in map {
let key = match **key_type {
CLType::I32 => json!(i32::from_str(key_as_str)?),
CLType::I64 => json!(i64::from_str(key_as_str)?),
Expand Down Expand Up @@ -294,7 +294,7 @@ fn write_json_to_bytesrepr(
actual: vec.len(),
});
}
write_json_to_bytesrepr(&inner_cl_types[0], &vec[0], output)?
write_json_to_bytesrepr(&inner_cl_types[0], &vec[0], output)?;
}
(CLType::Tuple2(ref inner_cl_types), Value::Array(vec)) => {
if vec.len() != inner_cl_types.len() {
Expand All @@ -304,7 +304,7 @@ fn write_json_to_bytesrepr(
});
}
write_json_to_bytesrepr(&inner_cl_types[0], &vec[0], output)?;
write_json_to_bytesrepr(&inner_cl_types[1], &vec[1], output)?
write_json_to_bytesrepr(&inner_cl_types[1], &vec[1], output)?;
}
(CLType::Tuple3(ref inner_cl_types), Value::Array(vec)) => {
if vec.len() != inner_cl_types.len() {
Expand All @@ -315,7 +315,7 @@ fn write_json_to_bytesrepr(
}
write_json_to_bytesrepr(&inner_cl_types[0], &vec[0], output)?;
write_json_to_bytesrepr(&inner_cl_types[1], &vec[1], output)?;
write_json_to_bytesrepr(&inner_cl_types[2], &vec[2], output)?
write_json_to_bytesrepr(&inner_cl_types[2], &vec[2], output)?;
}
_ => return Err(ErrorDetails::IncompatibleType),
};
Expand Down
8 changes: 8 additions & 0 deletions lib/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ pub enum Error {
/// Failed to validate response.
#[error("invalid response: {0}")]
ResponseFailedValidation(#[from] ValidateResponseError),

/// Failed to verify contract.
#[error("contract verification failed")]
ContractVerificationFailed,

/// Failed to construct HTTP client.
#[error("failed to construct HTTP client")]
FailedToConstructHttpClient,
}

impl From<ToBytesError> for Error {
Expand Down
53 changes: 52 additions & 1 deletion lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ mod transfer_target;
pub mod types;
mod validation;
mod verbosity;
mod verification;
mod verification_types;

use std::{env::current_dir, path::Path};
#[cfg(feature = "std-fs-io")]
use std::{
fs,
io::{Cursor, Read, Write},
path::Path,
};

#[cfg(feature = "std-fs-io")]
Expand Down Expand Up @@ -111,6 +113,10 @@ use types::{Account, Block, StoredValue};
use types::{Deploy, DeployHash};
pub use validation::ValidateResponseError;
pub use verbosity::Verbosity;
pub use verification::{build_archive, send_verification_request};
use verification_types::VerificationDetails;

use base64::{engine::general_purpose::STANDARD, Engine};

/// Puts a [`Deploy`] to the network for execution.
///
Expand Down Expand Up @@ -554,3 +560,48 @@ pub async fn get_era_info(
.send_request(GET_ERA_INFO_METHOD, params)
.await
}

/// Verifies the smart contract code againt the one deployed at deploy hash.
pub async fn verify_contract(
key: Key,
verification_url_base_path: &str,
project_path: Option<&str>,
verbosity: Verbosity,
) -> Result<VerificationDetails, Error> {
if verbosity == Verbosity::Medium || verbosity == Verbosity::High {
println!("Key: {key}");
println!("Verification service base path: {verification_url_base_path}",);
}

let project_path = match project_path {
Some(path) => Path::new(path).to_path_buf(),
None => match current_dir() {
Ok(path) => path,
Err(error) => {
eprintln!("Cannot get current directory: {error}");
return Err(Error::ContractVerificationFailed);
}
},
};

let archive = match build_archive(&project_path) {
Ok(archive) => {
if verbosity == Verbosity::Medium || verbosity == Verbosity::High {
println!("Created project archive (size: {})", archive.len());
}
archive
}
Err(error) => {
eprintln!("Cannot create project archive: {error}");
return Err(Error::ContractVerificationFailed);
}
};

send_verification_request(
key,
verification_url_base_path,
STANDARD.encode(&archive),
verbosity,
)
.await
}
4 changes: 1 addition & 3 deletions lib/rpcs/v1_6_0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ pub(crate) mod get_block_transfers {
}

pub(crate) mod get_dictionary_item {
pub use crate::rpcs::v1_5_0::get_dictionary_item::{
DictionaryItemIdentifier, GetDictionaryItemResult,
};
pub use crate::rpcs::v1_5_0::get_dictionary_item::GetDictionaryItemResult;
pub(crate) use crate::rpcs::v1_5_0::get_dictionary_item::{
GetDictionaryItemParams, GET_DICTIONARY_ITEM_METHOD,
};
Expand Down
Loading
Loading