From 356757e2c776132e4c8db0a85e4b1733137330fd Mon Sep 17 00:00:00 2001 From: noot <36753753+noot@users.noreply.github.com> Date: Fri, 17 Dec 2021 20:03:29 -0500 Subject: [PATCH] drand v14 update: fix fetching around null tipsets (#1339) --- Cargo.lock | 2 + blockchain/blocks/src/header/mod.rs | 4 +- blockchain/chain/src/store/chain_store.rs | 125 +-------- blockchain/chain_sync/src/tipset_syncer.rs | 6 +- blockchain/state_manager/Cargo.toml | 2 + blockchain/state_manager/src/chain_rand.rs | 243 ++++++++++++++++-- blockchain/state_manager/src/lib.rs | 99 ++++++- forest/src/daemon.rs | 46 ++-- node/rpc/src/chain_api.rs | 3 - node/rpc/src/sync_api.rs | 9 +- tests/conformance_tests/src/rand_replay.rs | 36 ++- tests/conformance_tests/src/stubs.rs | 16 +- .../tests/conformance_runner.rs | 2 +- utils/genesis/src/lib.rs | 40 ++- vm/actor/src/builtin/miner/mod.rs | 1 - vm/interpreter/src/default_runtime.rs | 14 +- vm/interpreter/src/rand.rs | 15 +- vm/interpreter/tests/transfer_test.rs | 17 +- 18 files changed, 459 insertions(+), 221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43c90b82088a..296af9695d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6371,6 +6371,8 @@ dependencies = [ "async-log", "async-std", "beacon", + "blake2b_simd", + "byteorder 1.4.3", "chain", "fil_clock", "fil_types", diff --git a/blockchain/blocks/src/header/mod.rs b/blockchain/blocks/src/header/mod.rs index 9215190e265b..f7ca5eadf1bb 100644 --- a/blockchain/blocks/src/header/mod.rs +++ b/blockchain/blocks/src/header/mod.rs @@ -457,12 +457,10 @@ impl fmt::Display for BlockHeader { #[cfg(test)] mod tests { - use crate::{errors::Error, BlockHeader, Ticket, TipsetKeys}; + use crate::{errors::Error, BlockHeader}; use address::Address; use beacon::{BeaconEntry, BeaconPoint, BeaconSchedule, MockBeacon}; - use cid::Code::Identity; use encoding::Cbor; - use num_bigint::BigInt; use std::sync::Arc; use std::time::Duration; diff --git a/blockchain/chain/src/store/chain_store.rs b/blockchain/chain/src/store/chain_store.rs index 8db0fa5cd0b5..11b4307d6807 100644 --- a/blockchain/chain/src/store/chain_store.rs +++ b/blockchain/chain/src/store/chain_store.rs @@ -8,15 +8,12 @@ use async_std::channel::{self, bounded, Receiver}; use async_std::sync::RwLock; use async_std::task; use beacon::{BeaconEntry, IGNORE_DRAND_VAR}; -use blake2b_simd::Params; use blocks::{Block, BlockHeader, FullTipset, Tipset, TipsetKeys, TxMeta}; -use byteorder::{BigEndian, WriteBytesExt}; use cid::Cid; use cid::Code::Blake2b256; use clock::ChainEpoch; use crossbeam::atomic::AtomicCell; -use crypto::DomainSeparationTag; -use encoding::{blake2b_256, de::DeserializeOwned, from_slice, Cbor}; +use encoding::{de::DeserializeOwned, from_slice, Cbor}; use forest_car::CarHeader; use forest_ipld::recurse_links; use futures::AsyncWrite; @@ -32,7 +29,6 @@ use num_traits::Zero; use serde::Serialize; use state_tree::StateTree; use std::error::Error as StdError; -use std::io::Write; use std::sync::Arc; use std::{ collections::{HashMap, HashSet, VecDeque}, @@ -290,107 +286,6 @@ where } } - pub async fn get_chain_randomness_looking_forward( - &self, - blocks: &TipsetKeys, - pers: DomainSeparationTag, - round: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; 32], Box> { - self.get_chain_randomness(blocks, pers, round, entropy, false) - .await - } - - pub async fn get_chain_randomness_looking_backward( - &self, - blocks: &TipsetKeys, - pers: DomainSeparationTag, - round: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; 32], Box> { - self.get_chain_randomness(blocks, pers, round, entropy, true) - .await - } - - /// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, ChainEpoch, - /// Entropy from the ticket chain. - pub async fn get_chain_randomness( - &self, - blocks: &TipsetKeys, - pers: DomainSeparationTag, - round: ChainEpoch, - entropy: &[u8], - lookback: bool, - ) -> Result<[u8; 32], Box> { - let ts = self.tipset_from_keys(blocks).await?; - - if round > ts.epoch() { - return Err("cannot draw randomness from the future".into()); - } - - let search_height = if round < 0 { 0 } else { round }; - - let rand_ts = self.tipset_by_height(search_height, ts, lookback).await?; - - draw_randomness( - rand_ts - .min_ticket() - .ok_or("No ticket exists for block")? - .vrfproof - .as_bytes(), - pers, - round, - entropy, - ) - } - - pub async fn get_beacon_randomness_looking_forward( - &self, - blocks: &TipsetKeys, - pers: DomainSeparationTag, - round: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; 32], Box> { - self.get_beacon_randomness(blocks, pers, round, entropy, false) - .await - } - - pub async fn get_beacon_randomness_looking_backward( - &self, - blocks: &TipsetKeys, - pers: DomainSeparationTag, - round: ChainEpoch, - entropy: &[u8], - ) -> Result<[u8; 32], Box> { - self.get_beacon_randomness(blocks, pers, round, entropy, true) - .await - } - - /// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, ChainEpoch, - /// Entropy from the latest beacon entry. - pub async fn get_beacon_randomness( - &self, - blocks: &TipsetKeys, - pers: DomainSeparationTag, - round: ChainEpoch, - entropy: &[u8], - lookback: bool, - ) -> Result<[u8; 32], Box> { - let ts = self.tipset_from_keys(blocks).await?; - - if round > ts.epoch() { - return Err("cannot draw randomness from the future".into()); - } - - let search_height = if round < 0 { 0 } else { round }; - - let rand_ts = self.tipset_by_height(search_height, ts, lookback).await?; - - let be = self.latest_beacon_entry(&rand_ts).await?; - - draw_randomness(be.data(), pers, round, entropy) - } - /// Finds the latest beacon entry given a tipset up to 20 tipsets behind pub async fn latest_beacon_entry(&self, ts: &Tipset) -> Result { let check_for_beacon_entry = |ts: &Tipset| { @@ -843,24 +738,6 @@ where Ok(()) } -/// Computes a pseudorandom 32 byte Vec. -pub fn draw_randomness( - rbase: &[u8], - pers: DomainSeparationTag, - round: ChainEpoch, - entropy: &[u8], -) -> Result<[u8; 32], Box> { - let mut state = Params::new().hash_length(32).to_state(); - state.write_i64::(pers as i64)?; - let vrf_digest = blake2b_256(rbase); - state.write_all(&vrf_digest)?; - state.write_i64::(round as i64)?; - state.write_all(entropy)?; - let mut ret = [0u8; 32]; - ret.clone_from_slice(state.finalize().as_bytes()); - Ok(ret) -} - /// Returns a vector of cids from provided root cid fn read_amt_cids(db: &DB, root: &Cid) -> Result, Error> where diff --git a/blockchain/chain_sync/src/tipset_syncer.rs b/blockchain/chain_sync/src/tipset_syncer.rs index 4f991dc09c82..e89b529ef497 100644 --- a/blockchain/chain_sync/src/tipset_syncer.rs +++ b/blockchain/chain_sync/src/tipset_syncer.rs @@ -1376,7 +1376,7 @@ async fn validate_block< } let r_beacon = header.beacon_entries().last().unwrap_or(&v_prev_beacon); let miner_address_buf = header.miner_address().marshal_cbor()?; - let vrf_base = chain::draw_randomness( + let vrf_base = state_manager::chain_rand::draw_randomness( r_beacon.data(), DomainSeparationTag::ElectionProofProduction, header.epoch(), @@ -1447,7 +1447,7 @@ async fn validate_block< let beacon_base = header.beacon_entries().last().unwrap_or(&v_prev_beacon); - let vrf_base = chain::draw_randomness( + let vrf_base = state_manager::chain_rand::draw_randomness( beacon_base.data(), DomainSeparationTag::TicketProduction, header.epoch() - TICKET_RANDOMNESS_LOOKBACK, @@ -1559,7 +1559,7 @@ fn verify_winning_post_proof { blks: TipsetKeys, cs: Arc>, + beacon: Arc>, } -impl ChainRand { - pub fn new(blks: TipsetKeys, cs: Arc>) -> Self { - Self { blks, cs } +impl ChainRand +where + DB: BlockStore + Send + Sync + 'static, +{ + pub fn new( + blks: TipsetKeys, + cs: Arc>, + beacon: Arc>, + ) -> Self { + Self { blks, cs, beacon } + } + + /// Gets 32 bytes of randomness for ChainRand parameterized by the DomainSeparationTag, ChainEpoch, + /// Entropy from the ticket chain. + pub async fn get_chain_randomness( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + lookback: bool, + ) -> Result<[u8; 32], Box> { + let ts = self.cs.tipset_from_keys(blocks).await?; + + if round > ts.epoch() { + return Err("cannot draw randomness from the future".into()); + } + + let search_height = if round < 0 { 0 } else { round }; + + let rand_ts = self + .cs + .tipset_by_height(search_height, ts, lookback) + .await?; + + draw_randomness( + rand_ts + .min_ticket() + .ok_or("No ticket exists for block")? + .vrfproof + .as_bytes(), + pers, + round, + entropy, + ) + } + + /// network v0-12 + pub async fn get_chain_randomness_v1( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + self.get_chain_randomness(blocks, pers, round, entropy, true) + .await + } + + /// network v13 onwards + pub async fn get_chain_randomness_v2( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + self.get_chain_randomness(blocks, pers, round, entropy, false) + .await + } + + /// network v0-12; with lookback + pub async fn get_beacon_randomness_v1( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + self.get_beacon_randomness(blocks, pers, round, entropy, true) + .await + } + + /// network v13; without lookback + pub async fn get_beacon_randomness_v2( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + self.get_beacon_randomness(blocks, pers, round, entropy, false) + .await + } + + /// network v14 onwards + pub async fn get_beacon_randomness_v3( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + if round < 0 { + return self + .get_beacon_randomness_v2(blocks, pers, round, entropy) + .await; + } + + let beacon_entry = self.extract_beacon_entry_for_epoch(blocks, round).await?; + draw_randomness(beacon_entry.data(), pers, round, entropy) + } + + /// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, ChainEpoch, + /// Entropy from the latest beacon entry. + pub async fn get_beacon_randomness( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + lookback: bool, + ) -> Result<[u8; 32], Box> { + let rand_ts: Arc = self + .get_beacon_randomness_tipset(blocks, round, lookback) + .await?; + let be = self.cs.latest_beacon_entry(&rand_ts).await?; + draw_randomness(be.data(), pers, round, entropy) + } + + pub async fn extract_beacon_entry_for_epoch( + &self, + blocks: &TipsetKeys, + epoch: ChainEpoch, + ) -> Result> { + let mut rand_ts: Arc = self + .get_beacon_randomness_tipset(blocks, epoch, false) + .await?; + let (_, beacon) = self.beacon.beacon_for_epoch(epoch)?; + let round = beacon.max_beacon_round_for_epoch(epoch); + + for _ in 0..20 { + let cbe = rand_ts.blocks()[0].beacon_entries(); + for v in cbe { + if v.round() == round { + return Ok(v.clone()); + } + } + + rand_ts = self.cs.tipset_from_keys(rand_ts.parents()).await?; + } + + Err(format!( + "didn't find beacon for round {:?} (epoch {:?})", + round, epoch + ) + .into()) + } + + pub async fn get_beacon_randomness_tipset( + &self, + blocks: &TipsetKeys, + round: ChainEpoch, + lookback: bool, + ) -> Result, Box> { + let ts = self.cs.tipset_from_keys(blocks).await?; + + if round > ts.epoch() { + return Err("cannot draw randomness from the future".into()); + } + + let search_height = if round < 0 { 0 } else { round }; + + self.cs + .tipset_by_height(search_height, ts, lookback) + .await + .map_err(|e| e.into()) } } @@ -28,51 +208,66 @@ impl Rand for ChainRand where DB: BlockStore + Send + Sync + 'static, { - fn get_chain_randomness( + fn get_chain_randomness_v1( &self, pers: DomainSeparationTag, round: ChainEpoch, entropy: &[u8], ) -> Result<[u8; 32], Box> { - task::block_on( - self.cs - .get_chain_randomness_looking_backward(&self.blks, pers, round, entropy), - ) + task::block_on(self.get_chain_randomness_v1(&self.blks, pers, round, entropy)) } - fn get_beacon_randomness( + fn get_beacon_randomness_v1( &self, pers: DomainSeparationTag, round: ChainEpoch, entropy: &[u8], ) -> Result<[u8; 32], Box> { - task::block_on( - self.cs - .get_beacon_randomness_looking_backward(&self.blks, pers, round, entropy), - ) + task::block_on(self.get_beacon_randomness_v1(&self.blks, pers, round, entropy)) } - fn get_chain_randomness_looking_forward( + fn get_chain_randomness_v2( &self, pers: DomainSeparationTag, round: ChainEpoch, entropy: &[u8], ) -> Result<[u8; 32], Box> { - task::block_on( - self.cs - .get_chain_randomness_looking_forward(&self.blks, pers, round, entropy), - ) + task::block_on(self.get_chain_randomness_v2(&self.blks, pers, round, entropy)) } - fn get_beacon_randomness_looking_forward( + fn get_beacon_randomness_v2( &self, pers: DomainSeparationTag, round: ChainEpoch, entropy: &[u8], ) -> Result<[u8; 32], Box> { - task::block_on( - self.cs - .get_beacon_randomness_looking_forward(&self.blks, pers, round, entropy), - ) + task::block_on(self.get_beacon_randomness_v2(&self.blks, pers, round, entropy)) } + + fn get_beacon_randomness_v3( + &self, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + task::block_on(self.get_beacon_randomness_v3(&self.blks, pers, round, entropy)) + } +} + +/// Computes a pseudorandom 32 byte Vec. +pub fn draw_randomness( + rbase: &[u8], + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], +) -> Result<[u8; 32], Box> { + let mut state = Params::new().hash_length(32).to_state(); + state.write_i64::(pers as i64)?; + let vrf_digest = blake2b_256(rbase); + state.write_all(&vrf_digest)?; + state.write_i64::(round as i64)?; + state.write_all(entropy)?; + let mut ret = [0u8; 32]; + ret.clone_from_slice(state.finalize().as_bytes()); + Ok(ret) } diff --git a/blockchain/state_manager/src/lib.rs b/blockchain/state_manager/src/lib.rs index 83a08a24722f..63e5698895fa 100644 --- a/blockchain/state_manager/src/lib.rs +++ b/blockchain/state_manager/src/lib.rs @@ -4,7 +4,7 @@ #[macro_use] extern crate lazy_static; -mod chain_rand; +pub mod chain_rand; mod errors; mod utils; mod vm_circ_supply; @@ -14,9 +14,9 @@ use actor::*; use address::{Address, BLSPublicKey, Payload, Protocol, BLS_PUB_LEN}; use async_log::span; use async_std::{sync::RwLock, task}; -use beacon::{Beacon, BeaconEntry, BeaconSchedule, IGNORE_DRAND_VAR}; +use beacon::{Beacon, BeaconEntry, BeaconSchedule, DrandBeacon, IGNORE_DRAND_VAR}; use blockstore::{BlockStore, BufferedBlockStore}; -use chain::{draw_randomness, ChainStore, HeadChange}; +use chain::{ChainStore, HeadChange}; use chain_rand::ChainRand; use cid::Cid; use clock::ChainEpoch; @@ -87,29 +87,45 @@ pub struct StateManager { cache: RwLock>>>>, publisher: Option>, genesis_info: GenesisInfo, + beacon: Arc>, } impl StateManager where DB: BlockStore + Send + Sync + 'static, { - pub fn new(cs: Arc>) -> Self { - Self { + pub async fn new(cs: Arc>) -> Result> { + let genesis = cs.genesis()?.ok_or("genesis header was none")?; + let beacon = Arc::new(networks::beacon_schedule_default(genesis.timestamp()).await?); + + Ok(Self { cs, cache: RwLock::new(HashMap::new()), publisher: None, genesis_info: GenesisInfo::default(), - } + beacon, + }) } /// Creates a constructor that passes in a HeadChange publisher. - pub fn new_with_publisher(cs: Arc>, chain_subs: Publisher) -> Self { - Self { + pub async fn new_with_publisher( + cs: Arc>, + chain_subs: Publisher, + ) -> Result> { + let genesis = cs.genesis()?.ok_or("genesis header was none")?; + let beacon = Arc::new(networks::beacon_schedule_default(genesis.timestamp()).await?); + + Ok(Self { cs, cache: RwLock::new(HashMap::new()), publisher: Some(chain_subs), genesis_info: GenesisInfo::default(), - } + beacon, + }) + } + + pub fn beacon_schedule(&self) -> Arc> { + self.beacon.clone() } /// Returns network version for the given epoch. @@ -138,6 +154,63 @@ where &self.cs } + /// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, ChainEpoch, + /// Entropy from the latest beacon entry. + pub async fn get_beacon_randomness( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box> { + let chain_rand = ChainRand::new(blocks.to_owned(), self.cs.clone(), self.beacon.clone()); + match self.get_network_version(round) { + NetworkVersion::V14 => { + chain_rand + .get_beacon_randomness_v3(blocks, pers, round, entropy) + .await + } + NetworkVersion::V13 => { + chain_rand + .get_beacon_randomness_v2(blocks, pers, round, entropy) + .await + } + NetworkVersion::V0 + | NetworkVersion::V1 + | NetworkVersion::V2 + | NetworkVersion::V3 + | NetworkVersion::V4 + | NetworkVersion::V5 + | NetworkVersion::V6 + | NetworkVersion::V7 + | NetworkVersion::V8 + | NetworkVersion::V9 + | NetworkVersion::V10 + | NetworkVersion::V11 + | NetworkVersion::V12 => { + chain_rand + .get_beacon_randomness_v1(blocks, pers, round, entropy) + .await + } + } + } + + /// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, ChainEpoch, + /// Entropy from the ticket chain. + pub async fn get_chain_randomness( + &self, + blocks: &TipsetKeys, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + lookback: bool, + ) -> Result<[u8; 32], Box> { + let chain_rand = ChainRand::new(blocks.to_owned(), self.cs.clone(), self.beacon.clone()); + chain_rand + .get_chain_randomness(blocks, pers, round, entropy, lookback) + .await + } + /// Returns the network name from the init actor state. pub fn get_network_name(&self, st: &Cid) -> Result { let init_act = self @@ -409,7 +482,7 @@ where .await .ok_or_else(|| Error::Other("No heaviest tipset".to_string()))? }; - let chain_rand = ChainRand::new(ts.key().to_owned(), self.cs.clone()); + let chain_rand = ChainRand::new(ts.key().to_owned(), self.cs.clone(), self.beacon.clone()); self.call_raw::(message, &chain_rand, &ts) } @@ -436,7 +509,7 @@ where .tipset_state::(&ts) .await .map_err(|_| Error::Other("Could not load tipset state".to_string()))?; - let chain_rand = ChainRand::new(ts.key().to_owned(), self.cs.clone()); + let chain_rand = ChainRand::new(ts.key().to_owned(), self.cs.clone(), self.beacon.clone()); // TODO investigate: this doesn't use a buffered store in any way, and can lead to // state bloat potentially? @@ -663,7 +736,7 @@ where let miner_state = miner::State::load(self.blockstore(), &actor)?; let buf = address.marshal_cbor()?; - let prand = draw_randomness( + let prand = chain_rand::draw_randomness( rbase.data(), DomainSeparationTag::WinningPoStChallengeSeed, round, @@ -755,7 +828,7 @@ where let tipset_keys = TipsetKeys::new(block_headers.iter().map(|s| s.cid()).cloned().collect()); - let chain_rand = ChainRand::new(tipset_keys, self.cs.clone()); + let chain_rand = ChainRand::new(tipset_keys, self.cs.clone(), self.beacon.clone()); let base_fee = first_block.parent_base_fee().clone(); let blocks = self diff --git a/forest/src/daemon.rs b/forest/src/daemon.rs index 0a582891cebc..e74fd88e4de0 100644 --- a/forest/src/daemon.rs +++ b/forest/src/daemon.rs @@ -7,7 +7,7 @@ use chain::ChainStore; use chain_sync::ChainMuxer; use fil_types::verifier::FullVerifier; use forest_libp2p::{get_keypair, Libp2pService}; -use genesis::{import_chain, initialize_genesis}; +use genesis::{get_network_name_from_genesis, import_chain, read_genesis_header}; use message_pool::{MessagePool, MpoolConfig, MpoolRpcProvider}; use paramfetch::{get_params_default, SectorSizeOpt}; use rpc::start_rpc; @@ -124,15 +124,23 @@ pub(super) async fn start(config: Config) { let db = Arc::new(db); - // Initialize StateManager + // Initialize ChainStore let chain_store = Arc::new(ChainStore::new(Arc::clone(&db))); - let state_manager = Arc::new(StateManager::new(Arc::clone(&chain_store))); let publisher = chain_store.publisher(); // Read Genesis file // * When snapshot command implemented, this genesis does not need to be initialized - let (genesis, network_name) = initialize_genesis(config.genesis_file.as_ref(), &state_manager) + let genesis = read_genesis_header(config.genesis_file.as_ref(), &chain_store) + .await + .unwrap(); + chain_store.set_genesis(&genesis.blocks()[0]).unwrap(); + + // Initialize StateManager + let sm = StateManager::new(Arc::clone(&chain_store)).await.unwrap(); + let state_manager = Arc::new(sm); + + let network_name = get_network_name_from_genesis(&genesis, &state_manager) .await .unwrap(); @@ -172,18 +180,12 @@ pub(super) async fn start(config: Config) { .unwrap(), ); - let beacon = Arc::new( - networks::beacon_schedule_default(genesis.min_timestamp()) - .await - .unwrap(), - ); - // Initialize ChainMuxer let (tipset_sink, tipset_stream) = bounded(20); let chain_muxer_tipset_sink = tipset_sink.clone(); let chain_muxer = ChainMuxer::<_, _, FullVerifier, _>::new( Arc::clone(&state_manager), - beacon.clone(), + state_manager.beacon_schedule(), Arc::clone(&mpool), network_send.clone(), network_rx, @@ -208,14 +210,14 @@ pub(super) async fn start(config: Config) { info!("JSON RPC Endpoint at {}", &rpc_listen); start_rpc::<_, _, FullVerifier>( Arc::new(RPCState { - state_manager, + state_manager: Arc::clone(&state_manager), keystore: keystore_rpc, mpool, bad_blocks, sync_state, network_send, network_name, - beacon, + beacon: state_manager.beacon_schedule(), // TODO: the RPCState can fetch this itself from the StateManager chain_store, new_mined_block_tx: tipset_sink, }), @@ -259,13 +261,21 @@ pub(super) async fn start(config: Config) { #[cfg(not(any(feature = "interopnet", feature = "devnet")))] mod test { use super::*; + use address::Address; + use blocks::BlockHeader; use db::MemoryDB; #[async_std::test] async fn import_snapshot_from_file() { let db = Arc::new(MemoryDB::default()); let cs = Arc::new(ChainStore::new(db)); - let sm = Arc::new(StateManager::new(cs)); + let genesis_header = BlockHeader::builder() + .miner_address(Address::new_id(0)) + .timestamp(7777) + .build() + .unwrap(); + cs.set_genesis(&genesis_header).unwrap(); + let sm = Arc::new(StateManager::new(cs).await.unwrap()); import_chain::(&sm, "test_files/chain4.car", None, false) .await .expect("Failed to import chain"); @@ -274,7 +284,13 @@ mod test { async fn import_chain_from_file() { let db = Arc::new(MemoryDB::default()); let cs = Arc::new(ChainStore::new(db)); - let sm = Arc::new(StateManager::new(cs)); + let genesis_header = BlockHeader::builder() + .miner_address(Address::new_id(0)) + .timestamp(7777) + .build() + .unwrap(); + cs.set_genesis(&genesis_header).unwrap(); + let sm = Arc::new(StateManager::new(cs).await.unwrap()); import_chain::(&sm, "test_files/chain4.car", Some(0), false) .await .expect("Failed to import chain"); diff --git a/node/rpc/src/chain_api.rs b/node/rpc/src/chain_api.rs index 04b5f53bb461..939366a1db62 100644 --- a/node/rpc/src/chain_api.rs +++ b/node/rpc/src/chain_api.rs @@ -271,7 +271,6 @@ where let entropy = entropy.unwrap_or_default(); Ok(data .state_manager - .chain_store() .get_chain_randomness( &tsk, DomainSeparationTag::from_i64(pers).ok_or("invalid DomainSeparationTag")?, @@ -295,13 +294,11 @@ where Ok(data .state_manager - .chain_store() .get_beacon_randomness( &tsk, DomainSeparationTag::from_i64(pers).ok_or("invalid DomainSeparationTag")?, epoch, &base64::decode(entropy)?, - epoch <= networks::UPGRADE_HYPERDRIVE_HEIGHT, ) .await?) } diff --git a/node/rpc/src/sync_api.rs b/node/rpc/src/sync_api.rs index ddcb2f149f29..717c570a3a63 100644 --- a/node/rpc/src/sync_api.rs +++ b/node/rpc/src/sync_api.rs @@ -109,6 +109,7 @@ where #[cfg(test)] mod tests { use super::*; + use address::Address; use async_std::channel::{bounded, Receiver}; use async_std::sync::RwLock; use async_std::task; @@ -138,7 +139,13 @@ mod tests { let (network_send, network_rx) = bounded(5); let db = Arc::new(MemoryDB::default()); let cs_arc = Arc::new(ChainStore::new(db.clone())); - let state_manager = Arc::new(StateManager::new(cs_arc.clone())); + let genesis_header = BlockHeader::builder() + .miner_address(Address::new_id(0)) + .timestamp(7777) + .build() + .unwrap(); + cs_arc.set_genesis(&genesis_header).unwrap(); + let state_manager = Arc::new(StateManager::new(cs_arc.clone()).await.unwrap()); let state_manager_for_thread = state_manager.clone(); let cs_for_test = cs_arc.clone(); let cs_for_chain = cs_arc.clone(); diff --git a/tests/conformance_tests/src/rand_replay.rs b/tests/conformance_tests/src/rand_replay.rs index a0970328bd17..32b324b6c492 100644 --- a/tests/conformance_tests/src/rand_replay.rs +++ b/tests/conformance_tests/src/rand_replay.rs @@ -29,7 +29,7 @@ impl<'a> ReplayingRand<'a> { } impl Rand for ReplayingRand<'_> { - fn get_chain_randomness( + fn get_chain_randomness_v1( &self, dst: DomainSeparationTag, epoch: ChainEpoch, @@ -44,10 +44,10 @@ impl Rand for ReplayingRand<'_> { if let Some(bz) = self.matches(rule) { Ok(bz) } else { - self.fallback.get_chain_randomness(dst, epoch, entropy) + self.fallback.get_chain_randomness_v1(dst, epoch, entropy) } } - fn get_beacon_randomness( + fn get_beacon_randomness_v1( &self, dst: DomainSeparationTag, epoch: ChainEpoch, @@ -62,11 +62,11 @@ impl Rand for ReplayingRand<'_> { if let Some(bz) = self.matches(rule) { Ok(bz) } else { - self.fallback.get_beacon_randomness(dst, epoch, entropy) + self.fallback.get_beacon_randomness_v1(dst, epoch, entropy) } } // TODO: Check if this is going to be correct for when we integrate v5 Actors test vectors - fn get_beacon_randomness_looking_forward( + fn get_beacon_randomness_v2( &self, dst: DomainSeparationTag, epoch: ChainEpoch, @@ -81,11 +81,11 @@ impl Rand for ReplayingRand<'_> { if let Some(bz) = self.matches(rule) { Ok(bz) } else { - self.fallback.get_beacon_randomness(dst, epoch, entropy) + self.fallback.get_beacon_randomness_v2(dst, epoch, entropy) } } // TODO: Check if this is going to be correct for when we integrate v5 Actors test vectors - fn get_chain_randomness_looking_forward( + fn get_chain_randomness_v2( &self, dst: DomainSeparationTag, epoch: ChainEpoch, @@ -100,7 +100,27 @@ impl Rand for ReplayingRand<'_> { if let Some(bz) = self.matches(rule) { Ok(bz) } else { - self.fallback.get_chain_randomness(dst, epoch, entropy) + self.fallback.get_chain_randomness_v2(dst, epoch, entropy) + } + } + + fn get_beacon_randomness_v3( + &self, + 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_beacon_randomness_v3(dst, epoch, entropy) } } } diff --git a/tests/conformance_tests/src/stubs.rs b/tests/conformance_tests/src/stubs.rs index 54105577b087..dc7acce0e6ac 100644 --- a/tests/conformance_tests/src/stubs.rs +++ b/tests/conformance_tests/src/stubs.rs @@ -5,7 +5,7 @@ use super::*; pub struct TestRand; impl Rand for TestRand { - fn get_chain_randomness( + fn get_chain_randomness_v1( &self, _: DomainSeparationTag, _: ChainEpoch, @@ -13,7 +13,7 @@ impl Rand for TestRand { ) -> Result<[u8; 32], Box> { Ok(*b"i_am_random_____i_am_random_____") } - fn get_beacon_randomness( + fn get_beacon_randomness_v1( &self, _: DomainSeparationTag, _: ChainEpoch, @@ -21,7 +21,7 @@ impl Rand for TestRand { ) -> Result<[u8; 32], Box> { Ok(*b"i_am_random_____i_am_random_____") } - fn get_beacon_randomness_looking_forward( + fn get_beacon_randomness_v2( &self, _: DomainSeparationTag, _: ChainEpoch, @@ -29,7 +29,15 @@ impl Rand for TestRand { ) -> Result<[u8; 32], Box> { Ok(*b"i_am_random_____i_am_random_____") } - fn get_chain_randomness_looking_forward( + fn get_chain_randomness_v2( + &self, + _: DomainSeparationTag, + _: ChainEpoch, + _: &[u8], + ) -> Result<[u8; 32], Box> { + Ok(*b"i_am_random_____i_am_random_____") + } + fn get_beacon_randomness_v3( &self, _: DomainSeparationTag, _: ChainEpoch, diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 64a918832779..7cdd412a100d 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -213,7 +213,7 @@ async fn execute_tipset_vector( ) -> Result<(), Box> { let bs = load_car(car).await?; let bs = Arc::new(bs); - let sm = Arc::new(StateManager::new(Arc::new(ChainStore::new(bs)))); + let sm = Arc::new(StateManager::new(Arc::new(ChainStore::new(bs))).await?); genesis::initialize_genesis(None, &sm).await.unwrap(); let base_epoch = variant.epoch; diff --git a/utils/genesis/src/lib.rs b/utils/genesis/src/lib.rs index 1f4275484cc1..12d8366f9bee 100644 --- a/utils/genesis/src/lib.rs +++ b/utils/genesis/src/lib.rs @@ -24,33 +24,57 @@ pub const EXPORT_SR_40: &[u8] = std::include_bytes!("export40.car"); /// Uses an optional file path or the default genesis to parse the genesis and determine if /// chain store has existing data for the given genesis. -pub async fn initialize_genesis( +pub async fn read_genesis_header( genesis_fp: Option<&String>, - state_manager: &StateManager, -) -> Result<(Tipset, String), Box> + cs: &ChainStore, +) -> Result> where - BS: BlockStore + Send + Sync + 'static, + DB: BlockStore + Send + Sync + 'static, { let genesis = match genesis_fp { Some(path) => { let file = File::open(path).await?; let reader = BufReader::new(file); - process_car(reader, state_manager.chain_store()).await? + process_car(reader, cs).await? } None => { debug!("No specified genesis in config. Using default genesis."); let reader = BufReader::<&[u8]>::new(DEFAULT_GENESIS); - process_car(reader, state_manager.chain_store()).await? + process_car(reader, cs).await? } }; info!("Initialized genesis: {}", genesis); + Ok(Tipset::new(vec![genesis])?) +} + +pub async fn get_network_name_from_genesis( + genesis_ts: &Tipset, + state_manager: &StateManager, +) -> Result> +where + BS: BlockStore + Send + Sync + 'static, +{ + // the genesis tipset has just one block, so fetch it + let genesis_header = genesis_ts.min_ticket_block(); // Get network name from genesis state. let network_name = state_manager - .get_network_name(genesis.state_root()) + .get_network_name(genesis_header.state_root()) .map_err(|e| format!("Failed to retrieve network name from genesis: {}", e))?; - Ok((Tipset::new(vec![genesis])?, network_name)) + Ok(network_name) +} + +pub async fn initialize_genesis( + genesis_fp: Option<&String>, + state_manager: &StateManager, +) -> Result<(Tipset, String), Box> +where + BS: BlockStore + Send + Sync + 'static, +{ + let ts = read_genesis_header(genesis_fp, state_manager.chain_store()).await?; + let network_name = get_network_name_from_genesis(&ts, state_manager).await?; + Ok((ts, network_name)) } async fn process_car( diff --git a/vm/actor/src/builtin/miner/mod.rs b/vm/actor/src/builtin/miner/mod.rs index e11fc5bff3a7..6a97cb04a822 100644 --- a/vm/actor/src/builtin/miner/mod.rs +++ b/vm/actor/src/builtin/miner/mod.rs @@ -4009,7 +4009,6 @@ where "unlocked balance can not repay fee debt", ) })?; - info!("RepayDebtsOrAbort was called and succeeded"); Ok(res) } diff --git a/vm/interpreter/src/default_runtime.rs b/vm/interpreter/src/default_runtime.rs index 952b70d4a35c..34076e8e94c7 100644 --- a/vm/interpreter/src/default_runtime.rs +++ b/vm/interpreter/src/default_runtime.rs @@ -666,11 +666,11 @@ where ) -> Result { let r = if rand_epoch > networks::UPGRADE_HYPERDRIVE_HEIGHT { self.rand - .get_chain_randomness_looking_forward(personalization, rand_epoch, entropy) + .get_chain_randomness_v2(personalization, rand_epoch, entropy) .map_err(|e| e.downcast_fatal("could not get randomness"))? } else { self.rand - .get_chain_randomness(personalization, rand_epoch, entropy) + .get_chain_randomness_v1(personalization, rand_epoch, entropy) .map_err(|e| e.downcast_fatal("could not get randomness"))? }; @@ -683,13 +683,17 @@ where rand_epoch: ChainEpoch, entropy: &[u8], ) -> Result { - let r = if rand_epoch > networks::UPGRADE_HYPERDRIVE_HEIGHT { + let r = if rand_epoch >= networks::UPGRADE_ACTORS_V6_HEIGHT { + self.rand + .get_beacon_randomness_v3(personalization, rand_epoch, entropy) + .map_err(|e| e.downcast_fatal("could not get randomness"))? + } else if rand_epoch > networks::UPGRADE_HYPERDRIVE_HEIGHT { self.rand - .get_beacon_randomness_looking_forward(personalization, rand_epoch, entropy) + .get_beacon_randomness_v2(personalization, rand_epoch, entropy) .map_err(|e| e.downcast_fatal("could not get randomness"))? } else { self.rand - .get_beacon_randomness(personalization, rand_epoch, entropy) + .get_beacon_randomness_v1(personalization, rand_epoch, entropy) .map_err(|e| e.downcast_fatal("could not get randomness"))? }; Ok(Randomness(r.to_vec())) diff --git a/vm/interpreter/src/rand.rs b/vm/interpreter/src/rand.rs index 69ccac9ccf54..fd08a2f4378a 100644 --- a/vm/interpreter/src/rand.rs +++ b/vm/interpreter/src/rand.rs @@ -9,14 +9,14 @@ use std::error::Error; pub trait Rand { /// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, /// ChainEpoch, Entropy from the ticket chain. - fn get_chain_randomness( + fn get_chain_randomness_v1( &self, pers: DomainSeparationTag, round: ChainEpoch, entropy: &[u8], ) -> Result<[u8; 32], Box>; - fn get_chain_randomness_looking_forward( + fn get_chain_randomness_v2( &self, pers: DomainSeparationTag, round: ChainEpoch, @@ -25,14 +25,21 @@ pub trait Rand { /// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, /// ChainEpoch, Entropy from the latest beacon entry. - fn get_beacon_randomness( + fn get_beacon_randomness_v1( &self, pers: DomainSeparationTag, round: ChainEpoch, entropy: &[u8], ) -> Result<[u8; 32], Box>; - fn get_beacon_randomness_looking_forward( + fn get_beacon_randomness_v2( + &self, + pers: DomainSeparationTag, + round: ChainEpoch, + entropy: &[u8], + ) -> Result<[u8; 32], Box>; + + fn get_beacon_randomness_v3( &self, pers: DomainSeparationTag, round: ChainEpoch, diff --git a/vm/interpreter/tests/transfer_test.rs b/vm/interpreter/tests/transfer_test.rs index 98b4445829a8..31f81cf0d45f 100644 --- a/vm/interpreter/tests/transfer_test.rs +++ b/vm/interpreter/tests/transfer_test.rs @@ -37,7 +37,7 @@ impl<'db> LookbackStateGetter<'db, MemoryDB> for MockStateLB<'db, MemoryDB> { struct MockRand; impl Rand for MockRand { - fn get_chain_randomness( + fn get_chain_randomness_v1( &self, _: DomainSeparationTag, _: ChainEpoch, @@ -45,7 +45,7 @@ impl Rand for MockRand { ) -> Result<[u8; 32], Box> { Ok(*b"i_am_random_____i_am_random_____") } - fn get_beacon_randomness( + fn get_beacon_randomness_v1( &self, _: DomainSeparationTag, _: ChainEpoch, @@ -54,7 +54,7 @@ impl Rand for MockRand { Ok(*b"i_am_random_____i_am_random_____") } - fn get_chain_randomness_looking_forward( + fn get_chain_randomness_v2( &self, _: DomainSeparationTag, _: ChainEpoch, @@ -63,7 +63,16 @@ impl Rand for MockRand { Ok(*b"i_am_random_____i_am_random_____") } - fn get_beacon_randomness_looking_forward( + fn get_beacon_randomness_v2( + &self, + _: DomainSeparationTag, + _: ChainEpoch, + _: &[u8], + ) -> Result<[u8; 32], Box> { + Ok(*b"i_am_random_____i_am_random_____") + } + + fn get_beacon_randomness_v3( &self, _: DomainSeparationTag, _: ChainEpoch,