Skip to content

Commit 75a130a

Browse files
kulikthebirdautofix-ci[bot]webmaster128
authored
feat: Add Tx hash to TransactionInfo and make it non exhaustive (#2480)
* feat: Add Tx checksum to TransactionInfo and make it non exhaustive * chore: Use Binary instead of Checksum * chore: Move impl_hidden_constructor macro to lib.rs * chore: Update Changelog * [autofix.ci] apply automated fixes * chore: Move the macro to utils module * Update CHANGELOG.md Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> * chore: Update after review * [autofix.ci] apply automated fixes * Update packages/vm/src/testing/mock.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> * Update packages/vm/src/testing/mock.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> * Update packages/std/src/testing/mock.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> * Update packages/std/src/testing/mock.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> * Update packages/std/src/types.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> * Update packages/std/src/types.rs Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> * chore: Update after review * [autofix.ci] apply automated fixes * chore: Add test for hex encoding --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com>
1 parent 7028327 commit 75a130a

File tree

16 files changed

+153
-59
lines changed

16 files changed

+153
-59
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ and this project adheres to
3131
- cosmwasm-std: Add support for `ibc2_packet_ack` endpoint ([#2474])
3232
- cosmwasm-std: Add `Ibc2PacketSendMsg` message - ([#2477])
3333
- cosmwasm-vm: Add `ibc2_packet_send` entrypoint ([#2477])
34+
- cosmwasm-std: Add Tx hash to TransactionInfo and make it non exhaustive
35+
([#2480])
3436

3537
## Changed
3638

@@ -130,6 +132,7 @@ and this project adheres to
130132
[#2472]: https://github.com/CosmWasm/cosmwasm/pull/2472
131133
[#2473]: https://github.com/CosmWasm/cosmwasm/pull/2473
132134
[#2477]: https://github.com/CosmWasm/cosmwasm/pull/2477
135+
[#2480]: https://github.com/CosmWasm/cosmwasm/pull/2480
133136

134137
## [2.2.0] - 2024-12-17
135138

contracts/cyberpunk/schema/cyberpunk.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,10 @@
443443
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
444444
"type": "string"
445445
},
446+
"Binary": {
447+
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.",
448+
"type": "string"
449+
},
446450
"BlockInfo": {
447451
"type": "object",
448452
"required": [
@@ -461,7 +465,7 @@
461465
"minimum": 0.0
462466
},
463467
"time": {
464-
"description": "Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC).\n\nThe source of this is the [BFT Time in Tendermint](https://github.com/tendermint/tendermint/blob/58dc1726/spec/consensus/bft-time.md), which has the same nanosecond precision as the `Timestamp` type.\n\n# Examples\n\nUsing chrono:\n\n``` # use cosmwasm_std::{Addr, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo { index: 3 }), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; # extern crate chrono; use chrono::NaiveDateTime; let seconds = env.block.time.seconds(); let nsecs = env.block.time.subsec_nanos(); let dt = NaiveDateTime::from_timestamp(seconds as i64, nsecs as u32); ```\n\nCreating a simple millisecond-precision timestamp (as used in JavaScript):\n\n``` # use cosmwasm_std::{Addr, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo { index: 3 }), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; let millis = env.block.time.nanos() / 1_000_000; ```",
468+
"description": "Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC).\n\nThe source of this is the [BFT Time in Tendermint](https://github.com/tendermint/tendermint/blob/58dc1726/spec/consensus/bft-time.md), which has the same nanosecond precision as the `Timestamp` type.\n\n# Examples\n\nUsing chrono:\n\n``` # use cosmwasm_std::{Addr, Binary, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo::new(3, Binary::from_hex(\"E5469DACEC17CEF8A260FD37675ED87E7FB6A2B5AD95193C51308006C7E494B3\").unwrap())), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; # extern crate chrono; use chrono::NaiveDateTime; let seconds = env.block.time.seconds(); let nsecs = env.block.time.subsec_nanos(); let dt = NaiveDateTime::from_timestamp(seconds as i64, nsecs as u32); ```\n\nCreating a simple millisecond-precision timestamp (as used in JavaScript):\n\n``` # use cosmwasm_std::{Addr, Binary, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo::new(3, Binary::from_hex(\"E5469DACEC17CEF8A260FD37675ED87E7FB6A2B5AD95193C51308006C7E494B3\").unwrap())), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; let millis = env.block.time.nanos() / 1_000_000; ```",
465469
"allOf": [
466470
{
467471
"$ref": "#/definitions/Timestamp"
@@ -497,6 +501,15 @@
497501
"index"
498502
],
499503
"properties": {
504+
"hash": {
505+
"description": "Hash of the transaction.\n\nIf the blockchain's CosmWasm version is below 3.0, this field will default to being empty.",
506+
"default": "",
507+
"allOf": [
508+
{
509+
"$ref": "#/definitions/Binary"
510+
}
511+
]
512+
},
500513
"index": {
501514
"description": "The position of this transaction in the block. The first transaction has index 0.\n\nThis allows you to get a unique transaction identifier in this chain using the pair (`env.block.height`, `env.transaction.index`).",
502515
"type": "integer",

contracts/cyberpunk/schema/raw/response_to_mirror_env.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
3232
"type": "string"
3333
},
34+
"Binary": {
35+
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.",
36+
"type": "string"
37+
},
3438
"BlockInfo": {
3539
"type": "object",
3640
"required": [
@@ -49,7 +53,7 @@
4953
"minimum": 0.0
5054
},
5155
"time": {
52-
"description": "Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC).\n\nThe source of this is the [BFT Time in Tendermint](https://github.com/tendermint/tendermint/blob/58dc1726/spec/consensus/bft-time.md), which has the same nanosecond precision as the `Timestamp` type.\n\n# Examples\n\nUsing chrono:\n\n``` # use cosmwasm_std::{Addr, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo { index: 3 }), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; # extern crate chrono; use chrono::NaiveDateTime; let seconds = env.block.time.seconds(); let nsecs = env.block.time.subsec_nanos(); let dt = NaiveDateTime::from_timestamp(seconds as i64, nsecs as u32); ```\n\nCreating a simple millisecond-precision timestamp (as used in JavaScript):\n\n``` # use cosmwasm_std::{Addr, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo { index: 3 }), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; let millis = env.block.time.nanos() / 1_000_000; ```",
56+
"description": "Absolute time of the block creation in seconds since the UNIX epoch (00:00:00 on 1970-01-01 UTC).\n\nThe source of this is the [BFT Time in Tendermint](https://github.com/tendermint/tendermint/blob/58dc1726/spec/consensus/bft-time.md), which has the same nanosecond precision as the `Timestamp` type.\n\n# Examples\n\nUsing chrono:\n\n``` # use cosmwasm_std::{Addr, Binary, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo::new(3, Binary::from_hex(\"E5469DACEC17CEF8A260FD37675ED87E7FB6A2B5AD95193C51308006C7E494B3\").unwrap())), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; # extern crate chrono; use chrono::NaiveDateTime; let seconds = env.block.time.seconds(); let nsecs = env.block.time.subsec_nanos(); let dt = NaiveDateTime::from_timestamp(seconds as i64, nsecs as u32); ```\n\nCreating a simple millisecond-precision timestamp (as used in JavaScript):\n\n``` # use cosmwasm_std::{Addr, Binary, BlockInfo, ContractInfo, Env, MessageInfo, Timestamp, TransactionInfo}; # let env = Env { # block: BlockInfo { # height: 12_345, # time: Timestamp::from_nanos(1_571_797_419_879_305_533), # chain_id: \"cosmos-testnet-14002\".to_string(), # }, # transaction: Some(TransactionInfo::new(3, Binary::from_hex(\"E5469DACEC17CEF8A260FD37675ED87E7FB6A2B5AD95193C51308006C7E494B3\").unwrap())), # contract: ContractInfo { # address: Addr::unchecked(\"contract\"), # }, # }; let millis = env.block.time.nanos() / 1_000_000; ```",
5357
"allOf": [
5458
{
5559
"$ref": "#/definitions/Timestamp"
@@ -85,6 +89,15 @@
8589
"index"
8690
],
8791
"properties": {
92+
"hash": {
93+
"description": "Hash of the transaction.\n\nIf the blockchain's CosmWasm version is below 3.0, this field will default to being empty.",
94+
"default": "",
95+
"allOf": [
96+
{
97+
"$ref": "#/definitions/Binary"
98+
}
99+
]
100+
},
88101
"index": {
89102
"description": "The position of this transaction in the block. The first transaction has index 0.\n\nThis allows you to get a unique transaction identifier in this chain using the pair (`env.block.height`, `env.transaction.index`).",
90103
"type": "integer",

packages/std/src/binary.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ impl Binary {
6969
out.copy_from_slice(&self.0);
7070
Ok(out)
7171
}
72+
73+
pub fn from_hex(input: &str) -> StdResult<Self> {
74+
let binary = hex::decode(input).map_err(StdError::invalid_hex)?;
75+
Ok(Self(binary))
76+
}
77+
78+
pub fn to_hex(&self) -> String {
79+
hex::encode(&self.0)
80+
}
7281
}
7382

7483
impl fmt::Display for Binary {
@@ -593,4 +602,20 @@ mod tests {
593602
assert_ne!(a, [0x11, 0x22]);
594603
assert_ne!([0x11, 0x22], a);
595604
}
605+
606+
#[test]
607+
fn hex_encoding_works() {
608+
let hash = "722c8c993fd75a7627d69ed941344fe2a1423a3e75efd3e6778a142884227104";
609+
let parsed = Binary::from_hex(hash).unwrap();
610+
assert_eq!(parsed.to_hex(), hash);
611+
612+
// invalid hex
613+
let odd_num_of_digits = "722c8c993fd75a7627d69ed941344fe2a1423a3e75efd3e6778a142884227";
614+
assert!(Binary::from_hex(odd_num_of_digits).is_err());
615+
let invalid_char = "722c8c993fd75a7627d69ed941344fe2a1423a3e75efd3e6778a1428842271g4";
616+
assert!(Binary::from_hex(invalid_char).is_err());
617+
let hex_prefix_unexpected =
618+
"0x722c8c993fd75a7627d69ed941344fe2a1423a3e75efd3e6778a142884227104";
619+
assert!(Binary::from_hex(hex_prefix_unexpected).is_err());
620+
}
596621
}

packages/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ mod stdack;
4141
mod timestamp;
4242
mod traits;
4343
mod types;
44+
mod utils;
4445

4546
/// This module is to simplify no_std imports
4647
pub(crate) mod prelude;

packages/std/src/query/bank.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::PageRequest;
99
use crate::{Binary, DenomMetadata};
1010

1111
use super::query_response::QueryResponseType;
12+
use crate::utils::impl_hidden_constructor;
1213

1314
#[non_exhaustive]
1415
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
@@ -41,7 +42,7 @@ pub struct SupplyResponse {
4142
pub amount: Coin,
4243
}
4344

44-
impl_response_constructor!(SupplyResponse, amount: Coin);
45+
impl_hidden_constructor!(SupplyResponse, amount: Coin);
4546

4647
impl QueryResponseType for SupplyResponse {}
4748

@@ -54,7 +55,7 @@ pub struct BalanceResponse {
5455
pub amount: Coin,
5556
}
5657

57-
impl_response_constructor!(BalanceResponse, amount: Coin);
58+
impl_hidden_constructor!(BalanceResponse, amount: Coin);
5859

5960
impl QueryResponseType for BalanceResponse {}
6061

@@ -66,7 +67,7 @@ pub struct DenomMetadataResponse {
6667
pub metadata: DenomMetadata,
6768
}
6869

69-
impl_response_constructor!(DenomMetadataResponse, metadata: DenomMetadata);
70+
impl_hidden_constructor!(DenomMetadataResponse, metadata: DenomMetadata);
7071

7172
impl QueryResponseType for DenomMetadataResponse {}
7273

@@ -79,7 +80,7 @@ pub struct AllDenomMetadataResponse {
7980
pub next_key: Option<Binary>,
8081
}
8182

82-
impl_response_constructor!(
83+
impl_hidden_constructor!(
8384
AllDenomMetadataResponse,
8485
metadata: Vec<DenomMetadata>,
8586
next_key: Option<Binary>

packages/std/src/query/distribution.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::prelude::*;
55
use crate::{Addr, Decimal256};
66

77
use super::query_response::QueryResponseType;
8+
use crate::utils::impl_hidden_constructor;
89

910
#[non_exhaustive]
1011
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
@@ -34,7 +35,7 @@ pub struct DelegatorWithdrawAddressResponse {
3435
pub withdraw_address: Addr,
3536
}
3637

37-
impl_response_constructor!(DelegatorWithdrawAddressResponse, withdraw_address: Addr);
38+
impl_hidden_constructor!(DelegatorWithdrawAddressResponse, withdraw_address: Addr);
3839
impl QueryResponseType for DelegatorWithdrawAddressResponse {}
3940

4041
/// See <https://github.com/cosmos/cosmos-sdk/blob/c74e2887b0b73e81d48c2f33e6b1020090089ee0/proto/cosmos/distribution/v1beta1/query.proto#L169-L178>
@@ -45,7 +46,7 @@ pub struct DelegationRewardsResponse {
4546
pub rewards: Vec<DecCoin>,
4647
}
4748

48-
impl_response_constructor!(DelegationRewardsResponse, rewards: Vec<DecCoin>);
49+
impl_hidden_constructor!(DelegationRewardsResponse, rewards: Vec<DecCoin>);
4950
impl QueryResponseType for DelegationRewardsResponse {}
5051

5152
/// A coin type with decimal amount.
@@ -85,7 +86,7 @@ pub struct DelegationTotalRewardsResponse {
8586
pub total: Vec<DecCoin>,
8687
}
8788

88-
impl_response_constructor!(
89+
impl_hidden_constructor!(
8990
DelegationTotalRewardsResponse,
9091
rewards: Vec<DelegatorReward>,
9192
total: Vec<DecCoin>
@@ -98,7 +99,7 @@ pub struct DelegatorReward {
9899
pub validator_address: String,
99100
pub reward: Vec<DecCoin>,
100101
}
101-
impl_response_constructor!(
102+
impl_hidden_constructor!(
102103
DelegatorReward,
103104
validator_address: String,
104105
reward: Vec<DecCoin>
@@ -111,5 +112,5 @@ pub struct DelegatorValidatorsResponse {
111112
pub validators: Vec<String>,
112113
}
113114

114-
impl_response_constructor!(DelegatorValidatorsResponse, validators: Vec<String>);
115+
impl_hidden_constructor!(DelegatorValidatorsResponse, validators: Vec<String>);
115116
impl QueryResponseType for DelegatorValidatorsResponse {}

packages/std/src/query/ibc.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize};
44
use crate::ibc::IbcChannel;
55
use crate::prelude::*;
66

7+
use crate::utils::impl_hidden_constructor;
8+
79
/// These are queries to the various IBC modules to see the state of the contract's
810
/// IBC connection.
911
/// Most of these will return errors if the contract is not "ibc enabled".
@@ -46,20 +48,20 @@ pub struct PortIdResponse {
4648
pub port_id: String,
4749
}
4850

49-
impl_response_constructor!(PortIdResponse, port_id: String);
51+
impl_hidden_constructor!(PortIdResponse, port_id: String);
5052

5153
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
5254
#[non_exhaustive]
5355
pub struct ChannelResponse {
5456
pub channel: Option<IbcChannel>,
5557
}
5658

57-
impl_response_constructor!(ChannelResponse, channel: Option<IbcChannel>);
59+
impl_hidden_constructor!(ChannelResponse, channel: Option<IbcChannel>);
5860

5961
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
6062
#[non_exhaustive]
6163
pub struct FeeEnabledChannelResponse {
6264
pub fee_enabled: bool,
6365
}
6466

65-
impl_response_constructor!(FeeEnabledChannelResponse, fee_enabled: bool);
67+
impl_hidden_constructor!(FeeEnabledChannelResponse, fee_enabled: bool);

packages/std/src/query/mod.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,6 @@ use crate::prelude::*;
88
use crate::Binary;
99
use crate::Empty;
1010

11-
/// Implements a hidden constructor for query responses.
12-
macro_rules! impl_response_constructor {
13-
( $response:ty, $( $field: ident : $t: ty),* ) => {
14-
impl $response {
15-
/// Constructor for testing frameworks such as cw-multi-test.
16-
/// This is required because query response types should be #[non_exhaustive].
17-
/// As a contract developer you should not need this constructor since
18-
/// query responses are constructed for you via deserialization.
19-
///
20-
/// Warning: This can change in breaking ways in minor versions.
21-
#[doc(hidden)]
22-
#[allow(dead_code)]
23-
pub fn new($( $field: $t),*) -> Self {
24-
Self { $( $field ),* }
25-
}
26-
}
27-
};
28-
}
29-
3011
mod bank;
3112
mod distribution;
3213
mod ibc;

0 commit comments

Comments
 (0)