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

Stmgr retrieval methods + is_ticket_winner calc for block validation #369

Merged
merged 12 commits into from
Apr 23, 2020
1 change: 1 addition & 0 deletions blockchain/blocks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ derive_builder = "0.9"
serde = { version = "1.0", features = ["derive"] }
encoding = { package = "forest_encoding", path = "../../encoding" }
num-bigint = { path = "../../utils/bigint", package = "forest_bigint" }
sha2 = { version = "0.8", default-features = false }
thiserror = "1.0"
vm = { path = "../../vm" }

Expand Down
29 changes: 29 additions & 0 deletions blockchain/blocks/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ use num_bigint::{
BigUint,
};
use serde::Deserialize;
use sha2::Digest;
use std::cmp::Ordering;
use std::fmt;
use std::time::{SystemTime, UNIX_EPOCH};
// TODO should probably have a central place for constants
const SHA_256_BITS: usize = 256;
const BLOCKS_PER_EPOCH: u64 = 5;

/// Header of a block
///
Expand Down Expand Up @@ -301,6 +305,31 @@ impl BlockHeader {

Ok(())
}
/// Returns true if (h(vrfout) * totalPower) < (e * sectorSize * 2^256)
pub fn is_ticket_winner(&self, mpow: BigUint, net_pow: BigUint) -> bool {
/*
Need to check that
(h(vrfout) + 1) / (max(h) + 1) <= e * myPower / totalPower
max(h) == 2^256-1
which in terms of integer math means:
(h(vrfout) + 1) * totalPower <= e * myPower * 2^256
in 2^256 space, it is equivalent to:
h(vrfout) * totalPower < e * myPower * 2^256
*/

// TODO switch ticket for election_proof
let h = sha2::Sha256::digest(self.ticket.vrfproof.bytes());
let mut lhs = BigUint::from_bytes_le(&h);
lhs *= net_pow;

// rhs = sectorSize * 2^256
// rhs = sectorSize << 256
let mut rhs = mpow << SHA_256_BITS;
rhs *= BigUint::from(BLOCKS_PER_EPOCH);

// h(vrfout) * totalPower < e * sectorSize * 2^256
lhs < rhs
}
}

/// human-readable string representation of a block CID
Expand Down
34 changes: 21 additions & 13 deletions blockchain/chain_sync/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,6 @@ where

