-
Notifications
You must be signed in to change notification settings - Fork 115
/
tx.rs
226 lines (207 loc) · 7.04 KB
/
tx.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//! Cosmos SDK transaction support.
//!
//! ## About
//!
//! The [Cosmos SDK](https://v1.cosmos.network/sdk) defines a standard
//! transaction format used by blockchain applications built on the SDK.
//!
//! Transactions are comprised of metadata held in contexts and [`Msg`]s
//! that trigger state changes within a module through the module's [`Msg`] service.
//!
//! When users want to interact with an application and make state changes
//! (e.g. sending coins), they create transactions. Each of a transaction's [`Msg`]s
//! must be signed using the private key associated with the appropriate account(s),
//! before the transaction is broadcasted to the network.
//!
//! A transaction must then be included in a block, validated, and approved by the
//! network through the consensus process.
//!
//! ## Usage
//!
//! The following example illustrates how to build, sign, and parse
//! a Cosmos SDK transaction:
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! use cosmrs::{
//! bank::MsgSend,
//! crypto::secp256k1,
//! tx::{self, Fee, Msg, SignDoc, SignerInfo, Tx},
//! AccountId, Coin
//! };
//!
//! // Generate sender private key.
//! // In real world usage, this account would need to be funded before use.
//! let sender_private_key = secp256k1::SigningKey::random();
//! let sender_public_key = sender_private_key.public_key();
//! let sender_account_id = sender_public_key.account_id("cosmos")?;
//!
//! // Parse recipient address from Bech32.
//! let recipient_account_id = "cosmos19dyl0uyzes4k23lscla02n06fc22h4uqsdwq6z"
//! .parse::<AccountId>()?;
//!
//! ///////////////////////////
//! // Building transactions //
//! ///////////////////////////
//!
//! // We'll be doing a simple send transaction.
//! // First we'll create a "Coin" amount to be sent, in this case 1 million uatoms.
//! let amount = Coin {
//! amount: 1_000_000u128,
//! denom: "uatom".parse()?,
//! };
//!
//! // Next we'll create a send message (from the "bank" module) for the coin
//! // amount we created above.
//! let msg_send = MsgSend {
//! from_address: sender_account_id.clone(),
//! to_address: recipient_account_id,
//! amount: vec![amount.clone()],
//! };
//!
//! // Transaction metadata: chain, account, sequence, gas, fee, timeout, and memo.
//! let chain_id = "cosmoshub-4".parse()?;
//! let account_number = 1;
//! let sequence_number = 0;
//! let gas = 100_000u64;
//! let timeout_height = 9001u16;
//! let memo = "example memo";
//!
//! // Create transaction body from the MsgSend, memo, and timeout height.
//! let tx_body = tx::Body::new(vec![msg_send.to_any()?], memo, timeout_height);
//!
//! // Create signer info from public key and sequence number.
//! // This uses a standard "direct" signature from a single signer.
//! let signer_info = SignerInfo::single_direct(Some(sender_public_key), sequence_number);
//!
//! // Compute auth info from signer info by associating a fee.
//! let auth_info = signer_info.auth_info(Fee::from_amount_and_gas(amount, gas));
//!
//! //////////////////////////
//! // Signing transactions //
//! //////////////////////////
//!
//! // The "sign doc" contains a message to be signed.
//! let sign_doc = SignDoc::new(&tx_body, &auth_info, &chain_id, account_number)?;
//!
//! // Sign the "sign doc" with the sender's private key, producing a signed raw transaction.
//! let tx_signed = sign_doc.sign(&sender_private_key)?;
//!
//! // Serialize the raw transaction as bytes (i.e. `Vec<u8>`).
//! let tx_bytes = tx_signed.to_bytes()?;
//!
//! //////////////////////////
//! // Parsing transactions //
//! //////////////////////////
//!
//! // Parse the serialized bytes from above into a `cosmrs::Tx`
//! let tx_parsed = Tx::from_bytes(&tx_bytes)?;
//! assert_eq!(tx_parsed.body, tx_body);
//! assert_eq!(tx_parsed.auth_info, auth_info);
//! # Ok(())
//! # }
//! ```
pub mod mode_info;
mod auth_info;
mod body;
mod builder;
mod fee;
mod msg;
mod raw;
mod sign_doc;
mod signer_info;
pub use self::{
auth_info::AuthInfo,
body::Body,
builder::BodyBuilder,
fee::Fee,
mode_info::ModeInfo,
msg::Msg,
raw::Raw,
sign_doc::SignDoc,
signer_info::{SignerInfo, SignerPublicKey},
};
pub use crate::{
proto::{cosmos::tx::signing::v1beta1::SignMode, traits::MessageExt},
ErrorReport,
};
use crate::{
proto::{self, traits::Message},
Error, Gas, Result,
};
use tendermint::Hash;
#[cfg(feature = "rpc")]
use crate::rpc;
/// Account number.
pub type AccountNumber = u64;
/// Sequence number.
pub type SequenceNumber = u64;
/// Serialized signature.
pub type SignatureBytes = Vec<u8>;
/// [`Tx`] is the standard type used for broadcasting transactions.
#[derive(Clone, Debug)]
pub struct Tx {
/// Processable content of the transaction
pub body: Body,
/// Authorization related content of the transaction, specifically signers, signer modes
/// and [`Fee`].
pub auth_info: AuthInfo,
/// List of signatures that matches the length and order of [`AuthInfo`]’s `signer_info`s to
/// allow connecting signature meta information like public key and signing mode by position.
///
/// Signatures are provided as raw bytes so as to support current and future signature types.
/// [`AuthInfo`] should be introspected to determine the signature algorithm used.
pub signatures: Vec<SignatureBytes>,
}
impl Tx {
/// Parse a [`Tx`] from serialized bytes.
pub fn from_bytes(bytes: &[u8]) -> Result<Tx> {
Tx::try_from(bytes)
}
/// Use RPC to find a transaction by its hash.
#[cfg(feature = "rpc")]
#[cfg_attr(docsrs, doc(cfg(feature = "rpc")))]
pub async fn find_by_hash<C>(rpc_client: &C, tx_hash: Hash) -> Result<Tx>
where
C: rpc::Client + Send + Sync,
{
// TODO(tarcieri): better conversion or unified `Hash` type, see tendermint-rs#1221
let tx_hash = match tx_hash {
Hash::Sha256(bytes) => tendermint_rpc::abci::transaction::Hash::new(bytes),
_ => return Err(Error::Crypto.into()),
};
let response = rpc_client.tx(tx_hash, false).await?;
Tx::from_bytes(response.tx.as_bytes())
}
}
impl TryFrom<&[u8]> for Tx {
type Error = ErrorReport;
fn try_from(bytes: &[u8]) -> Result<Tx> {
proto::cosmos::tx::v1beta1::Tx::decode(bytes)?.try_into()
}
}
impl TryFrom<proto::cosmos::tx::v1beta1::Tx> for Tx {
type Error = ErrorReport;
fn try_from(proto: proto::cosmos::tx::v1beta1::Tx) -> Result<Tx> {
Ok(Tx {
body: proto
.body
.ok_or(Error::MissingField { name: "body" })?
.try_into()?,
auth_info: proto
.auth_info
.ok_or(Error::MissingField { name: "auth_info" })?
.try_into()?,
signatures: proto.signatures,
})
}
}
impl From<Tx> for proto::cosmos::tx::v1beta1::Tx {
fn from(tx: Tx) -> proto::cosmos::tx::v1beta1::Tx {
proto::cosmos::tx::v1beta1::Tx {
body: Some(tx.body.into()),
auth_info: Some(tx.auth_info.into()),
signatures: tx.signatures,
}
}
}