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 = { 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 @@ -129,6 +129,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
3 changes: 3 additions & 0 deletions 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 = { 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,6 +21,8 @@ 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"

[dev-dependencies]
address = { package = "forest_address", path = "../../vm/address" }
Expand Down
55 changes: 54 additions & 1 deletion blockchain/chain/src/store/chain_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@

use super::{Error, TipIndex, TipsetMetadata};
use actor::{power::State as PowerState, STORAGE_POWER_ACTOR_ADDR};
use blake2b_simd::Params;
use blocks::{Block, BlockHeader, FullTipset, Tipset, TipsetKeys, TxMeta};
use byteorder::{BigEndian, WriteBytesExt};
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 @@ -273,6 +278,54 @@ where
out += &value;
Ok(out)
}

pub fn get_randomness_block(
ec2 marked this conversation as resolved.
Show resolved Hide resolved
&self,
blocks: &TipsetKeys,
pers: DomainSeparationTag,
round: ChainEpoch,
entropy: &[u8],
) -> Result<[u8; 32], Error> {
get_randomness(self.db.as_ref(), blocks, pers, round, entropy)
.map_err(|e| Error::Other(e.to_string()))
}
}

/// 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>
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
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" }
23 changes: 18 additions & 5 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 Down Expand Up @@ -39,6 +40,7 @@ pub struct DefaultRuntime<'db, 'msg, 'st, 'sys, BS, SYS> {
origin_nonce: u64,
num_actors_created: u64,
price_list: PriceList,
rand: ChainRand,
}

impl<'db, 'msg, 'st, 'sys, BS, SYS> DefaultRuntime<'db, 'msg, 'st, 'sys, BS, SYS>
Expand All @@ -58,6 +60,7 @@ where
origin: Address,
origin_nonce: u64,
num_actors_created: u64,
rand: 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 @@ -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(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.clone(),
ec2 marked this conversation as resolved.
Show resolved Hide resolved
);
internal_send::<BS, SYS>(&mut parent, &msg, 0)
};
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::*;
33 changes: 33 additions & 0 deletions vm/interpreter/src/rand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use blocks::TipsetKeys;
use chain;
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)
}
}
7 changes: 5 additions & 2 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 @@ -29,6 +28,7 @@ pub struct VM<'db, DB, SYS> {
store: &'db DB,
epoch: ChainEpoch,
syscalls: SYS,
rand: ChainRand,
// TODO: missing fields
}

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

Expand Down Expand Up @@ -361,6 +363,7 @@ where
*msg.from(),
msg.sequence(),
0,
self.rand.clone(),
ec2 marked this conversation as resolved.
Show resolved Hide resolved
);

let ser = match internal_send(&mut rt, msg, gas_cost) {
Expand Down
5 changes: 4 additions & 1 deletion vm/interpreter/tests/transfer_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

use actor::{init, ACCOUNT_ACTOR_CODE_ID, INIT_ACTOR_ADDR};
use address::Address;
use blocks::TipsetKeys;
use cid::multihash::{Blake2b256, Identity};
use db::MemoryDB;
use interpreter::{internal_send, DefaultRuntime, DefaultSyscalls};
use interpreter::{internal_send, ChainRand, DefaultRuntime, DefaultSyscalls};
use ipld_blockstore::BlockStore;
use ipld_hamt::Hamt;
use message::UnsignedMessage;
Expand Down Expand Up @@ -93,6 +94,7 @@ fn transfer_test() {

let default_syscalls = DefaultSyscalls::new(&store);

let dummyRand = ChainRand::new(TipsetKeys::new(vec![]));
let mut runtime = DefaultRuntime::new(
&mut state,
&store,
Expand All @@ -103,6 +105,7 @@ fn transfer_test() {
actor_addr_2.clone(),
0,
0,
dummyRand,
);
let _serialized = internal_send(&mut runtime, &message, 0).unwrap();

Expand Down
3 changes: 2 additions & 1 deletion vm/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ pub trait Runtime<BS: BlockStore> {
/// Randomness returns a (pseudo)random byte array drawing from a
/// random beacon at a given epoch and incorporating reequisite entropy
fn get_randomness(
&self,
personalization: DomainSeparationTag,
rand_epoch: ChainEpoch,
entropy: &[u8],
) -> Randomness;
) -> Result<Randomness, ActorError>;

/// Initializes the state object.
/// This is only valid in a constructor function and when the state has not yet been initialized.
Expand Down