diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 51a366438b6..8b98c242070 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2069,7 +2069,6 @@ version = "0.1.0" dependencies = [ "bech32", "byteorder", - "hex", "tw_coin_entry", "tw_encoding", "tw_hash", diff --git a/rust/chains/tw_pactus/Cargo.toml b/rust/chains/tw_pactus/Cargo.toml index 3c7499cbd8d..105b0aa7679 100644 --- a/rust/chains/tw_pactus/Cargo.toml +++ b/rust/chains/tw_pactus/Cargo.toml @@ -15,4 +15,4 @@ tw_encoding = { path = "../../tw_encoding" } [dev-dependencies] tw_encoding = { path = "../../tw_encoding" } -hex = "0.4.3" + diff --git a/rust/chains/tw_pactus/src/address.rs b/rust/chains/tw_pactus/src/address.rs index acad9ff671d..2e12bd8e7e9 100644 --- a/rust/chains/tw_pactus/src/address.rs +++ b/rust/chains/tw_pactus/src/address.rs @@ -45,7 +45,7 @@ impl TryFrom for AddressType { } impl Encodable for AddressType { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { (self.clone() as u8).encode(w) } @@ -120,16 +120,16 @@ impl fmt::Display for Address { } impl Encodable for Address { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { - let mut len = self.addr_type.encode(w)?; + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { + self.addr_type.encode(w)?; if self.is_treasury() { - return Ok(len); + return Ok(()); } - len += self.pub_hash.encode(w)?; + self.pub_hash.encode(w)?; - Ok(len) + Ok(()) } fn encoded_size(&self) -> usize { @@ -205,10 +205,9 @@ mod test { assert!(addr.is_treasury()); let mut w = Vec::new(); - let len = addr.encode(&mut w).unwrap(); + addr.encode(&mut w).unwrap(); assert_eq!(w.to_vec(), [0x00]); assert_eq!(addr.encoded_size(), 1); - assert_eq!(len, 1); } #[test] @@ -226,7 +225,7 @@ mod test { assert!(!addr.is_treasury()); let mut w = Vec::new(); - let len = addr.encode(&mut w).unwrap(); + addr.encode(&mut w).unwrap(); assert_eq!( w.to_vec(), "03000102030405060708090a0b0c0d0e0f00010203" @@ -234,7 +233,6 @@ mod test { .unwrap() ); assert_eq!(addr.encoded_size(), 21); - assert_eq!(len, 21); } #[test] @@ -245,7 +243,10 @@ mod test { let addr = deserialize::
(&data).unwrap(); assert!(!addr.is_treasury()); - assert_eq!(addr.to_string(), "pc1rqqqsyqcyq5rqwzqfpg9scrgwpuqqzqsr36kkra"); + assert_eq!( + addr.to_string(), + "pc1rqqqsyqcyq5rqwzqfpg9scrgwpuqqzqsr36kkra" + ); } #[test] diff --git a/rust/chains/tw_pactus/src/amount.rs b/rust/chains/tw_pactus/src/amount.rs index 06d12eb095f..84c9e156e6a 100644 --- a/rust/chains/tw_pactus/src/amount.rs +++ b/rust/chains/tw_pactus/src/amount.rs @@ -4,7 +4,7 @@ use crate::encoder::{error::Error, var_int::VarInt, Decodable, Encodable}; pub struct Amount(pub i64); impl Encodable for Amount { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { VarInt::from(self.0 as usize).encode(w) } diff --git a/rust/chains/tw_pactus/src/encoder/decode.rs b/rust/chains/tw_pactus/src/encoder/decode.rs index 8b1f564eb73..d87a4205d9a 100644 --- a/rust/chains/tw_pactus/src/encoder/decode.rs +++ b/rust/chains/tw_pactus/src/encoder/decode.rs @@ -18,7 +18,7 @@ pub(crate) fn decode_var_slice(r: &mut dyn std::io::Read) -> Result, Err } pub(crate) fn decode_fix_slice(r: &mut dyn std::io::Read, len: usize) -> Result, Error> { - let mut buf = vec![0; len as usize]; + let mut buf = vec![0; len]; r.read_exact(&mut buf)?; Ok(buf) diff --git a/rust/chains/tw_pactus/src/encoder/encode.rs b/rust/chains/tw_pactus/src/encoder/encode.rs index eebea7b6fd7..9840c1873a8 100644 --- a/rust/chains/tw_pactus/src/encoder/encode.rs +++ b/rust/chains/tw_pactus/src/encoder/encode.rs @@ -10,19 +10,21 @@ use super::error::Error; use crate::encoder::var_int::VarInt; use crate::encoder::Encodable; -pub(crate) fn encode_var_slice(data: &[u8], w: &mut dyn std::io::Write) -> Result { - let mut len = VarInt::from(data.len()).encode(w)?; - len += w.write(data)?; +pub(crate) fn encode_var_slice(data: &[u8], w: &mut dyn std::io::Write) -> Result<(), Error> { + VarInt::from(data.len()).encode(w)?; + w.write_all(data)?; - Ok(len) + Ok(()) } -pub(crate) fn encode_fix_slice(data: &[u8], w: &mut dyn std::io::Write) -> Result { - Ok(w.write(data)?) +pub(crate) fn encode_fix_slice(data: &[u8], w: &mut dyn std::io::Write) -> Result<(), Error> { + w.write_all(data)?; + + Ok(()) } impl Encodable for Vec { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { encode_var_slice(self, w) } @@ -32,7 +34,7 @@ impl Encodable for Vec { } impl Encodable for String { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { encode_var_slice(self.as_bytes(), w) } @@ -42,7 +44,7 @@ impl Encodable for String { } impl Encodable for PublicKey { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { encode_fix_slice(self.as_slice(), w) } @@ -52,7 +54,7 @@ impl Encodable for PublicKey { } impl Encodable for Signature { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { encode_fix_slice(self.to_bytes().as_slice(), w) } @@ -62,7 +64,7 @@ impl Encodable for Signature { } impl Encodable for Hash { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { encode_fix_slice(self.as_slice(), w) } @@ -73,10 +75,10 @@ impl Encodable for Hash { impl Encodable for u8 { #[inline] - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { w.write_u8(*self)?; - Ok(1) + Ok(()) } #[inline] @@ -89,10 +91,10 @@ macro_rules! impl_encodable_for_int { ($int:ty, $size:literal, $write_fn:tt) => { impl Encodable for $int { #[inline] - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { w.$write_fn::(*self)?; - Ok($size) + Ok(()) } #[inline] diff --git a/rust/chains/tw_pactus/src/encoder/error.rs b/rust/chains/tw_pactus/src/encoder/error.rs index a19fa9d58ad..b3643854a90 100644 --- a/rust/chains/tw_pactus/src/encoder/error.rs +++ b/rust/chains/tw_pactus/src/encoder/error.rs @@ -1,6 +1,5 @@ use tw_coin_entry::error::prelude::{SigningError, SigningErrorType}; - /// Errors encountered when encoding or decoding data. #[derive(Debug)] pub enum Error { @@ -14,7 +13,6 @@ impl From for Error { } } - impl From for SigningError { fn from(_: Error) -> Self { SigningError::new(SigningErrorType::Error_input_parse) diff --git a/rust/chains/tw_pactus/src/encoder/mod.rs b/rust/chains/tw_pactus/src/encoder/mod.rs index cb9da7c1984..506c3baec67 100644 --- a/rust/chains/tw_pactus/src/encoder/mod.rs +++ b/rust/chains/tw_pactus/src/encoder/mod.rs @@ -26,7 +26,7 @@ pub fn deserialize(data: &[u8]) -> Result { /// Trait for encoding an object into a consistent byte sequence. pub trait Encodable { /// Encode the object in consistent and deterministic way. - fn encode(&self, w: &mut dyn std::io::Write) -> Result; + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error>; /// Determine the size of serialized object. fn encoded_size(&self) -> usize; diff --git a/rust/chains/tw_pactus/src/encoder/var_int.rs b/rust/chains/tw_pactus/src/encoder/var_int.rs index 1029fafbb43..5660601d8c5 100644 --- a/rust/chains/tw_pactus/src/encoder/var_int.rs +++ b/rust/chains/tw_pactus/src/encoder/var_int.rs @@ -28,19 +28,18 @@ impl Deref for VarInt { } impl Encodable for VarInt { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), Error> { let mut val = self.0; - let mut len = 0; // Make sure that there is one after this while val >= 0x80 { let n = (val as u8 & 0x7f) | 0x80; - len += w.write(&[n])?; + w.write_all(&[n])?; val >>= 7; // It should be in multiples of 7, this should just get the next part } - len += w.write(&[val as u8])?; + w.write_all(&[val as u8])?; - Ok(len) + Ok(()) } fn encoded_size(&self) -> usize { @@ -94,7 +93,7 @@ impl Decodable for VarInt { mod tests { use super::*; - use crate::encoder::{self, deserialize}; + use crate::encoder::deserialize; #[test] fn test_var_int_encode_data() { @@ -160,19 +159,22 @@ mod tests { let mut w = Vec::new(); let var_int = VarInt(*value); let encoded_size = var_int.encoded_size(); - let len = var_int.encode(&mut w).unwrap(); + var_int.encode(&mut w).unwrap(); let out = w.as_slice(); assert_eq!(out, *encoded, "Test {i} failed: data mismatch"); - assert_eq!(len, out.len(), "Test {i} failed: encoded size mismatch"); - assert_eq!(len, encoded_size, "Test {i} failed: data size mismatch",); + assert_eq!( + encoded_size, + out.len(), + "Test {i} failed: encoded size mismatch" + ); } } #[test] fn test_var_int_decode() { for (i, (value, data)) in VARINT_TEST_CASES.iter().enumerate() { - let var_int = deserialize::(*data).unwrap(); + let var_int = deserialize::(data).unwrap(); assert_eq!(*value, *var_int, "Test {i} failed: value mismatch"); } diff --git a/rust/chains/tw_pactus/src/modules/transaction_util.rs b/rust/chains/tw_pactus/src/modules/transaction_util.rs index f6f628e685c..a28e72b8f35 100644 --- a/rust/chains/tw_pactus/src/modules/transaction_util.rs +++ b/rust/chains/tw_pactus/src/modules/transaction_util.rs @@ -7,6 +7,8 @@ use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::transaction_util::TransactionUtil; use tw_encoding::hex; +use crate::encoder::deserialize; +use crate::transaction::Transaction; pub struct PactusTransactionUtil; @@ -18,16 +20,10 @@ impl TransactionUtil for PactusTransactionUtil { impl PactusTransactionUtil { fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult { - let txn_bytes = hex::decode(encoded_tx).map_err(|_| SigningErrorType::Error_input_parse)?; + let trx_bytes = hex::decode(encoded_tx).map_err(|_| SigningErrorType::Error_input_parse)?; - todo!() - // // Deserialize the transaction - // let tx: Transaction = deserialize(&tx).map_err(|_| SigningErrorType::Error_input_parse)?; + let trx = deserialize::(&trx_bytes)?; - // // Calculate the transaction ID - // let txid = tx.txid(); - - // // Note: to_string() returns the reversed byte order, which is the RPC format - // Ok(txid.to_string()) + Ok(hex::encode(trx.id(), false)) } } diff --git a/rust/chains/tw_pactus/src/transaction.rs b/rust/chains/tw_pactus/src/transaction.rs index 33b72db5400..ec694a25540 100644 --- a/rust/chains/tw_pactus/src/transaction.rs +++ b/rust/chains/tw_pactus/src/transaction.rs @@ -1,5 +1,4 @@ use std::fmt::Debug; -use std::io::{Cursor, Write}; use std::str::FromStr; use tw_coin_entry::error::prelude::{SigningError, SigningErrorType, SigningResult}; @@ -11,10 +10,11 @@ use tw_proto::Pactus; use crate::address::Address; use crate::amount::Amount; -use crate::encoder::error::Error; -use crate::encoder::{decode, Decodable, Encodable}; +use crate::encoder::error::Error as EncoderError; +use crate::encoder::{deserialize, Decodable, Encodable}; const VERSION_LATEST: u8 = 1; +const FLAG_NOT_SIGNED: u8 = 0x02; #[derive(Debug, Clone)] pub enum PayloadType { @@ -26,7 +26,7 @@ pub enum PayloadType { } impl TryFrom for PayloadType { - type Error = Error; + type Error = EncoderError; fn try_from(value: u8) -> Result { match value { @@ -35,13 +35,13 @@ impl TryFrom for PayloadType { 3 => Ok(PayloadType::Sortition), 4 => Ok(PayloadType::Unbond), 5 => Ok(PayloadType::Withdraw), - _ => Err(Error::ParseFailed("Invalid PayloadType value")), + _ => Err(EncoderError::ParseFailed("Invalid PayloadType value")), } } } impl Encodable for PayloadType { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), EncoderError> { (self.clone() as u8).encode(w) } @@ -51,7 +51,7 @@ impl Encodable for PayloadType { } impl Decodable for PayloadType { - fn decode(r: &mut dyn std::io::Read) -> Result { + fn decode(r: &mut dyn std::io::Read) -> Result { PayloadType::try_from(u8::decode(r)?) } } @@ -70,12 +70,12 @@ pub struct TransferPayload { } impl Encodable for TransferPayload { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { - let mut len = self.sender.encode(w)?; - len += self.receiver.encode(w)?; - len += self.amount.encode(w)?; + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), EncoderError> { + self.sender.encode(w)?; + self.receiver.encode(w)?; + self.amount.encode(w)?; - Ok(len) + Ok(()) } fn encoded_size(&self) -> usize { @@ -84,8 +84,16 @@ impl Encodable for TransferPayload { } impl Decodable for TransferPayload { - fn decode(r: &mut dyn std::io::Read) -> Result { - todo!() + fn decode(r: &mut dyn std::io::Read) -> Result { + let sender = Address::decode(r)?; + let receiver = Address::decode(r)?; + let amount = Amount::decode(r)?; + + Ok(TransferPayload { + sender, + receiver, + amount, + }) } } @@ -103,21 +111,28 @@ impl Payload for TransferPayload { #[derive(Debug)] pub struct Transaction { - pub flags: u8, - pub version: u8, - pub lock_time: u32, - pub fee: Amount, - pub memo: String, - pub payload: Box, - pub signature: Option, - pub public_key: Option, + flags: u8, + version: u8, + lock_time: u32, + fee: Amount, + memo: String, + payload: Box, + signature: Option, + public_key: Option, } impl Transaction { - pub fn new(lock_time: u32, fee: Amount, memo: String, payload: Box) -> Self { + pub fn new( + flags: u8, + version: u8, + lock_time: u32, + fee: Amount, + memo: String, + payload: Box, + ) -> Self { Transaction { - flags: 0, - version: VERSION_LATEST, + flags, + version, lock_time, fee, memo, @@ -151,37 +166,22 @@ impl Transaction { } }; - Ok(Transaction { - flags: 0, - version: VERSION_LATEST, - lock_time: trx.lock_time, - fee: Amount(trx.fee), - memo: trx.memo.to_string(), + let flags = FLAG_NOT_SIGNED; + let version = VERSION_LATEST; + Ok(Transaction::new( + flags, + version, + trx.lock_time, + Amount(trx.fee), + trx.memo.to_string(), payload, - public_key: None, - signature: None, - }) + )) } } } pub fn from_bytes(input: &[u8]) -> SigningResult { - // let mut cursor = Cursor::new(input); - - // let version = u8::decode(&mut cursor)?; - // let lock_time = u32::decode(&mut cursor)?; - // let fee = Amount::decode(&mut cursor)?; - // let memo = String::decode(&mut cursor)?; - // let payload_type = PayloadType::decode(&mut cursor)?; - // let payload = match payload_type { - // PayloadType::Transfer => - // TransferPayload::decode(&mut cursor)?, - // _ => return SigningError::err(SigningErrorType::Error_not_supported) - // }; - - - - todo!() + Ok(deserialize::(input)?) } pub fn sign(&mut self, private_key: &PrivateKey) -> SigningResult<()> { @@ -194,6 +194,9 @@ impl Transaction { } pub fn set_signatory(&mut self, public_key: PublicKey, signature: Signature) { + // Unset "Not Signed" flag + self.flags &= !FLAG_NOT_SIGNED; + self.public_key = Some(public_key); self.signature = Some(signature); } @@ -203,34 +206,101 @@ impl Transaction { } pub fn to_bytes(&self) -> SigningResult> { + let mut w = Vec::with_capacity(self.encoded_size()); + + self.encode(&mut w)?; + + Ok(w.to_vec()) + } + + fn sign_bytes(&self) -> SigningResult> { let mut w = Vec::new(); + self.encode_with_no_signatory(&mut w)?; + let mut sign_bytes = w.to_vec(); + sign_bytes.remove(0); // Remove flags - self.flags.encode(&mut w)?; - w.write(&self.sign_bytes()?) - .map_err(|_| SigningError::new(SigningErrorType::Error_input_parse))?; + Ok(sign_bytes) + } + + fn encode_with_no_signatory(&self, w: &mut dyn std::io::Write) -> Result<(), EncoderError> { + self.flags.encode(w)?; + self.version.encode(w)?; + self.lock_time.encode(w)?; + self.fee.encode(w)?; + self.memo.encode(w)?; + self.payload.payload_type().encode(w)?; + self.payload.encode(w)?; + + Ok(()) + } + + fn is_signed(&self) -> bool { + self.flags & FLAG_NOT_SIGNED == 0 + } +} + +impl Encodable for Transaction { + fn encode(&self, w: &mut dyn std::io::Write) -> Result<(), EncoderError> { + self.encode_with_no_signatory(w)?; - if let Some(signature) = &self.signature { - signature.encode(&mut w)?; + if let Some(sig) = &self.signature { + sig.encode(w)?; } - if let Some(public_key) = &self.public_key { - public_key.encode(&mut w)?; + if let Some(pub_key) = &self.public_key { + pub_key.encode(w)?; } - Ok(w.to_vec()) + Ok(()) } - fn sign_bytes(&self) -> SigningResult> { - let mut w = Vec::new(); + fn encoded_size(&self) -> usize { + let mut len = self.flags.encoded_size() + + self.version.encoded_size() + + self.lock_time.encoded_size() + + self.payload.payload_type().encoded_size() + + self.fee.encoded_size() + + self.memo.encoded_size() + + self.payload.encoded_size(); + + if let Some(sig) = &self.signature { + len += sig.encoded_size(); + } - self.version.encode(&mut w)?; - self.lock_time.encode(&mut w)?; - self.fee.encode(&mut w)?; - self.memo.encode(&mut w)?; - self.payload.payload_type().encode(&mut w)?; - self.payload.encode(&mut w)?; + if let Some(pub_key) = &self.public_key { + len += pub_key.encoded_size(); + } - Ok(w.to_vec()) + len + } +} + +impl Decodable for Transaction { + fn decode(r: &mut dyn std::io::Read) -> Result { + let flags = u8::decode(r)?; + let version = u8::decode(r)?; + let lock_time = u32::decode(r)?; + let fee = Amount::decode(r)?; + let memo = String::decode(r)?; + let payload_type = PayloadType::decode(r)?; + let payload = match payload_type { + PayloadType::Transfer => Box::new(TransferPayload::decode(r)?), + _ => return Err(EncoderError::ParseFailed("Unsupported payload")), + }; + + let mut trx = Transaction::new(flags, version, lock_time, fee, memo, payload); + + if !trx.is_signed() { + return Ok(trx); + } + + let signature = Signature::decode(r)?; + let public_key = PublicKey::decode(r)?; + + trx.signature = Some(signature); + trx.public_key = Some(public_key); + + Ok(trx) } } @@ -251,33 +321,91 @@ mod tests { assert_eq!(stream.to_vec(), &[4]); } + const DATA_HEX_NOT_SIGNED: &str = concat!( + "02", // Flags + "01", // Version + "01020304", // LockTime + "01", // Fee + "0474657374", // Memo + "01", // PayloadType + "033333333333333333333333333333333333333333", // Sender + "032222222222222222222222222222222222222222", // Receiver + "02" + ); // Amount + + const DATA_HEX_SIGNED: &str = concat!( + "00", // Flags + "01", // Version + "01020304", // LockTime + "01", // Fee + "0474657374", // Memo + "01", // PayloadType + "033333333333333333333333333333333333333333", // Sender + "032222222222222222222222222222222222222222", // Receiver + "02", // Amount + "5bf1420418b7aec8f2a28e9c90f1c346ca8b28134e2cf983e6f541350da6774f", // Signature + "033aa2c42f12a5fa95be506c790e99731a9b5b9989e7b37bc192c642331d9c09", + "95794161374b22c696dabb98e93f6ca9300b22f3b904921fbf560bb72145f4fa" // PublicKey + ); + + const TX_ID: &str = "e5a0e1fb4ee6f26a867dd3c091fc9fdfcbd25a5caff8cf13a4485a716501150d"; + #[test] - fn test_encoding() { - let data_hex = "00".to_string()+ // Flags - "01"+ // Version - "01020304"+ // LockTime - "01"+ // Fee - "00" +// Memo - "01" +// PayloadType - "013333333333333333333333333333333333333333"+ // Sender - "012222222222222222222222222222222222222222"+ // Receiver - "01"; // Amount - - let expected_data = data_hex.as_str().decode_hex().unwrap(); - let expected_id = "1a8cedbb2ffce29df63210f112afb1c0295b27e2162323bfc774068f0573388e" - .decode_hex() - .unwrap(); + fn test_sign_signature() { + let expected_data = DATA_HEX_SIGNED.decode_hex().unwrap(); + let expected_id = TX_ID.decode_hex().unwrap(); - let sender = Address::from_str("pc1pxvenxvenxvenxvenxvenxvenxvenxven250tr7").unwrap(); - let receiver = Address::from_str("pc1pyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zc2ckkv").unwrap(); + let sender = Address::from_str("pc1rxvenxvenxvenxvenxvenxvenxvenxvenupgaeg").unwrap(); + let receiver = Address::from_str("pc1ryg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zwllqv6").unwrap(); let payload = Box::new(TransferPayload { sender, receiver, - amount: Amount(1), + amount: Amount(2), }); - let trx = Transaction::new(0x04030201, Amount(1), "".to_string(), payload); + let mut trx = Transaction::new( + FLAG_NOT_SIGNED, + VERSION_LATEST, + 0x04030201, + Amount(1), + "test".to_string(), + payload, + ); + + let private_key_data = "4e51f1f3721f644ac7a193be7f5e7b8c2abaa3467871daf4eacb5d3af080e5d6" + .decode_hex() + .unwrap(); + let private_key = PrivateKey::try_from(private_key_data.as_slice()).unwrap(); + trx.sign(&private_key).unwrap(); assert_eq!(expected_data, trx.to_bytes().unwrap()); assert_eq!(expected_id, trx.id()); } + + #[test] + fn test_encoding_not_signed() { + let data = DATA_HEX_NOT_SIGNED.decode_hex().unwrap(); + let trx = Transaction::from_bytes(&data).unwrap(); + let expected_id = TX_ID.decode_hex().unwrap(); + + let encoded_data = trx.to_bytes().unwrap(); + + assert_eq!(encoded_data, data); + assert_eq!(expected_id, trx.id()); + assert_eq!(trx.encoded_size(), data.len()); + assert!(!trx.is_signed()); + } + + #[test] + fn test_encoding_signed() { + let data = DATA_HEX_SIGNED.decode_hex().unwrap(); + let trx = Transaction::from_bytes(&data).unwrap(); + let expected_id = TX_ID.decode_hex().unwrap(); + + let encoded_data = trx.to_bytes().unwrap(); + + assert_eq!(encoded_data, data); + assert_eq!(expected_id, trx.id()); + assert_eq!(trx.encoded_size(), data.len()); + assert!(trx.is_signed()); + } }