mod common;
use address::Address;
use cid::Cid;
use clock::ChainEpoch;
use common::*;
use crypto::Signature;
use derive_builder::Builder;
use forest_actor::{
paych::{
ConstructorParams, LaneState, Merge, Method, ModVerifyParams, PaymentVerifyParams,
SignedVoucher, State as PState, UpdateChannelStateParams, MAX_LANE, SETTLE_DELAY,
},
ACCOUNT_ACTOR_CODE_ID, INIT_ACTOR_ADDR, INIT_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID,
PAYCH_ACTOR_CODE_ID,
};
use ipld_amt::Amt;
use num_bigint::BigInt;
use std::cell::RefCell;
use std::collections::HashMap;
use std::error::Error as StdError;
use vm::{ExitCode, Serialized, TokenAmount, METHOD_CONSTRUCTOR, METHOD_SEND};
const PAYCH_ID: u64 = 100;
const PAYER_ID: u64 = 102;
const PAYEE_ID: u64 = 103;
struct LaneParams {
epoch_num: ChainEpoch,
from: Address,
to: Address,
amt: BigInt,
lane: u64,
nonce: u64,
}
fn call(rt: &mut MockRuntime, method_num: u64, ser: &Serialized) -> Serialized {
rt.call(&*PAYCH_ACTOR_CODE_ID, method_num, ser).unwrap()
}
fn expect_error(rt: &mut MockRuntime, method_num: u64, ser: &Serialized, exp: ExitCode) {
let err = rt.call(&*PAYCH_ACTOR_CODE_ID, method_num, ser).unwrap_err();
assert_eq!(exp, err.exit_code());
}
fn construct_lane_state_amt(rt: &MockRuntime, lss: Vec<LaneState>) -> Cid {
let mut arr = Amt::new(&rt.store);
for (i, ls) in (0..).zip(lss.into_iter()) {
arr.set(i, ls).unwrap();
}
arr.flush().unwrap()
}
fn get_lane_state(rt: &MockRuntime, cid: &Cid, lane: usize) -> LaneState {
let arr: Amt<LaneState, _> = Amt::load(cid, &rt.store).unwrap();
arr.get(lane).unwrap().unwrap().clone()
}
mod paych_constructor {
use super::*;
const TEST_PAYCH_ADDR: u64 = 100;
const TEST_PAYER_ADDR: u64 = 101;
const TEST_CALLER_ADDR: u64 = 102;
fn construct_runtime() -> MockRuntime {
let paych_addr = Address::new_id(TEST_PAYCH_ADDR);
let payer_addr = Address::new_id(TEST_PAYER_ADDR);
let caller_addr = Address::new_id(TEST_CALLER_ADDR);
let mut actor_code_cids = HashMap::default();
actor_code_cids.insert(payer_addr, *ACCOUNT_ACTOR_CODE_ID);
MockRuntime {
receiver: paych_addr,
caller: caller_addr,
caller_type: *INIT_ACTOR_CODE_ID,
actor_code_cids,
..Default::default()
}
}
#[test]
fn create_paych_actor_test() {
let caller_addr = Address::new_id(TEST_CALLER_ADDR);
let mut rt = construct_runtime();
rt.actor_code_cids
.insert(caller_addr, *ACCOUNT_ACTOR_CODE_ID);
construct_and_verify(&mut rt, Address::new_id(TEST_PAYER_ADDR), caller_addr);
}
#[test]
fn actor_doesnt_exist_test() {
let mut rt = construct_runtime();
rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]);
let params = ConstructorParams {
to: Address::new_id(TEST_PAYCH_ADDR),
from: Address::new_id(TEST_PAYER_ADDR),
};
expect_error(
&mut rt,
METHOD_CONSTRUCTOR,
&Serialized::serialize(params).unwrap(),
ExitCode::ErrIllegalArgument,
);
}
#[test]
fn actor_constructor_fails() {
let paych_addr = Address::new_id(TEST_PAYCH_ADDR);
let payer_addr = Address::new_id(TEST_PAYER_ADDR);
let caller_addr = Address::new_id(TEST_CALLER_ADDR);
struct TestCase {
paych_addr: Address,
caller_code: Cid,
new_actor_code: Cid,
payer_code: Cid,
expected_exit_code: ExitCode,
}
let test_cases: Vec<TestCase> = vec![TestCase {
paych_addr,
caller_code: *INIT_ACTOR_CODE_ID,
new_actor_code: *MULTISIG_ACTOR_CODE_ID,
payer_code: *ACCOUNT_ACTOR_CODE_ID,
expected_exit_code: ExitCode::ErrForbidden,
}];
for test_case in test_cases {
let mut actor_code_cids = HashMap::default();
actor_code_cids.insert(test_case.paych_addr, test_case.new_actor_code);
actor_code_cids.insert(payer_addr, test_case.payer_code);
let mut rt = MockRuntime {
receiver: paych_addr,
caller: caller_addr,
caller_type: test_case.caller_code,
actor_code_cids,
..Default::default()
};
rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]);
let params = ConstructorParams {
to: test_case.paych_addr,
from: Address::new_id(10001),
};
expect_error(
&mut rt,
METHOD_CONSTRUCTOR,
&Serialized::serialize(params).unwrap(),
test_case.expected_exit_code,
);
}
}
}
mod create_lane_tests {
use super::*;
const TEST_INIT_ACTOR_ADDR: u64 = 100;
const PAYCH_ADDR: u64 = 101;
const PAYER_ADDR: u64 = 102;
const PAYEE_ADDR: u64 = 103;
const PAYCH_BALANCE: u64 = 9;
#[derive(Builder, Debug)]
#[builder(name = "TestCaseBuilder")]
struct TestCase {
#[builder(default = "Address::new_id(PAYCH_ADDR)")]
payment_channel: Address,
desc: String,
#[builder(default = "ACCOUNT_ACTOR_CODE_ID.clone()")]
target_code: Cid,
#[builder(default)]
balance: u64,
#[builder(default)]
recieved: u64,
#[builder(default = "1")]
epoch: ChainEpoch,
#[builder(default = "1")]
tl_min: ChainEpoch,
#[builder(default = "0")]
tl_max: ChainEpoch,
#[builder(default)]
lane: u64,
#[builder(default)]
nonce: u64,
#[builder(default = "1")]
amt: i64,
#[builder(default)]
secret_preimage: Vec<u8>,
#[builder(default)]
sig: Option<Signature>,
#[builder(default = "true")]
verify_sig: bool,
#[builder(default = "ExitCode::ErrIllegalArgument")]
exp_exit_code: ExitCode,
}
impl TestCase {
pub fn builder() -> TestCaseBuilder {
TestCaseBuilder::default()
}
}
#[test]
fn create_lane_test() {
let init_actor_addr = Address::new_id(TEST_INIT_ACTOR_ADDR);
let paych_addr = Address::new_id(PAYCH_ADDR);
let payer_addr = Address::new_id(PAYER_ADDR);
let payee_addr = Address::new_id(PAYEE_ADDR);
let paych_balance = TokenAmount::from(PAYCH_BALANCE);
let sig = Option::Some(Signature::new_bls("doesn't matter".as_bytes().to_vec()));
let test_cases: Vec<TestCase> = vec![
TestCase::builder()
.desc("succeeds".to_string())
.sig(sig.clone())
.exp_exit_code(ExitCode::Ok)
.build()
.unwrap(),
TestCase::builder()
.desc("fails if new send balance is negative".to_string())
.amt(-1)
.sig(sig.clone())
.build()
.unwrap(),
TestCase::builder()
.desc("fails if balance too low".to_string())
.amt(10)
.sig(sig.clone())
.build()
.unwrap(),
TestCase::builder()
.desc("fails is signature is not valid".to_string())
.sig(Option::None)
.build()
.unwrap(),
TestCase::builder()
.desc("fails if too early for a voucher".to_string())
.tl_min(10)
.sig(sig.clone())
.build()
.unwrap(),
TestCase::builder()
.desc("fails is beyond timelockmax".to_string())
.epoch(10)
.tl_max(5)
.sig(sig.clone())
.build()
.unwrap(),
TestCase::builder()
.desc("fails if signature is not verified".to_string())
.sig(sig)
.verify_sig(false)
.build()
.unwrap(),
];
for test_case in test_cases {
println!("Test Description {}", test_case.desc);
let mut actor_code_cids = HashMap::default();
actor_code_cids.insert(payee_addr, *ACCOUNT_ACTOR_CODE_ID);
actor_code_cids.insert(payer_addr, *ACCOUNT_ACTOR_CODE_ID);
let mut rt = MockRuntime {
receiver: paych_addr,
caller: init_actor_addr,
caller_type: *INIT_ACTOR_CODE_ID,
actor_code_cids,
epoch: test_case.epoch,
balance: RefCell::new(paych_balance.clone()),
..Default::default()
};
construct_and_verify(&mut rt, payer_addr, payee_addr);
let sv = SignedVoucher {
time_lock_min: test_case.tl_min,
time_lock_max: test_case.tl_max,
secret_pre_image: test_case.secret_preimage.clone(),
lane: test_case.lane as usize,
nonce: test_case.nonce,
amount: BigInt::from(test_case.amt),
signature: test_case.sig.clone(),
channel_addr: test_case.payment_channel,
extra: Default::default(),
min_settle_height: Default::default(),
merges: Default::default(),
};
let ucp = UpdateChannelStateParams::from(sv.clone());
rt.set_caller(test_case.target_code, payee_addr);
rt.expect_validate_caller_addr(vec![payer_addr, payee_addr]);
if test_case.sig.is_some() && test_case.secret_preimage.is_empty() {
let exp_exit_code = if !test_case.verify_sig {
Err(Box::<dyn StdError>::from("bad signature".to_string()))
} else {
Ok(())
};
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: payer_addr,
plaintext: sv.signing_bytes().unwrap(),
result: exp_exit_code,
});
}
if test_case.exp_exit_code == ExitCode::Ok {
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(ucp).unwrap(),
);
let st: PState = rt.get_state().unwrap();
let l_states = Amt::<LaneState, _>::load(&st.lane_states, &rt.store).unwrap();
assert_eq!(l_states.count(), 1);
let ls = l_states.get(sv.lane).unwrap().unwrap();
assert_eq!(sv.amount, ls.redeemed);
assert_eq!(sv.nonce, ls.nonce);
} else {
expect_error(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(ucp).unwrap(),
test_case.exp_exit_code,
);
verify_initial_state(&mut rt, payer_addr, payee_addr);
}
rt.verify();
}
}
}
mod update_channel_state_redeem {
use super::*;
#[test]
fn redeem_voucher_one_lane() {
let (mut rt, mut sv) = require_create_cannel_with_lanes(1);
let state: PState = rt.get_state().unwrap();
let payee_addr = Address::new_id(PAYEE_ID);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, payee_addr);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
sv.amount = BigInt::from(9);
let payer_addr = Address::new_id(PAYER_ID);
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: payer_addr,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(UpdateChannelStateParams::from(sv)).unwrap(),
);
rt.verify();
let exp_ls = LaneState {
redeemed: BigInt::from(9),
nonce: 2,
};
let exp_state = PState {
from: state.from,
to: state.to,
to_send: TokenAmount::from(9),
settling_at: state.settling_at,
min_settle_height: state.min_settle_height,
lane_states: construct_lane_state_amt(&rt, vec![exp_ls]),
};
verify_state(&mut rt, Some(1), exp_state);
}
#[test]
fn redeem_voucher_correct_lane() {
let (mut rt, mut sv) = require_create_cannel_with_lanes(3);
let state: PState = rt.get_state().unwrap();
let payee_addr = Address::new_id(PAYEE_ID);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, payee_addr);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
let initial_amount = state.to_send;
sv.amount = BigInt::from(9);
sv.lane = 1;
let ls_to_update: LaneState = get_lane_state(&rt, &state.lane_states, sv.lane);
sv.nonce = ls_to_update.nonce + 1;
let payer_addr = Address::new_id(PAYER_ID);
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: payer_addr,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(UpdateChannelStateParams::from(sv.clone())).unwrap(),
);
rt.verify();
let state: PState = rt.get_state().unwrap();
let ls_updated: LaneState = get_lane_state(&rt, &state.lane_states, sv.lane);
let big_delta = &sv.amount - &ls_to_update.redeemed;
let exp_send = big_delta + &initial_amount;
assert_eq!(exp_send, state.to_send);
assert_eq!(sv.amount, ls_updated.redeemed);
assert_eq!(sv.nonce, ls_updated.nonce);
}
}
mod merge_tests {
use super::*;
fn construct_runtime(num_lanes: u64) -> (MockRuntime, SignedVoucher, PState) {
let (mut rt, sv) = require_create_cannel_with_lanes(num_lanes);
let state: PState = rt.get_state().unwrap();
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
(rt, sv, state)
}
fn failure_end(rt: &mut MockRuntime, sv: SignedVoucher, exp_exit_code: ExitCode) {
let payee_addr = Address::new_id(PAYEE_ID);
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: payee_addr,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
expect_error(
rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(UpdateChannelStateParams::from(sv)).unwrap(),
exp_exit_code,
);
rt.verify();
}
#[test]
fn merge_success() {
let num_lanes = 3;
let (mut rt, mut sv, mut state) = construct_runtime(num_lanes);
let merge_to: LaneState = get_lane_state(&rt, &state.lane_states, 0);
let merge_from: LaneState = get_lane_state(&rt, &state.lane_states, 1);
sv.lane = 0;
let merge_nonce = merge_to.nonce + 10;
sv.merges = vec![Merge {
lane: 1,
nonce: merge_nonce,
}];
let payee_addr = Address::new_id(PAYEE_ID);
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: payee_addr,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(UpdateChannelStateParams::from(sv.clone())).unwrap(),
);
rt.verify();
let exp_merge_to = LaneState {
redeemed: sv.amount.clone(),
nonce: sv.nonce,
};
let exp_merge_from = LaneState {
redeemed: merge_from.redeemed.clone(),
nonce: merge_nonce,
};
let redeemed = &merge_from.redeemed + &merge_to.redeemed;
let exp_delta = &sv.amount - &redeemed;
state.to_send = exp_delta + &state.to_send;
state.lane_states = construct_lane_state_amt(
&rt,
vec![
exp_merge_to,
exp_merge_from,
get_lane_state(&rt, &state.lane_states, 2),
],
);
verify_state(&mut rt, Some(num_lanes), state);
}
#[test]
fn merge_failure() {
struct TestCase {
lane: u64,
voucher: u64,
balance: i32,
merge: u64,
exit: ExitCode,
}
let test_cases = vec![
TestCase {
lane: 1,
voucher: 10,
balance: 0,
merge: 1,
exit: ExitCode::ErrIllegalArgument,
},
TestCase {
lane: 1,
voucher: 0,
balance: 0,
merge: 10,
exit: ExitCode::ErrIllegalArgument,
},
TestCase {
lane: 1,
voucher: 10,
balance: 1,
merge: 10,
exit: ExitCode::ErrIllegalArgument,
},
TestCase {
lane: 0,
voucher: 10,
balance: 0,
merge: 10,
exit: ExitCode::ErrIllegalArgument,
},
];
for tc in test_cases {
let num_lanes = 2;
let (mut rt, mut sv, state) = construct_runtime(num_lanes);
rt.balance = RefCell::new(TokenAmount::from(tc.balance as u64));
sv.lane = 0;
sv.nonce = tc.voucher;
sv.merges = vec![Merge {
lane: tc.lane as usize,
nonce: tc.merge,
}];
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
failure_end(&mut rt, sv, tc.exit);
}
}
#[test]
fn invalid_merge_lane_999() {
let num_lanes = 2;
let (mut rt, mut sv) = require_create_cannel_with_lanes(num_lanes);
let state: PState = rt.get_state().unwrap();
sv.lane = 0;
sv.nonce = 10;
sv.merges = vec![Merge {
lane: 999,
nonce: sv.nonce,
}];
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
rt.expect_verify_signature(ExpectedVerifySig {
plaintext: sv.signing_bytes().unwrap(),
sig: sv.signature.clone().unwrap(),
signer: Address::new_id(PAYEE_ID),
result: Ok(()),
});
failure_end(&mut rt, sv, ExitCode::ErrIllegalArgument);
}
#[test]
fn lane_limit_exceeded() {
let (mut rt, mut sv, _) = construct_runtime(1);
sv.lane = MAX_LANE as usize + 1;
sv.nonce += 1;
sv.amount = BigInt::from(100);
failure_end(&mut rt, sv, ExitCode::ErrIllegalArgument);
}
}
mod update_channel_state_extra {
use super::*;
const OTHER_ADDR: u64 = 104;
fn construct_runtime(exit_code: ExitCode) -> (MockRuntime, SignedVoucher) {
let (mut rt, mut sv) = require_create_cannel_with_lanes(1);
let state: PState = rt.get_state().unwrap();
let other_addr = Address::new_id(OTHER_ADDR);
let fake_params = [1, 2, 3, 4];
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
sv.extra = Some(ModVerifyParams {
actor: other_addr,
method: Method::UpdateChannelState as u64,
data: Serialized::serialize(fake_params).unwrap(),
});
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: state.to,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
let exp_send_params = PaymentVerifyParams {
extra: Serialized::serialize(fake_params.to_vec()).unwrap(),
proof: vec![],
};
rt.expect_send(
other_addr,
Method::UpdateChannelState as u64,
Serialized::serialize(exp_send_params).unwrap(),
TokenAmount::from(0u8),
Serialized::default(),
exit_code,
);
(rt, sv)
}
#[test]
#[ignore = "old functionality -- test framework needs to be updated"]
fn extra_call_succeed() {
let (mut rt, sv) = construct_runtime(ExitCode::Ok);
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(UpdateChannelStateParams::from(sv)).unwrap(),
);
rt.verify();
}
#[test]
#[ignore = "old functionality -- test framework needs to be updated"]
fn extra_call_fail() {
let (mut rt, sv) = construct_runtime(ExitCode::ErrPlaceholder);
expect_error(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(UpdateChannelStateParams::from(sv)).unwrap(),
ExitCode::ErrPlaceholder,
);
rt.verify();
}
}
#[test]
fn update_channel_settling() {
let (mut rt, sv) = require_create_cannel_with_lanes(1);
rt.epoch = 10;
let state: PState = rt.get_state().unwrap();
rt.expect_validate_caller_addr(vec![state.from, state.to]);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
call(&mut rt, Method::Settle as u64, &Serialized::default());
let exp_settling_at = SETTLE_DELAY + 10;
let state: PState = rt.get_state().unwrap();
assert_eq!(exp_settling_at, state.settling_at);
assert_eq!(state.min_settle_height, 0);
struct TestCase {
min_settle: i64,
exp_min_settle_height: i64,
exp_settling_at: i64,
}
let test_cases = vec![
TestCase {
min_settle: 0,
exp_min_settle_height: state.min_settle_height,
exp_settling_at: state.settling_at,
},
TestCase {
min_settle: 2,
exp_min_settle_height: 2,
exp_settling_at: state.settling_at,
},
TestCase {
min_settle: 12,
exp_min_settle_height: 12,
exp_settling_at: state.settling_at,
},
TestCase {
min_settle: state.settling_at + 1,
exp_min_settle_height: state.settling_at + 1,
exp_settling_at: state.settling_at + 1,
},
];
let mut ucp = UpdateChannelStateParams::from(sv.clone());
for tc in test_cases {
ucp.sv.min_settle_height = tc.min_settle;
rt.expect_validate_caller_addr(vec![state.from, state.to]);
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: state.to,
plaintext: ucp.sv.signing_bytes().unwrap(),
result: Ok(()),
});
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(&ucp).unwrap(),
);
let new_state: PState = rt.get_state().unwrap();
assert_eq!(tc.exp_settling_at, new_state.settling_at);
assert_eq!(tc.exp_min_settle_height, new_state.min_settle_height);
ucp.sv.nonce += 1;
}
}
mod secret_preimage {
use super::*;
#[test]
fn succeed_correct_secret() {
let (mut rt, sv) = require_create_cannel_with_lanes(1);
let state: PState = rt.get_state().unwrap();
rt.expect_validate_caller_addr(vec![state.from, state.to]);
let ucp = UpdateChannelStateParams::from(sv.clone());
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.clone().signature.unwrap(),
signer: state.to,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(ucp).unwrap(),
);
rt.verify();
}
#[test]
fn incorrect_secret() {
let (mut rt, sv) = require_create_cannel_with_lanes(1);
let state: PState = rt.get_state().unwrap();
let mut ucp = UpdateChannelStateParams {
secret: b"Profesr".to_vec(),
sv: sv.clone(),
};
let mut mag = b"Magneto".to_vec();
mag.append(&mut vec![0; 25]);
ucp.sv.secret_pre_image = mag;
rt.expect_validate_caller_addr(vec![state.from, state.to]);
rt.expect_verify_signature(ExpectedVerifySig {
sig: sv.signature.unwrap(),
signer: state.to,
plaintext: ucp.sv.signing_bytes().unwrap(),
result: Ok(()),
});
expect_error(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(ucp).unwrap(),
ExitCode::ErrIllegalArgument,
);
rt.verify();
}
}
mod actor_settle {
use super::*;
const EP: i64 = 10;
#[test]
fn adjust_settling_at() {
let (mut rt, _sv) = require_create_cannel_with_lanes(1);
rt.epoch = EP;
let mut state: PState = rt.get_state().unwrap();
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
call(&mut rt, Method::Settle as u64, &Serialized::default());
let exp_settling_at = EP + SETTLE_DELAY;
state = rt.get_state().unwrap();
assert_eq!(state.settling_at, exp_settling_at);
assert_eq!(state.min_settle_height, 0);
}
#[test]
fn call_twice() {
let (mut rt, _sv) = require_create_cannel_with_lanes(1);
rt.epoch = EP;
let state: PState = rt.get_state().unwrap();
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
call(&mut rt, Method::Settle as u64, &Serialized::default());
rt.expect_validate_caller_addr(vec![state.from, state.to]);
expect_error(
&mut rt,
Method::Settle as u64,
&Serialized::default(),
ExitCode::ErrIllegalState,
);
}
#[test]
fn settle_if_height_less() {
let (mut rt, mut sv) = require_create_cannel_with_lanes(1);
rt.epoch = EP;
let mut state: PState = rt.get_state().unwrap();
sv.min_settle_height = (EP + SETTLE_DELAY) + 1;
let ucp = UpdateChannelStateParams::from(sv.clone());
rt.expect_validate_caller_addr(vec![state.from, state.to]);
rt.expect_verify_signature(ExpectedVerifySig {
sig: ucp.sv.signature.clone().unwrap(),
signer: state.to,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
call(
&mut rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(&ucp).unwrap(),
);
state = rt.get_state().unwrap();
assert_eq!(state.settling_at, 0);
assert_eq!(state.min_settle_height, ucp.sv.min_settle_height);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
call(&mut rt, Method::Settle as u64, &Serialized::default());
state = rt.get_state().unwrap();
assert_eq!(state.settling_at, ucp.sv.min_settle_height);
}
}
mod actor_collect {
use super::*;
#[test]
fn happy_path() {
let (mut rt, _sv) = require_create_cannel_with_lanes(1);
let curr_epoch: ChainEpoch = 10;
rt.epoch = curr_epoch;
let st: PState = rt.get_state().unwrap();
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, st.from);
rt.expect_validate_caller_addr(vec![st.from, st.to]);
call(&mut rt, Method::Settle as u64, &Default::default());
let st: PState = rt.get_state().unwrap();
assert_eq!(st.settling_at, SETTLE_DELAY + curr_epoch);
rt.expect_validate_caller_addr(vec![st.from, st.to]);
rt.epoch = st.settling_at + 1;
rt.expect_send(
st.to,
METHOD_SEND,
Default::default(),
st.to_send.clone(),
Default::default(),
ExitCode::Ok,
);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, st.to);
rt.expect_validate_caller_addr(vec![st.from, st.to]);
rt.expect_delete_actor(st.from);
let res = call(&mut rt, Method::Collect as u64, &Default::default());
assert_eq!(res, Serialized::default());
}
#[test]
fn actor_collect() {
struct TestCase {
dont_settle: bool,
exp_send_to: ExitCode,
exp_collect_exit: ExitCode,
}
let test_cases = vec![
TestCase {
dont_settle: true,
exp_send_to: ExitCode::Ok,
exp_collect_exit: ExitCode::ErrForbidden,
},
TestCase {
dont_settle: false,
exp_send_to: ExitCode::ErrPlaceholder,
exp_collect_exit: ExitCode::ErrPlaceholder,
},
];
for tc in test_cases {
let (mut rt, _sv) = require_create_cannel_with_lanes(1);
rt.epoch = 10;
let mut state: PState = rt.get_state().unwrap();
if !tc.dont_settle {
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
call(&mut rt, Method::Settle as u64, &Serialized::default());
state = rt.get_state().unwrap();
assert_eq!(state.settling_at, SETTLE_DELAY + rt.epoch);
}
rt.epoch = state.settling_at + 1;
rt.expect_send(
state.to,
METHOD_SEND,
Default::default(),
state.to_send.clone(),
Default::default(),
tc.exp_send_to,
);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, state.from);
rt.expect_validate_caller_addr(vec![state.from, state.to]);
expect_error(
&mut rt,
Method::Collect as u64,
&Serialized::default(),
tc.exp_collect_exit,
);
}
}
}
fn require_create_cannel_with_lanes(num_lanes: u64) -> (MockRuntime, SignedVoucher) {
let paych_addr = Address::new_id(100);
let payer_addr = Address::new_id(PAYER_ID);
let payee_addr = Address::new_id(PAYEE_ID);
let balance = RefCell::new(TokenAmount::from(100_000));
let received = TokenAmount::from(0);
let curr_epoch = 2;
let mut actor_code_cids = HashMap::default();
actor_code_cids.insert(payee_addr, *ACCOUNT_ACTOR_CODE_ID);
actor_code_cids.insert(payer_addr, *ACCOUNT_ACTOR_CODE_ID);
let mut rt = MockRuntime {
receiver: paych_addr,
caller: *INIT_ACTOR_ADDR,
caller_type: *INIT_ACTOR_CODE_ID,
actor_code_cids,
received,
balance,
epoch: curr_epoch,
..Default::default()
};
construct_and_verify(&mut rt, payer_addr, payee_addr);
let mut last_sv = None;
for i in 0..num_lanes {
let lane_param = LaneParams {
epoch_num: curr_epoch,
from: payer_addr,
to: payee_addr,
amt: (BigInt::from(i) + 1),
lane: i as u64,
nonce: i + 1,
};
last_sv = Some(require_add_new_lane(&mut rt, lane_param));
}
(rt, last_sv.unwrap())
}
fn require_add_new_lane(rt: &mut MockRuntime, param: LaneParams) -> SignedVoucher {
let payee_addr = Address::new_id(103_u64);
let sig = Signature::new_bls(vec![0, 1, 2, 3, 4, 5, 6, 7]);
let mut sv = SignedVoucher {
time_lock_min: param.epoch_num,
time_lock_max: i64::MAX,
lane: param.lane as usize,
nonce: param.nonce,
amount: param.amt.clone(),
signature: Some(sig.clone()),
secret_pre_image: Default::default(),
channel_addr: Address::new_id(PAYCH_ID),
extra: Default::default(),
min_settle_height: Default::default(),
merges: Default::default(),
};
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, param.from);
rt.expect_validate_caller_addr(vec![param.from, param.to]);
rt.expect_verify_signature(ExpectedVerifySig {
sig,
signer: payee_addr,
plaintext: sv.signing_bytes().unwrap(),
result: Ok(()),
});
call(
rt,
Method::UpdateChannelState as u64,
&Serialized::serialize(UpdateChannelStateParams::from(sv.clone())).unwrap(),
);
rt.verify();
sv.nonce += 1;
sv
}
fn construct_and_verify(rt: &mut MockRuntime, sender: Address, receiver: Address) {
let params = ConstructorParams {
from: sender,
to: receiver,
};
rt.expect_validate_caller_type(vec![*INIT_ACTOR_CODE_ID]);
call(
rt,
METHOD_CONSTRUCTOR,
&Serialized::serialize(¶ms).unwrap(),
);
rt.verify();
verify_initial_state(rt, sender, receiver);
}
fn verify_initial_state(rt: &mut MockRuntime, sender: Address, receiver: Address) {
let _state: PState = rt.get_state().unwrap();
let empt_arr_cid = Amt::<(), _>::new(&rt.store).flush().unwrap();
let expected_state = PState::new(sender, receiver, empt_arr_cid);
verify_state(rt, None, expected_state)
}
fn verify_state(rt: &mut MockRuntime, exp_lanes: Option<u64>, expected_state: PState) {
let state: PState = rt.get_state().unwrap();
assert_eq!(expected_state.to, state.to);
assert_eq!(expected_state.from, state.from);
assert_eq!(expected_state.min_settle_height, state.min_settle_height);
assert_eq!(expected_state.settling_at, state.settling_at);
assert_eq!(expected_state.to_send, state.to_send);
if let Some(exp_lanes) = exp_lanes {
assert_lane_states_length(rt, &state.lane_states, exp_lanes);
assert_eq!(expected_state.lane_states, state.lane_states);
} else {
assert_lane_states_length(rt, &state.lane_states, 0);
}
}
fn assert_lane_states_length(rt: &MockRuntime, cid: &Cid, l: u64) {
let arr = Amt::<LaneState, _>::load(cid, &rt.store).unwrap();
assert_eq!(arr.count(), l as usize);
}