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