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

Implement Weight method + st/gt for heaviest ts #359

Merged
merged 12 commits into from
Apr 20, 2020
27 changes: 27 additions & 0 deletions blockchain/blocks/src/tipset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use clock::ChainEpoch;
use encoding::{
de::{self, Deserializer},
ser::{self, Serializer},
to_vec, Cbor, Error as EncodingError,
};
use num_bigint::BigUint;
use serde::Deserialize;
Expand Down Expand Up @@ -204,6 +205,32 @@ impl Tipset {
}
}

impl Cbor for Tipset {
fn marshal_cbor(&self) -> Result<Vec<u8>, EncodingError> {
Ok(to_vec(&self.cids())?)
}
}

impl ser::Serialize for Tipset {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(&self.blocks, &self.key).serialize(serializer)
}
}

impl<'de> de::Deserialize<'de> for Tipset {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
let (blocks, key) = Deserialize::deserialize(deserializer)?;

Ok(Tipset { blocks, key })
}
}
dutterbutter marked this conversation as resolved.
Show resolved Hide resolved

/// FullTipSet is an expanded version of the TipSet that contains all the blocks and messages
#[derive(Debug, PartialEq, Clone)]
pub struct FullTipset {
Expand Down
4 changes: 4 additions & 0 deletions blockchain/chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ clock = { path = "../../node/clock" }
encoding = { package = "forest_encoding", path = "../../encoding" }
serde = { version = "1.0", features = ["derive"] }
num-bigint = { path = "../../utils/bigint", package = "forest_bigint" }
num-traits = "0.2"
message = { package = "forest_message", path = "../../vm/message" }
ipld_blockstore = { path = "../../ipld/blockstore" }
ipld_amt = { path = "../../ipld/amt/" }
thiserror = "1.0"
log = "0.4.8"
state_tree = { path = "../../vm/state_tree/" }
actor = { path = "../../vm/actor/" }

[dev-dependencies]
address = { package = "forest_address", path = "../../vm/address" }
Expand Down
92 changes: 81 additions & 11 deletions blockchain/chain/src/store/chain_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,31 @@
// SPDX-License-Identifier: Apache-2.0, MIT

use super::{Error, TipIndex, TipSetMetadata};
use actor::{power::State as PowerState, STORAGE_POWER_ACTOR_ADDR};
use blocks::{Block, BlockHeader, FullTipset, TipSetKeys, Tipset, TxMeta};
use cid::Cid;
use encoding::{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::{HamtStateTree, StateTree};
use std::sync::Arc;

const GENESIS_KEY: &str = "gen_block";
const HEAD_KEY: &str = "head";
// constants for Weight calculation
/// The ratio of weight contributed by short-term vs long-term factors in a given round
const W_RATIO_NUM: u64 = 1;
const W_RATIO_DEN: u64 = 2;
/// Blocks epoch allowed
const BLOCKS_PER_EPOCH: u64 = 5;

/// Generic implementation of the datastore trait and structures
pub struct ChainStore<DB> {
// TODO add IPLD Store
// TODO add StateTreeLoader
// TODO add a pubsub channel that publishes an event every time the head changes.

// key-value datastore
Expand Down Expand Up @@ -44,9 +54,11 @@ where
}
}

/// Sets heaviest tipset within ChainStore
pub fn set_heaviest_tipset(&mut self, ts: Arc<Tipset>) {
/// Sets heaviest tipset within ChainStore and store its tipset cids under HEAD_KEY
fn set_heaviest_tipset(&mut self, ts: Arc<Tipset>) -> Result<(), Error> {
self.db.write(HEAD_KEY, ts.marshal_cbor()?)?;
self.heaviest = ts;
Ok(())
}

/// Sets tip_index tracker
Expand All @@ -59,21 +71,16 @@ where
};
Ok(self.tip_index.put(&meta)?)
}
/// weight
pub fn weight(&self, _ts: &Tipset) -> Result<BigUint, Error> {
// TODO
Ok(BigUint::from(0 as u32))
}

/// Writes genesis to blockstore
pub fn set_genesis(&self, header: BlockHeader) -> Result<(), Error> {
pub fn set_genesis(&mut self, header: BlockHeader) -> Result<(), Error> {
austinabell marked this conversation as resolved.
Show resolved Hide resolved
self.db.write(GENESIS_KEY, header.marshal_cbor()?)?;
let ts: Tipset = Tipset::new(vec![header])?;
Ok(self.persist_headers(&ts)?)
}

/// Writes encoded blockheader data to blockstore
pub fn persist_headers(&self, tip: &Tipset) -> Result<(), Error> {
pub fn persist_headers(&mut self, tip: &Tipset) -> Result<(), Error> {
let mut raw_header_data = Vec::new();
let mut keys = Vec::new();
// loop through block to push blockheader raw data and cid into vector to be stored
Expand All @@ -83,6 +90,8 @@ where
keys.push(block.cid().key());
}
}
self.is_heaviest(tip)?;

Ok(self.db.bulk_write(&keys, &raw_header_data)?)
}

