Skip to content

Commit

Permalink
Automatically approve built-in market as operator for all minted datacap
Browse files Browse the repository at this point in the history
  • Loading branch information
anorth committed Sep 7, 2022
1 parent 4033db9 commit 6076bcf
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 34 deletions.
14 changes: 12 additions & 2 deletions actors/datacap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ impl Actor {
}

/// Mints new data cap tokens for an address (a verified client).
/// Simultaneously increases the operator allowance by the same amount for specified addresses.
/// Only the registry can call this method.
/// This method is not part of the fungible token standard.
// TODO: return the new balance when the token library does
Expand All @@ -152,15 +153,24 @@ impl Actor {
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
// Mint tokens "from" the operator to the beneficiary.
token
let hook_params = token
.mint(
&operator,
&params.to,
&params.amount,
RawBytes::default(),
RawBytes::default(),
)
.actor_result()
.actor_result();

// Increase allowance for any specified operators.
for delegate in &params.operators {
token
.increase_allowance(&params.to, delegate, &params.amount)
.actor_result()?;
}

hook_params
})
.context("state transaction failed")?;

Expand Down
11 changes: 8 additions & 3 deletions actors/datacap/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
use fvm_ipld_encoding::tuple::*;
use fvm_ipld_encoding::Cbor;
use fvm_shared::address::Address;
use fvm_shared::bigint::{bigint_ser, BigInt};
use fvm_shared::bigint::bigint_ser;
use fvm_shared::econ::TokenAmount;

#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)]
pub struct MintParams {
// Recipient of the newly minted tokens.
pub to: Address,
// Amount of tokens to mint.
#[serde(with = "bigint_ser")]
pub amount: BigInt,
pub amount: TokenAmount,
// Addresses to be granted operator allowance for the newly minted tokens.
pub operators: Vec<Address>,
}

impl Cbor for MintParams {}
Expand All @@ -16,7 +21,7 @@ impl Cbor for MintParams {}
pub struct DestroyParams {
pub owner: Address,
#[serde(with = "bigint_ser")]
pub amount: BigInt,
pub amount: TokenAmount,
}

impl Cbor for DestroyParams {}
102 changes: 93 additions & 9 deletions actors/datacap/tests/datacap_actor_test.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,111 @@
use fvm_shared::address::Address;
use lazy_static::lazy_static;

use fil_actors_runtime::test_utils::MockRuntime;
use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR;

use crate::harness::{new_runtime, Harness};

mod harness;

lazy_static! {
static ref VERIFIER: Address = Address::new_id(201);
static ref VERIFIER2: Address = Address::new_id(202);
static ref CLIENT: Address = Address::new_id(301);
static ref CLIENT2: Address = Address::new_id(302);
static ref CLIENT3: Address = Address::new_id(303);
static ref CLIENT4: Address = Address::new_id(304);
static ref ALICE: Address = Address::new_id(101);
static ref BOB: Address = Address::new_id(102);
static ref CARLA: Address = Address::new_id(103);
}

mod construction {
use harness::*;

use crate::*;
use fil_actors_runtime::VERIFIED_REGISTRY_ACTOR_ADDR;

#[test]
fn construct_with_verified() {
let mut rt = new_runtime();
let h = Harness { registry: *REGISTRY_ADDR };
let h = Harness { registry: *VERIFIED_REGISTRY_ACTOR_ADDR };
h.construct_and_verify(&mut rt, &h.registry);
h.check_state(&rt);
}
}

mod mint {
use fil_fungible_token::token::TOKEN_PRECISION;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use fvm_shared::MethodNum;

use fil_actor_datacap::{Actor, Method, MintParams};
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 crate::*;

#[test]
fn mint_balances() {
// The token library has far more extensive tests, this is just a sanity check.
let (mut rt, h) = make_harness();

let amt = TokenAmount::from(TOKEN_PRECISION);
h.mint(&mut rt, &*ALICE, &amt, vec![]).unwrap();
assert_eq!(amt, h.get_supply(&rt));
assert_eq!(amt, h.get_balance(&rt, &*ALICE));

h.mint(&mut rt, &*BOB, &amt, vec![]).unwrap();
assert_eq!(&amt * 2, h.get_supply(&rt));
assert_eq!(amt, h.get_balance(&rt, &*BOB));

h.check_state(&rt);
}

#[test]
fn requires_verifreg_caller() {
let (mut rt, _) = make_harness();
let amt = TokenAmount::from(TOKEN_PRECISION);
let params = MintParams { to: *ALICE, amount: amt, operators: vec![] };

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

#[test]
fn requires_whole_tokens() {
let (mut rt, h) = make_harness();
let amt = TokenAmount::from(100);
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"must be a multiple of 1000000000000000000",
h.mint(&mut rt, &*ALICE, &amt, vec![]),
);
}

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

let amt = TokenAmount::from(TOKEN_PRECISION);
h.mint(&mut rt, &*ALICE, &amt, vec![*BOB]).unwrap();
assert_eq!(amt, h.get_allowance(&rt, &*ALICE, &BOB));

h.mint(&mut rt, &*ALICE, &amt, vec![*BOB]).unwrap();
assert_eq!(&amt * 2, h.get_allowance(&rt, &*ALICE, &BOB));

// Different operator
h.mint(&mut rt, &*ALICE, &amt, vec![*CARLA]).unwrap();
assert_eq!(&amt * 2, h.get_allowance(&rt, &*ALICE, &BOB));
assert_eq!(amt, h.get_allowance(&rt, &*ALICE, &CARLA));

h.check_state(&rt);
}
}

