From a62dfeec619f78071651f6e981d1b67296fb4284 Mon Sep 17 00:00:00 2001 From: Jonathan Jove Date: Tue, 16 Aug 2022 10:24:51 -0500 Subject: [PATCH 1/7] Implement transfer_account_balance and transfer_trustline_balance --- soroban-env-host/src/host.rs | 98 ++++++++++++++++++++++++ soroban-env-host/src/host/data_helper.rs | 45 ++++++++--- 2 files changed, 134 insertions(+), 9 deletions(-) diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 0287c0c09..92092599d 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -91,6 +91,7 @@ pub struct LedgerInfo { sequence_number: u32, timestamp: u64, network_id: Vec, + base_reserve: u32, } #[derive(Clone, Default)] @@ -693,6 +694,103 @@ impl Host { ScContractCode::Wasm(contract_wasm.try_into().map_err(|_| self.err_general(""))?); self.create_contract_with_id(contract_code, contract_id) } + + fn transfer_account_balance(&self, account_id: Object, amount: i64) -> Result<(), HostError> { + use xdr::{AccountEntryExt, AccountEntryExtensionV1Ext, LedgerKeyAccount}; + + let lk = LedgerKey::Account(LedgerKeyAccount { + account_id: AccountId(PublicKey::PublicKeyTypeEd25519(self.to_u256(account_id)?)), + }); + self.visit_storage(|storage| { + let mut le = storage.get(&lk)?; + let ae = match &mut le.data { + LedgerEntryData::Account(ae) => Ok(ae), + _ => Err(self.err_general("not account")), + }?; + if ae.balance < 0 { + return Err(self.err_general("balance is negative")); + } + + let base_reserve = self.with_ledger_info(|li| Ok(li.base_reserve))? as i64; + let (min_balance, max_balance) = if let AccountEntryExt::V1(ext1) = &ae.ext { + let net_entries = if let AccountEntryExtensionV1Ext::V2(ext2) = &ext1.ext { + 2i64 + (ae.num_sub_entries as i64) + (ext2.num_sponsored as i64) + - (ext2.num_sponsoring as i64) + } else { + 2i64 + ae.num_sub_entries as i64 + }; + let min_balance = net_entries * base_reserve + ext1.liabilities.selling; + let max_balance = i64::MAX - ext1.liabilities.buying; + (min_balance, max_balance) + } else { + let net_entries = 2i64 + (ae.num_sub_entries as i64); + let min_balance = net_entries * base_reserve; + let max_balance = i64::MAX; + (min_balance, max_balance) + }; + + let new_balance = if amount <= 0 { + ae.balance + amount + } else if ae.balance <= i64::MAX - amount { + ae.balance + amount + } else { + return Err(self.err_general("balance overflowed")); + }; + if new_balance >= min_balance && new_balance <= max_balance { + ae.balance = new_balance; + storage.put(&lk, &le) + } else { + Err(self.err_general("invalid balance")) + } + }) + } + + fn transfer_trustline_balance( + &self, + account_id: Object, + asset_code: Object, + issuer: Object, + amount: i64, + ) -> Result<(), HostError> { + use xdr::TrustLineEntryExt; + + let lk = self.to_trustline_key(account_id, asset_code, issuer)?; + self.visit_storage(|storage| { + let mut le = storage.get(&lk)?; + let tl = match &mut le.data { + LedgerEntryData::Trustline(tl) => Ok(tl), + _ => Err(self.err_general("not trustline")), + }?; + if tl.balance < 0 { + return Err(self.err_general("balance is negative")); + } + + let base_reserve = self.with_ledger_info(|li| Ok(li.base_reserve))? as i64; + let (min_balance, max_balance) = if let TrustLineEntryExt::V1(ext1) = &tl.ext { + let min_balance = ext1.liabilities.selling; + let max_balance = i64::MAX - ext1.liabilities.buying; + (min_balance, max_balance) + } else { + let min_balance = 0; + let max_balance = i64::MAX; + (min_balance, max_balance) + }; + + let new_balance = if amount <= 0 { + tl.balance + amount + } else if tl.balance <= i64::MAX - amount { + tl.balance + amount + } else { + return Err(self.err_general("balance overflowed")); + }; + if new_balance >= min_balance && new_balance <= max_balance { + tl.balance = new_balance; + storage.put(&lk, &le) + } else { + Err(self.err_general("invalid balance")) + } + }) + } } impl EnvBase for Host { diff --git a/soroban-env-host/src/host/data_helper.rs b/soroban-env-host/src/host/data_helper.rs index 359906df7..b19c65b4a 100644 --- a/soroban-env-host/src/host/data_helper.rs +++ b/soroban-env-host/src/host/data_helper.rs @@ -1,11 +1,10 @@ use crate::xdr::{ - ContractDataEntry, HashIdPreimage, HashIdPreimageContractId, HashIdPreimageEd25519ContractId, - LedgerEntry, LedgerEntryData, LedgerEntryExt, LedgerKey, LedgerKeyAccount, - LedgerKeyContractData, ScContractCode, ScHostStorageErrorCode, ScHostValErrorCode, ScObject, - ScStatic, ScVal, + AccountEntry, AccountId, ContractDataEntry, Hash, HashIdPreimage, HashIdPreimageContractId, + HashIdPreimageEd25519ContractId, LedgerEntry, LedgerEntryData, LedgerEntryExt, LedgerKey, + LedgerKeyAccount, LedgerKeyContractData, LedgerKeyTrustLine, PublicKey, ScContractCode, + ScHostStorageErrorCode, ScHostValErrorCode, ScObject, ScStatic, ScVal, Uint256, WriteXdr, }; use crate::{Host, HostError, Object}; -use soroban_env_common::xdr::{AccountEntry, AccountId, Hash, PublicKey, Uint256, WriteXdr}; impl Host { pub fn contract_code_ledger_key(&self, contract_id: Hash) -> LedgerKey { @@ -85,13 +84,41 @@ impl Host { Ok(buf) } - pub fn load_account(&self, a: Object) -> Result { - let acc = LedgerKey::Account(LedgerKeyAccount { - account_id: AccountId(PublicKey::PublicKeyTypeEd25519(self.to_u256(a)?)), + pub fn load_account(&self, account_id: Object) -> Result { + let lk = LedgerKey::Account(LedgerKeyAccount { + account_id: AccountId(PublicKey::PublicKeyTypeEd25519(self.to_u256(account_id)?)), }); - self.visit_storage(|storage| match storage.get(&acc)?.data { + self.visit_storage(|storage| match storage.get(&lk)?.data { LedgerEntryData::Account(ae) => Ok(ae), _ => Err(self.err_general("not account")), }) } + + pub fn to_trustline_key( + &self, + account_id: 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().unwrap()), + issuer: AccountId(PublicKey::PublicKeyTypeEd25519(self.to_u256(issuer)?)), + })) + } else if b.len() > 0 && b.len() <= 12 { + Ok(TrustLineAsset::CreditAlphanum12(AlphaNum12 { + asset_code: AssetCode12(b.as_slice().try_into().unwrap()), + issuer: AccountId(PublicKey::PublicKeyTypeEd25519(self.to_u256(issuer)?)), + })) + } else { + Err(self.err_general("invalid asset code")) + } + })?; + Ok(LedgerKey::Trustline(LedgerKeyTrustLine { + account_id: AccountId(PublicKey::PublicKeyTypeEd25519(self.to_u256(account_id)?)), + asset, + })) + } } From cac8f66a9af74b799feebe920d2f38f9d3ea1b0e Mon Sep 17 00:00:00 2001 From: Jonathan Jove Date: Tue, 16 Aug 2022 14:37:14 -0500 Subject: [PATCH 2/7] Add push and append to Bytes --- .../src/native_contract/base_types.rs | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/soroban-env-host/src/native_contract/base_types.rs b/soroban-env-host/src/native_contract/base_types.rs index 1ef85df46..58859c95e 100644 --- a/soroban-env-host/src/native_contract/base_types.rs +++ b/soroban-env-host/src/native_contract/base_types.rs @@ -105,8 +105,35 @@ impl TryIntoVal for Bytes { } impl From for Object { - fn from(vec: Bytes) -> Self { - vec.0.val + fn from(b: Bytes) -> Self { + b.0.val + } +} + +impl From> for Bytes { + fn from(b: BytesN) -> Self { + Self(b.0) + } +} + +impl Bytes { + pub fn push(&mut self, x: u8) -> Result<(), HostError> { + let x32: u32 = x.into(); + self.0 = self + .0 + .env + .binary_push(self.0.val, x32.into())? + .in_env(&self.0.env); + Ok(()) + } + + pub fn append(&mut self, other: Bytes) -> Result<(), HostError> { + self.0 = self + .0 + .env + .binary_append(self.0.val, other.0.val)? + .in_env(&self.0.env); + Ok(()) } } From 517c43db4090e6796682025b3578f192712a11ad Mon Sep 17 00:00:00 2001 From: Jonathan Jove Date: Tue, 16 Aug 2022 14:37:43 -0500 Subject: [PATCH 3/7] Change metadata for token to support classic assets --- .../src/native_contract/token/contract.rs | 27 +++----- .../src/native_contract/token/metadata.rs | 63 ++++++++++++------- .../src/native_contract/token/public_types.rs | 33 +++++++++- .../native_contract/token/storage_types.rs | 4 +- 4 files changed, 79 insertions(+), 48 deletions(-) diff --git a/soroban-env-host/src/native_contract/token/contract.rs b/soroban-env-host/src/native_contract/token/contract.rs index 41543eb5c..6b29f89f8 100644 --- a/soroban-env-host/src/native_contract/token/contract.rs +++ b/soroban-env-host/src/native_contract/token/contract.rs @@ -10,21 +10,17 @@ use crate::native_contract::token::balance::{ use crate::native_contract::token::cryptography::{check_auth, Domain}; use crate::native_contract::token::error::Error; use crate::native_contract::token::metadata::{ - read_decimal, read_name, read_symbol, write_decimal, write_name, write_symbol, + read_decimal, read_name, read_symbol, write_metadata, }; use crate::native_contract::token::nonce::read_nonce; -use crate::native_contract::token::public_types::{Authorization, Identifier, KeyedAuthorization}; +use crate::native_contract::token::public_types::{ + Authorization, Identifier, KeyedAuthorization, Metadata, +}; use soroban_env_common::TryIntoVal; use soroban_native_sdk_macros::contractimpl; pub trait TokenTrait { - fn initialize( - e: &Host, - admin: Identifier, - decimal: u32, - name: Bytes, - symbol: Bytes, - ) -> Result<(), Error>; + fn initialize(e: &Host, admin: Identifier, metadata: Metadata) -> Result<(), Error>; fn nonce(e: &Host, id: Identifier) -> Result; @@ -77,21 +73,12 @@ pub struct Token; #[contractimpl] impl TokenTrait for Token { - fn initialize( - e: &Host, - admin: Identifier, - decimal: u32, - name: Bytes, - symbol: Bytes, - ) -> Result<(), Error> { + fn initialize(e: &Host, admin: Identifier, metadata: Metadata) -> Result<(), Error> { if has_administrator(&e)? { return Err(Error::ContractError); } write_administrator(&e, admin)?; - - write_decimal(&e, u8::try_from(decimal).map_err(|_| Error::ContractError)?)?; - write_name(&e, name)?; - write_symbol(&e, symbol)?; + write_metadata(&e, metadata)?; Ok(()) } diff --git a/soroban-env-host/src/native_contract/token/metadata.rs b/soroban-env-host/src/native_contract/token/metadata.rs index 99e496dec..6b80226f2 100644 --- a/soroban-env-host/src/native_contract/token/metadata.rs +++ b/soroban-env-host/src/native_contract/token/metadata.rs @@ -1,41 +1,56 @@ use crate::host::Host; use crate::native_contract::base_types::Bytes; use crate::native_contract::token::error::Error; +use crate::native_contract::token::public_types::Metadata; use crate::native_contract::token::storage_types::DataKey; -use soroban_env_common::{CheckedEnv, TryIntoVal}; +use soroban_env_common::{CheckedEnv, EnvBase, TryIntoVal}; -pub fn read_decimal(e: &Host) -> Result { - let key = DataKey::Decimals; - let rv = e.get_contract_data(key.try_into_val(e)?)?; - Ok(rv.try_into()?) -} - -pub fn write_decimal(e: &Host, d: u8) -> Result<(), Error> { - let key = DataKey::Decimals; - e.put_contract_data(key.try_into_val(e)?, u32::from(d).into())?; +pub fn write_metadata(e: &Host, metadata: Metadata) -> Result<(), Error> { + let key = DataKey::Metadata; + e.put_contract_data(key.try_into_val(e)?, metadata.try_into_val(e)?)?; Ok(()) } pub fn read_name(e: &Host) -> Result { - let key = DataKey::Name; + let key = DataKey::Metadata; let rv = e.get_contract_data(key.try_into_val(e)?)?; - Ok(rv.in_env(e).try_into()?) -} - -pub fn write_name(e: &Host, d: Bytes) -> Result<(), Error> { - let key = DataKey::Name; - e.put_contract_data(key.try_into_val(e)?, d.try_into_val(e)?)?; - Ok(()) + let metadata: Metadata = rv.in_env(e).try_into()?; + match metadata { + Metadata::Token(token) => Ok(token.name), + Metadata::Native => Ok(e.binary_new_from_slice(b"native").in_env(e).try_into()?), + Metadata::AlphaNum4(asset) => { + let mut res: Bytes = asset.asset_code.into(); + res.push(b':')?; + res.append(asset.issuer.into())?; + Ok(res) + } + Metadata::AlphaNum12(asset) => { + let mut res: Bytes = asset.asset_code.into(); + res.push(b':')?; + res.append(asset.issuer.into())?; + Ok(res) + } + } } pub fn read_symbol(e: &Host) -> Result { - let key = DataKey::Symbol; + let key = DataKey::Metadata; let rv = e.get_contract_data(key.try_into_val(e)?)?; - Ok(rv.in_env(e).try_into()?) + let metadata: Metadata = rv.in_env(e).try_into()?; + match metadata { + Metadata::Token(token) => Ok(token.symbol), + Metadata::Native => Ok(e.binary_new_from_slice(b"native").in_env(e).try_into()?), + Metadata::AlphaNum4(asset) => Ok(asset.asset_code.into()), + Metadata::AlphaNum12(asset) => Ok(asset.asset_code.into()), + } } -pub fn write_symbol(e: &Host, d: Bytes) -> Result<(), Error> { - let key = DataKey::Symbol; - e.put_contract_data(key.try_into_val(e)?, d.try_into_val(e)?)?; - Ok(()) +pub fn read_decimal(e: &Host) -> Result { + let key = DataKey::Metadata; + let rv = e.get_contract_data(key.try_into_val(e)?)?; + let metadata: Metadata = rv.in_env(e).try_into()?; + match metadata { + Metadata::Token(token) => Ok(token.decimals), + Metadata::Native | Metadata::AlphaNum4(_) | Metadata::AlphaNum12(_) => Ok(7), + } } 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 ddea3f30f..0740367e0 100644 --- a/soroban-env-host/src/native_contract/token/public_types.rs +++ b/soroban-env-host/src/native_contract/token/public_types.rs @@ -1,5 +1,5 @@ use crate::host::Host; -use crate::native_contract::base_types::{BigInt, BytesN, Map, Vec}; +use crate::native_contract::base_types::{BigInt, Bytes, BytesN, Map, Vec}; use crate::native_contract::token::error::Error; use soroban_env_common::{CheckedEnv, TryIntoVal}; use soroban_native_sdk_macros::contracttype; @@ -70,3 +70,34 @@ pub struct MessageV0 { pub enum Message { V0(MessageV0), } + +#[derive(Clone)] +#[contracttype] +pub struct TokenMetadata { + pub name: Bytes, + pub symbol: Bytes, + pub decimals: u32, +} + +#[derive(Clone)] +#[contracttype] +pub struct AlphaNum4Metadata { + pub asset_code: BytesN<4>, + pub issuer: BytesN<32>, +} + +#[derive(Clone)] +#[contracttype] +pub struct AlphaNum12Metadata { + pub asset_code: BytesN<12>, + pub issuer: BytesN<32>, +} + +#[derive(Clone)] +#[contracttype] +pub enum Metadata { + Token(TokenMetadata), + Native, + AlphaNum4(AlphaNum4Metadata), + AlphaNum12(AlphaNum12Metadata), +} diff --git a/soroban-env-host/src/native_contract/token/storage_types.rs b/soroban-env-host/src/native_contract/token/storage_types.rs index f4c6b457f..b402a3b97 100644 --- a/soroban-env-host/src/native_contract/token/storage_types.rs +++ b/soroban-env-host/src/native_contract/token/storage_types.rs @@ -16,7 +16,5 @@ pub enum DataKey { Nonce(Identifier), State(Identifier), Admin, - Decimals, - Name, - Symbol, + Metadata, } From 462b96ed1fbd51527a202f6d0f14758456f6ce90 Mon Sep 17 00:00:00 2001 From: Jonathan Jove Date: Tue, 16 Aug 2022 16:47:00 -0500 Subject: [PATCH 4/7] Implement to_smart and to_classic --- soroban-env-host/src/host.rs | 8 ++- .../src/native_contract/token/balance.rs | 25 +++++++++- .../src/native_contract/token/contract.rs | 50 ++++++++++++++++++- .../src/native_contract/token/cryptography.rs | 2 + .../src/native_contract/token/metadata.rs | 19 +++---- 5 files changed, 88 insertions(+), 16 deletions(-) diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 92092599d..831a508a6 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -695,7 +695,11 @@ impl Host { self.create_contract_with_id(contract_code, contract_id) } - fn transfer_account_balance(&self, account_id: Object, amount: i64) -> Result<(), HostError> { + pub(crate) fn transfer_account_balance( + &self, + account_id: Object, + amount: i64, + ) -> Result<(), HostError> { use xdr::{AccountEntryExt, AccountEntryExtensionV1Ext, LedgerKeyAccount}; let lk = LedgerKey::Account(LedgerKeyAccount { @@ -745,7 +749,7 @@ impl Host { }) } - fn transfer_trustline_balance( + pub(crate) fn transfer_trustline_balance( &self, account_id: Object, asset_code: Object, diff --git a/soroban-env-host/src/native_contract/token/balance.rs b/soroban-env-host/src/native_contract/token/balance.rs index 62a153bac..2eb6b32bf 100644 --- a/soroban-env-host/src/native_contract/token/balance.rs +++ b/soroban-env-host/src/native_contract/token/balance.rs @@ -1,7 +1,8 @@ use crate::host::Host; -use crate::native_contract::base_types::BigInt; +use crate::native_contract::base_types::{BigInt, BytesN}; use crate::native_contract::token::error::Error; -use crate::native_contract::token::public_types::Identifier; +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, TryIntoVal}; @@ -57,3 +58,23 @@ pub fn write_state(e: &Host, id: Identifier, is_frozen: bool) -> Result<(), Erro e.put_contract_data(key.try_into_val(e)?, is_frozen.into())?; Ok(()) } + +pub fn transfer_classic_balance(e: &Host, to_key: BytesN<32>, amount: i64) -> Result<(), Error> { + match read_metadata(e)? { + Metadata::Token(_) => return Err(Error::ContractError), + Metadata::Native => e.transfer_account_balance(to_key.into(), amount)?, + Metadata::AlphaNum4(asset) => e.transfer_trustline_balance( + to_key.into(), + asset.asset_code.into(), + asset.issuer.into(), + amount, + )?, + Metadata::AlphaNum12(asset) => e.transfer_trustline_balance( + to_key.into(), + asset.asset_code.into(), + asset.issuer.into(), + amount, + )?, + }; + Ok(()) +} diff --git a/soroban-env-host/src/native_contract/token/contract.rs b/soroban-env-host/src/native_contract/token/contract.rs index 6b29f89f8..3d49b745b 100644 --- a/soroban-env-host/src/native_contract/token/contract.rs +++ b/soroban-env-host/src/native_contract/token/contract.rs @@ -5,7 +5,7 @@ use crate::native_contract::token::admin::{ }; use crate::native_contract::token::allowance::{read_allowance, spend_allowance, write_allowance}; use crate::native_contract::token::balance::{ - read_balance, read_state, receive_balance, spend_balance, write_state, + read_balance, read_state, receive_balance, spend_balance, transfer_classic_balance, write_state, }; use crate::native_contract::token::cryptography::{check_auth, Domain}; use crate::native_contract::token::error::Error; @@ -67,6 +67,10 @@ pub trait TokenTrait { fn name(e: &Host) -> Result; fn symbol(e: &Host) -> Result; + + fn to_smart(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error>; + + fn to_classic(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error>; } pub struct Token; @@ -206,4 +210,48 @@ impl TokenTrait for Token { fn symbol(e: &Host) -> Result { read_symbol(&e) } + + fn to_smart(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error> { + if amount < 0 { + return Err(Error::ContractError); + } + + let id_key = match &id { + KeyedAuthorization::Account(acc) => Ok(acc.public_key.clone()), + _ => Err(Error::ContractError), + }?; + let mut args = Vec::new(e)?; + args.push(amount.clone())?; + check_auth(&e, id, Domain::ToSmart, args)?; + + transfer_classic_balance(e, id_key.clone(), amount)?; + receive_balance( + &e, + Identifier::Account(id_key), + BigInt::from_u64(&e, amount.try_into().map_err(|_| Error::ContractError)?)?, + )?; + Ok(()) + } + + fn to_classic(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error> { + if amount < 0 { + return Err(Error::ContractError); + } + + let id_key = match &id { + KeyedAuthorization::Account(acc) => Ok(acc.public_key.clone()), + _ => Err(Error::ContractError), + }?; + let mut args = Vec::new(e)?; + args.push(amount.clone())?; + check_auth(&e, id, Domain::ToClassic, args)?; + + transfer_classic_balance(e, id_key.clone(), -amount)?; + spend_balance( + &e, + Identifier::Account(id_key), + BigInt::from_u64(&e, amount.try_into().map_err(|_| Error::ContractError)?)?, + )?; + Ok(()) + } } diff --git a/soroban-env-host/src/native_contract/token/cryptography.rs b/soroban-env-host/src/native_contract/token/cryptography.rs index 214c87344..9a3c1404d 100644 --- a/soroban-env-host/src/native_contract/token/cryptography.rs +++ b/soroban-env-host/src/native_contract/token/cryptography.rs @@ -19,6 +19,8 @@ pub enum Domain { Mint = 5, SetAdministrator = 6, Unfreeze = 7, + ToSmart = 8, + ToClassic = 9, } fn check_ed25519_auth( diff --git a/soroban-env-host/src/native_contract/token/metadata.rs b/soroban-env-host/src/native_contract/token/metadata.rs index 6b80226f2..0f0a12def 100644 --- a/soroban-env-host/src/native_contract/token/metadata.rs +++ b/soroban-env-host/src/native_contract/token/metadata.rs @@ -11,11 +11,14 @@ pub fn write_metadata(e: &Host, metadata: Metadata) -> Result<(), Error> { Ok(()) } -pub fn read_name(e: &Host) -> Result { +pub fn read_metadata(e: &Host) -> Result { let key = DataKey::Metadata; let rv = e.get_contract_data(key.try_into_val(e)?)?; - let metadata: Metadata = rv.in_env(e).try_into()?; - match metadata { + Ok(rv.in_env(e).try_into()?) +} + +pub fn read_name(e: &Host) -> Result { + match read_metadata(e)? { Metadata::Token(token) => Ok(token.name), Metadata::Native => Ok(e.binary_new_from_slice(b"native").in_env(e).try_into()?), Metadata::AlphaNum4(asset) => { @@ -34,10 +37,7 @@ pub fn read_name(e: &Host) -> Result { } pub fn read_symbol(e: &Host) -> Result { - let key = DataKey::Metadata; - let rv = e.get_contract_data(key.try_into_val(e)?)?; - let metadata: Metadata = rv.in_env(e).try_into()?; - match metadata { + match read_metadata(e)? { Metadata::Token(token) => Ok(token.symbol), Metadata::Native => Ok(e.binary_new_from_slice(b"native").in_env(e).try_into()?), Metadata::AlphaNum4(asset) => Ok(asset.asset_code.into()), @@ -46,10 +46,7 @@ pub fn read_symbol(e: &Host) -> Result { } pub fn read_decimal(e: &Host) -> Result { - let key = DataKey::Metadata; - let rv = e.get_contract_data(key.try_into_val(e)?)?; - let metadata: Metadata = rv.in_env(e).try_into()?; - match metadata { + match read_metadata(e)? { Metadata::Token(token) => Ok(token.decimals), Metadata::Native | Metadata::AlphaNum4(_) | Metadata::AlphaNum12(_) => Ok(7), } From 4e91469489c74b7244eed22caf82bd2c459663ab Mon Sep 17 00:00:00 2001 From: Jonathan Jove Date: Tue, 16 Aug 2022 16:58:49 -0500 Subject: [PATCH 5/7] Add frame check to ensure only token can transfer classic balance --- soroban-env-host/src/host.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 831a508a6..aee449d4f 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -702,6 +702,11 @@ impl Host { ) -> Result<(), HostError> { use xdr::{AccountEntryExt, AccountEntryExtensionV1Ext, LedgerKeyAccount}; + 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: AccountId(PublicKey::PublicKeyTypeEd25519(self.to_u256(account_id)?)), }); @@ -758,6 +763,11 @@ impl Host { ) -> Result<(), HostError> { use xdr::TrustLineEntryExt; + self.with_current_frame(|frame| match frame { + Frame::Token(id) => Ok(()), + _ => Err(self.err_general("only native token can transfer classic balance")), + })?; + let lk = self.to_trustline_key(account_id, asset_code, issuer)?; self.visit_storage(|storage| { let mut le = storage.get(&lk)?; From 0528987f0985f7c45fbb91fa39e48a0731864699 Mon Sep 17 00:00:00 2001 From: Jonathan Jove Date: Thu, 18 Aug 2022 11:10:42 -0500 Subject: [PATCH 6/7] Add missing auth check for transfer_trustline_balance --- soroban-env-host/src/host.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index aee449d4f..df663922f 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -761,7 +761,7 @@ impl Host { issuer: Object, amount: i64, ) -> Result<(), HostError> { - use xdr::TrustLineEntryExt; + use xdr::{TrustLineEntryExt, TrustLineFlags}; self.with_current_frame(|frame| match frame { Frame::Token(id) => Ok(()), @@ -778,6 +778,9 @@ impl Host { if tl.balance < 0 { return Err(self.err_general("balance is negative")); } + if tl.flags & (TrustLineFlags::AuthorizedFlag as u32) == 0 { + return Err(self.err_general("not authorized")); + } let base_reserve = self.with_ledger_info(|li| Ok(li.base_reserve))? as i64; let (min_balance, max_balance) = if let TrustLineEntryExt::V1(ext1) = &tl.ext { From d09fef8a72371a1b39153591d054c4b108bfc585 Mon Sep 17 00:00:00 2001 From: Jonathan Jove Date: Tue, 13 Sep 2022 10:31:02 -0500 Subject: [PATCH 7/7] Fixes from merge --- soroban-env-host/src/host.rs | 5 +++-- .../src/native_contract/base_types.rs | 4 ++-- .../src/native_contract/token/contract.rs | 18 +++++++++--------- .../src/native_contract/token/metadata.rs | 13 ++++++++----- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index c97e57121..c55d67562 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -102,6 +102,7 @@ pub struct LedgerInfo { pub sequence_number: u32, pub timestamp: u64, pub network_passphrase: Vec, + pub base_reserve: u32, } #[derive(Clone, Default)] @@ -818,7 +819,7 @@ impl Host { use xdr::{AccountEntryExt, AccountEntryExtensionV1Ext, LedgerKeyAccount}; self.with_current_frame(|frame| match frame { - Frame::Token(id) => Ok(()), + Frame::Token(id, _) => Ok(()), _ => Err(self.err_general("only native token can transfer classic balance")), })?; @@ -879,7 +880,7 @@ impl Host { use xdr::{TrustLineEntryExt, TrustLineFlags}; self.with_current_frame(|frame| match frame { - Frame::Token(id) => Ok(()), + Frame::Token(id, _) => Ok(()), _ => Err(self.err_general("only native token can transfer classic balance")), })?; diff --git a/soroban-env-host/src/native_contract/base_types.rs b/soroban-env-host/src/native_contract/base_types.rs index b72eb67da..461862cf1 100644 --- a/soroban-env-host/src/native_contract/base_types.rs +++ b/soroban-env-host/src/native_contract/base_types.rs @@ -140,7 +140,7 @@ impl Bytes { self.0 = self .0 .env - .binary_push(self.0.val, x32.into())? + .bytes_push(self.0.val, x32.into())? .in_env(&self.0.env); Ok(()) } @@ -149,7 +149,7 @@ impl Bytes { self.0 = self .0 .env - .binary_append(self.0.val, other.0.val)? + .bytes_append(self.0.val, other.0.val)? .in_env(&self.0.env); Ok(()) } diff --git a/soroban-env-host/src/native_contract/token/contract.rs b/soroban-env-host/src/native_contract/token/contract.rs index 37bcfda4d..29c4a9327 100644 --- a/soroban-env-host/src/native_contract/token/contract.rs +++ b/soroban-env-host/src/native_contract/token/contract.rs @@ -11,7 +11,7 @@ use crate::native_contract::token::metadata::{ read_decimal, read_name, read_symbol, write_metadata, }; use crate::native_contract::token::nonce::read_nonce; -use crate::native_contract::token::public_types::{Identifier, Signature}; +use crate::native_contract::token::public_types::{Identifier, Metadata, Signature}; use soroban_env_common::{Symbol, TryIntoVal}; use soroban_native_sdk_macros::contractimpl; @@ -84,9 +84,9 @@ pub trait TokenTrait { fn symbol(e: &Host) -> Result; - fn to_smart(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error>; + fn to_smart(e: &Host, id: Signature, nonce: BigInt, amount: i64) -> Result<(), Error>; - fn to_classic(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error>; + fn to_classic(e: &Host, id: Signature, nonce: BigInt, amount: i64) -> Result<(), Error>; } pub struct Token; @@ -263,18 +263,18 @@ impl TokenTrait for Token { read_symbol(&e) } - fn to_smart(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error> { + fn to_smart(e: &Host, id: Signature, nonce: BigInt, amount: i64) -> Result<(), Error> { if amount < 0 { return Err(Error::ContractError); } let id_key = match &id { - KeyedAuthorization::Account(acc) => Ok(acc.public_key.clone()), + Signature::Account(acc) => Ok(acc.account_id.clone()), _ => Err(Error::ContractError), }?; let mut args = Vec::new(e)?; args.push(amount.clone())?; - check_auth(&e, id, Domain::ToSmart, args)?; + check_auth(&e, id, nonce, Symbol::from_str("to_smart"), args)?; transfer_classic_balance(e, id_key.clone(), amount)?; receive_balance( @@ -285,18 +285,18 @@ impl TokenTrait for Token { Ok(()) } - fn to_classic(e: &Host, id: KeyedAuthorization, amount: i64) -> Result<(), Error> { + fn to_classic(e: &Host, id: Signature, nonce: BigInt, amount: i64) -> Result<(), Error> { if amount < 0 { return Err(Error::ContractError); } let id_key = match &id { - KeyedAuthorization::Account(acc) => Ok(acc.public_key.clone()), + Signature::Account(acc) => Ok(acc.account_id.clone()), _ => Err(Error::ContractError), }?; let mut args = Vec::new(e)?; args.push(amount.clone())?; - check_auth(&e, id, Domain::ToClassic, args)?; + check_auth(&e, id, nonce, Symbol::from_str("to_classic"), args)?; transfer_classic_balance(e, id_key.clone(), -amount)?; spend_balance( diff --git a/soroban-env-host/src/native_contract/token/metadata.rs b/soroban-env-host/src/native_contract/token/metadata.rs index 03e8c215b..3469ba66c 100644 --- a/soroban-env-host/src/native_contract/token/metadata.rs +++ b/soroban-env-host/src/native_contract/token/metadata.rs @@ -3,7 +3,7 @@ use crate::native_contract::base_types::Bytes; use crate::native_contract::token::error::Error; use crate::native_contract::token::public_types::Metadata; use crate::native_contract::token::storage_types::DataKey; -use soroban_env_common::{CheckedEnv, EnvBase, TryIntoVal}; +use soroban_env_common::{CheckedEnv, EnvBase, TryFromVal, TryIntoVal}; pub fn write_metadata(e: &Host, metadata: Metadata) -> Result<(), Error> { let key = DataKey::Metadata; @@ -20,7 +20,7 @@ pub fn read_metadata(e: &Host) -> Result { pub fn read_name(e: &Host) -> Result { match read_metadata(e)? { Metadata::Token(token) => Ok(token.name), - Metadata::Native => Ok(e.binary_new_from_slice(b"native").in_env(e).try_into()?), + Metadata::Native => Ok(Bytes::try_from_val(e, e.bytes_new_from_slice(b"native"))?), Metadata::AlphaNum4(asset) => { let mut res: Bytes = asset.asset_code.into(); res.push(b':')?; @@ -37,9 +37,12 @@ pub fn read_name(e: &Host) -> Result { } pub fn read_symbol(e: &Host) -> Result { - let key = DataKey::Symbol; - let rv = e.get_contract_data(key.try_into_val(e)?)?; - Ok(rv.try_into_val(e)?) + match read_metadata(e)? { + Metadata::Token(token) => Ok(token.symbol), + Metadata::Native => Ok(Bytes::try_from_val(e, e.bytes_new_from_slice(b"native"))?), + Metadata::AlphaNum4(asset) => Ok(asset.asset_code.into()), + Metadata::AlphaNum12(asset) => Ok(asset.asset_code.into()), + } } pub fn read_decimal(e: &Host) -> Result {