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 for feat-2.0 branch #184

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
17bb79f
Added verify-contract command
kchudy Dec 22, 2023
0148785
Added verify contract to lib
kchudy Dec 22, 2023
70ac387
Added function for creating archive
kchudy Jan 2, 2024
7ff89c0
Added arg parsing
kchudy Jan 2, 2024
eeabc5b
Added verificator api client
kchudy Jan 2, 2024
3a93b2c
Added waiting for verification to finish
kchudy Jan 3, 2024
d705c97
Switched to deploy hash
kchudy Jan 3, 2024
41e3816
Updated comments
kchudy Jan 3, 2024
c2a8e89
Added getting verification details
kchudy Jan 5, 2024
906a080
Imploved error handling
kchudy Jan 8, 2024
3c30a1b
Improved logging
kchudy Jan 8, 2024
13e9698
Refactored verification function
kchudy Jan 8, 2024
5c2624c
Initialized API client
kchudy Jan 8, 2024
6d098d7
Adopt new datatypes; use reqwest
moubctez Jan 22, 2024
31b511a
Cleanup
moubctez Jan 22, 2024
85a5192
Fix result type
moubctez Jan 22, 2024
2b5b981
Fixes and cleanups
moubctez Jan 22, 2024
58b73b1
Sync structs with verificator
moubctez Jan 23, 2024
ce01727
More retries
moubctez Jan 24, 2024
103606c
Increase number of retries
moubctez Jan 25, 2024
935262a
Shorten message
moubctez Jan 25, 2024
2d530d5
Resolve comments
moubctez Apr 10, 2024
18c263e
Update src/verify_contract.rs
moubctez Apr 11, 2024
ad14347
cargo fmt
moubctez Apr 11, 2024
4159153
Update vergen to v8. This resolves audit error.
moubctez Apr 19, 2024
091215e
Resolve pull request comments
moubctez Jun 20, 2024
c2bb909
Restore vergen
moubctez Jun 24, 2024
b09ec9b
Use correct build.rs
moubctez Jun 24, 2024
8fb5f21
Fix build without default features. Now tokio is required with "time"…
moubctez Jun 26, 2024
4ec2565
Fix wasm build
moubctez Jun 27, 2024
8ee8610
Fix CI-CD tokio not optional
gRoussac Jun 27, 2024
c39952f
post-merge fixes
Jul 17, 2024
713a7cc
remove references to deploy hash in contract verification
Jul 17, 2024
09b6993
fix typo
Jul 18, 2024
5adcd50
remove duplicate CLI argument
Jul 26, 2024
f657151
pass deploy/transaction hash to verification service as string
Jul 26, 2024
5481a59
make struct fields public
Jul 26, 2024
9f408ca
update gitignore
Aug 5, 2024
9aa1ca2
Merge branch 'refs/heads/feat-track-node-2.0' into contract-verificat…
Aug 5, 2024
f5eb8c8
add missing newline
Aug 5, 2024
44f2d6b
Merge branch 'refs/heads/feat-track-node-2.0' into contract-verificat…
Sep 12, 2024
2aaa4ef
resolve review comments
Sep 12, 2024
25c3ed9
update toolchain
gRoussac Sep 12, 2024
9831851
Merge pull request #3 from gRoussac/teonite-feat-2.0
wojcik91 Sep 12, 2024
5178548
update sample transaction
Sep 12, 2024
a5901c3
Merge branch 'feat-track-node-2.0' into contract-verification-command…
Oct 23, 2024
c0e562b
add AddressableEntity export
Oct 23, 2024
191b80d
cargo fmt
gRoussac Oct 23, 2024
77d05c1
clippy
gRoussac Oct 23, 2024
e9409fb
clippy
gRoussac Oct 23, 2024
8df82e9
Merge pull request #5 from gRoussac/teonite-compile
wojcik91 Oct 23, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/target
Cargo.lock
*casper_keygen_test*
.idea/
.envrc
.direnv/
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ std-fs-io = ["casper-types/std-fs-io"]
[dependencies]
async-trait = { version = "0.1.74", default-features = false, optional = true }
base16 = "0.2.1"
base64 = { version = "0.22.1", default-features = false }
bytes = { version = "1.6.0", default-features = false }
casper-types = { version = "5.0.0", features = ["std", "json-schema"] }
clap = { version = "~4.4", features = ["cargo", "deprecated"], optional = true }
clap_complete = { version = "~4.4", default-features = false, optional = true }
flate2 = "1.0.30"
hex-buffer-serde = "0.4.0"
humantime = "2.1.0"
itertools = "0.12.0"
Expand All @@ -47,6 +50,7 @@ schemars = "0.8.18"
serde = { version = "1", default-features = false, features = ["derive"] }
serde-map-to-array = "1.1.1"
serde_json = { version = "1", features = ["preserve_order"] }
tar = { version = "0.4.41", default-features = false }
thiserror = "1"
tokio = { version = "1.39.3", features = ["macros", "rt", "sync", "time"] }
uint = "0.9.5"
Expand Down
23 changes: 23 additions & 0 deletions lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ use crate::{
},
SuccessResponse,
};

