Skip to content

Commit

Permalink
Add token events (#523)
Browse files Browse the repository at this point in the history
  • Loading branch information
jayz22 authored Oct 14, 2022
1 parent 7367f00 commit 72543d4
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 65 deletions.
50 changes: 4 additions & 46 deletions soroban-env-host/src/host/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use crate::xdr::{
};
use crate::{
budget::CostType,
events::{DebugError, CONTRACT_EVENT_TOPICS_LIMIT, TOPIC_BYTES_LENGTH_LIMIT},
events::{DebugError, CONTRACT_EVENT_TOPICS_LIMIT},
host_object::{HostObject, HostVec},
Host, HostError, Object, RawVal, Tag,
Host, HostError, Object, RawVal,
};
use ed25519_dalek::{PublicKey, Signature, SIGNATURE_LENGTH};
use num_bigint::Sign;
Expand Down Expand Up @@ -204,50 +204,8 @@ impl Host {
}

fn event_topic_from_rawval(&self, topic: RawVal) -> Result<ScVal, HostError> {
self.charge_budget(CostType::ValXdrConv, 1)?;
if topic.is_u63() {
Ok(ScVal::U63(unsafe { topic.unchecked_as_u63() }))
} else {
match topic.get_tag() {
Tag::Object => {
unsafe {
self.unchecked_visit_val_obj(topic, |ob| {
// charge budget
let sco = match ob {
None => Err(self.err_status(ScHostObjErrorCode::UnknownReference)),
Some(ho) => match ho {
// TODO: use more event-specific error codes than `UnexpectedType`
HostObject::Vec(_)
| HostObject::Map(_)
| HostObject::ContractCode(_) => {
Err(self.err_status(ScHostObjErrorCode::UnexpectedType))
}
HostObject::Bytes(b) => {
if b.len() > TOPIC_BYTES_LENGTH_LIMIT {
// TODO: use more event-specific error codes than `UnexpectedType`.
// Something like "topic bytes exceeds length limit"
return Err(
self.err_status(ScHostObjErrorCode::UnexpectedType)
);
}
self.charge_budget(CostType::BytesClone, b.len() as u64)?;
Ok(ScObject::Bytes(self.map_err(b.clone().try_into())?))
}
HostObject::U64(u) => Ok(ScObject::U64(*u)),
HostObject::I64(i) => Ok(ScObject::I64(*i)),
HostObject::BigInt(bi) => self.scobj_from_bigint(bi),
HostObject::AccountId(aid) => {
Ok(ScObject::AccountId(aid.metered_clone(&self.0.budget)?))
}
},
}?;
Ok(ScVal::Object(Some(sco)))
})
}
}
_ => self.from_host_val(topic),
}
}
self.validate_event_topic(topic)?;
self.from_host_val(topic)
}

pub(crate) fn event_topics_from_host_obj(&self, topics: Object) -> Result<ScVec, HostError> {
Expand Down
40 changes: 38 additions & 2 deletions soroban-env-host/src/host/validity.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::ops::Range;

use crate::events::DebugError;
use crate::events::{DebugError, TOPIC_BYTES_LENGTH_LIMIT};
use crate::xdr::{ScHostFnErrorCode, ScHostObjErrorCode};
use crate::{Host, HostError, RawVal};
use crate::{host_object::HostObject, Host, HostError, RawVal, Tag};

impl Host {
// Notes on metering: free
Expand Down Expand Up @@ -80,4 +80,40 @@ impl Host {
})?;
self.valid_range_from_start_end_bound(start, end, bound)
}

// Metering: covered by components
// TODO: the validation is incomplete. Need to further restrict Map, Vec sizes.
pub(crate) fn validate_event_topic(&self, topic: RawVal) -> Result<(), HostError> {
if topic.is_u63() {
Ok(())
} else {
match topic.get_tag() {
Tag::Object => {
unsafe {
self.unchecked_visit_val_obj(topic, |ob| {
match ob {
None => Err(self.err_status(ScHostObjErrorCode::UnknownReference)),
Some(ho) => match ho {
HostObject::ContractCode(_) => {
Err(self.err_status(ScHostObjErrorCode::UnexpectedType))
}
HostObject::Bytes(b) => {
if b.len() > TOPIC_BYTES_LENGTH_LIMIT {
// TODO: use more event-specific error codes than `UnexpectedType`.
// Something like "topic bytes exceeds length limit"
Err(self.err_status(ScHostObjErrorCode::UnexpectedType))
} else {
Ok(())
}
}
_ => Ok(()),
},
}
})
}
}
_ => Ok(()),
}
}
}
}
1 change: 1 addition & 0 deletions soroban-env-host/src/native_contract/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod balance;
mod contract;
mod cryptography;
mod error;
mod event;
mod metadata;
mod nonce;
pub(crate) mod public_types;
Expand Down
52 changes: 35 additions & 17 deletions soroban-env-host/src/native_contract/token/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::native_contract::token::balance::{
};
use crate::native_contract::token::cryptography::check_auth;
use crate::native_contract::token::error::Error;
use crate::native_contract::token::event;
use crate::native_contract::token::metadata::{
has_metadata, read_decimal, read_name, read_symbol, write_metadata,
};
Expand Down Expand Up @@ -202,7 +203,8 @@ impl TokenTrait for Token {
args.push(spender.clone())?;
args.push(amount.clone())?;
check_auth(&e, from, nonce, Symbol::from_str("approve"), args)?;
write_allowance(&e, from_id, spender, amount)?;
write_allowance(&e, from_id.clone(), spender.clone(), amount.clone())?;
event::approve(e, from_id, spender, amount)?;
Ok(())
}