/// Validates block semantically according to https://github.com/filecoin-project/specs/blob/6ab401c0b92efb6420c6e198ec387cf56dc86057/validation.md
fn validate(&self, block: &Block) -> Result<(), Error> {
// get header from full block
let header = block.header();

// check if block has been signed
Expand All @@ -613,23 +612,32 @@ where
// check messages to ensure valid state transitions
self.check_blk_msgs(block.clone(), &parent_tipset)?;

// TODO use computed state_root instead of parent_tipset.parent_state()
let wrk_addr = self
dutterbutter marked this conversation as resolved.
Show resolved Hide resolved
.state_manager
.get_miner_work_addr(&parent_tipset.parent_state(), header.miner_address())?;
// block signature check
// TODO need to pass in raw miner address; temp using header miner address
// see https://github.com/filecoin-project/lotus/blob/master/chain/sync.go#L611
header.check_block_signature(header.miner_address())?;
header.check_block_signature(&wrk_addr)?;

// TODO: incomplete, still need to retrieve power in order to ensure ticket is the winner
let _slash = self
.state_manager
.miner_slashed(header.miner_address(), &parent_tipset)?;
let _sector_size = self
let slash = self
.state_manager
.miner_sector_size(header.miner_address(), &parent_tipset)?;
.is_miner_slashed(header.miner_address(), &parent_tipset.parent_state())?;
if slash {
return Err(Error::Validation(
"Received block was from slashed or invalid miner".to_owned(),
));
}

// TODO winner_check
// TODO miner_check
let (c_pow, net_pow) = self
.state_manager
.get_power(&parent_tipset.parent_state(), header.miner_address())?;
// ticket winner check
if !header.is_ticket_winner(c_pow, net_pow) {
return Err(Error::Validation(
"Miner created a block but was not a winner".to_owned(),
));
}
// TODO verify_ticket_vrf
// TODO verify_election_proof_check

Ok(())
}
Expand Down
4 changes: 3 additions & 1 deletion blockchain/state_manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ edition = "2018"
[dependencies]
address = { package = "forest_address", path = "../../vm/address/" }
actor = { path = "../../vm/actor/" }
cid = { package = "forest_cid", path = "../../ipld/cid" }
db = { path = "../../node/db/" }
encoding = { package = "forest_encoding", path = "../../encoding/" }
num-bigint = { path = "../../utils/bigint", package = "forest_bigint" }
state_tree = { path = "../../vm/state_tree/" }
vm = { path = "../../vm" }
default_runtime = { path = "../../vm/default_runtime/" }
blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/" }
forest_blocks = { path = "../../blockchain/blocks" }
thiserror = "1.0"
9 changes: 9 additions & 0 deletions blockchain/state_manager/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ pub enum Error {
/// Error originating from key-value store
#[error(transparent)]
DB(#[from] DbErr),
/// Other state manager error
#[error("{0}")]
Other(String),
}

impl From<String> for Error {
fn from(e: String) -> Self {
Error::Other(e)
}
}
65 changes: 44 additions & 21 deletions blockchain/state_manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
mod errors;

pub use self::errors::*;
use actor::{miner, ActorState};
use actor::{miner, power, ActorState, STORAGE_POWER_ACTOR_ADDR};
use address::Address;
use blockstore::BlockStore;
use cid::Cid;
use default_runtime::resolve_to_key_addr;
use encoding::de::DeserializeOwned;
use forest_blocks::Tipset;
use num_bigint::BigUint;
use state_tree::{HamtStateTree, StateTree};
use std::sync::Arc;
use vm::SectorSize;

/// Intermediary for retrieving state objects and updating actor states
pub struct StateManager<DB> {
Expand All @@ -27,12 +28,12 @@ where
Self { bs }
}
/// Loads actor state from IPLD Store
fn load_actor_state<D>(&self, addr: &Address, ts: &Tipset) -> Result<D, Error>
fn load_actor_state<D>(&self, addr: &Address, state_cid: &Cid) -> Result<D, Error>
where
D: DeserializeOwned,
{
let actor = self
.get_actor(addr, ts)?
.get_actor(addr, state_cid)?
.ok_or_else(|| Error::ActorNotFound(addr.to_string()))?;
let act: D = self
.bs
Expand All @@ -41,23 +42,45 @@ where
.ok_or_else(|| Error::ActorStateNotFound(actor.state.to_string()))?;
Ok(act)
}
/// Returns the epoch at which the miner was slashed at
pub fn miner_slashed(&self, _addr: &Address, _ts: &Tipset) -> Result<u64, Error> {
// TODO update to use power actor if needed
todo!()
fn get_actor(&self, addr: &Address, state_cid: &Cid) -> Result<Option<ActorState>, Error> {
let state =
HamtStateTree::new_from_root(self.bs.as_ref(), state_cid).map_err(Error::State)?;
state.get_actor(addr).map_err(Error::State)
}
/// Returns the amount of space in each sector committed to the network by this miner
pub fn miner_sector_size(&self, addr: &Address, ts: &Tipset) -> Result<SectorSize, Error> {
let act: miner::State = self.load_actor_state(addr, ts)?;
// * Switch back to retrieving from Cid if/when changed in actors
// let info: miner::MinerInfo = self.bs.get(&act.info)?.ok_or_else(|| {
// Error::State("Could not retrieve miner info from IPLD store".to_owned())
// })?;
Ok(act.info.sector_size)
/// Returns true if miner has been slashed or is considered invalid
pub fn is_miner_slashed(&self, addr: &Address, state_cid: &Cid) -> Result<bool, Error> {
let ms: miner::State = self.load_actor_state(addr, state_cid)?;
if ms.post_state.has_failed_post() {
return Ok(true);
}

let ps: power::State = self.load_actor_state(&*STORAGE_POWER_ACTOR_ADDR, state_cid)?;
match ps.get_claim(self.bs.as_ref(), addr)? {
Some(_) => Ok(false),
None => Ok(true),
}
}
pub fn get_actor(&self, addr: &Address, ts: &Tipset) -> Result<Option<ActorState>, Error> {
let state = HamtStateTree::new_from_root(self.bs.as_ref(), ts.parent_state())
.map_err(Error::State)?;
state.get_actor(addr).map_err(Error::State)
/// Returns raw work address of a miner
pub fn get_miner_work_addr(&self, state_cid: &Cid, addr: &Address) -> Result<Address, Error> {
let ms: miner::State = self.load_actor_state(addr, state_cid)?;

let state =
HamtStateTree::new_from_root(self.bs.as_ref(), state_cid).map_err(Error::State)?;
// Note: miner::State info likely to be changed to CID
let addr = resolve_to_key_addr(&state, self.bs.as_ref(), &ms.info.worker)
.map_err(|e| Error::Other(format!("Failed to resolve key address; error: {}", e)))?;
Ok(addr)
}
/// Returns specified actor's claimed power and total network power as a tuple
pub fn get_power(&self, state_cid: &Cid, addr: &Address) -> Result<(BigUint, BigUint), Error> {
let ps: power::State = self.load_actor_state(&*STORAGE_POWER_ACTOR_ADDR, state_cid)?;

if let Some(claim) = ps.get_claim(self.bs.as_ref(), addr)? {
Ok((claim.power, ps.total_network_power))
} else {
Err(Error::State(
"Failed to retrieve claimed power from actor state".to_owned(),
))
}
}
}
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/power/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ impl State {
}

/// Gets claim from claims map by address
pub(super) fn get_claim<BS: BlockStore>(
pub fn get_claim<BS: BlockStore>(
&self,
store: &BS,
a: &Address,
Expand Down
89 changes: 52 additions & 37 deletions vm/default_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ mod gas_syscalls;
use self::gas_block_store::GasBlockStore;
use self::gas_syscalls::GasSyscalls;
use actor::{
self, 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, POWER_ACTOR_CODE_ID,
REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID,
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,
POWER_ACTOR_CODE_ID, REWARD_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID,
};
use address::{Address, Protocol};
use byteorder::{BigEndian, WriteBytesExt};
Expand Down Expand Up @@ -156,39 +156,6 @@ where

Ok(())
}
fn resolve_to_key_addr(&self, addr: &Address) -> Result<Address, ActorError> {
if addr.protocol() == Protocol::BLS || addr.protocol() == Protocol::Secp256k1 {
return Ok(addr.clone());
}
let act = self.get_actor(&addr)?;
if act.code != *ACCOUNT_ACTOR_CODE_ID {
return Err(self.abort(
ExitCode::SysErrInternal,
format!("address {:?} was not for an account actor", addr),
));
}
let actor_state: actor::account::State = self
.store
.get(&act.state)
// TODO this needs to be a fatal error
.map_err(|e| {
self.abort(
ExitCode::SysErrInternal,
format!(
"Could not get actor state in resolve_to_key_addr: {:?}",
e.to_string()
),
)
})?
.ok_or_else(|| {
self.abort(
ExitCode::SysErrInternal,
"Actor state not found in resolve_to_key_addr",
)
})?;

Ok(actor_state.address)
}
}

impl<ST, BS, SYS> Runtime<BS> for DefaultRuntime<'_, '_, '_, ST, BS, SYS>
Expand Down Expand Up @@ -378,7 +345,7 @@ where
ActorError::new(exit_code, msg.as_ref().to_owned())
}
fn new_actor_address(&mut self) -> Result<Address, ActorError> {
let oa = self.resolve_to_key_addr(&self.origin)?;
let oa = resolve_to_key_addr(self.state, &self.store, &self.origin)?;
let mut b = to_vec(&oa).map_err(|e| {
self.abort(
ExitCode::ErrSerialization,
Expand Down Expand Up @@ -546,3 +513,51 @@ fn transfer<ST: StateTree>(

Ok(())
}

/// Returns public address of the specified actor address
pub fn resolve_to_key_addr<'st, 'bs, ST, BS>(
st: &'st ST,
store: &'bs BS,
dutterbutter marked this conversation as resolved.
Show resolved Hide resolved
addr: &Address,
) -> Result<Address, ActorError>
where
ST: StateTree,
BS: BlockStore,
{
if addr.protocol() == Protocol::BLS || addr.protocol() == Protocol::Secp256k1 {
return Ok(addr.clone());
}

let act = st
.get_actor(&addr)
.map_err(|e| ActorError::new(ExitCode::SysErrInternal, e))?
.ok_or_else(|| {
ActorError::new(
ExitCode::SysErrInternal,
format!("Failed to retrieve actor: {}", addr),
)
})?;

if act.code != *ACCOUNT_ACTOR_CODE_ID {
return Err(ActorError::new_fatal(format!(
"Address was not found for an account actor: {}",
addr
)));
}
let acc_st: account::State = store
.get(&act.state)
.map_err(|e| {
ActorError::new_fatal(format!(
"Failed to get account actor state for: {}, e: {}",
addr, e
))
})?
.ok_or_else(|| {
ActorError::new_fatal(format!(
"Address was not found for an account actor: {}",
addr
))
})?;

Ok(acc_st.address)
}