Skip to content

Commit f9b5760

Browse files
authored
Test VM state mutation (#1356)
* make power scenario test portable * port extend sectors test to run on the dyn VM * port evm tests * port placeholder_deploy test * cleanup and refactor replica update tests
1 parent 4d02c0c commit f9b5760

19 files changed

+598
-971
lines changed

Cargo.lock

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integration_tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ fvm_ipld_encoding = { workspace = true }
4545
fvm_ipld_hamt = { workspace = true }
4646
fvm_shared = { workspace = true }
4747
hex = { workspace = true }
48+
hex-literal = { workspace = true }
4849
indexmap = { workspace = true }
4950
integer-encoding = { workspace = true }
5051
lazy_static = { workspace = true }

integration_tests/src/tests/evm_test.rs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1-
use std::sync::Arc;
2-
31
use ethers::prelude::abigen;
42
use ethers::providers::Provider;
53
use ethers::{core::types::Address as EthAddress, prelude::builders::ContractCall};
64
use fil_actors_evm_shared::uints::U256;
7-
use fil_actors_runtime::{test_utils::EVM_ACTOR_CODE_ID, EAM_ACTOR_ADDR, EAM_ACTOR_ID};
5+
use fil_actors_runtime::{
6+
test_utils::ETHACCOUNT_ACTOR_CODE_ID, test_utils::EVM_ACTOR_CODE_ID, EAM_ACTOR_ADDR,
7+
EAM_ACTOR_ID,
8+
};
9+
use fvm_ipld_encoding::ipld_block::IpldBlock;
10+
use fvm_ipld_encoding::RawBytes;
811
use fvm_ipld_encoding::{strict_bytes, BytesDe};
912
use fvm_shared::ActorID;
13+
use fvm_shared::METHOD_SEND;
1014
use fvm_shared::{address::Address, econ::TokenAmount};
1115
use num_traits::Zero;
1216
use serde::{Deserialize, Serialize};
13-
use vm_api::util::serialize_ok;
17+
use std::sync::Arc;
18+
use vm_api::util::{apply_ok, serialize_ok};
1419
use vm_api::VM;
1520

1621
use crate::util::create_accounts;
22+
use crate::TEST_FAUCET_ADDR;
1723

1824
// Generate a statically typed interface for the contracts.
1925
abigen!(Recursive, "../actors/evm/tests/contracts/Recursive.abi");
@@ -29,7 +35,63 @@ pub fn id_to_eth(id: ActorID) -> EthAddress {
2935

3036
#[derive(Debug, Clone, Serialize, Deserialize)]
3137
#[serde(transparent)]
32-
struct ContractParams(#[serde(with = "strict_bytes")] pub Vec<u8>);
38+
pub struct ContractParams(#[serde(with = "strict_bytes")] pub Vec<u8>);
39+
40+
pub fn evm_eth_create_external_test(v: &dyn VM) {
41+
// create the EthAccount
42+
let eth_bits = hex_literal::hex!("FEEDFACECAFEBEEF000000000000000000000000");
43+
let eth_addr = Address::new_delegated(EAM_ACTOR_ID, &eth_bits).unwrap();
44+
apply_ok(
45+
v,
46+
&TEST_FAUCET_ADDR,
47+
&eth_addr,
48+
&TokenAmount::from_whole(10_000),
49+
METHOD_SEND,
50+
None::<RawBytes>,
51+
);
52+
53+
let account = v.resolve_id_address(&eth_addr).unwrap();
54+
55+
let mut actor = v.actor(&account).unwrap();
56+
actor.code = *ETHACCOUNT_ACTOR_CODE_ID;
57+
v.set_actor(&account, actor);
58+
59+
// now create an empty contract
60+
let params = IpldBlock::serialize_cbor(&fil_actor_eam::CreateExternalParams(vec![])).unwrap();
61+
let create_result = v
62+
.execute_message(
63+
&account,
64+
&EAM_ACTOR_ADDR,
65+
&TokenAmount::zero(),
66+
fil_actor_eam::Method::CreateExternal as u64,
67+
params,
68+
)
69+
.unwrap();
70+
71+
assert!(
72+
create_result.code.is_success(),
73+
"failed to create the new actor {}",
74+
create_result.message
75+
);
76+
77+
// and call it
78+
let create_return: fil_actor_eam::CreateExternalReturn =
79+
create_result.ret.unwrap().deserialize().expect("failed to decode results");
80+
81+
let robust_addr = create_return.robust_address.unwrap();
82+
83+
let params = IpldBlock::serialize_cbor(&ContractParams(vec![])).unwrap();
84+
let call_result = v
85+
.execute_message(
86+
&account,
87+
&robust_addr,
88+
&TokenAmount::zero(),
89+
fil_actor_evm::Method::InvokeContract as u64,
90+
params,
91+
)
92+
.unwrap();
93+
assert!(call_result.code.is_success(), "failed to call the new actor {}", call_result.message);
94+
}
3395

3496
pub fn evm_call_test(v: &dyn VM) {
3597
let account = create_accounts(v, 1, &TokenAmount::from_whole(10_000))[0];

integration_tests/src/tests/extend_sectors_test.rs

Lines changed: 221 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use fil_actor_market::{DealMetaArray, SectorDeals, State as MarketState};
22
use fil_actor_miner::{
33
max_prove_commit_duration, power_for_sector, ExpirationExtension, ExpirationExtension2,
44
ExtendSectorExpiration2Params, ExtendSectorExpirationParams, Method as MinerMethod, PowerPair,
5-
ProveReplicaUpdatesParams2, ReplicaUpdate2, SectorClaim, State as MinerState,
5+
ProveReplicaUpdatesParams2, ReplicaUpdate2, SectorClaim, Sectors, State as MinerState,
66
};
77
use fil_actor_verifreg::Method as VerifregMethod;
88
use fil_actors_runtime::runtime::Policy;
@@ -18,15 +18,16 @@ use fvm_shared::econ::TokenAmount;
1818
use fvm_shared::piece::PaddedPieceSize;
1919
use fvm_shared::sector::{RegisteredSealProof, SectorNumber, StoragePower};
2020
use vm_api::trace::ExpectInvocation;
21-
use vm_api::util::{apply_ok, get_state, DynBlockstore};
21+
use vm_api::util::{apply_ok, get_state, mutate_state, DynBlockstore};
2222
use vm_api::VM;
2323

2424
use crate::expects::Expect;
2525
use crate::util::{
26-
advance_by_deadline_to_epoch, advance_by_deadline_to_index, advance_to_proving_deadline,
27-
bf_all, create_accounts, create_miner, cron_tick, market_add_balance, market_publish_deal,
28-
miner_precommit_sector, miner_prove_sector, sector_deadline, submit_windowed_post,
29-
verifreg_add_client, verifreg_add_verifier,
26+
advance_by_deadline_to_epoch, advance_by_deadline_to_epoch_while_proving,
27+
advance_by_deadline_to_index, advance_to_proving_deadline, bf_all, create_accounts,
28+
create_miner, cron_tick, expect_invariants, invariant_failure_patterns, market_add_balance,
29+
market_publish_deal, miner_precommit_sector, miner_prove_sector, sector_deadline,
30+
submit_windowed_post, verifreg_add_client, verifreg_add_verifier,
3031
};
3132

3233
#[allow(clippy::too_many_arguments)]
@@ -103,6 +104,220 @@ pub fn extend(
103104
.matches(v.take_invocations().last().unwrap());
104105
}
105106

107+
pub fn extend_legacy_sector_with_deals_test(v: &dyn VM, do_extend2: bool) {
108+
let addrs = create_accounts(v, 3, &TokenAmount::from_whole(10_000));
109+
let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1;
110+
let (owner, worker, verifier, verified_client) = (addrs[0], addrs[0], addrs[1], addrs[2]);
111+
let sector_number: SectorNumber = 100;
112+
let policy = Policy::default();
113+
114+
// create miner
115+
let miner_id = create_miner(
116+
v,
117+
&owner,
118+
&worker,
119+
seal_proof.registered_window_post_proof().unwrap(),
120+
&TokenAmount::from_whole(1_000),
121+
)
122+
.0;
123+
v.set_epoch(200);
124+
125+
//
126+
// publish verified deals
127+
//
128+
129+
// register verifier then verified client
130+
let datacap = StoragePower::from(32_u128 << 40);
131+
verifreg_add_verifier(v, &verifier, datacap.clone());
132+
verifreg_add_client(v, &verifier, &verified_client, datacap);
133+
134+
// add market collateral for clients and miner
135+
market_add_balance(v, &verified_client, &verified_client, &TokenAmount::from_whole(3));
136+
market_add_balance(v, &worker, &miner_id, &TokenAmount::from_whole(64));
137+
138+
// create 1 verified deal for total sector capacity for 6 months
139+
let deal_start = v.epoch() + max_prove_commit_duration(&Policy::default(), seal_proof).unwrap();
140+
let deals = market_publish_deal(
141+
v,
142+
&worker,
143+
&verified_client,
144+
&miner_id,
145+
"deal1".to_string(),
146+
PaddedPieceSize(32u64 << 30),
147+
true,
148+
deal_start,
149+
180 * EPOCHS_IN_DAY,
150+
)
151+
.ids;
152+
153+
//
154+
// Precommit, prove and PoSt empty sector (more fully tested in TestCommitPoStFlow)
155+
//
156+
157+
miner_precommit_sector(
158+
v,
159+
&worker,
160+
&miner_id,
161+
seal_proof,
162+
sector_number,
163+
deals,
164+
deal_start + 180 * EPOCHS_IN_DAY,
165+
);
166+
167+
// advance time to max seal duration and prove the sector
168+
advance_by_deadline_to_epoch(v, &miner_id, deal_start);
169+
miner_prove_sector(v, &worker, &miner_id, sector_number);
170+
// trigger cron to validate the prove commit
171+
cron_tick(v);
172+
173+
// inspect sector info
174+
175+
let miner_state: MinerState = get_state(v, &miner_id).unwrap();
176+
let mut sector_info = miner_state
177+
.get_sector(&DynBlockstore::wrap(v.blockstore()), sector_number)
178+
.unwrap()
179+
.unwrap();
180+
assert_eq!(180 * EPOCHS_IN_DAY, sector_info.expiration - sector_info.activation);
181+
assert_eq!(StoragePower::zero(), sector_info.deal_weight); // 0 space time
182+
assert_eq!(
183+
DealWeight::from(180 * EPOCHS_IN_DAY * (32i64 << 30)),
184+
sector_info.verified_deal_weight
185+
); // (180 days *2880 epochs per day) * 32 GiB
186+
187+
// Note: we don't need to explicitly set verified weight using the legacy method
188+
// because legacy and simple qa power deal weight calculations line up for fully packed sectors
189+
// We do need to set simple_qa_power to false
190+
sector_info.simple_qa_power = false;
191+
192+
// Manually craft state to match legacy sectors
193+
mutate_state(v, &miner_id, |st: &mut MinerState| {
194+
let store = &DynBlockstore::wrap(v.blockstore());
195+
let mut sectors = Sectors::load(store, &st.sectors).unwrap();
196+
sectors.store(vec![sector_info.clone()]).unwrap();
197+
st.sectors = sectors.amt.flush().unwrap();
198+
});
199+
200+
let initial_verified_deal_weight = sector_info.verified_deal_weight;
201+
let initial_deal_weight = sector_info.deal_weight;
202+
203+
// advance to proving period and submit post
204+
let (deadline_info, partition_index) = advance_to_proving_deadline(v, &miner_id, sector_number);
205+
let expected_power_delta = PowerPair {
206+
raw: StoragePower::from(32u64 << 30),
207+
qa: StoragePower::from(10 * (32u64 << 30)),
208+
};
209+
submit_windowed_post(
210+
v,
211+
&worker,
212+
&miner_id,
213+
deadline_info,
214+
partition_index,
215+
Some(expected_power_delta),
216+
);
217+
218+
// move forward one deadline so advanceWhileProving doesn't fail double submitting posts
219+
advance_by_deadline_to_index(
220+
v,
221+
&miner_id,
222+
deadline_info.index + 1 % policy.wpost_period_deadlines,
223+
);
224+
225+
// Advance halfway through life and extend another 6 months. We need to spread the remaining 90
226+
// days of 10x power over 90 + 180 days
227+
// subtract half the remaining deal weight:
228+
// - verified deal weight /= 2
229+
//
230+
// normalize 90 days of 10x power plus 180 days of 1x power over 90+180 days:
231+
// - multiplier = ((10 * 90) + (1 * 180)) / (90 + 180)
232+
// - multiplier = 4
233+
//
234+
// delta from the previous 10x power multiplier:
235+
// - power delta = (10-4)*32GiB = 6*32GiB
236+
advance_by_deadline_to_epoch_while_proving(
237+
v,
238+
&miner_id,
239+
&worker,
240+
sector_number,
241+
deal_start + 90 * EPOCHS_IN_DAY,
242+
);
243+
244+
let new_expiration = deal_start + 2 * 180 * EPOCHS_IN_DAY;
245+
let expected_power_delta =
246+
PowerPair { raw: StoragePower::zero(), qa: StoragePower::from(-6 * (32i64 << 30)) };
247+
extend(
248+
v,
249+
worker,
250+
miner_id,
251+
deadline_info.index,
252+
partition_index,
253+
sector_number,
254+
new_expiration,
255+
expected_power_delta,
256+
do_extend2,
257+
);
258+
259+
let miner_state: MinerState = get_state(v, &miner_id).unwrap();
260+
sector_info = miner_state
261+
.get_sector(&DynBlockstore::wrap(v.blockstore()), sector_number)
262+
.unwrap()
263+
.unwrap();
264+
assert_eq!(180 * 2 * EPOCHS_IN_DAY, sector_info.expiration - sector_info.activation);
265+
assert_eq!(initial_deal_weight, sector_info.deal_weight); // 0 space time, unchanged
266+
assert_eq!(&initial_verified_deal_weight / 2, sector_info.verified_deal_weight);
267+
268+
// advance to 6 months (original expiration) and extend another 6 months
269+
//
270+
// We're 1/3rd of the way through the last extension, so keep 2/3 of the power.
271+
// - verified deal weight *= 2/3
272+
//
273+
// normalize 180 days of 4x power plus 180 days of 1x power over 180+180 days:
274+
// - multiplier = ((4 * 180) + (1 * 180)) / (90 + 180)
275+
// - multiplier = 2.5
276+
//
277+
// delta from the previous 4x power multiplier:
278+
// - power delta = (4-2.5)*32GiB = 1.5*32GiB
279+
280+
advance_by_deadline_to_epoch_while_proving(
281+
v,
282+
&miner_id,
283+
&worker,
284+
sector_number,
285+
deal_start + 180 * EPOCHS_IN_DAY,
286+
);
287+
288+
let new_expiration = deal_start + 3 * 180 * EPOCHS_IN_DAY;
289+
let expected_power_delta =
290+
PowerPair { raw: StoragePower::zero(), qa: StoragePower::from(-15 * (32i64 << 30) / 10) };
291+
extend(
292+
v,
293+
worker,
294+
miner_id,
295+
deadline_info.index,
296+
partition_index,
297+
sector_number,
298+
new_expiration,
299+
expected_power_delta,
300+
do_extend2,
301+
);
302+
303+
let miner_state: MinerState = get_state(v, &miner_id).unwrap();
304+
let sector_info = miner_state
305+
.get_sector(&DynBlockstore::wrap(v.blockstore()), sector_number)
306+
.unwrap()
307+
.unwrap();
308+
assert_eq!(180 * 3 * EPOCHS_IN_DAY, sector_info.expiration - sector_info.activation);
309+
// 0 space time, unchanged
310+
assert_eq!(initial_deal_weight, sector_info.deal_weight);
311+
// 1/2 * 2/3 -> 1/3
312+
assert_eq!(initial_verified_deal_weight / 3, sector_info.verified_deal_weight);
313+
314+
expect_invariants(
315+
v,
316+
&Policy::default(),
317+
&[invariant_failure_patterns::REWARD_STATE_EPOCH_MISMATCH.to_owned()],
318+
);
319+
}
320+
106321
pub fn commit_sector_with_max_duration_deal_test(v: &dyn VM) {
107322
let addrs = create_accounts(v, 3, &TokenAmount::from_whole(10_000));
108323
let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1;

0 commit comments

Comments
 (0)