From 4bf9faf17071c87074423eb7bf3807dbf6932783 Mon Sep 17 00:00:00 2001 From: Dmytro Kozhevin Date: Mon, 26 Sep 2022 20:46:37 -0400 Subject: [PATCH 1/3] Reduce the amount of roundtrips for built-in token. - `AccountId` is decoded straight into XDR, as `AccountId` XDR is a host object type now - `AccountId` is passed around in internal host fns instead of `Object` - Removed `ClassicMetadata` as it became an internal-only structure. Instead, just pass the encoded asset to the contract asset initializer --- soroban-env-host/src/host.rs | 188 +++--------------- soroban-env-host/src/host/conversion.rs | 22 +- soroban-env-host/src/host/data_helper.rs | 121 ++++++----- .../src/native_contract/base_types.rs | 114 +++-------- .../src/native_contract/invoker.rs | 12 +- .../src/native_contract/token/balance.rs | 19 +- .../src/native_contract/token/contract.rs | 116 +++++------ .../src/native_contract/token/cryptography.rs | 19 +- .../src/native_contract/token/metadata.rs | 4 +- .../src/native_contract/token/public_types.rs | 11 +- 10 files changed, 219 insertions(+), 407 deletions(-) diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 6e7407bf2..d5bf155c4 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -4,7 +4,6 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::fmt::Debug; -use std::cmp::min; use im_rc::{OrdMap, Vector}; use num_bigint::Sign; @@ -15,7 +14,7 @@ use soroban_env_common::{ use soroban_env_common::xdr::{ AccountId, Asset, ContractEvent, ContractEventBody, ContractEventType, ContractEventV0, - ExtensionPoint, Hash, PublicKey, ScStatus, ScStatusType, ThresholdIndexes, + ExtensionPoint, Hash, ScStatus, ScStatusType, ThresholdIndexes, TrustLineAsset, }; use crate::budget::{Budget, CostType}; @@ -236,6 +235,10 @@ impl Host { f(self.0.budget.clone()) } + pub(crate) fn get_budget_ref(&self) -> &Budget { + &self.0.budget + } + pub fn charge_budget(&self, ty: CostType, input: u64) -> Result<(), HostError> { self.0.budget.clone().charge(ty, input) } @@ -902,22 +905,20 @@ impl Host { Ok(Status::OK.into()) } - // Metering: *mostly* covered by components. The arithmatics are free. + // Metering: *mostly* covered by components. The arithmetics are free. pub(crate) fn transfer_account_balance( &self, - account: Object, + account_id: AccountId, amount: i64, ) -> Result<(), HostError> { - use xdr::{AccountEntryExt, AccountEntryExtensionV1Ext, LedgerKeyAccount}; + use xdr::{AccountEntryExt, AccountEntryExtensionV1Ext}; self.with_current_frame(|frame| match frame { Frame::Token(id, _) => Ok(()), _ => Err(self.err_general("only native token can transfer classic balance")), })?; - let lk = LedgerKey::Account(LedgerKeyAccount { - account_id: self.to_account_id(account)?, - }); + let lk = self.to_account_key(account_id); self.with_mut_storage(|storage| { let mut le = storage.get(&lk)?; @@ -966,9 +967,8 @@ impl Host { // Metering: *mostly* covered by components. The arithmatics are free. pub(crate) fn transfer_trustline_balance( &self, - account: Object, - asset_code: Object, - issuer: Object, + account_id: AccountId, + asset: TrustLineAsset, amount: i64, ) -> Result<(), HostError> { use xdr::{TrustLineEntryExt, TrustLineFlags}; @@ -978,7 +978,7 @@ impl Host { _ => Err(self.err_general("only native token can transfer classic balance")), })?; - let lk = self.to_trustline_key(account, asset_code, issuer)?; + let lk = self.to_trustline_key(account_id, asset); self.with_mut_storage(|storage| { let mut le = storage.get(&lk)?; let tl = match &mut le.data { @@ -1868,124 +1868,15 @@ impl VmCallerCheckedEnv for Host { fn create_token_from_asset( &self, vmcaller: &mut VmCaller, - asset: Object, + asset_bytes: Object, ) -> Result { - let a = self.visit_obj(asset, |hv: &Vec| { - self.metered_from_xdr::(hv.as_slice()) - })?; - - // TODO: Are these error msgs necessary? Should they be simplified? - - // We want to convert xdr::Asset into an ScVal that matches the ClassicMetadata - // contracttype in the token contract - let arg = - match &a { - Asset::Native => ScVal::Object(Some(ScObject::Vec(ScVec( - vec![ScVal::Symbol( - "Native" - .to_string() - .try_into() - .map_err(|_| self.err_general("invalid Native"))?, - )] - .try_into() - .map_err(|_| self.err_general("invalid vec"))?, - )))), - Asset::CreditAlphanum4(a4) => { - let PublicKey::PublicKeyTypeEd25519(issuer_key) = a4.issuer.0.clone(); - ScVal::Object(Some(ScObject::Vec(ScVec( - vec![ - ScVal::Symbol( - "AlphaNum4" - .to_string() - .try_into() - .map_err(|_| self.err_general("invalid AlphaNum4"))?, - ), - ScVal::Object(Some(ScObject::Map( - ScMap::try_from(vec![ - ScMapEntry { - key: ScVal::Symbol( - "asset_code".to_string().try_into().map_err(|_| { - self.err_general("invalid code4 key") - })?, - ), - val: ScVal::Object(Some(ScObject::Bytes( - a4.asset_code.0.try_into().map_err(|_| { - self.err_general("invalid code4 val") - })?, - ))), - }, - ScMapEntry { - key: ScVal::Symbol( - "issuer".to_string().try_into().map_err(|_| { - self.err_general("invalid issuer4 key") - })?, - ), - val: ScVal::Object(Some(ScObject::Bytes( - issuer_key.0.try_into().map_err(|_| { - self.err_general("invalid issuer4 key") - })?, - ))), - }, - ]) - .map_err(|_| self.err_general("invalid map"))?, - ))), - ] - .try_into() - .map_err(|_| self.err_general("invalid vec"))?, - )))) - } - Asset::CreditAlphanum12(a12) => { - let PublicKey::PublicKeyTypeEd25519(issuer_key) = a12.issuer.0.clone(); - ScVal::Object(Some(ScObject::Vec(ScVec( - vec![ - ScVal::Symbol( - "AlphaNum12" - .to_string() - .try_into() - .map_err(|_| self.err_general("invalid AlphaNum12"))?, - ), - ScVal::Object(Some(ScObject::Map( - ScMap::try_from(vec![ - ScMapEntry { - key: ScVal::Symbol( - "asset_code".to_string().try_into().map_err(|_| { - self.err_general("invalid code12 key") - })?, - ), - val: ScVal::Object(Some(ScObject::Bytes( - a12.asset_code.0.try_into().map_err(|_| { - self.err_general("invalid code12 val") - })?, - ))), - }, - ScMapEntry { - key: ScVal::Symbol( - "issuer".to_string().try_into().map_err(|_| { - self.err_general("invalid issuer12 key") - })?, - ), - val: ScVal::Object(Some(ScObject::Bytes( - issuer_key.0.try_into().map_err(|_| { - self.err_general("invalid issuer12 val") - })?, - ))), - }, - ]) - .map_err(|_| self.err_general("invalid map"))?, - ))), - ] - .try_into() - .map_err(|_| self.err_general("invalid vec"))?, - )))) - } - }; - - let buf = self.id_preimage_from_asset(a)?; - let id = self.create_contract_with_id_preimage(ScContractCode::Token, buf)?; + let asset = self.deserialize_asset(asset_bytes)?; + let id_preimage = self.id_preimage_from_asset(asset)?; + let id = self.create_contract_with_id_preimage(ScContractCode::Token, id_preimage)?; self.call_n( id, Symbol::from_str("init_asset"), - &[self.to_host_val(&arg)?.val], + &[asset_bytes.try_into()?], )?; Ok(id) @@ -2684,7 +2575,8 @@ impl VmCallerCheckedEnv for Host { _vmcaller: &mut VmCaller, a: Object, ) -> Result { - let threshold = self.load_account(a)?.thresholds.0[ThresholdIndexes::Low as usize]; + let threshold = + self.load_account(self.to_account_id(a)?)?.thresholds.0[ThresholdIndexes::Low as usize]; let threshold = Into::::into(threshold); Ok(threshold.into()) } @@ -2695,7 +2587,8 @@ impl VmCallerCheckedEnv for Host { _vmcaller: &mut VmCaller, a: Object, ) -> Result { - let threshold = self.load_account(a)?.thresholds.0[ThresholdIndexes::Med as usize]; + let threshold = + self.load_account(self.to_account_id(a)?)?.thresholds.0[ThresholdIndexes::Med as usize]; let threshold = Into::::into(threshold); Ok(threshold.into()) } @@ -2706,7 +2599,8 @@ impl VmCallerCheckedEnv for Host { _vmcaller: &mut VmCaller, a: Object, ) -> Result { - let threshold = self.load_account(a)?.thresholds.0[ThresholdIndexes::High as usize]; + let threshold = self.load_account(self.to_account_id(a)?)?.thresholds.0 + [ThresholdIndexes::High as usize]; let threshold = Into::::into(threshold); Ok(threshold.into()) } @@ -2717,7 +2611,7 @@ impl VmCallerCheckedEnv for Host { _vmcaller: &mut VmCaller, a: Object, ) -> Result { - Ok(self.has_account(a)?.into()) + Ok(self.has_account(self.to_account_id(a)?)?.into()) } // Notes on metering: some covered. The for loop and comparisons are free (for now). @@ -2727,39 +2621,11 @@ impl VmCallerCheckedEnv for Host { a: Object, s: Object, ) -> Result { - use xdr::{Signer, SignerKey}; - let target_signer = self.to_u256(s)?; - let ae = self.load_account(a)?; - if ae.account_id - == AccountId(PublicKey::PublicKeyTypeEd25519( - target_signer.metered_clone(&self.0.budget)?, - )) - { - // Target signer is the master key, so return the master weight - let threshold = ae.thresholds.0[ThresholdIndexes::MasterWeight as usize]; - let threshold = Into::::into(threshold); - Ok(threshold.into()) - } else { - // Target signer is not the master key, so search the account signers - let signers: &Vec = ae.signers.as_ref(); - for signer in signers { - if let SignerKey::Ed25519(ref this_signer) = signer.key { - if &target_signer == this_signer { - // Clamp the weight at 255. Stellar protocol before v10 - // allowed weights to exceed 255, but the max threshold - // is 255, hence there is no point in having a larger - // weight. - let weight = min(signer.weight, u8::MAX as u32); - // We've found the target signer in the account signers, so return the weight - return Ok(weight.into()); - } - } - } - // We didn't find the target signer, return 0 weight to indicate that. - Ok(0u32.into()) - } + let ae = self.load_account(self.to_account_id(a)?)?; + let weight = self.get_signer_weight_from_account(target_signer, &ae)?; + Ok((weight as u32).into()) } fn get_ledger_version(&self, _vmcaller: &mut VmCaller) -> Result { diff --git a/soroban-env-host/src/host/conversion.rs b/soroban-env-host/src/host/conversion.rs index 4ef10b9f7..7d2c3cb03 100644 --- a/soroban-env-host/src/host/conversion.rs +++ b/soroban-env-host/src/host/conversion.rs @@ -13,7 +13,7 @@ use crate::{ use ed25519_dalek::{PublicKey, Signature, SIGNATURE_LENGTH}; use num_bigint::Sign; use sha2::{Digest, Sha256}; -use soroban_env_common::xdr::AccountId; +use soroban_env_common::xdr::{AccountId, Asset, ReadXdr}; impl Host { // Notes on metering: free @@ -64,11 +64,13 @@ impl Host { }) } - pub(crate) fn to_u256_from_account(&self, a: Object) -> Result { - self.visit_obj(a, |account_id: &AccountId| { - let crate::xdr::PublicKey::PublicKeyTypeEd25519(ed25519) = &account_id.0; - Ok(ed25519.metered_clone(&self.0.budget)?) - }) + pub(crate) fn to_u256_from_account( + &self, + account_id: &AccountId, + ) -> Result { + let crate::xdr::PublicKey::PublicKeyTypeEd25519(ed25519) = + account_id.metered_clone(&self.0.budget)?.0; + Ok(ed25519) } pub(crate) fn to_u256(&self, a: Object) -> Result { @@ -333,4 +335,12 @@ impl Host { } } } + + pub(crate) fn deserialize_asset(&self, asset: Object) -> Result { + self.visit_obj(asset, |hv: &Vec| { + self.charge_budget(CostType::ValDeser, hv.len() as u64)?; + Asset::from_xdr(&mut hv.as_slice()) + .map_err(|_| self.err_general("failed to de-serialize Asset")) + }) + } } diff --git a/soroban-env-host/src/host/data_helper.rs b/soroban-env-host/src/host/data_helper.rs index 8c34eb369..8b7744b37 100644 --- a/soroban-env-host/src/host/data_helper.rs +++ b/soroban-env-host/src/host/data_helper.rs @@ -1,5 +1,10 @@ +use std::cmp::min; + use soroban_env_common::{ - xdr::{Asset, HashIdPreimageSourceAccountContractId}, + xdr::{ + Asset, HashIdPreimageSourceAccountContractId, PublicKey, Signer, SignerKey, + ThresholdIndexes, TrustLineAsset, + }, CheckedEnv, InvokerType, }; @@ -9,7 +14,7 @@ use crate::xdr::{ LedgerKeyAccount, LedgerKeyContractData, LedgerKeyTrustLine, ScContractCode, ScHostStorageErrorCode, ScHostValErrorCode, ScObject, ScStatic, ScVal, Uint256, }; -use crate::{Host, HostError, Object}; +use crate::{Host, HostError}; use super::metered_clone::MeteredClone; @@ -116,60 +121,86 @@ impl Host { Ok(buf) } - // notes on metering: `get` from storage and `to_u256` covered. Rest are free. - pub fn load_account(&self, a: Object) -> Result { - let acc = LedgerKey::Account(LedgerKeyAccount { - account_id: self - .visit_obj(a, |id: &AccountId| Ok(id.metered_clone(&self.0.budget)?))?, - }); + // notes on metering: `get` from storage is covered. Rest are free. + pub fn load_account(&self, account_id: AccountId) -> Result { + let acc = self.to_account_key(account_id); self.with_mut_storage(|storage| match storage.get(&acc)?.data { LedgerEntryData::Account(ae) => Ok(ae), _ => Err(self.err_general("not account")), }) } - // notes on metering: covered by `has` and `to_u256`. - pub fn has_account(&self, a: Object) -> Result { - let acc = LedgerKey::Account(LedgerKeyAccount { - account_id: self - .visit_obj(a, |id: &AccountId| Ok(id.metered_clone(&self.0.budget)?))?, - }); + // notes on metering: covered by `has`. + pub fn has_account(&self, account_id: AccountId) -> Result { + let acc = self.to_account_key(account_id); self.with_mut_storage(|storage| storage.has(&acc)) } - pub fn to_trustline_key( + pub(crate) fn to_account_key(&self, account_id: AccountId) -> LedgerKey { + LedgerKey::Account(LedgerKeyAccount { account_id }) + } + + pub(crate) fn create_asset_4(&self, asset_code: [u8; 4], issuer: AccountId) -> TrustLineAsset { + use crate::xdr::{AlphaNum4, AssetCode4}; + TrustLineAsset::CreditAlphanum4(AlphaNum4 { + asset_code: AssetCode4(asset_code), + issuer, + }) + } + + pub(crate) fn create_asset_12( &self, - account: Object, - asset_code: Object, - issuer: Object, - ) -> Result { - use crate::xdr::{AlphaNum12, AlphaNum4, AssetCode12, AssetCode4, TrustLineAsset}; - let asset = self.visit_obj(asset_code, |b: &Vec| { - if b.len() > 0 && b.len() <= 4 { - Ok(TrustLineAsset::CreditAlphanum4(AlphaNum4 { - asset_code: AssetCode4( - b.as_slice() - .try_into() - .map_err(|_| self.err_general("invalid AssetCode4"))?, - ), - issuer: self.to_account_id(issuer)?, - })) - } else if b.len() > 0 && b.len() <= 12 { - Ok(TrustLineAsset::CreditAlphanum12(AlphaNum12 { - asset_code: AssetCode12( - b.as_slice() + asset_code: [u8; 12], + issuer: AccountId, + ) -> TrustLineAsset { + use crate::xdr::{AlphaNum12, AssetCode12}; + TrustLineAsset::CreditAlphanum12(AlphaNum12 { + asset_code: AssetCode12(asset_code), + issuer, + }) + } + + pub(crate) fn to_trustline_key( + &self, + account_id: AccountId, + asset: TrustLineAsset, + ) -> LedgerKey { + LedgerKey::Trustline(LedgerKeyTrustLine { account_id, asset }) + } + + pub(crate) fn get_signer_weight_from_account( + &self, + target_signer: Uint256, + account: &AccountEntry, + ) -> Result { + if account.account_id + == AccountId(PublicKey::PublicKeyTypeEd25519( + target_signer.metered_clone(&self.0.budget)?, + )) + { + // Target signer is the master key, so return the master weight + let threshold = account.thresholds.0[ThresholdIndexes::MasterWeight as usize]; + Ok(threshold) + } else { + // Target signer is not the master key, so search the account signers + let signers: &Vec = account.signers.as_ref(); + for signer in signers { + if let SignerKey::Ed25519(ref this_signer) = signer.key { + if &target_signer == this_signer { + // Clamp the weight at 255. Stellar protocol before v10 + // allowed weights to exceed 255, but the max threshold + // is 255, hence there is no point in having a larger + // weight. + let weight = min(signer.weight, u8::MAX as u32); + // We've found the target signer in the account signers, so return the weight + return Ok(weight .try_into() - .map_err(|_| self.err_general("invalid AssetCode12"))?, - ), - issuer: self.to_account_id(issuer)?, - })) - } else { - Err(self.err_general("invalid asset code")) + .map_err(|_| self.err_general("signer weight overflow"))?); + } + } } - })?; - Ok(LedgerKey::Trustline(LedgerKeyTrustLine { - account_id: self.to_account_id(account)?, - asset, - })) + // We didn't find the target signer, return 0 weight to indicate that. + Ok(0u8) + } } } diff --git a/soroban-env-host/src/native_contract/base_types.rs b/soroban-env-host/src/native_contract/base_types.rs index f82d9847e..edc37421f 100644 --- a/soroban-env-host/src/native_contract/base_types.rs +++ b/soroban-env-host/src/native_contract/base_types.rs @@ -1,8 +1,9 @@ +use crate::budget::CostType; use crate::host::{Host, HostError}; use core::cmp::Ordering; -use soroban_env_common::xdr::ScObjectType; +use soroban_env_common::xdr::{AccountId, ScObjectType}; use soroban_env_common::{ - CheckedEnv, ConversionError, EnvBase, EnvVal, IntoVal, Object, RawVal, TryFromVal, TryIntoVal, + CheckedEnv, ConversionError, EnvBase, EnvVal, Object, RawVal, TryFromVal, TryIntoVal, }; #[derive(Clone)] @@ -128,7 +129,7 @@ impl From for Object { } } -impl From> for Bytes { +impl From> for Bytes { fn from(b: BytesN) -> Self { Self(b.0) } @@ -156,14 +157,17 @@ impl Bytes { } #[derive(Clone)] -pub struct BytesN(EnvVal); +pub struct BytesN(EnvVal); -impl TryFromVal for BytesN { +impl TryFromVal for BytesN { type Error = HostError; fn try_from_val(env: &Host, val: Object) -> Result { let len: u32 = env.bytes_len(val)?.try_into()?; - if len == N { + if len + == N.try_into() + .map_err(|_| env.err_general("bytes buffer overflow"))? + { Ok(Self(val.in_env(env))) } else { Err(ConversionError.into()) @@ -171,27 +175,27 @@ impl TryFromVal for BytesN { } } -impl Eq for BytesN {} +impl Eq for BytesN {} -impl PartialEq for BytesN { +impl PartialEq for BytesN { fn eq(&self, other: &Self) -> bool { self.partial_cmp(other) == Some(Ordering::Equal) } } -impl PartialOrd for BytesN { +impl PartialOrd for BytesN { fn partial_cmp(&self, other: &Self) -> Option { Some(Ord::cmp(self, other)) } } -impl Ord for BytesN { +impl Ord for BytesN { fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.cmp(&other.0) } } -impl TryFromVal for BytesN { +impl TryFromVal for BytesN { type Error = HostError; fn try_from_val(env: &Host, val: RawVal) -> Result { @@ -199,7 +203,7 @@ impl TryFromVal for BytesN { } } -impl TryIntoVal> for RawVal { +impl TryIntoVal> for RawVal { type Error = HostError; fn try_into_val(self, env: &Host) -> Result, Self::Error> { @@ -207,7 +211,7 @@ impl TryIntoVal> for RawVal { } } -impl TryIntoVal for BytesN { +impl TryIntoVal for BytesN { type Error = HostError; fn try_into_val(self, _env: &Host) -> Result { @@ -215,29 +219,27 @@ impl TryIntoVal for BytesN { } } -impl From> for Object { +impl From> for Object { fn from(bytes: BytesN) -> Self { bytes.0.val } } -impl BytesN { +impl BytesN { pub fn compare(&self, other: &BytesN) -> Result { let res = self.0.env.obj_cmp(self.0.val.into(), other.0.val.into())?; Ok(res.cmp(&0)) } } -impl BytesN { - /// Copy the bytes in [BytesN] into the given slice. - /// - /// The minimum number of bytes are copied to either exhaust [BytesN] or - /// fill slice. +impl BytesN { #[inline(always)] - pub fn copy_into_slice(&self, slice: &mut [u8]) -> Result<(), HostError> { + pub fn to_array(&self) -> Result<[u8; N], HostError> { let env = self.0.env(); - env.bytes_copy_to_slice(self.0.to_object(), RawVal::U32_ZERO, slice) - .map_err(|status| status.into()) + let mut slice = [0_u8; N]; + env.charge_budget(CostType::BytesClone, 4)?; + env.bytes_copy_to_slice(self.0.to_object(), RawVal::U32_ZERO, &mut slice)?; + Ok(slice) } #[inline(always)] @@ -400,46 +402,11 @@ impl Vec { } } -#[derive(Clone)] -pub struct AccountId(EnvVal); - -impl Eq for AccountId {} - -impl PartialEq for AccountId { - fn eq(&self, other: &Self) -> bool { - self.partial_cmp(other) == Some(Ordering::Equal) - } -} - -impl PartialOrd for AccountId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(Ord::cmp(self, other)) - } -} - -impl Ord for AccountId { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - impl TryFromVal for AccountId { type Error = HostError; fn try_from_val(env: &Host, val: Object) -> Result { - if val.is_obj_type(ScObjectType::AccountId) { - Ok(AccountId(val.in_env(env))) - } else { - Err(ConversionError.into()) - } - } -} - -impl TryIntoVal for Object { - type Error = HostError; - - fn try_into_val(self, env: &Host) -> Result { - <_ as TryFromVal<_, Object>>::try_from_val(env, self) + env.visit_obj(val, |acc: &AccountId| Ok(acc.clone())) } } @@ -451,35 +418,10 @@ impl TryFromVal for AccountId { } } -impl TryIntoVal for RawVal { - type Error = HostError; - - fn try_into_val(self, env: &Host) -> Result { - <_ as TryFromVal<_, RawVal>>::try_from_val(env, self) - } -} - impl TryIntoVal for AccountId { type Error = HostError; - fn try_into_val(self, _env: &Host) -> Result { - Ok(self.0.val.into()) - } -} - -impl IntoVal for AccountId { - fn into_val(self, _env: &Host) -> Object { - self.to_object() - } -} - -impl AccountId { - pub fn new(env: &Host) -> Result { - let map = env.map_new()?; - Ok(Self(map.in_env(env))) - } - - pub fn to_object(&self) -> Object { - self.0.to_object() + fn try_into_val(self, env: &Host) -> Result { + Ok(env.add_host_object(self.clone())?.to_raw()) } } diff --git a/soroban-env-host/src/native_contract/invoker.rs b/soroban-env-host/src/native_contract/invoker.rs index 5de3590e5..daeedb5d2 100644 --- a/soroban-env-host/src/native_contract/invoker.rs +++ b/soroban-env-host/src/native_contract/invoker.rs @@ -1,9 +1,9 @@ -use soroban_env_common::{CheckedEnv, InvokerType, TryFromVal, TryIntoVal}; +use soroban_env_common::{xdr::AccountId, CheckedEnv, InvokerType, TryFromVal, TryIntoVal}; use soroban_native_sdk_macros::contracttype; use crate::{Host, HostError}; -use super::base_types::{AccountId, BytesN}; +use super::base_types::BytesN; #[derive(Clone, PartialEq)] #[contracttype] @@ -15,11 +15,9 @@ pub enum Invoker { pub fn invoker(env: &Host) -> Result { let invoker_type: InvokerType = Host::get_invoker_type(&env)?.try_into()?; Ok(match invoker_type { - InvokerType::Account => Invoker::Account(>::try_into_val( - Host::get_invoking_account(&env)?, &env + InvokerType::Account => Invoker::Account(AccountId::try_from_val( + env, + Host::get_invoking_account(&env)?, )?), InvokerType::Contract => Invoker::Contract(BytesN::<32>::try_from_val( env, diff --git a/soroban-env-host/src/native_contract/token/balance.rs b/soroban-env-host/src/native_contract/token/balance.rs index 1f3ed9ac1..c16cde976 100644 --- a/soroban-env-host/src/native_contract/token/balance.rs +++ b/soroban-env-host/src/native_contract/token/balance.rs @@ -1,11 +1,12 @@ use crate::host::Host; -use crate::native_contract::base_types::{AccountId, BigInt}; +use crate::native_contract::base_types::BigInt; use crate::native_contract::token::error::Error; use crate::native_contract::token::metadata::read_metadata; use crate::native_contract::token::public_types::{Identifier, Metadata}; use crate::native_contract::token::storage_types::DataKey; use core::cmp::Ordering; -use soroban_env_common::{CheckedEnv, IntoVal, TryIntoVal}; +use soroban_env_common::xdr::AccountId; +use soroban_env_common::{CheckedEnv, TryIntoVal}; // Metering: *mostly* covered by components. Not sure about `try_into_val`. pub fn read_balance(e: &Host, id: Identifier) -> Result { @@ -66,20 +67,18 @@ pub fn write_state(e: &Host, id: Identifier, is_frozen: bool) -> Result<(), Erro } // Metering: covered by components -pub fn transfer_classic_balance(e: &Host, to_key: &AccountId, amount: i64) -> Result<(), Error> { +pub fn transfer_classic_balance(e: &Host, to_key: AccountId, amount: i64) -> Result<(), Error> { match read_metadata(e)? { Metadata::Token(_) => return Err(Error::ContractError), - Metadata::Native => e.transfer_account_balance(to_key.clone().into_val(&e), amount)?, + Metadata::Native => e.transfer_account_balance(to_key, amount)?, Metadata::AlphaNum4(asset) => e.transfer_trustline_balance( - to_key.clone().into_val(&e), - asset.asset_code.into(), - asset.issuer.into_val(&e), + to_key, + e.create_asset_4(asset.asset_code.to_array()?, asset.issuer), amount, )?, Metadata::AlphaNum12(asset) => e.transfer_trustline_balance( - to_key.clone().into_val(&e), - asset.asset_code.into(), - asset.issuer.into_val(&e), + to_key, + e.create_asset_12(asset.asset_code.to_array()?, asset.issuer), amount, )?, }; diff --git a/soroban-env-host/src/native_contract/token/contract.rs b/soroban-env-host/src/native_contract/token/contract.rs index 75be043ea..e8e86ccda 100644 --- a/soroban-env-host/src/native_contract/token/contract.rs +++ b/soroban-env-host/src/native_contract/token/contract.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use crate::budget::CostType; +use crate::host::metered_clone::MeteredClone; use crate::host::Host; use crate::native_contract::base_types::{BigInt, Bytes, BytesN, Vec}; use crate::native_contract::token::admin::{check_admin, write_administrator}; @@ -14,12 +14,10 @@ use crate::native_contract::token::metadata::{ has_metadata, read_decimal, read_name, read_symbol, write_metadata, }; use crate::native_contract::token::nonce::read_nonce; -use crate::native_contract::token::public_types::{ - ClassicMetadata, Identifier, Metadata, Signature, TokenMetadata, -}; +use crate::native_contract::token::public_types::{Identifier, Metadata, Signature, TokenMetadata}; -use soroban_env_common::xdr::{AlphaNum12, AlphaNum4, Asset, AssetCode12, AssetCode4}; -use soroban_env_common::{CheckedEnv, Symbol, TryFromVal, TryIntoVal}; +use soroban_env_common::xdr::Asset; +use soroban_env_common::{CheckedEnv, EnvBase, Symbol, TryFromVal, TryIntoVal}; use soroban_native_sdk_macros::contractimpl; use super::public_types::{AlphaNum12Metadata, AlphaNum4Metadata}; @@ -33,7 +31,7 @@ pub trait TokenTrait { /// /// No admin will be set for the Native token, so any function that checks the admin /// (burn, freeze, unfreeze, mint, set_admin) will always fail - fn init_asset(e: &Host, metadata: ClassicMetadata) -> Result<(), Error>; + fn init_asset(e: &Host, asset_bytes: Bytes) -> Result<(), Error>; /// init creates a token contract that does not wrap an asset on the classic side. fn init(e: &Host, admin: Identifier, metadata: TokenMetadata) -> Result<(), Error>; @@ -71,21 +69,23 @@ pub trait TokenTrait { amount: BigInt, ) -> Result<(), Error>; - fn burn( + fn freeze(e: &Host, admin: Signature, nonce: BigInt, id: Identifier) -> Result<(), Error>; + + fn unfreeze(e: &Host, admin: Signature, nonce: BigInt, id: Identifier) -> Result<(), Error>; + + fn mint( e: &Host, admin: Signature, nonce: BigInt, - from: Identifier, + to: Identifier, amount: BigInt, ) -> Result<(), Error>; - fn freeze(e: &Host, admin: Signature, nonce: BigInt, id: Identifier) -> Result<(), Error>; - - fn mint( + fn burn( e: &Host, admin: Signature, nonce: BigInt, - to: Identifier, + from: Identifier, amount: BigInt, ) -> Result<(), Error>; @@ -96,8 +96,6 @@ pub trait TokenTrait { new_admin: Identifier, ) -> Result<(), Error>; - fn unfreeze(e: &Host, admin: Signature, nonce: BigInt, id: Identifier) -> Result<(), Error>; - fn decimals(e: &Host) -> Result; fn name(e: &Host) -> Result; @@ -114,77 +112,53 @@ pub struct Token; #[contractimpl] // Metering: *mostly* covered by components. impl TokenTrait for Token { - fn init_asset(e: &Host, metadata: ClassicMetadata) -> Result<(), Error> { + fn init_asset(e: &Host, asset_bytes: Bytes) -> Result<(), Error> { if has_metadata(&e)? { return Err(Error::ContractError); } - let contract_id = BytesN::<32>::try_from_val(e, e.get_current_contract()?)?; - - match metadata.clone() { - ClassicMetadata::Native => { - let xlm_contract_id = - BytesN::<32>::try_from_val(e, e.get_contract_id_from_asset(Asset::Native)?)?; - if contract_id != xlm_contract_id { - return Err(Error::ContractError); - } + let asset = e.deserialize_asset(asset_bytes.into())?; + let curr_contract_id = BytesN::<32>::try_from_val(e, e.get_current_contract()?)?; + let expected_contract_id = + BytesN::<32>::try_from_val(e, e.get_contract_id_from_asset(asset.clone())?)?; + if curr_contract_id != expected_contract_id { + return Err(Error::ContractError); + } + match asset { + Asset::Native => { write_metadata(&e, Metadata::Native)?; //No admin for the Native token } - ClassicMetadata::AlphaNum4(asset) => { - //TODO: Better way to do this? - let mut code4 = [0u8; 4]; - e.charge_budget(CostType::BytesClone, code4.len() as u64)?; - asset.asset_code.copy_into_slice(&mut code4)?; - - let issuer_id = e.to_account_id(asset.issuer.to_object())?; - let asset4 = Asset::CreditAlphanum4(AlphaNum4 { - asset_code: AssetCode4(code4), - issuer: issuer_id, - }); - - let asset_contract_id = - BytesN::<32>::try_from_val(e, e.get_contract_id_from_asset(asset4)?)?; - - if contract_id != asset_contract_id { - return Err(Error::ContractError); - } - - write_administrator(&e, Identifier::Account(asset.issuer.clone()))?; + Asset::CreditAlphanum4(asset4) => { + write_administrator( + &e, + Identifier::Account(asset4.issuer.metered_clone(e.get_budget_ref())?), + )?; write_metadata( &e, Metadata::AlphaNum4(AlphaNum4Metadata { - asset_code: asset.asset_code, - issuer: asset.issuer, + asset_code: BytesN::<4>::try_from_val( + e, + e.bytes_new_from_slice(&asset4.asset_code.0)?, + )?, + issuer: asset4.issuer, }), )?; } - ClassicMetadata::AlphaNum12(asset) => { - //TODO: Better way to do this? - let mut code12 = [0u8; 12]; - e.charge_budget(CostType::BytesClone, code12.len() as u64)?; - asset.asset_code.copy_into_slice(&mut code12)?; - - let issuer_id = e.to_account_id(asset.issuer.to_object())?; - let asset12 = Asset::CreditAlphanum12(AlphaNum12 { - asset_code: AssetCode12(code12), - issuer: issuer_id, - }); - - let asset_contract_id = - BytesN::<32>::try_from_val(e, e.get_contract_id_from_asset(asset12)?)?; - - if contract_id != asset_contract_id { - return Err(Error::ContractError); - } - - write_administrator(&e, Identifier::Account(asset.issuer.clone()))?; + Asset::CreditAlphanum12(asset12) => { + write_administrator( + &e, + Identifier::Account(asset12.issuer.metered_clone(e.get_budget_ref())?), + )?; write_metadata( &e, Metadata::AlphaNum12(AlphaNum12Metadata { - asset_code: asset.asset_code, - issuer: asset.issuer, + asset_code: BytesN::<12>::try_from_val( + e, + e.bytes_new_from_slice(&asset12.asset_code.0)?, + )?, + issuer: asset12.issuer, }), )?; } @@ -402,7 +376,7 @@ impl TokenTrait for Token { args.push(amount.clone())?; check_auth(&e, id, nonce, Symbol::from_str("import"), args)?; - transfer_classic_balance(e, &account_id, -amount)?; + transfer_classic_balance(e, account_id.metered_clone(e.get_budget_ref())?, -amount)?; receive_balance( &e, Identifier::Account(account_id), @@ -425,7 +399,7 @@ impl TokenTrait for Token { args.push(amount.clone())?; check_auth(&e, id, nonce, Symbol::from_str("export"), args)?; - transfer_classic_balance(e, &account_id, amount)?; + transfer_classic_balance(e, account_id.metered_clone(e.get_budget_ref())?, amount)?; spend_balance( &e, Identifier::Account(account_id), diff --git a/soroban-env-host/src/native_contract/token/cryptography.rs b/soroban-env-host/src/native_contract/token/cryptography.rs index 6d4168a63..5a428d5ef 100644 --- a/soroban-env-host/src/native_contract/token/cryptography.rs +++ b/soroban-env-host/src/native_contract/token/cryptography.rs @@ -7,7 +7,8 @@ use crate::native_contract::token::public_types::{ SignaturePayloadV0, }; use core::cmp::Ordering; -use soroban_env_common::{CheckedEnv, IntoVal, InvokerType, Symbol, TryFromVal, TryIntoVal}; +use soroban_env_common::xdr::{ThresholdIndexes, Uint256}; +use soroban_env_common::{CheckedEnv, InvokerType, Symbol, TryFromVal, TryIntoVal}; const MAX_ACCOUNT_SIGNATURES: u32 = 20; @@ -52,6 +53,7 @@ fn check_account_auth( if sigs.len()? > MAX_ACCOUNT_SIGNATURES { return Err(Error::ContractError); } + let account = e.load_account(auth.account_id)?; let mut prev_pk: Option> = None; for i in 0..sigs.len()? { let sig: Ed25519Signature = sigs.get(i)?; @@ -67,11 +69,9 @@ fn check_account_auth( sig.public_key.clone().into(), sig.signature.into(), )?; - let signer_weight_rv = e.account_get_signer_weight( - auth.account_id.clone().into_val(&e), - sig.public_key.clone().into(), - )?; - let signer_weight: u32 = signer_weight_rv.try_into()?; + + let signer_weight = + e.get_signer_weight_from_account(Uint256(sig.public_key.to_array()?), &account)?; // 0 weight indicates that signer doesn't belong to this account. Treat // this as an error to indicate a bug in signatures, even if another // signers would have enough weight. @@ -80,12 +80,11 @@ fn check_account_auth( } // Overflow isn't possible here as // 255 * MAX_ACCOUNT_SIGNATURES is < u32::MAX. - weight += signer_weight; + weight += signer_weight as u32; prev_pk = Some(sig.public_key); } - - let threshold_rv = e.account_get_medium_threshold(auth.account_id.into_val(&e))?; - if weight < threshold_rv.try_into()? { + let threshold = account.thresholds.0[ThresholdIndexes::Med as usize]; + if weight < threshold as u32 { Err(Error::ContractError) } else { Ok(()) diff --git a/soroban-env-host/src/native_contract/token/metadata.rs b/soroban-env-host/src/native_contract/token/metadata.rs index 36d50d929..7869b8335 100644 --- a/soroban-env-host/src/native_contract/token/metadata.rs +++ b/soroban-env-host/src/native_contract/token/metadata.rs @@ -34,7 +34,7 @@ pub fn read_name(e: &Host) -> Result { Metadata::AlphaNum4(asset) => { let mut res: Bytes = asset.asset_code.into(); res.push(b':')?; - let issuer_id = e.to_u256_from_account(asset.issuer.to_object())?; + let issuer_id = e.to_u256_from_account(&asset.issuer)?; res.append(Bytes::try_from_val( e, e.bytes_new_from_slice(&issuer_id.0)?, @@ -44,7 +44,7 @@ pub fn read_name(e: &Host) -> Result { Metadata::AlphaNum12(asset) => { let mut res: Bytes = asset.asset_code.into(); res.push(b':')?; - let issuer_id = e.to_u256_from_account(asset.issuer.to_object())?; + let issuer_id = e.to_u256_from_account(&asset.issuer)?; res.append(Bytes::try_from_val( e, e.bytes_new_from_slice(&issuer_id.0)?, diff --git a/soroban-env-host/src/native_contract/token/public_types.rs b/soroban-env-host/src/native_contract/token/public_types.rs index 03974d00e..9d5a7c00d 100644 --- a/soroban-env-host/src/native_contract/token/public_types.rs +++ b/soroban-env-host/src/native_contract/token/public_types.rs @@ -1,7 +1,8 @@ use crate::host::Host; -use crate::native_contract::base_types::{AccountId, Bytes, BytesN, Map, Vec}; +use crate::native_contract::base_types::{Bytes, BytesN, Map, Vec}; use crate::native_contract::invoker::{invoker, Invoker}; use crate::native_contract::token::error::Error; +use soroban_env_common::xdr::AccountId; use soroban_env_common::{Symbol, TryIntoVal}; use soroban_native_sdk_macros::contracttype; @@ -96,14 +97,6 @@ pub struct AlphaNum12Metadata { pub issuer: AccountId, } -#[derive(Clone)] -#[contracttype] -pub enum ClassicMetadata { - Native, - AlphaNum4(AlphaNum4Metadata), - AlphaNum12(AlphaNum12Metadata), -} - #[derive(Clone)] #[contracttype] pub enum Metadata { From af372caac9eef0a07f0ef235913031889de3fef3 Mon Sep 17 00:00:00 2001 From: Dmytro Kozhevin Date: Mon, 3 Oct 2022 18:08:45 -0400 Subject: [PATCH 2/3] Use generic XDR conversion function instead of a special one. --- soroban-env-host/src/host.rs | 2 +- soroban-env-host/src/host/conversion.rs | 8 -------- soroban-env-host/src/host/metered_xdr.rs | 6 ++++++ soroban-env-host/src/native_contract/token/contract.rs | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index d5bf155c4..1de00a1b8 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -1870,7 +1870,7 @@ impl VmCallerCheckedEnv for Host { vmcaller: &mut VmCaller, asset_bytes: Object, ) -> Result { - let asset = self.deserialize_asset(asset_bytes)?; + let asset = self.metered_from_xdr_obj(asset_bytes)?; let id_preimage = self.id_preimage_from_asset(asset)?; let id = self.create_contract_with_id_preimage(ScContractCode::Token, id_preimage)?; self.call_n( diff --git a/soroban-env-host/src/host/conversion.rs b/soroban-env-host/src/host/conversion.rs index 7d2c3cb03..f649d7857 100644 --- a/soroban-env-host/src/host/conversion.rs +++ b/soroban-env-host/src/host/conversion.rs @@ -335,12 +335,4 @@ impl Host { } } } - - pub(crate) fn deserialize_asset(&self, asset: Object) -> Result { - self.visit_obj(asset, |hv: &Vec| { - self.charge_budget(CostType::ValDeser, hv.len() as u64)?; - Asset::from_xdr(&mut hv.as_slice()) - .map_err(|_| self.err_general("failed to de-serialize Asset")) - }) - } } diff --git a/soroban-env-host/src/host/metered_xdr.rs b/soroban-env-host/src/host/metered_xdr.rs index 528b7b3bc..c81832dbd 100644 --- a/soroban-env-host/src/host/metered_xdr.rs +++ b/soroban-env-host/src/host/metered_xdr.rs @@ -1,5 +1,7 @@ use std::{error::Error, io::Write}; +use soroban_env_common::Object; + use crate::{ budget::CostType, xdr::{ReadXdr, WriteXdr}, @@ -51,4 +53,8 @@ impl Host { self.charge_budget(CostType::ValDeser, bytes.len() as u64)?; T::from_xdr(bytes).map_err(|_| self.err_general("failed to read from xdr")) } + + pub(crate) fn metered_from_xdr_obj(&self, bytes: Object) -> Result { + self.visit_obj(bytes, |hv: &Vec| self.metered_from_xdr(hv.as_slice())) + } } diff --git a/soroban-env-host/src/native_contract/token/contract.rs b/soroban-env-host/src/native_contract/token/contract.rs index e8e86ccda..38e53dde8 100644 --- a/soroban-env-host/src/native_contract/token/contract.rs +++ b/soroban-env-host/src/native_contract/token/contract.rs @@ -117,7 +117,7 @@ impl TokenTrait for Token { return Err(Error::ContractError); } - let asset = e.deserialize_asset(asset_bytes.into())?; + let asset: Asset = e.metered_from_xdr_obj(asset_bytes.into())?; let curr_contract_id = BytesN::<32>::try_from_val(e, e.get_current_contract()?)?; let expected_contract_id = From 203f8ff66e2b26d9fcadaaf911906091979dd414 Mon Sep 17 00:00:00 2001 From: Dmytro Kozhevin Date: Mon, 3 Oct 2022 18:15:37 -0400 Subject: [PATCH 3/3] Removed unused imports --- soroban-env-host/src/host/conversion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soroban-env-host/src/host/conversion.rs b/soroban-env-host/src/host/conversion.rs index f649d7857..12c2a1f12 100644 --- a/soroban-env-host/src/host/conversion.rs +++ b/soroban-env-host/src/host/conversion.rs @@ -13,7 +13,7 @@ use crate::{ use ed25519_dalek::{PublicKey, Signature, SIGNATURE_LENGTH}; use num_bigint::Sign; use sha2::{Digest, Sha256}; -use soroban_env_common::xdr::{AccountId, Asset, ReadXdr}; +use soroban_env_common::xdr::AccountId; impl Host { // Notes on metering: free