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}
84
85impl<'a> Default for Transaction<'a> {
86 fn default() -> Self {
87 Self {
88 blockchain_rid: vec![],
89 operations: None,
90 signers: None,
91 signatures: None
92 }
93 }
94}
95
96impl<'a> Transaction<'a> {
97 /// Creates a new transaction with the specified parameters.
98 ///
99 /// # Arguments
100 /// * `blockchain_rid` - Unique identifier of the blockchain
101 /// * `operations` - Optional list of operations to be executed
102 /// * `signers` - Optional list of public keys of the signers
103 /// * `signatures` - Optional list of signatures
104 ///
105 /// # Returns
106 /// A new Transaction instance
107 pub fn new(blockchain_rid: Vec<u8>,
108 operations: Option<Vec<Operation<'a>>>,
109 signers: Option<Vec<Vec<u8>>>,
110 signatures: Option<Vec<Vec<u8>>>) -> Self {
111 Self {
112 blockchain_rid,
113 operations,
114 signers,
115 signatures
116 }
117 }
118
119 /// Returns the hex-encoded GTV (Generic Tree Value) representation of the transaction.
120 ///
121 /// This method encodes the transaction into GTV format and returns it as a
122 /// hexadecimal string.
123 ///
124 /// # Returns
125 /// Hex-encoded string of the GTV-encoded transaction
126 pub fn gvt_hex_encoded(&self) -> String {
127 let gtv_e = gtv::encode_tx(self);
128 let hex_encode = hex::encode(gtv_e);
129 hex_encode
130 }
131
132 /// Computes the unique identifier (RID) of this transaction.
133 ///
134 /// The transaction RID is computed by hashing the GTV representation
135 /// of the transaction using the GTX hash function.
136 ///
137 /// # Returns
138 /// A fixed-size 32 bytes containing the transaction RID
139 pub fn tx_rid(&self) -> Result<[u8; 32], hasher::HashError> {
140 let to_draw_gtx = gtv::to_draw_gtx(self);
141 gtv_hash(to_draw_gtx)
142 }
143
144 /// Returns the hex-encoded transaction RID.
145 ///
146 /// This is a convenience method that returns the transaction RID
147 /// as a hexadecimal string.
148 ///
149 /// # Returns
150 /// Hex-encoded string of the transaction RID
151 pub fn tx_rid_hex(&self) -> Result<String, hasher::HashError> {
152 Ok(hex::encode(self.tx_rid()?))
153 }
154
155 /// Signs the transaction using a raw private key string.
156 ///
157 /// # Arguments
158 /// * `private_key` - Private key as a string
159 ///
160 /// # Returns
161 /// Result indicating success or a secp256k1 error
162 ///
163 /// # Errors
164 /// Returns an error if the private key is invalid or signing fails
165 pub fn sign_from_raw_priv_key(&mut self, private_key: &str) -> Result<(), secp256k1::Error> {
166 let private_key_bytes = Vec::from_hex(private_key).map_err(|_| secp256k1::Error::InvalidSecretKey)?;
167 let private_key = private_key_bytes.try_into().map_err(|_| secp256k1::Error::InvalidSecretKey)?;
168 self.sign(&private_key)
169 }
170
171 /// Signs the transaction with multiple raw private key strings.
172 ///
173 /// This method iteratively signs the transaction with each provided
174 /// private key string, enabling multi-signature transactions.
175 ///
176 /// # Arguments
177 /// * `private_keys` - Slice of raw private key strings
178 ///
179 /// # Returns
180 /// Result indicating success or a secp256k1 error
181 ///
182 /// # Errors
183 /// Returns an error if any private key is invalid or signing fails
184 pub fn multi_sign_from_raw_priv_keys(&mut self, private_keys: &[&str]) -> Result<(), secp256k1::Error> {
185 let private_keys_bytes: Vec<[u8; 32]> = private_keys
186 .iter()
187 .map(|private_key_hex| {
188 let private_key_bytes = Vec::from_hex(private_key_hex).map_err(|_| secp256k1::Error::InvalidSecretKey)?;
189 private_key_bytes.try_into().map_err(|_| secp256k1::Error::InvalidSecretKey)
190 })
191 .collect::<Result<Vec<[u8; 32]>, secp256k1::Error>>()?;
192
193 let private_keys_refs: Vec<&[u8; 32]> = private_keys_bytes.iter().collect();
194
195 self.multi_sign(private_keys_refs.as_slice())
196 }
197
198 /// Signs the transaction using a private key.
199 ///
200 /// This method:
201 /// 1. Derives the public key from the private key
202 /// 2. Adds the public key to the signers list
203 /// 3. Signs the transaction RID
204 /// 4. Adds the signature to the signatures list
205 ///
206 /// # Arguments
207 /// * `private_key` - 32-byte private key
208 ///
209 /// # Returns
210 /// Result indicating success or a secp256k1 error
211 ///
212 /// # Errors
213 /// Returns an error if the private key is invalid or signing fails
214 pub fn sign(&mut self, private_key: &[u8; 32]) -> Result<(), secp256k1::Error> {
215 let public_key = get_public_key(private_key)?;
216
217 self.signers.get_or_insert_with(Vec::new).push(public_key.to_vec());
218
219 let digest = self.tx_rid().map_err(|_| secp256k1::Error::InvalidMessage)?;
220 let signature = sign(&digest, private_key)?;
221
222 self.signatures.get_or_insert_with(Vec::new).push(signature.to_vec());
223
224 Ok(())
225 }
226
227 /// Signs the transaction with multiple private keys.
228 ///
229 /// This method iteratively signs the transaction with each provided
230 /// private key, enabling multi-signature transactions.
231 ///
232 /// # Arguments
233 /// * `private_keys` - Slice of 32-byte private keys
234 ///
235 /// # Returns
236 /// Result indicating success or a secp256k1 error
237 ///
238 /// # Errors
239 /// Returns an error if any private key is invalid or signing fails
240 pub fn multi_sign(&mut self, private_keys: &[&[u8; 32]]) -> Result<(), secp256k1::Error> {
241 let public_keys = get_public_keys(private_keys)?;
242
243 self.signers.get_or_insert_with(Vec::new).extend(public_keys.iter().map(|pk| pk.to_vec()));
244
245 let digest = self.tx_rid().map_err(|_| secp256k1::Error::InvalidMessage)?;
246
247 for private_key in private_keys {
248 let signature = sign(&digest, private_key)?;
249 self.signatures.get_or_insert_with(Vec::new).push(signature.to_vec());
250 }
251
252 Ok(())
253 }
254}
255
256/// Signs a message digest using ECDSA with secp256k1.
257///
258/// # Arguments
259/// * `digest` - 32-byte message digest to sign
260/// * `private_key` - 32-byte private key
261///
262/// # Returns
263/// Result containing the 64-byte signature or a secp256k1 error
264///
265/// # Errors
266/// Returns an error if the private key is invalid or signing fails
267fn sign(digest: &[u8; 32], private_key: &[u8; 32]) -> Result<[u8; 64], secp256k1::Error> {
268 let secp = Secp256k1::new();
269 let secret_key = SecretKey::from_slice(private_key)?;
270 let message = Message::from_digest(*digest);
271 let signature: Signature = secp.sign_ecdsa(&message, &secret_key);
272 let serialized_signature = signature.serialize_compact();
273 Ok(serialized_signature)
274}
275
276/// Derives a public key from a private key using secp256k1.
277///
278/// # Arguments
279/// * `private_key` - 32-byte private key
280///
281/// # Returns
282/// Result containing the 33-byte compressed public key or a secp256k1 error
283///
284/// # Errors
285/// Returns an error if the private key is invalid
286fn get_public_key(private_key: &[u8; 32]) -> Result<[u8; 33], secp256k1::Error> {
287 let secp = Secp256k1::new();
288 let secret_key = SecretKey::from_slice(private_key)?;
289 let public_key = PublicKey::from_secret_key(&secp, &secret_key).serialize();
290 Ok(public_key)
291}
292
293/// Derives multiple public keys from a slice of private keys using secp256k1.
294///
295/// # Arguments
296/// * `private_keys` - Slice of 32-byte private keys
297///
298/// # Returns
299/// Result containing a vector of 33-byte compressed public keys or a secp256k1 error
300///
301/// # Errors
302/// Returns an error if any private key is invalid
303fn get_public_keys(private_keys: &[&[u8; 32]]) -> Result<Vec<[u8; 33]>, secp256k1::Error> {
304 let mut public_keys = Vec::new();
305
306 for private_key in private_keys {
307 let public_key = get_public_key(private_key)?;
308 public_keys.push(public_key);
309 }
310
311 Ok(public_keys)
312}