From 547ba52c2a47d3243aa4525f8693b9fe4efb4d5c Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Tue, 28 Nov 2023 10:23:54 +0000 Subject: [PATCH] feat: add possibility to provide new arguments in json format (#871) ## Description The PR adds a new option to pass init arguments in JSON format. ## Performance / NEAR gas cost considerations There are no changes in performance or gas cost. ## Testing A corresponding unit test has been added. ## Additional information The controller contract needs the change to allow passing generic JSON object as initialise arguments. --- Cargo.toml | 8 +++ engine-tests/src/tests/sanity.rs | 2 +- engine-types/src/parameters/engine.rs | 96 +++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8aec4bd6f..e975287a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,3 +128,11 @@ rpath = false # it to actually happen when running tests with --release lto = true opt-level = 3 + +# The profile is needed for faster linking in case we need to run locally a small amount of tests or just test +# business logic rather than test gas cost. E.g. of using the profile with the cargo: +# `cargo test --profile fast-link --features mainnet-test name_of_test_we_want_execute` +[profile.fast-link] +inherits = "dev" +opt-level = 0 +lto = false diff --git a/engine-tests/src/tests/sanity.rs b/engine-tests/src/tests/sanity.rs index ed658418c..462fb097d 100644 --- a/engine-tests/src/tests/sanity.rs +++ b/engine-tests/src/tests/sanity.rs @@ -689,7 +689,7 @@ fn test_num_wasm_functions() { let module = walrus::ModuleConfig::default() .parse(runner.code.code()) .unwrap(); - let expected_number = 1550; + let expected_number = 1600; let actual_number = module.funcs.iter().count(); assert!( diff --git a/engine-types/src/parameters/engine.rs b/engine-types/src/parameters/engine.rs index 684b7f955..cb9ad34fd 100644 --- a/engine-types/src/parameters/engine.rs +++ b/engine-types/src/parameters/engine.rs @@ -20,13 +20,18 @@ pub enum NewCallArgs { } impl NewCallArgs { + /// Creates a `NewCallArs` from the provided bytes which could be represented + /// in JSON or Borsh format. Supporting arguments in JSON format starting from V4. pub fn deserialize(bytes: &[u8]) -> Result { - Self::try_from_slice(bytes).map_or_else( - |_| LegacyNewCallArgs::try_from_slice(bytes).map(Self::V1), - Ok, - ) + Self::try_from_json(bytes).or_else(|_| { + Self::try_from_slice(bytes).map_or_else( + |_| LegacyNewCallArgs::try_from_slice(bytes).map(Self::V1), + Ok, + ) + }) } + /// Returns a genesis hash of the Hashchain if present. #[must_use] pub const fn initial_hashchain(&self) -> Option { match self { @@ -34,6 +39,25 @@ impl NewCallArgs { Self::V1(_) | Self::V2(_) | Self::V3(_) => None, } } + + fn try_from_json(bytes: &[u8]) -> Result { + serde_json::from_slice::(bytes).map(Into::into) + } +} + +impl From for NewCallArgs { + fn from(value: NewCallJsonArgs) -> Self { + match value { + NewCallJsonArgs::V1(args) => Self::V4(args), + } + } +} + +/// JSON encoded new parameters. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum NewCallJsonArgs { + V1(NewCallArgsV4), } /// Old Borsh-encoded parameters for the `new` function. @@ -75,9 +99,10 @@ pub struct NewCallArgsV3 { pub key_manager: AccountId, } -#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)] pub struct NewCallArgsV4 { /// Chain id, according to the EIP-115 / ethereum-lists spec. + #[serde(with = "chain_id_deserialize")] pub chain_id: RawU256, /// Account which can upgrade this contract. /// Use empty to disable updatability. @@ -93,14 +118,14 @@ pub struct NewCallArgsV4 { /// Borsh-encoded parameters for the `set_owner` function. #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -#[cfg_attr(feature = "impl-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))] pub struct SetOwnerArgs { pub new_owner: AccountId, } /// Borsh-encoded parameters for the `set_upgrade_delay_blocks` function. #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -#[cfg_attr(feature = "impl-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))] pub struct SetUpgradeDelayBlocksArgs { pub upgrade_delay_blocks: u64, } @@ -117,14 +142,14 @@ pub struct SubmitArgs { } #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -#[cfg_attr(feature = "impl-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))] pub struct StartHashchainArgs { pub block_height: u64, pub block_hashchain: RawH256, } /// Fungible token storage balance -#[derive(Default, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Default, Debug, Serialize, Deserialize)] pub struct StorageBalance { pub total: Yocto, pub available: Yocto, @@ -148,7 +173,7 @@ pub struct PausePrecompilesCallArgs { } #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -#[cfg_attr(feature = "impl-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))] pub struct ResultLog { pub address: Address, pub topics: Vec, @@ -157,7 +182,7 @@ pub struct ResultLog { /// The status of a transaction. #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)] -#[cfg_attr(feature = "impl-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))] pub enum TransactionStatus { Succeed(Vec), Revert(Vec), @@ -203,7 +228,7 @@ impl AsRef<[u8]> for TransactionStatus { /// Borsh-encoded parameters for the `call`, `call_with_args`, `deploy_code`, /// and `deploy_with_input` methods. #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -#[cfg_attr(feature = "impl-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "impl-serde", derive(Serialize, Deserialize))] pub struct SubmitResult { version: u8, pub status: TransactionStatus, @@ -317,6 +342,27 @@ pub struct RelayerKeyArgs { pub type FullAccessKeyArgs = RelayerKeyArgs; +mod chain_id_deserialize { + use crate::types::{u256_to_arr, RawU256}; + use primitive_types::U256; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + u64::deserialize(deserializer).map(|v| u256_to_arr(&(v.into()))) + } + + pub fn serialize(value: &RawU256, serializer: S) -> Result + where + S: Serializer, + { + let chain_id = U256::from_big_endian(value.as_slice()).low_u64(); + serializer.serialize_u64(chain_id) + } +} + pub mod errors { use crate::{account_id::ParseAccountError, String, ToString}; @@ -431,4 +477,30 @@ mod tests { assert_eq!(args.public_key, public_key); } + + #[test] + fn test_deserialize_new_call_args_json() { + let chain_id = 1_313_161_559; + let json = serde_json::json!({ + "chain_id": chain_id, + "owner_id": "aurora", + "upgrade_delay_blocks": 10, + "key_manager": "manager.near", + "initial_hashchain": null + }); + let arguments = NewCallArgs::deserialize(&serde_json::to_vec(&json).unwrap()); + let Ok(NewCallArgs::V4(arguments)) = arguments else { + panic!("Wrong type of arguments"); + }; + let value = serde_json::to_value(arguments).unwrap(); + assert_eq!(value.get("chain_id").unwrap().as_u64(), Some(chain_id)); + + let outdated = serde_json::json!({ + "chain_id": chain_id, + "owner_id": "aurora", + "upgrade_delay_blocks": 19 + }); + let arguments = NewCallArgs::deserialize(&serde_json::to_vec(&outdated).unwrap()); + assert!(matches!(arguments, Err(_))); + } }