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" diff --git a/cosmwasm/contracts/v010/compute-tests/test-compute-contract/src/contract.rs b/cosmwasm/contracts/v010/compute-tests/test-compute-contract/src/contract.rs index 9397aae58..74a1ce0e6 100644 --- a/cosmwasm/contracts/v010/compute-tests/test-compute-contract/src/contract.rs +++ b/cosmwasm/contracts/v010/compute-tests/test-compute-contract/src/contract.rs @@ -103,6 +103,7 @@ pub enum InitMsg { to: HumanAddr, code_hash: String, }, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -318,6 +319,7 @@ pub enum HandleMsg { }, CosmosMsgCustom {}, InitNewContract {}, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -546,7 +548,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 +561,24 @@ 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![], - }) + }), + InitMsg::GetEnv {} => Ok(InitResponse { + log: vec![log("env", serde_json_wasm::to_string(&env).unwrap())], + messages: vec![], + }), } } @@ -1243,7 +1257,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 +1271,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/cosmwasm/contracts/v1/compute-tests/test-compute-contract/src/contract.rs b/cosmwasm/contracts/v1/compute-tests/test-compute-contract/src/contract.rs index 1f16fbc60..7f49d4316 100644 --- a/cosmwasm/contracts/v1/compute-tests/test-compute-contract/src/contract.rs +++ b/cosmwasm/contracts/v1/compute-tests/test-compute-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())), } } @@ -1168,6 +1171,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())), } } @@ -1737,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/cosmwasm/contracts/v1/compute-tests/test-compute-contract/src/msg.rs b/cosmwasm/contracts/v1/compute-tests/test-compute-contract/src/msg.rs index 7165edf57..74147e698 100644 --- a/cosmwasm/contracts/v1/compute-tests/test-compute-contract/src/msg.rs +++ b/cosmwasm/contracts/v1/compute-tests/test-compute-contract/src/msg.rs @@ -98,6 +98,7 @@ pub enum InstantiateMsg { to: String, code_hash: String, }, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -397,6 +398,7 @@ pub enum ExecuteMsg { amount: Vec, }, CosmosMsgCustom {}, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -436,6 +438,7 @@ pub enum QueryMsg { msg: String, }, GetContractVersion {}, + GetEnv {}, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 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 b93e54b42..f9160f9ea 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" @@ -6239,3 +6240,136 @@ 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, 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) + + 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{ + { + {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":"","contract_code_hash":"%s"}`, + ctx.BlockHeight(), + // 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: "env", + Value: expectedV1Env, + }, + { + Key: "info", + Value: expectedV1MsgInfo, + }, + }, + }, + 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, + ) + } + + 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 + } + }) + } +} + +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)) +}