mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
Add test for duplicate rangeproof inflation bug (#3630)
* invalid range proof test * create output with 1 million grin * Move `output_negative` into `tests/` So it cannot be called from outside of tests! * Verify InvalidBlockProof & InvalidRangeProof types Co-authored-by: Snape 479 <Severus_Snape93@protonmail.com>
This commit is contained in:
parent
f6ec77a592
commit
df91bff0f1
1 changed files with 151 additions and 3 deletions
|
@ -15,12 +15,18 @@
|
|||
use self::chain::types::{NoopAdapter, Tip};
|
||||
use self::chain::Chain;
|
||||
use self::core::core::hash::Hashed;
|
||||
use self::core::core::{Block, BlockHeader, KernelFeatures, Transaction};
|
||||
use self::core::core::{
|
||||
block, transaction, Block, BlockHeader, KernelFeatures, Output, OutputFeatures, Transaction,
|
||||
};
|
||||
use self::core::global::ChainTypes;
|
||||
use self::core::libtx::{self, build, ProofBuilder};
|
||||
use self::core::libtx::build::{self, Append};
|
||||
use self::core::libtx::proof::{self, ProofBuild};
|
||||
use self::core::libtx::{self, Error, ProofBuilder};
|
||||
use self::core::pow::Difficulty;
|
||||
use self::core::{consensus, global, pow};
|
||||
use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain};
|
||||
use self::keychain::{
|
||||
BlindSum, ExtKeychain, ExtKeychainPath, Identifier, Keychain, SwitchCommitmentType,
|
||||
};
|
||||
use self::util::RwLock;
|
||||
use chrono::Duration;
|
||||
use grin_chain as chain;
|
||||
|
@ -814,6 +820,148 @@ fn output_header_mappings() {
|
|||
clean_output_dir(".grin_header_for_output");
|
||||
}
|
||||
|
||||
/// Build a negative output. This function must not be used outside of tests.
|
||||
/// The commitment will be an inversion of the value passed in and the value is
|
||||
/// subtracted from the sum.
|
||||
fn build_output_negative<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
|
||||
where
|
||||
K: Keychain,
|
||||
B: ProofBuild,
|
||||
{
|
||||
Box::new(
|
||||
move |build, acc| -> Result<(Transaction, BlindSum), Error> {
|
||||
let (tx, sum) = acc?;
|
||||
|
||||
// TODO: proper support for different switch commitment schemes
|
||||
let switch = SwitchCommitmentType::Regular;
|
||||
|
||||
let commit = build.keychain.commit(value, &key_id, switch)?;
|
||||
|
||||
// invert commitment
|
||||
let commit = build.keychain.secp().commit_sum(vec![], vec![commit])?;
|
||||
|
||||
eprintln!("Building output: {}, {:?}", value, commit);
|
||||
|
||||
// build a proof with a rangeproof of 0 as a placeholder
|
||||
// the test will replace this later
|
||||
let proof = proof::create(
|
||||
build.keychain,
|
||||
build.builder,
|
||||
0,
|
||||
&key_id,
|
||||
switch,
|
||||
commit,
|
||||
None,
|
||||
)?;
|
||||
|
||||
// we return the output and the value is subtracted instead of added
|
||||
Ok((
|
||||
tx.with_output(Output::new(OutputFeatures::Plain, commit, proof)),
|
||||
sum.sub_key_id(key_id.to_value_path(value)),
|
||||
))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Test the duplicate rangeproof bug
|
||||
#[test]
|
||||
fn test_overflow_cached_rangeproof() {
|
||||
clean_output_dir(".grin_overflow");
|
||||
global::set_local_chain_type(ChainTypes::AutomatedTesting);
|
||||
|
||||
util::init_test_logger();
|
||||
{
|
||||
let chain = init_chain(".grin_overflow", pow::mine_genesis_block().unwrap());
|
||||
let prev = chain.head_header().unwrap();
|
||||
let kc = ExtKeychain::from_random_seed(false).unwrap();
|
||||
let pb = ProofBuilder::new(&kc);
|
||||
|
||||
let mut head = prev;
|
||||
|
||||
// mine the first block and keep track of the block_hash
|
||||
// so we can spend the coinbase later
|
||||
let b = prepare_block(&kc, &head, &chain, 2);
|
||||
|
||||
assert!(b.outputs()[0].is_coinbase());
|
||||
head = b.header.clone();
|
||||
chain
|
||||
.process_block(b.clone(), chain::Options::SKIP_POW)
|
||||
.unwrap();
|
||||
|
||||
// now mine three further blocks
|
||||
for n in 3..6 {
|
||||
let b = prepare_block(&kc, &head, &chain, n);
|
||||
head = b.header.clone();
|
||||
chain.process_block(b, chain::Options::SKIP_POW).unwrap();
|
||||
}
|
||||
|
||||
// create a few keys for use in txns
|
||||
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
|
||||
let key_id30 = ExtKeychainPath::new(1, 30, 0, 0, 0).to_identifier();
|
||||
let key_id31 = ExtKeychainPath::new(1, 31, 0, 0, 0).to_identifier();
|
||||
let key_id32 = ExtKeychainPath::new(1, 32, 0, 0, 0).to_identifier();
|
||||
|
||||
// build a regular transaction so we have a rangeproof to copy
|
||||
let tx1 = build::transaction(
|
||||
KernelFeatures::Plain { fee: 20000.into() },
|
||||
&[
|
||||
build::coinbase_input(consensus::REWARD, key_id2.clone()),
|
||||
build::output(consensus::REWARD - 20000, key_id30.clone()),
|
||||
],
|
||||
&kc,
|
||||
&pb,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// mine block with tx1
|
||||
let next = prepare_block_tx(&kc, &head, &chain, 7, &[tx1.clone()]);
|
||||
let prev_main = next.header.clone();
|
||||
chain
|
||||
.process_block(next.clone(), chain::Options::SKIP_POW)
|
||||
.unwrap();
|
||||
chain.validate(false).unwrap();
|
||||
|
||||
// create a second tx that contains a negative output
|
||||
// and a positive output for 1m grin
|
||||
let mut tx2 = build::transaction(
|
||||
KernelFeatures::Plain { fee: 0.into() },
|
||||
&[
|
||||
build::input(consensus::REWARD - 20000, key_id30.clone()),
|
||||
build::output(
|
||||
consensus::REWARD - 20000 + 1_000_000_000_000_000,
|
||||
key_id31.clone(),
|
||||
),
|
||||
build_output_negative(1_000_000_000_000_000, key_id32.clone()),
|
||||
],
|
||||
&kc,
|
||||
&pb,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// make sure tx1 only has one output as expected
|
||||
assert_eq!(tx1.body.outputs.len(), 1);
|
||||
let last_rp = tx1.body.outputs[0].proof;
|
||||
|
||||
// overwrite all our rangeproofs with the rangeproof from last block
|
||||
for i in 0..tx2.body.outputs.len() {
|
||||
tx2.body.outputs[i].proof = last_rp;
|
||||
}
|
||||
|
||||
let next = prepare_block_tx(&kc, &prev_main, &chain, 8, &[tx2.clone()]);
|
||||
// process_block fails with verifier_cache disabled or with correct verifier_cache
|
||||
// implementations
|
||||
let res = chain.process_block(next, chain::Options::SKIP_POW);
|
||||
|
||||
assert_eq!(
|
||||
res.unwrap_err().kind(),
|
||||
chain::ErrorKind::InvalidBlockProof(block::Error::Transaction(
|
||||
transaction::Error::Secp(util::secp::Error::InvalidRangeProof)
|
||||
))
|
||||
);
|
||||
}
|
||||
clean_output_dir(".grin_overflow");
|
||||
}
|
||||
|
||||
// Use diff as both diff *and* key_idx for convenience (deterministic private key for test blocks)
|
||||
fn prepare_block<K>(kc: &K, prev: &BlockHeader, chain: &Chain, diff: u64) -> Block
|
||||
where
|
||||
|
|
Loading…
Reference in a new issue