#[cfg(feature = "std-fs-io")]
use crate::verification_types::VerificationDetails;
#[cfg(doc)]
use crate::{Account, Block, Error, StoredValue, Transfer};
#[cfg(doc)]
Expand Down Expand Up @@ -614,3 +617,23 @@ pub async fn get_era_info(
.await
.map_err(CliError::from)
}

/// Verifies the smart contract code against the one installed
/// by deploy or transaction with given hash.
#[cfg(feature = "std-fs-io")]
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 verbosity = parse::verbosity(verbosity_level);
crate::verify_contract(
hash_str,
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 @@ -158,6 +158,14 @@ pub enum Error {
/// Underlying error.
error: std::str::Utf8Error,
},

/// 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
54 changes: 54 additions & 0 deletions lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ pub mod rpcs;
pub mod types;
mod validation;
mod verbosity;
mod verification;
mod verification_types;

#[cfg(any(feature = "std-fs-io", test))]
use std::{
env::current_dir,
fs,
io::{Cursor, Read, Write},
path::Path,
Expand All @@ -65,6 +68,8 @@ use casper_types::{
#[cfg(any(feature = "std-fs-io", test))]
use casper_types::{SecretKey, TransactionV1};

#[cfg(any(feature = "std-fs-io", test))]
use base64::{engine::general_purpose::STANDARD, Engine};
pub use error::Error;
use json_rpc::JsonRpcCall;
pub use json_rpc::{JsonRpcId, SuccessResponse};
Expand Down Expand Up @@ -112,6 +117,9 @@ use rpcs::{
};
pub use validation::ValidateResponseError;
pub use verbosity::Verbosity;
pub use verification::{build_archive, send_verification_request};
#[cfg(any(feature = "std-fs-io", test))]
use verification_types::VerificationDetails;

/// The maximum permissible size in bytes of a Deploy when serialized via `ToBytes`.
///
Expand Down Expand Up @@ -749,3 +757,49 @@ pub async fn get_era_info(
.send_request(GET_ERA_INFO_METHOD, params)
.await
}

/// Verifies the smart contract code against the one deployed at given deploy or transaction hash.
#[cfg(any(feature = "std-fs-io", test))]
pub async fn verify_contract(
hash_str: &str,
verification_url_base_path: &str,
project_path: Option<&str>,
verbosity: Verbosity,
) -> Result<VerificationDetails, Error> {
if verbosity == Verbosity::Medium || verbosity == Verbosity::High {
println!("Hash: {hash_str}");
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(
hash_str,
verification_url_base_path,
STANDARD.encode(&archive),
verbosity,
)
.await
}
2 changes: 1 addition & 1 deletion lib/rpcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub(crate) mod v2_0_0;
pub use v2_0_0::{
get_account::AccountIdentifier,
get_dictionary_item::DictionaryItemIdentifier,
get_entity::{EntityIdentifier, EntityOrAccount},
get_entity::{AddressableEntity, EntityIdentifier, EntityOrAccount},
get_reward::EraIdentifier,
query_balance::PurseIdentifier,
query_global_state::GlobalStateIdentifier,
Expand Down
17 changes: 16 additions & 1 deletion lib/rpcs/v2_0_0/get_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,26 @@ pub enum EntityIdentifier {
EntityAddr(EntityAddr),
}

/// An addressable entity with named keys and entry points.
/// Represents an entity that is addressable within the Casper system.
/// This struct holds the entity, its associated named keys, and its
/// entry points for interaction.
///
/// # Fields
///
/// * `entity` - The addressable entity which could be a contract, account, or other entity types in Casper.
/// * `named_keys` - A collection of named keys that are associated with the entity. Named keys
/// are a mapping from string identifiers to keys (e.g., contracts, URefs, etc.).
/// * `entry_points` - A list of entry points representing methods or functions that the entity exposes.
/// Entry points define the public interface of a contract or other executable object.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
pub struct AddressableEntity {
/// The core addressable entity in Casper (account, contract, etc.).
pub entity: CasperTypesAddressableEntity,

/// The named keys associated with the entity, mapping identifiers to actual keys.
pub named_keys: NamedKeys,

/// A collection of entry points for the entity, defining its public interface.
pub entry_points: Vec<EntryPointValue>,
}

Expand Down
6 changes: 3 additions & 3 deletions lib/rpcs/v2_0_0/get_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ impl GetTransactionParams {
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct ExecutionInfo {
pub(crate) block_hash: BlockHash,
pub(crate) block_height: u64,
pub(crate) execution_result: Option<ExecutionResult>,
pub block_hash: BlockHash,
pub block_height: u64,
pub execution_result: Option<ExecutionResult>,
}

/// Result for "info_get_transaction" RPC response.
Expand Down
Loading
Loading