mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-08 04:11:08 +03:00
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:
parent
7ff323c882
commit
ef55b35416
5 changed files with 83 additions and 18 deletions
|
@ -28,7 +28,7 @@ use core::core::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
use core::core::merkle_proof::MerkleProof;
|
use core::core::merkle_proof::MerkleProof;
|
||||||
use core::core::verifier_cache::VerifierCache;
|
use core::core::verifier_cache::VerifierCache;
|
||||||
use core::core::{
|
use core::core::{
|
||||||
Block, BlockHeader, BlockSums, Output, OutputIdentifier, Transaction, TxKernelEntry,
|
Block, BlockHeader, BlockSums, Committed, Output, OutputIdentifier, Transaction, TxKernelEntry,
|
||||||
};
|
};
|
||||||
use core::global;
|
use core::global;
|
||||||
use core::pow;
|
use core::pow;
|
||||||
|
@ -1231,6 +1231,8 @@ fn setup_head(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(NotFoundErr(_)) => {
|
Err(NotFoundErr(_)) => {
|
||||||
|
let mut sums = BlockSums::default();
|
||||||
|
|
||||||
// Save the genesis header with a "zero" header_root.
|
// Save the genesis header with a "zero" header_root.
|
||||||
// We will update this later once we have the correct header_root.
|
// We will update this later once we have the correct header_root.
|
||||||
batch.save_block_header(&genesis.header)?;
|
batch.save_block_header(&genesis.header)?;
|
||||||
|
@ -1239,16 +1241,27 @@ fn setup_head(
|
||||||
let tip = Tip::from_header(&genesis.header);
|
let tip = Tip::from_header(&genesis.header);
|
||||||
batch.save_head(&tip)?;
|
batch.save_head(&tip)?;
|
||||||
|
|
||||||
// Initialize our header MM with the genesis header.
|
batch.save_block_header(&genesis.header)?;
|
||||||
txhashset::header_extending(txhashset, &mut batch, |extension| {
|
|
||||||
extension.apply_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(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
batch.save_block_header(&genesis.header)?;
|
|
||||||
|
|
||||||
// Save the block_sums to the db for use later.
|
// 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());
|
info!("init: saved genesis: {:?}", genesis.hash());
|
||||||
}
|
}
|
||||||
|
|
|
@ -999,7 +999,7 @@ impl<'a> Extension<'a> {
|
||||||
/// Get the header at the specified height based on the current state of the extension.
|
/// 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.
|
/// Derives the MMR pos from the height (insertion index) and retrieves the header hash.
|
||||||
/// Looks the header up in the db by 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);
|
let pos = pmmr::insertion_to_pmmr_index(height + 1);
|
||||||
if let Some(hash) = self.get_header_hash(pos) {
|
if let Some(hash) = self.get_header_hash(pos) {
|
||||||
let header = self.batch.get_block_header(&hash)?;
|
let header = self.batch.get_block_header(&hash)?;
|
||||||
|
@ -1227,8 +1227,9 @@ impl<'a> Extension<'a> {
|
||||||
/// from the respective MMRs.
|
/// from the respective MMRs.
|
||||||
/// For a significantly faster way of validating full kernel sums see BlockSums.
|
/// For a significantly faster way of validating full kernel sums see BlockSums.
|
||||||
pub fn validate_kernel_sums(&self) -> Result<((Commitment, Commitment)), Error> {
|
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(
|
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(),
|
self.header.total_kernel_offset(),
|
||||||
)?;
|
)?;
|
||||||
Ok((utxo_sum, kernel_sum))
|
Ok((utxo_sum, kernel_sum))
|
||||||
|
|
|
@ -31,8 +31,9 @@ use chain::Chain;
|
||||||
use core::core::hash::Hashed;
|
use core::core::hash::Hashed;
|
||||||
use core::core::verifier_cache::LruVerifierCache;
|
use core::core::verifier_cache::LruVerifierCache;
|
||||||
use core::core::{Block, BlockHeader, OutputFeatures, OutputIdentifier, Transaction};
|
use core::core::{Block, BlockHeader, OutputFeatures, OutputIdentifier, Transaction};
|
||||||
|
use core::genesis;
|
||||||
use core::global::ChainTypes;
|
use core::global::ChainTypes;
|
||||||
use core::libtx::{self, build};
|
use core::libtx::{self, build, reward};
|
||||||
use core::pow::Difficulty;
|
use core::pow::Difficulty;
|
||||||
use core::{consensus, global, pow};
|
use core::{consensus, global, pow};
|
||||||
use keychain::{ExtKeychain, ExtKeychainPath, Keychain};
|
use keychain::{ExtKeychain, ExtKeychainPath, Keychain};
|
||||||
|
@ -60,14 +61,51 @@ fn setup(dir_name: &str, genesis: Block) -> Chain {
|
||||||
#[test]
|
#[test]
|
||||||
fn mine_empty_chain() {
|
fn mine_empty_chain() {
|
||||||
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
||||||
let chain = setup(".grin", pow::mine_genesis_block().unwrap());
|
let keychain = keychain::ExtKeychain::from_random_seed().unwrap();
|
||||||
let 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 {
|
for n in 1..4 {
|
||||||
let prev = chain.head_header().unwrap();
|
let prev = chain.head_header().unwrap();
|
||||||
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter());
|
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 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 =
|
let mut b =
|
||||||
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
|
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -262,8 +262,13 @@ impl BlockHeader {
|
||||||
|
|
||||||
/// The "total overage" to use when verifying the kernel sums for a full
|
/// 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).
|
/// chain state. For a full chain state this is 0 - (height * reward).
|
||||||
pub fn total_overage(&self) -> i64 {
|
pub fn total_overage(&self, genesis_had_reward: bool) -> i64 {
|
||||||
((self.height * REWARD) as i64).checked_neg().unwrap_or(0)
|
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.
|
/// Total kernel offset for the chain state up to and including this block.
|
||||||
|
@ -358,7 +363,7 @@ impl Block {
|
||||||
reward_output: (Output, TxKernel),
|
reward_output: (Output, TxKernel),
|
||||||
) -> Result<Block, Error> {
|
) -> Result<Block, Error> {
|
||||||
let mut block =
|
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.
|
// 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,
|
/// Builds a new block ready to mine from the header of the previous block,
|
||||||
/// a vector of transactions and the reward information. Checks
|
/// a vector of transactions and the reward information. Checks
|
||||||
/// that all transactions are valid and calculates the Merkle tree.
|
/// that all transactions are valid and calculates the Merkle tree.
|
||||||
pub fn with_reward(
|
pub fn from_reward(
|
||||||
prev: &BlockHeader,
|
prev: &BlockHeader,
|
||||||
txs: Vec<Transaction>,
|
txs: Vec<Transaction>,
|
||||||
reward_out: Output,
|
reward_out: Output,
|
||||||
|
@ -461,6 +466,14 @@ impl Block {
|
||||||
}.cut_through()
|
}.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
|
/// Get inputs
|
||||||
pub fn inputs(&self) -> &Vec<Input> {
|
pub fn inputs(&self) -> &Vec<Input> {
|
||||||
&self.body.inputs
|
&self.body.inputs
|
||||||
|
|
|
@ -120,7 +120,7 @@ fn build_block(
|
||||||
};
|
};
|
||||||
|
|
||||||
let (output, kernel, block_fees) = get_coinbase(wallet_listener_url, block_fees)?;
|
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
|
// making sure we're not spending time mining a useless block
|
||||||
b.validate(&head.total_kernel_offset, verifier_cache)?;
|
b.validate(&head.total_kernel_offset, verifier_cache)?;
|
||||||
|
|
Loading…
Reference in a new issue