fn make_harness() -> (MockRuntime, Harness) {
let mut rt = new_runtime();
let h = Harness { registry: *VERIFIED_REGISTRY_ACTOR_ADDR };
h.construct_and_verify(&mut rt, &h.registry);
(rt, h)
}
82 changes: 72 additions & 10 deletions actors/datacap/tests/harness/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
use fil_fungible_token::receiver::types::TokensReceivedParams;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use fvm_shared::MethodNum;
use lazy_static::lazy_static;
use num_traits::Zero;

use fil_actor_datacap::testing::check_state_invariants;
use fil_actor_datacap::{Actor as DataCapActor, Method, State};
use fil_actor_datacap::{Actor as DataCapActor, Method, MintParams, State};
use fil_actors_runtime::cbor::serialize;
use fil_actors_runtime::runtime::Runtime;
use fil_actors_runtime::test_utils::*;
use fil_actors_runtime::{SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR};

lazy_static! {
pub static ref RECEIVER_ADDR: Address = Address::new_id(10);
pub static ref REGISTRY_ADDR: Address = *VERIFIED_REGISTRY_ACTOR_ADDR;
}
use fil_actors_runtime::{
ActorError, DATACAP_TOKEN_ACTOR_ADDR, FUNGIBLE_TOKEN_RECEIVER_HOOK_METHOD_NUM,
SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR,
};

