Skip to content

Commit

Permalink
Support for genesis block that includes a coinbase (mimblewimble#2079)
Browse files Browse the repository at this point in the history
* Chain init now handles genesis body properly, related unit test creating the genesis with reward
* Avoid making block body public by adding a with_reward method
* apply_block in all genesis cases works
  • Loading branch information
ignopeverell authored Dec 6, 2018
1 parent 7ff323c commit ef55b35
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 18 deletions.
27 changes: 20 additions & 7 deletions chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use core::core::hash::{Hash, Hashed, ZERO_HASH};
use core::core::merkle_proof::MerkleProof;
use core::core::verifier_cache::VerifierCache;
use core::core::{
Block, BlockHeader, BlockSums, Output, OutputIdentifier, Transaction, TxKernelEntry,
Block, BlockHeader, BlockSums, Committed, Output, OutputIdentifier, Transaction, TxKernelEntry,
};
use core::global;
use core::pow;
Expand Down Expand Up @@ -1231,6 +1231,8 @@ fn setup_head(
}
}
Err(NotFoundErr(_)) => {
let mut sums = BlockSums::default();

// Save the genesis header with a "zero" header_root.
// We will update this later once we have the correct header_root.
batch.save_block_header(&genesis.header)?;
Expand All @@ -1239,16 +1241,27 @@ fn setup_head(
let tip = Tip::from_header(&genesis.header);
batch.save_head(&tip)?;

// Initialize our header MM with the genesis header.
txhashset::header_extending(txhashset, &mut batch, |extension| {
extension.apply_header(&genesis.header)?;
batch.save_block_header(&genesis.header)?;

if genesis.kernels().len() > 0 {
let (utxo_sum, kernel_sum) = (sums, &genesis as &Committed).verify_kernel_sums(
genesis.header.overage(),
genesis.header.total_kernel_offset(),
)?;
sums = BlockSums {
utxo_sum,
kernel_sum,
};
}
txhashset::extending(txhashset, &mut batch, |extension| {
extension.apply_block(&genesis)?;
extension.validate_roots()?;
extension.validate_sizes()?;
Ok(())
})?;

batch.save_block_header(&genesis.header)?;

// Save the block_sums to the db for use later.
batch.save_block_sums(&genesis.hash(), &BlockSums::default())?;
batch.save_block_sums(&genesis.hash(), &sums)?;

info!("init: saved genesis: {:?}", genesis.hash());
}
Expand Down
5 changes: 3 additions & 2 deletions chain/src/txhashset/txhashset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,7 @@ impl<'a> Extension<'a> {
/// Get the header at the specified height based on the current state of the extension.
/// Derives the MMR pos from the height (insertion index) and retrieves the header hash.
/// Looks the header up in the db by hash.
pub fn get_header_by_height(&mut self, height: u64) -> Result<BlockHeader, Error> {
pub fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, Error> {
let pos = pmmr::insertion_to_pmmr_index(height + 1);
if let Some(hash) = self.get_header_hash(pos) {
let header = self.batch.get_block_header(&hash)?;
Expand Down Expand Up @@ -1227,8 +1227,9 @@ impl<'a> Extension<'a> {
/// from the respective MMRs.
/// For a significantly faster way of validating full kernel sums see BlockSums.
pub fn validate_kernel_sums(&self) -> Result<((Commitment, Commitment)), Error> {
let genesis = self.get_header_by_height(0)?;
let (utxo_sum, kernel_sum) = self.verify_kernel_sums(
self.header.total_overage(),
self.header.total_overage(genesis.kernel_mmr_size > 0),
self.header.total_kernel_offset(),
)?;
Ok((utxo_sum, kernel_sum))
Expand Down
46 changes: 42 additions & 4 deletions chain/tests/mine_simple_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ use chain::Chain;
use core::core::hash::Hashed;
use core::core::verifier_cache::LruVerifierCache;
use core::core::{Block, BlockHeader, OutputFeatures, OutputIdentifier, Transaction};
use core::genesis;
use core::global::ChainTypes;
use core::libtx::{self, build};
use core::libtx::{self, build, reward};
use core::pow::Difficulty;
use core::{consensus, global, pow};
use keychain::{ExtKeychain, ExtKeychainPath, Keychain};
Expand Down Expand Up @@ -60,14 +61,51 @@ fn setup(dir_name: &str, genesis: Block) -> Chain {
#[test]
fn mine_empty_chain() {
global::set_mining_mode(ChainTypes::AutomatedTesting);
let chain = setup(".grin", pow::mine_genesis_block().unwrap());
let keychain = ExtKeychain::from_random_seed().unwrap();
let keychain = keychain::ExtKeychain::from_random_seed().unwrap();
mine_some_on_top(".grin", pow::mine_genesis_block().unwrap(), &keychain);
}

#[test]
fn mine_genesis_reward_chain() {
global::set_mining_mode(ChainTypes::AutomatedTesting);

// add coinbase data from the dev genesis block
let mut genesis = genesis::genesis_dev();
let keychain = keychain::ExtKeychain::from_random_seed().unwrap();
let key_id = keychain::ExtKeychain::derive_key_id(0, 1, 0, 0, 0);
let reward = reward::output(&keychain, &key_id, 0, 0).unwrap();
genesis = genesis.with_reward(reward.0, reward.1);

{
// setup a tmp chain to hande tx hashsets
let tmp_chain = setup(".grin.tmp", pow::mine_genesis_block().unwrap());
tmp_chain.set_txhashset_roots(&mut genesis).unwrap();
genesis.header.output_mmr_size = 1;
genesis.header.kernel_mmr_size = 1;
}

// get a valid PoW
pow::pow_size(
&mut genesis.header,
Difficulty::unit(),
global::proofsize(),
global::min_edge_bits(),
).unwrap();

mine_some_on_top(".grin.genesis", genesis, &keychain);
}

fn mine_some_on_top<K>(dir: &str, genesis: Block, keychain: &K)
where
K: Keychain,
{
let chain = setup(dir, genesis);

for n in 1..4 {
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, prev.height).unwrap();
let reward = libtx::reward::output(keychain, &pk, 0, prev.height).unwrap();
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
.unwrap();
Expand Down
21 changes: 17 additions & 4 deletions core/src/core/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,13 @@ impl BlockHeader {

/// The "total overage" to use when verifying the kernel sums for a full
/// chain state. For a full chain state this is 0 - (height * reward).
pub fn total_overage(&self) -> i64 {
((self.height * REWARD) as i64).checked_neg().unwrap_or(0)
pub fn total_overage(&self, genesis_had_reward: bool) -> i64 {
let mut reward_count = self.height;
if genesis_had_reward {
reward_count += 1;
}

((reward_count * REWARD) as i64).checked_neg().unwrap_or(0)
}

/// Total kernel offset for the chain state up to and including this block.
Expand Down Expand Up @@ -358,7 +363,7 @@ impl Block {
reward_output: (Output, TxKernel),
) -> Result<Block, Error> {
let mut block =
Block::with_reward(prev, txs, reward_output.0, reward_output.1, difficulty)?;
Block::from_reward(prev, txs, reward_output.0, reward_output.1, difficulty)?;

// Now set the pow on the header so block hashing works as expected.
{
Expand Down Expand Up @@ -421,7 +426,7 @@ impl Block {
/// Builds a new block ready to mine from the header of the previous block,
/// a vector of transactions and the reward information. Checks
/// that all transactions are valid and calculates the Merkle tree.
pub fn with_reward(
pub fn from_reward(
prev: &BlockHeader,
txs: Vec<Transaction>,
reward_out: Output,
Expand Down Expand Up @@ -461,6 +466,14 @@ impl Block {
}.cut_through()
}

/// Consumes this block and returns a new block with the coinbase output
/// and kernels added
pub fn with_reward(mut self, reward_out: Output, reward_kern: TxKernel) -> Block {
self.body.outputs.push(reward_out);
self.body.kernels.push(reward_kern);
self
}

/// Get inputs
pub fn inputs(&self) -> &Vec<Input> {
&self.body.inputs
Expand Down
2 changes: 1 addition & 1 deletion servers/src/mining/mine_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ fn build_block(
};

let (output, kernel, block_fees) = get_coinbase(wallet_listener_url, block_fees)?;
let mut b = core::Block::with_reward(&head, txs, output, kernel, difficulty.difficulty)?;
let mut b = core::Block::from_reward(&head, txs, output, kernel, difficulty.difficulty)?;

// making sure we're not spending time mining a useless block
b.validate(&head.total_kernel_offset, verifier_cache)?;
Expand Down

0 comments on commit ef55b35

Please sign in to comment.