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

Refactor Blockheader #169

Merged
merged 12 commits into from
Jan 16, 2020
3 changes: 2 additions & 1 deletion blockchain/blocks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ clock = { path = "../../node/clock" }
cid = { package = "ferret_cid", path = "../../ipld/cid" }
multihash = "0.9.3"
derive_builder = "0.9"
serde_cbor = "0.11.0"
serde = { version = "1.0", features = ["derive"] }
encoding = { path = "../../encoding" }
134 changes: 19 additions & 115 deletions blockchain/blocks/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,165 +3,69 @@

#![allow(dead_code)]

use super::ticket::Ticket;
use super::TipSetKeys;
use address::Address;
use super::{BlockHeader, RawBlock};
use cid::Cid;
use clock::ChainEpoch;
use crypto::Signature;
use derive_builder::Builder;
use encoding::{Cbor, Error as EncodingError};
use message::{SignedMessage, UnsignedMessage};
use multihash::Hash;
use serde::{Deserialize, Serialize};
use std::fmt;

// DefaultHashFunction represents the default hashing function to use
// TODO SHOULD BE BLAKE2B
const DEFAULT_HASH_FUNCTION: Hash = Hash::Keccak256;
// TODO SHOULD BE BLAKE2B256 (256 hashing not implemented)
const DEFAULT_HASH_FUNCTION: Hash = Hash::Blake2b512;
// TODO determine the purpose for these structures, currently spec includes them but with no definition
struct ChallengeTicketsCommitment {}
struct PoStCandidate {}
struct PoStRandomness {}
struct PoStProof {}

/// Header of a block
///
/// Usage:
/// ```
/// use blocks::{BlockHeader, TipSetKeys, Ticket, TxMeta};
/// use address::Address;
/// use cid::{Cid, Codec, Prefix, Version};
/// use clock::ChainEpoch;
///
/// BlockHeader::builder()
/// .parents(TipSetKeys::default())
/// .miner_address(Address::new_id(0).unwrap())
/// .bls_aggregate(vec![])
/// .weight(0) //optional
/// .epoch(ChainEpoch::default()) //optional
/// .messages(TxMeta::default()) //optional
/// .message_receipts(Cid::default()) //optional
/// .state_root(Cid::default()) //optional
/// .timestamp(0) //optional
/// .ticket(Ticket::default()) //optional
/// .build()
/// .unwrap();
/// ```
#[derive(Clone, Debug, PartialEq, Builder)]
#[builder(name = "BlockHeaderBuilder")]
pub struct BlockHeader {
// CHAIN LINKING
/// Parents is the set of parents this block was based on. Typically one,
/// but can be several in the case where there were multiple winning ticket-
/// holders for an epoch
pub parents: TipSetKeys,

/// weight is the aggregate chain weight of the parent set
#[builder(default)]
pub weight: u64,

/// epoch is the period in which a new block is generated. There may be multiple rounds in an epoch
#[builder(default)]
pub epoch: ChainEpoch,

// MINER INFO
/// miner_address is the address of the miner actor that mined this block
pub miner_address: Address,

// STATE
/// messages contains the merkle links for bls_messages and secp_messages
#[builder(default)]
pub messages: TxMeta,

/// message_receipts is the Cid of the root of an array of MessageReceipts
#[builder(default)]
pub message_receipts: Cid,

/// state_root is a cid pointer to the state tree after application of the transactions state transitions
#[builder(default)]
pub state_root: Cid,

// CONSENSUS
/// timestamp, in seconds since the Unix epoch, at which this block was created
#[builder(default)]
pub timestamp: u64,

/// the ticket submitted with this block
#[builder(default)]
pub ticket: Ticket,

// SIGNATURES
/// aggregate signature of miner in block
pub bls_aggregate: Signature,

// CACHE
/// stores the cid for the block after the first call to `cid()`
#[builder(default)]
pub cached_cid: Cid,
/// stores the hashed bytes of the block after the fist call to `cid()`
#[builder(default)]
pub cached_bytes: Vec<u8>,
}

