Преглед изворни кода

blockchain/Block: refactored in preparation for mining integration

aggstam пре 2 година
родитељ
комит
e5081d55ce

+ 10 - 10
bin/darkfid2/src/tests/harness.rs

@@ -165,7 +165,7 @@ impl Harness {
         previous: &BlockInfo,
         slots_count: usize,
     ) -> Result<BlockInfo> {
-        let previous_hash = previous.blockhash();
+        let previous_hash = previous.hash()?;
 
         // Generate empty slots
         let mut slots = Vec::with_capacity(slots_count);
@@ -200,17 +200,17 @@ impl Harness {
             previous.header.epoch,
             slots.last().unwrap().id,
             timestamp,
-            previous.header.root,
+            previous.header.nonce,
         );
 
-        // Generate block
-        let block = BlockInfo::new(
-            header,
-            vec![Transaction::default()],
-            previous.signature,
-            previous.eta,
-            slots,
-        );
+        // Generate the block
+        let mut block = BlockInfo::new_empty(header, slots);
+
+        // Add transactions to the block
+        block.append_txs(vec![Transaction::default()])?;
+
+        // Attach signature
+        block.signature = previous.signature;
 
         Ok(block)
     }

+ 59 - 29
src/blockchain/block_store.rs

@@ -19,12 +19,12 @@
 use darkfi_sdk::{
     blockchain::Slot,
     crypto::schnorr::Signature,
-    pasta::{group::ff::Field, pallas},
+    pasta::{group::ff::FromUniformBytes, pallas},
 };
 #[cfg(feature = "async-serial")]
 use darkfi_serial::async_trait;
 
-use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
+use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable};
 
 use crate::{tx::Transaction, Error, Result};
 
@@ -33,7 +33,7 @@ use super::{parse_record, parse_u64_key_record, Header, SledDbOverlayPtr};
 /// Block version number
 pub const BLOCK_VERSION: u8 = 1;
 
-/// This struct represents a tuple of the form (`header`, `txs`, `signature`, `eta`).
+/// This struct represents a tuple of the form (`header`, `txs`, `signature`).
 /// The header and transactions are stored as hashes, serving as pointers to the actual data
 /// in the sled database.
 /// NOTE: This struct fields are considered final, as it represents a blockchain block.
