diff --git a/Cargo.lock b/Cargo.lock index f324d537a432..853c0976f34e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1125,6 +1125,7 @@ name = "conformance_tests" version = "0.1.0" dependencies = [ "actor", + "async-std", "base64 0.12.3", "clock", "colored", @@ -1146,6 +1147,7 @@ dependencies = [ "ipld_hamt", "lazy_static", "log", + "paramfetch", "pretty_env_logger", "regex", "runtime", @@ -2106,7 +2108,6 @@ dependencies = [ "async-std", "auth", "beacon", - "blake2b_simd", "chain", "chain_sync", "ctrlc", @@ -2128,8 +2129,7 @@ dependencies = [ "libp2p", "log", "message_pool", - "pbr", - "pin-project-lite", + "paramfetch", "pretty_env_logger", "rpc", "rpc-client", @@ -2137,7 +2137,6 @@ dependencies = [ "serde_json", "state_manager", "structopt", - "surf", "utils", "uuid", ] @@ -4305,6 +4304,22 @@ dependencies = [ "serde", ] +[[package]] +name = "paramfetch" +version = "0.1.0" +dependencies = [ + "async-std", + "blake2b_simd", + "fil_types", + "futures 0.3.6", + "log", + "pbr", + "pin-project-lite", + "serde", + "serde_json", + "surf", +] + [[package]] name = "parity-multiaddr" version = "0.9.2" diff --git a/blockchain/state_manager/src/lib.rs b/blockchain/state_manager/src/lib.rs index 82c6d0178bc6..a962796a649c 100644 --- a/blockchain/state_manager/src/lib.rs +++ b/blockchain/state_manager/src/lib.rs @@ -20,9 +20,7 @@ use flo_stream::Subscriber; use forest_blocks::{BlockHeader, Tipset, TipsetKeys}; use futures::channel::oneshot; use futures::stream::{FuturesUnordered, StreamExt}; -use interpreter::{ - resolve_to_key_addr, ApplyRet, BlockMessages, ChainRand, DefaultSyscalls, Rand, VM, -}; +use interpreter::{resolve_to_key_addr, ApplyRet, BlockMessages, ChainRand, Rand, VM}; use ipld_amt::Amt; use log::{trace, warn}; use message::{message_receipt, unsigned_message}; @@ -196,14 +194,14 @@ where { let mut buf_store = BufferedBlockStore::new(self.bs.as_ref()); // TODO change from statically using devnet params when needed - let mut vm = VM::<_, _, _, _>::new( + let mut vm = VM::<_, _, _, V>::new( p_state, &buf_store, epoch, - DefaultSyscalls::<_, V>::new(&buf_store), rand, base_fee, get_network_version_default, + None, )?; // Apply tipset messages @@ -276,14 +274,14 @@ where span!("state_call_raw", { let block_store = self.blockstore(); let buf_store = BufferedBlockStore::new(block_store); - let mut vm = VM::<_, _, _, _>::new( + let mut vm = VM::<_, _, _, V>::new( bstate, &buf_store, *bheight, - DefaultSyscalls::<_, V>::new(&buf_store), rand, 0.into(), get_network_version_default, + None, )?; if msg.gas_limit() == 0 { @@ -352,14 +350,14 @@ where .map_err(|_| Error::Other("Could not load tipset state".to_string()))?; let chain_rand = ChainRand::new(ts.key().to_owned()); - let mut vm = VM::<_, _, _, _>::new( + let mut vm = VM::<_, _, _, V>::new( &st, self.bs.as_ref(), ts.epoch() + 1, - DefaultSyscalls::<_, V>::new(self.bs.as_ref()), &chain_rand, ts.blocks()[0].parent_base_fee().clone(), get_network_version_default, + None, )?; for msg in prior_messages { diff --git a/crypto/src/randomness.rs b/crypto/src/randomness.rs index eed5e6617963..8347ef33d6f7 100644 --- a/crypto/src/randomness.rs +++ b/crypto/src/randomness.rs @@ -1,11 +1,12 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use encoding::repr::*; use num_derive::FromPrimitive; use num_traits::FromPrimitive; /// Specifies a domain for randomness generation. -#[derive(PartialEq, Eq, Copy, Clone, FromPrimitive, Debug, Hash)] +#[derive(PartialEq, Eq, Copy, Clone, FromPrimitive, Debug, Hash, Deserialize_repr)] #[repr(i64)] pub enum DomainSeparationTag { TicketProduction = 1, diff --git a/forest/Cargo.toml b/forest/Cargo.toml index 29ca89bea16f..23da5e80bff3 100644 --- a/forest/Cargo.toml +++ b/forest/Cargo.toml @@ -33,10 +33,6 @@ rpc = { path = "../node/rpc" } rpc_client = { package = "rpc-client", path = "../node/rpc-client" } fil_types = { path = "../types" } serde_json = "1.0" -blake2b_simd = "0.5.9" -surf = "2.0" -pbr = "1.0.3" -pin-project-lite = "0.1" message_pool = { package = "message_pool", path = "../blockchain/message_pool" } wallet = { package = "key_management", path = "../key_management" } jsonrpc-v2 = { version = "0.5.2", git = "https://github.com/ChainSafe/jsonrpc-v2", features = ["easy-errors", "macros"], default-features = false } @@ -44,3 +40,4 @@ uuid = { version = "0.8.1", features = ["v4"] } auth = { path = "../utils/auth"} actor = { path = "../vm/actor/" } genesis = { path = "../utils/genesis" } +paramfetch = { path = "../utils/paramfetch" } diff --git a/forest/src/cli/fetch_params_cmd.rs b/forest/src/cli/fetch_params_cmd.rs index 15464e404684..2408d8feb52d 100644 --- a/forest/src/cli/fetch_params_cmd.rs +++ b/forest/src/cli/fetch_params_cmd.rs @@ -1,8 +1,8 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::paramfetch::{get_params_default, SectorSizeOpt}; use fil_types::SectorSize; +use paramfetch::{get_params_default, SectorSizeOpt}; use structopt::StructOpt; #[allow(missing_docs)] diff --git a/forest/src/daemon.rs b/forest/src/daemon.rs index f187c318f6e4..85f9ffcef402 100644 --- a/forest/src/daemon.rs +++ b/forest/src/daemon.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0, MIT use super::cli::{block_until_sigint, Config}; -use super::paramfetch::{get_params_default, SectorSizeOpt}; use actor::EPOCH_DURATION_SECONDS; use async_std::sync::RwLock; use async_std::task; @@ -18,6 +17,7 @@ use genesis::initialize_genesis; use libp2p::identity::{ed25519, Keypair}; use log::{debug, info, trace}; use message_pool::{MessagePool, MpoolConfig, MpoolRpcProvider}; +use paramfetch::{get_params_default, SectorSizeOpt}; use rpc::{start_rpc, RpcState}; use state_manager::StateManager; use std::sync::Arc; diff --git a/forest/src/main.rs b/forest/src/main.rs index e0db1d7d4ff8..c813ebbec3af 100644 --- a/forest/src/main.rs +++ b/forest/src/main.rs @@ -4,7 +4,6 @@ mod cli; mod daemon; mod logger; -pub(crate) mod paramfetch; mod subcommand; use cli::CLI; diff --git a/tests/conformance_tests/Cargo.toml b/tests/conformance_tests/Cargo.toml index e0df43c5ae40..caa7dffeacec 100644 --- a/tests/conformance_tests/Cargo.toml +++ b/tests/conformance_tests/Cargo.toml @@ -5,6 +5,7 @@ authors = ["ChainSafe Systems "] edition = "2018" [features] +default = [] submodule_tests = [ "serde", "cid", @@ -62,3 +63,5 @@ colored = "2.0" ipld = { package = "forest_ipld", path = "../../ipld", features = ["json"] } ipld_hamt = { path = "../../ipld/hamt", features = ["ignore-dead-links"] } log = "0.4" +paramfetch = { path = "../../utils/paramfetch" } +async-std = "1.6" \ No newline at end of file diff --git a/tests/conformance_tests/src/lib.rs b/tests/conformance_tests/src/lib.rs index 49aecc99c376..385bb6472978 100644 --- a/tests/conformance_tests/src/lib.rs +++ b/tests/conformance_tests/src/lib.rs @@ -4,10 +4,12 @@ #![cfg(feature = "submodule_tests")] mod message; +mod rand_replay; mod stubs; mod tipset; pub use self::message::*; +pub use self::rand_replay::*; pub use self::stubs::*; pub use self::tipset::*; use actor::CHAOS_ACTOR_CODE_ID; @@ -16,7 +18,7 @@ use blockstore::BlockStore; use cid::Cid; use clock::ChainEpoch; use crypto::{DomainSeparationTag, Signature}; -use encoding::Cbor; +use encoding::{tuple::*, Cbor}; use fil_types::{SealVerifyInfo, WindowPoStVerifyInfo}; use forest_message::{ChainMessage, Message, MessageReceipt, SignedMessage, UnsignedMessage}; use interpreter::{ApplyRet, BlockMessages, Rand, VM}; @@ -86,7 +88,7 @@ pub struct StateTreeVector { pub root_cid: Cid, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct GenerationData { #[serde(default)] pub source: String, @@ -94,7 +96,7 @@ pub struct GenerationData { pub version: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct MetaData { pub id: String, #[serde(default)] @@ -108,10 +110,13 @@ pub struct MetaData { #[derive(Debug, Deserialize)] pub struct PreConditions { - pub epoch: ChainEpoch, pub state_tree: StateTreeVector, #[serde(default)] pub basefee: Option, + #[serde(default)] + pub circ_supply: Option, + #[serde(default)] + pub variants: Vec, } #[derive(Debug, Deserialize)] @@ -129,6 +134,43 @@ pub struct Selector { pub puppet_actor: Option, #[serde(default)] pub chaos_actor: Option, + #[serde(default)] + pub min_protocol_version: Option, +} + +#[derive(Debug, Deserialize)] +pub struct Variant { + pub id: String, + pub epoch: ChainEpoch, + pub nv: u32, +} + +/// Encoded VM randomness used to be replayed. +pub type Randomness = Vec; + +/// One randomness entry. +#[derive(Debug, Deserialize)] +pub struct RandomnessMatch { + pub on: RandomnessRule, + #[serde(with = "base64_bytes")] + pub ret: Vec, +} + +#[derive(Debug, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum RandomnessKind { + Beacon, + Chain, +} + +/// Rule for matching when randomness is returned. +#[derive(Debug, Deserialize_tuple, PartialEq)] +pub struct RandomnessRule { + pub kind: RandomnessKind, + pub dst: DomainSeparationTag, + pub epoch: ChainEpoch, + #[serde(with = "base64_bytes")] + pub entropy: Vec, } #[derive(Debug, Deserialize)] @@ -145,12 +187,9 @@ pub enum TestVector { preconditions: PreConditions, apply_messages: Vec, postconditions: PostConditions, - }, - #[serde(rename = "block")] - Block { - selector: Option, - #[serde(rename = "_meta")] - meta: Option, + + #[serde(default)] + randomness: Randomness, }, #[serde(rename = "tipset")] Tipset { @@ -164,12 +203,6 @@ pub enum TestVector { apply_tipsets: Vec, postconditions: PostConditions, }, - #[serde(rename = "chain")] - Chain { - selector: Option, - #[serde(rename = "_meta")] - meta: Option, - }, } // This might be changed to be encoded into vector, matching go runner for now diff --git a/tests/conformance_tests/src/message.rs b/tests/conformance_tests/src/message.rs index 641157e441a9..0df79f6157b3 100644 --- a/tests/conformance_tests/src/message.rs +++ b/tests/conformance_tests/src/message.rs @@ -10,25 +10,32 @@ pub struct MessageVector { #[serde(with = "base64_bytes")] pub bytes: Vec, #[serde(default)] - pub epoch: Option, + pub epoch_offset: Option, +} + +pub struct ExecuteMessageParams<'a> { + pub pre_root: &'a Cid, + pub epoch: ChainEpoch, + pub msg: &'a ChainMessage, + pub circ_supply: TokenAmount, + pub basefee: TokenAmount, + pub randomness: ReplayingRand<'a>, } pub fn execute_message( bs: &db::MemoryDB, - msg: &ChainMessage, - pre_root: &Cid, - epoch: ChainEpoch, - basefee: TokenAmount, selector: &Option, + params: ExecuteMessageParams, ) -> Result<(ApplyRet, Cid), Box> { - let mut vm = VM::<_, _, _, _>::new( - pre_root, + let circ_supply = params.circ_supply; + let mut vm = VM::<_, _, _>::new( + params.pre_root, bs, - epoch, - TestSyscalls, - &TestRand, - basefee, + params.epoch, + ¶ms.randomness, + params.basefee, get_network_version_default, + Some(Box::new(move |_, _| Ok(circ_supply.clone()))), )?; if let Some(s) = &selector { @@ -41,7 +48,7 @@ pub fn execute_message( } } - let ret = vm.apply_message(msg)?; + let ret = vm.apply_message(params.msg)?; let root = vm.flush()?; Ok((ret, root)) diff --git a/tests/conformance_tests/src/rand_replay.rs b/tests/conformance_tests/src/rand_replay.rs new file mode 100644 index 000000000000..8c7753fac723 --- /dev/null +++ b/tests/conformance_tests/src/rand_replay.rs @@ -0,0 +1,70 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use super::*; + +pub struct ReplayingRand<'a> { + pub recorded: &'a [RandomnessMatch], + pub fallback: TestRand, +} + +impl<'a> ReplayingRand<'a> { + pub fn new(recorded: &'a [RandomnessMatch]) -> Self { + Self { + recorded, + fallback: TestRand, + } + } + + pub fn matches(&self, requested: RandomnessRule) -> Option<[u8; 32]> { + for other in self.recorded { + if other.on == requested { + let mut randomness = [0u8; 32]; + randomness.copy_from_slice(&other.ret); + return Some(randomness); + } + } + None + } +} + +impl Rand for ReplayingRand<'_> { + fn get_chain_randomness( + &self, + db: &DB, + dst: DomainSeparationTag, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + let rule = RandomnessRule { + kind: RandomnessKind::Chain, + dst, + epoch, + entropy: entropy.to_vec(), + }; + if let Some(bz) = self.matches(rule) { + Ok(bz) + } else { + self.fallback.get_chain_randomness(db, dst, epoch, entropy) + } + } + fn get_beacon_randomness( + &self, + db: &DB, + dst: DomainSeparationTag, + epoch: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + let rule = RandomnessRule { + kind: RandomnessKind::Beacon, + dst, + epoch, + entropy: entropy.to_vec(), + }; + if let Some(bz) = self.matches(rule) { + Ok(bz) + } else { + self.fallback.get_beacon_randomness(db, dst, epoch, entropy) + } + } +} diff --git a/tests/conformance_tests/src/tipset.rs b/tests/conformance_tests/src/tipset.rs index 4bf74478834d..ed930a5e4034 100644 --- a/tests/conformance_tests/src/tipset.rs +++ b/tests/conformance_tests/src/tipset.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use super::*; -use fil_types::verifier::MockVerifier; +use fil_types::verifier::FullVerifier; use num_bigint::ToBigInt; use state_manager::StateManager; use std::sync::Arc; @@ -56,7 +56,7 @@ mod block_messages_json { #[derive(Debug, Deserialize)] pub struct TipsetVector { - pub epoch: ChainEpoch, + pub epoch_offset: ChainEpoch, pub basefee: f64, #[serde(with = "block_messages_json")] pub blocks: Vec, @@ -74,15 +74,16 @@ pub fn execute_tipset( pre_root: &Cid, parent_epoch: ChainEpoch, tipset: &TipsetVector, + exec_epoch: ChainEpoch, ) -> Result> { let sm = StateManager::new(bs); let mut _applied_messages = Vec::new(); let mut applied_results = Vec::new(); - let (post_state_root, receipts_root) = sm.apply_blocks::<_, MockVerifier, _>( + let (post_state_root, receipts_root) = sm.apply_blocks::<_, FullVerifier, _>( parent_epoch, pre_root, &tipset.blocks, - tipset.epoch, + exec_epoch, &TestRand, tipset.basefee.to_bigint().unwrap_or_default(), Some(|_, msg: &ChainMessage, ret| { diff --git a/tests/conformance_tests/test-vectors b/tests/conformance_tests/test-vectors index 75f517e3666c..d9a75a7873ae 160000 --- a/tests/conformance_tests/test-vectors +++ b/tests/conformance_tests/test-vectors @@ -1 +1 @@ -Subproject commit 75f517e3666cd6f10430de62430eb5354cb6276a +Subproject commit d9a75a7873aee0db28b87e3970d2ea16a2f37c6a diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 40719fdfbb2c..da62fbabe276 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -9,11 +9,12 @@ extern crate lazy_static; use address::Address; use blockstore::resolve::resolve_cids_recursive; use cid::{json::CidJson, Cid}; +use clock::ChainEpoch; use colored::*; use conformance_tests::*; use difference::{Changeset, Difference}; use encoding::Cbor; -use fil_types::HAMT_BIT_WIDTH; +use fil_types::{HAMT_BIT_WIDTH, TOTAL_FILECOIN}; use flate2::read::GzDecoder; use forest_message::{MessageReceipt, UnsignedMessage}; use interpreter::ApplyRet; @@ -21,6 +22,7 @@ use ipld::json::{IpldJson, IpldJsonRef}; use ipld::Ipld; use ipld_hamt::{BytesKey, Hamt}; use num_bigint::{BigInt, ToBigInt}; +use paramfetch::{get_params_default, SectorSizeOpt}; use regex::Regex; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -33,7 +35,7 @@ use vm::ActorState; use walkdir::{DirEntry, WalkDir}; lazy_static! { - static ref DEFAULT_BASE_FEE: BigInt = 100.to_bigint().unwrap(); + static ref DEFAULT_BASE_FEE: BigInt = BigInt::from(100); static ref SKIP_TESTS: Vec = vec![ Regex::new(r"test-vectors/corpus/vm_violations/x--").unwrap(), Regex::new(r"test-vectors/corpus/nested/x--").unwrap(), @@ -55,9 +57,10 @@ lazy_static! { // Extracted miner faults Regex::new(r"fil_1_storageminer-DeclareFaults-Ok-3").unwrap(), Regex::new(r"fil_1_storageminer-DeclareFaults-Ok-7").unwrap(), + Regex::new(r"fil_1_storageminer-SubmitWindowedPoSt").unwrap(), - // Extracted market faults - Regex::new(r"fil_1_storagemarket-PublishStorageDeals-").unwrap(), + // Other fault + Regex::new(r"fail-insufficient-funds-for-transfer-in-inner-send--genesis").unwrap(), ]; } @@ -229,34 +232,43 @@ fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Re } fn execute_message_vector( - selector: Option, - car: Vec, - preconditions: PreConditions, - apply_messages: Vec, - postconditions: PostConditions, + selector: &Option, + car: &[u8], + root_cid: Cid, + base_fee: Option, + circ_supply: Option, + apply_messages: &[MessageVector], + postconditions: &PostConditions, + randomness: &Randomness, + variant: &Variant, ) -> Result<(), Box> { - let bs = load_car(car.as_slice())?; + let bs = load_car(car)?; - let mut epoch = preconditions.epoch; - let mut root = preconditions.state_tree.root_cid; + let mut base_epoch: ChainEpoch = variant.epoch; + let mut root = root_cid; for (i, m) in apply_messages.iter().enumerate() { let msg = UnsignedMessage::unmarshal_cbor(&m.bytes)?; - if let Some(ep) = m.epoch { - epoch = ep; + if let Some(ep) = m.epoch_offset { + base_epoch += ep; } let (ret, post_root) = execute_message( &bs, - &to_chain_msg(msg), - &root, - epoch, - preconditions - .basefee - .map(|i| i.to_bigint().unwrap()) - .unwrap_or(DEFAULT_BASE_FEE.clone()), &selector, + ExecuteMessageParams { + pre_root: &root, + epoch: base_epoch, + msg: &to_chain_msg(msg), + circ_supply: circ_supply + .map(|i| i.to_bigint().unwrap()) + .unwrap_or(TOTAL_FILECOIN.clone()), + basefee: base_fee + .map(|i| i.to_bigint().unwrap()) + .unwrap_or(DEFAULT_BASE_FEE.clone()), + randomness: ReplayingRand::new(randomness), + }, )?; root = post_root; @@ -270,25 +282,28 @@ fn execute_message_vector( } fn execute_tipset_vector( - _selector: Option, - car: Vec, - preconditions: PreConditions, - tipsets: Vec, - postconditions: PostConditions, + _selector: &Option, + car: &[u8], + root_cid: Cid, + tipsets: &[TipsetVector], + postconditions: &PostConditions, + variant: &Variant, ) -> Result<(), Box> { - let bs = Arc::new(load_car(car.as_slice())?); + let bs = Arc::new(load_car(car)?); - let mut prev_epoch = preconditions.epoch; - let mut root = preconditions.state_tree.root_cid; + let base_epoch = variant.epoch; + let mut root = root_cid; let mut receipt_idx = 0; + let mut prev_epoch = base_epoch; for (i, ts) in tipsets.into_iter().enumerate() { + let exec_epoch = base_epoch + ts.epoch_offset; let ExecuteTipsetResult { receipts_root, post_state_root, applied_results, .. - } = execute_tipset(Arc::clone(&bs), &root, prev_epoch, &ts)?; + } = execute_tipset(Arc::clone(&bs), &root, prev_epoch, &ts, exec_epoch)?; for (j, apply_ret) in applied_results.into_iter().enumerate() { check_msg_result( @@ -309,7 +324,7 @@ fn execute_tipset_vector( .into()); } - prev_epoch = ts.epoch; + prev_epoch = exec_epoch; root = post_state_root; } @@ -321,6 +336,10 @@ fn execute_tipset_vector( #[test] fn conformance_test_runner() { pretty_env_logger::init(); + + // Retrieve verification params + async_std::task::block_on(get_params_default(SectorSizeOpt::Keys, false)).unwrap(); + let walker = WalkDir::new("test-vectors/corpus").into_iter(); let mut failed = Vec::new(); let mut succeeded = 0; @@ -328,13 +347,7 @@ fn conformance_test_runner() { let file = File::open(entry.path()).unwrap(); let reader = BufReader::new(file); let test_name = entry.path().display(); - let vector: TestVector = match serde_json::from_reader(reader) { - Ok(v) => v, - Err(e) => { - log::warn!("Could not deserialize vector: {}", e); - continue; - } - }; + let vector: TestVector = serde_json::from_reader(reader).unwrap(); match vector { TestVector::Message { @@ -344,18 +357,33 @@ fn conformance_test_runner() { preconditions, apply_messages, postconditions, + randomness, } => { - if let Err(e) = execute_message_vector( - selector, - car, - preconditions, - apply_messages, - postconditions, - ) { - failed.push((test_name.to_string(), meta, e)); - } else { - println!("{} succeeded", test_name); - succeeded += 1; + for variant in preconditions.variants { + if variant.nv > 3 { + // Skip v2 upgrade and above + continue; + } + if let Err(e) = execute_message_vector( + &selector, + &car, + preconditions.state_tree.root_cid.clone(), + preconditions.basefee, + preconditions.circ_supply, + &apply_messages, + &postconditions, + &randomness, + &variant, + ) { + failed.push(( + format!("{} variant {}", test_name, variant.id), + meta.clone(), + e, + )); + } else { + println!("{} succeeded", test_name); + succeeded += 1; + } } } TestVector::Tipset { @@ -366,20 +394,30 @@ fn conformance_test_runner() { apply_tipsets, postconditions, } => { - if let Err(e) = execute_tipset_vector( - selector, - car, - preconditions, - apply_tipsets, - postconditions, - ) { - failed.push((test_name.to_string(), meta, e)); - } else { - println!("{} succeeded", test_name); - succeeded += 1; + for variant in preconditions.variants { + if variant.nv > 3 { + // Skip v2 upgrade and above + continue; + } + if let Err(e) = execute_tipset_vector( + &selector, + &car, + preconditions.state_tree.root_cid.clone(), + &apply_tipsets, + &postconditions, + &variant, + ) { + failed.push(( + format!("{} variant {}", test_name, variant.id), + meta.clone(), + e, + )); + } else { + println!("{} succeeded", test_name); + succeeded += 1; + } } } - _ => panic!("Unsupported test vector class"), } } diff --git a/utils/paramfetch/Cargo.toml b/utils/paramfetch/Cargo.toml new file mode 100644 index 000000000000..50bdf6305bf5 --- /dev/null +++ b/utils/paramfetch/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "paramfetch" +version = "0.1.0" +authors = ["ChainSafe Systems "] +edition = "2018" + +[dependencies] +surf = "2.0" +async-std = { version = "1.6.3", features = ["attributes"] } +pbr = "1.0.3" +futures = "0.3.5" +pin-project-lite = "0.1" +fil_types = { path = "../../types" } +log = "0.4.8" +blake2b_simd = "0.5.9" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/forest/src/paramfetch/mod.rs b/utils/paramfetch/src/lib.rs similarity index 100% rename from forest/src/paramfetch/mod.rs rename to utils/paramfetch/src/lib.rs diff --git a/forest/src/paramfetch/parameters.json b/utils/paramfetch/src/parameters.json similarity index 100% rename from forest/src/paramfetch/parameters.json rename to utils/paramfetch/src/parameters.json diff --git a/vm/actor/src/builtin/miner/state.rs b/vm/actor/src/builtin/miner/state.rs index 87bf13a4ae92..1fe425f1fdc0 100644 --- a/vm/actor/src/builtin/miner/state.rs +++ b/vm/actor/src/builtin/miner/state.rs @@ -603,7 +603,7 @@ impl State { proven_sectors: &BitField, expected_faults: &BitField, ) -> Result, Box> { - let non_faults = expected_faults - proven_sectors; + let non_faults = proven_sectors - expected_faults; if non_faults.is_empty() { return Ok(Vec::new()); diff --git a/vm/interpreter/src/default_runtime.rs b/vm/interpreter/src/default_runtime.rs index 9bbc7e7da524..6f17b2f1415b 100644 --- a/vm/interpreter/src/default_runtime.rs +++ b/vm/interpreter/src/default_runtime.rs @@ -2,27 +2,32 @@ // SPDX-License-Identifier: Apache-2.0, MIT use super::gas_block_store::GasBlockStore; -use super::gas_syscalls::GasSyscalls; use super::gas_tracker::{price_list_by_epoch, GasCharge, GasTracker, PriceList}; -use super::Rand; +use super::{CircSupplyCalc, Rand}; use actor::*; use address::{Address, Protocol}; +use blocks::BlockHeader; use byteorder::{BigEndian, WriteBytesExt}; use cid::{multihash::Blake2b256, Cid}; use clock::ChainEpoch; -use crypto::DomainSeparationTag; -use fil_types::{DevnetParams, NetworkParams, NetworkVersion, Randomness}; -use forest_encoding::to_vec; -use forest_encoding::Cbor; +use crypto::{DomainSeparationTag, Signature}; +use fil_types::{verifier::ProofVerifier, DevnetParams, NetworkParams, NetworkVersion, Randomness}; +use fil_types::{PieceInfo, RegisteredSealProof, SealVerifyInfo, WindowPoStVerifyInfo}; +use forest_encoding::{blake2b_256, to_vec, Cbor}; use ipld_blockstore::BlockStore; use log::warn; use message::{Message, UnsignedMessage}; use num_bigint::BigInt; use num_traits::Zero; -use runtime::{ActorCode, MessageInfo, Runtime, Syscalls}; +use rayon::prelude::*; +use runtime::{ + compute_unsealed_sector_cid, ActorCode, ConsensusFault, ConsensusFaultType, MessageInfo, + Runtime, Syscalls, +}; use state_tree::StateTree; use std::cell::RefCell; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; +use std::error::Error as StdError; use std::marker::PhantomData; use std::rc::Rc; use vm::{ @@ -57,11 +62,10 @@ impl MessageInfo for VMMsg { } /// Implementation of the Runtime trait. -pub struct DefaultRuntime<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P = DevnetParams> { +pub struct DefaultRuntime<'db, 'vm, BS, R, V, P = DevnetParams> { version: NetworkVersion, - state: &'st mut StateTree<'db, BS>, + state: &'vm mut StateTree<'db, BS>, store: GasBlockStore<'db, BS>, - syscalls: GasSyscalls<'sys, SYS>, gas_tracker: Rc>, vm_msg: VMMsg, epoch: ChainEpoch, @@ -69,18 +73,19 @@ pub struct DefaultRuntime<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P = DevnetParams origin_nonce: u64, num_actors_created: u64, price_list: PriceList, - rand: &'r R, + rand: &'vm R, caller_validated: bool, allow_internal: bool, - registered_actors: &'act HashSet, + registered_actors: &'vm HashSet, + circ_supply_calc: &'vm Option>, + verifier: PhantomData, params: PhantomData

, } -impl<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P> - DefaultRuntime<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P> +impl<'db, 'vm, BS, R, V, P> DefaultRuntime<'db, 'vm, BS, R, V, P> where BS: BlockStore, - SYS: Syscalls, + V: ProofVerifier, P: NetworkParams, R: Rand, { @@ -88,17 +93,17 @@ where #[allow(clippy::too_many_arguments)] pub fn new( version: NetworkVersion, - state: &'st mut StateTree<'db, BS>, + state: &'vm mut StateTree<'db, BS>, store: &'db BS, - syscalls: &'sys SYS, gas_used: i64, message: &UnsignedMessage, epoch: ChainEpoch, origin: Address, origin_nonce: u64, num_actors_created: u64, - rand: &'r R, - registered_actors: &'act HashSet, + rand: &'vm R, + registered_actors: &'vm HashSet, + circ_supply_calc: &'vm Option>, ) -> Result { let price_list = price_list_by_epoch(epoch); let gas_tracker = Rc::new(RefCell::new(GasTracker::new(message.gas_limit(), gas_used))); @@ -107,11 +112,6 @@ where gas: Rc::clone(&gas_tracker), store, }; - let gas_syscalls = GasSyscalls { - price_list: price_list.clone(), - gas: Rc::clone(&gas_tracker), - syscalls, - }; let caller_id = state .lookup_id(&message.from()) @@ -130,7 +130,6 @@ where version, state, store: gas_block_store, - syscalls: gas_syscalls, gas_tracker, vm_msg, epoch, @@ -140,9 +139,11 @@ where price_list, rand, registered_actors, + circ_supply_calc, allow_internal: true, caller_validated: false, params: PhantomData, + verifier: PhantomData, }) } @@ -273,7 +274,7 @@ where }; self.caller_validated = false; - let send_res = vm_send::(self, &msg, None); + let send_res = vm_send::(self, &msg, None); // Reset values back to their values before the call self.vm_msg = prev_msg; @@ -332,13 +333,29 @@ where Ok(act) } + + fn verify_block_signature(&self, bh: &BlockHeader) -> Result<(), Box> { + let actor = self + .state + .get_actor(bh.miner_address())? + .ok_or_else(|| format!("actor not found {:?}", bh.miner_address()))?; + + let ms: miner::State = self + .store + .get(&actor.state)? + .ok_or_else(|| format!("actor state not found {:?}", actor.state.to_string()))?; + + let info = ms.get_info(&self.store)?; + let work_address = resolve_to_key_addr(&self.state, &self.store, &info.worker)?; + bh.check_block_signature(&work_address)?; + Ok(()) + } } -impl<'bs, BS, SYS, R, P> Runtime> - for DefaultRuntime<'bs, '_, '_, '_, '_, BS, SYS, R, P> +impl<'bs, BS, R, V, P> Runtime> for DefaultRuntime<'bs, '_, BS, R, V, P> where BS: BlockStore, - SYS: Syscalls, + V: ProofVerifier, P: NetworkParams, R: Rand, { @@ -427,7 +444,7 @@ where ) -> Result { let r = self .rand - .get_chain_randomness(&self.store, personalization, rand_epoch, entropy) + .get_beacon_randomness(&self.store, personalization, rand_epoch, entropy) .map_err(|e| e.downcast_fatal("could not get randomness"))?; Ok(Randomness(r)) @@ -596,9 +613,15 @@ where .map_err(|e| e.downcast_fatal("failed to delete actor")) } fn syscalls(&self) -> &dyn Syscalls { - &self.syscalls + self } fn total_fil_circ_supply(&self) -> Result { + if let Some(circ_supply_calc) = self.circ_supply_calc.as_ref() { + // TODO all circ supply calculations should go through trait and not only override + return circ_supply_calc(self.epoch, &self.state).map_err(|e| { + actor_error!(ErrIllegalState, "failed to get total circ supply: {}", e) + }); + } let get_actor_state = |addr: &Address| -> Result { self.state .get_actor(&addr) @@ -641,16 +664,201 @@ where } } +impl<'bs, BS, R, V, P> Syscalls for DefaultRuntime<'bs, '_, BS, R, V, P> +where + BS: BlockStore, + V: ProofVerifier, + P: NetworkParams, + R: Rand, +{ + fn verify_signature( + &self, + signature: &Signature, + signer: &Address, + plaintext: &[u8], + ) -> Result<(), Box> { + self.gas_tracker.borrow_mut().charge_gas( + self.price_list + .on_verify_signature(signature.signature_type()), + )?; + + // Resolve to key address before verifying signature. + let signing_addr = resolve_to_key_addr(self.state, &self.store, signer)?; + Ok(signature.verify(plaintext, &signing_addr)?) + } + fn hash_blake2b(&self, data: &[u8]) -> Result<[u8; 32], Box> { + self.gas_tracker + .borrow_mut() + .charge_gas(self.price_list.on_hashing(data.len()))?; + + Ok(blake2b_256(data)) + } + fn compute_unsealed_sector_cid( + &self, + reg: RegisteredSealProof, + pieces: &[PieceInfo], + ) -> Result> { + self.gas_tracker + .borrow_mut() + .charge_gas(self.price_list.on_compute_unsealed_sector_cid(reg, pieces))?; + + compute_unsealed_sector_cid(reg, pieces) + } + fn verify_seal(&self, vi: &SealVerifyInfo) -> Result<(), Box> { + self.gas_tracker + .borrow_mut() + .charge_gas(self.price_list.on_verify_seal(vi))?; + + V::verify_seal(vi) + } + fn verify_post(&self, vi: &WindowPoStVerifyInfo) -> Result<(), Box> { + self.gas_tracker + .borrow_mut() + .charge_gas(self.price_list.on_verify_post(vi))?; + + V::verify_window_post(vi.randomness, &vi.proofs, &vi.challenged_sectors, vi.prover) + } + fn verify_consensus_fault( + &self, + h1: &[u8], + h2: &[u8], + extra: &[u8], + ) -> Result, Box> { + self.gas_tracker + .borrow_mut() + .charge_gas(self.price_list.on_verify_consensus_fault())?; + + // Note that block syntax is not validated. Any validly signed block will be accepted pursuant to the below conditions. + // Whether or not it could ever have been accepted in a chain is not checked/does not matter here. + // for that reason when checking block parent relationships, rather than instantiating a Tipset to do so + // (which runs a syntactic check), we do it directly on the CIDs. + + // (0) cheap preliminary checks + + if h1 == h2 { + return Err(format!( + "no consensus fault: submitted blocks are the same: {:?}, {:?}", + h1, h2 + ) + .into()); + }; + let bh_1 = BlockHeader::unmarshal_cbor(h1)?; + let bh_2 = BlockHeader::unmarshal_cbor(h2)?; + + // (1) check conditions necessary to any consensus fault + + if bh_1.miner_address() != bh_2.miner_address() { + return Err(format!( + "no consensus fault: blocks not mined by same miner: {:?}, {:?}", + bh_1.miner_address(), + bh_2.miner_address() + ) + .into()); + }; + // block a must be earlier or equal to block b, epoch wise (ie at least as early in the chain). + if bh_1.epoch() < bh_2.epoch() { + return Err(format!( + "first block must not be of higher height than second: {:?}, {:?}", + bh_1.epoch(), + bh_2.epoch() + ) + .into()); + }; + let mut cf: Option = None; + // (a) double-fork mining fault + if bh_1.epoch() == bh_2.epoch() { + cf = Some(ConsensusFault { + target: *bh_1.miner_address(), + epoch: bh_2.epoch(), + fault_type: ConsensusFaultType::DoubleForkMining, + }) + }; + // (b) time-offset mining fault + // strictly speaking no need to compare heights based on double fork mining check above, + // but at same height this would be a different fault. + if bh_1.parents() != bh_2.parents() && bh_1.epoch() != bh_2.epoch() { + cf = Some(ConsensusFault { + target: *bh_1.miner_address(), + epoch: bh_2.epoch(), + fault_type: ConsensusFaultType::TimeOffsetMining, + }) + }; + // (c) parent-grinding fault + // Here extra is the "witness", a third block that shows the connection between A and B as + // A's sibling and B's parent. + // Specifically, since A is of lower height, it must be that B was mined omitting A from its tipset + if !extra.is_empty() { + let bh_3 = BlockHeader::unmarshal_cbor(extra)?; + if bh_1.parents() != bh_3.parents() + && bh_1.epoch() != bh_3.epoch() + && bh_2.parents().cids().contains(bh_3.cid()) + && !bh_2.parents().cids().contains(bh_1.cid()) + { + cf = Some(ConsensusFault { + target: *bh_1.miner_address(), + epoch: bh_2.epoch(), + fault_type: ConsensusFaultType::ParentGrinding, + }) + } + }; + + // (3) return if no consensus fault by now + if cf.is_none() { + Ok(cf) + } else { + // (4) expensive final checks + + // check blocks are properly signed by their respective miner + // note we do not need to check extra's: it is a parent to block b + // which itself is signed, so it was willingly included by the miner + self.verify_block_signature(&bh_1)?; + self.verify_block_signature(&bh_2)?; + + Ok(cf) + } + } + + fn batch_verify_seals( + &self, + vis: &[(Address, &Vec)], + ) -> Result>, Box> { + // Gas charged for batch verify in actor + + // TODO ideal to not use rayon https://github.com/ChainSafe/forest/issues/676 + let out = vis + .par_iter() + .map(|(addr, seals)| { + let results = seals + .par_iter() + .map(|s| { + if let Err(err) = V::verify_seal(s) { + warn!( + "seal verify in batch failed (miner: {}) (err: {})", + addr, err + ); + false + } else { + true + } + }) + .collect(); + (*addr, results) + }) + .collect(); + Ok(out) + } +} + /// Shared logic between the DefaultRuntime and the Interpreter. /// It invokes methods on different Actors based on the Message. -pub fn vm_send<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P>( - rt: &mut DefaultRuntime<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P>, +pub fn vm_send<'db, 'vm, BS, R, V, P>( + rt: &mut DefaultRuntime<'db, 'vm, BS, R, V, P>, msg: &UnsignedMessage, gas_cost: Option, ) -> Result where BS: BlockStore, - SYS: Syscalls, + V: ProofVerifier, P: NetworkParams, R: Rand, { @@ -751,8 +959,8 @@ fn transfer( } /// Calls actor code with method and parameters. -fn invoke<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P>( - rt: &mut DefaultRuntime<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P>, +fn invoke<'db, 'vm, BS, R, V, P>( + rt: &mut DefaultRuntime<'db, 'vm, BS, R, V, P>, code: Cid, method_num: MethodNum, params: &Serialized, @@ -760,7 +968,7 @@ fn invoke<'db, 'st, 'sys, 'r, 'act, BS, SYS, R, P>( ) -> Result where BS: BlockStore, - SYS: Syscalls, + V: ProofVerifier, P: NetworkParams, R: Rand, { diff --git a/vm/interpreter/src/default_syscalls.rs b/vm/interpreter/src/default_syscalls.rs deleted file mode 100644 index 5b38dacc01b4..000000000000 --- a/vm/interpreter/src/default_syscalls.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2020 ChainSafe Systems -// SPDX-License-Identifier: Apache-2.0, MIT - -use crate::resolve_to_key_addr; -use actor::miner; -use address::Address; -use blocks::BlockHeader; -use fil_types::{verifier::ProofVerifier, SealVerifyInfo, WindowPoStVerifyInfo}; -use forest_encoding::from_slice; -use ipld_blockstore::BlockStore; -use log::warn; -use rayon::prelude::*; -use runtime::{ConsensusFault, ConsensusFaultType, Syscalls}; -use state_tree::StateTree; -use std::marker::PhantomData; -use std::{collections::HashMap, error::Error as StdError}; - -/// Default syscalls information -pub struct DefaultSyscalls<'bs, BS, V> { - store: &'bs BS, - verifier: PhantomData, -} - -impl<'bs, BS, V> DefaultSyscalls<'bs, BS, V> { - pub fn new(store: &'bs BS) -> Self { - Self { - store, - verifier: Default::default(), - } - } -} - -impl<'bs, BS, V> Syscalls for DefaultSyscalls<'bs, BS, V> -where - BS: BlockStore, - V: ProofVerifier, -{ - fn verify_consensus_fault( - &self, - h1: &[u8], - h2: &[u8], - extra: &[u8], - ) -> Result, Box> { - // Note that block syntax is not validated. Any validly signed block will be accepted pursuant to the below conditions. - // Whether or not it could ever have been accepted in a chain is not checked/does not matter here. - // for that reason when checking block parent relationships, rather than instantiating a Tipset to do so - // (which runs a syntactic check), we do it directly on the CIDs. - - // (0) cheap preliminary checks - - if h1 == h2 { - return Err(format!( - "no consensus fault: submitted blocks are the same: {:?}, {:?}", - h1, h2 - ) - .into()); - }; - let bh_1: BlockHeader = from_slice(h1)?; - let bh_2: BlockHeader = from_slice(h2)?; - - // (1) check conditions necessary to any consensus fault - - if bh_1.miner_address() != bh_2.miner_address() { - return Err(format!( - "no consensus fault: blocks not mined by same miner: {:?}, {:?}", - bh_1.miner_address(), - bh_2.miner_address() - ) - .into()); - }; - // block a must be earlier or equal to block b, epoch wise (ie at least as early in the chain). - if bh_1.epoch() < bh_2.epoch() { - return Err(format!( - "first block must not be of higher height than second: {:?}, {:?}", - bh_1.epoch(), - bh_2.epoch() - ) - .into()); - }; - let mut cf: Option = None; - // (a) double-fork mining fault - if bh_1.epoch() == bh_2.epoch() { - cf = Some(ConsensusFault { - target: *bh_1.miner_address(), - epoch: bh_2.epoch(), - fault_type: ConsensusFaultType::DoubleForkMining, - }) - }; - // (b) time-offset mining fault - // strictly speaking no need to compare heights based on double fork mining check above, - // but at same height this would be a different fault. - if bh_1.parents() != bh_2.parents() && bh_1.epoch() != bh_2.epoch() { - cf = Some(ConsensusFault { - target: *bh_1.miner_address(), - epoch: bh_2.epoch(), - fault_type: ConsensusFaultType::TimeOffsetMining, - }) - }; - // (c) parent-grinding fault - // Here extra is the "witness", a third block that shows the connection between A and B as - // A's sibling and B's parent. - // Specifically, since A is of lower height, it must be that B was mined omitting A from its tipset - if !extra.is_empty() { - let bh_3: BlockHeader = from_slice(extra)?; - if bh_1.parents() != bh_3.parents() - && bh_1.epoch() != bh_3.epoch() - && bh_2.parents().cids().contains(bh_3.cid()) - && !bh_2.parents().cids().contains(bh_1.cid()) - { - cf = Some(ConsensusFault { - target: *bh_1.miner_address(), - epoch: bh_2.epoch(), - fault_type: ConsensusFaultType::ParentGrinding, - }) - } - }; - - // (3) return if no consensus fault by now - if cf.is_none() { - Ok(cf) - } else { - // (4) expensive final checks - - // check blocks are properly signed by their respective miner - // note we do not need to check extra's: it is a parent to block b - // which itself is signed, so it was willingly included by the miner - self.verify_block_signature(&bh_1)?; - self.verify_block_signature(&bh_2)?; - - Ok(cf) - } - } - - fn verify_seal(&self, vi: &SealVerifyInfo) -> Result<(), Box> { - V::verify_seal(vi) - } - - fn verify_post( - &self, - WindowPoStVerifyInfo { - randomness, - proofs, - challenged_sectors, - prover, - }: &WindowPoStVerifyInfo, - ) -> Result<(), Box> { - V::verify_window_post(*randomness, &proofs, challenged_sectors, *prover) - } - - fn batch_verify_seals( - &self, - vis: &[(Address, &Vec)], - ) -> Result>, Box> { - // TODO ideal to not use rayon https://github.com/ChainSafe/forest/issues/676 - let out = vis - .par_iter() - .map(|(addr, seals)| { - let results = seals - .par_iter() - .map(|s| { - if let Err(err) = V::verify_seal(s) { - warn!( - "seal verify in batch failed (miner: {}) (err: {})", - addr, err - ); - false - } else { - true - } - }) - .collect(); - (*addr, results) - }) - .collect(); - Ok(out) - } -} - -impl<'bs, BS, V> DefaultSyscalls<'bs, BS, V> -where - BS: BlockStore, -{ - fn verify_block_signature(&self, bh: &BlockHeader) -> Result<(), Box> { - // TODO look into attaching StateTree to DefaultSyscalls - let state = StateTree::new_from_root(self.store, bh.state_root())?; - - let actor = state - .get_actor(bh.miner_address())? - .ok_or_else(|| format!("actor not found {:?}", bh.miner_address()))?; - - let ms: miner::State = self - .store - .get(&actor.state)? - .ok_or_else(|| format!("actor state not found {:?}", actor.state.to_string()))?; - - let info = ms.get_info(self.store)?; - let work_address = resolve_to_key_addr(&state, self.store, &info.worker)?; - bh.check_block_signature(&work_address)?; - Ok(()) - } -} diff --git a/vm/interpreter/src/gas_syscalls.rs b/vm/interpreter/src/gas_syscalls.rs deleted file mode 100644 index cb4fbdd379a9..000000000000 --- a/vm/interpreter/src/gas_syscalls.rs +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2020 ChainSafe Systems -// SPDX-License-Identifier: Apache-2.0, MIT - -use super::gas_tracker::{GasTracker, PriceList}; -use address::Address; -use cid::Cid; -use crypto::Signature; -use fil_types::{PieceInfo, RegisteredSealProof, SealVerifyInfo, WindowPoStVerifyInfo}; -use runtime::{ConsensusFault, Syscalls}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::error::Error as StdError; -use std::rc::Rc; - -/// Syscall wrapper to charge gas on syscalls -pub(crate) struct GasSyscalls<'sys, S> { - pub price_list: PriceList, - pub gas: Rc>, - pub syscalls: &'sys S, -} - -impl<'sys, S> Syscalls for GasSyscalls<'sys, S> -where - S: Syscalls, -{ - fn verify_signature( - &self, - signature: &Signature, - signer: &Address, - plaintext: &[u8], - ) -> Result<(), Box> { - self.gas.borrow_mut().charge_gas( - self.price_list - .on_verify_signature(signature.signature_type()), - )?; - self.syscalls.verify_signature(signature, signer, plaintext) - } - fn hash_blake2b(&self, data: &[u8]) -> Result<[u8; 32], Box> { - self.gas - .borrow_mut() - .charge_gas(self.price_list.on_hashing(data.len()))?; - self.syscalls.hash_blake2b(data) - } - fn compute_unsealed_sector_cid( - &self, - reg: RegisteredSealProof, - pieces: &[PieceInfo], - ) -> Result> { - self.gas - .borrow_mut() - .charge_gas(self.price_list.on_compute_unsealed_sector_cid(reg, pieces))?; - self.syscalls.compute_unsealed_sector_cid(reg, pieces) - } - fn verify_seal(&self, vi: &SealVerifyInfo) -> Result<(), Box> { - self.gas - .borrow_mut() - .charge_gas(self.price_list.on_verify_seal(vi))?; - self.syscalls.verify_seal(vi) - } - fn verify_post(&self, vi: &WindowPoStVerifyInfo) -> Result<(), Box> { - self.gas - .borrow_mut() - .charge_gas(self.price_list.on_verify_post(vi))?; - self.syscalls.verify_post(vi) - } - fn verify_consensus_fault( - &self, - h1: &[u8], - h2: &[u8], - extra: &[u8], - ) -> Result, Box> { - self.gas - .borrow_mut() - .charge_gas(self.price_list.on_verify_consensus_fault())?; - self.syscalls.verify_consensus_fault(h1, h2, extra) - } - - fn batch_verify_seals( - &self, - vis: &[(Address, &Vec)], - ) -> Result>, Box> { - // Gas charged for batch verify in actor - self.syscalls.batch_verify_seals(vis) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime::{ConsensusFault, ConsensusFaultType, Syscalls}; - - #[derive(Copy, Debug, Clone)] - struct TestSyscalls; - impl Syscalls for TestSyscalls { - fn verify_signature( - &self, - _signature: &Signature, - _signer: &Address, - _plaintext: &[u8], - ) -> Result<(), Box> { - Ok(()) - } - fn hash_blake2b(&self, _data: &[u8]) -> Result<[u8; 32], Box> { - Ok([0u8; 32]) - } - fn compute_unsealed_sector_cid( - &self, - _reg: RegisteredSealProof, - _pieces: &[PieceInfo], - ) -> Result> { - Ok(Default::default()) - } - fn verify_seal(&self, _vi: &SealVerifyInfo) -> Result<(), Box> { - Ok(Default::default()) - } - fn verify_post(&self, _vi: &WindowPoStVerifyInfo) -> Result<(), Box> { - Ok(Default::default()) - } - fn verify_consensus_fault( - &self, - _h1: &[u8], - _h2: &[u8], - _extra: &[u8], - ) -> Result, Box> { - Ok(Some(ConsensusFault { - target: Address::new_id(0), - epoch: 0, - fault_type: ConsensusFaultType::DoubleForkMining, - })) - } - fn batch_verify_seals( - &self, - _vis: &[(Address, &Vec)], - ) -> Result>, Box> { - Ok(Default::default()) - } - } - - #[test] - fn gas_syscalls() { - let gsys = GasSyscalls { - price_list: PriceList { - on_chain_message_compute_base: 1, - on_chain_message_storage_per_byte: 1, - on_chain_return_value_per_byte: 1, - hashing_base: 2, - compute_unsealed_sector_cid_base: 1, - bls_sig_cost: 5, - verify_seal_base: 1, - verify_consensus_fault: 1, - ..Default::default() - }, - gas: Rc::new(RefCell::new(GasTracker::new(20, 0))), - syscalls: &TestSyscalls, - }; - - assert_eq!(gsys.gas.borrow().gas_used(), 0); - gsys.verify_signature(&Default::default(), &Address::new_id(0), &[0u8]) - .unwrap(); - assert_eq!(gsys.gas.borrow().gas_used(), 5); - - gsys.hash_blake2b(&[0u8]).unwrap(); - assert_eq!(gsys.gas.borrow().gas_used(), 7); - - gsys.compute_unsealed_sector_cid(RegisteredSealProof::from(0), &[]) - .unwrap(); - assert_eq!(gsys.gas.borrow().gas_used(), 8); - - gsys.verify_seal(&SealVerifyInfo { - registered_proof: RegisteredSealProof::from(1), - sector_id: Default::default(), - deal_ids: Vec::new(), - randomness: Default::default(), - interactive_randomness: Default::default(), - proof: Default::default(), - sealed_cid: Default::default(), - unsealed_cid: Default::default(), - }) - .unwrap(); - assert_eq!(gsys.gas.borrow().gas_used(), 9); - - gsys.verify_consensus_fault(&[], &[], &[]).unwrap(); - assert_eq!(gsys.gas.borrow().gas_used(), 10); - } -} diff --git a/vm/interpreter/src/lib.rs b/vm/interpreter/src/lib.rs index 8cf81536b987..57b86c0fc04a 100644 --- a/vm/interpreter/src/lib.rs +++ b/vm/interpreter/src/lib.rs @@ -5,14 +5,12 @@ extern crate lazy_static; mod default_runtime; -mod default_syscalls; mod gas_block_store; -mod gas_syscalls; mod gas_tracker; mod rand; mod vm; + pub use self::default_runtime::*; -pub use self::default_syscalls::DefaultSyscalls; pub use self::gas_tracker::*; pub use self::rand::*; pub use self::vm::*; diff --git a/vm/interpreter/src/vm.rs b/vm/interpreter/src/vm.rs index 8946d39beb1d..0db7b8d2ccae 100644 --- a/vm/interpreter/src/vm.rs +++ b/vm/interpreter/src/vm.rs @@ -12,14 +12,16 @@ use actor::{ use address::Address; use cid::Cid; use clock::ChainEpoch; -use fil_types::{DevnetParams, NetworkParams, NetworkVersion}; +use fil_types::{ + verifier::{FullVerifier, ProofVerifier}, + DevnetParams, NetworkParams, NetworkVersion, +}; use forest_encoding::Cbor; use ipld_blockstore::BlockStore; use log::warn; use message::{ChainMessage, Message, MessageReceipt, UnsignedMessage}; use num_bigint::{BigInt, Sign}; use num_traits::Zero; -use runtime::Syscalls; use state_tree::StateTree; use std::collections::HashSet; use std::convert::TryFrom; @@ -37,36 +39,42 @@ pub struct BlockMessages { pub win_count: i64, } +// TODO replace with some trait or some generic solution (needs to use context) +pub type CircSupplyCalc = + Box) -> Result>; + /// Interpreter which handles execution of state transitioning messages and returns receipts /// from the vm execution. -pub struct VM<'db, 'r, DB, SYS, R, N, P = DevnetParams> { +pub struct VM<'db, 'r, DB, R, N, V = FullVerifier, P = DevnetParams> { state: StateTree<'db, DB>, store: &'db DB, epoch: ChainEpoch, - syscalls: SYS, rand: &'r R, base_fee: BigInt, registered_actors: HashSet, network_version_getter: N, + circ_supply_calc: Option>, + verifier: PhantomData, params: PhantomData

, } -impl<'db, 'r, DB, SYS, R, N, P> VM<'db, 'r, DB, SYS, R, N, P> +impl<'db, 'r, DB, R, N, V, P> VM<'db, 'r, DB, R, N, V, P> where DB: BlockStore, - SYS: Syscalls, + V: ProofVerifier, P: NetworkParams, R: Rand, N: Fn(ChainEpoch) -> NetworkVersion, { + #[allow(clippy::too_many_arguments)] pub fn new( root: &Cid, store: &'db DB, epoch: ChainEpoch, - syscalls: SYS, rand: &'r R, base_fee: BigInt, network_version_getter: N, + circ_supply_calc: Option>, ) -> Result { let state = StateTree::new_from_root(store, root).map_err(|e| e.to_string())?; let registered_actors = HashSet::new(); @@ -75,10 +83,11 @@ where state, store, epoch, - syscalls, rand, base_fee, registered_actors, + circ_supply_calc, + verifier: PhantomData, params: PhantomData, }) } @@ -476,14 +485,13 @@ where gas_cost: Option, ) -> ( Serialized, - Option>, + Option>, Option, ) { let res = DefaultRuntime::new( (self.network_version_getter)(self.epoch), &mut self.state, self.store, - &self.syscalls, 0, &msg, self.epoch, @@ -492,6 +500,7 @@ where 0, self.rand, &self.registered_actors, + &self.circ_supply_calc, ); match res { diff --git a/vm/interpreter/tests/transfer_test.rs b/vm/interpreter/tests/transfer_test.rs index a941ee8dbff8..a40bf3530075 100644 --- a/vm/interpreter/tests/transfer_test.rs +++ b/vm/interpreter/tests/transfer_test.rs @@ -7,7 +7,7 @@ use blocks::TipsetKeys; use cid::multihash::{Blake2b256, Identity}; use db::MemoryDB; use fil_types::{verifier::MockVerifier, NetworkVersion}; -use interpreter::{vm_send, ChainRand, DefaultRuntime, DefaultSyscalls}; +use interpreter::{vm_send, ChainRand, DefaultRuntime}; use ipld_blockstore::BlockStore; use ipld_hamt::Hamt; use message::UnsignedMessage; @@ -92,15 +92,12 @@ fn transfer_test() { .build() .unwrap(); - let default_syscalls = DefaultSyscalls::<_, MockVerifier>::new(&store); - let dummy_rand = ChainRand::new(TipsetKeys::new(vec![])); let registered = HashSet::new(); - let mut runtime = DefaultRuntime::<_, _, _>::new( + let mut runtime = DefaultRuntime::<_, _, MockVerifier>::new( NetworkVersion::V0, &mut state, &store, - &default_syscalls, 0, &message, 0, @@ -109,6 +106,7 @@ fn transfer_test() { 0, &dummy_rand, ®istered, + &None, ) .unwrap(); let _serialized = vm_send(&mut runtime, &message, None).unwrap(); diff --git a/vm/runtime/src/lib.rs b/vm/runtime/src/lib.rs index a9d619763469..7f6f196221b2 100644 --- a/vm/runtime/src/lib.rs +++ b/vm/runtime/src/lib.rs @@ -167,9 +167,7 @@ pub trait Syscalls { signature: &Signature, signer: &Address, plaintext: &[u8], - ) -> Result<(), Box> { - Ok(signature.verify(plaintext, signer)?) - } + ) -> Result<(), Box>; /// Hashes input data using blake2b with 256 bit output. fn hash_blake2b(&self, data: &[u8]) -> Result<[u8; 32], Box> { Ok(blake2b_256(data)) @@ -180,34 +178,7 @@ pub trait Syscalls { proof_type: RegisteredSealProof, pieces: &[PieceInfo], ) -> Result> { - let sum: u64 = pieces.iter().map(|p| p.size.0).sum(); - - let ssize = proof_type.sector_size()? as u64; - - let mut fcp_pieces: Vec = pieces - .iter() - .map(proofs::PieceInfo::try_from) - .collect::>()?; - - // pad remaining space with 0 piece commitments - { - let mut to_fill = ssize - sum; - let n = to_fill.count_ones(); - for _ in 0..n { - let next = to_fill.trailing_zeros(); - let p_size = 1 << next; - to_fill ^= p_size; - let padded = PaddedPieceSize(p_size); - fcp_pieces.push(filecoin_proofs_api::PieceInfo { - commitment: zero_piece_commitment(padded), - size: padded.unpadded().into(), - }); - } - } - - let comm_d = compute_comm_d(proof_type.try_into()?, &fcp_pieces)?; - - Ok(data_commitment_v1_to_cid(&comm_d)?) + compute_unsealed_sector_cid(proof_type, pieces) } /// Verifies a sector seal proof. fn verify_seal(&self, vi: &SealVerifyInfo) -> Result<(), Box>; @@ -263,3 +234,37 @@ pub enum ConsensusFaultType { ParentGrinding = 2, TimeOffsetMining = 3, } + +pub fn compute_unsealed_sector_cid( + proof_type: RegisteredSealProof, + pieces: &[PieceInfo], +) -> Result> { + let sum: u64 = pieces.iter().map(|p| p.size.0).sum(); + + let ssize = proof_type.sector_size()? as u64; + + let mut fcp_pieces: Vec = pieces + .iter() + .map(proofs::PieceInfo::try_from) + .collect::>()?; + + // pad remaining space with 0 piece commitments + { + let mut to_fill = ssize - sum; + let n = to_fill.count_ones(); + for _ in 0..n { + let next = to_fill.trailing_zeros(); + let p_size = 1 << next; + to_fill ^= p_size; + let padded = PaddedPieceSize(p_size); + fcp_pieces.push(filecoin_proofs_api::PieceInfo { + commitment: zero_piece_commitment(padded), + size: padded.unpadded().into(), + }); + } + } + + let comm_d = compute_comm_d(proof_type.try_into()?, &fcp_pieces)?; + + Ok(data_commitment_v1_to_cid(&comm_d)?) +}