Expand Down Expand Up @@ -234,8 +236,9 @@ impl TokenTrait for Token {
args.push(to.clone())?;
args.push(amount.clone())?;
check_auth(&e, from, nonce, Symbol::from_str("xfer"), args)?;
spend_balance(&e, from_id, amount.clone())?;
receive_balance(&e, to, amount)?;
spend_balance(&e, from_id.clone(), amount.clone())?;
receive_balance(&e, to.clone(), amount.clone())?;
event::transfer(e, from_id, to, amount)?;
Ok(())
}

Expand All @@ -260,8 +263,9 @@ impl TokenTrait for Token {
args.push(amount.clone())?;
check_auth(&e, spender, nonce, Symbol::from_str("xfer_from"), args)?;
spend_allowance(&e, from.clone(), spender_id, amount.clone())?;
spend_balance(&e, from, amount.clone())?;
receive_balance(&e, to, amount)?;
spend_balance(&e, from.clone(), amount.clone())?;
receive_balance(&e, to.clone(), amount.clone())?;
event::transfer(e, from, to, amount)?;
Ok(())
}

Expand All @@ -278,24 +282,28 @@ impl TokenTrait for Token {
}
check_admin(&e, &admin)?;
let mut args = Vec::new(e)?;
args.push(admin.get_identifier(&e)?)?;
let admin_id = admin.get_identifier(&e)?;
args.push(admin_id.clone())?;
args.push(nonce.clone())?;
args.push(from.clone())?;
args.push(amount.clone())?;
check_auth(&e, admin, nonce, Symbol::from_str("burn"), args)?;
spend_balance(&e, from, amount)?;
spend_balance(&e, from.clone(), amount.clone())?;
event::burn(e, admin_id, from, amount)?;
Ok(())
}

// Metering: covered by components
fn freeze(e: &Host, admin: Signature, nonce: BigInt, id: Identifier) -> Result<(), Error> {
check_admin(&e, &admin)?;
let mut args = Vec::new(e)?;
args.push(admin.get_identifier(&e)?)?;
let admin_id = admin.get_identifier(&e)?;
args.push(admin_id.clone())?;
args.push(nonce.clone())?;
args.push(id.clone())?;
check_auth(&e, admin, nonce, Symbol::from_str("freeze"), args)?;
write_state(&e, id, true)?;
write_state(&e, id.clone(), true)?;
event::freeze(e, admin_id, id)?;
Ok(())
}

Expand All @@ -312,12 +320,14 @@ impl TokenTrait for Token {
}
check_admin(&e, &admin)?;
let mut args = Vec::new(e)?;
args.push(admin.get_identifier(&e)?)?;
let admin_id = admin.get_identifier(&e)?;
args.push(admin_id.clone())?;
args.push(nonce.clone())?;
args.push(to.clone())?;
args.push(amount.clone())?;
check_auth(&e, admin, nonce, Symbol::from_str("mint"), args)?;
receive_balance(&e, to, amount)?;
receive_balance(&e, to.clone(), amount.clone())?;
event::mint(e, admin_id, to, amount)?;
Ok(())
}

Expand All @@ -330,23 +340,27 @@ impl TokenTrait for Token {
) -> Result<(), Error> {
check_admin(&e, &admin)?;
let mut args = Vec::new(e)?;
args.push(admin.get_identifier(&e)?)?;
let admin_id = admin.get_identifier(&e)?;
args.push(admin_id.clone())?;
args.push(nonce.clone())?;
args.push(new_admin.clone())?;
check_auth(&e, admin, nonce, Symbol::from_str("set_admin"), args)?;
write_administrator(&e, new_admin)?;
write_administrator(&e, new_admin.clone())?;
event::set_admin(e, admin_id, new_admin)?;
Ok(())
}

