Skip to content

Commit

Permalink
Merge branch 'master' into test/helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
arajasek authored Oct 14, 2022
2 parents 183719b + 2e0ace8 commit e799b00
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 5 deletions.
2 changes: 1 addition & 1 deletion actors/datacap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub const DATACAP_GRANULARITY: u64 = TOKEN_PRECISION as u64;

lazy_static! {
// > 800 EiB
static ref INFINITE_ALLOWANCE: TokenAmount = TokenAmount::from_atto(
pub static ref INFINITE_ALLOWANCE: TokenAmount = TokenAmount::from_atto(
BigInt::from(TOKEN_PRECISION)
* BigInt::from(1_000_000_000_000_000_000_000_i128)
);
Expand Down
106 changes: 104 additions & 2 deletions actors/datacap/tests/datacap_actor_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ mod mint {
use fvm_shared::error::ExitCode;
use fvm_shared::MethodNum;

use fil_actor_datacap::{Actor, Method, MintParams};
use fil_actor_datacap::{Actor, Method, MintParams, INFINITE_ALLOWANCE};
use fil_actors_runtime::cbor::serialize;
use fil_actors_runtime::test_utils::{expect_abort_contains_message, MARKET_ACTOR_CODE_ID};
use fil_actors_runtime::{STORAGE_MARKET_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR};
use fvm_ipld_encoding::RawBytes;
use std::ops::Sub;

use crate::*;

Expand Down Expand Up @@ -87,12 +89,40 @@ mod mint {
);
h.check_state(&rt);
}

#[test]
fn auto_allowance_on_mint() {
let (mut rt, h) = make_harness();
let amt = TokenAmount::from_whole(42);
h.mint(&mut rt, &*ALICE, &amt, vec![*BOB]).unwrap();
let allowance = h.get_allowance_between(&rt, &*ALICE, &*BOB);
assert!(allowance.eq(&INFINITE_ALLOWANCE));

// mint again
h.mint(&mut rt, &*ALICE, &amt, vec![*BOB]).unwrap();
let allowance2 = h.get_allowance_between(&rt, &*ALICE, &*BOB);
assert!(allowance2.eq(&INFINITE_ALLOWANCE));

// transfer of an allowance *does* deduct allowance even though it is too small to matter in practice
let operator_data = RawBytes::new(vec![1, 2, 3, 4]);
h.transfer_from(&mut rt, &*BOB, &*ALICE, &h.governor, &(2 * amt.clone()), operator_data)
.unwrap();
let allowance3 = h.get_allowance_between(&rt, &*ALICE, &*BOB);
assert!(allowance3.eq(&INFINITE_ALLOWANCE.clone().sub(2 * amt.clone())));

// minting any amount to this address at the same operator resets at infinite
h.mint(&mut rt, &*ALICE, &TokenAmount::from_whole(1), vec![*BOB]).unwrap();
let allowance = h.get_allowance_between(&rt, &*ALICE, &*BOB);
assert!(allowance.eq(&INFINITE_ALLOWANCE));

h.check_state(&rt);
}
}

mod transfer {
// Tests for the specific transfer restrictions of the datacap token.

use crate::{make_harness, ALICE, BOB};
use crate::{make_harness, ALICE, BOB, CARLA};
use fil_actors_runtime::test_utils::expect_abort_contains_message;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::econ::TokenAmount;
Expand All @@ -119,6 +149,78 @@ mod transfer {
// The governor can transfer out.
h.transfer(&mut rt, &h.governor, &*BOB, &amt, operator_data).unwrap();
}

#[test]
fn transfer_from_restricted() {
let (mut rt, h) = make_harness();
let operator_data = RawBytes::new(vec![1, 2, 3, 4]);

let amt = TokenAmount::from_whole(1);
h.mint(&mut rt, &*ALICE, &amt, vec![*BOB]).unwrap();

// operator can't transfer out to third address
expect_abort_contains_message(
ExitCode::USR_FORBIDDEN,
"transfer not allowed",
h.transfer_from(&mut rt, &*BOB, &*ALICE, &*CARLA, &amt, operator_data.clone()),
);
rt.reset();

// operator can't transfer out to self
expect_abort_contains_message(
ExitCode::USR_FORBIDDEN,
"transfer not allowed",
h.transfer_from(&mut rt, &*BOB, &*ALICE, &*BOB, &amt, operator_data.clone()),
);
rt.reset();
// even if governor has a delegate operator and enough tokens, delegated transfer
// cannot send to non governor
h.mint(&mut rt, &h.governor, &amt, vec![*BOB]).unwrap();
expect_abort_contains_message(
ExitCode::USR_FORBIDDEN,
"transfer not allowed",
h.transfer_from(&mut rt, &*BOB, &h.governor, &*ALICE, &amt, operator_data),
);
rt.reset();
}
}

mod destroy {
use crate::{make_harness, ALICE, BOB};
use fil_actor_datacap::DestroyParams;
use fil_actors_runtime::test_utils::{expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID};
use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR;
use fvm_shared::econ::TokenAmount;
use fvm_shared::MethodNum;

use fil_actor_datacap::{Actor, Method};
use fil_actors_runtime::cbor::serialize;
use fvm_shared::error::ExitCode;

#[test]
fn only_governor_allowed() {
let (mut rt, h) = make_harness();

let amt = TokenAmount::from_whole(1);
h.mint(&mut rt, &*ALICE, &(2 * amt.clone()), vec![*BOB]).unwrap();

// destroying from operator does not work
let params = DestroyParams { owner: *ALICE, amount: amt.clone() };

rt.expect_validate_caller_addr(vec![VERIFIED_REGISTRY_ACTOR_ADDR]);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *BOB);
expect_abort_contains_message(
ExitCode::USR_FORBIDDEN,
"caller address",
rt.call::<Actor>(Method::Destroy as MethodNum, &serialize(&params, "params").unwrap()),
);

// Destroying from 0 allowance having governor works
assert!(h.get_allowance_between(&rt, &*ALICE, &h.governor).is_zero());
let ret = h.destroy(&mut rt, &*ALICE, &amt).unwrap();
assert_eq!(ret.balance, amt); // burned 2 amt - amt = amt
h.check_state(&rt)
}
}

