diff --git a/.gitignore b/.gitignore index ea8c4bf7..81cf4658 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/.vscode diff --git a/Makefile b/Makefile index a7ebf306..e709287f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ export RUSTFLAGS=-Dwarnings -Dclippy::all -Dclippy::pedantic -CARGO_HACK_ARGS=--feature-powerset --exclude-features default --group-features base64,serde,arbitrary,hex +CARGO_HACK_ARGS=--feature-powerset --exclude-features default --group-features base64,serde,arbitrary,hex,sha2 CARGO_DOC_ARGS?=--open diff --git a/src/curr/account_conversions.rs b/src/curr/account_conversions.rs index 0bdfd6eb..7f83b63e 100644 --- a/src/curr/account_conversions.rs +++ b/src/curr/account_conversions.rs @@ -1,118 +1,145 @@ use super::{ - AccountId, Hash, MuxedAccount, MuxedAccountMed25519, PublicKey, ScAddress, ScVal, Uint256, + AccountId, Error, Hash, MuxedAccount, MuxedAccountMed25519, PublicKey, ScAddress, ScVal, + Uint256, }; -impl From for PublicKey { - fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { - PublicKey::PublicKeyTypeEd25519(k.0.into()) +#[cfg(feature = "alloc")] +mod strkey { + use super::*; + impl From for PublicKey { + fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { + PublicKey::PublicKeyTypeEd25519(k.0.into()) + } } -} -impl From<&stellar_strkey::ed25519::PublicKey> for PublicKey { - fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { - PublicKey::PublicKeyTypeEd25519(k.0.into()) + impl From<&stellar_strkey::ed25519::PublicKey> for PublicKey { + fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { + PublicKey::PublicKeyTypeEd25519(k.0.into()) + } } -} -impl From for AccountId { - fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { - AccountId(k.into()) + impl From for AccountId { + fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { + AccountId(k.into()) + } } -} -impl From<&stellar_strkey::ed25519::PublicKey> for AccountId { - fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { - AccountId(k.into()) + impl From<&stellar_strkey::ed25519::PublicKey> for AccountId { + fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { + AccountId(k.into()) + } } -} -impl From for ScAddress { - fn from(t: stellar_strkey::ed25519::PublicKey) -> Self { - ScAddress::Account(t.into()) + impl From for ScAddress { + fn from(t: stellar_strkey::ed25519::PublicKey) -> Self { + ScAddress::Account(t.into()) + } } -} -impl From<&stellar_strkey::ed25519::PublicKey> for ScAddress { - fn from(t: &stellar_strkey::ed25519::PublicKey) -> Self { - ScAddress::Account(t.into()) + impl From<&stellar_strkey::ed25519::PublicKey> for ScAddress { + fn from(t: &stellar_strkey::ed25519::PublicKey) -> Self { + ScAddress::Account(t.into()) + } } -} -impl From for ScVal { - fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { - ScVal::Address(k.into()) + impl From for ScVal { + fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { + ScVal::Address(k.into()) + } } -} -impl From<&stellar_strkey::ed25519::PublicKey> for ScVal { - fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { - ScVal::Address(k.into()) + impl From<&stellar_strkey::ed25519::PublicKey> for ScVal { + fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { + ScVal::Address(k.into()) + } } -} -impl From for PublicKey { - fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { - PublicKey::PublicKeyTypeEd25519(k.ed25519.into()) + impl From for PublicKey { + fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { + PublicKey::PublicKeyTypeEd25519(k.ed25519.into()) + } } -} -impl From<&stellar_strkey::ed25519::MuxedAccount> for PublicKey { - fn from( - stellar_strkey::ed25519::MuxedAccount{ ed25519, .. }: &stellar_strkey::ed25519::MuxedAccount, - ) -> Self { - PublicKey::PublicKeyTypeEd25519(ed25519.into()) + impl From<&stellar_strkey::ed25519::MuxedAccount> for PublicKey { + fn from( + stellar_strkey::ed25519::MuxedAccount{ ed25519, .. }: &stellar_strkey::ed25519::MuxedAccount, + ) -> Self { + PublicKey::PublicKeyTypeEd25519(ed25519.into()) + } } -} -impl From for AccountId { - fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { - AccountId(k.into()) + impl From for AccountId { + fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { + AccountId(k.into()) + } } -} -impl From<&stellar_strkey::ed25519::MuxedAccount> for AccountId { - fn from(k: &stellar_strkey::ed25519::MuxedAccount) -> Self { - AccountId(k.into()) + impl From<&stellar_strkey::ed25519::MuxedAccount> for AccountId { + fn from(k: &stellar_strkey::ed25519::MuxedAccount) -> Self { + AccountId(k.into()) + } } -} -impl From for ScAddress { - fn from(t: stellar_strkey::ed25519::MuxedAccount) -> Self { - ScAddress::Account(t.into()) + impl From for ScAddress { + fn from(t: stellar_strkey::ed25519::MuxedAccount) -> Self { + ScAddress::Account(t.into()) + } } -} -impl From<&stellar_strkey::ed25519::MuxedAccount> for ScAddress { - fn from(t: &stellar_strkey::ed25519::MuxedAccount) -> Self { - ScAddress::Account(t.into()) + impl From<&stellar_strkey::ed25519::MuxedAccount> for ScAddress { + fn from(t: &stellar_strkey::ed25519::MuxedAccount) -> Self { + ScAddress::Account(t.into()) + } } -} -impl From for ScVal { - fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { - ScVal::Address(k.into()) + impl From for ScVal { + fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { + ScVal::Address(k.into()) + } } -} -impl From<&stellar_strkey::ed25519::MuxedAccount> for ScVal { - fn from(k: &stellar_strkey::ed25519::MuxedAccount) -> Self { - ScVal::Address(k.into()) + impl From<&stellar_strkey::ed25519::MuxedAccount> for ScVal { + fn from(k: &stellar_strkey::ed25519::MuxedAccount) -> Self { + ScVal::Address(k.into()) + } } -} -impl TryFrom<&stellar_strkey::Strkey> for ScAddress { - type Error = super::Error; - fn try_from(strkey: &stellar_strkey::Strkey) -> Result { - match strkey { - stellar_strkey::Strkey::PublicKeyEd25519(k) => Ok(ScAddress::Account(k.into())), - stellar_strkey::Strkey::MuxedAccountEd25519(m) => Ok(ScAddress::Account(m.into())), - stellar_strkey::Strkey::Contract(k) => Ok(ScAddress::Contract(k.into())), - _ => Err(super::Error::Invalid), + impl TryFrom<&stellar_strkey::Strkey> for ScAddress { + type Error = super::Error; + fn try_from(strkey: &stellar_strkey::Strkey) -> Result { + match strkey { + stellar_strkey::Strkey::PublicKeyEd25519(k) => Ok(ScAddress::Account(k.into())), + stellar_strkey::Strkey::MuxedAccountEd25519(m) => Ok(ScAddress::Account(m.into())), + stellar_strkey::Strkey::Contract(k) => Ok(ScAddress::Contract(k.into())), + _ => Err(super::Error::Invalid), + } } } -} -impl From for stellar_strkey::ed25519::PublicKey { - fn from(k: PublicKey) -> Self { - (&k).into() + impl From for stellar_strkey::ed25519::PublicKey { + fn from(k: PublicKey) -> Self { + (&k).into() + } } -} -impl From<&PublicKey> for stellar_strkey::ed25519::PublicKey { - fn from(k: &PublicKey) -> Self { - match k { - PublicKey::PublicKeyTypeEd25519(k) => stellar_strkey::ed25519::PublicKey(k.into()), + impl From<&PublicKey> for stellar_strkey::ed25519::PublicKey { + fn from(k: &PublicKey) -> Self { + match k { + PublicKey::PublicKeyTypeEd25519(k) => stellar_strkey::ed25519::PublicKey(k.into()), + } + } + } + impl From for stellar_strkey::Strkey { + fn from(sc_address: ScAddress) -> Self { + match sc_address { + ScAddress::Account(account_id) => { + stellar_strkey::Strkey::PublicKeyEd25519(account_id.into()) + } + ScAddress::Contract(contract) => stellar_strkey::Strkey::Contract(contract.into()), + } + } + } + impl From<&ScAddress> for stellar_strkey::Strkey { + fn from(sc_address: &ScAddress) -> Self { + match sc_address { + ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => { + stellar_strkey::Strkey::PublicKeyEd25519(stellar_strkey::ed25519::PublicKey(*k)) + } + ScAddress::Contract(Hash(h)) => { + stellar_strkey::Strkey::Contract(stellar_strkey::Contract(*h)) + } + } } } } @@ -253,29 +280,6 @@ impl From<&MuxedAccount> for MuxedAccount { } } -impl From for stellar_strkey::Strkey { - fn from(sc_address: ScAddress) -> Self { - match sc_address { - ScAddress::Account(account_id) => { - stellar_strkey::Strkey::PublicKeyEd25519(account_id.into()) - } - ScAddress::Contract(contract) => stellar_strkey::Strkey::Contract(contract.into()), - } - } -} -impl From<&ScAddress> for stellar_strkey::Strkey { - fn from(sc_address: &ScAddress) -> Self { - match sc_address { - ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => { - stellar_strkey::Strkey::PublicKeyEd25519(stellar_strkey::ed25519::PublicKey(*k)) - } - ScAddress::Contract(Hash(h)) => { - stellar_strkey::Strkey::Contract(stellar_strkey::Contract(*h)) - } - } - } -} - impl From<&ScAddress> for ScAddress { fn from(sc_address: &ScAddress) -> Self { match sc_address { diff --git a/src/curr/asset_conversions.rs b/src/curr/asset_conversions.rs index 05122196..21b86d73 100644 --- a/src/curr/asset_conversions.rs +++ b/src/curr/asset_conversions.rs @@ -2,23 +2,14 @@ use super::{ AlphaNum12, AlphaNum4, Asset, AssetCode12, AssetCode4, ChangeTrustAsset, ContractIdPreimage, }; #[cfg(feature = "sha2")] -use super::{Hash, HashIdPreimage, HashIdPreimageContractId, HashBytes}; - -pub trait IntoContractId { - type Error; - fn into_contract_id( - self, - network_passphrase: &str, - ) -> Result; -} +use super::{Hash, HashIdPreimage, HashIdPreimageContractId}; #[cfg(feature = "sha2")] -impl IntoContractId for Asset { - type Error = super::Error; - fn into_contract_id( +impl Asset { + pub fn into_contract_id( self, network_passphrase: &str, - ) -> Result { + ) -> Result { let network_id = Hash::hash_bytes(network_passphrase); HashIdPreimage::ContractId(HashIdPreimageContractId { network_id, diff --git a/src/curr/bytes_conversions.rs b/src/curr/bytes_conversions.rs index 8a0bacc8..30fec326 100644 --- a/src/curr/bytes_conversions.rs +++ b/src/curr/bytes_conversions.rs @@ -1,6 +1,7 @@ use super::{BytesM, ScVal}; + impl From for ScVal { fn from(value: BytesM) -> Self { ScVal::Bytes(value.into()) } -} \ No newline at end of file +} diff --git a/src/curr/contract_conversions.rs b/src/curr/contract_conversions.rs index c3315b74..86741c38 100644 --- a/src/curr/contract_conversions.rs +++ b/src/curr/contract_conversions.rs @@ -1,13 +1,71 @@ -use super::{Hash, ScAddress, ScVal}; +use super::{ + Hash, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, Operation, OperationBody, + ScAddress, ScVal, VecM, +}; -impl From for stellar_strkey::Contract { - fn from(v: Hash) -> Self { - stellar_strkey::Contract(v.0) +#[cfg(feature = "alloc")] +mod stellar_strkey_contract { + use super::super::Error; + use super::*; + impl From for stellar_strkey::Contract { + fn from(v: Hash) -> Self { + stellar_strkey::Contract(v.0) + } } -} -impl From<&Hash> for stellar_strkey::Contract { - fn from(v: &Hash) -> Self { - stellar_strkey::Contract(v.into()) + impl From<&Hash> for stellar_strkey::Contract { + fn from(v: &Hash) -> Self { + stellar_strkey::Contract(v.into()) + } + } + + impl From for Hash { + fn from(v: stellar_strkey::Contract) -> Self { + Hash(v.0) + } + } + impl From<&stellar_strkey::Contract> for Hash { + fn from(stellar_strkey::Contract(bytes): &stellar_strkey::Contract) -> Self { + Hash(*bytes) + } + } + + impl From for ScAddress { + fn from(v: stellar_strkey::Contract) -> Self { + ScAddress::Contract(v.into()) + } + } + impl From<&stellar_strkey::Contract> for ScAddress { + fn from(v: &stellar_strkey::Contract) -> Self { + ScAddress::Contract(v.into()) + } + } + + impl TryFrom for stellar_strkey::Contract { + type Error = Error; + fn try_from(sc_address: ScAddress) -> Result { + match sc_address { + ScAddress::Contract(c) => Ok(c.into()), + _ => Err(Error::Invalid), + } + } + } + + impl From for ScVal { + fn from(contract: stellar_strkey::Contract) -> Self { + ScVal::Address(contract.into()) + } + } + + impl From<&stellar_strkey::Contract> for ScVal { + fn from(contract: &stellar_strkey::Contract) -> Self { + ScVal::Address(contract.into()) + } + } + impl TryFrom> for HostFunction { + type Error = Error; + fn try_from(bytes: Vec) -> Result { + bytes.as_slice().try_into() + } } } @@ -17,7 +75,6 @@ impl From<&Hash> for [u8; 32] { } } - impl From for ScAddress { fn from(hash: Hash) -> Self { ScAddress::Contract(hash) @@ -36,49 +93,39 @@ impl From<&Hash> for Hash { } } - -impl From for Hash { - fn from(v: stellar_strkey::Contract) -> Self { - Hash(v.0) - } -} -impl From<&stellar_strkey::Contract> for Hash { - fn from(stellar_strkey::Contract(bytes): &stellar_strkey::Contract) -> Self { - Hash(*bytes) +impl From for HostFunction { + fn from(parameters: InvokeContractArgs) -> Self { + HostFunction::InvokeContract(parameters) } } -impl From for ScAddress { - fn from(v: stellar_strkey::Contract) -> Self { - ScAddress::Contract(v.into()) - } -} -impl From<&stellar_strkey::Contract> for ScAddress { - fn from(v: &stellar_strkey::Contract) -> Self { - ScAddress::Contract(v.into()) - } -} - -impl TryFrom for stellar_strkey::Contract { - type Error = super::Error; - fn try_from(sc_address: ScAddress) -> Result { - match sc_address { - ScAddress::Contract(c) => Ok(c.into()), - _ => Err(super::Error::Invalid), +impl From for Operation { + fn from(parameters: InvokeContractArgs) -> Self { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: parameters.into(), + auth: VecM::default(), + }), } } } - -impl From for ScVal { - fn from(contract: stellar_strkey::Contract) -> Self { - ScVal::Address(contract.into()) +impl From for Operation { + fn from(host_function: HostFunction) -> Self { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function, + auth: VecM::default(), + }), + } } } -impl From<&stellar_strkey::Contract> for ScVal { - fn from(contract: &stellar_strkey::Contract) -> Self { - ScVal::Address(contract.into()) +impl TryFrom<&[u8]> for HostFunction { + type Error = super::Error; + fn try_from(bytes: &[u8]) -> Result { + Ok(HostFunction::UploadContractWasm(bytes.try_into()?)) } } - diff --git a/src/curr/hash.rs b/src/curr/hash.rs index d40ed655..1bc3b840 100644 --- a/src/curr/hash.rs +++ b/src/curr/hash.rs @@ -1,67 +1,9 @@ -use super::Hash; #[cfg(feature = "sha2")] -use super::{HashIdPreimage, Limits, WriteXdr}; -#[cfg(feature = "sha2")] -use sha2::{Digest, Sha256}; - -#[cfg(feature = "sha2")] -pub trait HashBytes { - fn hash_bytes(bytes: impl AsRef<[u8]>) -> Self; -} -#[cfg(feature = "sha2")] -impl HashBytes for Hash { - fn hash_bytes(bytes: impl AsRef<[u8]>) -> Self { - Hash(Sha256::digest(bytes.as_ref()).into()) - } -} - -#[cfg(feature = "sha2")] -impl TryFrom for Hash { - type Error = super::Error; - fn try_from(value: HashIdPreimage) -> Result { - Ok(Hash::hash_bytes(&value.to_xdr(Limits::none())?)) - } -} - -#[cfg(feature = "sha2")] -impl TryFrom for stellar_strkey::Contract { - type Error = super::Error; - fn try_from(value: HashIdPreimage) -> Result { - let hash: Hash = match &value { - HashIdPreimage::ContractId(_) => value.try_into()?, - _ => return Err(super::Error::Invalid), - }; - Ok(hash.into()) - } -} +mod bytes; -pub trait FromHex { - type Error; - fn from_hex(s: &str) -> Result - where - Self: std::marker::Sized; -} - -impl FromHex for Hash { - type Error = hex::FromHexError; - - fn from_hex(s: &str) -> Result - where - Self: std::marker::Sized, - { - Ok(Hash(padded_hex_from_str(s)?)) - } -} -/// # Errors -/// -/// Might return an error -pub fn padded_hex_from_str(s: &str) -> Result<[u8; 32], hex::FromHexError> { - let n = 32; - if s.len() > n * 2 { - return Err(hex::FromHexError::InvalidStringLength); +#[cfg(feature = "hex")] +impl super::Hash { + pub fn from_hex(s: &str) -> Result { + Ok(super::Hash(create::hex::padded_hex_from_str(s)?)) } - let mut decoded = [0u8; 32]; - let padded = format!("{s:0>width$}", width = n * 2); - hex::decode_to_slice(padded, &mut decoded)?; - Ok(decoded) } diff --git a/src/curr/hash/bytes.rs b/src/curr/hash/bytes.rs new file mode 100644 index 00000000..a1531d09 --- /dev/null +++ b/src/curr/hash/bytes.rs @@ -0,0 +1,27 @@ +use super::super::{Hash, HashIdPreimage, Limits, WriteXdr}; + +use sha2::{Digest, Sha256}; + +impl Hash { + pub fn hash_bytes(bytes: impl AsRef<[u8]>) -> Self { + Hash(Sha256::digest(bytes.as_ref()).into()) + } +} + +impl TryFrom for Hash { + type Error = super::super::Error; + fn try_from(value: HashIdPreimage) -> Result { + Ok(Hash::hash_bytes(&value.to_xdr(Limits::none())?)) + } +} + +impl TryFrom for stellar_strkey::Contract { + type Error = super::super::Error; + fn try_from(value: HashIdPreimage) -> Result { + let hash: Hash = match &value { + HashIdPreimage::ContractId(_) => value.try_into()?, + _ => return Err(Self::Error::Invalid), + }; + Ok(hash.into()) + } +} diff --git a/src/curr/mod.rs b/src/curr/mod.rs index 1045e36b..e31237d7 100644 --- a/src/curr/mod.rs +++ b/src/curr/mod.rs @@ -4,7 +4,6 @@ pub use generated::*; mod hash; mod jsonschema; mod str; -pub use hash::*; mod bytes_conversions; mod contract_conversions; @@ -13,7 +12,6 @@ pub use scval_conversions::*; mod account_conversions; mod asset_conversions; mod num_conversions; -pub use asset_conversions::*; mod transaction_conversions; mod scval_validations; diff --git a/src/curr/num_conversions.rs b/src/curr/num_conversions.rs index 38bc8cfd..153e6673 100644 --- a/src/curr/num_conversions.rs +++ b/src/curr/num_conversions.rs @@ -1,83 +1,112 @@ +use core::str::FromStr; + use super::{Int128Parts, Int256Parts, UInt128Parts, UInt256Parts}; -impl TryFrom<(u128, u128)> for UInt256Parts { - type Error = super::Error; - fn try_from((hi, lo): (u128, u128)) -> Result { - let hi_bytes = hi.to_be_bytes(); - let (hi_hi, hi_lo) = hi_bytes.split_at(8); - let lo_bytes = lo.to_be_bytes(); - let (lo_hi, lo_lo) = lo_bytes.split_at(8); - Ok(UInt256Parts { - hi_hi: u64::from_be_bytes(hi_hi.try_into()?), - hi_lo: u64::from_be_bytes(hi_lo.try_into()?), - lo_hi: u64::from_be_bytes(lo_hi.try_into()?), - lo_lo: u64::from_be_bytes(lo_lo.try_into()?), - }) +impl From<(u128, u128)> for UInt256Parts { + fn from((hi, lo): (u128, u128)) -> Self { + let UInt128Parts { + hi: hi_hi, + lo: hi_lo, + } = hi.into(); + let UInt128Parts { + hi: lo_hi, + lo: lo_lo, + } = lo.into(); + UInt256Parts { + hi_hi, + hi_lo, + lo_hi, + lo_lo, + } } } -impl TryFrom<(i128, i128)> for Int256Parts { - type Error = super::Error; - fn try_from((hi, lo): (i128, i128)) -> Result { - let hi_bytes = hi.to_be_bytes(); - let (hi_hi, hi_lo) = hi_bytes.split_at(8); - let lo_bytes = lo.to_be_bytes(); - let (lo_hi, lo_lo) = lo_bytes.split_at(8); - Ok(Int256Parts { - hi_hi: i64::from_be_bytes(hi_hi.try_into()?), - hi_lo: u64::from_be_bytes(hi_lo.try_into()?), - lo_hi: u64::from_be_bytes(lo_hi.try_into()?), - lo_lo: u64::from_be_bytes(lo_lo.try_into()?), - }) +impl From<(i128, i128)> for Int256Parts { + fn from((hi, lo): (i128, i128)) -> Self { + let Int128Parts { + hi: hi_hi, + lo: hi_lo, + } = hi.into(); + let UInt128Parts { + hi: lo_hi, + lo: lo_lo, + } = (lo as u128).into(); + Int256Parts { + hi_hi, + hi_lo, + lo_hi, + lo_lo, + } } } -/* -/ Number parsing - (ScType::U128, Value::String(s)) => { - let val: u128 = u128::from_str(s) - .map(Into::into) - .map_err(|_| Error::InvalidValue(Some(t.clone())))?; - let bytes = val.to_be_bytes(); - let (hi, lo) = bytes.split_at(8); - ScVal::U128(UInt128Parts { - hi: u64::from_be_bytes(hi.try_into()?), - lo: u64::from_be_bytes(lo.try_into()?), - }) +impl From for UInt128Parts { + fn from(val: u128) -> Self { + let hi = (val >> 64) as u64; + let lo = val as u64; + let hi: [u8; 8] = hi.to_be_bytes(); + let lo: [u8; 8] = lo.to_be_bytes(); + Self { + hi: u64::from_be_bytes(hi), + lo: u64::from_be_bytes(lo), } + } +} - (ScType::I128, Value::String(s)) => { - let val: i128 = i128::from_str(s) - .map(Into::into) - .map_err(|_| Error::InvalidValue(Some(t.clone())))?; - let bytes = val.to_be_bytes(); - let (hi, lo) = bytes.split_at(8); - ScVal::I128(Int128Parts { - hi: i64::from_be_bytes(hi.try_into()?), - lo: u64::from_be_bytes(lo.try_into()?), - }) +impl From for u128 { + fn from(parts: UInt128Parts) -> Self { + let hi: [u8; 8] = parts.hi.to_be_bytes(); + let lo: [u8; 8] = parts.lo.to_be_bytes(); + let hi = u64::from_be_bytes(hi); + let lo = u64::from_be_bytes(lo); + (hi as u128) << 64 | lo as u128 + } +} + +impl From for Int128Parts { + fn from(val: i128) -> Self { + let hi = (val >> 64) as u64; + let lo = val as i64; + let hi: [u8; 8] = hi.to_be_bytes(); + let lo: [u8; 8] = lo.to_be_bytes(); + Int128Parts { + hi: i64::from_be_bytes(hi), + lo: u64::from_be_bytes(lo), } - */ + } +} - impl TryFrom for UInt128Parts { - type Error = super::Error; - fn try_from(val: u128) -> Result { - let bytes = val.to_be_bytes(); - let (hi, lo) = bytes.split_at(8); - Ok(UInt128Parts { - hi: u64::from_be_bytes(hi.try_into()?), - lo: u64::from_be_bytes(lo.try_into()?), - }) +impl From for i128 { + fn from(parts: Int128Parts) -> Self { + let hi: [u8; 8] = parts.hi.to_be_bytes(); + let lo: [u8; 8] = parts.lo.to_be_bytes(); + let hi = i64::from_be_bytes(hi); + let lo = u64::from_be_bytes(lo); + (hi as i128) << 64 | lo as i128 } } -impl TryFrom for Int128Parts { - type Error = super::Error; - fn try_from(val: i128) -> Result { - let bytes = val.to_be_bytes(); - let (hi, lo) = bytes.split_at(8); - Ok(Int128Parts { - hi: i64::from_be_bytes(hi.try_into()?), - lo: u64::from_be_bytes(lo.try_into()?), - }) + +impl FromStr for UInt128Parts { + type Err = super::Error; + fn from_str(s: &str) -> Result { + Ok(u128::from_str(s).map_err(|_| Self::Err::Invalid)?.into()) + } +} + +impl FromStr for Int128Parts { + type Err = super::Error; + fn from_str(s: &str) -> Result { + Ok(i128::from_str(s).map_err(|_| Self::Err::Invalid)?.into()) } -} \ No newline at end of file +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn round_trip_u128() { + let u128_val: u128 = 0x1234567890abcdef1234567890abcdefu128; + let xdr_val: UInt128Parts = u128_val.into(); + assert_eq!(xdr_val.into(), u128_val); + } +} diff --git a/src/curr/str.rs b/src/curr/str.rs index f4b01f98..9b4bfa85 100644 --- a/src/curr/str.rs +++ b/src/curr/str.rs @@ -14,7 +14,8 @@ //# - SignerKeyEd25519SignedPayload //# - NodeId //# -//# ## Asset Codes +//# ## Asset Types +//# - Asset //# - AssetCode //# - AssetCode4 //# - AssetCode12 @@ -400,4 +401,3 @@ impl core::fmt::Display for ClaimableBalanceId { Ok(()) } } - diff --git a/src/curr/transaction_conversions.rs b/src/curr/transaction_conversions.rs index a821a51f..11306549 100644 --- a/src/curr/transaction_conversions.rs +++ b/src/curr/transaction_conversions.rs @@ -1,7 +1,51 @@ use super::{ - FeeBumpTransaction, FeeBumpTransactionEnvelope, Transaction, TransactionEnvelope, - TransactionV1Envelope, VecM, + FeeBumpTransaction, FeeBumpTransactionEnvelope, Memo, MuxedAccount, Operation, Preconditions, + SequenceNumber, Transaction, TransactionEnvelope, TransactionExt, TransactionV1Envelope, VecM, }; +#[cfg(feature = "sha2")] +use super::{ + Hash, Limits, TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, + WriteXdr, +}; + +use super::Error; + +impl Transaction { + pub fn new_tx( + source_account: impl Into, + fee: u32, + seq_num: impl Into, + operation: Operation, + ) -> Transaction { + Transaction { + source_account: source_account.into(), + fee, + seq_num: seq_num.into(), + cond: Preconditions::None, + memo: Memo::None, + operations: [operation].try_into().unwrap(), + ext: TransactionExt::V0, + } + } + + pub fn add_operation(mut self, operation: Operation) -> Result { + let mut ops = self.operations.to_vec(); + ops.push(operation); + self.operations = ops.try_into().map_err(|_| Error::Invalid)?; + Ok(self) + } + + #[cfg(feature = "sha2")] + pub fn hash(&self, network_passphrase: &str) -> Result { + let signature_payload = TransactionSignaturePayload { + network_id: Hash::hash_bytes(network_passphrase), + tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(self.clone()), + }; + signature_payload + .to_xdr(Limits::none()) + .map(Hash::hash_bytes) + } +} impl From for TransactionEnvelope { fn from(tx: Transaction) -> Self { diff --git a/src/hex.rs b/src/hex.rs new file mode 100644 index 00000000..8af3d984 --- /dev/null +++ b/src/hex.rs @@ -0,0 +1,11 @@ +pub(crate) fn padded_hex_from_str(s: &str) -> Result<[u8; 32], hex::FromHexError> { + let n = 32; + if s.len() > n * 2 { + return Err(hex::FromHexError::InvalidStringLength); + } + let mut decoded = [0u8; 32]; + let mut padded = [b'0'; 64]; // 32 bytes * 2 chars per byte + padded[(64 - s.len())..].copy_from_slice(s.as_bytes()); + hex::decode_to_slice(padded, &mut decoded)?; + Ok(decoded) +} diff --git a/src/lib.rs b/src/lib.rs index 60583ea1..e7751320 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,3 +135,6 @@ pub mod next; #[cfg(feature = "cli")] pub mod cli; + +#[cfg(feature = "hex")] +pub mod hex; diff --git a/src/next/account_conversions.rs b/src/next/account_conversions.rs index 96663483..7f83b63e 100644 --- a/src/next/account_conversions.rs +++ b/src/next/account_conversions.rs @@ -1,45 +1,323 @@ -use super::{AccountId, MuxedAccount, PublicKey}; +use super::{ + AccountId, Error, Hash, MuxedAccount, MuxedAccountMed25519, PublicKey, ScAddress, ScVal, + Uint256, +}; -impl From for MuxedAccount { - fn from(account_id: AccountId) -> Self { - account_id.0.into() +#[cfg(feature = "alloc")] +mod strkey { + use super::*; + impl From for PublicKey { + fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { + PublicKey::PublicKeyTypeEd25519(k.0.into()) + } + } + impl From<&stellar_strkey::ed25519::PublicKey> for PublicKey { + fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { + PublicKey::PublicKeyTypeEd25519(k.0.into()) + } + } + + impl From for AccountId { + fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { + AccountId(k.into()) + } + } + impl From<&stellar_strkey::ed25519::PublicKey> for AccountId { + fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { + AccountId(k.into()) + } } -} -impl From for PublicKey { - fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { - PublicKey::PublicKeyTypeEd25519(k.0.into()) + impl From for ScAddress { + fn from(t: stellar_strkey::ed25519::PublicKey) -> Self { + ScAddress::Account(t.into()) + } + } + impl From<&stellar_strkey::ed25519::PublicKey> for ScAddress { + fn from(t: &stellar_strkey::ed25519::PublicKey) -> Self { + ScAddress::Account(t.into()) + } + } + + impl From for ScVal { + fn from(k: stellar_strkey::ed25519::PublicKey) -> Self { + ScVal::Address(k.into()) + } + } + impl From<&stellar_strkey::ed25519::PublicKey> for ScVal { + fn from(k: &stellar_strkey::ed25519::PublicKey) -> Self { + ScVal::Address(k.into()) + } + } + + impl From for PublicKey { + fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { + PublicKey::PublicKeyTypeEd25519(k.ed25519.into()) + } + } + impl From<&stellar_strkey::ed25519::MuxedAccount> for PublicKey { + fn from( + stellar_strkey::ed25519::MuxedAccount{ ed25519, .. }: &stellar_strkey::ed25519::MuxedAccount, + ) -> Self { + PublicKey::PublicKeyTypeEd25519(ed25519.into()) + } + } + + impl From for AccountId { + fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { + AccountId(k.into()) + } + } + impl From<&stellar_strkey::ed25519::MuxedAccount> for AccountId { + fn from(k: &stellar_strkey::ed25519::MuxedAccount) -> Self { + AccountId(k.into()) + } } -} -impl From for stellar_strkey::ed25519::PublicKey { - fn from(k: PublicKey) -> Self { - match k { - PublicKey::PublicKeyTypeEd25519(k) => stellar_strkey::ed25519::PublicKey(k.into()), + impl From for ScAddress { + fn from(t: stellar_strkey::ed25519::MuxedAccount) -> Self { + ScAddress::Account(t.into()) + } + } + impl From<&stellar_strkey::ed25519::MuxedAccount> for ScAddress { + fn from(t: &stellar_strkey::ed25519::MuxedAccount) -> Self { + ScAddress::Account(t.into()) + } + } + + impl From for ScVal { + fn from(k: stellar_strkey::ed25519::MuxedAccount) -> Self { + ScVal::Address(k.into()) + } + } + impl From<&stellar_strkey::ed25519::MuxedAccount> for ScVal { + fn from(k: &stellar_strkey::ed25519::MuxedAccount) -> Self { + ScVal::Address(k.into()) + } + } + + impl TryFrom<&stellar_strkey::Strkey> for ScAddress { + type Error = super::Error; + fn try_from(strkey: &stellar_strkey::Strkey) -> Result { + match strkey { + stellar_strkey::Strkey::PublicKeyEd25519(k) => Ok(ScAddress::Account(k.into())), + stellar_strkey::Strkey::MuxedAccountEd25519(m) => Ok(ScAddress::Account(m.into())), + stellar_strkey::Strkey::Contract(k) => Ok(ScAddress::Contract(k.into())), + _ => Err(super::Error::Invalid), + } + } + } + + impl From for stellar_strkey::ed25519::PublicKey { + fn from(k: PublicKey) -> Self { + (&k).into() + } + } + impl From<&PublicKey> for stellar_strkey::ed25519::PublicKey { + fn from(k: &PublicKey) -> Self { + match k { + PublicKey::PublicKeyTypeEd25519(k) => stellar_strkey::ed25519::PublicKey(k.into()), + } + } + } + impl From for stellar_strkey::Strkey { + fn from(sc_address: ScAddress) -> Self { + match sc_address { + ScAddress::Account(account_id) => { + stellar_strkey::Strkey::PublicKeyEd25519(account_id.into()) + } + ScAddress::Contract(contract) => stellar_strkey::Strkey::Contract(contract.into()), + } + } + } + impl From<&ScAddress> for stellar_strkey::Strkey { + fn from(sc_address: &ScAddress) -> Self { + match sc_address { + ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => { + stellar_strkey::Strkey::PublicKeyEd25519(stellar_strkey::ed25519::PublicKey(*k)) + } + ScAddress::Contract(Hash(h)) => { + stellar_strkey::Strkey::Contract(stellar_strkey::Contract(*h)) + } + } } } } impl From for MuxedAccount { fn from(public_key: PublicKey) -> Self { + (&public_key).into() + } +} +impl From<&PublicKey> for MuxedAccount { + fn from(public_key: &PublicKey) -> Self { match public_key { - PublicKey::PublicKeyTypeEd25519(k) => MuxedAccount::Ed25519(k), + PublicKey::PublicKeyTypeEd25519(k) => MuxedAccount::Ed25519(k.into()), } } } -impl MuxedAccount { - #[must_use] - pub fn account_id(self) -> AccountId { - match self { - MuxedAccount::Ed25519(k) => AccountId(PublicKey::PublicKeyTypeEd25519(k)), - MuxedAccount::MuxedEd25519(m) => AccountId(PublicKey::PublicKeyTypeEd25519(m.ed25519)), +impl From<&PublicKey> for PublicKey { + fn from(public_key: &PublicKey) -> Self { + match public_key { + PublicKey::PublicKeyTypeEd25519(k) => PublicKey::PublicKeyTypeEd25519(k.into()), } } } +impl From for ScAddress { + fn from(t: PublicKey) -> Self { + ScAddress::Account(t.into()) + } +} +impl From<&PublicKey> for ScAddress { + fn from(t: &PublicKey) -> Self { + ScAddress::Account(t.into()) + } +} + +// From for AccountId already exists +impl From<&PublicKey> for AccountId { + fn from(public_key: &PublicKey) -> Self { + AccountId(public_key.into()) + } +} + +impl From for ScAddress { + fn from(account_id: AccountId) -> Self { + ScAddress::Account(account_id) + } +} +impl From<&AccountId> for ScAddress { + fn from(AccountId(public_key): &AccountId) -> Self { + ScAddress::Account(public_key.into()) + } +} + +impl From<&PublicKey> for ScVal { + fn from(public_key: &PublicKey) -> Self { + ScVal::Address(public_key.into()) + } +} + +impl From for ScVal { + fn from(public_key: PublicKey) -> Self { + ScVal::Address(public_key.into()) + } +} + +impl From for MuxedAccount { + fn from(account_id: AccountId) -> Self { + account_id.0.into() + } +} +impl From<&AccountId> for MuxedAccount { + fn from(AccountId(public_key): &AccountId) -> Self { + public_key.into() + } +} + +impl From for stellar_strkey::ed25519::PublicKey { + fn from(value: AccountId) -> Self { + match value { + AccountId(key) => key.into(), + } + } +} +impl From<&AccountId> for stellar_strkey::ed25519::PublicKey { + fn from(value: &AccountId) -> Self { + match value { + AccountId(key) => key.into(), + } + } +} + +impl From<&AccountId> for AccountId { + fn from(AccountId(public_key): &AccountId) -> Self { + AccountId(public_key.into()) + } +} + +// MuxedAccount conversions impl From for AccountId { fn from(muxed_account: MuxedAccount) -> Self { - muxed_account.account_id() + (&muxed_account).into() + } +} +impl From<&MuxedAccount> for AccountId { + fn from(muxed_account: &MuxedAccount) -> Self { + match muxed_account { + MuxedAccount::Ed25519(k) => AccountId(PublicKey::PublicKeyTypeEd25519(k.into())), + MuxedAccount::MuxedEd25519(MuxedAccountMed25519 { ed25519, .. }) => { + AccountId(PublicKey::PublicKeyTypeEd25519(ed25519.into())) + } + } + } +} + +impl From for ScAddress { + fn from(t: MuxedAccount) -> Self { + ScAddress::Account(t.into()) + } +} +impl From<&MuxedAccount> for ScAddress { + fn from(t: &MuxedAccount) -> Self { + ScAddress::Account(t.into()) + } +} + +impl From<&MuxedAccount> for MuxedAccount { + fn from(value: &MuxedAccount) -> Self { + match value { + MuxedAccount::Ed25519(k) => MuxedAccount::Ed25519(k.into()), + MuxedAccount::MuxedEd25519(MuxedAccountMed25519 { ed25519, id }) => { + MuxedAccount::MuxedEd25519(MuxedAccountMed25519 { + ed25519: ed25519.into(), + id: *id, + }) + } + } + } +} + +impl From<&ScAddress> for ScAddress { + fn from(sc_address: &ScAddress) -> Self { + match sc_address { + ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => { + ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(*k)))) + } + ScAddress::Contract(Hash(h)) => ScAddress::Contract(Hash(*h)), + } + } +} + +impl From for ScVal { + fn from(sc_address: ScAddress) -> Self { + ScVal::Address(sc_address) + } +} + +impl From<&ScAddress> for ScVal { + fn from(sc_address: &ScAddress) -> Self { + let sc_address: ScAddress = sc_address.into(); + sc_address.into() + } +} + +impl From<&Uint256> for Uint256 { + fn from(Uint256(k): &Uint256) -> Self { + Uint256(*k) + } +} + +impl From<&[u8; 32]> for Uint256 { + fn from(k: &[u8; 32]) -> Self { + Uint256(*k) + } +} + +impl From<&Uint256> for [u8; 32] { + fn from(Uint256(k): &Uint256) -> Self { + *k } } diff --git a/src/next/asset_conversions.rs b/src/next/asset_conversions.rs new file mode 100644 index 00000000..21b86d73 --- /dev/null +++ b/src/next/asset_conversions.rs @@ -0,0 +1,76 @@ +use super::{ + AlphaNum12, AlphaNum4, Asset, AssetCode12, AssetCode4, ChangeTrustAsset, ContractIdPreimage, +}; +#[cfg(feature = "sha2")] +use super::{Hash, HashIdPreimage, HashIdPreimageContractId}; + +#[cfg(feature = "sha2")] +impl Asset { + pub fn into_contract_id( + self, + network_passphrase: &str, + ) -> Result { + let network_id = Hash::hash_bytes(network_passphrase); + HashIdPreimage::ContractId(HashIdPreimageContractId { + network_id, + contract_id_preimage: self.into(), + }) + .try_into() + } +} + +impl From for ContractIdPreimage { + fn from(value: Asset) -> Self { + ContractIdPreimage::Asset(value) + } +} + +impl From for ChangeTrustAsset { + fn from(asset: Asset) -> Self { + match asset { + Asset::CreditAlphanum4(asset) => ChangeTrustAsset::CreditAlphanum4(asset), + Asset::CreditAlphanum12(asset) => ChangeTrustAsset::CreditAlphanum12(asset), + Asset::Native => ChangeTrustAsset::Native, + } + } +} + +impl From<&Asset> for ChangeTrustAsset { + fn from(asset: &Asset) -> Self { + match asset { + Asset::CreditAlphanum4(asset) => ChangeTrustAsset::CreditAlphanum4(asset.into()), + Asset::CreditAlphanum12(asset) => ChangeTrustAsset::CreditAlphanum12(asset.into()), + Asset::Native => ChangeTrustAsset::Native, + } + } +} + +impl From<&AssetCode4> for AssetCode4 { + fn from(AssetCode4(inner): &AssetCode4) -> Self { + AssetCode4(*inner) + } +} + +impl From<&AssetCode12> for AssetCode12 { + fn from(AssetCode12(inner): &AssetCode12) -> Self { + AssetCode12(*inner) + } +} + +impl From<&AlphaNum4> for AlphaNum4 { + fn from(AlphaNum4 { asset_code, issuer }: &AlphaNum4) -> Self { + AlphaNum4 { + asset_code: asset_code.into(), + issuer: issuer.into(), + } + } +} + +impl From<&AlphaNum12> for AlphaNum12 { + fn from(AlphaNum12 { asset_code, issuer }: &AlphaNum12) -> Self { + AlphaNum12 { + asset_code: asset_code.into(), + issuer: issuer.into(), + } + } +} diff --git a/src/next/bytes_conversions.rs b/src/next/bytes_conversions.rs new file mode 100644 index 00000000..30fec326 --- /dev/null +++ b/src/next/bytes_conversions.rs @@ -0,0 +1,7 @@ +use super::{BytesM, ScVal}; + +impl From for ScVal { + fn from(value: BytesM) -> Self { + ScVal::Bytes(value.into()) + } +} diff --git a/src/next/contract_conversions.rs b/src/next/contract_conversions.rs index 6370492a..86741c38 100644 --- a/src/next/contract_conversions.rs +++ b/src/next/contract_conversions.rs @@ -1,29 +1,131 @@ -use super::{Hash, ScAddress}; +use super::{ + Hash, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, Operation, OperationBody, + ScAddress, ScVal, VecM, +}; -impl From for stellar_strkey::Contract { - fn from(v: Hash) -> Self { - stellar_strkey::Contract(v.0) +#[cfg(feature = "alloc")] +mod stellar_strkey_contract { + use super::super::Error; + use super::*; + impl From for stellar_strkey::Contract { + fn from(v: Hash) -> Self { + stellar_strkey::Contract(v.0) + } + } + impl From<&Hash> for stellar_strkey::Contract { + fn from(v: &Hash) -> Self { + stellar_strkey::Contract(v.into()) + } + } + + impl From for Hash { + fn from(v: stellar_strkey::Contract) -> Self { + Hash(v.0) + } + } + impl From<&stellar_strkey::Contract> for Hash { + fn from(stellar_strkey::Contract(bytes): &stellar_strkey::Contract) -> Self { + Hash(*bytes) + } + } + + impl From for ScAddress { + fn from(v: stellar_strkey::Contract) -> Self { + ScAddress::Contract(v.into()) + } + } + impl From<&stellar_strkey::Contract> for ScAddress { + fn from(v: &stellar_strkey::Contract) -> Self { + ScAddress::Contract(v.into()) + } + } + + impl TryFrom for stellar_strkey::Contract { + type Error = Error; + fn try_from(sc_address: ScAddress) -> Result { + match sc_address { + ScAddress::Contract(c) => Ok(c.into()), + _ => Err(Error::Invalid), + } + } + } + + impl From for ScVal { + fn from(contract: stellar_strkey::Contract) -> Self { + ScVal::Address(contract.into()) + } + } + + impl From<&stellar_strkey::Contract> for ScVal { + fn from(contract: &stellar_strkey::Contract) -> Self { + ScVal::Address(contract.into()) + } + } + impl TryFrom> for HostFunction { + type Error = Error; + fn try_from(bytes: Vec) -> Result { + bytes.as_slice().try_into() + } } } -impl From for Hash { - fn from(v: stellar_strkey::Contract) -> Self { - Hash(v.0) +impl From<&Hash> for [u8; 32] { + fn from(Hash(bytes): &Hash) -> Self { + *bytes } } -impl From for ScAddress { - fn from(v: stellar_strkey::Contract) -> Self { - ScAddress::Contract(v.into()) +impl From for ScAddress { + fn from(hash: Hash) -> Self { + ScAddress::Contract(hash) } } -impl TryFrom for stellar_strkey::Contract { - type Error = super::Error; - fn try_from(sc_address: ScAddress) -> Result { - match sc_address { - ScAddress::Contract(c) => Ok(c.into()), - _ => Err(super::Error::Invalid), +impl From<&Hash> for ScAddress { + fn from(hash: &Hash) -> Self { + ScAddress::Contract(hash.into()) + } +} + +impl From<&Hash> for Hash { + fn from(Hash(bytes): &Hash) -> Self { + Hash(*bytes) + } +} + +impl From for HostFunction { + fn from(parameters: InvokeContractArgs) -> Self { + HostFunction::InvokeContract(parameters) + } +} + +impl From for Operation { + fn from(parameters: InvokeContractArgs) -> Self { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: parameters.into(), + auth: VecM::default(), + }), + } + } +} + +impl From for Operation { + fn from(host_function: HostFunction) -> Self { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function, + auth: VecM::default(), + }), } } } + +impl TryFrom<&[u8]> for HostFunction { + type Error = super::Error; + fn try_from(bytes: &[u8]) -> Result { + Ok(HostFunction::UploadContractWasm(bytes.try_into()?)) + } +} diff --git a/src/next/hash.rs b/src/next/hash.rs new file mode 100644 index 00000000..1bc3b840 --- /dev/null +++ b/src/next/hash.rs @@ -0,0 +1,9 @@ +#[cfg(feature = "sha2")] +mod bytes; + +#[cfg(feature = "hex")] +impl super::Hash { + pub fn from_hex(s: &str) -> Result { + Ok(super::Hash(create::hex::padded_hex_from_str(s)?)) + } +} diff --git a/src/next/hash/bytes.rs b/src/next/hash/bytes.rs new file mode 100644 index 00000000..a1531d09 --- /dev/null +++ b/src/next/hash/bytes.rs @@ -0,0 +1,27 @@ +use super::super::{Hash, HashIdPreimage, Limits, WriteXdr}; + +use sha2::{Digest, Sha256}; + +impl Hash { + pub fn hash_bytes(bytes: impl AsRef<[u8]>) -> Self { + Hash(Sha256::digest(bytes.as_ref()).into()) + } +} + +impl TryFrom for Hash { + type Error = super::super::Error; + fn try_from(value: HashIdPreimage) -> Result { + Ok(Hash::hash_bytes(&value.to_xdr(Limits::none())?)) + } +} + +impl TryFrom for stellar_strkey::Contract { + type Error = super::super::Error; + fn try_from(value: HashIdPreimage) -> Result { + let hash: Hash = match &value { + HashIdPreimage::ContractId(_) => value.try_into()?, + _ => return Err(Self::Error::Invalid), + }; + Ok(hash.into()) + } +} diff --git a/src/next/mod.rs b/src/next/mod.rs index 06caa071..e31237d7 100644 --- a/src/next/mod.rs +++ b/src/next/mod.rs @@ -1,13 +1,17 @@ mod generated; pub use generated::*; +mod hash; mod jsonschema; mod str; +mod bytes_conversions; mod contract_conversions; mod scval_conversions; pub use scval_conversions::*; mod account_conversions; +mod asset_conversions; +mod num_conversions; mod transaction_conversions; mod scval_validations; diff --git a/src/next/num_conversions.rs b/src/next/num_conversions.rs new file mode 100644 index 00000000..153e6673 --- /dev/null +++ b/src/next/num_conversions.rs @@ -0,0 +1,112 @@ +use core::str::FromStr; + +use super::{Int128Parts, Int256Parts, UInt128Parts, UInt256Parts}; + +impl From<(u128, u128)> for UInt256Parts { + fn from((hi, lo): (u128, u128)) -> Self { + let UInt128Parts { + hi: hi_hi, + lo: hi_lo, + } = hi.into(); + let UInt128Parts { + hi: lo_hi, + lo: lo_lo, + } = lo.into(); + UInt256Parts { + hi_hi, + hi_lo, + lo_hi, + lo_lo, + } + } +} + +impl From<(i128, i128)> for Int256Parts { + fn from((hi, lo): (i128, i128)) -> Self { + let Int128Parts { + hi: hi_hi, + lo: hi_lo, + } = hi.into(); + let UInt128Parts { + hi: lo_hi, + lo: lo_lo, + } = (lo as u128).into(); + Int256Parts { + hi_hi, + hi_lo, + lo_hi, + lo_lo, + } + } +} + +impl From for UInt128Parts { + fn from(val: u128) -> Self { + let hi = (val >> 64) as u64; + let lo = val as u64; + let hi: [u8; 8] = hi.to_be_bytes(); + let lo: [u8; 8] = lo.to_be_bytes(); + Self { + hi: u64::from_be_bytes(hi), + lo: u64::from_be_bytes(lo), + } + } +} + +impl From for u128 { + fn from(parts: UInt128Parts) -> Self { + let hi: [u8; 8] = parts.hi.to_be_bytes(); + let lo: [u8; 8] = parts.lo.to_be_bytes(); + let hi = u64::from_be_bytes(hi); + let lo = u64::from_be_bytes(lo); + (hi as u128) << 64 | lo as u128 + } +} + +impl From for Int128Parts { + fn from(val: i128) -> Self { + let hi = (val >> 64) as u64; + let lo = val as i64; + let hi: [u8; 8] = hi.to_be_bytes(); + let lo: [u8; 8] = lo.to_be_bytes(); + Int128Parts { + hi: i64::from_be_bytes(hi), + lo: u64::from_be_bytes(lo), + } + } +} + +impl From for i128 { + fn from(parts: Int128Parts) -> Self { + let hi: [u8; 8] = parts.hi.to_be_bytes(); + let lo: [u8; 8] = parts.lo.to_be_bytes(); + let hi = i64::from_be_bytes(hi); + let lo = u64::from_be_bytes(lo); + (hi as i128) << 64 | lo as i128 + } +} + +impl FromStr for UInt128Parts { + type Err = super::Error; + fn from_str(s: &str) -> Result { + Ok(u128::from_str(s).map_err(|_| Self::Err::Invalid)?.into()) + } +} + +impl FromStr for Int128Parts { + type Err = super::Error; + fn from_str(s: &str) -> Result { + Ok(i128::from_str(s).map_err(|_| Self::Err::Invalid)?.into()) + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn round_trip_u128() { + let u128_val: u128 = 0x1234567890abcdef1234567890abcdefu128; + let xdr_val: UInt128Parts = u128_val.into(); + assert_eq!(xdr_val.into(), u128_val); + } +} diff --git a/src/next/scval_conversions.rs b/src/next/scval_conversions.rs index dd64b316..32a27474 100644 --- a/src/next/scval_conversions.rs +++ b/src/next/scval_conversions.rs @@ -1,5 +1,6 @@ use super::{ - Int128Parts, ScBytes, ScError, ScMap, ScMapEntry, ScSymbol, ScVal, ScVec, UInt128Parts, + Int128Parts, ScBytes, ScError, ScMap, ScMapEntry, ScString, ScSymbol, ScVal, ScVec, + UInt128Parts, }; #[cfg(all(not(feature = "std"), feature = "alloc"))] @@ -301,80 +302,83 @@ impl TryFrom for ScSymbol { } #[cfg(feature = "alloc")] -impl TryFrom for ScVal { +impl TryFrom for ScSymbol { type Error = (); - fn try_from(v: String) -> Result { - Ok(ScVal::Symbol(v.try_into()?)) + fn try_from(v: String) -> Result { + Ok(ScSymbol(v.try_into().map_err(|_| ())?)) } } #[cfg(feature = "alloc")] -impl TryFrom<&String> for ScVal { +impl TryFrom<&String> for ScSymbol { type Error = (); - fn try_from(v: &String) -> Result { - Ok(ScVal::Symbol(v.try_into()?)) + fn try_from(v: &String) -> Result { + Ok(ScSymbol(v.try_into().map_err(|_| ())?)) } } #[cfg(feature = "alloc")] -impl TryFrom for ScSymbol { +impl TryFrom<&str> for ScSymbol { type Error = (); - fn try_from(v: String) -> Result { + fn try_from(v: &str) -> Result { Ok(ScSymbol(v.try_into().map_err(|_| ())?)) } } -#[cfg(feature = "alloc")] -impl TryFrom<&String> for ScSymbol { +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for ScSymbol { type Error = (); - fn try_from(v: &String) -> Result { + fn try_from(v: &'static str) -> Result { Ok(ScSymbol(v.try_into().map_err(|_| ())?)) } } -#[cfg(feature = "alloc")] -impl TryFrom<&str> for ScVal { - type Error = (); - fn try_from(v: &str) -> Result { - Ok(ScVal::Symbol(v.try_into()?)) +impl From for ScVal { + fn from(v: ScString) -> Self { + ScVal::String(v) } } -#[cfg(not(feature = "alloc"))] -impl TryFrom<&'static str> for ScVal { +impl TryFrom for ScString { type Error = (); - fn try_from(v: &'static str) -> Result { - Ok(ScVal::Symbol(v.try_into()?)) + fn try_from(v: ScVal) -> Result { + if let ScVal::String(s) = v { + Ok(s) + } else { + Err(()) + } } } #[cfg(feature = "alloc")] -impl TryFrom<&str> for ScSymbol { +impl TryFrom for ScString { type Error = (); - fn try_from(v: &str) -> Result { - Ok(ScSymbol(v.try_into().map_err(|_| ())?)) + fn try_from(v: String) -> Result { + Ok(ScString(v.try_into().map_err(|_| ())?)) } } -#[cfg(not(feature = "alloc"))] -impl TryFrom<&'static str> for ScSymbol { +#[cfg(feature = "alloc")] +impl TryFrom<&String> for ScString { type Error = (); - fn try_from(v: &'static str) -> Result { - Ok(ScSymbol(v.try_into().map_err(|_| ())?)) + fn try_from(v: &String) -> Result { + Ok(ScString(v.try_into().map_err(|_| ())?)) } } #[cfg(feature = "alloc")] -impl TryFrom for String { +impl TryFrom<&str> for ScString { type Error = (); - fn try_from(v: ScVal) -> Result { - if let ScVal::Symbol(s) = v { - // TODO: It might be worth distinguishing the error case where this - // is an invalid symbol with invalid characters. - Ok(s.0.into_utf8_string().map_err(|_| ())?) - } else { - Err(()) - } + fn try_from(v: &str) -> Result { + Ok(ScString(v.try_into().map_err(|_| ())?)) + } +} + +#[cfg(not(feature = "alloc"))] +impl TryFrom<&'static str> for ScString { + type Error = (); + fn try_from(v: &'static str) -> Result { + Ok(ScString(v.try_into().map_err(|_| ())?)) } } diff --git a/src/next/str.rs b/src/next/str.rs index 461dd67d..9b4bfa85 100644 --- a/src/next/str.rs +++ b/src/next/str.rs @@ -14,7 +14,8 @@ //# - SignerKeyEd25519SignedPayload //# - NodeId //# -//# ## Asset Codes +//# ## Asset Types +//# - Asset //# - AssetCode //# - AssetCode4 //# - AssetCode12 @@ -295,10 +296,21 @@ impl core::str::FromStr for AssetCode12 { impl core::fmt::Display for AssetCode12 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - if let Some(last_idx) = self.0.iter().rposition(|c| *c != 0) { - for b in escape_bytes::Escape::new(&self.0[..=last_idx]) { - write!(f, "{}", b as char)?; - } + // AssetCode12's are always rendered as at least 5 characters, because + // any asset code shorter than 5 characters is an AssetCode4. + // AssetCode12 contains a fixed length 12-byte array, and the constant + // and slices in this function never operate out-of-bounds because of + // that. + const MIN_LENGTH: usize = 5; + let len = MIN_LENGTH + + self + .0 + .iter() + .skip(MIN_LENGTH) + .rposition(|c| *c != 0) + .map_or(0, |last_idx| last_idx + 1); + for b in escape_bytes::Escape::new(&self.0[..len]) { + write!(f, "{}", b as char)?; } Ok(()) } diff --git a/src/next/transaction_conversions.rs b/src/next/transaction_conversions.rs index a821a51f..11306549 100644 --- a/src/next/transaction_conversions.rs +++ b/src/next/transaction_conversions.rs @@ -1,7 +1,51 @@ use super::{ - FeeBumpTransaction, FeeBumpTransactionEnvelope, Transaction, TransactionEnvelope, - TransactionV1Envelope, VecM, + FeeBumpTransaction, FeeBumpTransactionEnvelope, Memo, MuxedAccount, Operation, Preconditions, + SequenceNumber, Transaction, TransactionEnvelope, TransactionExt, TransactionV1Envelope, VecM, }; +#[cfg(feature = "sha2")] +use super::{ + Hash, Limits, TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, + WriteXdr, +}; + +use super::Error; + +impl Transaction { + pub fn new_tx( + source_account: impl Into, + fee: u32, + seq_num: impl Into, + operation: Operation, + ) -> Transaction { + Transaction { + source_account: source_account.into(), + fee, + seq_num: seq_num.into(), + cond: Preconditions::None, + memo: Memo::None, + operations: [operation].try_into().unwrap(), + ext: TransactionExt::V0, + } + } + + pub fn add_operation(mut self, operation: Operation) -> Result { + let mut ops = self.operations.to_vec(); + ops.push(operation); + self.operations = ops.try_into().map_err(|_| Error::Invalid)?; + Ok(self) + } + + #[cfg(feature = "sha2")] + pub fn hash(&self, network_passphrase: &str) -> Result { + let signature_payload = TransactionSignaturePayload { + network_id: Hash::hash_bytes(network_passphrase), + tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(self.clone()), + }; + signature_payload + .to_xdr(Limits::none()) + .map(Hash::hash_bytes) + } +} impl From for TransactionEnvelope { fn from(tx: Transaction) -> Self { diff --git a/tests/hash.rs b/tests/hash.rs new file mode 100644 index 00000000..ae515c59 --- /dev/null +++ b/tests/hash.rs @@ -0,0 +1,9 @@ +#![cfg(all(feature = "curr", feature = "hex"))] +use stellar_xdr::curr::Hash; + +#[test] +fn hash_padding() { + let padded = "0000000000000000000000000000000011111111111111111111111111111111"; + let non_padded = "11111111111111111111111111111111"; + assert_eq!(Hash::from_hex(padded), Hash::from_hex(non_padded)); +} diff --git a/tests/num_conversions.rs b/tests/num_conversions.rs new file mode 100644 index 00000000..0b16c309 --- /dev/null +++ b/tests/num_conversions.rs @@ -0,0 +1,36 @@ +#![cfg(all( + any(feature = "curr", feature = "next"), + not(all(feature = "curr", feature = "next")) +))] +#![cfg(feature = "std")] + +#[cfg(feature = "curr")] +use stellar_xdr::curr as stellar_xdr; +// #[cfg(feature = "next")] +// use stellar_xdr::next as stellar_xdr; + +use stellar_xdr::{Int128Parts, UInt128Parts}; + +#[test] +fn round_trip_u128() { + let u128_val = 0x1234567890abcdef1234567890abcdefu128; + let xdr_val: UInt128Parts = u128_val.into(); + let u128_val2: u128 = xdr_val.into(); + assert_eq!(u128_val, u128_val2); +} + +#[test] +fn round_trip_i128() { + let i128_val = 0x1234567890abcdef1234567890abcdefi128; + let xdr_val: Int128Parts = i128_val.into(); + let i128_val2: i128 = xdr_val.into(); + assert_eq!(i128_val, i128_val2); +} + +#[test] +fn round_trip_u256() { + let u256_val = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefu128; + let xdr_val: UInt128Parts = u256_val.into(); + let u256_val2: u128 = xdr_val.into(); + assert_eq!(u256_val, u256_val2); +}