From 4aca649237d92f5f9daff0092bd8f6a3ef59f09c Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 25 Nov 2019 19:51:08 -0600 Subject: [PATCH 01/14] Blockchain Interfaces WIP * Added multihash package to retrieve blake2b hash * Added sort_key method for Ticket struct * Added equals method for TipSetKeys struct type * WIP for Tipset new fn * WIP cid method for block type which would return CID of block --- blockchain/Cargo.toml | 1 + blockchain/src/blocks/block.rs | 31 +++++++++++++++++++++++++-- blockchain/src/blocks/ticket.rs | 9 ++++++++ blockchain/src/blocks/tipset.rs | 37 ++++++++++++++++++++++++++++++++- 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/blockchain/Cargo.toml b/blockchain/Cargo.toml index 9fa3605964bd..de4b70b7fcb0 100644 --- a/blockchain/Cargo.toml +++ b/blockchain/Cargo.toml @@ -9,3 +9,4 @@ edition = "2018" [dependencies] vm = {path = "../vm"} cid = "0.3.1" +multihash = "*" diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index 41215921e7e9..7e52e9f57c5d 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -2,10 +2,16 @@ use super::ticket::{Ticket, VRFProofIndex}; use super::TipSetKeys; -use cid::Cid; +extern crate multihash; +use multihash::{encode, decode, Hash}; +use cid::{Cid, Version, Codec, Error, Prefix}; use vm::address::Address; -/// BlockHeader defines header of a block in the Filecoin blockchain +// DefaultHashFunction represents the default hashing function to use +const DEFAULT_HASH_FUNCTION: Hash = Hash::Blake2b; + +/// BlockHeader defines header of a block in the Filecoin blockchain#[derive(Clone)] +#[derive(Clone)] pub struct BlockHeader { /// CHAIN LINKING /// @@ -47,6 +53,12 @@ pub struct BlockHeader { // // block_sig filCrypto Signature // BLSAggregateSig + + /// CACHE + /// + cachedCid: cid::Cid, + + cachedBytes: Vec, } /// Block defines a full block @@ -55,3 +67,18 @@ pub struct Block { // Messages // Receipts } + +impl Block { + // cid returns the content id of this block + fn cid(&self) { + + let mut c = Prefix { + version: Version::V0, + codec: Codec::DagProtobuf, + mh_type: DEFAULT_HASH_FUNCTION, + mh_len: 0, + }; + let res = c.as_bytes(); + let resp = Prefix::new_from_bytes(&res); + } +} diff --git a/blockchain/src/blocks/ticket.rs b/blockchain/src/blocks/ticket.rs index 4bf1d6257a3d..2bbea60320ff 100644 --- a/blockchain/src/blocks/ticket.rs +++ b/blockchain/src/blocks/ticket.rs @@ -1,3 +1,6 @@ +#![allow(unused_variables)] +#![allow(dead_code)] + /// VRFProofIndex is the proof output from running a VRF pub type VRFProofIndex = Vec; @@ -9,3 +12,9 @@ pub struct Ticket { /// A proof output by running a VRF on the VDFResult of the parent ticket pub vrfproof: VRFProofIndex, } + +impl Ticket { + fn sort_key(&self) -> Vec { + self.vrfproof.clone() + } +} \ No newline at end of file diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 03a1b6d805a8..82e5ffbfc1b9 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -18,7 +18,7 @@ pub struct Tipset { /// TipSetKeys is 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)] +#[derive(PartialEq, Clone)] pub struct TipSetKeys { pub cids: Vec, } @@ -36,6 +36,24 @@ impl Tipset { if headers.is_empty() { return Err(Error::NoBlocks); } + + let mut sorted_headers = Vec::new(); + //let mut sorted_cids = Vec::new(); + + for i in 1..headers.len() { + if headers[i].height != headers[0].height.clone() { + return Err(Error::UndefinedTipSet) + } + if !headers[i].parents.equals(headers[0].parents.clone()) { + return Err(Error::UndefinedTipSet) + } + if headers[i].weight != headers[0].weight.clone() { + return Err(Error::UndefinedTipSet) + } + sorted_headers[i] = headers[i].clone(); + //sorted_cids[i] = headers[i].CIDs() + } + Tipset::new(headers) } /// min_ticket returns the smallest ticket of all blocks in the tipset @@ -83,3 +101,20 @@ impl Tipset { self.blocks[0].weight } } + +impl TipSetKeys { + /// equals checks whether the set contains exactly the same CIDs as another. + fn equals(&self, key: TipSetKeys) -> bool { + if self.cids.len() != key.cids.len() { + return false + } + let mut i = 0; + while i > key.cids.len() { + i += 1; + if self.cids[i] == key.cids[i] { + return false + } + } + return true + } +} From c8c993485e939a89d9ab202d5315b3938fe9b23b Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Tue, 26 Nov 2019 17:24:46 -0600 Subject: [PATCH 02/14] New Tipset fn WIP * Added new conditional checks as per spec update * WIP sorted and compare logic for ticket size * Fix cid method for blockHeader * WIP new fn tests --- blockchain/src/blocks/block.rs | 48 +++++------ blockchain/src/blocks/ticket.rs | 4 +- blockchain/src/blocks/tipset.rs | 145 ++++++++++++++++++++++++++------ 3 files changed, 145 insertions(+), 52 deletions(-) diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index 7e52e9f57c5d..b6c277d3e7d8 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -3,12 +3,12 @@ use super::ticket::{Ticket, VRFProofIndex}; use super::TipSetKeys; extern crate multihash; -use multihash::{encode, decode, Hash}; -use cid::{Cid, Version, Codec, Error, Prefix}; +use cid::{Cid, Codec, Prefix, Version}; +use multihash::Hash; use vm::address::Address; // DefaultHashFunction represents the default hashing function to use -const DEFAULT_HASH_FUNCTION: Hash = Hash::Blake2b; +const DEFAULT_HASH_FUNCTION: Hash = Hash::Keccak256; /// BlockHeader defines header of a block in the Filecoin blockchain#[derive(Clone)] #[derive(Clone)] @@ -22,23 +22,23 @@ pub struct BlockHeader { /// weight is the aggregate chain weight of the parent set pub weight: u64, /// epoch is the period in which a new block is generated. There may be multiple rounds in an epoch - epoch: u64, + pub epoch: u64, /// height is the block height pub height: u64, /// MINER INFO /// /// miner_address is the address of the miner actor that mined this block - miner_address: Address, + pub miner_address: Address, /// STATE /// /// messages is the Cid of the root of an array of Messages - messages: Cid, + pub messages: Cid, /// message_receipts is the Cid of the root of an array of MessageReceipts - message_receipts: Cid, + pub message_receipts: Cid, /// state_root is a cid pointer to the state tree after application of the transactions state transitions - state_root: Cid, + pub state_root: Cid, /// CONSENSUS /// @@ -48,17 +48,16 @@ pub struct BlockHeader { pub ticket: Ticket, /// election_proof is the "scratched ticket" proving that this block won /// an election - election_proof: VRFProofIndex, + pub election_proof: VRFProofIndex, // SIGNATURES // // block_sig filCrypto Signature // BLSAggregateSig - /// CACHE - /// - cachedCid: cid::Cid, + /// + pub cached_cid: cid::Cid, - cachedBytes: Vec, + pub cached_bytes: u8, } /// Block defines a full block @@ -68,17 +67,18 @@ pub struct Block { // Receipts } -impl Block { +impl BlockHeader { // cid returns the content id of this block - fn cid(&self) { - - let mut c = Prefix { - version: Version::V0, - codec: Codec::DagProtobuf, - mh_type: DEFAULT_HASH_FUNCTION, - mh_len: 0, - }; - let res = c.as_bytes(); - let resp = Prefix::new_from_bytes(&res); + pub fn cid(mut self) -> cid::Cid { + let c = Prefix { + version: Version::V0, + codec: Codec::DagProtobuf, + mh_type: DEFAULT_HASH_FUNCTION, + mh_len: 0, + }; + let new_cid = cid::Cid::new_from_prefix(&c, &[self.cached_bytes]); + self.cached_cid = new_cid; + println!("CID:: {}", self.cached_cid); + self.cached_cid } } diff --git a/blockchain/src/blocks/ticket.rs b/blockchain/src/blocks/ticket.rs index 2bbea60320ff..84b89614423b 100644 --- a/blockchain/src/blocks/ticket.rs +++ b/blockchain/src/blocks/ticket.rs @@ -14,7 +14,7 @@ pub struct Ticket { } impl Ticket { - fn sort_key(&self) -> Vec { + pub fn sort_key(&self) -> Vec { self.vrfproof.clone() } -} \ No newline at end of file +} diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 82e5ffbfc1b9..aeb8b8913106 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -1,12 +1,11 @@ #![allow(unused_variables)] #![allow(dead_code)] -use cid::Cid; - use super::block::BlockHeader; -use super::ticket::Ticket; - use super::errors::Error; +use super::ticket::Ticket; +use cid::Cid; +use vm::address::Address; /// TipSet is an immutable set of blocks at the same height with the same parent set /// Blocks in a tipset are canonically ordered by ticket size @@ -27,35 +26,73 @@ impl Tipset { /// new builds a new TipSet from a collection of blocks //// The blocks must be distinct (different CIDs), have the same height, and same parent set fn new(headers: Vec) -> Result { - // TODO - // check length of blocks is not 0 - // loop through headers to ensure blocks have same height and parent set - // sort headers by ticket size - // check and assign uniqueness of key - // return TipSet type if headers.is_empty() { return Err(Error::NoBlocks); } - - let mut sorted_headers = Vec::new(); - //let mut sorted_cids = Vec::new(); - for i in 1..headers.len() { - if headers[i].height != headers[0].height.clone() { - return Err(Error::UndefinedTipSet) + let mut sorted_headers = Vec::new(); + let mut sorted_cids = Vec::new(); + let mut i = 0; + while i < headers.len() { + if headers[i].height != headers[0].height { + return Err(Error::UndefinedTipSet); } if !headers[i].parents.equals(headers[0].parents.clone()) { - return Err(Error::UndefinedTipSet) + return Err(Error::UndefinedTipSet); + } + if headers[i].weight != headers[0].weight { + return Err(Error::UndefinedTipSet); + } + if headers[i].state_root != headers[0].state_root.clone() { + return Err(Error::UndefinedTipSet); + } + if headers[i].epoch != headers[0].epoch { + return Err(Error::UndefinedTipSet); } - if headers[i].weight != headers[0].weight.clone() { - return Err(Error::UndefinedTipSet) + if headers[i].message_receipts != headers[0].message_receipts.clone() { + return Err(Error::UndefinedTipSet); } - sorted_headers[i] = headers[i].clone(); - //sorted_cids[i] = headers[i].CIDs() + sorted_headers.push(headers[i].clone()); + sorted_cids.push(headers[i].clone().cid()); + i += 1; } - - Tipset::new(headers) + // sort headers by ticket + // + // GO FILE COIN LOGIC BELOW + // + // sort.Slice(sorted, func(i, j int) bool { + // cmp := bytes.Compare(sorted[i].Ticket.SortKey(), sorted[j].Ticket.SortKey()) + // if cmp == 0 { + // // Break ticket ties with the block CIDs, which are distinct. + // cmp = bytes.Compare(sorted[i].Cid().Bytes(), sorted[j].Cid().Bytes()) + // } + // return cmp < 0 + // }) + + // sort headers by ticket size + // if tie; Break ticket ties with the block CIDs, which are distinct + sorted_headers.sort_by(|a, b| { + a.ticket + .sort_key() + .partial_cmp(&b.ticket.sort_key()) + .unwrap() + }); + + // INTERIM TO SATISFY STRUCT + let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" + .parse() + .unwrap(); + let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" + .parse() + .unwrap(); + let arr = vec![cid1.clone(), cid2.clone()]; + + Ok(Self { + blocks: sorted_headers, + key: TipSetKeys { cids: arr }, + }) } + /// min_ticket returns the smallest ticket of all blocks in the tipset fn min_ticket(&self) -> Result<(Ticket), Error> { if self.blocks.is_empty() { @@ -106,15 +143,71 @@ impl TipSetKeys { /// equals checks whether the set contains exactly the same CIDs as another. fn equals(&self, key: TipSetKeys) -> bool { if self.cids.len() != key.cids.len() { - return false + return false; } let mut i = 0; while i > key.cids.len() { i += 1; if self.cids[i] == key.cids[i] { - return false + return false; } } - return true + true } } + +fn test_header() -> Vec { + let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" + .parse() + .unwrap(); + let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" + .parse() + .unwrap(); + + let data: Vec = vec![1, 4, 3, 6, 7, 1, 2]; + let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3]; + let new_addr = Address::new_secp256k1(data.clone()).unwrap(); + let arr = vec![cid1.clone(), cid2.clone()]; + + let h = vec![ + BlockHeader { + parents: TipSetKeys { cids: arr.clone() }, + weight: 0, + epoch: 1, + height: 1, + miner_address: new_addr.clone(), + messages: cid1.clone(), + message_receipts: cid1.clone(), + state_root: cid1.clone(), + timestamp: 0, + ticket: Ticket { + vrfproof: data.clone(), + }, + election_proof: data.clone(), + cached_cid: cid1.clone(), + cached_bytes: 0, + }, + BlockHeader { + parents: TipSetKeys { cids: arr }, + weight: 0, + epoch: 1, + height: 1, + miner_address: new_addr, + messages: cid1.clone(), + message_receipts: cid1.clone(), + state_root: cid1.clone(), + timestamp: 0, + ticket: Ticket { vrfproof: data2 }, + election_proof: data.clone(), + cached_cid: cid1.clone(), + cached_bytes: 0, + }, + ]; + h +} + +#[test] +fn new_test() { + let headers = test_header(); + let result = Tipset::new(headers); +} From dc60488508ea7fc4d03a0066d90f6c97379aa500 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Wed, 27 Nov 2019 18:14:33 -0600 Subject: [PATCH 03/14] Added unit tests * Added unit tests for tipset methods --- blockchain/src/blocks/block.rs | 4 +- blockchain/src/blocks/mod.rs | 6 +- blockchain/src/blocks/tipset.rs | 99 +++++++++++++++++++++++++++++---- 3 files changed, 94 insertions(+), 15 deletions(-) diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index b6c277d3e7d8..6785c6593bd9 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -8,10 +8,11 @@ use multihash::Hash; use vm::address::Address; // DefaultHashFunction represents the default hashing function to use +// SHOULD BE BLAKE2B const DEFAULT_HASH_FUNCTION: Hash = Hash::Keccak256; /// BlockHeader defines header of a block in the Filecoin blockchain#[derive(Clone)] -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BlockHeader { /// CHAIN LINKING /// @@ -78,7 +79,6 @@ impl BlockHeader { }; let new_cid = cid::Cid::new_from_prefix(&c, &[self.cached_bytes]); self.cached_cid = new_cid; - println!("CID:: {}", self.cached_cid); self.cached_cid } } diff --git a/blockchain/src/blocks/mod.rs b/blockchain/src/blocks/mod.rs index f8a331aa0306..193aefc0f0c7 100644 --- a/blockchain/src/blocks/mod.rs +++ b/blockchain/src/blocks/mod.rs @@ -1,7 +1,7 @@ -mod block; +pub mod block; mod errors; -mod ticket; -mod tipset; +pub mod ticket; +pub mod tipset; pub use block::*; pub use errors::*; diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index aeb8b8913106..ebe98d677bda 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -24,8 +24,8 @@ pub struct TipSetKeys { impl Tipset { /// new builds a new TipSet from a collection of blocks - //// The blocks must be distinct (different CIDs), have the same height, and same parent set - fn new(headers: Vec) -> Result { + /// The blocks must be distinct (different CIDs), have the same height, and same parent set + pub fn new(headers: Vec) -> Result { if headers.is_empty() { return Err(Error::NoBlocks); } @@ -169,7 +169,7 @@ fn test_header() -> Vec { let new_addr = Address::new_secp256k1(data.clone()).unwrap(); let arr = vec![cid1.clone(), cid2.clone()]; - let h = vec![ + let headers = vec![ BlockHeader { parents: TipSetKeys { cids: arr.clone() }, weight: 0, @@ -196,18 +196,97 @@ fn test_header() -> Vec { messages: cid1.clone(), message_receipts: cid1.clone(), state_root: cid1.clone(), - timestamp: 0, + timestamp: 1, ticket: Ticket { vrfproof: data2 }, election_proof: data.clone(), - cached_cid: cid1.clone(), + cached_cid: cid2.clone(), cached_bytes: 0, }, ]; - h + headers } -#[test] -fn new_test() { - let headers = test_header(); - let result = Tipset::new(headers); +#[cfg(test)] +mod tests { + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + + fn setup() -> Result<(Tipset), Error> { + let headers = test_header(); + let tipset = Tipset::new(headers.clone())?; + Ok(tipset) + } + + #[test] + fn new_test() { + let headers = test_header(); + assert!(Tipset::new(headers).is_ok(), "result is okay!"); + } + + #[test] + fn min_ticket_test() -> Result<(), Error> { + let tipset = setup()?; + let min = Tipset::min_ticket(&tipset)?; + assert_eq!(min.vrfproof, tipset.blocks[0].ticket.vrfproof); + Ok(()) + } + + #[test] + fn min_timestamp_test() -> Result<(), Error> { + let tipset = setup()?; + let min_time = Tipset::min_timestamp(&tipset)?; + assert_eq!(min_time, tipset.blocks[1].timestamp); + Ok(()) + } + + #[test] + fn len_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::len(&tipset), 2); + Ok(()) + } + + #[test] + fn is_empty_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::is_empty(&tipset), false); + Ok(()) + } + + #[test] + fn key_test() -> Result<(), Error> { + let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" + .parse() + .unwrap(); + let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" + .parse() + .unwrap(); + let arr = vec![cid1.clone(), cid2.clone()]; + let k = TipSetKeys { cids: arr }; + let headers = test_header(); + let tipset = Tipset::new(headers.clone())?; + assert_eq!(Tipset::key(&tipset), k); + Ok(()) + } + + #[test] + fn height_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::height(&tipset), tipset.blocks[1].height); + Ok(()) + } + + #[test] + fn parents_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::parents(&tipset), tipset.blocks[1].parents); + Ok(()) + } + + #[test] + fn weight_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::weight(&tipset), tipset.blocks[1].weight); + Ok(()) + } } From 0a5b9a8e86097195d6bf1ca77cfbab43604ccb2c Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Thu, 28 Nov 2019 14:54:59 -0600 Subject: [PATCH 04/14] WIP tipset new fn --- blockchain/Cargo.toml | 2 +- blockchain/src/blocks/ticket.rs | 13 ++- blockchain/src/blocks/tipset.rs | 173 ++++++++++++++++++-------------- vm/src/address/mod.rs | 2 +- vm/src/address/protocol.rs | 2 +- 5 files changed, 110 insertions(+), 82 deletions(-) diff --git a/blockchain/Cargo.toml b/blockchain/Cargo.toml index de4b70b7fcb0..94c9da509de7 100644 --- a/blockchain/Cargo.toml +++ b/blockchain/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" [dependencies] vm = {path = "../vm"} cid = "0.3.1" -multihash = "*" +multihash = "*" \ No newline at end of file diff --git a/blockchain/src/blocks/ticket.rs b/blockchain/src/blocks/ticket.rs index 84b89614423b..e6b06bdab09a 100644 --- a/blockchain/src/blocks/ticket.rs +++ b/blockchain/src/blocks/ticket.rs @@ -1,5 +1,6 @@ #![allow(unused_variables)] #![allow(dead_code)] +use std::cmp::Ordering; /// VRFProofIndex is the proof output from running a VRF pub type VRFProofIndex = Vec; @@ -7,7 +8,7 @@ pub type VRFProofIndex = Vec; /// 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)] +#[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct Ticket { /// A proof output by running a VRF on the VDFResult of the parent ticket pub vrfproof: VRFProofIndex, @@ -18,3 +19,13 @@ impl Ticket { self.vrfproof.clone() } } + +impl Ord for Ticket { + fn cmp(&self, other: &Self) -> Ordering { + let val = self.sort_key().cmp(&other.sort_key()); + println!("ord {:?}", val); + return val + // check if equal or return ordering + // return comparison of cid bytes + } +} \ No newline at end of file diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index ebe98d677bda..769b3feb394a 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -4,8 +4,7 @@ use super::block::BlockHeader; use super::errors::Error; use super::ticket::Ticket; -use cid::Cid; -use vm::address::Address; +use cid::{Cid, Codec, Version}; /// TipSet is an immutable set of blocks at the same height with the same parent set /// Blocks in a tipset are canonically ordered by ticket size @@ -17,7 +16,7 @@ pub struct Tipset { /// TipSetKeys is 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(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct TipSetKeys { pub cids: Vec, } @@ -68,28 +67,33 @@ impl Tipset { // } // return cmp < 0 // }) + + println!("sorted headers -> {:?} \n \n", sorted_headers[0].ticket.sort_key()); // sort headers by ticket size // if tie; Break ticket ties with the block CIDs, which are distinct - sorted_headers.sort_by(|a, b| { + let sorted = sorted_headers.sort_by(|a, b| { a.ticket .sort_key() - .partial_cmp(&b.ticket.sort_key()) - .unwrap() + .cmp(&b.ticket.sort_key()) }); + + println!("sorted headers after sort -> {:?}", sorted); // INTERIM TO SATISFY STRUCT - let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" - .parse() - .unwrap(); - let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" - .parse() - .unwrap(); - let arr = vec![cid1.clone(), cid2.clone()]; + let data = b"awesome test content"; + let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); + + let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); + let prefix = cid.prefix(); + + let cid2 = Cid::new_from_prefix(&prefix, data); Ok(Self { blocks: sorted_headers, - key: TipSetKeys { cids: arr }, + key: TipSetKeys { + cids: vec![cid.clone(), cid2.clone()], + }, }) } @@ -156,70 +160,80 @@ impl TipSetKeys { } } -fn test_header() -> Vec { - let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" - .parse() - .unwrap(); - let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" - .parse() - .unwrap(); - - let data: Vec = vec![1, 4, 3, 6, 7, 1, 2]; - let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3]; - let new_addr = Address::new_secp256k1(data.clone()).unwrap(); - let arr = vec![cid1.clone(), cid2.clone()]; - - let headers = vec![ - BlockHeader { - parents: TipSetKeys { cids: arr.clone() }, - weight: 0, - epoch: 1, - height: 1, - miner_address: new_addr.clone(), - messages: cid1.clone(), - message_receipts: cid1.clone(), - state_root: cid1.clone(), - timestamp: 0, - ticket: Ticket { - vrfproof: data.clone(), - }, - election_proof: data.clone(), - cached_cid: cid1.clone(), - cached_bytes: 0, - }, - BlockHeader { - parents: TipSetKeys { cids: arr }, - weight: 0, - epoch: 1, - height: 1, - miner_address: new_addr, - messages: cid1.clone(), - message_receipts: cid1.clone(), - state_root: cid1.clone(), - timestamp: 1, - ticket: Ticket { vrfproof: data2 }, - election_proof: data.clone(), - cached_cid: cid2.clone(), - cached_bytes: 0, - }, - ]; - headers -} - #[cfg(test)] mod tests { - // Note this useful idiom: importing names from outer (for mod tests) scope. use super::*; + use vm::address::Address; + + const WEIGHT: u64 = 0; + const EPOCH: u64 = 1; + const HEIGHT: u64 = 1; + const CACHED_BYTES: u8 = 0; + + fn key_setup() -> Vec { + let data = b"awesome test content"; + let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); + + let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); + let prefix = cid.prefix(); + + let cid2 = Cid::new_from_prefix(&prefix, data); + + return vec![cid.clone(), cid2.clone()]; + } + + fn header_setup() -> Vec { + let data: Vec = vec![1, 4, 3, 6, 7, 1, 2]; + let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3]; + let new_addr = Address::new_secp256k1(data.clone()).unwrap(); + + return vec![ + BlockHeader { + parents: TipSetKeys { + cids: key_setup().clone(), + }, + weight: WEIGHT, + epoch: EPOCH, + height: HEIGHT, + miner_address: new_addr.clone(), + messages: key_setup()[0].clone(), + message_receipts: key_setup()[0].clone(), + state_root: key_setup()[0].clone(), + timestamp: 0, + ticket: Ticket { + vrfproof: data2.clone(), + }, + election_proof: data.clone(), + cached_cid: key_setup()[0].clone(), + cached_bytes: CACHED_BYTES, + }, + BlockHeader { + parents: TipSetKeys { cids: key_setup() }, + weight: WEIGHT, + epoch: EPOCH, + height: HEIGHT, + miner_address: new_addr, + messages: key_setup()[0].clone(), + message_receipts: key_setup()[0].clone(), + state_root: key_setup()[0].clone(), + timestamp: 1, + ticket: Ticket { vrfproof: data.clone() }, + election_proof: data.clone(), + cached_cid: key_setup()[1].clone(), + cached_bytes: CACHED_BYTES, + }, + ]; + } fn setup() -> Result<(Tipset), Error> { - let headers = test_header(); + let headers = header_setup(); let tipset = Tipset::new(headers.clone())?; Ok(tipset) } #[test] fn new_test() { - let headers = test_header(); + let headers = header_setup(); assert!(Tipset::new(headers).is_ok(), "result is okay!"); } @@ -235,7 +249,7 @@ mod tests { fn min_timestamp_test() -> Result<(), Error> { let tipset = setup()?; let min_time = Tipset::min_timestamp(&tipset)?; - assert_eq!(min_time, tipset.blocks[1].timestamp); + assert_eq!(min_time, tipset.blocks[0].timestamp); Ok(()) } @@ -255,17 +269,12 @@ mod tests { #[test] fn key_test() -> Result<(), Error> { - let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" - .parse() - .unwrap(); - let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" - .parse() - .unwrap(); - let arr = vec![cid1.clone(), cid2.clone()]; - let k = TipSetKeys { cids: arr }; - let headers = test_header(); + let keys = TipSetKeys { + cids: key_setup().clone(), + }; + let headers = header_setup(); let tipset = Tipset::new(headers.clone())?; - assert_eq!(Tipset::key(&tipset), k); + assert_eq!(Tipset::key(&tipset), keys); Ok(()) } @@ -289,4 +298,12 @@ mod tests { assert_eq!(Tipset::weight(&tipset), tipset.blocks[1].weight); Ok(()) } + + #[test] + fn equals_test() { + let tipset_keys = TipSetKeys { + cids: key_setup().clone(), + }; + assert!(TipSetKeys::equals(&tipset_keys, tipset_keys.clone())); + } } diff --git a/vm/src/address/mod.rs b/vm/src/address/mod.rs index 8da90fb6d332..bd8018ec4666 100644 --- a/vm/src/address/mod.rs +++ b/vm/src/address/mod.rs @@ -26,7 +26,7 @@ const TESTNET_PREFIX: &str = "t"; /// Address is the struct that defines the protocol and data payload conversion from either /// a public key or value -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct Address { protocol: Protocol, payload: Vec, diff --git a/vm/src/address/protocol.rs b/vm/src/address/protocol.rs index fb323b74bf9c..160ea74e9336 100644 --- a/vm/src/address/protocol.rs +++ b/vm/src/address/protocol.rs @@ -3,7 +3,7 @@ use num_traits::FromPrimitive; use std::fmt; /// Protocol defines the addressing protocol used to derive data to an address -#[derive(PartialEq, Copy, Clone, FromPrimitive)] +#[derive(PartialEq, Copy, Clone, FromPrimitive, Debug)] pub enum Protocol { // ID protocol addressing ID = 0, From e09cf5a4a5d1865d470db4f15756a7c9adf5d3be Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Wed, 27 Nov 2019 18:14:33 -0600 Subject: [PATCH 05/14] Added unit tests * Added unit tests for tipset methods --- blockchain/Cargo.toml | 2 +- blockchain/src/blocks/block.rs | 4 +- blockchain/src/blocks/mod.rs | 6 +- blockchain/src/blocks/ticket.rs | 13 +- blockchain/src/blocks/tipset.rs | 232 ++++++++++++++++++++++---------- vm/src/address/mod.rs | 2 +- vm/src/address/protocol.rs | 2 +- 7 files changed, 184 insertions(+), 77 deletions(-) diff --git a/blockchain/Cargo.toml b/blockchain/Cargo.toml index de4b70b7fcb0..94c9da509de7 100644 --- a/blockchain/Cargo.toml +++ b/blockchain/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" [dependencies] vm = {path = "../vm"} cid = "0.3.1" -multihash = "*" +multihash = "*" \ No newline at end of file diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index b6c277d3e7d8..6785c6593bd9 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -8,10 +8,11 @@ use multihash::Hash; use vm::address::Address; // DefaultHashFunction represents the default hashing function to use +// SHOULD BE BLAKE2B const DEFAULT_HASH_FUNCTION: Hash = Hash::Keccak256; /// BlockHeader defines header of a block in the Filecoin blockchain#[derive(Clone)] -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BlockHeader { /// CHAIN LINKING /// @@ -78,7 +79,6 @@ impl BlockHeader { }; let new_cid = cid::Cid::new_from_prefix(&c, &[self.cached_bytes]); self.cached_cid = new_cid; - println!("CID:: {}", self.cached_cid); self.cached_cid } } diff --git a/blockchain/src/blocks/mod.rs b/blockchain/src/blocks/mod.rs index f8a331aa0306..193aefc0f0c7 100644 --- a/blockchain/src/blocks/mod.rs +++ b/blockchain/src/blocks/mod.rs @@ -1,7 +1,7 @@ -mod block; +pub mod block; mod errors; -mod ticket; -mod tipset; +pub mod ticket; +pub mod tipset; pub use block::*; pub use errors::*; diff --git a/blockchain/src/blocks/ticket.rs b/blockchain/src/blocks/ticket.rs index 84b89614423b..e6b06bdab09a 100644 --- a/blockchain/src/blocks/ticket.rs +++ b/blockchain/src/blocks/ticket.rs @@ -1,5 +1,6 @@ #![allow(unused_variables)] #![allow(dead_code)] +use std::cmp::Ordering; /// VRFProofIndex is the proof output from running a VRF pub type VRFProofIndex = Vec; @@ -7,7 +8,7 @@ pub type VRFProofIndex = Vec; /// 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)] +#[derive(Clone, Debug, PartialEq, PartialOrd, Eq)] pub struct Ticket { /// A proof output by running a VRF on the VDFResult of the parent ticket pub vrfproof: VRFProofIndex, @@ -18,3 +19,13 @@ impl Ticket { self.vrfproof.clone() } } + +impl Ord for Ticket { + fn cmp(&self, other: &Self) -> Ordering { + let val = self.sort_key().cmp(&other.sort_key()); + println!("ord {:?}", val); + return val + // check if equal or return ordering + // return comparison of cid bytes + } +} \ No newline at end of file diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index aeb8b8913106..769b3feb394a 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -4,8 +4,7 @@ use super::block::BlockHeader; use super::errors::Error; use super::ticket::Ticket; -use cid::Cid; -use vm::address::Address; +use cid::{Cid, Codec, Version}; /// TipSet is an immutable set of blocks at the same height with the same parent set /// Blocks in a tipset are canonically ordered by ticket size @@ -17,15 +16,15 @@ pub struct Tipset { /// TipSetKeys is 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(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct TipSetKeys { pub cids: Vec, } impl Tipset { /// new builds a new TipSet from a collection of blocks - //// The blocks must be distinct (different CIDs), have the same height, and same parent set - fn new(headers: Vec) -> Result { + /// The blocks must be distinct (different CIDs), have the same height, and same parent set + pub fn new(headers: Vec) -> Result { if headers.is_empty() { return Err(Error::NoBlocks); } @@ -68,28 +67,33 @@ impl Tipset { // } // return cmp < 0 // }) + + println!("sorted headers -> {:?} \n \n", sorted_headers[0].ticket.sort_key()); // sort headers by ticket size // if tie; Break ticket ties with the block CIDs, which are distinct - sorted_headers.sort_by(|a, b| { + let sorted = sorted_headers.sort_by(|a, b| { a.ticket .sort_key() - .partial_cmp(&b.ticket.sort_key()) - .unwrap() + .cmp(&b.ticket.sort_key()) }); + + println!("sorted headers after sort -> {:?}", sorted); // INTERIM TO SATISFY STRUCT - let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" - .parse() - .unwrap(); - let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" - .parse() - .unwrap(); - let arr = vec![cid1.clone(), cid2.clone()]; + let data = b"awesome test content"; + let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); + + let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); + let prefix = cid.prefix(); + + let cid2 = Cid::new_from_prefix(&prefix, data); Ok(Self { blocks: sorted_headers, - key: TipSetKeys { cids: arr }, + key: TipSetKeys { + cids: vec![cid.clone(), cid2.clone()], + }, }) } @@ -156,58 +160,150 @@ impl TipSetKeys { } } -fn test_header() -> Vec { - let cid1: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" - .parse() - .unwrap(); - let cid2: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR12" - .parse() - .unwrap(); - - let data: Vec = vec![1, 4, 3, 6, 7, 1, 2]; - let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3]; - let new_addr = Address::new_secp256k1(data.clone()).unwrap(); - let arr = vec![cid1.clone(), cid2.clone()]; - - let h = vec![ - BlockHeader { - parents: TipSetKeys { cids: arr.clone() }, - weight: 0, - epoch: 1, - height: 1, - miner_address: new_addr.clone(), - messages: cid1.clone(), - message_receipts: cid1.clone(), - state_root: cid1.clone(), - timestamp: 0, - ticket: Ticket { - vrfproof: data.clone(), +#[cfg(test)] +mod tests { + use super::*; + use vm::address::Address; + + const WEIGHT: u64 = 0; + const EPOCH: u64 = 1; + const HEIGHT: u64 = 1; + const CACHED_BYTES: u8 = 0; + + fn key_setup() -> Vec { + let data = b"awesome test content"; + let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); + + let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); + let prefix = cid.prefix(); + + let cid2 = Cid::new_from_prefix(&prefix, data); + + return vec![cid.clone(), cid2.clone()]; + } + + fn header_setup() -> Vec { + let data: Vec = vec![1, 4, 3, 6, 7, 1, 2]; + let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3]; + let new_addr = Address::new_secp256k1(data.clone()).unwrap(); + + return vec![ + BlockHeader { + parents: TipSetKeys { + cids: key_setup().clone(), + }, + weight: WEIGHT, + epoch: EPOCH, + height: HEIGHT, + miner_address: new_addr.clone(), + messages: key_setup()[0].clone(), + message_receipts: key_setup()[0].clone(), + state_root: key_setup()[0].clone(), + timestamp: 0, + ticket: Ticket { + vrfproof: data2.clone(), + }, + election_proof: data.clone(), + cached_cid: key_setup()[0].clone(), + cached_bytes: CACHED_BYTES, }, - election_proof: data.clone(), - cached_cid: cid1.clone(), - cached_bytes: 0, - }, - BlockHeader { - parents: TipSetKeys { cids: arr }, - weight: 0, - epoch: 1, - height: 1, - miner_address: new_addr, - messages: cid1.clone(), - message_receipts: cid1.clone(), - state_root: cid1.clone(), - timestamp: 0, - ticket: Ticket { vrfproof: data2 }, - election_proof: data.clone(), - cached_cid: cid1.clone(), - cached_bytes: 0, - }, - ]; - h -} + BlockHeader { + parents: TipSetKeys { cids: key_setup() }, + weight: WEIGHT, + epoch: EPOCH, + height: HEIGHT, + miner_address: new_addr, + messages: key_setup()[0].clone(), + message_receipts: key_setup()[0].clone(), + state_root: key_setup()[0].clone(), + timestamp: 1, + ticket: Ticket { vrfproof: data.clone() }, + election_proof: data.clone(), + cached_cid: key_setup()[1].clone(), + cached_bytes: CACHED_BYTES, + }, + ]; + } + + fn setup() -> Result<(Tipset), Error> { + let headers = header_setup(); + let tipset = Tipset::new(headers.clone())?; + Ok(tipset) + } + + #[test] + fn new_test() { + let headers = header_setup(); + assert!(Tipset::new(headers).is_ok(), "result is okay!"); + } -#[test] -fn new_test() { - let headers = test_header(); - let result = Tipset::new(headers); + #[test] + fn min_ticket_test() -> Result<(), Error> { + let tipset = setup()?; + let min = Tipset::min_ticket(&tipset)?; + assert_eq!(min.vrfproof, tipset.blocks[0].ticket.vrfproof); + Ok(()) + } + + #[test] + fn min_timestamp_test() -> Result<(), Error> { + let tipset = setup()?; + let min_time = Tipset::min_timestamp(&tipset)?; + assert_eq!(min_time, tipset.blocks[0].timestamp); + Ok(()) + } + + #[test] + fn len_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::len(&tipset), 2); + Ok(()) + } + + #[test] + fn is_empty_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::is_empty(&tipset), false); + Ok(()) + } + + #[test] + fn key_test() -> Result<(), Error> { + let keys = TipSetKeys { + cids: key_setup().clone(), + }; + let headers = header_setup(); + let tipset = Tipset::new(headers.clone())?; + assert_eq!(Tipset::key(&tipset), keys); + Ok(()) + } + + #[test] + fn height_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::height(&tipset), tipset.blocks[1].height); + Ok(()) + } + + #[test] + fn parents_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::parents(&tipset), tipset.blocks[1].parents); + Ok(()) + } + + #[test] + fn weight_test() -> Result<(), Error> { + let tipset = setup()?; + assert_eq!(Tipset::weight(&tipset), tipset.blocks[1].weight); + Ok(()) + } + + #[test] + fn equals_test() { + let tipset_keys = TipSetKeys { + cids: key_setup().clone(), + }; + assert!(TipSetKeys::equals(&tipset_keys, tipset_keys.clone())); + } } diff --git a/vm/src/address/mod.rs b/vm/src/address/mod.rs index 8da90fb6d332..bd8018ec4666 100644 --- a/vm/src/address/mod.rs +++ b/vm/src/address/mod.rs @@ -26,7 +26,7 @@ const TESTNET_PREFIX: &str = "t"; /// Address is the struct that defines the protocol and data payload conversion from either /// a public key or value -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct Address { protocol: Protocol, payload: Vec, diff --git a/vm/src/address/protocol.rs b/vm/src/address/protocol.rs index fb323b74bf9c..160ea74e9336 100644 --- a/vm/src/address/protocol.rs +++ b/vm/src/address/protocol.rs @@ -3,7 +3,7 @@ use num_traits::FromPrimitive; use std::fmt; /// Protocol defines the addressing protocol used to derive data to an address -#[derive(PartialEq, Copy, Clone, FromPrimitive)] +#[derive(PartialEq, Copy, Clone, FromPrimitive, Debug)] pub enum Protocol { // ID protocol addressing ID = 0, From 935734b20becc5566440f13d6479476509b619a8 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Fri, 29 Nov 2019 14:49:50 -0600 Subject: [PATCH 06/14] Added tipset new logic * Added new tipset logic, includes conditional checks and sorting based on ticket size * Added unit tests for tipset methods * Added cid fn although it is incomplete as we need cbor encoding --- blockchain/src/blocks/block.rs | 14 +- blockchain/src/blocks/tipset.rs | 241 +++++++++++++++----------------- 2 files changed, 122 insertions(+), 133 deletions(-) diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index 6785c6593bd9..89a5d15a3cd1 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -11,7 +11,7 @@ use vm::address::Address; // SHOULD BE BLAKE2B const DEFAULT_HASH_FUNCTION: Hash = Hash::Keccak256; -/// BlockHeader defines header of a block in the Filecoin blockchain#[derive(Clone)] +/// BlockHeader defines header of a block in the Filecoin blockchain #[derive(Clone, Debug)] pub struct BlockHeader { /// CHAIN LINKING @@ -69,10 +69,18 @@ pub struct Block { } impl BlockHeader { - // cid returns the content id of this block + // cid returns the content id of this header pub fn cid(mut self) -> cid::Cid { + // TODO + // + // Currently content id for headers will be incomplete until the below + // points are resolved + // + // Requires encoding blockheader into cached_bytes which is currently unavailable + // Until mulithash supports blake2b using Keccak256 for mh_type + // Using DagProtobuf as placeholder until CBOR support let c = Prefix { - version: Version::V0, + version: Version::V1, codec: Codec::DagProtobuf, mh_type: DEFAULT_HASH_FUNCTION, mh_len: 0, diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 769b3feb394a..279513d148fc 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -4,7 +4,7 @@ use super::block::BlockHeader; use super::errors::Error; use super::ticket::Ticket; -use cid::{Cid, Codec, Version}; +use cid::{Cid}; /// TipSet is an immutable set of blocks at the same height with the same parent set /// Blocks in a tipset are canonically ordered by ticket size @@ -16,15 +16,18 @@ pub struct Tipset { /// TipSetKeys is 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(PartialEq, Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct TipSetKeys { pub cids: Vec, } impl Tipset { /// new builds a new TipSet from a collection of blocks - /// The blocks must be distinct (different CIDs), have the same height, and same parent set + /// A valid tipset contains a non-empty collection of blocks that have distinct miners and all specify identical + /// epoch, parents, weight, height, state root, receipt root; + /// contentID for headers are supposed to be distinct but until encoding is added will be equal pub fn new(headers: Vec) -> Result { + // check header is non-empty if headers.is_empty() { return Err(Error::NoBlocks); } @@ -32,67 +35,69 @@ impl Tipset { let mut sorted_headers = Vec::new(); let mut sorted_cids = Vec::new(); let mut i = 0; - while i < headers.len() { - if headers[i].height != headers[0].height { - return Err(Error::UndefinedTipSet); - } - if !headers[i].parents.equals(headers[0].parents.clone()) { - return Err(Error::UndefinedTipSet); - } - if headers[i].weight != headers[0].weight { - return Err(Error::UndefinedTipSet); - } - if headers[i].state_root != headers[0].state_root.clone() { - return Err(Error::UndefinedTipSet); - } - if headers[i].epoch != headers[0].epoch { - return Err(Error::UndefinedTipSet); - } - if headers[i].message_receipts != headers[0].message_receipts.clone() { - return Err(Error::UndefinedTipSet); + + // loop through headers and validate conditions against 0th header + while i <= headers.len() -1 { + if i > 0 { // skip redundant checks for first block + // check height is equal + if headers[i].height != headers[0].height { + return Err(Error::UndefinedTipSet); + } + // check parent cids are equal + if !headers[i].parents.equals(headers[0].parents.clone()) { + return Err(Error::UndefinedTipSet); + } + // check weights are equal + if headers[i].weight != headers[0].weight { + return Err(Error::UndefinedTipSet); + } + // check state_roots are equal + if headers[i].state_root != headers[0].state_root.clone() { + return Err(Error::UndefinedTipSet); + } + // check epochs are equal + if headers[i].epoch != headers[0].epoch { + return Err(Error::UndefinedTipSet); + } + // check message_receipts are equal + if headers[i].message_receipts != headers[0].message_receipts.clone() { + return Err(Error::UndefinedTipSet); + } + // check miner_addresses are distinct + if headers[i].miner_address == headers[0].miner_address.clone() { + return Err(Error::UndefinedTipSet); + } } + // push headers into vec for sorting sorted_headers.push(headers[i].clone()); + // push header cid into vec for tie breaker sorted_cids.push(headers[i].clone().cid()); i += 1; } - // sort headers by ticket - // - // GO FILE COIN LOGIC BELOW - // - // sort.Slice(sorted, func(i, j int) bool { - // cmp := bytes.Compare(sorted[i].Ticket.SortKey(), sorted[j].Ticket.SortKey()) - // if cmp == 0 { - // // Break ticket ties with the block CIDs, which are distinct. - // cmp = bytes.Compare(sorted[i].Cid().Bytes(), sorted[j].Cid().Bytes()) - // } - // return cmp < 0 - // }) - - println!("sorted headers -> {:?} \n \n", sorted_headers[0].ticket.sort_key()); // sort headers by ticket size - // if tie; Break ticket ties with the block CIDs, which are distinct - let sorted = sorted_headers.sort_by(|a, b| { - a.ticket + // break ticket ties with the header CIDs, which are distinct + sorted_headers.sort_by(|a, b| { + let a1 = a.clone(); + let b1 = b.clone(); + + a1.ticket .sort_key() - .cmp(&b.ticket.sort_key()) + .cmp(&b1.ticket.sort_key()).reverse() + .then(a1.cid().hash.cmp(&b1.cid().hash)) }); - - println!("sorted headers after sort -> {:?}", sorted); - - // INTERIM TO SATISFY STRUCT - let data = b"awesome test content"; - let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); - - let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); - let prefix = cid.prefix(); - let cid2 = Cid::new_from_prefix(&prefix, data); + // TODO + // Have a check the ensures CIDs are distinct + // blocked by CBOR encoding + // return tipset where sorted headers have smallest ticket size is in the 0th index + // and the distinct keys Ok(Self { blocks: sorted_headers, key: TipSetKeys { - cids: vec![cid.clone(), cid2.clone()], + // interim until CID check is in place + cids: sorted_cids, }, }) } @@ -150,7 +155,7 @@ impl TipSetKeys { return false; } let mut i = 0; - while i > key.cids.len() { + while i <= key.cids.len() -1 { i += 1; if self.cids[i] == key.cids[i] { return false; @@ -164,64 +169,58 @@ impl TipSetKeys { mod tests { use super::*; use vm::address::Address; + use cid::{Cid, Codec, Version}; const WEIGHT: u64 = 0; const EPOCH: u64 = 1; const HEIGHT: u64 = 1; const CACHED_BYTES: u8 = 0; + // key_setup returns a vec of 3 distinct CIDs fn key_setup() -> Vec { - let data = b"awesome test content"; - let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); + let data0 = b"awesome test content!"; + let data1 = b"awesome test content am I right?"; + let data2 = b"awesome test content but seriously right?"; + let h = multihash::encode(multihash::Hash::SHA2256, data0).unwrap(); let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); let prefix = cid.prefix(); - let cid2 = Cid::new_from_prefix(&prefix, data); + let cid2 = Cid::new_from_prefix(&prefix, data1); + let cid3 = Cid::new_from_prefix(&prefix, data2); + return vec![cid.clone(), cid2.clone(), cid3.clone()]; + } - return vec![cid.clone(), cid2.clone()]; + // 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: cids.clone() }, + weight: WEIGHT, + epoch: EPOCH, + height: HEIGHT, + miner_address: Address::new_secp256k1(ticket_p.clone()).unwrap(), + messages: cids[0].clone(), + message_receipts: cids[0].clone(), + state_root: cids[0].clone(), + timestamp, + ticket: Ticket { vrfproof: ticket_p }, + election_proof: vec![], + cached_cid: cid, + cached_bytes: 0, + } } + // header_setup returns a vec of block headers to be used for testing purposes fn header_setup() -> Vec { - let data: Vec = vec![1, 4, 3, 6, 7, 1, 2]; - let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3]; - let new_addr = Address::new_secp256k1(data.clone()).unwrap(); - + let data0: Vec = vec![1, 4, 3, 6, 7, 1, 2]; + let data1: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3, 12, 2, 1]; + let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3, 12, 2]; + let cids = key_setup(); return vec![ - BlockHeader { - parents: TipSetKeys { - cids: key_setup().clone(), - }, - weight: WEIGHT, - epoch: EPOCH, - height: HEIGHT, - miner_address: new_addr.clone(), - messages: key_setup()[0].clone(), - message_receipts: key_setup()[0].clone(), - state_root: key_setup()[0].clone(), - timestamp: 0, - ticket: Ticket { - vrfproof: data2.clone(), - }, - election_proof: data.clone(), - cached_cid: key_setup()[0].clone(), - cached_bytes: CACHED_BYTES, - }, - BlockHeader { - parents: TipSetKeys { cids: key_setup() }, - weight: WEIGHT, - epoch: EPOCH, - height: HEIGHT, - miner_address: new_addr, - messages: key_setup()[0].clone(), - message_receipts: key_setup()[0].clone(), - state_root: key_setup()[0].clone(), - timestamp: 1, - ticket: Ticket { vrfproof: data.clone() }, - election_proof: data.clone(), - cached_cid: key_setup()[1].clone(), - cached_bytes: CACHED_BYTES, - }, + template_header(data1.clone(), cids[2].clone(), 1), + template_header(data0.clone(), cids[1].clone(), 2), + template_header(data2.clone(), cids[0].clone(), 3), ]; } @@ -238,65 +237,47 @@ mod tests { } #[test] - fn min_ticket_test() -> Result<(), Error> { - let tipset = setup()?; - let min = Tipset::min_ticket(&tipset)?; + fn min_ticket_test() { + let tipset = setup().unwrap(); + let min = Tipset::min_ticket(&tipset).unwrap(); assert_eq!(min.vrfproof, tipset.blocks[0].ticket.vrfproof); - Ok(()) } #[test] - fn min_timestamp_test() -> Result<(), Error> { - let tipset = setup()?; - let min_time = Tipset::min_timestamp(&tipset)?; - assert_eq!(min_time, tipset.blocks[0].timestamp); - Ok(()) + fn min_timestamp_test() { + let tipset = setup().unwrap(); + let min_time = Tipset::min_timestamp(&tipset).unwrap(); + assert_eq!(min_time, tipset.blocks[1].timestamp); } #[test] - fn len_test() -> Result<(), Error> { - let tipset = setup()?; - assert_eq!(Tipset::len(&tipset), 2); - Ok(()) + fn len_test() { + let tipset = setup().unwrap(); + assert_eq!(Tipset::len(&tipset), 3); } #[test] - fn is_empty_test() -> Result<(), Error> { - let tipset = setup()?; + fn is_empty_test() { + let tipset = setup().unwrap(); assert_eq!(Tipset::is_empty(&tipset), false); - Ok(()) - } - - #[test] - fn key_test() -> Result<(), Error> { - let keys = TipSetKeys { - cids: key_setup().clone(), - }; - let headers = header_setup(); - let tipset = Tipset::new(headers.clone())?; - assert_eq!(Tipset::key(&tipset), keys); - Ok(()) } #[test] - fn height_test() -> Result<(), Error> { - let tipset = setup()?; + fn height_test() { + let tipset = setup().unwrap(); assert_eq!(Tipset::height(&tipset), tipset.blocks[1].height); - Ok(()) } #[test] - fn parents_test() -> Result<(), Error> { - let tipset = setup()?; + fn parents_test() { + let tipset = setup().unwrap(); assert_eq!(Tipset::parents(&tipset), tipset.blocks[1].parents); - Ok(()) } #[test] - fn weight_test() -> Result<(), Error> { - let tipset = setup()?; + fn weight_test() { + let tipset = setup().unwrap(); assert_eq!(Tipset::weight(&tipset), tipset.blocks[1].weight); - Ok(()) } #[test] @@ -304,6 +285,6 @@ mod tests { let tipset_keys = TipSetKeys { cids: key_setup().clone(), }; - assert!(TipSetKeys::equals(&tipset_keys, tipset_keys.clone())); + assert_eq!(TipSetKeys::equals(&tipset_keys, tipset_keys.clone()), false); } } From 592b48b8fd50741816cb886a94a27eb21a16098f Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Fri, 29 Nov 2019 15:23:43 -0600 Subject: [PATCH 07/14] Fixed equals fn * Fixed equals fn check * Linted --- .gitignore | 3 ++- blockchain/src/blocks/ticket.rs | 11 --------- blockchain/src/blocks/tipset.rs | 42 ++++++++++++++++++++------------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 212181d360e4..9102759836a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target **/*.rs.bk -/Cargo.lock \ No newline at end of file +/Cargo.lock +.idea/ \ No newline at end of file diff --git a/blockchain/src/blocks/ticket.rs b/blockchain/src/blocks/ticket.rs index e6b06bdab09a..1e6871f79396 100644 --- a/blockchain/src/blocks/ticket.rs +++ b/blockchain/src/blocks/ticket.rs @@ -1,6 +1,5 @@ #![allow(unused_variables)] #![allow(dead_code)] -use std::cmp::Ordering; /// VRFProofIndex is the proof output from running a VRF pub type VRFProofIndex = Vec; @@ -19,13 +18,3 @@ impl Ticket { self.vrfproof.clone() } } - -impl Ord for Ticket { - fn cmp(&self, other: &Self) -> Ordering { - let val = self.sort_key().cmp(&other.sort_key()); - println!("ord {:?}", val); - return val - // check if equal or return ordering - // return comparison of cid bytes - } -} \ No newline at end of file diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 279513d148fc..68f714039def 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -4,7 +4,7 @@ use super::block::BlockHeader; use super::errors::Error; use super::ticket::Ticket; -use cid::{Cid}; +use cid::Cid; /// TipSet is an immutable set of blocks at the same height with the same parent set /// Blocks in a tipset are canonically ordered by ticket size @@ -35,16 +35,18 @@ impl Tipset { let mut sorted_headers = Vec::new(); let mut sorted_cids = Vec::new(); let mut i = 0; - + let size = headers.len() - 1; // loop through headers and validate conditions against 0th header - while i <= headers.len() -1 { - if i > 0 { // skip redundant checks for first block + while i <= size { + if i > 0 { + // skip redundant checks for first block // check height is equal if headers[i].height != headers[0].height { return Err(Error::UndefinedTipSet); } // check parent cids are equal if !headers[i].parents.equals(headers[0].parents.clone()) { + println!("FAILS HERE::"); return Err(Error::UndefinedTipSet); } // check weights are equal @@ -83,7 +85,8 @@ impl Tipset { a1.ticket .sort_key() - .cmp(&b1.ticket.sort_key()).reverse() + .cmp(&b1.ticket.sort_key()) + .reverse() .then(a1.cid().hash.cmp(&b1.cid().hash)) }); @@ -155,11 +158,12 @@ impl TipSetKeys { return false; } let mut i = 0; - while i <= key.cids.len() -1 { - i += 1; - if self.cids[i] == key.cids[i] { + let size = key.cids.len() - 1; + while i <= size { + if self.cids[i] != key.cids[i] { return false; } + i += 1; } true } @@ -168,8 +172,8 @@ impl TipSetKeys { #[cfg(test)] mod tests { use super::*; - use vm::address::Address; use cid::{Cid, Codec, Version}; + use vm::address::Address; const WEIGHT: u64 = 0; const EPOCH: u64 = 1; @@ -181,6 +185,7 @@ mod tests { let data0 = b"awesome test content!"; let data1 = b"awesome test content am I right?"; let data2 = b"awesome test content but seriously right?"; + let data3 = b"awesome test content for parents?"; let h = multihash::encode(multihash::Hash::SHA2256, data0).unwrap(); let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); @@ -188,14 +193,19 @@ mod tests { let cid2 = Cid::new_from_prefix(&prefix, data1); let cid3 = Cid::new_from_prefix(&prefix, data2); - return vec![cid.clone(), cid2.clone(), cid3.clone()]; + // parents needs its own unique CID + let cid4 = Cid::new_from_prefix(&prefix, data3); + + return vec![cid.clone(), cid2.clone(), cid3.clone(), cid4.clone()]; } // 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: cids.clone() }, + parents: TipSetKeys { + cids: vec![cids[3].clone()], + }, weight: WEIGHT, epoch: EPOCH, height: HEIGHT, @@ -218,9 +228,9 @@ mod tests { let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3, 12, 2]; let cids = key_setup(); return vec![ - template_header(data1.clone(), cids[2].clone(), 1), - template_header(data0.clone(), cids[1].clone(), 2), - template_header(data2.clone(), cids[0].clone(), 3), + template_header(data1.clone(), cids[1].clone(), 1), + template_header(data0.clone(), cids[0].clone(), 2), + template_header(data2.clone(), cids[2].clone(), 3), ]; } @@ -263,7 +273,7 @@ mod tests { } #[test] - fn height_test() { + fn height_test() { let tipset = setup().unwrap(); assert_eq!(Tipset::height(&tipset), tipset.blocks[1].height); } @@ -285,6 +295,6 @@ mod tests { let tipset_keys = TipSetKeys { cids: key_setup().clone(), }; - assert_eq!(TipSetKeys::equals(&tipset_keys, tipset_keys.clone()), false); + assert_eq!(TipSetKeys::equals(&tipset_keys, tipset_keys.clone()), true); } } From 423c49c9e5e631cf189186fe8d0e0d956e9a8300 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Fri, 29 Nov 2019 15:48:53 -0600 Subject: [PATCH 08/14] Remove typo --- blockchain/src/blocks/block.rs | 2 +- blockchain/src/blocks/tipset.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index 79524c42c6e2..0b79c95d622a 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -79,7 +79,7 @@ impl BlockHeader { // // Requires encoding blockheader into cached_bytes which is currently unavailable // Until mulithash supports blake2b using Keccak256 for mh_type - // Using DagProtobuf as placeholder until CBOR support + // Using DagProtobuf as placeholder let c = Prefix { version: Version::V1, codec: Codec::DagProtobuf, diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 5632c79fb50b..07e6c86928ed 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -46,7 +46,6 @@ impl Tipset { } // check parent cids are equal if !headers[i].parents.equals(headers[0].parents.clone()) { - println!("FAILS HERE::"); return Err(Error::UndefinedTipSet); } // check weights are equal @@ -72,7 +71,7 @@ impl Tipset { } // push headers into vec for sorting sorted_headers.push(headers[i].clone()); - // push header cid into vec for unqiue check + // push header cid into vec for unique check sorted_cids.push(headers[i].clone().cid()); i += 1; } From 84be3f095c5766a0109548920efda40c0cc7d82b Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 2 Dec 2019 11:33:13 -0600 Subject: [PATCH 09/14] Made requested changes * Updated multihash to specific version * Removed extern crate statement * Improved commenting * Updated sort statement * Improved unit test setup * Updated error messages for tipset condition checks --- blockchain/Cargo.toml | 2 +- blockchain/src/blocks/block.rs | 17 +++--- blockchain/src/blocks/errors.rs | 4 +- blockchain/src/blocks/mod.rs | 4 +- blockchain/src/blocks/tipset.rs | 99 ++++++++++++++------------------- 5 files changed, 54 insertions(+), 72 deletions(-) diff --git a/blockchain/Cargo.toml b/blockchain/Cargo.toml index 483b14563b4b..6e8074bf9956 100644 --- a/blockchain/Cargo.toml +++ b/blockchain/Cargo.toml @@ -10,4 +10,4 @@ edition = "2018" vm = {path = "../vm"} address = {path = "../vm/address"} cid = "0.3.1" -multihash = "*" \ No newline at end of file +multihash = "0.8.0" \ No newline at end of file diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index 0b79c95d622a..a4cac6487080 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -3,14 +3,13 @@ use super::ticket::{Ticket, VRFProofIndex}; use super::TipSetKeys; -extern crate multihash; use address::Address; use cid::{Cid, Codec, Prefix, Version}; use multihash::Hash; use vm::message::Message; // DefaultHashFunction represents the default hashing function to use -// SHOULD BE BLAKE2B +// TODO SHOULD BE BLAKE2B const DEFAULT_HASH_FUNCTION: Hash = Hash::Keccak256; /// BlockHeader defines header of a block in the Filecoin blockchain @@ -70,16 +69,14 @@ pub struct Block { } impl BlockHeader { - // cid returns the content id of this header + /// cid returns the content id of this header pub fn cid(mut self) -> cid::Cid { // TODO + // Encode blockheader into cache_bytes + // Update codec to use DagCBOR + // Change DEFAULT_HASH_FUNCTION to utilize blake2b // - // Currently content id for headers will be incomplete until the below - // points are resolved - // - // Requires encoding blockheader into cached_bytes which is currently unavailable - // Until mulithash supports blake2b using Keccak256 for mh_type - // Using DagProtobuf as placeholder + // Currently content id for headers will be incomplete until encoding and supporting libraries are completed let c = Prefix { version: Version::V1, codec: Codec::DagProtobuf, @@ -88,6 +85,6 @@ impl BlockHeader { }; let new_cid = cid::Cid::new_from_prefix(&c, &[self.cached_bytes]); self.cached_cid = new_cid; - self.cached_cid + self.cached_cid.clone() } } diff --git a/blockchain/src/blocks/errors.rs b/blockchain/src/blocks/errors.rs index e213bb2e9e6a..a9f6f93f66e5 100644 --- a/blockchain/src/blocks/errors.rs +++ b/blockchain/src/blocks/errors.rs @@ -2,14 +2,14 @@ use std::fmt; #[derive(Debug, PartialEq)] pub enum Error { - UndefinedTipSet, + UndefinedTipSet(&'static str), NoBlocks, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - Error::UndefinedTipSet => write!(f, "Undefined tipset"), + Error::UndefinedTipSet(ref msg) => write!(f, "Invalid tipset: {}", msg), Error::NoBlocks => write!(f, "No blocks for tipset"), } } diff --git a/blockchain/src/blocks/mod.rs b/blockchain/src/blocks/mod.rs index 193aefc0f0c7..39783d2fa111 100644 --- a/blockchain/src/blocks/mod.rs +++ b/blockchain/src/blocks/mod.rs @@ -1,7 +1,7 @@ pub mod block; mod errors; -pub mod ticket; -pub mod tipset; +mod ticket; +mod tipset; pub use block::*; pub use errors::*; diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 07e6c86928ed..f612be680984 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -5,7 +5,7 @@ use super::block::BlockHeader; use super::errors::Error; use super::ticket::Ticket; use cid::Cid; - +use std::cmp::Reverse; /// TipSet is an immutable set of blocks at the same height with the same parent set /// Blocks in a tipset are canonically ordered by ticket size pub struct Tipset { @@ -33,60 +33,52 @@ impl Tipset { } let mut sorted_headers = Vec::new(); - let mut sorted_cids = Vec::new(); - let mut i = 0; - let size = headers.len() - 1; + let mut cids = Vec::new(); + // loop through headers and validate conditions against 0th header - while i <= size { + for i in 0..headers.len() { if i > 0 { - // skip redundant checks for first block + // Skip redundant check // check height is equal if headers[i].height != headers[0].height { - return Err(Error::UndefinedTipSet); + return Err(Error::UndefinedTipSet("heights are not equal")); } // check parent cids are equal if !headers[i].parents.equals(headers[0].parents.clone()) { - return Err(Error::UndefinedTipSet); + return Err(Error::UndefinedTipSet("parent cids are not equal")); } // check weights are equal if headers[i].weight != headers[0].weight { - return Err(Error::UndefinedTipSet); + return Err(Error::UndefinedTipSet("weights are not equal")); } // check state_roots are equal if headers[i].state_root != headers[0].state_root.clone() { - return Err(Error::UndefinedTipSet); + return Err(Error::UndefinedTipSet("state_roots are not equal")); } // check epochs are equal if headers[i].epoch != headers[0].epoch { - return Err(Error::UndefinedTipSet); + return Err(Error::UndefinedTipSet("epochs are not equal")); } // check message_receipts are equal if headers[i].message_receipts != headers[0].message_receipts.clone() { - return Err(Error::UndefinedTipSet); + return Err(Error::UndefinedTipSet("message_receipts are not equal")); } // check miner_addresses are distinct if headers[i].miner_address == headers[0].miner_address.clone() { - return Err(Error::UndefinedTipSet); + return Err(Error::UndefinedTipSet("miner_addresses are not distinct")); } } // push headers into vec for sorting sorted_headers.push(headers[i].clone()); // push header cid into vec for unique check - sorted_cids.push(headers[i].clone().cid()); - i += 1; + cids.push(headers[i].clone().cid()); } // sort headers by ticket size // break ticket ties with the header CIDs, which are distinct - sorted_headers.sort_by(|a, b| { - let a1 = a.clone(); - let b1 = b.clone(); - - a1.ticket - .sort_key() - .cmp(&b1.ticket.sort_key()) - .reverse() - .then(a1.cid().hash.cmp(&b1.cid().hash)) + sorted_headers.sort_by_key(|header| { + let h = header.clone(); + (Reverse(h.ticket.sort_key()), h.cid().hash) }); // TODO @@ -99,13 +91,13 @@ impl Tipset { blocks: sorted_headers, key: TipSetKeys { // interim until CID check is in place - cids: sorted_cids, + cids, }, }) } /// min_ticket returns the smallest ticket of all blocks in the tipset - fn min_ticket(&self) -> Result<(Ticket), Error> { + fn min_ticket(&self) -> Result { if self.blocks.is_empty() { return Err(Error::NoBlocks); } @@ -156,13 +148,10 @@ impl TipSetKeys { if self.cids.len() != key.cids.len() { return false; } - let mut i = 0; - let size = key.cids.len() - 1; - while i <= size { + for i in 0..key.cids.len() { if self.cids[i] != key.cids[i] { return false; } - i += 1; } true } @@ -179,23 +168,10 @@ mod tests { const HEIGHT: u64 = 1; const CACHED_BYTES: u8 = 0; - // key_setup returns a vec of 3 distinct CIDs - fn key_setup() -> Vec { - let data0 = b"awesome test content!"; - let data1 = b"awesome test content am I right?"; - let data2 = b"awesome test content but seriously right?"; - let data3 = b"awesome test content for parents?"; - - let h = multihash::encode(multihash::Hash::SHA2256, data0).unwrap(); + fn template_key(data: &[u8]) -> Cid { + let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap(); let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h); - let prefix = cid.prefix(); - - let cid2 = Cid::new_from_prefix(&prefix, data1); - let cid3 = Cid::new_from_prefix(&prefix, data2); - // parents needs its own unique CID - let cid4 = Cid::new_from_prefix(&prefix, data3); - - return vec![cid.clone(), cid2.clone(), cid3.clone(), cid4.clone()]; + return cid; } // template_header defines a block header used in testing @@ -233,59 +209,68 @@ mod tests { ]; } - fn setup() -> Result<(Tipset), Error> { + // key_setup returns a vec of 4 distinct CIDs + fn key_setup() -> Vec { + return vec![ + template_key(b"test content"), + template_key(b"awesome test content "), + template_key(b"even better test content"), + template_key(b"the best test content out there"), + ]; + } + + fn setup() -> Tipset { let headers = header_setup(); - let tipset = Tipset::new(headers.clone())?; - Ok(tipset) + return Tipset::new(headers.clone()).expect("tipset is invalid"); } #[test] fn new_test() { let headers = header_setup(); - assert!(Tipset::new(headers).is_ok(), "result is okay!"); + assert!(Tipset::new(headers).is_ok(), "result is invalid"); } #[test] fn min_ticket_test() { - let tipset = setup().unwrap(); + let tipset = setup(); let min = Tipset::min_ticket(&tipset).unwrap(); assert_eq!(min.vrfproof, tipset.blocks[0].ticket.vrfproof); } #[test] fn min_timestamp_test() { - let tipset = setup().unwrap(); + let tipset = setup(); let min_time = Tipset::min_timestamp(&tipset).unwrap(); assert_eq!(min_time, tipset.blocks[1].timestamp); } #[test] fn len_test() { - let tipset = setup().unwrap(); + let tipset = setup(); assert_eq!(Tipset::len(&tipset), 3); } #[test] fn is_empty_test() { - let tipset = setup().unwrap(); + let tipset = setup(); assert_eq!(Tipset::is_empty(&tipset), false); } #[test] fn height_test() { - let tipset = setup().unwrap(); + let tipset = setup(); assert_eq!(Tipset::height(&tipset), tipset.blocks[1].height); } #[test] fn parents_test() { - let tipset = setup().unwrap(); + let tipset = setup(); assert_eq!(Tipset::parents(&tipset), tipset.blocks[1].parents); } #[test] fn weight_test() { - let tipset = setup().unwrap(); + let tipset = setup(); assert_eq!(Tipset::weight(&tipset), tipset.blocks[1].weight); } From 4b2e5099b986408da4b67b6ec2444a20a59f808e Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 2 Dec 2019 12:22:26 -0600 Subject: [PATCH 10/14] Made requested changes * Updated cid reference * Removed pub from mod.rs --- blockchain/src/blocks/block.rs | 2 +- blockchain/src/blocks/mod.rs | 2 +- blockchain/src/blocks/tipset.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index a4cac6487080..35fdcb345dfd 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -70,7 +70,7 @@ pub struct Block { impl BlockHeader { /// cid returns the content id of this header - pub fn cid(mut self) -> cid::Cid { + pub fn cid(&mut self) -> cid::Cid { // TODO // Encode blockheader into cache_bytes // Update codec to use DagCBOR diff --git a/blockchain/src/blocks/mod.rs b/blockchain/src/blocks/mod.rs index 39783d2fa111..f8a331aa0306 100644 --- a/blockchain/src/blocks/mod.rs +++ b/blockchain/src/blocks/mod.rs @@ -1,4 +1,4 @@ -pub mod block; +mod block; mod errors; mod ticket; mod tipset; diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index f612be680984..0395cef32108 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -77,7 +77,7 @@ impl Tipset { // sort headers by ticket size // break ticket ties with the header CIDs, which are distinct sorted_headers.sort_by_key(|header| { - let h = header.clone(); + let mut h = header.clone(); (Reverse(h.ticket.sort_key()), h.cid().hash) }); From 8c1cf22c3e53b1be17020683e735540b402844a4 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 2 Dec 2019 16:32:42 -0600 Subject: [PATCH 11/14] Requested changes made * Updated error to handle String * Removed unnecessary import * Removed sort_key method for Ticket --- blockchain/src/blocks/block.rs | 6 +++--- blockchain/src/blocks/errors.rs | 2 +- blockchain/src/blocks/ticket.rs | 6 ------ blockchain/src/blocks/tipset.rs | 24 ++++++++++++++++-------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index 35fdcb345dfd..0888ec1b1d58 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -57,7 +57,7 @@ pub struct BlockHeader { // BLSAggregateSig /// CACHE /// - pub cached_cid: cid::Cid, + pub cached_cid: Cid, pub cached_bytes: u8, } @@ -70,7 +70,7 @@ pub struct Block { impl BlockHeader { /// cid returns the content id of this header - pub fn cid(&mut self) -> cid::Cid { + pub fn cid(&mut self) -> Cid { // TODO // Encode blockheader into cache_bytes // Update codec to use DagCBOR @@ -83,7 +83,7 @@ impl BlockHeader { mh_type: DEFAULT_HASH_FUNCTION, mh_len: 0, }; - let new_cid = cid::Cid::new_from_prefix(&c, &[self.cached_bytes]); + let new_cid = Cid::new_from_prefix(&c, &[self.cached_bytes]); self.cached_cid = new_cid; self.cached_cid.clone() } diff --git a/blockchain/src/blocks/errors.rs b/blockchain/src/blocks/errors.rs index a9f6f93f66e5..e69b4aea93ed 100644 --- a/blockchain/src/blocks/errors.rs +++ b/blockchain/src/blocks/errors.rs @@ -2,7 +2,7 @@ use std::fmt; #[derive(Debug, PartialEq)] pub enum Error { - UndefinedTipSet(&'static str), + UndefinedTipSet(String), NoBlocks, } diff --git a/blockchain/src/blocks/ticket.rs b/blockchain/src/blocks/ticket.rs index 1e6871f79396..35faeb54680c 100644 --- a/blockchain/src/blocks/ticket.rs +++ b/blockchain/src/blocks/ticket.rs @@ -12,9 +12,3 @@ pub struct Ticket { /// A proof output by running a VRF on the VDFResult of the parent ticket pub vrfproof: VRFProofIndex, } - -impl Ticket { - pub fn sort_key(&self) -> Vec { - self.vrfproof.clone() - } -} diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 0395cef32108..777f4fb7cc3f 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -41,31 +41,39 @@ impl Tipset { // Skip redundant check // check height is equal if headers[i].height != headers[0].height { - return Err(Error::UndefinedTipSet("heights are not equal")); + return Err(Error::UndefinedTipSet("heights are not equal".to_string())); } // check parent cids are equal if !headers[i].parents.equals(headers[0].parents.clone()) { - return Err(Error::UndefinedTipSet("parent cids are not equal")); + return Err(Error::UndefinedTipSet( + "parent cids are not equal".to_string(), + )); } // check weights are equal if headers[i].weight != headers[0].weight { - return Err(Error::UndefinedTipSet("weights are not equal")); + return Err(Error::UndefinedTipSet("weights are not equal".to_string())); } // check state_roots are equal if headers[i].state_root != headers[0].state_root.clone() { - return Err(Error::UndefinedTipSet("state_roots are not equal")); + return Err(Error::UndefinedTipSet( + "state_roots are not equal".to_string(), + )); } // check epochs are equal if headers[i].epoch != headers[0].epoch { - return Err(Error::UndefinedTipSet("epochs are not equal")); + return Err(Error::UndefinedTipSet("epochs are not equal".to_string())); } // check message_receipts are equal if headers[i].message_receipts != headers[0].message_receipts.clone() { - return Err(Error::UndefinedTipSet("message_receipts are not equal")); + return Err(Error::UndefinedTipSet( + "message_receipts are not equal".to_string(), + )); } // check miner_addresses are distinct if headers[i].miner_address == headers[0].miner_address.clone() { - return Err(Error::UndefinedTipSet("miner_addresses are not distinct")); + return Err(Error::UndefinedTipSet( + "miner_addresses are not distinct".to_string(), + )); } } // push headers into vec for sorting @@ -78,7 +86,7 @@ impl Tipset { // break ticket ties with the header CIDs, which are distinct sorted_headers.sort_by_key(|header| { let mut h = header.clone(); - (Reverse(h.ticket.sort_key()), h.cid().hash) + (Reverse(h.ticket.vrfproof.clone()), h.cid().hash) }); // TODO From c36e37bba752f6bf72f1455ce06a338d36ac7850 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 2 Dec 2019 17:12:37 -0600 Subject: [PATCH 12/14] Requested changes made * Removed dead code and unused variable tag --- blockchain/src/blocks/ticket.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/blockchain/src/blocks/ticket.rs b/blockchain/src/blocks/ticket.rs index 35faeb54680c..6e2999bc1d8a 100644 --- a/blockchain/src/blocks/ticket.rs +++ b/blockchain/src/blocks/ticket.rs @@ -1,6 +1,3 @@ -#![allow(unused_variables)] -#![allow(dead_code)] - /// VRFProofIndex is the proof output from running a VRF pub type VRFProofIndex = Vec; From e31303b7fa108e5ab4469c7d34d49c407d20e2d3 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Tue, 3 Dec 2019 10:18:24 -0600 Subject: [PATCH 13/14] Remove reverse in sort --- blockchain/src/blocks/block.rs | 3 +-- blockchain/src/blocks/tipset.rs | 37 ++++++++++++++++----------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/blockchain/src/blocks/block.rs b/blockchain/src/blocks/block.rs index 0888ec1b1d58..ee20519a1aa0 100644 --- a/blockchain/src/blocks/block.rs +++ b/blockchain/src/blocks/block.rs @@ -73,13 +73,12 @@ impl BlockHeader { pub fn cid(&mut self) -> Cid { // TODO // Encode blockheader into cache_bytes - // Update codec to use DagCBOR // Change DEFAULT_HASH_FUNCTION to utilize blake2b // // Currently content id for headers will be incomplete until encoding and supporting libraries are completed let c = Prefix { version: Version::V1, - codec: Codec::DagProtobuf, + codec: Codec::DagCBOR, mh_type: DEFAULT_HASH_FUNCTION, mh_len: 0, }; diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 777f4fb7cc3f..3888221c770b 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -5,7 +5,6 @@ use super::block::BlockHeader; use super::errors::Error; use super::ticket::Ticket; use cid::Cid; -use std::cmp::Reverse; /// TipSet is an immutable set of blocks at the same height with the same parent set /// Blocks in a tipset are canonically ordered by ticket size pub struct Tipset { @@ -86,7 +85,7 @@ impl Tipset { // break ticket ties with the header CIDs, which are distinct sorted_headers.sort_by_key(|header| { let mut h = header.clone(); - (Reverse(h.ticket.vrfproof.clone()), h.cid().hash) + (h.ticket.vrfproof.clone(), h.cid().hash) }); // TODO @@ -171,7 +170,7 @@ mod tests { use address::Address; use cid::{Cid, Codec, Version}; - const WEIGHT: u64 = 0; + const WEIGHT: u64 = 1; const EPOCH: u64 = 1; const HEIGHT: u64 = 1; const CACHED_BYTES: u8 = 0; @@ -182,6 +181,16 @@ mod tests { return cid; } + // key_setup returns a vec of 4 distinct CIDs + fn key_setup() -> Vec { + return vec![ + template_key(b"test content"), + template_key(b"awesome test content "), + template_key(b"even better test content"), + template_key(b"the best test content out there"), + ]; + } + // template_header defines a block header used in testing fn template_header(ticket_p: Vec, cid: Cid, timestamp: u64) -> BlockHeader { let cids = key_setup(); @@ -200,7 +209,7 @@ mod tests { ticket: Ticket { vrfproof: ticket_p }, election_proof: vec![], cached_cid: cid, - cached_bytes: 0, + cached_bytes: CACHED_BYTES, } } @@ -211,22 +220,12 @@ mod tests { let data2: Vec = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3, 12, 2]; let cids = key_setup(); return vec![ - template_header(data1.clone(), cids[1].clone(), 1), - template_header(data0.clone(), cids[0].clone(), 2), + template_header(data0.clone(), cids[0].clone(), 1), + template_header(data1.clone(), cids[1].clone(), 2), template_header(data2.clone(), cids[2].clone(), 3), ]; } - // key_setup returns a vec of 4 distinct CIDs - fn key_setup() -> Vec { - return vec![ - template_key(b"test content"), - template_key(b"awesome test content "), - template_key(b"even better test content"), - template_key(b"the best test content out there"), - ]; - } - fn setup() -> Tipset { let headers = header_setup(); return Tipset::new(headers.clone()).expect("tipset is invalid"); @@ -249,7 +248,7 @@ mod tests { fn min_timestamp_test() { let tipset = setup(); let min_time = Tipset::min_timestamp(&tipset).unwrap(); - assert_eq!(min_time, tipset.blocks[1].timestamp); + assert_eq!(min_time, 1); } #[test] @@ -267,7 +266,7 @@ mod tests { #[test] fn height_test() { let tipset = setup(); - assert_eq!(Tipset::height(&tipset), tipset.blocks[1].height); + assert_eq!(Tipset::height(&tipset), 1); } #[test] @@ -279,7 +278,7 @@ mod tests { #[test] fn weight_test() { let tipset = setup(); - assert_eq!(Tipset::weight(&tipset), tipset.blocks[1].weight); + assert_eq!(Tipset::weight(&tipset), 1); } #[test] From f71cd4134a68f7b464cd3fda6205762dd42377b7 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Tue, 3 Dec 2019 10:37:23 -0600 Subject: [PATCH 14/14] improved tests --- blockchain/src/blocks/tipset.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/blockchain/src/blocks/tipset.rs b/blockchain/src/blocks/tipset.rs index 3888221c770b..e33a641786d1 100644 --- a/blockchain/src/blocks/tipset.rs +++ b/blockchain/src/blocks/tipset.rs @@ -240,8 +240,9 @@ mod tests { #[test] fn min_ticket_test() { let tipset = setup(); + let expected_value = vec![1, 4, 3, 6, 1, 1, 2, 2, 4, 5, 3, 12, 2]; let min = Tipset::min_ticket(&tipset).unwrap(); - assert_eq!(min.vrfproof, tipset.blocks[0].ticket.vrfproof); + assert_eq!(min.vrfproof, expected_value); } #[test] @@ -272,7 +273,13 @@ mod tests { #[test] fn parents_test() { let tipset = setup(); - assert_eq!(Tipset::parents(&tipset), tipset.blocks[1].parents); + let expected_value = template_key(b"the best test content out there"); + assert_eq!( + Tipset::parents(&tipset), + TipSetKeys { + cids: vec!(expected_value) + } + ); } #[test]