2020-01-20 14:40:58 +03:00
|
|
|
// Copyright 2020 The Grin Developers
|
2018-05-09 12:15:58 +03:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2019-11-26 23:21:49 +03:00
|
|
|
mod common;
|
2018-12-08 02:59:40 +03:00
|
|
|
use crate::common::{new_block, tx1i2o, tx2i1o, txspend1i1o};
|
2020-05-29 11:56:24 +03:00
|
|
|
use crate::core::consensus::{self, BLOCK_OUTPUT_WEIGHT, TESTING_THIRD_HARD_FORK};
|
|
|
|
use crate::core::core::block::{Block, BlockHeader, Error, HeaderVersion};
|
2018-12-08 02:59:40 +03:00
|
|
|
use crate::core::core::hash::Hashed;
|
|
|
|
use crate::core::core::id::ShortIdentifiable;
|
2020-05-29 11:56:24 +03:00
|
|
|
use crate::core::core::transaction::{
|
|
|
|
self, KernelFeatures, NRDRelativeHeight, OutputFeatures, Transaction,
|
2019-05-08 23:10:42 +03:00
|
|
|
};
|
2020-05-29 11:56:24 +03:00
|
|
|
use crate::core::core::verifier_cache::{LruVerifierCache, VerifierCache};
|
|
|
|
use crate::core::core::{Committed, CompactBlock};
|
2019-11-01 13:56:58 +03:00
|
|
|
use crate::core::libtx::build::{self, input, output};
|
2019-06-12 12:28:55 +03:00
|
|
|
use crate::core::libtx::ProofBuilder;
|
2018-12-08 02:59:40 +03:00
|
|
|
use crate::core::{global, ser};
|
|
|
|
use chrono::Duration;
|
|
|
|
use grin_core as core;
|
2019-11-14 18:27:30 +03:00
|
|
|
use keychain::{BlindingFactor, ExtKeychain, Keychain};
|
2018-10-20 03:13:07 +03:00
|
|
|
use std::sync::Arc;
|
2020-04-24 12:18:26 +03:00
|
|
|
use util::{secp, RwLock, ToHex};
|
2018-08-30 17:44:34 +03:00
|
|
|
|
2020-05-22 14:51:58 +03:00
|
|
|
// Setup test with AutomatedTesting chain_type;
|
|
|
|
fn test_setup() {
|
|
|
|
global::set_local_chain_type(global::ChainTypes::AutomatedTesting);
|
|
|
|
}
|
|
|
|
|
2018-12-08 02:59:40 +03:00
|
|
|
fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
|
2018-08-30 17:44:34 +03:00
|
|
|
Arc::new(RwLock::new(LruVerifierCache::new()))
|
|
|
|
}
|
|
|
|
|
2019-07-16 14:02:21 +03:00
|
|
|
#[test]
|
2018-05-09 12:15:58 +03:00
|
|
|
fn too_large_block() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2019-02-01 13:44:04 +03:00
|
|
|
let max_out = global::max_block_weight() / BLOCK_OUTPUT_WEIGHT;
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
let mut pks = vec![];
|
|
|
|
for n in 0..(max_out + 1) {
|
2018-10-10 12:11:01 +03:00
|
|
|
pks.push(ExtKeychain::derive_key_id(1, n as u32, 0, 0, 0));
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut parts = vec![];
|
|
|
|
for _ in 0..max_out {
|
|
|
|
parts.push(output(5, pks.pop().unwrap()));
|
|
|
|
}
|
|
|
|
|
2019-11-01 13:56:58 +03:00
|
|
|
parts.append(&mut vec![input(500000, pks.pop().unwrap())]);
|
2020-07-27 13:07:18 +03:00
|
|
|
let tx = build::transaction(
|
|
|
|
KernelFeatures::Plain { fee: 2 },
|
|
|
|
&parts,
|
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[tx], &keychain, &builder, &prev, &key_id);
|
2018-12-08 02:59:40 +03:00
|
|
|
assert!(b
|
|
|
|
.validate(&BlindingFactor::zero(), verifier_cache())
|
|
|
|
.is_err());
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
// block with no inputs/outputs/kernels
|
|
|
|
// no fees, no reward, no coinbase
|
|
|
|
fn very_empty_block() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-08-16 00:14:48 +03:00
|
|
|
let b = Block::with_header(BlockHeader::default());
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
b.verify_coinbase(),
|
|
|
|
Err(Error::Secp(secp::Error::IncorrectCommitSum))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-29 11:56:24 +03:00
|
|
|
#[test]
|
|
|
|
fn block_with_nrd_kernel_pre_post_hf3() {
|
|
|
|
// automated testing - HF{1|2|3} at block heights {3, 6, 9}
|
|
|
|
// Enable the global NRD feature flag. NRD kernels valid at HF3 at height 9.
|
|
|
|
global::set_local_chain_type(global::ChainTypes::AutomatedTesting);
|
|
|
|
global::set_local_nrd_enabled(true);
|
|
|
|
|
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
|
|
|
let builder = ProofBuilder::new(&keychain);
|
|
|
|
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
|
|
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
|
|
|
|
2020-07-27 13:07:18 +03:00
|
|
|
let tx = build::transaction(
|
2020-05-29 11:56:24 +03:00
|
|
|
KernelFeatures::NoRecentDuplicate {
|
|
|
|
fee: 2,
|
|
|
|
relative_height: NRDRelativeHeight::new(1440).unwrap(),
|
|
|
|
},
|
2020-07-27 13:07:18 +03:00
|
|
|
&[input(7, key_id1), output(5, key_id2)],
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-07-27 13:07:18 +03:00
|
|
|
let txs = &[tx];
|
2020-05-29 11:56:24 +03:00
|
|
|
|
|
|
|
let prev_height = TESTING_THIRD_HARD_FORK - 2;
|
|
|
|
let prev = BlockHeader {
|
|
|
|
height: prev_height,
|
|
|
|
version: consensus::header_version(prev_height),
|
|
|
|
..BlockHeader::default()
|
|
|
|
};
|
|
|
|
let b = new_block(
|
2020-07-27 13:07:18 +03:00
|
|
|
txs,
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
&prev,
|
|
|
|
&ExtKeychain::derive_key_id(1, 1, 0, 0, 0),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Block is invalid at header version 3 if it contains an NRD kernel.
|
|
|
|
assert_eq!(b.header.version, HeaderVersion(3));
|
|
|
|
assert_eq!(
|
|
|
|
b.validate(&BlindingFactor::zero(), verifier_cache()),
|
|
|
|
Err(Error::NRDKernelPreHF3)
|
|
|
|
);
|
|
|
|
|
|
|
|
let prev_height = TESTING_THIRD_HARD_FORK - 1;
|
|
|
|
let prev = BlockHeader {
|
|
|
|
height: prev_height,
|
|
|
|
version: consensus::header_version(prev_height),
|
|
|
|
..BlockHeader::default()
|
|
|
|
};
|
|
|
|
let b = new_block(
|
2020-07-27 13:07:18 +03:00
|
|
|
txs,
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
&prev,
|
|
|
|
&ExtKeychain::derive_key_id(1, 1, 0, 0, 0),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Block is valid at header version 4 (at HF height) if it contains an NRD kernel.
|
|
|
|
assert_eq!(b.header.height, TESTING_THIRD_HARD_FORK);
|
|
|
|
assert_eq!(b.header.version, HeaderVersion(4));
|
|
|
|
assert!(b
|
|
|
|
.validate(&BlindingFactor::zero(), verifier_cache())
|
|
|
|
.is_ok());
|
|
|
|
|
|
|
|
let prev_height = TESTING_THIRD_HARD_FORK;
|
|
|
|
let prev = BlockHeader {
|
|
|
|
height: prev_height,
|
|
|
|
version: consensus::header_version(prev_height),
|
|
|
|
..BlockHeader::default()
|
|
|
|
};
|
|
|
|
let b = new_block(
|
2020-07-27 13:07:18 +03:00
|
|
|
txs,
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
&prev,
|
|
|
|
&ExtKeychain::derive_key_id(1, 1, 0, 0, 0),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Block is valid at header version 4 if it contains an NRD kernel.
|
|
|
|
assert_eq!(b.header.version, HeaderVersion(4));
|
|
|
|
assert!(b
|
|
|
|
.validate(&BlindingFactor::zero(), verifier_cache())
|
|
|
|
.is_ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn block_with_nrd_kernel_nrd_not_enabled() {
|
|
|
|
// automated testing - HF{1|2|3} at block heights {3, 6, 9}
|
|
|
|
global::set_local_chain_type(global::ChainTypes::AutomatedTesting);
|
|
|
|
|
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
|
|
|
let builder = ProofBuilder::new(&keychain);
|
|
|
|
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
|
|
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
|
|
|
|
2020-07-27 13:07:18 +03:00
|
|
|
let tx = build::transaction(
|
2020-05-29 11:56:24 +03:00
|
|
|
KernelFeatures::NoRecentDuplicate {
|
|
|
|
fee: 2,
|
|
|
|
relative_height: NRDRelativeHeight::new(1440).unwrap(),
|
|
|
|
},
|
2020-07-27 13:07:18 +03:00
|
|
|
&[input(7, key_id1), output(5, key_id2)],
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2020-07-27 13:07:18 +03:00
|
|
|
let txs = &[tx];
|
|
|
|
|
2020-05-29 11:56:24 +03:00
|
|
|
let prev_height = TESTING_THIRD_HARD_FORK - 2;
|
|
|
|
let prev = BlockHeader {
|
|
|
|
height: prev_height,
|
|
|
|
version: consensus::header_version(prev_height),
|
|
|
|
..BlockHeader::default()
|
|
|
|
};
|
|
|
|
let b = new_block(
|
2020-07-27 13:07:18 +03:00
|
|
|
txs,
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
&prev,
|
|
|
|
&ExtKeychain::derive_key_id(1, 1, 0, 0, 0),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Block is invalid as NRD not enabled.
|
|
|
|
assert_eq!(b.header.version, HeaderVersion(3));
|
|
|
|
assert_eq!(
|
|
|
|
b.validate(&BlindingFactor::zero(), verifier_cache()),
|
|
|
|
Err(Error::NRDKernelNotEnabled)
|
|
|
|
);
|
|
|
|
|
|
|
|
let prev_height = TESTING_THIRD_HARD_FORK - 1;
|
|
|
|
let prev = BlockHeader {
|
|
|
|
height: prev_height,
|
|
|
|
version: consensus::header_version(prev_height),
|
|
|
|
..BlockHeader::default()
|
|
|
|
};
|
|
|
|
let b = new_block(
|
2020-07-27 13:07:18 +03:00
|
|
|
txs,
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
&prev,
|
|
|
|
&ExtKeychain::derive_key_id(1, 1, 0, 0, 0),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Block is invalid as NRD not enabled.
|
|
|
|
assert_eq!(b.header.height, TESTING_THIRD_HARD_FORK);
|
|
|
|
assert_eq!(b.header.version, HeaderVersion(4));
|
|
|
|
assert_eq!(
|
|
|
|
b.validate(&BlindingFactor::zero(), verifier_cache()),
|
|
|
|
Err(Error::NRDKernelNotEnabled)
|
|
|
|
);
|
|
|
|
|
|
|
|
let prev_height = TESTING_THIRD_HARD_FORK;
|
|
|
|
let prev = BlockHeader {
|
|
|
|
height: prev_height,
|
|
|
|
version: consensus::header_version(prev_height),
|
|
|
|
..BlockHeader::default()
|
|
|
|
};
|
|
|
|
let b = new_block(
|
2020-07-27 13:07:18 +03:00
|
|
|
txs,
|
2020-05-29 11:56:24 +03:00
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
&prev,
|
|
|
|
&ExtKeychain::derive_key_id(1, 1, 0, 0, 0),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Block is invalid as NRD not enabled.
|
|
|
|
assert_eq!(b.header.version, HeaderVersion(4));
|
|
|
|
assert_eq!(
|
|
|
|
b.validate(&BlindingFactor::zero(), verifier_cache()),
|
|
|
|
Err(Error::NRDKernelNotEnabled)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-05-09 12:15:58 +03:00
|
|
|
#[test]
|
|
|
|
// builds a block with a tx spending another and check that cut_through occurred
|
|
|
|
fn block_with_cut_through() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
|
|
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
|
|
|
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2020-07-27 13:07:18 +03:00
|
|
|
let btx1 = tx2i1o();
|
|
|
|
let btx2 = build::transaction(
|
2019-11-01 13:56:58 +03:00
|
|
|
KernelFeatures::Plain { fee: 2 },
|
2020-07-27 13:07:18 +03:00
|
|
|
&[input(7, key_id1), output(5, key_id2.clone())],
|
2018-05-09 12:15:58 +03:00
|
|
|
&keychain,
|
2019-06-12 12:28:55 +03:00
|
|
|
&builder,
|
2018-12-08 02:59:40 +03:00
|
|
|
)
|
|
|
|
.unwrap();
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
// spending tx2 - reuse key_id2
|
|
|
|
|
2020-07-27 13:07:18 +03:00
|
|
|
let btx3 = txspend1i1o(5, &keychain, &builder, key_id2, key_id3);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[btx1, btx2, btx3], &keychain, &builder, &prev, &key_id);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
// block should have been automatically compacted (including reward
|
|
|
|
// output) and should still be valid
|
2018-10-13 04:15:29 +03:00
|
|
|
b.validate(&BlindingFactor::zero(), verifier_cache())
|
2018-08-30 17:44:34 +03:00
|
|
|
.unwrap();
|
2018-08-16 00:14:48 +03:00
|
|
|
assert_eq!(b.inputs().len(), 3);
|
|
|
|
assert_eq!(b.outputs().len(), 3);
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn empty_block_with_coinbase_is_valid() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2018-08-16 00:14:48 +03:00
|
|
|
assert_eq!(b.inputs().len(), 0);
|
|
|
|
assert_eq!(b.outputs().len(), 1);
|
|
|
|
assert_eq!(b.kernels().len(), 1);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2018-08-16 00:14:48 +03:00
|
|
|
let coinbase_outputs = b
|
|
|
|
.outputs()
|
2018-05-09 12:15:58 +03:00
|
|
|
.iter()
|
2018-12-18 21:26:34 +03:00
|
|
|
.filter(|out| out.is_coinbase())
|
2020-02-05 19:02:07 +03:00
|
|
|
.cloned()
|
2018-05-09 12:15:58 +03:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(coinbase_outputs.len(), 1);
|
|
|
|
|
2018-08-16 00:14:48 +03:00
|
|
|
let coinbase_kernels = b
|
|
|
|
.kernels()
|
2018-05-09 12:15:58 +03:00
|
|
|
.iter()
|
2018-12-18 21:26:34 +03:00
|
|
|
.filter(|out| out.is_coinbase())
|
2020-02-05 19:02:07 +03:00
|
|
|
.cloned()
|
2018-05-09 12:15:58 +03:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(coinbase_kernels.len(), 1);
|
|
|
|
|
|
|
|
// the block should be valid here (single coinbase output with corresponding
|
|
|
|
// txn kernel)
|
2018-12-08 02:59:40 +03:00
|
|
|
assert!(b
|
|
|
|
.validate(&BlindingFactor::zero(), verifier_cache())
|
|
|
|
.is_ok());
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-12-18 21:26:34 +03:00
|
|
|
// test that flipping the COINBASE flag on the output features
|
2018-05-09 12:15:58 +03:00
|
|
|
// invalidates the block and specifically it causes verify_coinbase to fail
|
|
|
|
// additionally verifying the merkle_inputs_outputs also fails
|
|
|
|
fn remove_coinbase_output_flag() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let mut b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2020-07-27 13:54:43 +03:00
|
|
|
let mut output = b.outputs()[0].clone();
|
|
|
|
output.features = OutputFeatures::Plain;
|
|
|
|
b.body.outputs = vec![output];
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
assert_eq!(b.verify_coinbase(), Err(Error::CoinbaseSumMismatch));
|
2018-12-08 02:59:40 +03:00
|
|
|
assert!(b
|
|
|
|
.verify_kernel_sums(b.header.overage(), b.header.total_kernel_offset())
|
|
|
|
.is_ok());
|
2018-05-09 12:15:58 +03:00
|
|
|
assert_eq!(
|
2018-10-13 04:15:29 +03:00
|
|
|
b.validate(&BlindingFactor::zero(), verifier_cache()),
|
2018-05-09 12:15:58 +03:00
|
|
|
Err(Error::CoinbaseSumMismatch)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-12-18 21:26:34 +03:00
|
|
|
// test that flipping the COINBASE flag on the kernel features
|
2018-05-09 12:15:58 +03:00
|
|
|
// invalidates the block and specifically it causes verify_coinbase to fail
|
|
|
|
fn remove_coinbase_kernel_flag() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let mut b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2020-07-27 13:54:43 +03:00
|
|
|
let mut kernel = b.kernels()[0].clone();
|
|
|
|
kernel.features = KernelFeatures::Plain { fee: 0 };
|
|
|
|
b.body = b.body.replace_kernel(kernel);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2018-12-12 12:19:36 +03:00
|
|
|
// Flipping the coinbase flag results in kernels not summing correctly.
|
2018-05-09 12:15:58 +03:00
|
|
|
assert_eq!(
|
|
|
|
b.verify_coinbase(),
|
|
|
|
Err(Error::Secp(secp::Error::IncorrectCommitSum))
|
|
|
|
);
|
|
|
|
|
2018-12-12 12:19:36 +03:00
|
|
|
// Also results in the block no longer validating correctly
|
|
|
|
// because the message being signed on each tx kernel includes the kernel features.
|
2018-05-09 12:15:58 +03:00
|
|
|
assert_eq!(
|
2018-10-13 04:15:29 +03:00
|
|
|
b.validate(&BlindingFactor::zero(), verifier_cache()),
|
2018-12-12 12:19:36 +03:00
|
|
|
Err(Error::Transaction(transaction::Error::IncorrectSignature))
|
2018-05-09 12:15:58 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-05-08 23:10:42 +03:00
|
|
|
#[test]
|
|
|
|
fn serialize_deserialize_header_version() {
|
|
|
|
let mut vec1 = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec1, &1_u16).expect("serialization failed");
|
2019-05-08 23:10:42 +03:00
|
|
|
|
|
|
|
let mut vec2 = Vec::new();
|
2019-12-02 15:35:17 +03:00
|
|
|
ser::serialize_default(&mut vec2, &HeaderVersion(1)).expect("serialization failed");
|
2019-05-08 23:10:42 +03:00
|
|
|
|
|
|
|
// Check that a header_version serializes to a
|
|
|
|
// single u16 value with no extraneous bytes wrapping it.
|
|
|
|
assert_eq!(vec1, vec2);
|
|
|
|
|
|
|
|
// Check we can successfully deserialize a header_version.
|
2019-06-27 19:19:41 +03:00
|
|
|
let version: HeaderVersion = ser::deserialize_default(&mut &vec2[..]).unwrap();
|
2019-05-08 23:10:42 +03:00
|
|
|
assert_eq!(version.0, 1)
|
|
|
|
}
|
|
|
|
|
2018-08-22 17:40:24 +03:00
|
|
|
#[test]
|
|
|
|
fn serialize_deserialize_block_header() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-08-22 17:40:24 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2018-08-22 17:40:24 +03:00
|
|
|
let header1 = b.header;
|
|
|
|
|
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &header1).expect("serialization failed");
|
2019-06-27 19:19:41 +03:00
|
|
|
let header2: BlockHeader = ser::deserialize_default(&mut &vec[..]).unwrap();
|
2018-08-22 17:40:24 +03:00
|
|
|
|
|
|
|
assert_eq!(header1.hash(), header2.hash());
|
|
|
|
assert_eq!(header1, header2);
|
|
|
|
}
|
|
|
|
|
2018-05-09 12:15:58 +03:00
|
|
|
#[test]
|
|
|
|
fn serialize_deserialize_block() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-08-22 17:40:24 +03:00
|
|
|
let tx1 = tx1i2o();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[tx1], &keychain, &builder, &prev, &key_id);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &b).expect("serialization failed");
|
2019-06-27 19:19:41 +03:00
|
|
|
let b2: Block = ser::deserialize_default(&mut &vec[..]).unwrap();
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2018-08-22 17:40:24 +03:00
|
|
|
assert_eq!(b.hash(), b2.hash());
|
2018-05-09 12:15:58 +03:00
|
|
|
assert_eq!(b.header, b2.header);
|
2018-08-16 00:14:48 +03:00
|
|
|
assert_eq!(b.inputs(), b2.inputs());
|
|
|
|
assert_eq!(b.outputs(), b2.outputs());
|
|
|
|
assert_eq!(b.kernels(), b2.kernels());
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn empty_block_serialized_size() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2018-05-09 12:15:58 +03:00
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &b).expect("serialization failed");
|
2019-09-19 16:31:46 +03:00
|
|
|
assert_eq!(vec.len(), 1_096);
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn block_single_tx_serialized_size() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let tx1 = tx1i2o();
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[tx1], &keychain, &builder, &prev, &key_id);
|
2018-05-09 12:15:58 +03:00
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &b).expect("serialization failed");
|
2019-09-19 16:31:46 +03:00
|
|
|
assert_eq!(vec.len(), 2_670);
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn empty_compact_block_serialized_size() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2018-08-22 22:19:37 +03:00
|
|
|
let cb: CompactBlock = b.into();
|
2018-05-09 12:15:58 +03:00
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &cb).expect("serialization failed");
|
2019-09-19 16:31:46 +03:00
|
|
|
assert_eq!(vec.len(), 1_104);
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn compact_block_single_tx_serialized_size() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let tx1 = tx1i2o();
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[tx1], &keychain, &builder, &prev, &key_id);
|
2018-08-22 22:19:37 +03:00
|
|
|
let cb: CompactBlock = b.into();
|
2018-05-09 12:15:58 +03:00
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &cb).expect("serialization failed");
|
2019-09-19 16:31:46 +03:00
|
|
|
assert_eq!(vec.len(), 1_110);
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn block_10_tx_serialized_size() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
let mut txs = vec![];
|
|
|
|
for _ in 0..10 {
|
|
|
|
let tx = tx1i2o();
|
|
|
|
txs.push(tx);
|
|
|
|
}
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&txs, &keychain, &builder, &prev, &key_id);
|
2019-09-19 16:31:46 +03:00
|
|
|
|
|
|
|
// Default protocol version.
|
|
|
|
{
|
|
|
|
let mut vec = Vec::new();
|
|
|
|
ser::serialize_default(&mut vec, &b).expect("serialization failed");
|
|
|
|
assert_eq!(vec.len(), 16_836);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Explicit protocol version 1
|
|
|
|
{
|
|
|
|
let mut vec = Vec::new();
|
|
|
|
ser::serialize(&mut vec, ser::ProtocolVersion(1), &b).expect("serialization failed");
|
|
|
|
assert_eq!(vec.len(), 16_932);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Explicit protocol version 2
|
|
|
|
{
|
|
|
|
let mut vec = Vec::new();
|
|
|
|
ser::serialize(&mut vec, ser::ProtocolVersion(2), &b).expect("serialization failed");
|
|
|
|
assert_eq!(vec.len(), 16_836);
|
|
|
|
}
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn compact_block_10_tx_serialized_size() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
let mut txs = vec![];
|
|
|
|
for _ in 0..10 {
|
|
|
|
let tx = tx1i2o();
|
|
|
|
txs.push(tx);
|
|
|
|
}
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&txs, &keychain, &builder, &prev, &key_id);
|
2018-08-22 22:19:37 +03:00
|
|
|
let cb: CompactBlock = b.into();
|
2018-05-09 12:15:58 +03:00
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &cb).expect("serialization failed");
|
2019-09-19 16:31:46 +03:00
|
|
|
assert_eq!(vec.len(), 1_164);
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn compact_block_hash_with_nonce() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let tx = tx1i2o();
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[tx.clone()], &keychain, &builder, &prev, &key_id);
|
2018-08-22 22:19:37 +03:00
|
|
|
let cb1: CompactBlock = b.clone().into();
|
|
|
|
let cb2: CompactBlock = b.clone().into();
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
// random nonce will not affect the hash of the compact block itself
|
|
|
|
// hash is based on header POW only
|
|
|
|
assert!(cb1.nonce != cb2.nonce);
|
|
|
|
assert_eq!(b.hash(), cb1.hash());
|
|
|
|
assert_eq!(cb1.hash(), cb2.hash());
|
|
|
|
|
2018-08-22 22:19:37 +03:00
|
|
|
assert!(cb1.kern_ids()[0] != cb2.kern_ids()[0]);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
// check we can identify the specified kernel from the short_id
|
|
|
|
// correctly in both of the compact_blocks
|
|
|
|
assert_eq!(
|
2018-08-22 22:19:37 +03:00
|
|
|
cb1.kern_ids()[0],
|
2018-08-16 00:14:48 +03:00
|
|
|
tx.kernels()[0].short_id(&cb1.hash(), cb1.nonce)
|
2018-05-09 12:15:58 +03:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2018-08-22 22:19:37 +03:00
|
|
|
cb2.kern_ids()[0],
|
2018-08-16 00:14:48 +03:00
|
|
|
tx.kernels()[0].short_id(&cb2.hash(), cb2.nonce)
|
2018-05-09 12:15:58 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn convert_block_to_compact_block() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let tx1 = tx1i2o();
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[tx1], &keychain, &builder, &prev, &key_id);
|
2018-08-22 22:19:37 +03:00
|
|
|
let cb: CompactBlock = b.clone().into();
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2018-08-22 22:19:37 +03:00
|
|
|
assert_eq!(cb.out_full().len(), 1);
|
|
|
|
assert_eq!(cb.kern_full().len(), 1);
|
|
|
|
assert_eq!(cb.kern_ids().len(), 1);
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
assert_eq!(
|
2018-08-22 22:19:37 +03:00
|
|
|
cb.kern_ids()[0],
|
2018-08-16 00:14:48 +03:00
|
|
|
b.kernels()
|
2018-05-09 12:15:58 +03:00
|
|
|
.iter()
|
2018-12-18 21:26:34 +03:00
|
|
|
.find(|x| !x.is_coinbase())
|
2018-05-09 12:15:58 +03:00
|
|
|
.unwrap()
|
|
|
|
.short_id(&cb.hash(), cb.nonce)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn hydrate_empty_compact_block() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-05-09 12:15:58 +03:00
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2018-08-22 22:19:37 +03:00
|
|
|
let cb: CompactBlock = b.clone().into();
|
2020-07-27 13:07:18 +03:00
|
|
|
let hb = Block::hydrate_from(cb, &[]).unwrap();
|
2018-05-09 12:15:58 +03:00
|
|
|
assert_eq!(hb.header, b.header);
|
2018-08-16 00:14:48 +03:00
|
|
|
assert_eq!(hb.outputs(), b.outputs());
|
|
|
|
assert_eq!(hb.kernels(), b.kernels());
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn serialize_deserialize_compact_block() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2018-12-29 01:46:21 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2018-08-22 22:19:37 +03:00
|
|
|
let tx1 = tx1i2o();
|
|
|
|
let prev = BlockHeader::default();
|
2018-10-10 12:11:01 +03:00
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[tx1], &keychain, &builder, &prev, &key_id);
|
2018-08-22 22:19:37 +03:00
|
|
|
|
|
|
|
let mut cb1: CompactBlock = b.into();
|
2018-05-09 12:15:58 +03:00
|
|
|
|
|
|
|
let mut vec = Vec::new();
|
2019-07-06 17:51:03 +03:00
|
|
|
ser::serialize_default(&mut vec, &cb1).expect("serialization failed");
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2018-08-22 22:19:37 +03:00
|
|
|
// After header serialization, timestamp will lose 'nanos' info, that's the designed behavior.
|
|
|
|
// To suppress 'nanos' difference caused assertion fail, we force b.header also lose 'nanos'.
|
|
|
|
let origin_ts = cb1.header.timestamp;
|
|
|
|
cb1.header.timestamp =
|
|
|
|
origin_ts - Duration::nanoseconds(origin_ts.timestamp_subsec_nanos() as i64);
|
|
|
|
|
2019-06-27 19:19:41 +03:00
|
|
|
let cb2: CompactBlock = ser::deserialize_default(&mut &vec[..]).unwrap();
|
2018-08-22 22:19:37 +03:00
|
|
|
|
|
|
|
assert_eq!(cb1.header, cb2.header);
|
|
|
|
assert_eq!(cb1.kern_ids(), cb2.kern_ids());
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
2019-01-14 21:15:42 +03:00
|
|
|
|
|
|
|
// Duplicate a range proof from a valid output into another of the same amount
|
|
|
|
#[test]
|
|
|
|
fn same_amount_outputs_copy_range_proof() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2019-01-14 21:15:42 +03:00
|
|
|
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2019-01-14 21:15:42 +03:00
|
|
|
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
|
|
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
|
|
|
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
|
|
|
|
|
|
|
let tx = build::transaction(
|
2019-11-01 13:56:58 +03:00
|
|
|
KernelFeatures::Plain { fee: 1 },
|
2020-07-27 13:07:18 +03:00
|
|
|
&[input(7, key_id1), output(3, key_id2), output(3, key_id3)],
|
2019-01-14 21:15:42 +03:00
|
|
|
&keychain,
|
2019-06-12 12:28:55 +03:00
|
|
|
&builder,
|
2019-01-14 21:15:42 +03:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// now we reconstruct the transaction, swapping the rangeproofs so they
|
|
|
|
// have the wrong privkey
|
2020-07-28 23:21:57 +03:00
|
|
|
let ins: Vec<_> = tx.inputs().into();
|
2020-07-27 13:07:18 +03:00
|
|
|
let mut outs = tx.outputs().to_vec();
|
2019-01-14 21:15:42 +03:00
|
|
|
outs[0].proof = outs[1].proof;
|
|
|
|
|
|
|
|
let key_id = keychain::ExtKeychain::derive_key_id(1, 4, 0, 0, 0);
|
|
|
|
let prev = BlockHeader::default();
|
|
|
|
let b = new_block(
|
2020-07-28 23:21:57 +03:00
|
|
|
&[Transaction::new(&ins, &outs, tx.kernels())],
|
2019-01-14 21:15:42 +03:00
|
|
|
&keychain,
|
2019-06-12 12:28:55 +03:00
|
|
|
&builder,
|
2019-01-14 21:15:42 +03:00
|
|
|
&prev,
|
|
|
|
&key_id,
|
|
|
|
);
|
|
|
|
|
|
|
|
// block should have been automatically compacted (including reward
|
|
|
|
// output) and should still be valid
|
|
|
|
match b.validate(&BlindingFactor::zero(), verifier_cache()) {
|
|
|
|
Err(Error::Transaction(transaction::Error::Secp(secp::Error::InvalidRangeProof))) => {}
|
|
|
|
_ => panic!("Bad range proof should be invalid"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Swap a range proof with the right private key but wrong amount
|
|
|
|
#[test]
|
|
|
|
fn wrong_amount_range_proof() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2019-01-14 21:15:42 +03:00
|
|
|
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
|
2019-06-12 12:28:55 +03:00
|
|
|
let builder = ProofBuilder::new(&keychain);
|
2019-01-14 21:15:42 +03:00
|
|
|
let key_id1 = keychain::ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
|
|
let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
|
|
|
let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
|
|
|
|
|
|
|
let tx1 = build::transaction(
|
2019-11-01 13:56:58 +03:00
|
|
|
KernelFeatures::Plain { fee: 1 },
|
2020-07-27 13:07:18 +03:00
|
|
|
&[
|
2019-01-14 21:15:42 +03:00
|
|
|
input(7, key_id1.clone()),
|
|
|
|
output(3, key_id2.clone()),
|
|
|
|
output(3, key_id3.clone()),
|
|
|
|
],
|
|
|
|
&keychain,
|
2019-06-12 12:28:55 +03:00
|
|
|
&builder,
|
2019-01-14 21:15:42 +03:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
let tx2 = build::transaction(
|
2019-11-01 13:56:58 +03:00
|
|
|
KernelFeatures::Plain { fee: 1 },
|
2020-07-27 13:07:18 +03:00
|
|
|
&[input(7, key_id1), output(2, key_id2), output(4, key_id3)],
|
2019-01-14 21:15:42 +03:00
|
|
|
&keychain,
|
2019-06-12 12:28:55 +03:00
|
|
|
&builder,
|
2019-01-14 21:15:42 +03:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// we take the range proofs from tx2 into tx1 and rebuild the transaction
|
2020-07-28 23:21:57 +03:00
|
|
|
let ins: Vec<_> = tx1.inputs().into();
|
2020-07-27 13:07:18 +03:00
|
|
|
let mut outs = tx1.outputs().to_vec();
|
2019-01-14 21:15:42 +03:00
|
|
|
outs[0].proof = tx2.outputs()[0].proof;
|
|
|
|
outs[1].proof = tx2.outputs()[1].proof;
|
|
|
|
|
|
|
|
let key_id = keychain::ExtKeychain::derive_key_id(1, 4, 0, 0, 0);
|
|
|
|
let prev = BlockHeader::default();
|
|
|
|
let b = new_block(
|
2020-07-28 23:21:57 +03:00
|
|
|
&[Transaction::new(&ins, &outs, tx1.kernels())],
|
2019-01-14 21:15:42 +03:00
|
|
|
&keychain,
|
2019-06-12 12:28:55 +03:00
|
|
|
&builder,
|
2019-01-14 21:15:42 +03:00
|
|
|
&prev,
|
|
|
|
&key_id,
|
|
|
|
);
|
|
|
|
|
|
|
|
// block should have been automatically compacted (including reward
|
|
|
|
// output) and should still be valid
|
|
|
|
match b.validate(&BlindingFactor::zero(), verifier_cache()) {
|
|
|
|
Err(Error::Transaction(transaction::Error::Secp(secp::Error::InvalidRangeProof))) => {}
|
|
|
|
_ => panic!("Bad range proof should be invalid"),
|
|
|
|
}
|
|
|
|
}
|
2020-01-23 17:29:54 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn validate_header_proof() {
|
2020-05-22 14:51:58 +03:00
|
|
|
test_setup();
|
2020-01-23 17:29:54 +03:00
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
|
|
|
let builder = ProofBuilder::new(&keychain);
|
|
|
|
let prev = BlockHeader::default();
|
|
|
|
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
2020-07-27 13:07:18 +03:00
|
|
|
let b = new_block(&[], &keychain, &builder, &prev, &key_id);
|
2020-01-23 17:29:54 +03:00
|
|
|
|
|
|
|
let mut header_buf = vec![];
|
|
|
|
{
|
|
|
|
let mut writer = ser::BinWriter::default(&mut header_buf);
|
|
|
|
b.header.write_pre_pow(&mut writer).unwrap();
|
|
|
|
b.header.pow.write_pre_pow(&mut writer).unwrap();
|
|
|
|
}
|
2020-04-24 12:18:26 +03:00
|
|
|
let pre_pow = header_buf.to_hex();
|
2020-01-23 17:29:54 +03:00
|
|
|
|
|
|
|
let reconstructed = BlockHeader::from_pre_pow_and_proof(
|
|
|
|
pre_pow,
|
|
|
|
b.header.pow.nonce,
|
|
|
|
b.header.pow.proof.clone(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(reconstructed, b.header);
|
|
|
|
|
|
|
|
// assert invalid pre_pow returns error
|
|
|
|
assert!(BlockHeader::from_pre_pow_and_proof(
|
|
|
|
"0xaf1678".to_string(),
|
|
|
|
b.header.pow.nonce,
|
2020-02-05 19:02:07 +03:00
|
|
|
b.header.pow.proof,
|
2020-01-23 17:29:54 +03:00
|
|
|
)
|
|
|
|
.is_err());
|
|
|
|
}
|
2020-08-18 22:09:54 +03:00
|
|
|
|
|
|
|
// Test coverage for verifying cut-through during block validation.
|
|
|
|
// It is not valid for a block to spend an output and produce a new output with the same commitment.
|
|
|
|
// This test covers the case where a plain output is spent, producing a plain output with the same commitment.
|
|
|
|
#[test]
|
|
|
|
fn test_verify_cut_through_plain() -> Result<(), Error> {
|
|
|
|
global::set_local_chain_type(global::ChainTypes::UserTesting);
|
|
|
|
|
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
|
|
|
|
|
|
|
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
|
|
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
|
|
|
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
|
|
|
|
|
|
|
let builder = ProofBuilder::new(&keychain);
|
|
|
|
|
|
|
|
let tx = build::transaction(
|
|
|
|
KernelFeatures::Plain { fee: 0 },
|
|
|
|
&[
|
|
|
|
build::input(10, key_id1.clone()),
|
|
|
|
build::input(10, key_id2.clone()),
|
|
|
|
build::output(10, key_id1.clone()),
|
|
|
|
build::output(6, key_id2.clone()),
|
|
|
|
build::output(4, key_id3.clone()),
|
|
|
|
],
|
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
)
|
|
|
|
.expect("valid tx");
|
|
|
|
|
|
|
|
let prev = BlockHeader::default();
|
|
|
|
let key_id = ExtKeychain::derive_key_id(0, 0, 0, 0, 0);
|
|
|
|
let mut block = new_block(&[tx], &keychain, &builder, &prev, &key_id);
|
|
|
|
|
|
|
|
// The block should fail validation due to cut-through.
|
|
|
|
assert_eq!(
|
|
|
|
block.validate(&BlindingFactor::zero(), verifier_cache()),
|
|
|
|
Err(Error::Transaction(transaction::Error::CutThrough))
|
|
|
|
);
|
|
|
|
|
|
|
|
// The block should fail lightweight "read" validation due to cut-through.
|
|
|
|
assert_eq!(
|
|
|
|
block.validate_read(),
|
|
|
|
Err(Error::Transaction(transaction::Error::CutThrough))
|
|
|
|
);
|
|
|
|
|
|
|
|
// Apply cut-through to eliminate the offending input and output.
|
|
|
|
let mut inputs: Vec<_> = block.inputs().into();
|
|
|
|
let mut outputs = block.outputs().to_vec();
|
|
|
|
let (inputs, outputs, _, _) = transaction::cut_through(&mut inputs[..], &mut outputs[..])?;
|
|
|
|
|
|
|
|
block.body = block
|
|
|
|
.body
|
|
|
|
.replace_inputs(inputs.into())
|
|
|
|
.replace_outputs(outputs);
|
|
|
|
|
|
|
|
// Block validates successfully after applying cut-through.
|
|
|
|
block.validate(&BlindingFactor::zero(), verifier_cache())?;
|
|
|
|
|
|
|
|
// Block validates via lightweight "read" validation.
|
|
|
|
block.validate_read()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test coverage for verifying cut-through during block validation.
|
|
|
|
// It is not valid for a block to spend an output and produce a new output with the same commitment.
|
|
|
|
// This test covers the case where a coinbase output is spent, producing a plain output with the same commitment.
|
|
|
|
#[test]
|
|
|
|
fn test_verify_cut_through_coinbase() -> Result<(), Error> {
|
|
|
|
global::set_local_chain_type(global::ChainTypes::UserTesting);
|
|
|
|
|
|
|
|
let keychain = ExtKeychain::from_random_seed(false).unwrap();
|
|
|
|
|
|
|
|
let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
|
|
let key_id2 = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
|
|
|
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
|
|
|
|
|
|
|
let builder = ProofBuilder::new(&keychain);
|
|
|
|
|
|
|
|
let tx = build::transaction(
|
|
|
|
KernelFeatures::Plain { fee: 0 },
|
|
|
|
&[
|
|
|
|
build::coinbase_input(consensus::REWARD, key_id1.clone()),
|
|
|
|
build::coinbase_input(consensus::REWARD, key_id2.clone()),
|
|
|
|
build::output(60_000_000_000, key_id1.clone()),
|
|
|
|
build::output(50_000_000_000, key_id2.clone()),
|
|
|
|
build::output(10_000_000_000, key_id3.clone()),
|
|
|
|
],
|
|
|
|
&keychain,
|
|
|
|
&builder,
|
|
|
|
)
|
|
|
|
.expect("valid tx");
|
|
|
|
|
|
|
|
let prev = BlockHeader::default();
|
|
|
|
let key_id = ExtKeychain::derive_key_id(0, 0, 0, 0, 0);
|
|
|
|
let mut block = new_block(&[tx], &keychain, &builder, &prev, &key_id);
|
|
|
|
|
|
|
|
// The block should fail validation due to cut-through.
|
|
|
|
assert_eq!(
|
|
|
|
block.validate(&BlindingFactor::zero(), verifier_cache()),
|
|
|
|
Err(Error::Transaction(transaction::Error::CutThrough))
|
|
|
|
);
|
|
|
|
|
|
|
|
// The block should fail lightweight "read" validation due to cut-through.
|
|
|
|
assert_eq!(
|
|
|
|
block.validate_read(),
|
|
|
|
Err(Error::Transaction(transaction::Error::CutThrough))
|
|
|
|
);
|
|
|
|
|
|
|
|
// Apply cut-through to eliminate the offending input and output.
|
|
|
|
let mut inputs: Vec<_> = block.inputs().into();
|
|
|
|
let mut outputs = block.outputs().to_vec();
|
|
|
|
let (inputs, outputs, _, _) = transaction::cut_through(&mut inputs[..], &mut outputs[..])?;
|
|
|
|
|
|
|
|
block.body = block
|
|
|
|
.body
|
|
|
|
.replace_inputs(inputs.into())
|
|
|
|
.replace_outputs(outputs);
|
|
|
|
|
|
|
|
// Block validates successfully after applying cut-through.
|
|
|
|
block.validate(&BlindingFactor::zero(), verifier_cache())?;
|
|
|
|
|
|
|
|
// Block validates via lightweight "read" validation.
|
|
|
|
block.validate_read()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|