near_kit/types/
transaction.rs1use borsh::{BorshDeserialize, BorshSerialize};
4
5use super::{AccountId, Action, CryptoHash, PublicKey, SecretKey, Signature};
6
7#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
9pub struct Transaction {
10 pub signer_id: AccountId,
12 pub public_key: PublicKey,
14 pub nonce: u64,
16 pub receiver_id: AccountId,
18 pub block_hash: CryptoHash,
20 pub actions: Vec<Action>,
22}
23
24impl Transaction {
25 pub fn new(
27 signer_id: AccountId,
28 public_key: PublicKey,
29 nonce: u64,
30 receiver_id: AccountId,
31 block_hash: CryptoHash,
32 actions: Vec<Action>,
33 ) -> Self {
34 Self {
35 signer_id,
36 public_key,
37 nonce,
38 receiver_id,
39 block_hash,
40 actions,
41 }
42 }
43
44 pub fn get_hash(&self) -> CryptoHash {
46 let bytes = borsh::to_vec(self).expect("transaction serialization should never fail");
47 CryptoHash::hash(&bytes)
48 }
49
50 pub fn get_hash_and_size(&self) -> (CryptoHash, usize) {
52 let bytes = borsh::to_vec(self).expect("transaction serialization should never fail");
53 (CryptoHash::hash(&bytes), bytes.len())
54 }
55
56 pub fn sign(self, signer: &SecretKey) -> SignedTransaction {
58 let hash = self.get_hash();
59 let signature = signer.sign(hash.as_bytes());
60 SignedTransaction {
61 transaction: self,
62 signature,
63 }
64 }
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
69pub struct SignedTransaction {
70 pub transaction: Transaction,
72 pub signature: Signature,
74}
75
76impl SignedTransaction {
77 pub fn get_hash(&self) -> CryptoHash {
79 self.transaction.get_hash()
80 }
81
82 pub fn to_bytes(&self) -> Vec<u8> {
84 borsh::to_vec(self).expect("signed transaction serialization should never fail")
85 }
86
87 pub fn to_base64(&self) -> String {
89 use base64::{Engine as _, engine::general_purpose::STANDARD};
90 STANDARD.encode(self.to_bytes())
91 }
92
93 pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::error::Error> {
105 borsh::from_slice(bytes).map_err(|e| {
106 crate::error::Error::InvalidTransaction(format!(
107 "Failed to deserialize signed transaction: {}",
108 e
109 ))
110 })
111 }
112
113 pub fn from_base64(s: &str) -> Result<Self, crate::error::Error> {
126 use base64::{Engine as _, engine::general_purpose::STANDARD};
127 let bytes = STANDARD.decode(s).map_err(|e| {
128 crate::error::Error::InvalidTransaction(format!("Invalid base64: {}", e))
129 })?;
130 Self::from_bytes(&bytes)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_transaction_hash() {
140 let secret = SecretKey::generate_ed25519();
141 let public = secret.public_key();
142
143 let tx = Transaction::new(
144 "alice.testnet".parse().unwrap(),
145 public,
146 1,
147 "bob.testnet".parse().unwrap(),
148 CryptoHash::ZERO,
149 vec![],
150 );
151
152 let hash = tx.get_hash();
153 assert!(!hash.is_zero());
154 }
155
156 #[test]
157 fn test_sign_transaction() {
158 let secret = SecretKey::generate_ed25519();
159 let public = secret.public_key();
160
161 let tx = Transaction::new(
162 "alice.testnet".parse().unwrap(),
163 public,
164 1,
165 "bob.testnet".parse().unwrap(),
166 CryptoHash::ZERO,
167 vec![],
168 );
169
170 let signed = tx.sign(&secret);
171 assert!(!signed.to_bytes().is_empty());
172 }
173}