@@ -45,23 +45,18 @@ pub struct Block {
     pub txs: Vec<blake3::Hash>,
     /// Block producer signature
     pub signature: Signature,
-    /// Block producer ETA
-    pub eta: pallas::Base,
 }
 
 impl Block {
-    pub fn new(
-        header: blake3::Hash,
-        txs: Vec<blake3::Hash>,
-        signature: Signature,
-        eta: pallas::Base,
-    ) -> Self {
-        Self { header, txs, signature, eta }
+    pub fn new(header: blake3::Hash, txs: Vec<blake3::Hash>, signature: Signature) -> Self {
+        Self { header, txs, signature }
     }
 
-    /// Calculate the block hash
-    pub fn blockhash(&self) -> blake3::Hash {
-        blake3::hash(&serialize(self))
+    /// Compute the block's blockchain hash
+    pub fn hash(&self) -> Result<blake3::Hash> {
+        let mut hasher = blake3::Hasher::new();
+        self.encode(&mut hasher)?;
+        Ok(hasher.finalize())
     }
 }
 
@@ -77,8 +72,6 @@ pub struct BlockInfo {
     pub txs: Vec<Transaction>,
     /// Block producer signature
     pub signature: Signature,
-    /// Block producer ETA
-    pub eta: pallas::Base,
     /// Slots payload
     pub slots: Vec<Slot>,
 }
@@ -90,7 +83,6 @@ impl Default for BlockInfo {
             header: Header::default(),
             txs: vec![Transaction::default()],
             signature: Signature::dummy(),
-            eta: pallas::Base::ZERO,
             slots: vec![Slot::default()],
         }
     }
@@ -101,28 +93,66 @@ impl BlockInfo {
         header: Header,
         txs: Vec<Transaction>,
         signature: Signature,
-        eta: pallas::Base,
         slots: Vec<Slot>,
     ) -> Self {
-        Self { header, txs, signature, eta, slots }
+        Self { header, txs, signature, slots }
     }
 
-    /// Calculate the block hash
-    pub fn blockhash(&self) -> blake3::Hash {
+    /// Generate an empty block for provided Header
+    /// and slots vector. Transactions and the producer
+    /// signature must be added after.
+    pub fn new_empty(header: Header, slots: Vec<Slot>) -> Self {
+        let txs = vec![];
+        let signature = Signature::dummy();
+        Self { header, txs, signature, slots }
+    }
+
+    /// Compute the block's blockchain hash
+    pub fn hash(&self) -> Result<blake3::Hash> {
         let block: Block = self.clone().into();
-        block.blockhash()
+        block.hash()
+    }
+
+    /// Compute the block's hash used for mining
+    pub fn mining_hash(&self) -> Result<blake3::Hash> {
+        let mut hasher = blake3::Hasher::new();
+
+        let mut len: usize = 0;
+        len += self.header.encode(&mut hasher)?;
+        len += self.header.tree.root(0).unwrap().encode(&mut hasher)?;
+        len += self.txs.len().encode(&mut hasher)?;
+        len.encode(&mut hasher)?;
+
+        Ok(hasher.finalize())
+    }
+
+    /// Append a transaction to the block. Also adds it to the Merkle tree.
+    pub fn append_tx(&mut self, tx: Transaction) -> Result<()> {
+        let mut buf = [0u8; 64];
+        buf[..blake3::OUT_LEN].copy_from_slice(tx.hash()?.as_bytes());
+        let leaf = pallas::Base::from_uniform_bytes(&buf);
+
+        self.header.tree.append(leaf.into());
+        self.txs.push(tx);
+
+        Ok(())
+    }
+
+    /// Append a vector of transactions to the block. Also adds them to the
+    /// Merkle tree.
+    pub fn append_txs(&mut self, txs: Vec<Transaction>) -> Result<()> {
+        for tx in txs {
+            self.append_tx(tx)?;
+        }
+
+        Ok(())
     }
 }
 
 impl From<BlockInfo> for Block {
     fn from(block_info: BlockInfo) -> Self {
         let txs = block_info.txs.iter().map(|x| blake3::hash(&serialize(x))).collect();
-        Self {
-            header: block_info.header.headerhash().unwrap(),
-            txs,
-            signature: block_info.signature,
-            eta: block_info.eta,
-        }
+        Self { header: block_info.header.hash().unwrap(), txs, signature: block_info.signature }
     }
 }
 

+ 21 - 13
src/blockchain/header_store.rs

@@ -16,7 +16,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi_sdk::crypto::{MerkleNode, MerkleTree};
+use darkfi_sdk::{
+    crypto::MerkleTree,
+    pasta::{group::ff::Field, pallas},
+};
 
 #[cfg(feature = "async-serial")]
 use darkfi_serial::async_trait;
@@ -26,37 +29,42 @@ use crate::{util::time::Timestamp, Error, Result};
 
 use super::{block_store::BLOCK_VERSION, parse_record, SledDbOverlayPtr};
 
-/// This struct represents a tuple of the form (version, previous, epoch, slot, timestamp, merkle_root).
+/// This struct represents a tuple of the form (version, previous, epoch, height, timestamp, nonce, merkle_root).
 #[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
 pub struct Header {
     /// Block version
     pub version: u8,
     /// Previous block hash
     pub previous: blake3::Hash,
-    /// Epoch
+    /// Epoch number
     pub epoch: u64,
-    /// Slot UID
-    pub slot: u64,
+    /// Block/Slot height
+    pub height: u64,
     /// Block creation timestamp
     pub timestamp: Timestamp,
-    /// Root of the transaction hashes merkle tree
-    pub root: MerkleNode,
+    /// The block's nonce.
+    /// In PoW, this value changes arbitrarily with mining.
+    /// In PoS, we can use this value as our block producer ETA.
+    pub nonce: pallas::Base,
+    /// Merkle tree of the transactions contained in this block
+    pub tree: MerkleTree,
 }
 
 impl Header {
     pub fn new(
         previous: blake3::Hash,
         epoch: u64,
-        slot: u64,
+        height: u64,
         timestamp: Timestamp,
-        root: MerkleNode,
+        nonce: pallas::Base,
     ) -> Self {
         let version = BLOCK_VERSION;
-        Self { version, previous, epoch, slot, timestamp, root }
+        let tree = MerkleTree::new(1);
+        Self { version, previous, epoch, height, timestamp, nonce, tree }
     }
 
-    /// Calculate the header hash
-    pub fn headerhash(&self) -> Result<blake3::Hash> {
+    /// Compute the header's hash
+    pub fn hash(&self) -> Result<blake3::Hash> {
         let mut hasher = blake3::Hasher::new();
         self.encode(&mut hasher)?;
         Ok(hasher.finalize())
@@ -71,7 +79,7 @@ impl Default for Header {
             0,
             0,
             Timestamp::current_time(),
-            MerkleTree::new(100).root(0).unwrap(),
+            pallas::Base::ZERO,
         )
     }
 }

+ 11 - 10
src/blockchain/mod.rs

@@ -134,7 +134,8 @@ impl Blockchain {
         batches.push(bocks_batch);
 
         // Store block order
-        let blocks_order_batch = self.order.insert_batch(&[block.header.slot], &block_hash_vec)?;
+        let blocks_order_batch =
+            self.order.insert_batch(&[block.header.height], &block_hash_vec)?;
         trees.push(self.order.0.clone());
         batches.push(blocks_order_batch);
 
@@ -160,7 +161,7 @@ impl Blockchain {
 
     /// Check if the given [`BlockInfo`] is in the database and all trees.
     pub fn has_block(&self, block: &BlockInfo) -> Result<bool> {
-        let blockhash = match self.order.get(&[block.header.slot], true) {
+        let blockhash = match self.order.get(&[block.header.height], true) {
             Ok(v) => v[0].unwrap(),
             Err(_) => return Ok(false),
         };
@@ -191,7 +192,7 @@ impl Blockchain {
         }
 
         // Check provided info produces the same hash
-        Ok(blockhash == block.blockhash())
+        Ok(blockhash == block.hash()?)
     }
 
     /// Retrieve [`BlockInfo`]s by given hashes. Fails if any of them is not found.
@@ -218,13 +219,13 @@ impl Blockchain {
             // Retrieve extra stuff based on block version
             let mut block_slots = vec![];
             if header.version > 0 {
-                let slots = self.blocks_slots.get(&[block.blockhash()], true)?;
+                let slots = self.blocks_slots.get(&[block.hash()?], true)?;
                 let slots = slots[0].clone().unwrap();
                 let slots = self.slots.get(&slots, true)?;
                 block_slots = slots.iter().map(|x| x.clone().unwrap()).collect();
             }
 
-            let info = BlockInfo::new(header, txs, block.signature, block.eta, block_slots);
+            let info = BlockInfo::new(header, txs, block.signature, block_slots);
             ret.push(info);
         }
 
@@ -488,7 +489,7 @@ impl BlockchainOverlay {
         let block_hash_vec = [block_hash];
 
         // Store block order
-        self.order.insert(&[block.header.slot], &block_hash_vec)?;
+        self.order.insert(&[block.header.height], &block_hash_vec)?;
 
         // Store extra stuff based on block version
         if block.header.version > 0 {
@@ -505,7 +506,7 @@ impl BlockchainOverlay {
 
     /// Check if the given [`BlockInfo`] is in the database and all trees.
     pub fn has_block(&self, block: &BlockInfo) -> Result<bool> {
-        let blockhash = match self.order.get(&[block.header.slot], true) {
+        let blockhash = match self.order.get(&[block.header.height], true) {
             Ok(v) => v[0].unwrap(),
             Err(_) => return Ok(false),
         };
@@ -536,7 +537,7 @@ impl BlockchainOverlay {
         }
 
         // Check provided info produces the same hash
-        Ok(blockhash == block.blockhash())
+        Ok(blockhash == block.hash()?)
     }
 
     /// Retrieve [`BlockInfo`]s by given hashes. Fails if any of them is not found.
@@ -563,13 +564,13 @@ impl BlockchainOverlay {
             // Retrieve extra stuff based on block version
             let mut block_slots = vec![];
             if header.version > 0 {
-                let slots = self.blocks_slots.get(&[block.blockhash()], true)?;
+                let slots = self.blocks_slots.get(&[block.hash()?], true)?;
                 let slots = slots[0].clone().unwrap();
                 let slots = self.slots.get(&slots, true)?;
                 block_slots = slots.iter().map(|x| x.clone().unwrap()).collect();
             }
 
-            let info = BlockInfo::new(header, txs, block.signature, block.eta, block_slots);
+            let info = BlockInfo::new(header, txs, block.signature, block_slots);
             ret.push(info);
         }
 

+ 6 - 6
src/consensus/validator.rs

@@ -789,7 +789,7 @@ impl ValidatorState {
             // TODO: FIXME: The state transitions have already been written, they have to be in memory
             //              until this point.
             info!(target: "consensus::validator", "Applying state transition for finalized block");
-            match self.verify_transactions(&proposal.txs, proposal.header.slot, true).await {
+            match self.verify_transactions(&proposal.txs, proposal.header.height, true).await {
                 Ok(erroneous_txs) => {
                     if !erroneous_txs.is_empty() {
                         error!(target: "consensus::validator", "Finalized block contains erroneous transactions");
@@ -871,7 +871,7 @@ impl ValidatorState {
         info!(target: "consensus::validator", "receive_blocks(): Starting state transition validations");
 
         for block in blocks {
-            match self.verify_transactions(&block.txs, block.header.slot, true).await {
+            match self.verify_transactions(&block.txs, block.header.height, true).await {
                 Ok(erroneous_txs) => {
                     if !erroneous_txs.is_empty() {
                         error!(target: "consensus::validator", "receive_blocks(): Block contains erroneous transactions");
@@ -894,8 +894,8 @@ impl ValidatorState {
     /// Validate and append to canonical state received finalized block.
     /// Returns boolean flag indicating already existing block.
     pub async fn receive_finalized_block(&mut self, block: BlockInfo) -> Result<bool> {
-        if block.header.slot > self.consensus.time_keeper.current_slot() {
-            warn!(target: "consensus::validator", "receive_finalized_block(): Ignoring future block: {}", block.header.slot);
+        if block.header.height > self.consensus.time_keeper.current_slot() {
+            warn!(target: "consensus::validator", "receive_finalized_block(): Ignoring future block: {}", block.header.height);
             return Ok(false)
         }
         match self.blockchain.has_block(&block) {
@@ -934,8 +934,8 @@ impl ValidatorState {
     pub async fn receive_sync_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
         let mut new_blocks = vec![];
         for block in blocks {
-            if block.header.slot > self.consensus.time_keeper.current_slot() {
-                warn!(target: "consensus::validator", "receive_sync_blocks(): Ignoring future block: {}", block.header.slot);
+            if block.header.height > self.consensus.time_keeper.current_slot() {
+                warn!(target: "consensus::validator", "receive_sync_blocks(): Ignoring future block: {}", block.header.height);
                 continue
             }
             match self.blockchain.has_block(block) {

+ 4 - 4
src/contract/consensus/tests/stake_unstake.rs

@@ -61,7 +61,7 @@ fn consensus_contract_stake_unstake() -> Result<()> {
 
         // Now Alice can stake her owncoin
         let alice_staked_oc =
-            th.execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_oc, 21).await?;
+            th.execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_oc, 28).await?;
 
         // We progress after grace period
         current_slot += (calculate_grace_period() * EPOCH_LENGTH) + EPOCH_LENGTH;
@@ -98,7 +98,7 @@ fn consensus_contract_stake_unstake() -> Result<()> {
 
         // Now Alice can stake her unstaked owncoin again to try some mallicious cases
         let alice_staked_oc = th
-            .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 121)
+            .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 92)
             .await?;
 
         // Alice tries to stake her coin again
@@ -106,7 +106,7 @@ fn consensus_contract_stake_unstake() -> Result<()> {
         info!(target: "consensus", "[Malicious] Checking staking coin again");
         info!(target: "consensus", "[Malicious] ===========================");
         let (stake_tx, _, _) = th
-            .stake(&Holder::Alice, current_slot, &alice_unstaked_oc, pallas::Base::from(121))
+            .stake(&Holder::Alice, current_slot, &alice_unstaked_oc, pallas::Base::from(92))
             .await?;
         th.execute_erroneous_txs(
             TxAction::ConsensusStake,
@@ -188,7 +188,7 @@ fn consensus_contract_stake_unstake() -> Result<()> {
 
         // Now Alice can stake her unstaked owncoin again
         let alice_staked_oc = th
-            .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 181)
+            .execute_stake(&HOLDERS, &Holder::Alice, current_slot, &alice_unstaked_oc, 77)
             .await?;
 
         // We progress after grace period

+ 1 - 1
src/contract/test-harness/src/consensus_genesis_stake.rs

@@ -62,7 +62,7 @@ impl TestHarness {
             pallas::Scalar::random(&mut OsRng),
             pallas::Base::random(&mut OsRng),
             pallas::Scalar::random(&mut OsRng),
-            pallas::Base::from(4),
+            pallas::Base::from(41),
         )?;
 
         let (genesis_stake_params, genesis_stake_proofs) =

+ 1 - 1
src/contract/test-harness/src/lib.rs

@@ -273,7 +273,7 @@ impl TestHarness {
             holders,
             proving_keys,
             tx_action_benchmarks,
-            genesis_block: genesis_block.blockhash(),
+            genesis_block: genesis_block.hash()?,
         })
     }
 

+ 23 - 31
src/validator/consensus.rs

@@ -18,7 +18,7 @@
 
 use darkfi_sdk::{
     blockchain::{expected_reward, PidOutput, PreviousSlot, Slot},
-    crypto::{schnorr::SchnorrSecret, MerkleNode, MerkleTree, SecretKey},
+    crypto::{schnorr::SchnorrSecret, SecretKey},
     pasta::{group::ff::PrimeField, pallas},
 };
 use darkfi_serial::{async_trait, serialize, SerialDecodable, SerialEncodable};
@@ -94,7 +94,7 @@ impl Consensus {
 
         for fork in &self.forks {
             let last_proposal = fork.last_proposal()?;
-            if last_proposal.block.header.slot == slot {
+            if last_proposal.block.header.height == slot {
                 producers += 1;
             }
             last_hashes.push(last_proposal.hash);
@@ -132,16 +132,6 @@ impl Consensus {
         let mut unproposed_txs = fork.unproposed_txs(&self.blockchain, &time_keeper).await?;
         unproposed_txs.push(proposal_tx);
 
-        // Calculate transactions tree root
-        let mut tree = MerkleTree::new(100);
-        // The following is pretty weird, so something better should be done.
-        for tx in &unproposed_txs {
-            let mut hash = [0_u8; 32];
-            hash[0..31].copy_from_slice(&blake3::hash(&serialize(tx)).as_bytes()[0..31]);
-            tree.append(MerkleNode::from(pallas::Base::from_repr(hash).unwrap()));
-        }
-        let root = tree.root(0).unwrap();
-
         // Grab forks' last block proposal(previous)
         let previous = fork.last_proposal()?;
 
@@ -149,23 +139,25 @@ impl Consensus {
         let slot = fork.slots.last().unwrap();
         // TODO: verify if header timestamp should be blockchain or system timestamp
         let header = Header::new(
-            previous.block.blockhash(),
+            previous.block.hash()?,
             time_keeper.slot_epoch(slot.id),
             slot.id,
             Timestamp::current_time(),
-            root,
+            slot.last_eta,
         );
 
+        // Generate the block
+        let mut block = BlockInfo::new_empty(header, fork.slots.clone());
+
+        // Add transactions to the block
+        block.append_txs(unproposed_txs)?;
+
         // TODO: sign more stuff?
         // Sign block header using provided secret key
-        let signature = secret_key.sign(&mut OsRng, &header.headerhash()?.as_bytes()[..]);
-
-        // Generate the block and its proposal
-        let block =
-            BlockInfo::new(header, unproposed_txs, signature, slot.last_eta, fork.slots.clone());
-        let proposal = Proposal::new(block);
+        block.signature = secret_key.sign(&mut OsRng, &block.header.hash()?.as_bytes()[..]);
 
-        Ok(proposal)
+        // Generate the block proposal from the block
+        Proposal::new(block)
     }
 
     /// Given a proposal, the node verifys it and finds which fork it extends.
@@ -193,12 +185,12 @@ impl Consensus {
         let hdr = &proposal.block.header;
 
         // Ignore proposal if not for current slot (2)
-        if hdr.slot != time_keeper.verifying_slot {
+        if hdr.height != time_keeper.verifying_slot {
             return Err(Error::ProposalNotForCurrentSlotError)
         }
 
         // Check if proposal hash matches actual one (3)
-        let proposal_hash = proposal.block.blockhash();
+        let proposal_hash = proposal.block.hash()?;
         if proposal.hash != proposal_hash {
             warn!(
                 target: "validator::consensus::append_proposal", "Received proposal contains mismatched hashes: {} - {}",
@@ -302,7 +294,7 @@ impl Consensus {
             // Check if we extend canonical
             let (last_slot, last_block) = self.blockchain.last()?;
             if proposal.block.header.previous != last_block ||
-                proposal.block.header.slot <= last_slot
+                proposal.block.header.height <= last_slot
             {
                 return Err(Error::ExtendedChainIndexNotFound)
             }
@@ -333,7 +325,7 @@ impl Consensus {
         // Validate and insert each block
         for block in blocks {
             // Use block slot in time keeper
-            time_keeper.verifying_slot = block.header.slot;
+            time_keeper.verifying_slot = block.header.height;
 
             // Retrieve expected reward
             let expected_reward = expected_reward(time_keeper.verifying_slot);
@@ -352,7 +344,7 @@ impl Consensus {
             {
                 error!(target: "validator::consensus::find_extended_fork_overlay", "Erroneous block found in set");
                 fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
-                return Err(Error::BlockIsInvalid(block.blockhash().to_string()))
+                return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
             };
 
             // Use last inserted block as next iteration previous
@@ -432,9 +424,9 @@ pub struct Proposal {
 }
 
 impl Proposal {
-    pub fn new(block: BlockInfo) -> Self {
-        let hash = block.blockhash();
-        Self { hash, block }
+    pub fn new(block: BlockInfo) -> Result<Self> {
+        let hash = block.hash()?;
+        Ok(Self { hash, block })
     }
 }
 
@@ -477,7 +469,7 @@ impl Fork {
                 .clone()
         };
 
-        Ok(Proposal::new(block))
+        Proposal::new(block)
     }
 
     /// Utility function to extract leader selection lottery randomness(eta),
@@ -485,7 +477,7 @@ impl Fork {
     fn get_last_eta(&self) -> Result<pallas::Base> {
         // Retrieve last block(or proposal) hash
         let hash = if self.proposals.is_empty() {
-            self.overlay.lock().unwrap().last_block()?.blockhash()
+            self.overlay.lock().unwrap().last_block()?.hash()?
         } else {
             *self.proposals.last().unwrap()
         };

+ 7 - 7
src/validator/mod.rs

@@ -260,7 +260,7 @@ impl Validator {
     /// The node retrieves a block and tries to add it if it doesn't
     /// already exists.
     pub async fn append_block(&mut self, block: &BlockInfo) -> Result<()> {
-        let block_hash = block.blockhash().to_string();
+        let block_hash = block.hash()?.to_string();
 
         // Check if block already exists
         if self.blockchain.has_block(block)? {
@@ -301,8 +301,8 @@ impl Validator {
 
         // Validate and insert each block
         for block in blocks {
-            // Use block slot in time keeper
-            time_keeper.verifying_slot = block.header.slot;
+            // Use block height in time keeper
+            time_keeper.verifying_slot = block.header.height;
 
             // Retrieve expected reward
             let expected_reward = expected_reward(time_keeper.verifying_slot);
@@ -321,7 +321,7 @@ impl Validator {
             {
                 error!(target: "validator::add_blocks", "Erroneous block found in set");
                 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
-                return Err(Error::BlockIsInvalid(block.blockhash().to_string()))
+                return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
             };
 
             // Store block transactions
@@ -477,8 +477,8 @@ impl Validator {
 
         // Validate and insert each block
         for block in &blocks[1..] {
-            // Use block slot in time keeper
-            time_keeper.verifying_slot = block.header.slot;
+            // Use block height in time keeper
+            time_keeper.verifying_slot = block.header.height;
 
             // Retrieve expected reward
             let expected_reward = expected_reward(time_keeper.verifying_slot);
@@ -497,7 +497,7 @@ impl Validator {
             {
                 error!(target: "validator::validate_blockchain", "Erroneous block found in set");
                 overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
-                return Err(Error::BlockIsInvalid(block.blockhash().to_string()))
+                return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
             };
 
             // Use last inserted block as next iteration previous

+ 9 - 9
src/validator/validation.rs

@@ -35,8 +35,8 @@ use crate::{
 ///     5. Slot is the same as the slots vector last slot id
 /// Additional validity rules can be applied.
 pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, expected_reward: u64) -> Result<()> {
-    let error = Err(Error::BlockIsInvalid(block.blockhash().to_string()));
-    let previous_hash = previous.blockhash();
+    let error = Err(Error::BlockIsInvalid(block.hash()?.to_string()));
+    let previous_hash = previous.hash()?;
 
     // Check previous hash (1)
     if block.header.previous != previous_hash {
@@ -48,8 +48,8 @@ pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, expected_reward:
         return error
     }
 
-    // Check slots are incremental (3)
-    if block.header.slot <= previous.header.slot {
+    // Check heights are incremental (3)
+    if block.header.height <= previous.header.height {
         return error
     }
 
@@ -73,7 +73,7 @@ pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, expected_reward:
                     previous_slot,
                     &previous_hash,
                     &previous.header.previous,
-                    &previous.eta,
+                    &previous.header.nonce,
                     0,
                 )?;
                 previous_slot = slot;
@@ -85,12 +85,12 @@ pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, expected_reward:
             previous_slot,
             &previous_hash,
             &previous.header.previous,
-            &previous.eta,
+            &previous.header.nonce,
             expected_reward,
         )?;
 
-        // Check block slot is the last slot id (5)
-        if block.slots.last().unwrap().id != block.header.slot {
+        // Check block height is the last slot id (5)
+        if block.slots.last().unwrap().id != block.header.height {
             return error
         }
     }
@@ -172,7 +172,7 @@ pub fn validate_blockchain(blockchain: &Blockchain) -> Result<()> {
     let blocks = blockchain.order.get_all()?;
     for (index, block) in blocks[1..].iter().enumerate() {
         let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?;
-        let expected_reward = expected_reward(full_blocks[1].header.slot);
+        let expected_reward = expected_reward(full_blocks[1].header.height);
         validate_block(&full_blocks[1], &full_blocks[0], expected_reward)?;
     }
 

+ 6 - 6
src/validator/verification.rs

@@ -43,7 +43,7 @@ pub async fn verify_genesis_block(
     block: &BlockInfo,
     genesis_txs_total: u64,
 ) -> Result<()> {
-    let block_hash = block.blockhash().to_string();
+    let block_hash = block.hash()?.to_string();
     debug!(target: "validator::verification::verify_genesis_block", "Validating genesis block {}", block_hash);
 
     // Check if block already exists
@@ -51,8 +51,8 @@ pub async fn verify_genesis_block(
         return Err(Error::BlockAlreadyExists(block_hash))
     }
 
-    // Block slot must be the same as the time keeper verifying slot
-    if block.header.slot != time_keeper.verifying_slot {
+    // Block height must be the same as the time keeper verifying slot
+    if block.header.height != time_keeper.verifying_slot {
         return Err(Error::VerifyingSlotMissmatch())
     }
 
@@ -115,7 +115,7 @@ pub async fn verify_block(
     expected_reward: u64,
     testing_mode: bool,
 ) -> Result<()> {
-    let block_hash = block.blockhash().to_string();
+    let block_hash = block.hash()?.to_string();
     debug!(target: "validator::verification::verify_block", "Validating block {}", block_hash);
 
     // Check if block already exists
@@ -123,8 +123,8 @@ pub async fn verify_block(
         return Err(Error::BlockAlreadyExists(block_hash))
     }
 
-    // Block slot must be the same as the time keeper verifying slot
-    if block.header.slot != time_keeper.verifying_slot {
+    // Block height must be the same as the time keeper verifying slot
+    if block.header.height != time_keeper.verifying_slot {
         return Err(Error::VerifyingSlotMissmatch())
     }
 

+ 18 - 9
tests/blockchain.rs

@@ -55,8 +55,8 @@ impl Harness {
         Ok(())
     }
 
-    fn generate_next_block(&self, previous: &BlockInfo) -> BlockInfo {
-        let previous_hash = previous.blockhash();
+    fn generate_next_block(&self, previous: &BlockInfo) -> Result<BlockInfo> {
+        let previous_hash = previous.hash()?;
 
         // Generate slot
         let previous_slot = previous.slots.last().unwrap();
@@ -80,9 +80,18 @@ impl Harness {
 
         // Generate header
         let header =
-            Header::new(previous_hash, previous.header.epoch, id, timestamp, previous.header.root);
+            Header::new(previous_hash, previous.header.epoch, id, timestamp, previous.header.nonce);
 
-        BlockInfo::new(header, previous.txs.clone(), previous.signature, previous.eta, vec![slot])
+        // Generate the block
+        let mut block = BlockInfo::new_empty(header, vec![slot]);
+
+        // Add transactions to the block
+        block.append_txs(previous.txs.clone())?;
+
+        // Attach signature
+        block.signature = previous.signature;
+
+        Ok(block)
     }
 
     fn add_blocks(&self, blocks: &[BlockInfo]) -> Result<()> {
@@ -105,13 +114,13 @@ impl Harness {
         for block in blocks {
             // Check if block already exists
             if lock.has_block(block)? {
-                return Err(Error::BlockAlreadyExists(block.blockhash().to_string()))
+                return Err(Error::BlockAlreadyExists(block.hash()?.to_string()))
             }
 
             // This will be true for every insert, apart from genesis
             if let Some(p) = previous {
                 // Retrieve expected reward
-                let expected_reward = expected_reward(block.header.slot);
+                let expected_reward = expected_reward(block.header.height);
 
                 // Validate block
                 validate_block(block, &p, expected_reward)?;
@@ -146,13 +155,13 @@ fn blockchain_add_blocks() -> Result<()> {
         let genesis_block = BlockInfo::default();
         blocks.push(genesis_block.clone());
 
-        let block = th.generate_next_block(&genesis_block);
+        let block = th.generate_next_block(&genesis_block)?;
         blocks.push(block.clone());
 
-        let block = th.generate_next_block(&block);
+        let block = th.generate_next_block(&block)?;
         blocks.push(block.clone());
 
-        let block = th.generate_next_block(&block);
+        let block = th.generate_next_block(&block)?;
         blocks.push(block.clone());
 
         th.add_blocks(&blocks)?;