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}