diff --git a/chain/src/chain.rs b/chain/src/chain.rs index b814a418e3..f2563ec07c 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -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()); } diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 592fdb9bd3..5fc3afa836 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -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 { + pub fn get_header_by_height(&self, height: u64) -> Result { 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)) diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 05630a34cd..2adc93db62 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -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(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(); diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 287b33c05a..1d4cd788a1 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -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 { 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, 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 { &self.body.inputs diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index 18bb8ee938..4de6678212 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -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)?;