Support for genesis block that includes a coinbase (#2079)

* 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
This commit is contained in:
Ignotus Peverell 2018-12-06 15:38:15 -08:00 committed by GitHub
parent 7ff323c882
commit ef55b35416
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 83 additions and 18 deletions

View file

@ -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;
@ -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)?;
@ -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());
}

View file

@ -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)?;
@ -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))

View file

@ -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};
@ -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();

View file

@ -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.
@ -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.
{
@ -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,
@ -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

View file

@ -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)?;