fn make_harness() -> (MockRuntime, Harness) {
Expand Down
85 changes: 83 additions & 2 deletions actors/datacap/tests/harness/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use frc46_token::receiver::types::{FRC46TokenReceived, UniversalReceiverParams, FRC46_TOKEN_TYPE};
use frc46_token::token::types::{MintReturn, TransferParams, TransferReturn};
use frc46_token::token::types::{
BurnReturn, MintReturn, TransferFromParams, TransferFromReturn, TransferParams, TransferReturn,
};
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
use fvm_shared::econ::TokenAmount;
Expand All @@ -8,7 +10,7 @@ use fvm_shared::MethodNum;
use num_traits::Zero;

use fil_actor_datacap::testing::check_state_invariants;
use fil_actor_datacap::{Actor as DataCapActor, Method, MintParams, State};
use fil_actor_datacap::{Actor as DataCapActor, DestroyParams, Method, MintParams, State};
use fil_actors_runtime::cbor::serialize;
use fil_actors_runtime::runtime::Runtime;
use fil_actors_runtime::test_utils::*;
Expand Down Expand Up @@ -97,6 +99,24 @@ impl Harness {
Ok(ret.deserialize().unwrap())
}

pub fn destroy(
&self,
rt: &mut MockRuntime,
owner: &Address,
amount: &TokenAmount,
) -> Result<BurnReturn, ActorError> {
rt.expect_validate_caller_addr(vec![VERIFIED_REGISTRY_ACTOR_ADDR]);

let params = DestroyParams { owner: *owner, amount: amount.clone() };

rt.set_caller(*VERIFREG_ACTOR_CODE_ID, VERIFIED_REGISTRY_ACTOR_ADDR);
let ret =
rt.call::<DataCapActor>(Method::Destroy as MethodNum, &serialize(&params, "params")?)?;

rt.verify();
Ok(ret.deserialize().unwrap())
}

pub fn transfer(
&self,
rt: &mut MockRuntime,
Expand Down Expand Up @@ -141,6 +161,54 @@ impl Harness {
Ok(ret.deserialize().unwrap())
}

pub fn transfer_from(
&self,
rt: &mut MockRuntime,
operator: &Address,
from: &Address,
to: &Address,
amount: &TokenAmount,
operator_data: RawBytes,
) -> Result<TransferFromReturn, ActorError> {
rt.expect_validate_caller_any();
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *operator);

// Expect the token receiver hook to be called.
let hook_params = UniversalReceiverParams {
type_: FRC46_TOKEN_TYPE,
payload: serialize(
&FRC46TokenReceived {
from: from.id().unwrap(),
to: to.id().unwrap(),
operator: operator.id().unwrap(),
amount: amount.clone(),
operator_data: operator_data.clone(),
token_data: Default::default(),
},
"hook payload",
)?,
};
// UniversalReceiverParams
rt.expect_send(
*to,
frc42_dispatch::method_hash!("Receive"),
serialize(&hook_params, "hook params")?,
TokenAmount::zero(),
RawBytes::default(),
ExitCode::OK,
);

let params =
TransferFromParams { to: *to, from: *from, amount: amount.clone(), operator_data };
let ret = rt.call::<DataCapActor>(
Method::TransferFrom as MethodNum,
&serialize(&params, "params")?,
)?;

rt.verify();
Ok(ret.deserialize().unwrap())
}

// Reads the total supply from state directly.
pub fn get_supply(&self, rt: &MockRuntime) -> TokenAmount {
rt.get_state::<State>().token.supply
Expand All @@ -151,6 +219,19 @@ impl Harness {
rt.get_state::<State>().token.get_balance(rt.store(), address.id().unwrap()).unwrap()
}

// Reads allowance from state directly
pub fn get_allowance_between(
&self,
rt: &MockRuntime,
owner: &Address,
operator: &Address,
) -> TokenAmount {
rt.get_state::<State>()
.token
.get_allowance_between(rt.store(), owner.id().unwrap(), operator.id().unwrap())
.unwrap()
}

pub fn check_state(&self, rt: &MockRuntime) {
let (_, acc) = check_state_invariants(&rt.get_state(), rt.store());
acc.assert_empty();
Expand Down

0 comments on commit e799b00

Please sign in to comment.