impl BlockHeader {
pub fn builder() -> BlockHeaderBuilder {
BlockHeaderBuilder::default()
}
/// cid returns the content id of this header
pub fn cid(&mut self) -> Cid {
// TODO Encode blockheader using CBOR into cache_bytes
// Change DEFAULT_HASH_FUNCTION to utilize blake2b
//
// Currently content id for headers will be incomplete until encoding and supporting libraries are completed
let new_cid = Cid::from_bytes_default(&self.cached_bytes).unwrap();
self.cached_cid = new_cid;
self.cached_cid.clone()
}
}

/// A complete block
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Block {
header: BlockHeader,
bls_messages: UnsignedMessage,
secp_messages: SignedMessage,
}

/// Used to extract required encoded data and cid for persistent block storage
pub trait RawBlock {
fn raw_data(&self) -> Vec<u8>;
fn cid(&self) -> Cid;
fn multihash(&self) -> Hash;
}
// TODO verify format or implement custom serialize/deserialize function (if necessary):
// https://github.com/ChainSafe/ferret/issues/143

impl Cbor for Block {}

impl RawBlock for Block {
austinabell marked this conversation as resolved.
Show resolved Hide resolved
/// returns the block raw contents as a byte array
fn raw_data(&self) -> Vec<u8> {
fn raw_data(&self) -> Result<Vec<u8>, EncodingError> {
// TODO should serialize block header using CBOR encoding
self.header.cached_bytes.clone()
self.marshal_cbor()
}
/// returns the content identifier of the block
fn cid(&self) -> Cid {
self.header.clone().cid()
self.header.cid().clone()
}
/// returns the hash contained in the block CID
fn multihash(&self) -> Hash {
self.header.cached_cid.prefix().mh_type
self.cid().prefix().mh_type
}
}

/// human-readable string representation of a block CID
impl fmt::Display for Block {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "block: {:?}", self.header.cached_cid.clone())
write!(f, "block: {:?}", self.cid())
}
}

/// Tracks the merkleroots of both secp and bls messages separately
#[derive(Clone, Debug, PartialEq, Default)]
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct TxMeta {
pub bls_messages: Cid,
pub secp_messages: Cid,
}

// TODO verify format or implement custom serialize/deserialize function (if necessary):
// https://github.com/ChainSafe/ferret/issues/143

