From e64bbc392a91a2addce018d1cdab0a2734813dce Mon Sep 17 00:00:00 2001 From: Mostafa Date: Tue, 8 Oct 2024 16:31:52 +0800 Subject: [PATCH] encode and decode for amount and address --- rust/chains/tw_pactus/src/address.rs | 129 +++++++++++++----- rust/chains/tw_pactus/src/amount.rs | 12 +- rust/chains/tw_pactus/src/compiler.rs | 4 +- rust/chains/tw_pactus/src/encode/stream.rs | 65 --------- .../src/{encode => encoder}/decode.rs | 53 ++++--- .../src/{encode => encoder}/encode.rs | 63 ++++----- .../src/{encode => encoder}/error.rs | 10 +- .../tw_pactus/src/{encode => encoder}/mod.rs | 17 +-- .../src/{encode => encoder}/var_int.rs | 8 +- rust/chains/tw_pactus/src/lib.rs | 2 +- rust/chains/tw_pactus/src/modules/mod.rs | 2 +- .../tw_pactus/src/modules/transaction_util.rs | 2 - rust/chains/tw_pactus/src/signer.rs | 2 +- rust/chains/tw_pactus/src/transaction.rs | 98 +++++++++---- .../tests/chains/pactus/pactus_address.rs | 49 ------- rust/tw_coin_registry/src/dispatcher.rs | 2 +- .../tests/chains/aptos/aptos_address.rs | 2 +- .../tests/chains/pactus/mod.rs | 0 .../tests/chains/pactus/pactus_address.rs | 96 +++++++++++++ .../tests/chains/pactus/pactus_compile.rs | 0 .../tests/chains/pactus/pactus_sign.rs | 0 21 files changed, 363 insertions(+), 253 deletions(-) delete mode 100644 rust/chains/tw_pactus/src/encode/stream.rs rename rust/chains/tw_pactus/src/{encode => encoder}/decode.rs (71%) rename rust/chains/tw_pactus/src/{encode => encoder}/encode.rs (64%) rename rust/chains/tw_pactus/src/{encode => encoder}/error.rs (56%) rename rust/chains/tw_pactus/src/{encode => encoder}/mod.rs (77%) rename rust/chains/tw_pactus/src/{encode => encoder}/var_int.rs (96%) delete mode 100644 rust/tw_any_coin/tests/chains/pactus/pactus_address.rs rename rust/{tw_any_coin => tw_tests}/tests/chains/pactus/mod.rs (100%) create mode 100644 rust/tw_tests/tests/chains/pactus/pactus_address.rs rename rust/{tw_any_coin => tw_tests}/tests/chains/pactus/pactus_compile.rs (100%) rename rust/{tw_any_coin => tw_tests}/tests/chains/pactus/pactus_sign.rs (100%) diff --git a/rust/chains/tw_pactus/src/address.rs b/rust/chains/tw_pactus/src/address.rs index 3f900778416..acad9ff671d 100644 --- a/rust/chains/tw_pactus/src/address.rs +++ b/rust/chains/tw_pactus/src/address.rs @@ -2,21 +2,24 @@ // // Copyright © 2017 Trust Wallet. +use std::fmt; use std::str::FromStr; -use std::{fmt, io}; use bech32::{FromBase32, ToBase32}; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::prelude::*; use tw_hash::blake2::blake2_b; use tw_hash::ripemd::ripemd_160; +use tw_hash::Hash; use tw_keypair::ed25519::sha512::PublicKey; use tw_memory::Data; -use crate::encode; +use crate::encoder::error::Error; +use crate::encoder::{Decodable, Encodable}; const HRP: &str = "pc"; const TREASURY_ADDRESS_STRING: &str = "000000000000000000000000000000000000000000"; +const PUB_HASH_LEN: usize = 20; /// Enum for Pactus address types. #[derive(Debug, Clone, PartialEq)] @@ -25,20 +28,39 @@ pub enum AddressType { Validator = 1, BlsAccount = 2, Ed25519Account = 3, - Invalid, } -impl From for AddressType { - fn from(value: u8) -> Self { +impl TryFrom for AddressType { + type Error = AddressError; + + fn try_from(value: u8) -> Result { match value { - 1 => AddressType::Validator, - 2 => AddressType::BlsAccount, - 3 => AddressType::Ed25519Account, - _ => AddressType::Invalid, + 0 => Ok(AddressType::Treasury), + 1 => Ok(AddressType::Validator), + 2 => Ok(AddressType::BlsAccount), + 3 => Ok(AddressType::Ed25519Account), + _ => Err(AddressError::Unsupported), } } } +impl Encodable for AddressType { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { + (self.clone() as u8).encode(w) + } + + fn encoded_size(&self) -> usize { + 1 + } +} + +impl Decodable for AddressType { + fn decode(r: &mut dyn std::io::Read) -> Result { + AddressType::try_from(u8::decode(r)?) + .map_err(|_| Error::ParseFailed("Invalid address type")) + } +} + /// Pactus addresses are 21 bytes long. /// The first byte indicates the address type, and the remaining 20 bytes /// represent the hash of the public key. @@ -46,23 +68,28 @@ impl From for AddressType { #[derive(Debug, Clone, PartialEq)] pub struct Address { addr_type: AddressType, - pub_hash: [u8; 20], + pub_hash: Hash, } impl Address { pub fn from_public_key(public_key: &PublicKey) -> Result { let pud_data = public_key.to_h256(); - let pub_hash = + let pub_hash_data = ripemd_160(&blake2_b(pud_data.as_ref(), 32).map_err(|_| AddressError::Internal)?); + let pub_hash = Address::vec_to_pub_hash(pub_hash_data)?; Ok(Address { addr_type: AddressType::Ed25519Account, - pub_hash: pub_hash.try_into().map_err(|_| AddressError::Internal)?, + pub_hash, }) } pub fn is_treasury(&self) -> bool { - self.addr_type == AddressType::Treasury && self.pub_hash == [0; 20] + self.addr_type == AddressType::Treasury && self.pub_hash.is_zero() + } + + pub fn vec_to_pub_hash(vec: Vec) -> Result, AddressError> { + Hash::::try_from(vec.as_slice()).map_err(|_| AddressError::Internal) } } @@ -92,15 +119,17 @@ impl fmt::Display for Address { } } -impl encode::Encodable for Address { - fn encode(&self, w: &mut W) -> Result { - if self.is_treasury() { - &0u8.encode(w); +impl Encodable for Address { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { + let mut len = self.addr_type.encode(w)?; - return Ok(1); + if self.is_treasury() { + return Ok(len); } - self.data().encode(w) + len += self.pub_hash.encode(w)?; + + Ok(len) } fn encoded_size(&self) -> usize { @@ -112,6 +141,24 @@ impl encode::Encodable for Address { } } +impl Decodable for Address { + fn decode(r: &mut dyn std::io::Read) -> Result { + let addr_type = AddressType::decode(r)?; + if addr_type == AddressType::Treasury { + return Ok(Address { + addr_type, + pub_hash: Hash::::new(), + }); + } + + let pub_hash = Hash::::decode(r)?; + Ok(Address { + addr_type, + pub_hash, + }) + } +} + impl FromStr for Address { type Err = AddressError; @@ -119,7 +166,7 @@ impl FromStr for Address { if s == TREASURY_ADDRESS_STRING { return Ok(Address { addr_type: AddressType::Treasury, - pub_hash: [0; 20], + pub_hash: Hash::::new(), }); } @@ -133,13 +180,9 @@ impl FromStr for Address { return Err(AddressError::InvalidInput); } - let addr_type = AddressType::from(b32[0].to_u8()); - if addr_type == AddressType::Invalid { - return Err(AddressError::InvalidInput); - } - + let addr_type = AddressType::try_from(b32[0].to_u8())?; let b8 = Vec::::from_base32(&b32[1..]).map_err(|_| AddressError::InvalidInput)?; - let pub_hash = b8.try_into().map_err(|_| AddressError::InvalidInput)?; + let pub_hash = Address::vec_to_pub_hash(b8)?; Ok(Address { addr_type, @@ -150,11 +193,11 @@ impl FromStr for Address { #[cfg(test)] mod test { - use encode::Encodable; use tw_encoding::hex::DecodeHex; use tw_keypair::ed25519::sha512::PrivateKey; use super::*; + use crate::encoder::{deserialize, Encodable}; #[test] fn test_treasury_address_encoding() { @@ -162,9 +205,19 @@ mod test { assert!(addr.is_treasury()); let mut w = Vec::new(); - addr.encode(&mut w); + let len = addr.encode(&mut w).unwrap(); assert_eq!(w.to_vec(), [0x00]); assert_eq!(addr.encoded_size(), 1); + assert_eq!(len, 1); + } + + #[test] + fn test_treasury_address_decoding() { + let data = vec![0u8]; + + let addr = deserialize::
(&data).unwrap(); + assert!(addr.is_treasury()); + assert_eq!(addr.to_string(), TREASURY_ADDRESS_STRING); } #[test] @@ -173,7 +226,7 @@ mod test { assert!(!addr.is_treasury()); let mut w = Vec::new(); - addr.encode(&mut w); + let len = addr.encode(&mut w).unwrap(); assert_eq!( w.to_vec(), "03000102030405060708090a0b0c0d0e0f00010203" @@ -181,6 +234,18 @@ mod test { .unwrap() ); assert_eq!(addr.encoded_size(), 21); + assert_eq!(len, 21); + } + + #[test] + fn test_address_decoding() { + let data = "03000102030405060708090a0b0c0d0e0f00010203" + .decode_hex() + .unwrap(); + + let addr = deserialize::
(&data).unwrap(); + assert!(!addr.is_treasury()); + assert_eq!(addr.to_string(), "pc1rqqqsyqcyq5rqwzqfpg9scrgwpuqqzqsr36kkra"); } #[test] @@ -221,10 +286,10 @@ mod test { ]; for case in test_cases { - let pub_hash = case.pub_hash.decode_hex().unwrap(); + let pub_hash_data = case.pub_hash.decode_hex().unwrap(); let addr = Address { addr_type: case.addr_type, - pub_hash: pub_hash.try_into().unwrap(), + pub_hash: Address::vec_to_pub_hash(pub_hash_data).unwrap(), }; let addr_str = addr.to_string(); @@ -244,7 +309,7 @@ mod test { let address = Address::from_public_key(&private_key.public()).unwrap(); let mut w = Vec::new(); - address.encode(&mut w); + address.encode(&mut w).unwrap(); assert_eq!(expected_data, w.to_vec(),); assert_eq!(expected_data.len(), address.encoded_size()); diff --git a/rust/chains/tw_pactus/src/amount.rs b/rust/chains/tw_pactus/src/amount.rs index f6bd7e3aaf3..06d12eb095f 100644 --- a/rust/chains/tw_pactus/src/amount.rs +++ b/rust/chains/tw_pactus/src/amount.rs @@ -1,12 +1,10 @@ -use std::io; - -use crate::encode::{var_int::VarInt, Encodable}; +use crate::encoder::{error::Error, var_int::VarInt, Decodable, Encodable}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Amount(pub i64); impl Encodable for Amount { - fn encode(&self, w: &mut W) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { VarInt::from(self.0 as usize).encode(w) } @@ -14,3 +12,9 @@ impl Encodable for Amount { VarInt::from(self.0 as usize).encoded_size() } } + +impl Decodable for Amount { + fn decode(r: &mut dyn std::io::Read) -> Result { + Ok(Amount(*VarInt::decode(r)? as i64)) + } +} diff --git a/rust/chains/tw_pactus/src/compiler.rs b/rust/chains/tw_pactus/src/compiler.rs index 4a74297f7b4..7d4cfbf72fb 100644 --- a/rust/chains/tw_pactus/src/compiler.rs +++ b/rust/chains/tw_pactus/src/compiler.rs @@ -31,7 +31,7 @@ impl PactusCompiler { input: Proto::SigningInput<'_>, ) -> SigningResult> { let trx = Transaction::from_proto(&input)?; - let data = trx.to_bytes(); + let data = trx.to_bytes()?; let output = CompilerProto::PreSigningOutput { data_hash: Cow::Owned(trx.id()), @@ -72,7 +72,7 @@ impl PactusCompiler { let mut trx = Transaction::from_proto(&input)?; trx.set_signatory(public_key.to_owned(), signature.to_owned()); - let data = trx.to_bytes(); + let data = trx.to_bytes()?; let output = Proto::SigningOutput { encoded: data.into(), diff --git a/rust/chains/tw_pactus/src/encode/stream.rs b/rust/chains/tw_pactus/src/encode/stream.rs deleted file mode 100644 index 92d16d7d1e6..00000000000 --- a/rust/chains/tw_pactus/src/encode/stream.rs +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use std::io; -use std::io::Write; - -use tw_memory::Data; - -use crate::encode::Encodable; - -/// Stream used for serialization of Bitcoin structures -#[derive(Default)] -pub struct Stream { - buffer: Data, -} - -impl Stream { - /// New stream - pub fn new() -> Self { - Stream { - buffer: Data::default(), - } - } - - /// New stream with capacity - pub fn with_capacity(capacity: usize) -> Self { - Stream { - buffer: Data::with_capacity(capacity), - } - } - - /// Serializes the struct and appends it to the end of stream. - pub fn append(&mut self, t: &T) -> &mut Self - where - T: Encodable, - { - t.encode(self); - self - } - - /// Appends raw bytes to the end of the stream. - pub fn append_raw_slice(&mut self, bytes: &[u8]) -> &mut Self { - // discard error for now, since we write to simple vector - self.buffer.write_all(bytes).unwrap(); - self - } - - /// Full stream. - pub fn out(self) -> Data { - self.buffer - } -} - -impl Write for Stream { - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - self.buffer.write(buf) - } - - #[inline] - fn flush(&mut self) -> Result<(), io::Error> { - self.buffer.flush() - } -} diff --git a/rust/chains/tw_pactus/src/encode/decode.rs b/rust/chains/tw_pactus/src/encoder/decode.rs similarity index 71% rename from rust/chains/tw_pactus/src/encode/decode.rs rename to rust/chains/tw_pactus/src/encoder/decode.rs index e5ca1207190..8b1f564eb73 100644 --- a/rust/chains/tw_pactus/src/encode/decode.rs +++ b/rust/chains/tw_pactus/src/encoder/decode.rs @@ -2,19 +2,24 @@ // // Copyright © 2017 Trust Wallet. -use std::io; - use tw_hash::Hash; use tw_keypair::ed25519::{sha512::PublicKey, Signature}; use super::error::Error; -use crate::encode::var_int::VarInt; -use crate::encode::Decodable; +use crate::encoder::var_int::VarInt; +use crate::encoder::Decodable; pub(crate) fn decode_var_slice(r: &mut dyn std::io::Read) -> Result, Error> { let len = *VarInt::decode(r)?; let mut buf = vec![0; len as usize]; - r.read(&mut buf)?; + r.read_exact(&mut buf)?; + + Ok(buf) +} + +pub(crate) fn decode_fix_slice(r: &mut dyn std::io::Read, len: usize) -> Result, Error> { + let mut buf = vec![0; len as usize]; + r.read_exact(&mut buf)?; Ok(buf) } @@ -34,7 +39,7 @@ impl Decodable for String { impl Decodable for PublicKey { fn decode(r: &mut dyn std::io::Read) -> Result { - let data = decode_var_slice(r)?; + let data = decode_fix_slice(r, PublicKey::LEN)?; PublicKey::try_from(data.as_slice()) .map_err(|_| self::Error::ParseFailed("Invalid Public Key")) } @@ -42,7 +47,7 @@ impl Decodable for PublicKey { impl Decodable for Signature { fn decode(r: &mut dyn std::io::Read) -> Result { - let data = decode_var_slice(r)?; + let data = decode_fix_slice(r, Signature::LEN)?; Signature::try_from(data.as_slice()) .map_err(|_| self::Error::ParseFailed("Invalid Signature")) } @@ -50,38 +55,39 @@ impl Decodable for Signature { impl Decodable for Hash { fn decode(r: &mut dyn std::io::Read) -> Result { - let data = decode_var_slice(r)?; + let data = decode_fix_slice(r, N)?; Hash::try_from(data.as_slice()).map_err(|_| self::Error::ParseFailed("Invalid Hash")) } } macro_rules! impl_decodable_for_int { - ($int:ty, $size:literal, $write_fn:tt) => { + ($int:ty, $size:literal) => { impl Decodable for $int { #[inline] fn decode(r: &mut dyn std::io::Read) -> Result { let mut buf = [0; $size]; - r.read(&mut buf[..])?; + r.read_exact(&mut buf[..])?; Ok(<$int>::from_le_bytes(buf)) } } }; } -impl_decodable_for_int!(u8, 1, write_u8); -impl_decodable_for_int!(i32, 4, write_i32); -impl_decodable_for_int!(i64, 8, write_i64); -impl_decodable_for_int!(u16, 2, write_u16); -impl_decodable_for_int!(u32, 4, write_u32); -impl_decodable_for_int!(u64, 8, write_u64); +impl_decodable_for_int!(u8, 1); +impl_decodable_for_int!(i32, 4); +impl_decodable_for_int!(i64, 8); +impl_decodable_for_int!(u16, 2); +impl_decodable_for_int!(u32, 4); +impl_decodable_for_int!(u64, 8); #[cfg(test)] mod tests { - use io::Cursor; + use std::io::Cursor; + use tw_encoding::hex::DecodeHex; use super::*; - use crate::encode::{decode, deserialize, serialize}; + use crate::encoder::deserialize; #[test] fn test_decode_var_slice() { @@ -92,6 +98,15 @@ mod tests { assert_eq!(expected, slice); } + #[test] + fn test_decode_fix_slice() { + let expected = vec![1, 2, 3, 4]; + let mut cursor = Cursor::new("01020304".decode_hex().unwrap()); + let slice = decode_fix_slice(&mut cursor, 4).unwrap(); + + assert_eq!(expected, slice); + } + #[test] fn test_encode_numbers() { let data = vec![1_u8, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0]; @@ -114,7 +129,7 @@ mod tests { #[test] fn test_encode_string() { let expected = "hello".to_string(); - let bytes = "0568656c6c6f".decode_hex().unwrap(); + let bytes = "0568656c6c6f056844656c6c6e".decode_hex().unwrap(); assert_eq!(expected, deserialize::(&bytes).unwrap()); } diff --git a/rust/chains/tw_pactus/src/encode/encode.rs b/rust/chains/tw_pactus/src/encoder/encode.rs similarity index 64% rename from rust/chains/tw_pactus/src/encode/encode.rs rename to rust/chains/tw_pactus/src/encoder/encode.rs index ebdafe22083..eebea7b6fd7 100644 --- a/rust/chains/tw_pactus/src/encode/encode.rs +++ b/rust/chains/tw_pactus/src/encoder/encode.rs @@ -2,85 +2,78 @@ // // Copyright © 2017 Trust Wallet. -use std::io; - use byteorder::{LittleEndian, WriteBytesExt}; use tw_hash::Hash; use tw_keypair::ed25519::{sha512::PublicKey, Signature}; -use crate::encode::var_int::VarInt; -use crate::encode::Encodable; +use super::error::Error; +use crate::encoder::var_int::VarInt; +use crate::encoder::Encodable; -pub(crate) fn encode_var_slice( - data: &[u8], - w: &mut W, -) -> Result { +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)?; Ok(len) } -pub(crate) fn encode_size_var_slice(data: &[u8]) -> usize { - let mut len = VarInt::from(data.len()).encoded_size(); - len += data.len(); - - len +pub(crate) fn encode_fix_slice(data: &[u8], w: &mut dyn std::io::Write) -> Result { + Ok(w.write(data)?) } impl Encodable for Vec { - fn encode(&self, w: &mut W) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { encode_var_slice(self, w) } fn encoded_size(&self) -> usize { - encode_size_var_slice(self) + VarInt::from(self.len()).encoded_size() + self.len() } } impl Encodable for String { - fn encode(&self, w: &mut W) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { encode_var_slice(self.as_bytes(), w) } fn encoded_size(&self) -> usize { - encode_size_var_slice(self.as_bytes()) + VarInt::from(self.len()).encoded_size() + self.len() } } impl Encodable for PublicKey { - fn encode(&self, w: &mut W) -> Result { - encode_var_slice(self.as_slice(), w) + fn encode(&self, w: &mut dyn std::io::Write) -> Result { + encode_fix_slice(self.as_slice(), w) } fn encoded_size(&self) -> usize { - encode_size_var_slice(self.as_slice()) + PublicKey::LEN } } impl Encodable for Signature { - fn encode(&self, w: &mut W) -> Result { - encode_var_slice(self.to_bytes().as_slice(), w) + fn encode(&self, w: &mut dyn std::io::Write) -> Result { + encode_fix_slice(self.to_bytes().as_slice(), w) } fn encoded_size(&self) -> usize { - encode_size_var_slice(self.to_bytes().as_slice()) + Signature::LEN } } impl Encodable for Hash { - fn encode(&self, w: &mut W) -> Result { - encode_var_slice(self.as_slice(), w) + fn encode(&self, w: &mut dyn std::io::Write) -> Result { + encode_fix_slice(self.as_slice(), w) } fn encoded_size(&self) -> usize { - encode_size_var_slice(self.as_slice()) + N } } impl Encodable for u8 { #[inline] - fn encode(&self, w: &mut W) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { w.write_u8(*self)?; Ok(1) @@ -96,7 +89,7 @@ macro_rules! impl_encodable_for_int { ($int:ty, $size:literal, $write_fn:tt) => { impl Encodable for $int { #[inline] - fn encode(&self, w: &mut W) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { w.$write_fn::(*self)?; Ok($size) @@ -121,19 +114,27 @@ mod tests { use tw_encoding::hex::DecodeHex; use super::*; - use crate::encode::serialize; - + use crate::encoder::serialize; #[test] fn test_encode_var_slice() { let expected = "0401020304".decode_hex().unwrap(); - let slice = vec![1,2,3,4]; + let slice = vec![1, 2, 3, 4]; let mut w = Vec::new(); encode_var_slice(&slice, &mut w).unwrap(); assert_eq!(expected, w.to_vec()); } + #[test] + fn test_encode_fix_slice() { + let expected = "01020304".decode_hex().unwrap(); + let slice = vec![1, 2, 3, 4]; + let mut w = Vec::new(); + encode_fix_slice(&slice, &mut w).unwrap(); + + assert_eq!(expected, w.to_vec()); + } #[test] fn test_encode_numbers() { diff --git a/rust/chains/tw_pactus/src/encode/error.rs b/rust/chains/tw_pactus/src/encoder/error.rs similarity index 56% rename from rust/chains/tw_pactus/src/encode/error.rs rename to rust/chains/tw_pactus/src/encoder/error.rs index 3de70cdf8fd..a19fa9d58ad 100644 --- a/rust/chains/tw_pactus/src/encode/error.rs +++ b/rust/chains/tw_pactus/src/encoder/error.rs @@ -1,4 +1,5 @@ -use std::io; +use tw_coin_entry::error::prelude::{SigningError, SigningErrorType}; + /// Errors encountered when encoding or decoding data. #[derive(Debug)] @@ -12,3 +13,10 @@ impl From for Error { Error::IoError(err) } } + + +impl From for SigningError { + fn from(_: Error) -> Self { + SigningError::new(SigningErrorType::Error_input_parse) + } +} diff --git a/rust/chains/tw_pactus/src/encode/mod.rs b/rust/chains/tw_pactus/src/encoder/mod.rs similarity index 77% rename from rust/chains/tw_pactus/src/encode/mod.rs rename to rust/chains/tw_pactus/src/encoder/mod.rs index 3f4491fea50..cb9da7c1984 100644 --- a/rust/chains/tw_pactus/src/encode/mod.rs +++ b/rust/chains/tw_pactus/src/encoder/mod.rs @@ -2,28 +2,23 @@ // // Copyright © 2017 Trust Wallet. -use std::io::{self, Cursor}; +use std::io::Cursor; use error::Error; -use tw_memory::Data; -// use crate::encode::stream::Stream; - -pub mod var_int; -pub mod encode; pub mod decode; +pub mod encode; pub mod error; +pub mod var_int; -pub fn serialize(t: &T) -> Result -{ +pub fn serialize(t: &T) -> Result, Error> { let mut writer = Vec::with_capacity(t.encoded_size()); t.encode(&mut writer)?; Ok(writer.to_vec()) } -pub fn deserialize(data: &[u8]) -> Result -{ +pub fn deserialize(data: &[u8]) -> Result { let mut cursor = Cursor::new(data); T::decode(&mut cursor) } @@ -31,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 W) -> Result; + fn encode(&self, w: &mut dyn std::io::Write) -> Result; /// Determine the size of serialized object. fn encoded_size(&self) -> usize; diff --git a/rust/chains/tw_pactus/src/encode/var_int.rs b/rust/chains/tw_pactus/src/encoder/var_int.rs similarity index 96% rename from rust/chains/tw_pactus/src/encode/var_int.rs rename to rust/chains/tw_pactus/src/encoder/var_int.rs index d02fb140523..1029fafbb43 100644 --- a/rust/chains/tw_pactus/src/encode/var_int.rs +++ b/rust/chains/tw_pactus/src/encoder/var_int.rs @@ -2,12 +2,12 @@ // // Copyright © 2017 Trust Wallet. -use std::{io, ops::Deref}; +use std::ops::Deref; use byteorder::ReadBytesExt; use super::{error::Error, Decodable}; -use crate::encode::Encodable; +use crate::encoder::Encodable; /// A type of variable-length integer commonly used in the Bitcoin P2P protocol and Bitcoin serialized data structures. #[derive(Default, Debug, Clone, Copy, PartialEq)] @@ -28,7 +28,7 @@ impl Deref for VarInt { } impl Encodable for VarInt { - fn encode(&self, w: &mut W) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { let mut val = self.0; let mut len = 0; // Make sure that there is one after this @@ -94,7 +94,7 @@ impl Decodable for VarInt { mod tests { use super::*; - use crate::encode::{self, deserialize}; + use crate::encoder::{self, deserialize}; #[test] fn test_var_int_encode_data() { diff --git a/rust/chains/tw_pactus/src/lib.rs b/rust/chains/tw_pactus/src/lib.rs index 4a89098b148..e9f5573c2b0 100644 --- a/rust/chains/tw_pactus/src/lib.rs +++ b/rust/chains/tw_pactus/src/lib.rs @@ -5,7 +5,7 @@ pub mod address; pub mod amount; pub mod compiler; -pub mod encode; +pub mod encoder; pub mod entry; pub mod modules; pub mod signer; diff --git a/rust/chains/tw_pactus/src/modules/mod.rs b/rust/chains/tw_pactus/src/modules/mod.rs index 9fb41d7845f..c083bb0102e 100644 --- a/rust/chains/tw_pactus/src/modules/mod.rs +++ b/rust/chains/tw_pactus/src/modules/mod.rs @@ -2,4 +2,4 @@ // // Copyright © 2017 Trust Wallet. -pub mod transaction_util; \ No newline at end of file +pub mod transaction_util; diff --git a/rust/chains/tw_pactus/src/modules/transaction_util.rs b/rust/chains/tw_pactus/src/modules/transaction_util.rs index c6c65a48cc6..f6f628e685c 100644 --- a/rust/chains/tw_pactus/src/modules/transaction_util.rs +++ b/rust/chains/tw_pactus/src/modules/transaction_util.rs @@ -6,9 +6,7 @@ use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::transaction_util::TransactionUtil; use tw_encoding::hex; -use tw_hash::sha3::sha3_256; -use crate::transaction::Transaction; pub struct PactusTransactionUtil; diff --git a/rust/chains/tw_pactus/src/signer.rs b/rust/chains/tw_pactus/src/signer.rs index 816d2d07176..6b7dddefe5e 100644 --- a/rust/chains/tw_pactus/src/signer.rs +++ b/rust/chains/tw_pactus/src/signer.rs @@ -30,7 +30,7 @@ impl PactusSigner { let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; trx.sign(key_pair.private())?; - let data = trx.to_bytes(); + let data = trx.to_bytes()?; let output = Proto::SigningOutput { encoded: data.into(), diff --git a/rust/chains/tw_pactus/src/transaction.rs b/rust/chains/tw_pactus/src/transaction.rs index 13028220994..33b72db5400 100644 --- a/rust/chains/tw_pactus/src/transaction.rs +++ b/rust/chains/tw_pactus/src/transaction.rs @@ -1,5 +1,5 @@ use std::fmt::Debug; -use std::io; +use std::io::{Cursor, Write}; use std::str::FromStr; use tw_coin_entry::error::prelude::{SigningError, SigningErrorType, SigningResult}; @@ -11,7 +11,8 @@ use tw_proto::Pactus; use crate::address::Address; use crate::amount::Amount; -use crate::encode::Encodable; +use crate::encoder::error::Error; +use crate::encoder::{decode, Decodable, Encodable}; const VERSION_LATEST: u8 = 1; @@ -24,8 +25,23 @@ pub enum PayloadType { Withdraw = 5, } +impl TryFrom for PayloadType { + type Error = Error; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(PayloadType::Transfer), + 2 => Ok(PayloadType::Bond), + 3 => Ok(PayloadType::Sortition), + 4 => Ok(PayloadType::Unbond), + 5 => Ok(PayloadType::Withdraw), + _ => Err(Error::ParseFailed("Invalid PayloadType value")), + } + } +} + impl Encodable for PayloadType { - fn encode(&self, w: &mut W) -> Result { + fn encode(&self, w: &mut dyn std::io::Write) -> Result { (self.clone() as u8).encode(w) } @@ -34,9 +50,13 @@ impl Encodable for PayloadType { } } -pub trait Payload: Debug { - fn encode(&self, w: &mut dyn std::io::Write) -> Result; - fn encoded_size(&self) -> usize; +impl Decodable for PayloadType { + fn decode(r: &mut dyn std::io::Read) -> Result { + PayloadType::try_from(u8::decode(r)?) + } +} + +pub trait Payload: Debug + Encodable { fn signer(&self) -> &Address; fn value(&self) -> Amount; fn payload_type(&self) -> PayloadType; @@ -49,10 +69,8 @@ pub struct TransferPayload { amount: Amount, } - - -impl Payload for TransferPayload { - fn encode(&self, w: &mut dyn std::io::Write) -> Result { +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)?; @@ -63,7 +81,15 @@ impl Payload for TransferPayload { fn encoded_size(&self) -> usize { self.sender.encoded_size() + self.receiver.encoded_size() + self.amount.encoded_size() } +} + +impl Decodable for TransferPayload { + fn decode(r: &mut dyn std::io::Read) -> Result { + todo!() + } +} +impl Payload for TransferPayload { fn signer(&self) -> &Address { &self.sender } @@ -140,11 +166,26 @@ impl Transaction { } 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!() } pub fn sign(&mut self, private_key: &PrivateKey) -> SigningResult<()> { - let sign_bytes = self.sign_bytes(); + let sign_bytes = self.sign_bytes()?; let signature = private_key.sign(sign_bytes)?; self.set_signatory(private_key.public(), signature); @@ -158,37 +199,38 @@ impl Transaction { } pub fn id(&self) -> Vec { - blake2_b(&self.sign_bytes(), 32).unwrap_or_default() + blake2_b(&self.sign_bytes().unwrap_or_default(), 32).unwrap_or_default() } - pub fn to_bytes(&self) -> Vec { + pub fn to_bytes(&self) -> SigningResult> { let mut w = Vec::new(); - self.flags.encode(&mut w); - self.sign_bytes().encode(&mut w); + self.flags.encode(&mut w)?; + w.write(&self.sign_bytes()?) + .map_err(|_| SigningError::new(SigningErrorType::Error_input_parse))?; if let Some(signature) = &self.signature { - signature.encode(&mut w); + signature.encode(&mut w)?; } if let Some(public_key) = &self.public_key { - public_key.encode(&mut w); + public_key.encode(&mut w)?; } - w.to_vec() + Ok(w.to_vec()) } - fn sign_bytes(&self) -> Vec { + fn sign_bytes(&self) -> SigningResult> { let mut w = Vec::new(); - 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); + 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)?; - w.to_vec() + Ok(w.to_vec()) } } @@ -205,7 +247,7 @@ mod tests { let mut stream = Vec::new(); let payload = PayloadType::Unbond; - payload.encode(&mut stream); + payload.encode(&mut stream).unwrap(); assert_eq!(stream.to_vec(), &[4]); } @@ -235,7 +277,7 @@ mod tests { }); let trx = Transaction::new(0x04030201, Amount(1), "".to_string(), payload); - assert_eq!(expected_data, trx.to_bytes()); + assert_eq!(expected_data, trx.to_bytes().unwrap()); assert_eq!(expected_id, trx.id()); } } diff --git a/rust/tw_any_coin/tests/chains/pactus/pactus_address.rs b/rust/tw_any_coin/tests/chains/pactus/pactus_address.rs deleted file mode 100644 index 8747532316e..00000000000 --- a/rust/tw_any_coin/tests/chains/pactus/pactus_address.rs +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use tw_any_coin::test_utils::address_utils::{ - test_address_derive, test_address_get_data, test_address_invalid, test_address_normalization, - test_address_valid, -}; -use tw_coin_registry::coin_type::CoinType; - -#[test] -fn test_pactus_address_derive() { - test_address_derive(CoinType::Pactus, "2134ae97465505dfd5a1fd05a8a0f146209c601eb3f1b0363b4cfe4b47ba1ab4", "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl"); -} - -#[test] -fn test_pactus_address_normalization() { - test_address_normalization(CoinType::Pactus, "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl", "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl"); -} - - -#[test] -fn test_pactus_address_is_valid() { - test_address_valid(CoinType::Pactus, "000000000000000000000000000000000000000000"); - test_address_valid(CoinType::Pactus, "pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dcdzdfr"); - test_address_valid(CoinType::Pactus, "pc1zzqkzzu4vyddss052as6c37qrdcfptegquw826x"); - test_address_valid(CoinType::Pactus, "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl"); -} - -#[test] -fn test_pactus_address_invalid() { - test_address_invalid(CoinType::Pactus, ""); - test_address_invalid(CoinType::Pactus, "00"); - test_address_invalid(CoinType::Pactus, "not_proper_encoded"); - test_address_invalid(CoinType::Pactus, "pc1ioiooi"); - test_address_invalid(CoinType::Pactus, "pc19p72rf"); - test_address_invalid(CoinType::Pactus, "qc1z0hrct7eflrpw4ccrttxzs4qud2axex4dh8zz75"); - test_address_invalid(CoinType::Pactus, "pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dg8xaf5"); // invalid checksum - test_address_invalid(CoinType::Pactus, "pc1p0hrct7eflrpw4ccrttxzs4qud2axexs2dhdk8"); // invalid length - test_address_invalid(CoinType::Pactus, "pc1y0hrct7eflrpw4ccrttxzs4qud2axex4dksmred"); // invalid type -} - -#[test] -fn test_pactus_address_get_data() { - test_address_get_data(CoinType::Pactus, "000000000000000000000000000000000000000000", "000000000000000000000000000000000000000000"); - test_address_get_data(CoinType::Pactus, "pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dcdzdfr", "017dc785fb29f8c2eae3035acc28541c6aba6c9aad"); - test_address_get_data(CoinType::Pactus, "pc1zzqkzzu4vyddss052as6c37qrdcfptegquw826x", "02102c2172ac235b083e8aec3588f8036e1215e500"); - test_address_get_data(CoinType::Pactus, "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl", "03f4acc4cf284bc69a73afa245c2d872bc8d818c88"); -} diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 078dc467612..2c37542fb7c 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -18,12 +18,12 @@ use tw_greenfield::entry::GreenfieldEntry; use tw_internet_computer::entry::InternetComputerEntry; use tw_native_evmos::entry::NativeEvmosEntry; use tw_native_injective::entry::NativeInjectiveEntry; +use tw_pactus::entry::PactusEntry; use tw_ronin::entry::RoninEntry; use tw_solana::entry::SolanaEntry; use tw_sui::entry::SuiEntry; use tw_thorchain::entry::ThorchainEntry; use tw_ton::entry::TheOpenNetworkEntry; -use tw_pactus::entry::PactusEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; diff --git a/rust/tw_tests/tests/chains/aptos/aptos_address.rs b/rust/tw_tests/tests/chains/aptos/aptos_address.rs index 393411046e8..97a52410960 100644 --- a/rust/tw_tests/tests/chains/aptos/aptos_address.rs +++ b/rust/tw_tests/tests/chains/aptos/aptos_address.rs @@ -12,7 +12,7 @@ fn test_aptos_address_normalization() { test_address_normalization( CoinType::Aptos, "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", ); } diff --git a/rust/tw_any_coin/tests/chains/pactus/mod.rs b/rust/tw_tests/tests/chains/pactus/mod.rs similarity index 100% rename from rust/tw_any_coin/tests/chains/pactus/mod.rs rename to rust/tw_tests/tests/chains/pactus/mod.rs diff --git a/rust/tw_tests/tests/chains/pactus/pactus_address.rs b/rust/tw_tests/tests/chains/pactus/pactus_address.rs new file mode 100644 index 00000000000..9d64d1590a7 --- /dev/null +++ b/rust/tw_tests/tests/chains/pactus/pactus_address.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::address_utils::{ + test_address_derive, test_address_get_data, test_address_invalid, test_address_normalization, + test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_pactus_address_derive() { + test_address_derive( + CoinType::Pactus, + "2134ae97465505dfd5a1fd05a8a0f146209c601eb3f1b0363b4cfe4b47ba1ab4", + "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl", + ); +} + +#[test] +fn test_pactus_address_normalization() { + test_address_normalization( + CoinType::Pactus, + "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl", + "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl", + ); +} + +#[test] +fn test_pactus_address_is_valid() { + test_address_valid( + CoinType::Pactus, + "000000000000000000000000000000000000000000", + ); + test_address_valid( + CoinType::Pactus, + "pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dcdzdfr", + ); + test_address_valid( + CoinType::Pactus, + "pc1zzqkzzu4vyddss052as6c37qrdcfptegquw826x", + ); + test_address_valid( + CoinType::Pactus, + "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl", + ); +} + +#[test] +fn test_pactus_address_invalid() { + test_address_invalid(CoinType::Pactus, ""); + test_address_invalid(CoinType::Pactus, "00"); + test_address_invalid(CoinType::Pactus, "not_proper_encoded"); + test_address_invalid(CoinType::Pactus, "pc1ioiooi"); + test_address_invalid(CoinType::Pactus, "pc19p72rf"); + test_address_invalid( + CoinType::Pactus, + "qc1z0hrct7eflrpw4ccrttxzs4qud2axex4dh8zz75", + ); + test_address_invalid( + CoinType::Pactus, + "pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dg8xaf5", + ); // invalid checksum + test_address_invalid( + CoinType::Pactus, + "pc1p0hrct7eflrpw4ccrttxzs4qud2axexs2dhdk8", + ); // invalid length + test_address_invalid( + CoinType::Pactus, + "pc1y0hrct7eflrpw4ccrttxzs4qud2axex4dksmred", + ); // invalid type +} + +#[test] +fn test_pactus_address_get_data() { + test_address_get_data( + CoinType::Pactus, + "000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000", + ); + test_address_get_data( + CoinType::Pactus, + "pc1p0hrct7eflrpw4ccrttxzs4qud2axex4dcdzdfr", + "017dc785fb29f8c2eae3035acc28541c6aba6c9aad", + ); + test_address_get_data( + CoinType::Pactus, + "pc1zzqkzzu4vyddss052as6c37qrdcfptegquw826x", + "02102c2172ac235b083e8aec3588f8036e1215e500", + ); + test_address_get_data( + CoinType::Pactus, + "pc1r7jkvfnegf0rf5ua05fzu9krjhjxcrrygl3v4nl", + "03f4acc4cf284bc69a73afa245c2d872bc8d818c88", + ); +} diff --git a/rust/tw_any_coin/tests/chains/pactus/pactus_compile.rs b/rust/tw_tests/tests/chains/pactus/pactus_compile.rs similarity index 100% rename from rust/tw_any_coin/tests/chains/pactus/pactus_compile.rs rename to rust/tw_tests/tests/chains/pactus/pactus_compile.rs diff --git a/rust/tw_any_coin/tests/chains/pactus/pactus_sign.rs b/rust/tw_tests/tests/chains/pactus/pactus_sign.rs similarity index 100% rename from rust/tw_any_coin/tests/chains/pactus/pactus_sign.rs rename to rust/tw_tests/tests/chains/pactus/pactus_sign.rs