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

Runtime randomness and ChainStore randomness #415

Merged
merged 14 commits into from
May 27, 2020
1 change: 1 addition & 0 deletions blockchain/blocks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2018"
[dependencies]
address = { package = "forest_address", path = "../../vm/address" }
beacon = { path = "../beacon" }
byteorder = "1.3.4"
crypto = { package = "forest_crypto", path = "../../crypto" }
message = { package = "forest_message", path = "../../vm/message" }
clock = { path = "../../node/clock" }
Expand Down
4 changes: 4 additions & 0 deletions blockchain/blocks/src/tipset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ impl Tipset {
pub fn min_ticket(&self) -> Ticket {
self.first_block().ticket().clone()
}
/// Returns the block with the smallest ticket of all blocks in the tipset
pub fn min_ticket_block(&self) -> &BlockHeader {
&self.first_block()
}
ec2 marked this conversation as resolved.
Show resolved Hide resolved
/// Returns the smallest timestamp of all blocks in the tipset
pub fn min_timestamp(&self) -> u64 {
self.blocks
Expand Down
4 changes: 3 additions & 1 deletion blockchain/chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ blocks = { package = "forest_blocks", path = "../blocks" }
db = { path = "../../node/db" }
cid = { package = "forest_cid", path = "../../ipld/cid" }
clock = { path = "../../node/clock" }
crypto = { package = "forest_crypto", path = "../../crypto" }
encoding = { package = "forest_encoding", path = "../../encoding" }
serde = { version = "1.0", features = ["derive"] }
num-bigint = { path = "../../utils/bigint", package = "forest_bigint" }
Expand All @@ -20,11 +21,12 @@ thiserror = "1.0"
log = "0.4.8"
state_tree = { path = "../../vm/state_tree/" }
actor = { path = "../../vm/actor/" }
blake2b_simd = "0.5.9"
byteorder = "1.3.4"
beacon = { path = "../beacon" }

[dev-dependencies]
address = { package = "forest_address", path = "../../vm/address" }
crypto = { package = "forest_crypto", path = "../../crypto" }
multihash = "0.10.0"
test_utils = { version = "0.1.0", path = "../../utils/test_utils/", features = [
"test_constructors"
Expand Down
44 changes: 43 additions & 1 deletion blockchain/chain/src/store/chain_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@
use super::{Error, TipIndex, TipsetMetadata};
use actor::{power::State as PowerState, STORAGE_POWER_ACTOR_ADDR};
use beacon::BeaconEntry;
use blake2b_simd::Params;
use blocks::{Block, BlockHeader, FullTipset, Tipset, TipsetKeys, TxMeta};
use byteorder::{BigEndian, WriteBytesExt};
use cid::multihash::Blake2b256;
use cid::Cid;
use encoding::{de::DeserializeOwned, from_slice, Cbor};
use clock::ChainEpoch;
use crypto::DomainSeparationTag;
use encoding::{blake2b_256, de::DeserializeOwned, from_slice, Cbor};
use ipld_amt::Amt;
use ipld_blockstore::BlockStore;
use log::{info, warn};
use message::{SignedMessage, UnsignedMessage};
use num_bigint::BigUint;
use num_traits::Zero;
use state_tree::StateTree;
use std::io::Write;
use std::sync::Arc;

const GENESIS_KEY: &str = "gen_block";
Expand Down Expand Up @@ -248,6 +253,43 @@ where
Ok(())
}

/// Gets 32 bytes of randomness for ChainRand paramaterized by the DomainSeparationTag, ChainEpoch, Entropy
pub fn get_randomness<DB: BlockStore>(
db: &DB,
blocks: &TipsetKeys,
pers: DomainSeparationTag,
round: ChainEpoch,
entropy: &[u8],
) -> Result<[u8; 32], Box<dyn std::error::Error>> {
let mut blks = blocks.clone();
loop {
let nts = tipset_from_keys(db, &blks)?;
let mtb = nts.min_ticket_block();
if nts.epoch() <= round || mtb.epoch() == 0 {
return draw_randomness(mtb.ticket().vrfproof.bytes(), pers, round, entropy);
ec2 marked this conversation as resolved.
Show resolved Hide resolved
}
blks = mtb.parents().clone();
}
}

/// Computes a pseudorandom 32 byte Vec
fn draw_randomness(
rbase: &[u8],
pers: DomainSeparationTag,
round: ChainEpoch,
entropy: &[u8],
) -> Result<[u8; 32], Box<dyn std::error::Error>> {
let mut state = Params::new().hash_length(32).to_state();
state.write_i64::<BigEndian>(pers as i64)?;
austinabell marked this conversation as resolved.
Show resolved Hide resolved
let vrf_digest = blake2b_256(rbase);
state.write_all(&vrf_digest)?;
state.write_i64::<BigEndian>(round as i64)?;
state.write_all(entropy)?;
let mut ret = [0u8; 32];
ret.clone_from_slice(state.finalize().as_bytes());
Ok(ret)
}

fn get_heaviest_tipset<DB>(db: &DB) -> Result<Option<Tipset>, Error>
where
DB: BlockStore,
Expand Down
9 changes: 7 additions & 2 deletions blockchain/state_manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use blockstore::BufferedBlockStore;
use cid::Cid;
use encoding::de::DeserializeOwned;
use forest_blocks::FullTipset;
use interpreter::{resolve_to_key_addr, DefaultSyscalls, VM};
use interpreter::{resolve_to_key_addr, ChainRand, DefaultSyscalls, VM};
use ipld_amt::Amt;
use num_bigint::BigUint;
use state_tree::StateTree;
Expand Down Expand Up @@ -93,14 +93,19 @@ where

/// Performs the state transition for the tipset and applies all unique messages in all blocks.
/// This function returns the state root and receipt root of the transition.
pub fn apply_blocks(&self, ts: &FullTipset) -> Result<(Cid, Cid), Box<dyn StdError>> {
pub fn apply_blocks(
&self,
ts: &FullTipset,
rand: &ChainRand,
) -> Result<(Cid, Cid), Box<dyn StdError>> {
let mut buf_store = BufferedBlockStore::new(self.bs.as_ref());
// TODO possibly switch out syscalls to be saved at state manager level
let mut vm = VM::new(
ts.parent_state(),
&buf_store,
ts.epoch(),
DefaultSyscalls::new(&buf_store),
rand,
)?;

// Apply tipset messages
Expand Down
1 change: 1 addition & 0 deletions crypto/src/randomness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use num_traits::FromPrimitive;

/// Specifies a domain for randomness generation.
#[derive(PartialEq, Eq, Copy, Clone, FromPrimitive, Debug, Hash)]
#[repr(i64)]
pub enum DomainSeparationTag {
TicketProduction = 1,
ElectionPoStChallengeSeed = 2,
Expand Down
3 changes: 2 additions & 1 deletion vm/actor/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,11 @@ impl<BS: BlockStore> Runtime<BS> for MockRuntime<'_, BS> {
}

fn get_randomness(
&self,
_personalization: DomainSeparationTag,
_rand_epoch: ChainEpoch,
_entropy: &[u8],
) -> Randomness {
) -> Result<Randomness, ActorError> {
unimplemented!()
}

Expand Down
2 changes: 1 addition & 1 deletion vm/interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ byteorder = "1.3.4"
state_tree = { path = "../state_tree" }
log = "0.4.8"
db = { path = "../../node/db" }
chain = { path = "../../blockchain/chain" }
fil_types = { path = "../../types" }

[dev-dependencies]
ipld_hamt = { path = "../../ipld/hamt" }
31 changes: 22 additions & 9 deletions vm/interpreter/src/default_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use super::gas_block_store::GasBlockStore;
use super::gas_syscalls::GasSyscalls;
use super::gas_tracker::{price_list_by_epoch, GasTracker, PriceList};
use super::ChainRand;
use actor::{
self, account, ACCOUNT_ACTOR_CODE_ID, CRON_ACTOR_CODE_ID, INIT_ACTOR_CODE_ID,
MARKET_ACTOR_CODE_ID, MINER_ACTOR_CODE_ID, MULTISIG_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID,
Expand All @@ -28,7 +29,7 @@ use vm::{
};

/// Implementation of the Runtime trait.
pub struct DefaultRuntime<'db, 'msg, 'st, 'sys, BS, SYS> {
pub struct DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS> {
state: &'st mut StateTree<'db, BS>,
store: GasBlockStore<'db, BS>,
syscalls: GasSyscalls<'sys, SYS>,
Expand All @@ -39,9 +40,10 @@ pub struct DefaultRuntime<'db, 'msg, 'st, 'sys, BS, SYS> {
origin_nonce: u64,
num_actors_created: u64,
price_list: PriceList,
rand: &'r ChainRand,
}

impl<'db, 'msg, 'st, 'sys, BS, SYS> DefaultRuntime<'db, 'msg, 'st, 'sys, BS, SYS>
impl<'db, 'msg, 'st, 'sys, 'r, BS, SYS> DefaultRuntime<'db, 'msg, 'st, 'sys, 'r, BS, SYS>
where
BS: BlockStore,
SYS: Syscalls,
Expand All @@ -58,6 +60,7 @@ where
origin: Address,
origin_nonce: u64,
num_actors_created: u64,
rand: &'r ChainRand,
) -> Self {
let price_list = price_list_by_epoch(epoch);
let gas_tracker = Rc::new(RefCell::new(GasTracker::new(
Expand Down Expand Up @@ -85,6 +88,7 @@ where
origin_nonce,
num_actors_created,
price_list,
rand,
}
}

Expand Down Expand Up @@ -152,7 +156,7 @@ where
}
}

impl<BS, SYS> Runtime<BS> for DefaultRuntime<'_, '_, '_, '_, BS, SYS>
impl<BS, SYS> Runtime<BS> for DefaultRuntime<'_, '_, '_, '_, '_, BS, SYS>
where
BS: BlockStore,
SYS: Syscalls,
Expand Down Expand Up @@ -209,11 +213,19 @@ where
self.get_actor(&addr).map(|act| act.code)
}
fn get_randomness(
_personalization: DomainSeparationTag,
_rand_epoch: ChainEpoch,
_entropy: &[u8],
) -> Randomness {
todo!()
&self,
personalization: DomainSeparationTag,
rand_epoch: ChainEpoch,
entropy: &[u8],
) -> Result<Randomness, ActorError> {
let r = self
.rand
.get_randomness(&self.store, personalization, rand_epoch, entropy)
.map_err(|e| {
ActorError::new_fatal(format!("could not get randomness: {}", e.to_string()))
})?;

Ok(Randomness(r))
}

fn create<C: Cbor>(&mut self, obj: &C) -> Result<(), ActorError> {
Expand Down Expand Up @@ -323,6 +335,7 @@ where
self.origin,
self.origin_nonce,
self.num_actors_created,
self.rand,
);
internal_send::<BS, SYS>(&mut parent, &msg, 0)
};
Expand Down Expand Up @@ -402,7 +415,7 @@ where
/// Shared logic between the DefaultRuntime and the Interpreter.
/// It invokes methods on different Actors based on the Message.
pub fn internal_send<BS, SYS>(
runtime: &mut DefaultRuntime<'_, '_, '_, '_, BS, SYS>,
runtime: &mut DefaultRuntime<'_, '_, '_, '_, '_, BS, SYS>,
msg: &UnsignedMessage,
_gas_cost: i64,
) -> Result<Serialized, ActorError>
Expand Down
3 changes: 2 additions & 1 deletion vm/interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ 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::rand::*;
pub use self::vm::*;
32 changes: 32 additions & 0 deletions vm/interpreter/src/rand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use blocks::TipsetKeys;
use clock::ChainEpoch;
use crypto::DomainSeparationTag;
use ipld_blockstore::BlockStore;
use std::error::Error;

/// Allows for deriving the randomness from a particular tipset
#[derive(Debug, Clone)]
pub struct ChainRand {
pub blks: TipsetKeys,
}

impl ChainRand {
/// Construct a new ChainRand
pub fn new(blks: TipsetKeys) -> Self {
Self { blks }
}

/// Gets 32 bytes of randomness paramaterized by the DomainSeparationTag, ChainEpoch, Entropy, and Tipset
pub fn get_randomness<DB: BlockStore>(
&self,
db: &DB,
pers: DomainSeparationTag,
round: ChainEpoch,
entropy: &[u8],
) -> Result<[u8; 32], Box<dyn Error>> {
chain::get_randomness(db, &self.blks, pers, round, entropy)
}
}
13 changes: 8 additions & 5 deletions vm/interpreter/src/vm.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::gas_tracker::price_list_by_epoch;
use super::{internal_send, DefaultRuntime};
use super::{gas_tracker::price_list_by_epoch, internal_send, ChainRand, DefaultRuntime};
use actor::{
cron, reward, ACCOUNT_ACTOR_CODE_ID, CRON_ACTOR_ADDR, REWARD_ACTOR_ADDR, SYSTEM_ACTOR_ADDR,
};
Expand All @@ -23,16 +22,17 @@ use vm::{ActorError, ExitCode, Serialized};

/// Interpreter which handles execution of state transitioning messages and returns receipts
/// from the vm execution.
pub struct VM<'db, DB, SYS> {
pub struct VM<'db, 'r, DB, SYS> {
state: StateTree<'db, DB>,
// TODO revisit handling buffered store specifically in VM
store: &'db DB,
epoch: ChainEpoch,
syscalls: SYS,
rand: &'r ChainRand,
// TODO: missing fields
}

impl<'db, DB, SYS> VM<'db, DB, SYS>
impl<'db, 'r, DB, SYS> VM<'db, 'r, DB, SYS>
where
DB: BlockStore,
SYS: Syscalls,
Expand All @@ -42,13 +42,15 @@ where
store: &'db DB,
epoch: ChainEpoch,
syscalls: SYS,
rand: &'r ChainRand,
) -> Result<Self, String> {
let state = StateTree::new_from_root(store, root)?;
Ok(VM {
state,
store,
epoch,
syscalls,
rand,
})
}

Expand Down Expand Up @@ -348,7 +350,7 @@ where
gas_cost: i64,
) -> (
Serialized,
DefaultRuntime<'db, 'm, '_, '_, DB, SYS>,
DefaultRuntime<'db, 'm, '_, '_, '_, DB, SYS>,
Option<ActorError>,
) {
let mut rt = DefaultRuntime::new(
Expand All @@ -361,6 +363,7 @@ where
*msg.from(),
msg.sequence(),
0,
self.rand,
);

let ser = match internal_send(&mut rt, msg, gas_cost) {
Expand Down
Loading