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}