Skip to content

Commit

Permalink
Stmgr retrieval methods + is_ticket_winner calc for block validation (#…
Browse files Browse the repository at this point in the history
…369)

* Added retrieval methods from stmgr
* Added is_Ticket_winner calc
* Updated block validation checks to reflect changes
  • Loading branch information
dutterbutter authored Apr 23, 2020
1 parent cecd33d commit 3d5aeb2
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 73 deletions.
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 work_addr = self
.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(&work_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,
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)
}

0 comments on commit 3d5aeb2

Please sign in to comment.