// Metering: covered by components
fn unfreeze(e: &Host, admin: Signature, nonce: BigInt, id: Identifier) -> Result<(), Error> {
check_admin(&e, &admin)?;
let mut args = Vec::new(e)?;
args.push(admin.get_identifier(&e)?)?;
let admin_id = admin.get_identifier(&e)?;
args.push(admin_id.clone())?;
args.push(nonce.clone())?;
args.push(id.clone())?;
check_auth(&e, admin, nonce, Symbol::from_str("unfreeze"), args)?;
write_state(&e, id, false)?;
write_state(&e, id.clone(), false)?;
event::unfreeze(e, admin_id, id)?;
Ok(())
}

Expand All @@ -371,7 +385,8 @@ impl TokenTrait for Token {
let account_id = id.get_account_id(e)?;

let mut args = Vec::new(e)?;
args.push(id.get_identifier(&e)?)?;
let ident = id.get_identifier(&e)?;
args.push(ident.clone())?;
args.push(nonce.clone())?;
args.push(amount.clone())?;
check_auth(&e, id, nonce, Symbol::from_str("import"), args)?;
Expand All @@ -382,6 +397,7 @@ impl TokenTrait for Token {
Identifier::Account(account_id),
BigInt::from_u64(&e, amount.try_into().map_err(|_| Error::ContractError)?)?,
)?;
event::import(e, ident, amount)?;
Ok(())
}

Expand All @@ -394,7 +410,8 @@ impl TokenTrait for Token {
let account_id = id.get_account_id(e)?;

let mut args = Vec::new(e)?;
args.push(id.get_identifier(&e)?)?;
let ident = id.get_identifier(&e)?;
args.push(ident.clone())?;
args.push(nonce.clone())?;
args.push(amount.clone())?;
check_auth(&e, id, nonce, Symbol::from_str("export"), args)?;
Expand All @@ -405,6 +422,7 @@ impl TokenTrait for Token {
Identifier::Account(account_id),
BigInt::from_u64(&e, amount.try_into().map_err(|_| Error::ContractError)?)?,
)?;
event::export(e, ident, amount)?;
Ok(())
}
}
101 changes: 101 additions & 0 deletions soroban-env-host/src/native_contract/token/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use crate::host::Host;
use crate::native_contract::base_types::{BigInt, Vec};
use crate::native_contract::token::error::Error;
use crate::native_contract::token::public_types::Identifier;
use soroban_env_common::{CheckedEnv, Symbol, TryIntoVal};

pub(crate) fn approve(
e: &Host,
from: Identifier,
to: Identifier,
amount: BigInt,
) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("approve"))?;
topics.push(from)?;
topics.push(to)?;
e.contract_event(topics.into(), amount.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn transfer(
e: &Host,
from: Identifier,
to: Identifier,
amount: BigInt,
) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("transfer"))?;
topics.push(from)?;
topics.push(to)?;
e.contract_event(topics.into(), amount.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn mint(
e: &Host,
admin: Identifier,
to: Identifier,
amount: BigInt,
) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("mint"))?;
topics.push(admin)?;
topics.push(to)?;
e.contract_event(topics.into(), amount.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn burn(
e: &Host,
admin: Identifier,
from: Identifier,
amount: BigInt,
) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("burn"))?;
topics.push(admin)?;
topics.push(from)?;
e.contract_event(topics.into(), amount.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn freeze(e: &Host, admin: Identifier, id: Identifier) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("freeze"))?;
topics.push(admin)?;
e.contract_event(topics.into(), id.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn unfreeze(e: &Host, admin: Identifier, id: Identifier) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("unfreeze"))?;
topics.push(admin)?;
e.contract_event(topics.into(), id.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn set_admin(e: &Host, admin: Identifier, new_admin: Identifier) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("set_admin"))?;
topics.push(admin)?;
e.contract_event(topics.into(), new_admin.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn import(e: &Host, id: Identifier, amount: i64) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("import"))?;
topics.push(id)?;
e.contract_event(topics.into(), amount.try_into_val(e)?)?;
Ok(())
}

pub(crate) fn export(e: &Host, id: Identifier, amount: i64) -> Result<(), Error> {
let mut topics = Vec::new(e)?;
topics.push(Symbol::from_str("export"))?;
topics.push(id)?;
e.contract_event(topics.into(), amount.try_into_val(e)?)?;
Ok(())
}

0 comments on commit 72543d4

Please sign in to comment.