Make total_difficulty the sum of network difficulty [testnet2] (#523)

* Make total_difficulty the sum of network difficulty, not whatever the miner happened to mine.
  - Only for Testnet2 / Mainnet (hardforks Testnet1)
  - update chain::pipe validate_header to validate according to Testnet2 rules for cumulative difference

Fixes #280

* tests that should ignore (network) difficulty

* fn new_block is explained as "utility to create a block without worrying about the key or previous header" so it gets network difficulty := 0 too

* update tx pool tests (going with "minimum" for network difficulty for now)

* add ERR outputs about bannable offences
(#406 should know about these)

* whitespace fix

* mine_simple_chain: Probably DON'T overwrite difficulty (?)

* core/mod tests "reward_empty_block" and "reward_with_tx_block" tests set to use lowest network difficulty possible
This commit is contained in:
Simon B 2018-01-12 19:35:37 +01:00 committed by Ignotus Peverell
parent 5edc63f617
commit 8b2f9503c9
8 changed files with 114 additions and 28 deletions

View file

@ -213,18 +213,32 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
return Err(Error::DifficultyTooLow);
}
let diff_iter = store::DifficultyIter::from(header.previous, ctx.store.clone());
let difficulty =
consensus::next_difficulty(diff_iter).map_err(|e| Error::Other(e.to_string()))?;
// explicit check to ensure total_difficulty has increased by exactly
// the difficulty of the previous block
if header.total_difficulty != prev.total_difficulty.clone() + prev.pow.to_difficulty() {
// the _network_ difficulty of the previous block
// (during testnet1 we use _block_ difficulty here)
if header.total_difficulty != prev.total_difficulty.clone() + difficulty.clone() {
error!(
LOGGER,
"validate_header: BANNABLE OFFENCE: header cumulative difficulty {} != {}",
header.difficulty.into_num(),
prev.total_difficulty.into_num() + difficulty.into_num()
);
return Err(Error::WrongTotalDifficulty);
}
// now check that the difficulty is not less than that calculated by the
// difficulty iterator based on the previous block
let diff_iter = store::DifficultyIter::from(header.previous, ctx.store.clone());
let difficulty =
consensus::next_difficulty(diff_iter).map_err(|e| Error::Other(e.to_string()))?;
if header.difficulty < difficulty {
error!(
LOGGER,
"validate_header: BANNABLE OFFENCE: header difficulty {} < {}",
header.difficulty.into_num(),
difficulty.into_num()
);
return Err(Error::DifficultyTooLow);
}
}

View file

@ -74,12 +74,18 @@ fn mine_empty_chain() {
);
for n in 1..4 {
let prev = chain.head_header().unwrap();
let difficulty = consensus::next_difficulty(chain.difficulty_iter()).unwrap();
let pk = keychain.derive_key_id(n as u32).unwrap();
let mut b = core::core::Block::new(&prev, vec![], &keychain, &pk).unwrap();
let mut b = core::core::Block::new(
&prev,
vec![],
&keychain,
&pk,
difficulty.clone()
).unwrap();
b.header.timestamp = prev.timestamp + time::Duration::seconds(60);
let difficulty = consensus::next_difficulty(chain.difficulty_iter()).unwrap();
b.header.difficulty = difficulty.clone();
b.header.difficulty = difficulty.clone(); // TODO: overwrite here? really?
chain.set_sumtree_roots(&mut b, false).unwrap();
pow::pow_size(
@ -335,7 +341,7 @@ fn prepare_fork_block_tx(kc: &Keychain, prev: &BlockHeader, chain: &Chain, diff:
fn prepare_block_nosum(kc: &Keychain, prev: &BlockHeader, diff: u64, txs: Vec<&Transaction>) -> Block {
let key_id = kc.derive_key_id(diff as u32).unwrap();
let mut b = match core::core::Block::new(prev, txs, kc, &key_id) {
let mut b = match core::core::Block::new(prev, txs, kc, &key_id, Difficulty::from_num(diff)) {
Err(e) => panic!("{:?}",e),
Ok(b) => b
};

View file

@ -24,6 +24,7 @@ use std::fs;
use chain::{ChainStore, Tip};
use core::core::hash::Hashed;
use core::core::Block;
use core::core::target::Difficulty;
use keychain::Keychain;
use core::global;
use core::global::ChainTypes;
@ -47,7 +48,13 @@ fn test_various_store_indices() {
chain_store.save_block(&genesis).unwrap();
chain_store.setup_height(&genesis.header, &Tip::new(genesis.hash())).unwrap();
let block = Block::new(&genesis.header, vec![], &keychain, &key_id).unwrap();
let block = Block::new(
&genesis.header,
vec![],
&keychain,
&key_id,
Difficulty::minimum()
).unwrap();
let commit = block.outputs[0].commitment();
let block_hash = block.hash();

View file

@ -25,6 +25,7 @@ use std::sync::Arc;
use chain::types::*;
use core::core::build;
use core::core::target::Difficulty;
use core::core::transaction;
use core::consensus;
use core::global;
@ -74,7 +75,13 @@ fn test_coinbase_maturity() {
let key_id3 = keychain.derive_key_id(3).unwrap();
let key_id4 = keychain.derive_key_id(4).unwrap();
let mut block = core::core::Block::new(&prev, vec![], &keychain, &key_id1).unwrap();
let mut block = core::core::Block::new(
&prev,
vec![],
&keychain,
&key_id1,
Difficulty::minimum()
).unwrap();
block.header.timestamp = prev.timestamp + time::Duration::seconds(60);
let difficulty = consensus::next_difficulty(chain.difficulty_iter()).unwrap();
@ -110,7 +117,13 @@ fn test_coinbase_maturity() {
).unwrap();
let mut block =
core::core::Block::new(&prev, vec![&coinbase_txn], &keychain, &key_id3).unwrap();
core::core::Block::new(
&prev,
vec![&coinbase_txn],
&keychain,
&key_id3,
Difficulty::minimum()
).unwrap();
block.header.timestamp = prev.timestamp + time::Duration::seconds(60);
let difficulty = consensus::next_difficulty(chain.difficulty_iter()).unwrap();
@ -138,7 +151,13 @@ fn test_coinbase_maturity() {
let keychain = Keychain::from_random_seed().unwrap();
let pk = keychain.derive_key_id(1).unwrap();
let mut block = core::core::Block::new(&prev, vec![], &keychain, &pk).unwrap();
let mut block = core::core::Block::new(
&prev,
vec![],
&keychain,
&pk,
Difficulty::minimum()
).unwrap();
block.header.timestamp = prev.timestamp + time::Duration::seconds(60);
let difficulty = consensus::next_difficulty(chain.difficulty_iter()).unwrap();
@ -158,7 +177,13 @@ fn test_coinbase_maturity() {
let prev = chain.head_header().unwrap();
let mut block =
core::core::Block::new(&prev, vec![&coinbase_txn], &keychain, &key_id4).unwrap();
core::core::Block::new(
&prev,
vec![&coinbase_txn],
&keychain,
&key_id4,
Difficulty::minimum()
).unwrap();
block.header.timestamp = prev.timestamp + time::Duration::seconds(60);

View file

@ -292,10 +292,11 @@ impl Block {
txs: Vec<&Transaction>,
keychain: &keychain::Keychain,
key_id: &keychain::Identifier,
difficulty: Difficulty,
) -> Result<Block, Error> {
let fees = txs.iter().map(|tx| tx.fee).sum();
let (reward_out, reward_proof) = Block::reward_output(keychain, key_id, fees)?;
let block = Block::with_reward(prev, txs, reward_out, reward_proof)?;
let block = Block::with_reward(prev, txs, reward_out, reward_proof, difficulty)?;
Ok(block)
}
@ -307,6 +308,7 @@ impl Block {
txs: Vec<&Transaction>,
reward_out: Output,
reward_kern: TxKernel,
difficulty: Difficulty,
) -> Result<Block, Error> {
let mut kernels = vec![];
let mut inputs = vec![];
@ -350,7 +352,7 @@ impl Block {
..time::now_utc()
},
previous: prev.hash(),
total_difficulty: prev.pow.clone().to_difficulty() +
total_difficulty: difficulty +
prev.total_difficulty.clone(),
..Default::default()
},
@ -615,7 +617,13 @@ mod test {
// header
fn new_block(txs: Vec<&Transaction>, keychain: &Keychain) -> Block {
let key_id = keychain.derive_key_id(1).unwrap();
Block::new(&BlockHeader::default(), txs, keychain, &key_id).unwrap()
Block::new(
&BlockHeader::default(),
txs,
keychain,
&key_id,
Difficulty::minimum()
).unwrap()
}
// utility producing a transaction that spends an output with the provided

View file

@ -28,6 +28,7 @@ use std::cmp::Ordering;
use std::num::ParseFloatError;
use consensus::GRIN_BASE;
use core::target::Difficulty;
use util::{secp, static_secp_instance};
use util::secp::pedersen::*;
@ -388,7 +389,13 @@ mod test {
let keychain = keychain::Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
let b = Block::new(&BlockHeader::default(), vec![], &keychain, &key_id).unwrap();
let b = Block::new(
&BlockHeader::default(),
vec![],
&keychain,
&key_id,
Difficulty::minimum(),
).unwrap();
b.compact().validate().unwrap();
}
@ -400,7 +407,13 @@ mod test {
let mut tx1 = tx2i1o();
tx1.verify_sig().unwrap();
let b = Block::new(&BlockHeader::default(), vec![&mut tx1], &keychain, &key_id).unwrap();
let b = Block::new(
&BlockHeader::default(),
vec![&mut tx1],
&keychain,
&key_id,
Difficulty::minimum(),
).unwrap();
b.compact().validate().unwrap();
}
@ -417,6 +430,7 @@ mod test {
vec![&mut tx1, &mut tx2],
&keychain,
&key_id,
Difficulty::minimum(),
).unwrap();
b.validate().unwrap();
}
@ -447,6 +461,7 @@ mod test {
vec![&tx1],
&keychain,
&key_id3.clone(),
Difficulty::minimum(),
).unwrap();
b.validate().unwrap();
@ -467,6 +482,7 @@ mod test {
vec![&tx1],
&keychain,
&key_id3.clone(),
Difficulty::minimum(),
).unwrap();
match b.validate() {
Err(KernelLockHeight { lock_height: height }) => {

View file

@ -542,7 +542,7 @@ impl Miner {
head: &core::BlockHeader,
key_id: Option<Identifier>,
) -> Result<(core::Block, BlockFees), Error> {
// prepare the block header timestamp
let mut now_sec = time::get_time().sec;
let head_sec = head.timestamp.to_timespec().sec;
@ -571,15 +571,16 @@ impl Miner {
};
let (output, kernel, block_fees) = self.get_coinbase(block_fees)?;
let mut b = core::Block::with_reward(head, txs, output, kernel)?;
let mut b = core::Block::with_reward(head, txs, output, kernel, difficulty.clone())?;
debug!(
LOGGER,
"(Server ID: {}) Built new block with {} inputs and {} outputs, difficulty: {}",
"(Server ID: {}) Built new block with {} inputs and {} outputs, network difficulty: {}, block cumulative difficulty {}",
self.debug_output_id,
b.inputs.len(),
b.outputs.len(),
difficulty
difficulty.clone().into_num(),
b.header.clone().difficulty.clone().into_num(),
);
// making sure we're not spending time mining a useless block
@ -590,18 +591,19 @@ impl Miner {
b.header.difficulty = difficulty;
b.header.timestamp = time::at_utc(time::Timespec::new(now_sec, 0));
trace!(LOGGER, "Block: {:?}", b);
let roots_result = self.chain.set_sumtree_roots(&mut b, false);
match roots_result {
Ok(_) => Ok((b, block_fees)),
// If it's a duplicate commitment, it's likely trying to use
// a key that's already been derived but not in the wallet
// for some reason, allow caller to retry
Err(chain::Error::DuplicateCommitment(e)) => {
Err(Error::Chain(chain::Error::DuplicateCommitment(e)))
}
//Some other issue, possibly duplicate kernel
Err(e) => {
error!(LOGGER, "Error setting sumtree root to build a block: {:?}", e);

View file

@ -20,6 +20,7 @@ pub use graph;
use core::core::transaction;
use core::core::block;
use core::core::hash;
use core::core::target::Difficulty;
use core::global;
use util::secp::pedersen::Commitment;
@ -930,6 +931,7 @@ mod tests {
txs.iter().collect(),
&keychain,
&key_id,
Difficulty::minimum(),
).unwrap();
// now apply the block to ensure the chainstate is updated before we reconcile
@ -1060,6 +1062,7 @@ mod tests {
block_transactions,
&keychain,
&key_id,
Difficulty::minimum(),
).unwrap();
chain_ref.apply_block(&block);
@ -1183,8 +1186,13 @@ mod tests {
let keychain = Keychain::from_random_seed().unwrap();
let key_id = keychain.derive_key_id(1).unwrap();
block = block::Block::new(&block::BlockHeader::default(), tx_refs, &keychain, &key_id)
.unwrap();
block = block::Block::new(
&block::BlockHeader::default(),
tx_refs,
&keychain,
&key_id,
Difficulty::minimum()
).unwrap();
}
chain_ref.apply_block(&block);