From 4c3624fab91b385186cb6aeb07afdf50fb6d058a Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Sun, 11 Sep 2022 12:25:41 +0300 Subject: [PATCH 1/3] Fix v0.10 `env.block.time` + go-tests `TestEnv()` --- .../shared/cosmwasm-types/generic/src/lib.rs | 34 +++++++- .../shared/cosmwasm-types/v0.10/src/types.rs | 2 +- .../shared/cosmwasm-types/v1.0/src/types.rs | 2 +- .../internal/keeper/secret_contracts_test.go | 82 +++++++++++++++++++ .../testdata/test-contract/src/contract.rs | 38 +++++++-- .../v1-sanity-contract/src/contract.rs | 3 + .../testdata/v1-sanity-contract/src/msg.rs | 1 + 7 files changed, 150 insertions(+), 12 deletions(-) diff --git a/cosmwasm/enclaves/shared/cosmwasm-types/generic/src/lib.rs b/cosmwasm/enclaves/shared/cosmwasm-types/generic/src/lib.rs index 38ce4fff0..6ac22a8e3 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-types/generic/src/lib.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-types/generic/src/lib.rs @@ -71,7 +71,36 @@ impl BaseEnv { } fn into_v010(self) -> CwEnv { - CwEnv::V010Env { env: self.0 } + // Assaf: contract_key is irrelevant inside the contract, + // but existing v0.10 contracts might expect it to be populated :facepalm:, + // therefore we are going to leave it populated :shrug:. + + // in secretd v1.3 the timestamp passed from Go was unix time in seconds + // from secretd v1.4 the timestamp passed from Go is unix time in nanoseconds + // v0.10 time is seconds since unix epoch + // v1 time is nanoseconds since unix epoch + // so we need to convert it from nanoseconds to seconds + + CwEnv::V010Env { + env: V010Env { + block: v010types::BlockInfo { + height: self.0.block.height, + // v0.10 env.block.time is seconds since unix epoch + time: v1types::Timestamp::from_nanos(self.0.block.time).seconds(), + chain_id: self.0.block.chain_id, + }, + message: v010types::MessageInfo { + sender: self.0.message.sender, + sent_funds: self.0.message.sent_funds, + }, + contract: v010types::ContractInfo { + address: self.0.contract.address, + }, + contract_key: self.0.contract_key, + contract_code_hash: self.0.contract_code_hash, + transaction: None, + }, + } } /// This is the conversion function from the base to the new env. We assume that if there are @@ -81,8 +110,9 @@ impl BaseEnv { env: V1Env { block: v1types::BlockInfo { height: self.0.block.height, + // v1 env.block.time is nanoseconds since unix epoch time: v1types::Timestamp::from_nanos(self.0.block.time), - chain_id: self.0.block.chain_id.clone(), + chain_id: self.0.block.chain_id, }, contract: v1types::ContractInfo { address: v1types::Addr::unchecked(self.0.contract.address.0), diff --git a/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs b/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs index 277152efa..67ea7d28c 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-types/v0.10/src/types.rs @@ -118,7 +118,7 @@ pub struct TransactionInfo { #[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq)] pub struct BlockInfo { pub height: u64, - // time is seconds since epoch begin (Jan. 1, 1970) + /// Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC). pub time: u64, pub chain_id: String, } diff --git a/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/types.rs b/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/types.rs index b3c290953..e1175729b 100644 --- a/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/types.rs +++ b/cosmwasm/enclaves/shared/cosmwasm-types/v1.0/src/types.rs @@ -16,7 +16,7 @@ pub struct Env { pub struct BlockInfo { /// The height of a block is the number of blocks preceding it in the blockchain. pub height: u64, - /// Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC). + /// Absolute time of the block creation in nanoseconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC). /// /// The source of this is the [BFT Time in Tendermint](https://docs.tendermint.com/master/spec/consensus/bft-time.html), /// which has the same nanosecond precision as the `Timestamp` type. diff --git a/x/compute/internal/keeper/secret_contracts_test.go b/x/compute/internal/keeper/secret_contracts_test.go index 33ede3a3d..c4fd9153b 100644 --- a/x/compute/internal/keeper/secret_contracts_test.go +++ b/x/compute/internal/keeper/secret_contracts_test.go @@ -1,6 +1,7 @@ package keeper import ( + "crypto/sha256" "encoding/base64" "encoding/binary" "encoding/hex" @@ -6238,3 +6239,84 @@ func TestAddrValidateFunction(t *testing.T) { _, _, data, _, _, err = execHelper(t, keeper, ctx, v1ContractAddress, walletA, privKeyA, fmt.Sprintf(`{"validate_address":{"addr":"secret18vd8fpwxzck93qlwghaj6arh4p7c5nyf7hmag8"}}`), true, true, defaultGasForTests, 0) require.Equal(t, string(data), "\"Apple\"") } + +func TestEnv(t *testing.T) { + for _, testContract := range testContracts { + t.Run(testContract.CosmWasmVersion, func(t *testing.T) { + ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, testContract.WasmFilePath, sdk.NewCoins()) + + _, _, contractAddress, _, initErr := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, testContract.IsCosmWasmV1, defaultGasForTests) + require.Empty(t, initErr) + + _, _, _, execEvents, _, execErr := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"get_env":{}}`, true, testContract.IsCosmWasmV1, defaultGasForTests, 1) + + require.Empty(t, execErr) + + if testContract.IsCosmWasmV1 { + requireEvents(t, + []ContractEvent{ + { + {Key: "contract_address", Value: contractAddress.String()}, + { + Key: "env", + Value: fmt.Sprintf( + `{"block":{"height":%d,"time":"%d","chain_id":"%s"},"transaction":null,"contract":{"address":"%s","code_hash":"%s"}}`, + ctx.BlockHeight(), + // env.block.time is nanoseconds since unix epoch + ctx.BlockTime().UnixNano(), + ctx.ChainID(), + contractAddress.String(), + calcCodeHash(testContract.WasmFilePath), + ), + }, + { + Key: "info", + Value: fmt.Sprintf( + `{"sender":"%s","funds":[{"denom":"denom","amount":"1"}]}`, + walletA.String(), + ), + }, + }, + }, + execEvents, + ) + } else { + requireEvents(t, + []ContractEvent{ + { + {Key: "contract_address", Value: contractAddress.String()}, + { + Key: "env", + Value: fmt.Sprintf( + `{"block":{"height":%d,"time":%d,"chain_id":"%s"},"message":{"sender":"%s","sent_funds":[{"denom":"denom","amount":"1"}]},"contract":{"address":"%s"},"contract_key":"%s","contract_code_hash":"%s"}`, + ctx.BlockHeight(), + // env.block.time is seconds since unix epoch + ctx.BlockTime().Unix(), + ctx.ChainID(), + walletA.String(), + contractAddress.String(), + base64.StdEncoding.EncodeToString(keeper.GetContractKey(ctx, contractAddress)), + calcCodeHash(testContract.WasmFilePath), + ), + }, + }, + }, + execEvents, + ) + } + }) + } +} + +func calcCodeHash(wasmPath string) string { + wasmCode, err := os.ReadFile(wasmPath) + if err != nil { + panic(fmt.Sprintf("calcCodeHash: %+v", err)) + } + + h := sha256.New() + + h.Write(wasmCode) + + return hex.EncodeToString(h.Sum(nil)) +} diff --git a/x/compute/internal/keeper/testdata/test-contract/src/contract.rs b/x/compute/internal/keeper/testdata/test-contract/src/contract.rs index 9397aae58..cfa3e3935 100644 --- a/x/compute/internal/keeper/testdata/test-contract/src/contract.rs +++ b/x/compute/internal/keeper/testdata/test-contract/src/contract.rs @@ -318,6 +318,7 @@ pub enum HandleMsg { }, CosmosMsgCustom {}, InitNewContract {}, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -546,7 +547,11 @@ pub fn init( messages: vec![CosmosMsg::Custom(Empty {})], log: vec![], }), - InitMsg::SendMultipleFundsToExecCallback { coins, to, code_hash } => Ok(InitResponse { + InitMsg::SendMultipleFundsToExecCallback { + coins, + to, + code_hash, + } => Ok(InitResponse { messages: vec![CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: to, msg: Binary::from("{\"no_data\":{}}".as_bytes().to_vec()), @@ -555,16 +560,20 @@ pub fn init( })], log: vec![], }), - InitMsg::SendMultipleFundsToInitCallback { coins, code_id, code_hash } => Ok(InitResponse { + InitMsg::SendMultipleFundsToInitCallback { + coins, + code_id, + code_hash, + } => Ok(InitResponse { messages: vec![CosmosMsg::Wasm(WasmMsg::Instantiate { code_id, msg: Binary::from("{\"nop\":{}}".as_bytes().to_vec()), callback_code_hash: code_hash, send: coins, - label: "test".to_string() + label: "test".to_string(), })], log: vec![], - }) + }), } } @@ -1243,7 +1252,11 @@ pub fn handle( log: vec![], data: None, }), - HandleMsg::SendMultipleFundsToExecCallback { coins, to, code_hash } => Ok(HandleResponse { + HandleMsg::SendMultipleFundsToExecCallback { + coins, + to, + code_hash, + } => Ok(HandleResponse { messages: vec![CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: to, msg: Binary::from("{\"no_data\":{}}".as_bytes().to_vec()), @@ -1253,17 +1266,26 @@ pub fn handle( log: vec![], data: None, }), - HandleMsg::SendMultipleFundsToInitCallback { coins, code_id, code_hash } => Ok(HandleResponse { + HandleMsg::SendMultipleFundsToInitCallback { + coins, + code_id, + code_hash, + } => Ok(HandleResponse { messages: vec![CosmosMsg::Wasm(WasmMsg::Instantiate { code_id, msg: Binary::from("{\"nop\":{}}".as_bytes().to_vec()), callback_code_hash: code_hash, send: coins, - label: "test".to_string() + label: "test".to_string(), })], log: vec![], data: None, - }) + }), + HandleMsg::GetEnv {} => Ok(HandleResponse { + log: vec![log("env", serde_json_wasm::to_string(&env).unwrap())], + data: None, + messages: vec![], + }), } } diff --git a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs index 1f16fbc60..8ba03e05b 100644 --- a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs +++ b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs @@ -1168,6 +1168,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S Ok(a) => Ok(Response::new().set_data(a.as_bytes())), Err(_) => Ok(Response::new().set_data(to_binary("Apple")?)), }, + ExecuteMsg::GetEnv {} => Ok(Response::new() + .add_attribute("env", serde_json_wasm::to_string(&env).unwrap()) + .add_attribute("info", serde_json_wasm::to_string(&info).unwrap())), } } diff --git a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs index 7165edf57..c91d1bebe 100644 --- a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs +++ b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs @@ -397,6 +397,7 @@ pub enum ExecuteMsg { amount: Vec, }, CosmosMsgCustom {}, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] From f9464c1ad9460313d3b3143b1ecbbba68cc58a58 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Sun, 11 Sep 2022 12:58:56 +0300 Subject: [PATCH 2/3] Also go-tests env for init & query --- .../internal/keeper/secret_contracts_test.go | 76 ++++++++++++++++--- .../testdata/test-contract/src/contract.rs | 5 ++ .../v1-sanity-contract/src/contract.rs | 11 ++- .../testdata/v1-sanity-contract/src/msg.rs | 2 + 4 files changed, 81 insertions(+), 13 deletions(-) diff --git a/x/compute/internal/keeper/secret_contracts_test.go b/x/compute/internal/keeper/secret_contracts_test.go index c4fd9153b..52f779c8e 100644 --- a/x/compute/internal/keeper/secret_contracts_test.go +++ b/x/compute/internal/keeper/secret_contracts_test.go @@ -6245,14 +6245,41 @@ func TestEnv(t *testing.T) { t.Run(testContract.CosmWasmVersion, func(t *testing.T) { ctx, keeper, codeID, _, walletA, privKeyA, _, _ := setupTest(t, testContract.WasmFilePath, sdk.NewCoins()) - _, _, contractAddress, _, initErr := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, testContract.IsCosmWasmV1, defaultGasForTests) + _, _, contractAddress, initEvents, initErr := initHelperImpl(t, keeper, ctx, codeID, walletA, privKeyA, `{"get_env":{}}`, true, testContract.IsCosmWasmV1, defaultGasForTests, -1, sdk.NewCoins(sdk.NewInt64Coin("denom", 1))) require.Empty(t, initErr) - _, _, _, execEvents, _, execErr := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"get_env":{}}`, true, testContract.IsCosmWasmV1, defaultGasForTests, 1) - - require.Empty(t, execErr) + expectedV1Env := fmt.Sprintf( + `{"block":{"height":%d,"time":"%d","chain_id":"%s"},"transaction":null,"contract":{"address":"%s","code_hash":"%s"}}`, + ctx.BlockHeight(), + // env.block.time is nanoseconds since unix epoch + ctx.BlockTime().UnixNano(), + ctx.ChainID(), + contractAddress.String(), + calcCodeHash(testContract.WasmFilePath), + ) + expectedV1MsgInfo := fmt.Sprintf( + `{"sender":"%s","funds":[{"denom":"denom","amount":"1"}]}`, + walletA.String(), + ) if testContract.IsCosmWasmV1 { + requireEvents(t, + []ContractEvent{ + { + {Key: "contract_address", Value: contractAddress.String()}, + { + Key: "env", + Value: expectedV1Env, + }, + { + Key: "info", + Value: expectedV1MsgInfo, + }, + }, + }, + initEvents, + ) + } else { requireEvents(t, []ContractEvent{ { @@ -6260,21 +6287,36 @@ func TestEnv(t *testing.T) { { Key: "env", Value: fmt.Sprintf( - `{"block":{"height":%d,"time":"%d","chain_id":"%s"},"transaction":null,"contract":{"address":"%s","code_hash":"%s"}}`, + `{"block":{"height":%d,"time":%d,"chain_id":"%s"},"message":{"sender":"%s","sent_funds":[{"denom":"denom","amount":"1"}]},"contract":{"address":"%s"},"contract_key":"","contract_code_hash":"%s"}`, ctx.BlockHeight(), - // env.block.time is nanoseconds since unix epoch - ctx.BlockTime().UnixNano(), + // env.block.time is seconds since unix epoch + ctx.BlockTime().Unix(), ctx.ChainID(), + walletA.String(), contractAddress.String(), calcCodeHash(testContract.WasmFilePath), ), }, + }, + }, + initEvents, + ) + } + _, _, _, execEvents, _, execErr := execHelper(t, keeper, ctx, contractAddress, walletA, privKeyA, `{"get_env":{}}`, true, testContract.IsCosmWasmV1, defaultGasForTests, 1) + require.Empty(t, execErr) + + if testContract.IsCosmWasmV1 { + requireEvents(t, + []ContractEvent{ + { + {Key: "contract_address", Value: contractAddress.String()}, { - Key: "info", - Value: fmt.Sprintf( - `{"sender":"%s","funds":[{"denom":"denom","amount":"1"}]}`, - walletA.String(), - ), + Key: "env", + Value: expectedV1Env, + }, + { + Key: "info", + Value: expectedV1MsgInfo, }, }, }, @@ -6304,6 +6346,16 @@ func TestEnv(t *testing.T) { execEvents, ) } + + if testContract.IsCosmWasmV1 { + // only env (no msg info) in v1 query + queryRes, qErr := queryHelper(t, keeper, ctx, contractAddress, `{"get_env":{}}`, true, false, math.MaxUint64) + require.Empty(t, qErr) + + require.Equal(t, queryRes, expectedV1Env) + } else { + // no env or msg info in v0.10 query + } }) } } diff --git a/x/compute/internal/keeper/testdata/test-contract/src/contract.rs b/x/compute/internal/keeper/testdata/test-contract/src/contract.rs index cfa3e3935..74a1ce0e6 100644 --- a/x/compute/internal/keeper/testdata/test-contract/src/contract.rs +++ b/x/compute/internal/keeper/testdata/test-contract/src/contract.rs @@ -103,6 +103,7 @@ pub enum InitMsg { to: HumanAddr, code_hash: String, }, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -574,6 +575,10 @@ pub fn init( })], log: vec![], }), + InitMsg::GetEnv {} => Ok(InitResponse { + log: vec![log("env", serde_json_wasm::to_string(&env).unwrap())], + messages: vec![], + }), } } diff --git a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs index 8ba03e05b..7f49d4316 100644 --- a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs +++ b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/contract.rs @@ -17,7 +17,7 @@ use crate::state::{count, count_read, expiration, expiration_read}; pub fn instantiate( deps: DepsMut, env: Env, - _info: MessageInfo, + info: MessageInfo, msg: InstantiateMsg, ) -> StdResult { match msg { @@ -321,6 +321,9 @@ pub fn instantiate( funds: coins, })), ), + InstantiateMsg::GetEnv {} => Ok(Response::new() + .add_attribute("env", serde_json_wasm::to_string(&env).unwrap()) + .add_attribute("info", serde_json_wasm::to_string(&info).unwrap())), } } @@ -1740,6 +1743,12 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { let answer: u8 = 1; return Ok(to_binary(&answer)?); } + QueryMsg::GetEnv {} => Ok(Binary::from( + serde_json_wasm::to_string(&env) + .unwrap() + .as_bytes() + .to_vec(), + )), } } diff --git a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs index c91d1bebe..74147e698 100644 --- a/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs +++ b/x/compute/internal/keeper/testdata/v1-sanity-contract/src/msg.rs @@ -98,6 +98,7 @@ pub enum InstantiateMsg { to: String, code_hash: String, }, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -437,6 +438,7 @@ pub enum QueryMsg { msg: String, }, GetContractVersion {}, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] From 5506e4130889a6d471b71461b55ef621c22504c9 Mon Sep 17 00:00:00 2001 From: Assaf Morami Date: Sun, 11 Sep 2022 13:55:27 +0300 Subject: [PATCH 3/3] Update .gitignore & .vscode/setting.json for #1126 --- .gitignore | 8 +++++++- .vscode/settings.json | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index ad0316cc2..3c99d9456 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,10 @@ ias_bin_sw.go node_modules /secretjs /secret.js -tmp-swagger-gen \ No newline at end of file +tmp-swagger-gen +x/compute/internal/keeper/testdata/contract_with_floats.wasm +x/compute/internal/keeper/testdata/contract.wasm +x/compute/internal/keeper/testdata/ibc.wasm +x/compute/internal/keeper/testdata/static-too-high-initial-memory.wasm +x/compute/internal/keeper/testdata/too-high-initial-memory.wasm +x/compute/internal/keeper/testdata/v1-contract.wasm diff --git a/.vscode/settings.json b/.vscode/settings.json index 8318189d7..f19751d70 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,9 +2,9 @@ "rust-analyzer.linkedProjects": [ "cosmwasm/Cargo.toml", "cosmwasm/enclaves/Cargo.toml", - "x/compute/internal/keeper/testdata/v1-sanity-contract/Cargo.toml", - "x/compute/internal/keeper/testdata/test-contract/Cargo.toml", - "./x/compute/internal/keeper/testdata/ibc/Cargo.toml", + "cosmwasm/contracts/v1/compute-tests/test-compute-contract/Cargo.toml", + "cosmwasm/contracts/v1/compute-tests/ibc-test-contract/Cargo.toml", + "cosmwasm/contracts/v010/compute-tests/test-compute-contract/Cargo.toml", "integration-tests/contract-v1/Cargo.toml", "integration-tests/contract-v0.10/Cargo.toml", "go-cosmwasm/Cargo.toml"