diff --git a/blockchain/blocks/Cargo.toml b/blockchain/blocks/Cargo.toml index 09494f89b96e..1e84f8480385 100644 --- a/blockchain/blocks/Cargo.toml +++ b/blockchain/blocks/Cargo.toml @@ -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" } diff --git a/blockchain/blocks/src/block.rs b/blockchain/blocks/src/block.rs index 6bb6ccf7e62d..86dcda0fc37f 100644 --- a/blockchain/blocks/src/block.rs +++ b/blockchain/blocks/src/block.rs @@ -3,165 +3,53 @@ #![allow(dead_code)] -use super::ticket::Ticket; -use super::TipSetKeys; -use address::Address; +use super::BlockHeader; use cid::Cid; -use clock::ChainEpoch; -use crypto::Signature; -use derive_builder::Builder; +use encoding::Cbor; 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, -} - -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; - 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 RawBlock for Block { - /// returns the block raw contents as a byte array - fn raw_data(&self) -> Vec { - // TODO should serialize block header using CBOR encoding - self.header.cached_bytes.clone() - } - /// returns the content identifier of the block - fn cid(&self) -> Cid { - self.header.clone().cid() - } - /// returns the hash contained in the block CID - fn multihash(&self) -> Hash { - self.header.cached_cid.prefix().mh_type - } -} +impl Cbor for Block {} /// 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.header.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 { diff --git a/blockchain/blocks/src/header.rs b/blockchain/blocks/src/header.rs new file mode 100644 index 000000000000..03b06e87acbc --- /dev/null +++ b/blockchain/blocks/src/header.rs @@ -0,0 +1,187 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0 + +use super::{RawBlock, Ticket, TipSetKeys, TxMeta}; +use address::Address; +use cid::Cid; +use clock::ChainEpoch; +use crypto::Signature; +use derive_builder::Builder; +use encoding::{Cbor, Error as EncodingError}; +use multihash::Hash; +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, +} + +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + +impl Cbor for BlockHeader {} + +impl RawBlock for BlockHeader { + /// returns the block raw contents as a byte array + fn raw_data(&self) -> Result, EncodingError> { + // TODO should serialize block header using CBOR encoding + self.marshal_cbor() + } + /// returns the content identifier of the block + fn cid(&self) -> Cid { + self.cid().clone() + } + /// returns the hash contained in the block CID + fn multihash(&self) -> Hash { + self.cid().prefix().mh_type + } +} + +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 { + // 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) + } +} diff --git a/blockchain/blocks/src/lib.rs b/blockchain/blocks/src/lib.rs index 14f38593579e..bd065b7d91ff 100644 --- a/blockchain/blocks/src/lib.rs +++ b/blockchain/blocks/src/lib.rs @@ -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::*; diff --git a/blockchain/blocks/src/raw_block.rs b/blockchain/blocks/src/raw_block.rs new file mode 100644 index 000000000000..204e06290f13 --- /dev/null +++ b/blockchain/blocks/src/raw_block.rs @@ -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, EncodingError>; + fn cid(&self) -> Cid; + fn multihash(&self) -> Hash; +} diff --git a/blockchain/blocks/src/ticket.rs b/blockchain/blocks/src/ticket.rs index 47d0494faf4a..6d480e654e7a 100644 --- a/blockchain/blocks/src/ticket.rs +++ b/blockchain/blocks/src/ticket.rs @@ -2,16 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 use crypto::VRFResult; +use serde::{Deserialize, Serialize}; /// A Ticket is a marker of a tick of the blockchain's clock. It is the source /// of randomness for proofs of storage and leader election. It is generated /// by the miner of a block using a VRF and a VDF. -#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Default)] +#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Default, Serialize, Deserialize)] pub struct Ticket { /// A proof output by running a VRF on the VDFResult of the parent ticket pub vrfproof: VRFResult, } +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl Ticket { /// Ticket constructor pub fn new(vrfproof: VRFResult) -> Self { diff --git a/blockchain/blocks/src/tipset.rs b/blockchain/blocks/src/tipset.rs index 15ca9eeb030e..1577b5d1b388 100644 --- a/blockchain/blocks/src/tipset.rs +++ b/blockchain/blocks/src/tipset.rs @@ -4,23 +4,25 @@ #![allow(unused_variables)] #![allow(dead_code)] -use super::block::BlockHeader; -use super::errors::Error; -use super::ticket::Ticket; +use super::{BlockHeader, Error, Ticket}; use cid::Cid; use clock::ChainEpoch; +use serde::{Deserialize, Serialize}; /// A set of CIDs forming a unique key for a TipSet. /// Equal keys will have equivalent iteration order, but note that the CIDs are *not* maintained in /// the same order as the canonical iteration order of blocks in a tipset (which is by ticket) -#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] pub struct TipSetKeys { pub cids: Vec, } +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl TipSetKeys { /// checks whether the set contains exactly the same CIDs as another. - fn equals(&self, key: TipSetKeys) -> bool { + fn equals(&self, key: &TipSetKeys) -> bool { if self.cids.len() != key.cids.len() { return false; } @@ -64,33 +66,33 @@ impl Tipset { if i > 0 { // Skip redundant check // check parent cids are equal - if !headers[i].parents.equals(headers[0].parents.clone()) { + if !headers[i].parents().equals(headers[0].parents()) { return Err(Error::InvalidTipSet( "parent cids are not equal".to_string(), )); } // check weights are equal - if headers[i].weight != headers[0].weight { + if headers[i].weight() != headers[0].weight() { return Err(Error::InvalidTipSet("weights are not equal".to_string())); } // check state_roots are equal - if headers[i].state_root != headers[0].state_root.clone() { + if headers[i].state_root() != headers[0].state_root() { return Err(Error::InvalidTipSet( "state_roots are not equal".to_string(), )); } // check epochs are equal - if headers[i].epoch != headers[0].epoch { + if headers[i].epoch() != headers[0].epoch() { return Err(Error::InvalidTipSet("epochs are not equal".to_string())); } // check message_receipts are equal - if headers[i].message_receipts != headers[0].message_receipts.clone() { + if headers[i].message_receipts() != headers[0].message_receipts() { return Err(Error::InvalidTipSet( "message_receipts are not equal".to_string(), )); } // check miner_addresses are distinct - if headers[i].miner_address == headers[0].miner_address.clone() { + if headers[i].miner_address() == headers[0].miner_address() { return Err(Error::InvalidTipSet( "miner_addresses are not distinct".to_string(), )); @@ -98,20 +100,16 @@ impl Tipset { } // push headers into vec for sorting sorted_headers.push(headers[i].clone()); - // push header cid into vec for unique check - cids.push(headers[i].clone().cid()); + // push header cid into vec for unique check (can be changed to hashset later) + cids.push(headers[i].cid().clone()); } // sort headers by ticket size // break ticket ties with the header CIDs, which are distinct - sorted_headers.sort_by_key(|header| { - let mut h = header.clone(); - (h.ticket.vrfproof.clone(), h.cid().to_bytes()) - }); + sorted_headers + .sort_by_key(|header| (header.ticket().vrfproof.clone(), header.cid().to_bytes())); - // TODO - // Have a check the ensures CIDs are distinct - // blocked by CBOR encoding + // TODO Have a check the ensures CIDs are distinct // return tipset where sorted headers have smallest ticket size is in the 0th index // and the distinct keys @@ -129,17 +127,17 @@ impl Tipset { if self.blocks.is_empty() { return Err(Error::NoBlocks); } - Ok(self.blocks[0].ticket.clone()) + Ok(self.blocks[0].ticket().clone()) } /// Returns the smallest timestamp of all blocks in the tipset fn min_timestamp(&self) -> Result { if self.blocks.is_empty() { return Err(Error::NoBlocks); } - let mut min = self.blocks[0].timestamp; + let mut min = self.blocks[0].timestamp(); for i in 1..self.blocks.len() { - if self.blocks[i].timestamp < min { - min = self.blocks[i].timestamp + if self.blocks[i].timestamp() < min { + min = self.blocks[i].timestamp() } } Ok(min) @@ -153,30 +151,28 @@ impl Tipset { self.blocks.is_empty() } /// Returns a key for the tipset. - pub fn key(&self) -> TipSetKeys { - self.key.clone() + pub fn key(&self) -> &TipSetKeys { + &self.key } /// Returns the CIDs of the parents of the blocks in the tipset - pub fn parents(&self) -> TipSetKeys { - self.blocks[0].parents.clone() + pub fn parents(&self) -> &TipSetKeys { + &self.blocks[0].parents() } /// Returns the tipset's calculated weight pub fn weight(&self) -> u64 { - self.blocks[0].weight + self.blocks[0].weight() } /// Returns the tipset's epoch - pub fn tip_epoch(&self) -> ChainEpoch { - self.blocks[0].epoch.clone() + pub fn tip_epoch(&self) -> &ChainEpoch { + self.blocks[0].epoch() } } #[cfg(test)] mod tests { use super::*; - use crate::block::TxMeta; use address::Address; use cid::Cid; - use clock::ChainEpoch; use crypto::VRFResult; const WEIGHT: u64 = 1; @@ -199,27 +195,21 @@ mod tests { // template_header defines a block header used in testing fn template_header(ticket_p: Vec, cid: Cid, timestamp: u64) -> BlockHeader { let cids = key_setup(); - BlockHeader { - parents: TipSetKeys { + let header = BlockHeader::builder() + .parents(TipSetKeys { cids: vec![cids[3].clone()], - }, - weight: WEIGHT, - epoch: ChainEpoch::new(1), - miner_address: Address::new_secp256k1(ticket_p.clone()).unwrap(), - messages: TxMeta { - bls_messages: cids[0].clone(), - secp_messages: cids[0].clone(), - }, - message_receipts: cids[0].clone(), - state_root: cids[0].clone(), - timestamp, - ticket: Ticket { + }) + .miner_address(Address::new_secp256k1(ticket_p.clone()).unwrap()) + .timestamp(timestamp) + .ticket(Ticket { vrfproof: VRFResult::new(ticket_p), - }, - bls_aggregate: vec![1, 2, 3], - cached_cid: cid, - cached_bytes: CACHED_BYTES.to_vec(), - } + }) + .weight(WEIGHT) + .cached_cid(cid) + .build() + .unwrap(); + + header } // header_setup returns a vec of block headers to be used for testing purposes @@ -278,7 +268,7 @@ mod tests { let tipset = setup(); let expected_value = template_key(b"the best test content out there"); assert_eq!( - Tipset::parents(&tipset), + *tipset.parents(), TipSetKeys { cids: vec!(expected_value) } @@ -288,7 +278,7 @@ mod tests { #[test] fn weight_test() { let tipset = setup(); - assert_eq!(Tipset::weight(&tipset), 1); + assert_eq!(tipset.weight(), WEIGHT); } #[test] @@ -296,6 +286,9 @@ mod tests { let tipset_keys = TipSetKeys { cids: key_setup().clone(), }; - assert_eq!(TipSetKeys::equals(&tipset_keys, tipset_keys.clone()), true); + let tipset_keys2 = TipSetKeys { + cids: key_setup().clone(), + }; + assert_eq!(tipset_keys.equals(&tipset_keys2), true); } } diff --git a/blockchain/chain/src/store/tip_index.rs b/blockchain/chain/src/store/tip_index.rs index 4f73f8ae0ed7..2970348cc0a0 100644 --- a/blockchain/chain/src/store/tip_index.rs +++ b/blockchain/chain/src/store/tip_index.rs @@ -91,15 +91,10 @@ impl TipIndex { #[cfg(test)] mod tests { use super::*; - use address::Address; - use blocks::{BlockHeader, Ticket, Tipset, TxMeta}; + use blocks::{BlockHeader, Ticket, Tipset}; use cid::Cid; - use clock::ChainEpoch; use crypto::VRFResult; - const WEIGHT: u64 = 1; - const CACHED_BYTES: [u8; 1] = [0]; - fn template_key(data: &[u8]) -> Cid { Cid::from_bytes_default(data).unwrap() } @@ -111,28 +106,16 @@ mod tests { // template_header defines a block header used in testing fn template_header(ticket_p: Vec, cid: Cid, timestamp: u64) -> BlockHeader { - let cids = key_setup(); - BlockHeader { - parents: TipSetKeys { - cids: vec![cids[0].clone()], - }, - weight: WEIGHT, - epoch: ChainEpoch::new(1), - miner_address: Address::new_secp256k1(ticket_p.clone()).unwrap(), - messages: TxMeta { - bls_messages: cids[0].clone(), - secp_messages: cids[0].clone(), - }, - message_receipts: cids[0].clone(), - state_root: cids[0].clone(), - timestamp, - ticket: Ticket { + let header = BlockHeader::builder() + .timestamp(timestamp) + .ticket(Ticket { vrfproof: VRFResult::new(ticket_p), - }, - bls_aggregate: vec![1, 2, 3], - cached_cid: cid, - cached_bytes: CACHED_BYTES.to_vec(), - } + }) + .cached_cid(cid) + .build() + .unwrap(); + + header } // header_setup returns a vec of block headers to be used for testing purposes @@ -150,8 +133,8 @@ mod tests { fn meta_setup() -> TipSetMetadata { let tip_set = setup(); TipSetMetadata { - tipset_state_root: tip_set.blocks()[0].state_root.clone(), - tipset_receipts_root: tip_set.blocks()[0].message_receipts.clone(), + tipset_state_root: tip_set.blocks()[0].state_root().clone(), + tipset_receipts_root: tip_set.blocks()[0].message_receipts().clone(), tipset: tip_set, } } @@ -179,7 +162,7 @@ mod tests { let meta = meta_setup(); let mut tip = TipIndex::new(); tip.put(&meta).unwrap(); - let result = tip.get_tipset(&meta.tipset.parents()).unwrap(); + let result = tip.get_tipset(meta.tipset.parents()).unwrap(); assert_eq!(result, meta.tipset); } @@ -188,9 +171,7 @@ mod tests { let meta = meta_setup(); let mut tip = TipIndex::new(); tip.put(&meta).unwrap(); - let result = tip - .get_tipset_receipts_root(&meta.tipset.parents()) - .unwrap(); + let result = tip.get_tipset_receipts_root(meta.tipset.parents()).unwrap(); assert_eq!(result, meta.tipset_state_root); } @@ -199,9 +180,7 @@ mod tests { let meta = meta_setup(); let mut tip = TipIndex::new(); tip.put(&meta).unwrap(); - let result = tip - .get_tipset_receipts_root(&meta.tipset.parents()) - .unwrap(); + let result = tip.get_tipset_receipts_root(meta.tipset.parents()).unwrap(); assert_eq!(result, meta.tipset_receipts_root); } @@ -210,7 +189,7 @@ mod tests { let meta = meta_setup(); let mut tip = TipIndex::new(); tip.put(&meta).unwrap(); - let result = tip.get_tipset(&meta.tipset.tip_epoch()).unwrap(); + let result = tip.get_tipset(&meta.tipset.tip_epoch().clone()).unwrap(); assert_eq!(result, meta.tipset); } @@ -219,7 +198,9 @@ mod tests { let meta = meta_setup(); let mut tip = TipIndex::new(); tip.put(&meta).unwrap(); - let result = tip.get_tipset_state_root(&meta.tipset.tip_epoch()).unwrap(); + let result = tip + .get_tipset_state_root(&meta.tipset.tip_epoch().clone()) + .unwrap(); assert_eq!(result, meta.tipset_state_root); } @@ -229,7 +210,7 @@ mod tests { let mut tip = TipIndex::new(); tip.put(&meta).unwrap(); let result = tip - .get_tipset_receipts_root(&meta.tipset.tip_epoch()) + .get_tipset_receipts_root(&meta.tipset.tip_epoch().clone()) .unwrap(); assert_eq!(result, meta.tipset_receipts_root); } diff --git a/blockchain/sync_manager/src/bucket.rs b/blockchain/sync_manager/src/bucket.rs index 377cd8300109..eaf94a8abbdb 100644 --- a/blockchain/sync_manager/src/bucket.rs +++ b/blockchain/sync_manager/src/bucket.rs @@ -72,22 +72,17 @@ impl<'a> SyncBucketSet<'a> { #[cfg(test)] mod tests { use super::*; - use address::Address; - use blocks::{BlockHeader, TipSetKeys}; + use blocks::BlockHeader; use cid::Cid; fn create_header(weight: u64, parent_bz: &[u8], cached_bytes: &[u8]) -> BlockHeader { - let x = TipSetKeys { - cids: vec![Cid::from_bytes_default(parent_bz).unwrap()], - }; - BlockHeader::builder() - .parents(x) - .cached_bytes(cached_bytes.to_vec()) // TODO change to however cached bytes are generated in future - .miner_address(Address::new_id(0).unwrap()) - .bls_aggregate(vec![]) + let header = BlockHeader::builder() .weight(weight) + .cached_bytes(cached_bytes.to_vec()) + .cached_cid(Cid::from_bytes_default(parent_bz).unwrap()) .build() - .unwrap() + .unwrap(); + header } #[test] @@ -121,14 +116,27 @@ mod tests { // Assert a tipset on non relating chain is put in another bucket let tipset2 = Tipset::new(vec![create_header(2, b"2", b"2")]).unwrap(); set.insert(&tipset2); - assert_eq!(set.buckets.len(), 2); + assert_eq!( + set.buckets.len(), + 2, + "Inserting seperate tipset should create new bucket" + ); assert_eq!(set.buckets[1].tips.len(), 1); // Assert a tipset connected to the first let tipset3 = Tipset::new(vec![create_header(3, b"1", b"1")]).unwrap(); + assert_eq!(tipset1.key(), tipset3.key()); set.insert(&tipset3); - assert_eq!(set.buckets.len(), 2); - assert_eq!(set.buckets[0].tips.len(), 2); + assert_eq!( + set.buckets.len(), + 2, + "Inserting into first chain should not create 3rd bucket" + ); + assert_eq!( + set.buckets[0].tips.len(), + 2, + "Should be 2 tipsets in bucket 0" + ); // Assert that tipsets that are already added are not added twice set.insert(&tipset1); diff --git a/blockchain/sync_manager/tests/manager_test.rs b/blockchain/sync_manager/tests/manager_test.rs index a1a257ba037c..bedcf1ab38f0 100644 --- a/blockchain/sync_manager/tests/manager_test.rs +++ b/blockchain/sync_manager/tests/manager_test.rs @@ -1,23 +1,18 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0 -use address::Address; -use blocks::{BlockHeader, TipSetKeys, Tipset}; +use blocks::{BlockHeader, Tipset}; use cid::Cid; use sync_manager::SyncManager; fn create_header(weight: u64, parent_bz: &[u8], cached_bytes: &[u8]) -> BlockHeader { - let x = TipSetKeys { - cids: vec![Cid::from_bytes_default(parent_bz).unwrap()], - }; - BlockHeader::builder() - .parents(x) - .cached_bytes(cached_bytes.to_vec()) // TODO change to however cached bytes are generated in future - .miner_address(Address::new_id(0).unwrap()) - .bls_aggregate(vec![]) + let header = BlockHeader::builder() .weight(weight) + .cached_bytes(cached_bytes.to_vec()) + .cached_cid(Cid::from_bytes_default(parent_bz).unwrap()) .build() - .unwrap() + .unwrap(); + header } #[test] diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 95e3f4416e46..228ffdd8d2c0 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -12,6 +12,7 @@ address = {path = "../vm/address"} encoding = {path = "../encoding"} libsecp256k1 = "0.2.1" bls-signatures = "0.2.0" +serde = { version = "1.0", features = ["derive"] } [dev-dependencies] rand = "0.4" diff --git a/crypto/src/vrf.rs b/crypto/src/vrf.rs index 9e76398960e9..65bc2bc836d8 100644 --- a/crypto/src/vrf.rs +++ b/crypto/src/vrf.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::signature::{verify_bls_sig, BLS_SIG_LEN}; -use bls_signatures::{Serialize, Signature}; +use bls_signatures::{Serialize as BlsSerialize, Signature}; +use serde::{Deserialize, Serialize}; pub struct VRFPublicKey(Vec); @@ -14,9 +15,12 @@ impl VRFPublicKey { } /// The output from running a VRF -#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Default, Serialize, Deserialize)] pub struct VRFResult(Vec); +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl VRFResult { /// Creates a VRFResult from a raw vector pub fn new(output: Vec) -> Self { @@ -43,6 +47,9 @@ impl VRFResult { } } +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + #[cfg(test)] mod tests { use super::*; diff --git a/ipld/cid/src/error.rs b/ipld/cid/src/error.rs index b8319633bca0..3eef7de10cc5 100644 --- a/ipld/cid/src/error.rs +++ b/ipld/cid/src/error.rs @@ -1,22 +1,27 @@ // Copyright 2020 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0 +use encoding::Error as EncodingError; use multibase; use multihash; use std::{error, fmt, io}; /// Error types -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Debug)] pub enum Error { UnknownCodec, InputTooShort, ParsingError, InvalidCidVersion, + Other(String), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(error::Error::description(self)) + match self { + Error::Other(err) => write!(f, "Other cid Error: {}", err.clone()), + _ => f.write_str(error::Error::description(self)), + } } } @@ -24,11 +29,12 @@ impl error::Error for Error { fn description(&self) -> &str { use self::Error::*; - match *self { + match self { UnknownCodec => "Unknown codec", InputTooShort => "Input too short", ParsingError => "Failed to parse multihash", InvalidCidVersion => "Unrecognized CID version", + Other(_) => "Other Cid Error", } } } @@ -68,3 +74,9 @@ impl From for fmt::Error { fmt::Error {} } } + +impl From for Error { + fn from(err: EncodingError) -> Error { + Error::Other(err.to_string()) + } +} diff --git a/node/clock/Cargo.toml b/node/clock/Cargo.toml index 4c45a076b2ea..52ea89a3a903 100644 --- a/node/clock/Cargo.toml +++ b/node/clock/Cargo.toml @@ -6,3 +6,4 @@ edition = "2018" [dependencies] chrono = "0.4.9" +serde = { version = "1.0", features = ["derive"] } diff --git a/node/clock/src/lib.rs b/node/clock/src/lib.rs index fbd1c798ab1c..be92ce891e02 100644 --- a/node/clock/src/lib.rs +++ b/node/clock/src/lib.rs @@ -2,14 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc}; +use serde::{Deserialize, Serialize}; const _ISO_FORMAT: &str = "%FT%X.%.9F"; const EPOCH_DURATION: i32 = 15; -#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] /// An epoch represents a single valid state in the blockchain pub struct ChainEpoch(i64); +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + /// ChainEpochClock is used by the system node to assume weak clock synchrony amongst the other /// systems. pub struct ChainEpochClock { diff --git a/vm/actor/src/lib.rs b/vm/actor/src/lib.rs index 5868cac2c46c..ce28c8fc745f 100644 --- a/vm/actor/src/lib.rs +++ b/vm/actor/src/lib.rs @@ -16,6 +16,9 @@ use serde::{Deserialize, Serialize}; #[derive(PartialEq, Eq, Copy, Clone, Debug, Default, Serialize, Deserialize)] pub struct ActorID(u64); +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl Cbor for ActorID {} /// State of all actor implementations diff --git a/vm/address/src/lib.rs b/vm/address/src/lib.rs index 5843b09844e5..e98eebdb150b 100644 --- a/vm/address/src/lib.rs +++ b/vm/address/src/lib.rs @@ -37,7 +37,7 @@ const NETWORK_DEFAULT: Network = Network::Testnet; /// Address is the struct that defines the protocol and data payload conversion from either /// a public key or value -#[derive(PartialEq, Eq, Clone, Debug, Hash)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, Default)] pub struct Address { network: Network, protocol: Protocol, diff --git a/vm/address/src/network.rs b/vm/address/src/network.rs index 640a1be4d79e..cdeff0bf546e 100644 --- a/vm/address/src/network.rs +++ b/vm/address/src/network.rs @@ -10,6 +10,12 @@ pub enum Network { Testnet, } +impl Default for Network { + fn default() -> Self { + Network::Testnet + } +} + impl Network { /// to_prefix is used to convert the network into a string /// used when converting address to string diff --git a/vm/address/src/protocol.rs b/vm/address/src/protocol.rs index c8ed4096bfc4..ba9ba902cb43 100644 --- a/vm/address/src/protocol.rs +++ b/vm/address/src/protocol.rs @@ -18,6 +18,12 @@ pub enum Protocol { BLS = 3, } +impl Default for Protocol { + fn default() -> Self { + Protocol::ID + } +} + impl Protocol { /// from_byte allows referencing back to Protocol from encoded byte pub(super) fn from_byte(b: u8) -> Option { diff --git a/vm/message/src/signed_message.rs b/vm/message/src/signed_message.rs index 1db8003552c5..03170edb0859 100644 --- a/vm/message/src/signed_message.rs +++ b/vm/message/src/signed_message.rs @@ -16,6 +16,9 @@ pub struct SignedMessage { signature: Signature, } +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl SignedMessage { pub fn new(msg: &UnsignedMessage, s: &impl Signer) -> Result { let bz = msg.marshal_cbor()?; diff --git a/vm/message/src/unsigned_message.rs b/vm/message/src/unsigned_message.rs index 9ea09da1e0fd..432a53f3c63f 100644 --- a/vm/message/src/unsigned_message.rs +++ b/vm/message/src/unsigned_message.rs @@ -60,6 +60,9 @@ pub struct UnsignedMessage { gas_limit: BigUint, } +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl UnsignedMessage { pub fn builder() -> MessageBuilder { MessageBuilder::default() diff --git a/vm/src/method.rs b/vm/src/method.rs index fffcd11e0efe..7d5993a1bf29 100644 --- a/vm/src/method.rs +++ b/vm/src/method.rs @@ -9,6 +9,9 @@ use std::ops::{Deref, DerefMut}; #[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct MethodNum(i32); // TODO: add constraints to this +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl MethodNum { /// Constructor for new MethodNum pub fn new(num: i32) -> Self { @@ -39,6 +42,9 @@ pub struct Serialized { bytes: Vec, } +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl Deref for Serialized { type Target = Vec; fn deref(&self) -> &Self::Target { @@ -71,6 +77,9 @@ pub struct MethodParams { params: Vec, } +// TODO verify format or implement custom serialize/deserialize function (if necessary): +// https://github.com/ChainSafe/ferret/issues/143 + impl Deref for MethodParams { type Target = Vec; fn deref(&self) -> &Self::Target {