pub fn new_runtime() -> MockRuntime {
MockRuntime {
receiver: *RECEIVER_ADDR,
receiver: *DATACAP_TOKEN_ACTOR_ADDR,
caller: *SYSTEM_ACTOR_ADDR,
caller_type: *SYSTEM_ACTOR_CODE_ID,
..Default::default()
Expand All @@ -26,7 +28,7 @@ pub fn new_runtime() -> MockRuntime {
#[allow(dead_code)]
pub fn new_harness() -> (Harness, MockRuntime) {
let mut rt = new_runtime();
let h = Harness { registry: *REGISTRY_ADDR };
let h = Harness { registry: *VERIFIED_REGISTRY_ACTOR_ADDR };
h.construct_and_verify(&mut rt, &h.registry);
(h, rt)
}
Expand All @@ -52,6 +54,66 @@ impl Harness {
assert_eq!(self.registry, state.registry);
}

pub fn mint(
&self,
rt: &mut MockRuntime,
to: &Address,
amount: &TokenAmount,
operators: Vec<Address>,
) -> Result<(), ActorError> {
rt.expect_validate_caller_addr(vec![*VERIFIED_REGISTRY_ACTOR_ADDR]);

// Expect the token receiver hook to be called.
let hook_params = TokensReceivedParams {
from: DATACAP_TOKEN_ACTOR_ADDR.id().unwrap(),
to: to.id().unwrap(),
operator: VERIFIED_REGISTRY_ACTOR_ADDR.id().unwrap(),
amount: amount.clone(),
operator_data: Default::default(),
token_data: Default::default(),
};
rt.expect_send(
*to,
FUNGIBLE_TOKEN_RECEIVER_HOOK_METHOD_NUM,
serialize(&hook_params, "hook params")?,
TokenAmount::zero(),
RawBytes::default(),
ExitCode::OK,
);

let params = MintParams { to: *to, amount: amount.clone(), operators };
rt.set_caller(*VERIFREG_ACTOR_CODE_ID, *VERIFIED_REGISTRY_ACTOR_ADDR);
let ret =
rt.call::<DataCapActor>(Method::Mint as MethodNum, &serialize(&params, "params")?)?;

assert_eq!(RawBytes::default(), ret);
rt.verify();
Ok(())
}

// Reads the total supply from state directly.
pub fn get_supply(&self, rt: &MockRuntime) -> TokenAmount {
rt.get_state::<State>().token.supply
}

// Reads a balance from state directly.
pub fn get_balance(&self, rt: &MockRuntime, address: &Address) -> TokenAmount {
rt.get_state::<State>().token.get_balance(rt.store(), address.id().unwrap()).unwrap()
}

// Reads an allowance from state directly.
pub fn get_allowance(
&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
1 change: 1 addition & 0 deletions actors/verifreg/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub mod datacap {
pub to: Address,
#[serde(with = "bigint_ser")]
pub amount: BigInt,
pub operators: Vec<Address>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)]
Expand Down
9 changes: 6 additions & 3 deletions actors/verifreg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ impl Actor {
})?;

// Credit client token allowance.
mint(rt, &st.token, &client, &params.allowance).context(format!(
let operators = vec![*STORAGE_MARKET_ACTOR_ADDR];
mint(rt, &st.token, &client, &params.allowance, operators).context(format!(
"failed to mint {} data cap to client {}",
&params.allowance, client
))?;
Expand Down Expand Up @@ -287,7 +288,8 @@ impl Actor {
));
}

mint(rt, &st.token, &client, &params.deal_size).context(format!(
let operators = vec![*STORAGE_MARKET_ACTOR_ADDR];
mint(rt, &st.token, &client, &params.deal_size, operators).context(format!(
"failed to restore {} to allowance for {}",
&params.deal_size, &client
))
Expand Down Expand Up @@ -585,13 +587,14 @@ fn mint<BS, RT>(
token: &Address,
to: &Address,
amount: &DataCap,
operators: Vec<Address>,
) -> Result<(), ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
let token_amt = datacap_to_tokens(amount);
let params = MintParams { to: *to, amount: token_amt };
let params = MintParams { to: *to, amount: token_amt, operators };
rt.send(
*token,
ext::datacap::Method::Mint as u64,
Expand Down
14 changes: 10 additions & 4 deletions actors/verifreg/tests/harness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,11 @@ impl Harness {
let client_resolved = rt.get_id_address(client).unwrap_or(*client);

// Expect tokens to be minted.
let mint_params =
ext::datacap::MintParams { to: client_resolved, amount: allowance * TOKEN_PRECISION };
let mint_params = ext::datacap::MintParams {
to: client_resolved,
amount: allowance * TOKEN_PRECISION,
operators: vec![*STORAGE_MARKET_ACTOR_ADDR],
};
rt.expect_send(
*DATACAP_TOKEN_ACTOR_ADDR,
ext::datacap::Method::Mint as MethodNum,
Expand Down Expand Up @@ -245,8 +248,11 @@ impl Harness {
let client_resolved = rt.get_id_address(client).unwrap_or(*client);

// Expect tokens to be minted.
let mint_params =
ext::datacap::MintParams { to: client_resolved, amount: amount * TOKEN_PRECISION };
let mint_params = ext::datacap::MintParams {
to: client_resolved,
amount: amount * TOKEN_PRECISION,
operators: vec![*STORAGE_MARKET_ACTOR_ADDR],
};
rt.expect_send(
*DATACAP_TOKEN_ACTOR_ADDR,
ext::datacap::Method::Mint as MethodNum,
Expand Down
10 changes: 7 additions & 3 deletions test_vm/tests/verifreg_remove_datacap_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ use fil_actor_verifreg::{AddrPairKey, Method as VerifregMethod};
use fil_actor_verifreg::{RemoveDataCapProposal, RemoveDataCapProposalID, State as VerifregState};
use fil_actors_runtime::cbor::serialize;
use fil_actors_runtime::{
make_map_with_root_and_bitwidth, DATACAP_TOKEN_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR,
make_map_with_root_and_bitwidth, DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR,
VERIFIED_REGISTRY_ACTOR_ADDR,
};
use test_vm::util::{add_verifier, apply_ok, create_accounts};
use test_vm::{ExpectInvocation, TEST_VERIFREG_ROOT_ADDR, VM};
Expand All @@ -47,8 +48,11 @@ fn remove_datacap_simple_successful_path() {
// register the verified client
let add_verified_client_params =
AddVerifierClientParams { address: verified_client, allowance: verifier_allowance.clone() };
let mint_params =
MintParams { to: verified_client, amount: &verifier_allowance * TOKEN_PRECISION };
let mint_params = MintParams {
to: verified_client,
amount: &verifier_allowance * TOKEN_PRECISION,
operators: vec![*STORAGE_MARKET_ACTOR_ADDR],
};
apply_ok(
&v,
verifier1,
Expand Down

0 comments on commit 6076bcf

Please sign in to comment.