use anyhow::anyhow;
use crate::flow::entities::{transaction, Transaction};
use crate::keys::hex_to_bytes;
use derive_more::From;
use rlp::RlpStream;
use secp256k1::{Message, Secp256k1, SecretKey};
use sha3::{Digest, Sha3_256};
use crate::client::Error::DigestLenError;
#[derive(Debug, From)]
pub enum Error {
#[from]
KeysError(crate::keys::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
impl From<Error> for anyhow::Error {
fn from(value: Error) -> Self {
anyhow!(format!("{:?}", value))
}
}
pub(crate) const TRANSACTION_DOMAIN_TAG: &str =
"464c4f572d56302e302d7472616e73616374696f6e0000000000000000000000";
pub fn hash_transaction(tx: &Transaction) -> Result<Vec<u8>> {
let mut hasher = Sha3_256::new();
hasher.update(hex_to_bytes(TRANSACTION_DOMAIN_TAG)?);
let mut rlp: RlpStream = RlpStream::new_list(2);
rlp.begin_list(9);
rlp.append(&tx.script);
rlp.begin_list(tx.arguments.len());
for arg in &tx.arguments {
rlp.append(&arg.as_slice());
}
rlp.append(&tx.reference_block_id);
rlp.append(&tx.gas_limit);
if let Some(pk) = &tx.proposal_key {
rlp.append(&pk.address);
rlp.append(&pk.key_id);
rlp.append(&pk.sequence_number);
} else {
rlp.begin_list(0);
}
rlp.append(&tx.payer);
rlp.begin_list(tx.authorizers.len());
for auth in &tx.authorizers {
rlp.append(&auth.as_slice());
}
rlp.begin_list(tx.payload_signatures.len());
for sig in &tx.payload_signatures {
rlp.begin_list(3);
rlp.append(&sig.address);
rlp.append(&sig.key_id);
rlp.append(&sig.signature);
}
let encoded = rlp.out();
println!("RLP encoded: {}", hex::encode(encoded.clone()));
hasher.update(&encoded);
Ok(hasher.finalize().to_vec())
}
pub fn sign_transaction(
tx: &mut Transaction,
signer_address: &str,
key_index: u32,
private_key: &SecretKey,
) -> crate::client::Result<()> {
let hash = hash_transaction(tx)?;
let signer_address = hex_to_bytes(signer_address)?;
let secp = Secp256k1::new();
fn vec_to_array(vec: Vec<u8>) -> crate::client::Result<[u8; 32]> {
vec.try_into().map_err(DigestLenError)
}
let message = Message::from_digest(vec_to_array(hash.clone())?);
println!("Message to sign: {}", hex::encode(&message[..]));
let signature = secp.sign_ecdsa(&message, private_key);
let signature_bytes = signature.serialize_compact().to_vec();
let envelope_signature = transaction::Signature {
address: signer_address,
key_id: key_index,
signature: signature_bytes,
};
tx.envelope_signatures.push(envelope_signature);
Ok(())
}