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

Tool for applying test vectors from Ethereum on FEVM #1482

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9b29143
extend test vector format to support test vector from ethereum
samuerio Jan 11, 2023
bad00a4
add member fevm_test_vectors
amiable169 Jan 13, 2023
8e1023e
fevm_test_vectors fvm_shared version = "3.0.0-alpha.15"
amiable169 Jan 13, 2023
8f2d931
update ethaccount mock
samuerio Jan 15, 2023
0323390
update TestVector struct add _debug field
samuerio Jan 15, 2023
12e3073
only leave generate cmd and transaction move meta _debug
amiable169 Jan 15, 2023
341611b
update fevm-test-vectors Readme.md
samuerio Jan 15, 2023
405827f
document extract_eth_transaction_test_vector
oldfans Jan 15, 2023
9f33155
conformance vector add serialize
amiable169 Jan 18, 2023
cfc492d
direct use conformance vector
amiable169 Jan 18, 2023
2f15696
feat: get_most_recent_transactions_of_contracts
oldfans Jan 18, 2023
9b89419
feat: get_most_recent_transactions_of_contracts
oldfans Jan 18, 2023
e7266e7
MessageVector add class field
amiable169 Jan 18, 2023
ef0a1c2
rebuild cmd
amiable169 Jan 18, 2023
5334808
refactor extract_eth_transaction_test_vector
oldfans Jan 19, 2023
dc534dc
batch cmd
amiable169 Jan 19, 2023
b7a9d56
furthest_block_num fix
amiable169 Jan 19, 2023
8ecf61e
furthest_block_num fix
amiable169 Jan 19, 2023
144377d
feat: add progress bar to get_most_recent_transactions_of_contracts
oldfans Jan 19, 2023
f55ca0f
add consume subcommand
samuerio Jan 19, 2023
47e4f32
add tag
amiable169 Jan 19, 2023
90179d8
update batch generate subcommand
samuerio Jan 19, 2023
743c62c
add run script
amiable169 Jan 19, 2023
66a6999
generate tag dir
amiable169 Jan 19, 2023
72fbe8a
update fevm-test-vectors
samuerio Jan 19, 2023
998ef19
run update
amiable169 Jan 19, 2023
bb8b4a6
run update
amiable169 Jan 19, 2023
8e1d870
Merge remote-tracking branch 'origin/fevm-test-vectors' into fevm-tes…
amiable169 Jan 19, 2023
86f82ef
show transaction to test-vectors progress bar
amiable169 Jan 19, 2023
e321c4e
run script adjust
amiable169 Jan 19, 2023
809ae96
update re-generate subcommand
samuerio Jan 20, 2023
72c4cc6
update fevm-test-vectors
samuerio Jan 21, 2023
e30d11f
add return_data to test_report
samuerio Jan 25, 2023
c5374ab
add consume subcommand notice
samuerio Jan 28, 2023
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
1,845 changes: 1,797 additions & 48 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"testing/conformance",
"testing/integration",
"testing/calibration",
"testing/fevm_test_vectors",
"ipld/*",
"testing/integration/tests/*-actor",
"testing/calibration/contract/*-actor"
Expand Down
20 changes: 14 additions & 6 deletions testing/conformance/benches/bench_drivers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,23 @@ pub fn bench_vector_file(
// if we broke the test, it's not a valid optimization :P
let testresult = match check_strength {
CheckStrength::FullTest => {
run_variant(bs.clone(), vector, variant, engines, true, None, None).map_err(
|e| anyhow::anyhow!("run_variant failed (probably a test parsing bug): {}", e),
)?
}
CheckStrength::OnlyCheckSuccess => {
run_variant(bs.clone(), vector, variant, engines, false, None, None).map_err(
run_variant(bs.clone(), vector, variant, engines, true, None, None, None).map_err(
|e| anyhow::anyhow!("run_variant failed (probably a test parsing bug): {}", e),
)?
}
CheckStrength::OnlyCheckSuccess => run_variant(
bs.clone(),
vector,
variant,
engines,
false,
None,
None,
None,
)
.map_err(|e| {
anyhow::anyhow!("run_variant failed (probably a test parsing bug): {}", e)
})?,
CheckStrength::NoChecks => VariantResult::Ok {
id: variant.id.clone(),
},
Expand Down
117 changes: 89 additions & 28 deletions testing/conformance/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use libipld_core::ipld::Ipld;
use regex::Regex;
use walkdir::DirEntry;

use crate::tracing::TestTraceFun;
use crate::tracing::{CaptureApplyRetFun, TestTraceFun};
use crate::vector::{MessageVector, Variant};
use crate::vm::{TestKernel, TestMachine, TestStatsRef};

Expand Down Expand Up @@ -66,7 +66,12 @@ pub fn is_runnable(entry: &DirEntry) -> bool {
}

/// Compares the result of running a message with the expected result.
fn check_msg_result(expected_rec: &Receipt, ret: &ApplyRet, label: impl Display) -> Result<()> {
fn check_msg_result(
expected_rec: &Receipt,
ret: &ApplyRet,
label: impl Display,
skip_compare_gas_used: bool,
) -> Result<()> {
let error = ret
.failure_info
.as_ref()
Expand Down Expand Up @@ -94,14 +99,16 @@ fn check_msg_result(expected_rec: &Receipt, ret: &ApplyRet, label: impl Display)
));
}

let (expected, actual) = (expected_rec.gas_used, actual_rec.gas_used);
if expected != actual {
return Err(anyhow!(
"gas used of msg {} did not match; expected: {}, got {}",
label,
expected,
actual
));
if !skip_compare_gas_used {
let (expected, actual) = (expected_rec.gas_used, actual_rec.gas_used);
if expected != actual {
return Err(anyhow!(
"gas used of msg {} did not match; expected: {}, got {}",
label,
expected,
actual
));
}
}

Ok(())
Expand All @@ -112,9 +119,9 @@ fn compare_actors(
identifier: impl Display,
actual: Option<ActorState>,
expected: Option<ActorState>,
) -> Result<()> {
) -> Result<bool> {
if actual == expected {
return Ok(());
return Ok(true);
}
log::error!(
"{} actor state differs: {:?} != {:?}",
Expand All @@ -139,14 +146,24 @@ fn compare_actors(
}
_ => {}
}
Ok(())
Ok(false)
}

/// Compares the state-root with the postcondition state-root in the test vector. If they don't
/// match, it performs a basic actor & state-diff of the message senders and receivers in the test
/// vector, along with all system actors.
fn compare_state_roots(bs: &MemoryBlockstore, root: &Cid, vector: &MessageVector) -> Result<()> {
if root == &vector.postconditions.state_tree.root_cid {
let skip_compare_addresses = vector.skip_compare_addresses.clone();
let skip_compare_actor_ids = vector.skip_compare_actor_ids.clone();
let additional_compare_addresses = vector.additional_compare_addresses.clone();

let mut need_compare_root = false;
let mut compare_actors_success = true;
if matches!(skip_compare_addresses, None) && matches!(skip_compare_actor_ids, None) {
need_compare_root = true;
}

if need_compare_root && root == &vector.postconditions.state_tree.root_cid {
return Ok(());
}

Expand All @@ -160,35 +177,68 @@ fn compare_state_roots(bs: &MemoryBlockstore, root: &Cid, vector: &MessageVector

for m in &vector.apply_messages {
let msg: Message = from_slice(&m.bytes)?;
let actual_actor = actual_st.get_actor_by_address(&msg.from)?;
let expected_actor = expected_st.get_actor_by_address(&msg.from)?;
compare_actors(bs, "sender", actual_actor, expected_actor)?;
if matches!(skip_compare_addresses.clone(), Some(skip_addrs) if !skip_addrs.contains(&msg.from))
{
let actual_actor = actual_st.get_actor_by_address(&msg.from)?;
let expected_actor = expected_st.get_actor_by_address(&msg.from)?;
if !compare_actors(bs, "sender", actual_actor, expected_actor)? {
compare_actors_success = false;
}
}

let actual_actor = actual_st.get_actor_by_address(&msg.to)?;
let expected_actor = expected_st.get_actor_by_address(&msg.to)?;
compare_actors(bs, "receiver", actual_actor, expected_actor)?;
if matches!(skip_compare_addresses.clone(), Some(skip_addrs) if !skip_addrs.contains(&msg.to))
{
let actual_actor = actual_st.get_actor_by_address(&msg.to)?;
let expected_actor = expected_st.get_actor_by_address(&msg.to)?;
if !compare_actors(bs, "receiver", actual_actor, expected_actor)? {
compare_actors_success = false;
}
}
}

if let Some(addrs) = additional_compare_addresses {
for addr in addrs {
let actual_actor = actual_st.get_actor_by_address(&addr)?;
let expected_actor = expected_st.get_actor_by_address(&addr)?;
if !compare_actors(bs, &addr.to_string(), actual_actor, expected_actor)? {
compare_actors_success = false;
}
}
}

// All system actors
for id in 0..100 {
if matches!(skip_compare_actor_ids.clone(), Some(skip_actor_ids) if skip_actor_ids.contains(&id))
{
continue;
}
let expected_actor = match expected_st.get_actor(id) {
Ok(act) => act,
Err(_) => continue, // we don't expect it anyways.
};
let actual_actor = actual_st.get_actor(id)?;
compare_actors(
if !compare_actors(
bs,
format_args!("builtin {}", id),
actual_actor,
expected_actor,
)?;
)? {
compare_actors_success = false;
}
}

Err(anyhow!(
"wrong post root cid; expected {}, but got {}",
&vector.postconditions.state_tree.root_cid,
root
))
if need_compare_root {
return Err(anyhow!(
"wrong post root cid; expected {}, but got {}",
&vector.postconditions.state_tree.root_cid,
root
));
} else {
if compare_actors_success {
return Ok(());
}
return Err(anyhow!("compare actors fail"));
}
}

/// Represents the result from running a vector.
Expand All @@ -209,6 +259,7 @@ pub fn run_variant(
mut check_correctness: bool,
stats: TestStatsRef,
trace: Option<TestTraceFun>,
capture_apply_ret_fn: Option<CaptureApplyRetFun>,
) -> anyhow::Result<VariantResult> {
let id = variant.id.clone();

Expand Down Expand Up @@ -243,6 +294,14 @@ pub fn run_variant(
let mut exec: DefaultExecutor<TestKernel> = DefaultExecutor::new(engine, machine)?;
let mut rets = Vec::new();

let capture_apply_ret_fn = if let Some(f) = capture_apply_ret_fn {
f
} else {
Box::new(move |_| {
return Ok(());
})
};

// Apply all messages in the vector.
for (i, m) in v.apply_messages.iter().enumerate() {
let msg: Message = from_slice(&m.bytes)?;
Expand All @@ -260,10 +319,12 @@ pub fn run_variant(
Err(e) => return Ok(VariantResult::Failed { id, reason: e }),
};

capture_apply_ret_fn((i as i32, ret.clone()))?;

if check_correctness {
// Compare the actual receipt with the expected receipt.
let expected_receipt = &v.postconditions.receipts[i];
if let Err(err) = check_msg_result(expected_receipt, &ret, i) {
if let Err(err) = check_msg_result(expected_receipt, &ret, i, v.skip_compare_gas_used) {
return Ok(VariantResult::Failed { id, reason: err });
}
}
Expand Down
12 changes: 10 additions & 2 deletions testing/conformance/src/externs.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
use anyhow::anyhow;
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use fvm::externs::{Chain, Consensus, Externs, Rand};
use fvm_shared::clock::ChainEpoch;
use fvm_shared::consensus::ConsensusFault;

use crate::rand::ReplayingRand;
use crate::vector::Randomness;
use crate::vector::{Randomness, TipsetCid};

/// The externs stub for testing. Forwards randomness requests to the randomness
/// replayer, which replays randomness stored in the vector.
pub struct TestExterns {
pub tipset_cids: Vec<TipsetCid>,
rand: ReplayingRand,
}

impl TestExterns {
/// Creates a new TestExterns from randomness contained in a vector.
pub fn new(r: &Randomness) -> Self {
TestExterns {
tipset_cids: Default::default(),
rand: ReplayingRand::new(r.as_slice()),
}
}
Expand Down Expand Up @@ -57,6 +60,11 @@ impl Consensus for TestExterns {

impl Chain for TestExterns {
fn get_tipset_cid(&self, _epoch: ChainEpoch) -> anyhow::Result<cid::Cid> {
todo!()
for tipset in &self.tipset_cids {
if tipset.epoch == _epoch {
return Ok(tipset.cid);
}
}
Err(anyhow!("cannot find tipset cid, epoch {}", _epoch))
}
}
2 changes: 2 additions & 0 deletions testing/conformance/src/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub type TestTrace = (Duration, ApplyRet);
/// from all messages in the tests, in the order of execution.
pub type TestTraceFun = Box<dyn FnOnce(Vec<TestTrace>) -> IoResult<()>>;

pub type CaptureApplyRetFun = Box<dyn Fn((i32, ApplyRet)) -> IoResult<()>>;

/// Tombstone of a single message execution.
#[derive(Serialize)]
pub struct TestMessageTombstone {
Expand Down
Loading