/// ElectionPoStVerifyInfo seems to be connected to VRF
/// see https://github.com/filecoin-project/lotus/blob/master/chain/sync.go#L1099
struct ElectionPoStVerifyInfo {
Expand Down
170 changes: 170 additions & 0 deletions blockchain/blocks/src/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0

use super::{Ticket, TipSetKeys, TxMeta};
use address::Address;
use cid::Cid;
use clock::ChainEpoch;
use crypto::Signature;
use derive_builder::Builder;
use encoding::Cbor;
use serde::{Deserialize, Serialize};

/// Header of a block
///
/// Usage:
/// ```
/// use blocks::{BlockHeader, TipSetKeys, Ticket, TxMeta};
/// use address::Address;
/// use cid::{Cid, Codec, Prefix, Version};
/// use clock::ChainEpoch;
///
/// BlockHeader::builder()
/// .miner_address(Address::new_id(0).unwrap()) // optional
/// .bls_aggregate(vec![]) // optional
/// .parents(TipSetKeys::default()) // optional
/// .weight(0) // optional
/// .epoch(ChainEpoch::default()) // optional
/// .messages(TxMeta::default()) // optional
/// .message_receipts(Cid::default()) // optional
/// .state_root(Cid::default()) // optional
/// .timestamp(0) // optional
/// .ticket(Ticket::default()) // optional
/// .build_and_validate()
/// .unwrap();
/// ```
#[derive(Clone, Debug, PartialEq, Builder, Serialize, Deserialize)]
#[builder(name = "BlockHeaderBuilder")]
pub struct BlockHeader {
// CHAIN LINKING
/// Parents is the set of parents this block was based on. Typically one,
/// but can be several in the case where there were multiple winning ticket-
/// holders for an epoch
#[builder(default)]
parents: TipSetKeys,

/// weight is the aggregate chain weight of the parent set
#[builder(default)]
weight: u64,

/// epoch is the period in which a new block is generated.
/// There may be multiple rounds in an epoch
#[builder(default)]
epoch: ChainEpoch,
// MINER INFO
/// miner_address is the address of the miner actor that mined this block
#[builder(default)]
miner_address: Address,

// STATE
/// messages contains the merkle links for bls_messages and secp_messages
#[builder(default)]
messages: TxMeta,

/// message_receipts is the Cid of the root of an array of MessageReceipts
#[builder(default)]
message_receipts: Cid,

/// state_root is a cid pointer to the state tree after application of
/// the transactions state transitions
#[builder(default)]
state_root: Cid,

// CONSENSUS
/// timestamp, in seconds since the Unix epoch, at which this block was created
#[builder(default)]
timestamp: u64,
/// the ticket submitted with this block
#[builder(default)]
ticket: Ticket,
// SIGNATURES
/// aggregate signature of miner in block
#[builder(default)]
bls_aggregate: Signature,
// CACHE
/// stores the cid for the block after the first call to `cid()`
#[serde(skip_serializing)]
#[builder(default)]
cached_cid: Cid,
/// stores the hashed bytes of the block after the fist call to `cid()`
#[serde(skip_serializing)]
#[builder(default)]
cached_bytes: Vec<u8>,
}

// TODO verify format or implement custom serialize/deserialize function (if necessary):
// https://github.com/ChainSafe/ferret/issues/143

impl Cbor for BlockHeader {}

impl BlockHeader {
/// Generates a BlockHeader builder as a constructor
pub fn builder() -> BlockHeaderBuilder {
BlockHeaderBuilder::default()
}
/// Getter for BlockHeader parents
pub fn parents(&self) -> &TipSetKeys {
&self.parents
}
/// Getter for BlockHeader weight
pub fn weight(&self) -> u64 {
self.weight
}
/// Getter for BlockHeader epoch
pub fn epoch(&self) -> &ChainEpoch {
&self.epoch
}
/// Getter for BlockHeader miner_address
pub fn miner_address(&self) -> &Address {
&self.miner_address
}
/// Getter for BlockHeader messages
pub fn messages(&self) -> &TxMeta {
&self.messages
}
/// Getter for BlockHeader message_receipts
pub fn message_receipts(&self) -> &Cid {
&self.message_receipts
}
/// Getter for BlockHeader state_root
pub fn state_root(&self) -> &Cid {
&self.state_root
}
/// Getter for BlockHeader timestamp
pub fn timestamp(&self) -> u64 {
self.timestamp
}
/// Getter for BlockHeader ticket
pub fn ticket(&self) -> &Ticket {
&self.ticket
}
/// Getter for BlockHeader bls_aggregate
pub fn bls_aggregate(&self) -> &Signature {
&self.bls_aggregate
}
/// Getter for BlockHeader cid
pub fn cid(&self) -> &Cid {
// Cache should be initialized, otherwise will return default Cid
&self.cached_cid
}
/// Updates cache and returns mutable reference of header back
fn update_cache(&mut self) -> Result<(), String> {
self.cached_bytes = self.marshal_cbor().map_err(|e| e.to_string())?;
self.cached_cid = Cid::from_bytes_default(&self.cached_bytes).map_err(|e| e.to_string())?;
Ok(())
}
}

impl BlockHeaderBuilder {
pub fn build_and_validate(&self) -> Result<BlockHeader, String> {
// Convert header builder into header struct
let mut header = self.build()?;

// TODO add validation function

// Fill header cache with raw bytes and cid
header.update_cache()?;

Ok(header)
}
}
4 changes: 4 additions & 0 deletions blockchain/blocks/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@

mod block;
mod errors;
mod header;
mod raw_block;
mod ticket;
mod tipset;

pub use block::*;
pub use errors::*;
pub use header::*;
pub use raw_block::*;
pub use ticket::*;
pub use tipset::*;
14 changes: 14 additions & 0 deletions blockchain/blocks/src/raw_block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0

use cid::Cid;
use encoding::Error as EncodingError;
use multihash::Hash;

// TODO move raw block to own crate
/// Used to extract required encoded data and cid for persistent block storage
pub trait RawBlock {
fn raw_data(&self) -> Result<Vec<u8>, EncodingError>;
fn cid(&self) -> Cid;
fn multihash(&self) -> Hash;
}
Loading