Expand All @@ -99,6 +108,22 @@ where
Ok(())
}

/// Loads heaviest tipset from datastore and sets as heaviest in chainstore
fn _load_heaviest_tipset(&mut self) -> Result<(), Error> {
let keys: Vec<Cid> = match self.db.read(HEAD_KEY)? {
Some(bz) => from_slice(&bz)?,
None => {
warn!("No previous chain state found");
return Err(Error::NotFound("No chain state found"));
dutterbutter marked this conversation as resolved.
Show resolved Hide resolved
}
};

let heaviest_ts = self.tipset_from_keys(&TipSetKeys::new(keys))?;
// set as heaviest tipset
self.heaviest = Arc::new(heaviest_ts);
Ok(())
}

/// Returns genesis blockheader from blockstore
pub fn genesis(&self) -> Result<Option<BlockHeader>, Error> {
Ok(match self.db.read(GENESIS_KEY)? {
Expand Down Expand Up @@ -210,6 +235,51 @@ where

Ok(FullTipset::new(blocks))
}
/// Determines if provided tipset is heavier than existing known heaviest tipset
fn is_heaviest(&mut self, ts: &Tipset) -> Result<(), Error> {
// TODO determine if expanded tipset is required; see https://github.com/filecoin-project/lotus/blob/testnet/3/chain/store/store.go#L236
let new_weight = self.weight(ts)?;
let curr_weight = self.weight(&self.heaviest)?;

if new_weight > curr_weight {
// TODO potentially need to deal with re-orgs here
info!("New heaviest tipset");
self.set_heaviest_tipset(Arc::new(ts.clone()))?;
}

Ok(())
}
austinabell marked this conversation as resolved.
Show resolved Hide resolved

/// Returns the weight of provided tipset
fn weight(&self, ts: &Tipset) -> Result<BigUint, String> {
if ts.is_empty() {
return Ok(BigUint::zero());
}

let mut tpow = BigUint::zero();
let state = HamtStateTree::new_from_root(self.db.as_ref(), ts.parent_state())?;
if let Some(act) = state.get_actor(&*STORAGE_POWER_ACTOR_ADDR)? {
if let Some(state) = self
.db
.get::<PowerState>(&act.state)
.map_err(|e| e.to_string())?
{
tpow = state.total_network_power;
}
}
let log2_p = if tpow > BigUint::zero() {
BigUint::from(tpow.bits() - 1)
} else {
return Err("All power in the net is gone. You network might be disconnected, or the net is dead!".to_owned());
};

let mut out = ts.weight() + (&log2_p << 8);
let e_weight =
((log2_p * BigUint::from(ts.blocks().len())) * BigUint::from(W_RATIO_NUM)) << 8;
let value = e_weight / (BigUint::from(BLOCKS_PER_EPOCH) * BigUint::from(W_RATIO_DEN));
out += &value;
Ok(out)
}
}

#[cfg(test)]
Expand All @@ -222,7 +292,7 @@ mod tests {
fn genesis_test() {
let db = db::MemoryDB::default();

let cs = ChainStore::new(Arc::new(db));
let mut cs = ChainStore::new(Arc::new(db));
let gen_block = BlockHeader::builder()
.epoch(1)
.weight((2 as u32).into())
Expand Down
6 changes: 6 additions & 0 deletions blockchain/chain/src/store/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,9 @@ impl From<SerdeErr> for Error {
Error::Encoding(e.to_string())
}
}

impl From<String> for Error {
fn from(e: String) -> Self {
Error::Other(e)
}
}
2 changes: 1 addition & 1 deletion blockchain/chain_sync/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ where
}

/// Persists headers from tipset slice to chain store
fn persist_headers(&self, tipsets: &[Tipset]) -> Result<(), Error> {
fn persist_headers(&mut self, tipsets: &[Tipset]) -> Result<(), Error> {
austinabell marked this conversation as resolved.
Show resolved Hide resolved
Ok(tipsets
.iter()
.try_for_each(|ts| self.chain_store.persist_headers(ts))?)
Expand Down
2 changes: 1 addition & 1 deletion blockchain/state_manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ state_tree = { path = "../../vm/state_tree/" }
vm = { path = "../../vm" }
blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/" }
forest_blocks = { path = "../../blockchain/blocks" }
thiserror = "1.0"
thiserror = "1.0"
2 changes: 1 addition & 1 deletion forest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ async-std = { version = "1.4.0", features = ["attributes"] }
serde = { version = "1.0", features = ["derive"] }
pretty_env_logger = "0.4.0"
ctrlc = "3.1.4"
chain_sync = { path = "../blockchain/chain_sync" }
chain_sync = { path = "../blockchain/chain_sync" }
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/power/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ impl Actor {
"failed to get miner pledge balance".to_owned(),
)
})?;
assert!(claim.power >= StoragePower::from(0));
assert!(claim.power >= StoragePower::zero());

// Elapsed since the fault (i.e. since the higher of the two blocks)
let fault_age = curr_epoch.checked_sub(fault.epoch).ok_or_else(|| {
Expand Down
6 changes: 2 additions & 4 deletions vm/actor/src/builtin/power/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ lazy_static! {
pub static ref EPOCH_TOTAL_EXPECTED_REWARD: BigUint = reward::BLOCK_REWARD_TARGET.clone() * 5u8; // PARAM_FINISH

/// Minimum power of an individual miner to meet the threshold for leader election.
pub static ref CONSENSUS_MINER_MIN_POWER: StoragePower = StoragePower::from(2 << 30); // placeholder
pub static ref CONSENSUS_MINER_MIN_POWER: StoragePower = StoragePower::from_i32(2 << 30).unwrap(); // placeholder

}

Expand Down Expand Up @@ -101,9 +101,7 @@ pub fn pledge_for_weight(
* weight.duration
* &*EPOCH_TOTAL_EXPECTED_REWARD
* &*PLEDGE_FACTOR;
let denominator = network_power
.to_biguint()
.expect("Storage power should be positive");
let denominator = network_power;

numerator / denominator
}
20 changes: 5 additions & 15 deletions vm/actor/src/builtin/power/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ use clock::ChainEpoch;
use encoding::Cbor;
use ipld_blockstore::BlockStore;
use ipld_hamt::Hamt;
use num_bigint::{
bigint_ser::{BigIntDe, BigIntSer},
biguint_ser::{BigUintDe, BigUintSer},
Sign,
};
use num_bigint::biguint_ser::{BigUintDe, BigUintSer};
use num_traits::{CheckedSub, Zero};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use vm::{Serialized, TokenAmount};
Expand Down Expand Up @@ -184,10 +180,6 @@ impl State {
}
}

// Negative values check
if claim.power.sign() == Sign::Minus {
return Err(format!("negative claimed power: {}", claim.power));
}
if self.num_miners_meeting_min_power < 0 {
return Err(format!(
"negative number of miners: {}",
Expand Down Expand Up @@ -216,8 +208,6 @@ impl State {
addr: &Address,
claim: Claim,
) -> Result<(), String> {
assert!(claim.power.sign() == Sign::Minus);

let mut map: Hamt<BytesKey, _> =
Hamt::load_with_bit_width(&self.claims, store, HAMT_BIT_WIDTH)?;

Expand Down Expand Up @@ -374,7 +364,7 @@ impl Serialize for State {
S: Serializer,
{
(
BigIntSer(&self.total_network_power),
BigUintDe(self.total_network_power.clone()),
&self.miner_count,
&self.escrow_table,
&self.cron_event_queue,
Expand All @@ -393,7 +383,7 @@ impl<'de> Deserialize<'de> for State {
D: Deserializer<'de>,
{
let (
BigIntDe(total_network_power),
BigUintDe(total_network_power),
miner_count,
escrow_table,
cron_event_queue,
Expand Down Expand Up @@ -426,7 +416,7 @@ impl Serialize for Claim {
where
S: Serializer,
{
(BigIntSer(&self.power), BigUintSer(&self.pledge)).serialize(serializer)
(BigUintSer(&self.power), BigUintSer(&self.pledge)).serialize(serializer)
}
}

Expand All @@ -435,7 +425,7 @@ impl<'de> Deserialize<'de> for Claim {
where
D: Deserializer<'de>,
{
let (BigIntDe(power), BigUintDe(pledge)) = Deserialize::deserialize(deserializer)?;
let (BigUintDe(power), BigUintDe(pledge)) = Deserialize::deserialize(deserializer)?;
Ok(Self { power, pledge })
}
}
Expand Down
4 changes: 2 additions & 2 deletions vm/actor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub use vm::{ActorState, DealID, Serialized};
use encoding::Error as EncodingError;
use ipld_blockstore::BlockStore;
use ipld_hamt::{BytesKey, Hamt};
use num_bigint::BigInt;
use num_bigint::{BigInt, BigUint};
use unsigned_varint::decode::Error as UVarintError;

const HAMT_BIT_WIDTH: u8 = 5;
Expand All @@ -23,7 +23,7 @@ type EmptyType = [u8; 0];
const EMPTY_VALUE: EmptyType = [];

/// Storage power unit, could possibly be a BigUint
type StoragePower = BigInt;
type StoragePower = BigUint;

/// Deal weight
type DealWeight = BigInt;
Expand Down
1 change: 1 addition & 0 deletions vm/interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use num_bigint::BigUint;
use num_traits::Zero;
use runtime::Syscalls;
use vm::{price_list_by_epoch, ActorError, ExitCode, Serialized, StateTree};

/// Interpreter which handles execution of state transitioning messages and returns receipts
/// from the vm execution.
pub struct VM<'a, ST, DB, SYS> {
Expand Down