partner_chains_cardano_offchain/
sign_tx.rs

1use crate::{cardano_keys::CardanoPaymentSigningKey, csl::transaction_from_bytes};
2use cardano_serialization_lib::{Ed25519Signature, Vkey, Vkeywitness};
3
4/// Signs CBOR encoded Cardano transaction `tx` with `payment_key`.
5pub fn sign_tx(tx: Vec<u8>, payment_key: &CardanoPaymentSigningKey) -> anyhow::Result<Vec<u8>> {
6	let transaction = transaction_from_bytes(tx)?;
7	let tx_hash: [u8; 32] =
8		sidechain_domain::crypto::blake2b(transaction.body().to_bytes().as_ref());
9	let signature = payment_key.sign(&tx_hash)?;
10
11	let vkey_witness = Vkeywitness::new(
12		&Vkey::new(&payment_key.to_csl_pub_key()),
13		&Ed25519Signature::from_bytes(signature)?,
14	);
15
16	// 0x82 is the tag for a 2-element list in CBOR
17	// This is done to keep compatibility with cardano-cli signing commands
18	let mut result = vec![0x82, 0x00];
19	result.extend(vkey_witness.to_bytes());
20
21	// Return the CBOR binary data
22	Ok(result)
23}
24
25#[cfg(test)]
26mod tests {
27	use super::sign_tx;
28	use crate::test_values::{
29		test_payment_key, test_payment_key2, test_transaction_bytes, test_witness_cbor,
30	};
31	use hex;
32
33	#[test]
34	fn test_sign_tx() {
35		// Get test payment key and transaction
36		let payment_key = test_payment_key();
37		let tx_bytes = test_transaction_bytes();
38		let expected_signature_hex = hex::encode(test_witness_cbor()).to_uppercase();
39
40		// Sign the transaction
41		let signature_bytes = sign_tx(tx_bytes, &payment_key).unwrap();
42
43		// Check that the signature matches the expected format
44		assert_eq!(signature_bytes[0], 0x82); // CBOR 2-element array tag
45		assert_eq!(signature_bytes[1], 0x00); // First element is 0 for transaction witnesses
46
47		// Convert the signature to hex for comparison
48		let signature_hex = hex::encode(&signature_bytes).to_uppercase();
49
50		// Verify the signature contains the expected signature
51		assert_eq!(signature_hex, expected_signature_hex);
52	}
53
54	#[test]
55	fn test_sign_tx_with_wrong_key() {
56		let payment_key = test_payment_key2();
57		let tx_bytes = test_transaction_bytes();
58		let signature_bytes = sign_tx(tx_bytes, &payment_key).unwrap();
59		let unexpected_signature_hex = hex::encode(test_witness_cbor()).to_uppercase();
60
61		// Check that the signature matches the expected format
62		assert_eq!(signature_bytes[0], 0x82); // CBOR 2-element array tag
63		assert_eq!(signature_bytes[1], 0x00); // First element is 0 for transaction witnesses
64
65		let signature_hex = hex::encode(&signature_bytes).to_uppercase();
66
67		// Verify the signature contains the expected signature
68		assert_ne!(signature_hex, unexpected_signature_hex);
69	}
70}