Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verified registry burns datacap tokens received for successful claim extensions #725

Merged
merged 1 commit into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions actors/verifreg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,13 +648,12 @@ impl Actor {

// Validate receiver hook payload.
let tokens_received = validate_tokens_received(&params, my_id)?;
let token_datacap = tokens_to_datacap(&tokens_received.amount);
let client = tokens_received.from;

// Extract and validate allocation request from the operator data.
let reqs: AllocationRequests =
deserialize(&tokens_received.operator_data, "allocation requests")?;
let mut allocation_total = DataCap::zero();
let mut datacap_total = DataCap::zero();

// Construct new allocation records.
let mut new_allocs = Vec::with_capacity(reqs.allocations.len());
Expand All @@ -672,12 +671,13 @@ impl Actor {
term_max: req.term_max,
expiration: req.expiration,
});
allocation_total += DataCap::from(req.size.0);
datacap_total += DataCap::from(req.size.0);
}

let st: State = rt.state()?;
let mut claims = st.load_claims(rt.store())?;
let mut updated_claims = Vec::<(ClaimID, Claim)>::new();
let mut extension_total = DataCap::zero();
for req in &reqs.extensions {
// Note: we don't check the client address here, by design.
// Any client can spend datacap to extend an existing claim.
Expand All @@ -696,18 +696,25 @@ impl Actor {
// The claim's client is not changed to be the address of the token sender.
// It remains the original allocation client.
updated_claims.push((req.claim, Claim { term_max: req.term_max, ..*claim }));
allocation_total += DataCap::from(claim.size.0);
datacap_total += DataCap::from(claim.size.0);
extension_total += DataCap::from(claim.size.0);
}

// Allocation size must match the tokens received exactly (we don't return change).
if allocation_total != token_datacap {
let tokens_as_datacap = tokens_to_datacap(&tokens_received.amount);
if datacap_total != tokens_as_datacap {
return Err(actor_error!(
illegal_argument,
"total allocation size {} must match data cap amount received {}",
allocation_total,
token_datacap
datacap_total,
tokens_as_datacap
));
}

// Burn the received datacap tokens spent on extending existing claims.
// The tokens spent on new allocations will be burnt when claimed later, or refunded.
burn(rt, &extension_total)?;

// Partial success isn't supported yet, but these results make space for it in the future.
let allocation_results = BatchReturn::ok(new_allocs.len() as u32);
let extension_results = BatchReturn::ok(updated_claims.len() as u32);
Expand Down
13 changes: 13 additions & 0 deletions actors/verifreg/tests/harness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,13 +344,26 @@ impl Harness {
expected_alloc_results: BatchReturn,
expected_extension_results: BatchReturn,
expected_alloc_ids: Vec<AllocationID>,
expected_burn: u64,
) -> Result<(), ActorError> {
rt.set_caller(*DATACAP_TOKEN_ACTOR_CODE_ID, DATACAP_TOKEN_ACTOR_ADDR);
let params = UniversalReceiverParams {
type_: FRC46_TOKEN_TYPE,
payload: serialize(&payload, "payload").unwrap(),
};

if !expected_burn.is_zero() {
rt.expect_send(
DATACAP_TOKEN_ACTOR_ADDR,
ext::datacap::Method::Burn as MethodNum,
RawBytes::serialize(&BurnParams { amount: TokenAmount::from_whole(expected_burn) })
.unwrap(),
TokenAmount::zero(),
RawBytes::serialize(&BurnReturn { balance: TokenAmount::zero() }).unwrap(),
ExitCode::OK,
);
}

rt.expect_validate_caller_addr(vec![DATACAP_TOKEN_ACTOR_ADDR]);
let ret = rt.call::<VerifregActor>(
Method::UniversalReceiverHook as MethodNum,
Expand Down
52 changes: 32 additions & 20 deletions actors/verifreg/tests/verifreg_actor_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,7 @@ mod datacap {
make_alloc_req(&rt, PROVIDER2, SIZE * 2),
];
let payload = make_receiver_hook_token_payload(CLIENT1, reqs.clone(), vec![], SIZE * 3);
h.receive_tokens(&mut rt, payload, BatchReturn::ok(2), BATCH_EMPTY, vec![1, 2])
h.receive_tokens(&mut rt, payload, BatchReturn::ok(2), BATCH_EMPTY, vec![1, 2], 0)
.unwrap();

// Verify allocations in state.
Expand All @@ -932,7 +932,8 @@ mod datacap {
// Make another allocation from a different client
let reqs = vec![make_alloc_req(&rt, PROVIDER1, SIZE)];
let payload = make_receiver_hook_token_payload(CLIENT2, reqs.clone(), vec![], SIZE);
h.receive_tokens(&mut rt, payload, BatchReturn::ok(1), BATCH_EMPTY, vec![3]).unwrap();
h.receive_tokens(&mut rt, payload, BatchReturn::ok(1), BATCH_EMPTY, vec![3], 0)
.unwrap();

// Verify allocations in state.
assert_allocation(&rt, CLIENT2, 3, &alloc_from_req(CLIENT2, &reqs[0]));
Expand Down Expand Up @@ -964,7 +965,8 @@ mod datacap {
];
// Client1 extends both claims
let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE * 3);
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(2), vec![]).unwrap();
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(2), vec![], SIZE * 3)
.unwrap();

// Verify claims in state.
assert_claim(&rt, PROVIDER1, cid1, &Claim { term_max: term_max + 1000, ..claim1 });
Expand Down Expand Up @@ -999,8 +1001,15 @@ mod datacap {
// CLIENT1 makes two new allocations and extends two existing claims.
let payload =
make_receiver_hook_token_payload(CLIENT1, alloc_reqs.clone(), ext_reqs, SIZE * 6);
h.receive_tokens(&mut rt, payload, BatchReturn::ok(2), BatchReturn::ok(2), vec![3, 4])
.unwrap();
h.receive_tokens(
&mut rt,
payload,
BatchReturn::ok(2),
BatchReturn::ok(2),
vec![3, 4],
claim1.size.0 + claim2.size.0,
)
.unwrap();

// Verify state.
assert_allocation(&rt, CLIENT1, 3, &alloc_from_req(CLIENT1, &alloc_reqs[0]));
Expand Down Expand Up @@ -1088,7 +1097,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
format!("allocation provider {} must be a miner actor", provider1).as_str(),
h.receive_tokens(&mut rt, payload, BatchReturn::ok(1), BATCH_EMPTY, vec![1]),
h.receive_tokens(&mut rt, payload, BatchReturn::ok(1), BATCH_EMPTY, vec![1], 0),
);
h.check_state(&rt);
}
Expand All @@ -1106,7 +1115,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"allocation size 1048575 below minimum 1048576",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
}
// Min term too short
Expand All @@ -1117,7 +1126,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"allocation term min 518399 below limit 518400",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
}
// Max term too long
Expand All @@ -1128,7 +1137,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"allocation term max 5259486 above limit 5259485",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
}
// Term minimum greater than maximum
Expand All @@ -1140,7 +1149,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"allocation term min 2103795 exceeds term max 2103794",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
}
// Allocation expires too late
Expand All @@ -1151,7 +1160,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"allocation expiration 172801 exceeds maximum 172800",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
}
// Tokens received doesn't match sum of allocation sizes
Expand All @@ -1162,7 +1171,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"total allocation size 2097152 must match data cap amount received 2097153",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
}
// One bad request fails the lot
Expand All @@ -1176,7 +1185,7 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"allocation size 1048575 below minimum 1048576",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
}
h.check_state(&rt);
Expand Down Expand Up @@ -1207,12 +1216,13 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"term_max 5260486 for claim 1 exceeds maximum 5260485 at current epoch 1100",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
// But just on the limit is allowed
let reqs = vec![make_extension_req(PROVIDER1, cid1, max_allowed_term)];
let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE);
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![]).unwrap();
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![], SIZE)
.unwrap();
}
{
// Claim already expired
Expand All @@ -1225,15 +1235,16 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_FORBIDDEN,
"claim 1 expired at 518600, current epoch 518601",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
// But just at expiration is allowed
let epoch = term_start + term_max;
let new_term = epoch - term_start + MAXIMUM_VERIFIED_ALLOCATION_TERM; // Can get full max term now
rt.set_epoch(epoch);
let reqs = vec![make_extension_req(PROVIDER1, cid1, new_term)];
let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE);
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![]).unwrap();
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![], SIZE)
.unwrap();
}
{
// Extension is zero
Expand All @@ -1244,20 +1255,21 @@ mod datacap {
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"term_max 518500 for claim 1 is not larger than existing term max 518500",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
// Extension is negative
let reqs = vec![make_extension_req(PROVIDER1, cid1, term_max - 1)];
let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE);
expect_abort_contains_message(
ExitCode::USR_ILLEGAL_ARGUMENT,
"term_max 518499 for claim 1 is not larger than existing term max 518500",
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![]),
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0),
);
// But extension by just 1 epoch is allowed
let reqs = vec![make_extension_req(PROVIDER1, cid1, term_max + 1)];
let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE);
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![]).unwrap();
h.receive_tokens(&mut rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![], SIZE)
.unwrap();
}
}
}