postchain_client/utils/transaction.rs
1//! Transaction handling and digital signature functionality.
2//!
3//! This module provides functionality for creating, managing, and signing blockchain
4//! transactions. It supports single and multi-signature transactions using ECDSA
5//! with the secp256k1 curve.
6//!
7//! # Features
8//! - Transaction creation and management
9//! - Transaction ID generation
10//! - Single and multi-signature support
11//! - GTV (Generic Tree Value) encoding
12//!
13//! # Example
14//! ```
15//! use crate::utils::transaction::{Transaction, TransactionStatus};
16//!
17//! let brid = "FA189BEBA886669CF7DF7DB3D8CFD878D1F80ED360BDCF26B43ABE3D9B3D53CC"; // Replace with actual blockchain RID
18//!
19//! let brid_to_vec = hex::decode(brid).unwrap();
20//!
21//! // Create a new transaction
22//! let mut tx = Transaction::new(
23//! brid_to_vec, // blockchain RID
24//! Some(vec![]), // operations
25//! None, // signers
26//! None // signatures
27//! );
28//!
29//! // Sign the transaction
30//! let private_key1 = "C70D5A77CC10552019179B7390545C46647C9FCA1B6485850F2B913F87270300"; // Replace with actual private key
31//! tx.sign(&hex::decode(private_key1).unwrap().try_into().expect("Invalid private key 1")).expect("Failed to sign transaction");
32//!
33//! // Multi sign the transaction
34//! let private_key2 = "17106092B72489B785615BD2ACB2DDE8D0EA05A2029DCA4054987494781F988C"; // Replace with actual private key
35//! tx.sign(&[
36//! &hex::decode(private_key1).unwrap().try_into().expect("Invalid private key 1"),
37//! &hex::decode(private_key2).unwrap().try_into().expect("Invalid private key 2")
38//! ]).expect("Failed to multi sign transaction");
39//!
40//! // Sign the transaction from raw private key
41//! tx.sign_from_raw_priv_key(private_key1);
42//!
43//! // Multi sign the transaction from raw private keys
44//! tx.multi_sign_from_raw_priv_keys(&[private_key1, private_key2]);
45//!
46//! ```
47
48
49use crate::encoding::gtv;
50use crate::utils::hasher::gtv_hash;
51use super::{hasher, operation::Operation};
52use secp256k1::{PublicKey, Secp256k1, SecretKey, Message, ecdsa::Signature};
53use hex::FromHex;
54
55/// Represents the current status of a transaction in the blockchain.
56#[derive(Debug, PartialEq)]
57pub enum TransactionStatus {
58 /// Transaction was rejected by the blockchain
59 REJECTED,
60 /// Transaction has been confirmed and included in a block
61 CONFIRMED,
62 /// Transaction is waiting to be included in a block
63 WAITING,
64 /// Transaction status is unknown
65 UNKNOWN
66}
67
68/// Represents a blockchain transaction with operations and signatures.
69///
70/// A transaction contains a list of operations to be executed, along with
71/// the necessary signatures to authorize these operations. It supports
72/// both single and multi-signature scenarios.
73#[derive(Debug)]
74pub struct Transaction<'a> {
75 /// Unique identifier of the blockchain this transaction belongs to
76 pub blockchain_rid: Vec<u8>,
77 /// List of operations to be executed in this transaction
78 pub operations: Option<Vec<Operation<'a>>>,
79 /// List of public keys of the signers
80 pub signers: Option<Vec<Vec<u8>>>,
81 /// List of signatures corresponding to the signers
82 pub signatures: Option<Vec<Vec<u8>>>,
83 // Hash version (default is 1)
84 pub merkle_hash_version: u8
85}
86
87impl<'a> Default for Transaction<'a> {
88 /// Creates a new Transaction with default values and performs automatic initialization.
89 ///
90 /// # Example
91 /// ```
92 /// let mut tx = Transaction {
93 /// blockchain_rid: hex::decode(brid).unwrap(),
94 /// operations: Some(ops),
95 /// ..Default::default() // This will trigger auto initialization
96 /// };
97 /// ```
98 fn default() -> Self {
99 Self {
100 blockchain_rid: vec![],
101 operations: None,
102 signers: None,
103 signatures: None,
104 merkle_hash_version: 1
105 }
106 }
107}
108
109impl<'a> Transaction<'a> {
110 /// Creates a new transaction with the specified parameters.
111 ///
112 /// # Arguments
113 /// * `blockchain_rid` - Unique identifier of the blockchain
114 /// * `operations` - Optional list of operations to be executed
115 /// * `signers` - Optional list of public keys of the signers
116 /// * `signatures` - Optional list of signatures
117 ///
118 /// # Returns
119 /// A new Transaction instance
120 pub fn new(blockchain_rid: Vec<u8>,
121 operations: Option<Vec<Operation<'a>>>,
122 signers: Option<Vec<Vec<u8>>>,
123 signatures: Option<Vec<Vec<u8>>>) -> Self {
124 Self {
125 blockchain_rid,
126 operations,
127 signers,
128 signatures,
129 ..Default::default()
130 }
131 }
132
133 /// Returns the hex-encoded GTV (Generic Tree Value) representation of the transaction.
134 ///
135 /// This method encodes the transaction into GTV format and returns it as a
136 /// hexadecimal string.
137 ///
138 /// # Returns
139 /// Hex-encoded string of the GTV-encoded transaction
140 pub fn gvt_hex_encoded(&self) -> String {
141 let gtv_e = gtv::encode_tx(self);
142 let hex_encode = hex::encode(gtv_e);
143 hex_encode
144 }
145
146 /// Computes the unique identifier (RID) of this transaction.
147 ///
148 /// The transaction RID is computed by hashing the GTV representation
149 /// of the transaction using the GTX hash function.
150 ///
151 /// # Returns
152 /// A fixed-size 32 bytes containing the transaction RID
153 pub fn tx_rid(&self) -> Result<[u8; 32], hasher::HashError> {
154 let to_draw_gtx = gtv::to_draw_gtx(self);
155 gtv_hash(to_draw_gtx, self.merkle_hash_version)
156 }
157
158 /// Returns the hex-encoded transaction RID.
159 ///
160 /// This is a convenience method that returns the transaction RID
161 /// as a hexadecimal string.
162 ///
163 /// # Returns
164 /// Hex-encoded string of the transaction RID
165 pub fn tx_rid_hex(&self) -> Result<String, hasher::HashError> {
166 Ok(hex::encode(self.tx_rid()?))
167 }
168
169 /// Signs the transaction using a raw private key string.
170 ///
171 /// # Arguments
172 /// * `private_key` - Private key as a string
173 ///
174 /// # Returns
175 /// Result indicating success or a secp256k1 error
176 ///
177 /// # Errors
178 /// Returns an error if the private key is invalid or signing fails
179 pub fn sign_from_raw_priv_key(&mut self, private_key: &str) -> Result<(), secp256k1::Error> {
180 let private_key_bytes = Vec::from_hex(private_key).map_err(|_| secp256k1::Error::InvalidSecretKey)?;
181 let private_key = private_key_bytes.try_into().map_err(|_| secp256k1::Error::InvalidSecretKey)?;
182 self.sign(&private_key)
183 }
184
185 /// Signs the transaction with multiple raw private key strings.
186 ///
187 /// This method iteratively signs the transaction with each provided
188 /// private key string, enabling multi-signature transactions.
189 ///
190 /// # Arguments
191 /// * `private_keys` - Slice of raw private key strings
192 ///
193 /// # Returns
194 /// Result indicating success or a secp256k1 error
195 ///
196 /// # Errors
197 /// Returns an error if any private key is invalid or signing fails
198 pub fn multi_sign_from_raw_priv_keys(&mut self, private_keys: &[&str]) -> Result<(), secp256k1::Error> {
199 let private_keys_bytes: Vec<[u8; 32]> = private_keys
200 .iter()
201 .map(|private_key_hex| {
202 let private_key_bytes = Vec::from_hex(private_key_hex).map_err(|_| secp256k1::Error::InvalidSecretKey)?;
203 private_key_bytes.try_into().map_err(|_| secp256k1::Error::InvalidSecretKey)
204 })
205 .collect::<Result<Vec<[u8; 32]>, secp256k1::Error>>()?;
206
207 let private_keys_refs: Vec<&[u8; 32]> = private_keys_bytes.iter().collect();
208
209 self.multi_sign(private_keys_refs.as_slice())
210 }
211
212 /// Signs the transaction using a private key.
213 ///
214 /// This method:
215 /// 1. Derives the public key from the private key
216 /// 2. Adds the public key to the signers list
217 /// 3. Signs the transaction RID
218 /// 4. Adds the signature to the signatures list
219 ///
220 /// # Arguments
221 /// * `private_key` - 32-byte private key
222 ///
223 /// # Returns
224 /// Result indicating success or a secp256k1 error
225 ///
226 /// # Errors
227 /// Returns an error if the private key is invalid or signing fails
228 pub fn sign(&mut self, private_key: &[u8; 32]) -> Result<(), secp256k1::Error> {
229 let public_key = get_public_key(private_key)?;
230
231 self.signers.get_or_insert_with(Vec::new).push(public_key.to_vec());
232
233 let digest = self.tx_rid().map_err(|_| secp256k1::Error::InvalidMessage)?;
234 let signature = sign(&digest, private_key)?;
235
236 self.signatures.get_or_insert_with(Vec::new).push(signature.to_vec());
237
238 Ok(())
239 }
240
241 /// Signs the transaction with multiple private keys.
242 ///
243 /// This method iteratively signs the transaction with each provided
244 /// private key, enabling multi-signature transactions.
245 ///
246 /// # Arguments
247 /// * `private_keys` - Slice of 32-byte private keys
248 ///
249 /// # Returns
250 /// Result indicating success or a secp256k1 error
251 ///
252 /// # Errors
253 /// Returns an error if any private key is invalid or signing fails
254 pub fn multi_sign(&mut self, private_keys: &[&[u8; 32]]) -> Result<(), secp256k1::Error> {
255 let public_keys = get_public_keys(private_keys)?;
256
257 self.signers.get_or_insert_with(Vec::new).extend(public_keys.iter().map(|pk| pk.to_vec()));
258
259 let digest = self.tx_rid().map_err(|_| secp256k1::Error::InvalidMessage)?;
260
261 for private_key in private_keys {
262 let signature = sign(&digest, private_key)?;
263 self.signatures.get_or_insert_with(Vec::new).push(signature.to_vec());
264 }
265
266 Ok(())
267 }
268
269}
270
271/// Signs a message digest using ECDSA with secp256k1.
272///
273/// # Arguments
274/// * `digest` - 32-byte message digest to sign
275/// * `private_key` - 32-byte private key
276///
277/// # Returns
278/// Result containing the 64-byte signature or a secp256k1 error
279///
280/// # Errors
281/// Returns an error if the private key is invalid or signing fails
282fn sign(digest: &[u8; 32], private_key: &[u8; 32]) -> Result<[u8; 64], secp256k1::Error> {
283 let secp = Secp256k1::new();
284 let secret_key = SecretKey::from_slice(private_key)?;
285 let message = Message::from_digest(*digest);
286 let signature: Signature = secp.sign_ecdsa(&message, &secret_key);
287 let serialized_signature = signature.serialize_compact();
288 Ok(serialized_signature)
289}
290
291/// Derives a public key from a private key using secp256k1.
292///
293/// # Arguments
294/// * `private_key` - 32-byte private key
295///
296/// # Returns
297/// Result containing the 33-byte compressed public key or a secp256k1 error
298///
299/// # Errors
300/// Returns an error if the private key is invalid
301fn get_public_key(private_key: &[u8; 32]) -> Result<[u8; 33], secp256k1::Error> {
302 let secp = Secp256k1::new();
303 let secret_key = SecretKey::from_slice(private_key)?;
304 let public_key = PublicKey::from_secret_key(&secp, &secret_key).serialize();
305 Ok(public_key)
306}
307
308/// Derives multiple public keys from a slice of private keys using secp256k1.
309///
310/// # Arguments
311/// * `private_keys` - Slice of 32-byte private keys
312///
313/// # Returns
314/// Result containing a vector of 33-byte compressed public keys or a secp256k1 error
315///
316/// # Errors
317/// Returns an error if any private key is invalid
318fn get_public_keys(private_keys: &[&[u8; 32]]) -> Result<Vec<[u8; 33]>, secp256k1::Error> {
319 let mut public_keys = Vec::new();
320
321 for private_key in private_keys {
322 let public_key = get_public_key(private_key)?;
323 public_keys.push(public_key);
324 }
325
326 Ok(public_keys)
327}