Consolidate validation in Block and Transaction (#1354)

* Consolidate validation in Block and Transaction

Introduce TransactionBody which is included into block and tx.
Fixes #1333
This commit is contained in:
hashmap
2018-08-15 23:14:48 +02:00
committed by GitHub
parent 37fa413329
commit 99a66c1960
20 changed files with 570 additions and 476 deletions
+82 -136
View File
@@ -21,14 +21,14 @@ use std::collections::HashSet;
use std::fmt;
use std::iter::FromIterator;
use consensus::{self, reward, VerifySortOrder, REWARD};
use consensus::{self, reward, REWARD};
use core::committed::{self, Committed};
use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH};
use core::id::ShortIdentifiable;
use core::target::Difficulty;
use core::{
transaction, Commitment, Input, KernelFeatures, Output, OutputFeatures, Proof, ShortId,
Transaction, TxKernel,
Transaction, TransactionBody, TxKernel,
};
use global;
use keychain::{self, BlindingFactor};
@@ -350,13 +350,8 @@ impl Readable for CompactBlock {
pub struct Block {
/// The header with metadata and commitments to the rest of the data
pub header: BlockHeader,
/// List of transaction inputs
pub inputs: Vec<Input>,
/// List of transaction outputs
pub outputs: Vec<Output>,
/// List of kernels with associated proofs (note these are offset from
/// tx_kernels)
pub kernels: Vec<TxKernel>,
/// The body - inputs/outputs/kernels
body: TransactionBody,
}
/// Implementation of Writeable for a block, defines how to write the block to a
@@ -367,22 +362,7 @@ impl Writeable for Block {
self.header.write(writer)?;
if writer.serialization_mode() != ser::SerializationMode::Hash {
ser_multiwrite!(
writer,
[write_u64, self.inputs.len() as u64],
[write_u64, self.outputs.len() as u64],
[write_u64, self.kernels.len() as u64]
);
let mut inputs = self.inputs.clone();
let mut outputs = self.outputs.clone();
let mut kernels = self.kernels.clone();
// Consensus rule that everything is sorted in lexicographical order on the
// wire.
inputs.write_sorted(writer)?;
outputs.write_sorted(writer)?;
kernels.write_sorted(writer)?;
self.body.write(writer)?;
}
Ok(())
}
@@ -394,25 +374,11 @@ impl Readable for Block {
fn read(reader: &mut Reader) -> Result<Block, ser::Error> {
let header = BlockHeader::read(reader)?;
let (input_len, output_len, kernel_len) =
ser_multiread!(reader, read_u64, read_u64, read_u64);
let inputs = read_and_verify_sorted(reader, input_len)?;
let outputs = read_and_verify_sorted(reader, output_len)?;
let kernels = read_and_verify_sorted(reader, kernel_len)?;
// TODO - we do not verify the input|output|kernel counts here.
// I think should call block.validate() as part of a call to read()
// but block.validate() as it stands currently requires the previous sums etc.
// So there is no easy way to do this in isolation.
// Maybe we need two variations of validate() where one handles the validation
// rules that *can* be done in isolation.
let body = TransactionBody::read(reader)?;
body.validate(true).map_err(|_| ser::Error::CorruptedData)?;
Ok(Block {
header: header,
inputs: inputs,
outputs: outputs,
kernels: kernels,
body: body,
})
}
}
@@ -421,15 +387,15 @@ impl Readable for Block {
/// Pedersen commitment.
impl Committed for Block {
fn inputs_committed(&self) -> Vec<Commitment> {
self.inputs.iter().map(|x| x.commitment()).collect()
self.body.inputs_committed()
}
fn outputs_committed(&self) -> Vec<Commitment> {
self.outputs.iter().map(|x| x.commitment()).collect()
self.body.outputs_committed()
}
fn kernels_committed(&self) -> Vec<Commitment> {
self.kernels.iter().map(|x| x.excess()).collect()
self.body.kernels_committed()
}
}
@@ -438,9 +404,7 @@ impl Default for Block {
fn default() -> Block {
Block {
header: Default::default(),
inputs: vec![],
outputs: vec![],
kernels: vec![],
body: Default::default(),
}
}
}
@@ -488,9 +452,10 @@ impl Block {
// collect all the inputs, outputs and kernels from the txs
for tx in txs {
all_inputs.extend(tx.inputs);
all_outputs.extend(tx.outputs);
all_kernels.extend(tx.kernels);
let tb: TransactionBody = tx.into();
all_inputs.extend(tb.inputs);
all_outputs.extend(tb.outputs);
all_kernels.extend(tb.kernels);
}
// include the coinbase output(s) and kernel(s) from the compact_block
@@ -512,9 +477,7 @@ impl Block {
// leave it to the caller to actually validate the block
Block {
header: cb.header,
inputs: all_inputs,
outputs: all_outputs,
kernels: all_kernels,
body: TransactionBody::new(all_inputs, all_outputs, all_kernels),
}.cut_through()
}
@@ -524,6 +487,7 @@ impl Block {
let nonce = thread_rng().next_u64();
let mut out_full = self
.body
.outputs
.iter()
.filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
@@ -533,7 +497,7 @@ impl Block {
let mut kern_full = vec![];
let mut kern_ids = vec![];
for k in &self.kernels {
for k in self.kernels() {
if k.features.contains(KernelFeatures::COINBASE_KERNEL) {
kern_full.push(k.clone());
} else {
@@ -555,6 +519,14 @@ impl Block {
}
}
/// Build a new empty block from a specified header
pub fn with_header(header: BlockHeader) -> Block {
Block {
header: header,
..Default::default()
}
}
/// 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.
@@ -577,7 +549,7 @@ impl Block {
let zero_commit = secp_static::commit_to_zero_value();
let secp = static_secp_instance();
let secp = secp.lock().unwrap();
let mut excesses = map_vec!(agg_tx.kernels, |x| x.excess());
let mut excesses = map_vec!(agg_tx.kernels(), |x| x.excess());
excesses.push(prev.total_kernel_sum);
excesses.retain(|x| *x != zero_commit);
secp.commit_sum(excesses, vec![])?
@@ -593,12 +565,40 @@ impl Block {
total_kernel_sum,
..Default::default()
},
inputs: agg_tx.inputs,
outputs: agg_tx.outputs,
kernels: agg_tx.kernels,
body: agg_tx.into(),
}.cut_through())
}
/// Get inputs
pub fn inputs(&self) -> &Vec<Input> {
&self.body.inputs
}
/// Get inputs mutable
pub fn inputs_mut(&mut self) -> &mut Vec<Input> {
&mut self.body.inputs
}
/// Get outputs
pub fn outputs(&self) -> &Vec<Output> {
&self.body.outputs
}
/// Get outputs mutable
pub fn outputs_mut(&mut self) -> &mut Vec<Output> {
&mut self.body.outputs
}
/// Get kernels
pub fn kernels(&self) -> &Vec<TxKernel> {
&self.body.kernels
}
/// Get kernels mut
pub fn kernels_mut(&mut self) -> &mut Vec<TxKernel> {
&mut self.body.kernels
}
/// Blockhash, computed using only the POW
pub fn hash(&self) -> Hash {
self.header.hash()
@@ -606,7 +606,7 @@ impl Block {
/// Sum of all fees (inputs less outputs) in the block
pub fn total_fees(&self) -> u64 {
self.kernels.iter().map(|p| p.fee).sum()
self.body.kernels.iter().map(|p| p.fee).sum()
}
/// Matches any output with a potential spending input, eliminating them
@@ -621,12 +621,14 @@ impl Block {
///
pub fn cut_through(self) -> Block {
let in_set = self
.body
.inputs
.iter()
.map(|inp| inp.commitment())
.collect::<HashSet<_>>();
let out_set = self
.body
.outputs
.iter()
.filter(|out| !out.features.contains(OutputFeatures::COINBASE_OUTPUT))
@@ -636,12 +638,14 @@ impl Block {
let to_cut_through = in_set.intersection(&out_set).collect::<HashSet<_>>();
let new_inputs = self
.body
.inputs
.into_iter()
.filter(|inp| !to_cut_through.contains(&inp.commitment()))
.collect::<Vec<_>>();
let new_outputs = self
.body
.outputs
.into_iter()
.filter(|out| !to_cut_through.contains(&out.commitment()))
@@ -653,9 +657,7 @@ impl Block {
total_difficulty: self.header.total_difficulty,
..self.header
},
inputs: new_inputs,
outputs: new_outputs,
kernels: self.kernels,
body: TransactionBody::new(new_inputs, new_outputs, self.body.kernels),
}
}
@@ -667,17 +669,13 @@ impl Block {
prev_kernel_offset: &BlindingFactor,
prev_kernel_sum: &Commitment,
) -> Result<(Commitment), Error> {
// Verify we do not exceed the max number of inputs|outputs|kernels
// and that the "weight" based on these does not exceed the max permitted weight.
self.verify_weight()?;
self.body.validate(true)?;
self.verify_sorted()?;
self.verify_cut_through()?;
self.verify_coinbase()?;
self.verify_kernel_lock_heights()?;
self.verify_coinbase()?;
// take the kernel offset for this block (block offset minus previous) and
// verify outputs and kernel sums
// verify.body.outputs and kernel sums
let block_kernel_offset = if self.header.total_kernel_offset() == prev_kernel_offset.clone()
{
// special case when the sum hasn't changed (typically an empty block),
@@ -698,85 +696,22 @@ impl Block {
return Err(Error::InvalidTotalKernelSum);
}
self.verify_rangeproofs()?;
self.verify_kernel_signatures()?;
Ok(kernel_sum)
}
// Verify the block is not too big in terms of number of inputs|outputs|kernels.
fn verify_weight(&self) -> Result<(), Error> {
let tx_block_weight = self.inputs.len() * consensus::BLOCK_INPUT_WEIGHT
+ self.outputs.len() * consensus::BLOCK_OUTPUT_WEIGHT
+ self.kernels.len() * consensus::BLOCK_KERNEL_WEIGHT;
if tx_block_weight > consensus::MAX_BLOCK_WEIGHT {
return Err(Error::TooHeavy);
}
Ok(())
}
// Verify that inputs|outputs|kernels are all sorted in lexicographical order.
fn verify_sorted(&self) -> Result<(), Error> {
self.inputs.verify_sort_order()?;
self.outputs.verify_sort_order()?;
self.kernels.verify_sort_order()?;
Ok(())
}
// Verify that no input is spending an output from the same block.
fn verify_cut_through(&self) -> Result<(), Error> {
for inp in &self.inputs {
if self
.outputs
.iter()
.any(|out| out.commitment() == inp.commitment())
{
return Err(Error::CutThrough);
}
}
Ok(())
}
fn verify_kernel_lock_heights(&self) -> Result<(), Error> {
for k in &self.kernels {
// check we have no kernels with lock_heights greater than current height
// no tx can be included in a block earlier than its lock_height
if k.lock_height > self.header.height {
return Err(Error::KernelLockHeight(k.lock_height));
}
}
Ok(())
}
/// Verify the kernel signatures.
/// Note: this is expensive.
fn verify_kernel_signatures(&self) -> Result<(), Error> {
for x in &self.kernels {
x.verify()?;
}
Ok(())
}
/// Verify all the output rangeproofs.
/// Note: this is expensive.
fn verify_rangeproofs(&self) -> Result<(), Error> {
for x in &self.outputs {
x.verify_proof()?;
}
Ok(())
}
/// Validate the coinbase outputs generated by miners.
/// Validate the coinbase.body.outputs generated by miners.
/// Check the sum of coinbase-marked outputs match
/// the sum of coinbase-marked kernels accounting for fees.
pub fn verify_coinbase(&self) -> Result<(), Error> {
let cb_outs = self
.body
.outputs
.iter()
.filter(|out| out.features.contains(OutputFeatures::COINBASE_OUTPUT))
.collect::<Vec<&Output>>();
let cb_kerns = self
.body
.kernels
.iter()
.filter(|kernel| kernel.features.contains(KernelFeatures::COINBASE_KERNEL))
@@ -802,4 +737,15 @@ impl Block {
Ok(())
}
fn verify_kernel_lock_heights(&self) -> Result<(), Error> {
for k in &self.body.kernels {
// check we have no kernels with lock_heights greater than current height
// no tx can be included in a block earlier than its lock_height
if k.lock_height > self.header.height {
return Err(Error::KernelLockHeight(k.lock_height));
}
}
Ok(())
}
}
+267 -109
View File
@@ -43,7 +43,7 @@ bitflags! {
}
}
/// Errors thrown by Block validation
/// Errors thrown by Transaction validation
#[derive(Clone, Eq, Debug, PartialEq)]
pub enum Error {
/// Underlying Secp256k1 error (signature validation or invalid public key
@@ -240,35 +240,28 @@ impl PMMRable for TxKernel {
}
}
/// A transaction
/// TransactionBody is acommon abstraction for transaction and block
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Transaction {
pub struct TransactionBody {
/// List of inputs spent by the transaction.
pub inputs: Vec<Input>,
/// List of outputs the transaction produces.
pub outputs: Vec<Output>,
/// List of kernels that make up this transaction (usually a single kernel).
pub kernels: Vec<TxKernel>,
/// The kernel "offset" k2
/// excess is k1G after splitting the key k = k1 + k2
pub offset: BlindingFactor,
}
/// PartialEq
impl PartialEq for Transaction {
fn eq(&self, tx: &Transaction) -> bool {
self.inputs == tx.inputs
&& self.outputs == tx.outputs
&& self.kernels == tx.kernels
&& self.offset == tx.offset
impl PartialEq for TransactionBody {
fn eq(&self, l: &TransactionBody) -> bool {
self.inputs == l.inputs && self.outputs == l.outputs && self.kernels == l.kernels
}
}
/// Implementation of Writeable for a fully blinded transaction, defines how to
/// write the transaction as binary.
impl Writeable for Transaction {
/// Implementation of Writeable for a body, defines how to
/// write the body as binary.
impl Writeable for TransactionBody {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
self.offset.write(writer)?;
ser_multiwrite!(
writer,
[write_u64, self.inputs.len() as u64],
@@ -290,12 +283,10 @@ impl Writeable for Transaction {
}
}
/// Implementation of Readable for a transaction, defines how to read a full
/// transaction from a binary stream.
impl Readable for Transaction {
fn read(reader: &mut Reader) -> Result<Transaction, ser::Error> {
let offset = BlindingFactor::read(reader)?;
/// Implementation of Readable for a body, defines how to read a
/// body from a binary stream.
impl Readable for TransactionBody {
fn read(reader: &mut Reader) -> Result<TransactionBody, ser::Error> {
let (input_len, output_len, kernel_len) =
ser_multiread!(reader, read_u64, read_u64, read_u64);
@@ -303,24 +294,17 @@ impl Readable for Transaction {
let outputs = read_and_verify_sorted(reader, output_len)?;
let kernels = read_and_verify_sorted(reader, kernel_len)?;
let tx = Transaction {
offset,
let body = TransactionBody {
inputs,
outputs,
kernels,
};
// Now validate the tx.
// Treat any validation issues as data corruption.
// An example of this would be reading a tx
// that exceeded the allowed number of inputs.
tx.validate(false).map_err(|_| ser::Error::CorruptedData)?;
Ok(tx)
Ok(body)
}
}
impl Committed for Transaction {
impl Committed for TransactionBody {
fn inputs_committed(&self) -> Vec<Commitment> {
self.inputs.iter().map(|x| x.commitment()).collect()
}
@@ -334,17 +318,16 @@ impl Committed for Transaction {
}
}
impl Default for Transaction {
fn default() -> Transaction {
Transaction::empty()
impl Default for TransactionBody {
fn default() -> TransactionBody {
TransactionBody::empty()
}
}
impl Transaction {
impl TransactionBody {
/// Creates a new empty transaction (no inputs or outputs, zero fee).
pub fn empty() -> Transaction {
Transaction {
offset: BlindingFactor::zero(),
pub fn empty() -> TransactionBody {
TransactionBody {
inputs: vec![],
outputs: vec![],
kernels: vec![],
@@ -353,49 +336,43 @@ impl Transaction {
/// Creates a new transaction initialized with
/// the provided inputs, outputs, kernels
pub fn new(inputs: Vec<Input>, outputs: Vec<Output>, kernels: Vec<TxKernel>) -> Transaction {
Transaction {
offset: BlindingFactor::zero(),
pub fn new(
inputs: Vec<Input>,
outputs: Vec<Output>,
kernels: Vec<TxKernel>,
) -> TransactionBody {
TransactionBody {
inputs: inputs,
outputs: outputs,
kernels: kernels,
}
}
/// Creates a new transaction using this transaction as a template
/// and with the specified offset.
pub fn with_offset(self, offset: BlindingFactor) -> Transaction {
Transaction {
offset: offset,
..self
}
}
/// Builds a new transaction with the provided inputs added. Existing
/// Builds a new body with the provided inputs added. Existing
/// inputs, if any, are kept intact.
pub fn with_input(self, input: Input) -> Transaction {
pub fn with_input(self, input: Input) -> TransactionBody {
let mut new_ins = self.inputs;
new_ins.push(input);
new_ins.sort();
Transaction {
TransactionBody {
inputs: new_ins,
..self
}
}
/// Builds a new transaction with the provided output added. Existing
/// Builds a new TransactionBody with the provided output added. Existing
/// outputs, if any, are kept intact.
pub fn with_output(self, output: Output) -> Transaction {
pub fn with_output(self, output: Output) -> TransactionBody {
let mut new_outs = self.outputs;
new_outs.push(output);
new_outs.sort();
Transaction {
TransactionBody {
outputs: new_outs,
..self
}
}
/// Total fee for a transaction is the sum of fees of all kernels.
/// Total fee for a TransactionBody is the sum of fees of all kernels.
pub fn fee(&self) -> u64 {
self.kernels
.iter()
@@ -406,7 +383,21 @@ impl Transaction {
self.fee() as i64
}
/// Lock height of a transaction is the max lock height of the kernels.
/// Calculate transaction weight
pub fn body_weight(&self) -> u32 {
TransactionBody::weight(self.inputs.len(), self.outputs.len())
}
/// Calculate transaction weight from transaction details
pub fn weight(input_len: usize, output_len: usize) -> u32 {
let mut body_weight = -1 * (input_len as i32) + (4 * output_len as i32) + 1;
if body_weight < 1 {
body_weight = 1;
}
body_weight as u32
}
/// Lock height of a body is the max lock height of the kernels.
pub fn lock_height(&self) -> u64 {
self.kernels
.iter()
@@ -431,13 +422,14 @@ impl Transaction {
Ok(())
}
// Verify the tx is not too big in terms of number of inputs|outputs|kernels.
fn verify_weight(&self) -> Result<(), Error> {
// check the tx as if it was a block, with an additional output and
// Verify the body is not too big in terms of number of inputs|outputs|kernels.
fn verify_weight(&self, with_reward: bool) -> Result<(), Error> {
// if as_block check the body as if it was a block, with an additional output and
// kernel for reward
let reserve = if with_reward { 0 } else { 1 };
let tx_block_weight = self.inputs.len() * consensus::BLOCK_INPUT_WEIGHT
+ (self.outputs.len() + 1) * consensus::BLOCK_OUTPUT_WEIGHT
+ (self.kernels.len() + 1) * consensus::BLOCK_KERNEL_WEIGHT;
+ (self.outputs.len() + reserve) * consensus::BLOCK_OUTPUT_WEIGHT
+ (self.kernels.len() + reserve) * consensus::BLOCK_KERNEL_WEIGHT;
if tx_block_weight > consensus::MAX_BLOCK_WEIGHT {
return Err(Error::TooHeavy);
@@ -445,36 +437,6 @@ impl Transaction {
Ok(())
}
/// Validates all relevant parts of a fully built transaction. Checks the
/// excess value against the signature as well as range proofs for each
/// output.
pub fn validate(&self, as_block: bool) -> Result<(), Error> {
if !as_block {
self.verify_features()?;
self.verify_weight()?;
self.verify_kernel_sums(self.overage(), self.offset)?;
}
self.verify_sorted()?;
self.verify_cut_through()?;
self.verify_rangeproofs()?;
self.verify_kernel_signatures()?;
Ok(())
}
/// Calculate transaction weight
pub fn tx_weight(&self) -> u32 {
Transaction::weight(self.inputs.len(), self.outputs.len())
}
/// Calculate transaction weight from transaction details
pub fn weight(input_len: usize, output_len: usize) -> u32 {
let mut tx_weight = -1 * (input_len as i32) + (4 * output_len as i32) + 1;
if tx_weight < 1 {
tx_weight = 1;
}
tx_weight as u32
}
// Verify that inputs|outputs|kernels are all sorted in lexicographical order.
fn verify_sorted(&self) -> Result<(), Error> {
self.inputs.verify_sort_order()?;
@@ -497,10 +459,10 @@ impl Transaction {
Ok(())
}
// Verify we have no invalid outputs or kernels in the transaction
// due to invalid features.
// Specifically, a transaction cannot contain a coinbase output or a coinbase kernel.
fn verify_features(&self) -> Result<(), Error> {
/// Verify we have no invalid outputs or kernels in the transaction
/// due to invalid features.
/// Specifically, a transaction cannot contain a coinbase output or a coinbase kernel.
pub fn verify_features(&self) -> Result<(), Error> {
self.verify_output_features()?;
self.verify_kernel_features()?;
Ok(())
@@ -529,6 +491,202 @@ impl Transaction {
}
Ok(())
}
/// Validates all relevant parts of a transaction body. Checks the
/// excess value against the signature as well as range proofs for each
/// output.
pub fn validate(&self, with_reward: bool) -> Result<(), Error> {
self.verify_weight(with_reward)?;
self.verify_sorted()?;
self.verify_cut_through()?;
self.verify_rangeproofs()?;
self.verify_kernel_signatures()?;
Ok(())
}
}
/// A transaction
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Transaction {
/// The kernel "offset" k2
/// excess is k1G after splitting the key k = k1 + k2
pub offset: BlindingFactor,
/// The transaction body - inputs/outputs/kernels
body: TransactionBody,
}
/// PartialEq
impl PartialEq for Transaction {
fn eq(&self, tx: &Transaction) -> bool {
self.body == tx.body && self.offset == tx.offset
}
}
impl Into<TransactionBody> for Transaction {
fn into(self) -> TransactionBody {
self.body
}
}
/// Implementation of Writeable for a fully blinded transaction, defines how to
/// write the transaction as binary.
impl Writeable for Transaction {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
self.offset.write(writer)?;
self.body.write(writer)?;
Ok(())
}
}
/// Implementation of Readable for a transaction, defines how to read a full
/// transaction from a binary stream.
impl Readable for Transaction {
fn read(reader: &mut Reader) -> Result<Transaction, ser::Error> {
let offset = BlindingFactor::read(reader)?;
let body = TransactionBody::read(reader)?;
let tx = Transaction { offset, body };
// Now validate the tx.
// Treat any validation issues as data corruption.
// An example of this would be reading a tx
// that exceeded the allowed number of inputs.
tx.validate(false).map_err(|_| ser::Error::CorruptedData)?;
Ok(tx)
}
}
impl Committed for Transaction {
fn inputs_committed(&self) -> Vec<Commitment> {
self.body.inputs_committed()
}
fn outputs_committed(&self) -> Vec<Commitment> {
self.body.outputs_committed()
}
fn kernels_committed(&self) -> Vec<Commitment> {
self.body.kernels_committed()
}
}
impl Default for Transaction {
fn default() -> Transaction {
Transaction::empty()
}
}
impl Transaction {
/// Creates a new empty transaction (no inputs or outputs, zero fee).
pub fn empty() -> Transaction {
Transaction {
offset: BlindingFactor::zero(),
body: Default::default(),
}
}
/// Creates a new transaction initialized with
/// the provided inputs, outputs, kernels
pub fn new(inputs: Vec<Input>, outputs: Vec<Output>, kernels: Vec<TxKernel>) -> Transaction {
Transaction {
offset: BlindingFactor::zero(),
body: TransactionBody::new(inputs, outputs, kernels),
}
}
/// Creates a new transaction using this transaction as a template
/// and with the specified offset.
pub fn with_offset(self, offset: BlindingFactor) -> Transaction {
Transaction {
offset: offset,
..self
}
}
/// Builds a new transaction with the provided inputs added. Existing
/// inputs, if any, are kept intact.
pub fn with_input(self, input: Input) -> Transaction {
Transaction {
body: self.body.with_input(input),
..self
}
}
/// Builds a new transaction with the provided output added. Existing
/// outputs, if any, are kept intact.
pub fn with_output(self, output: Output) -> Transaction {
Transaction {
body: self.body.with_output(output),
..self
}
}
/// Get inputs
pub fn inputs(&self) -> &Vec<Input> {
&self.body.inputs
}
/// Get inputs mutable
pub fn inputs_mut(&mut self) -> &mut Vec<Input> {
&mut self.body.inputs
}
/// Get outputs
pub fn outputs(&self) -> &Vec<Output> {
&self.body.outputs
}
/// Get outputs mutable
pub fn outputs_mut(&mut self) -> &mut Vec<Output> {
&mut self.body.outputs
}
/// Get kernels
pub fn kernels(&self) -> &Vec<TxKernel> {
&self.body.kernels
}
/// Get kernels mut
pub fn kernels_mut(&mut self) -> &mut Vec<TxKernel> {
&mut self.body.kernels
}
/// Total fee for a transaction is the sum of fees of all kernels.
pub fn fee(&self) -> u64 {
self.body.fee()
}
fn overage(&self) -> i64 {
self.body.overage()
}
/// Lock height of a transaction is the max lock height of the kernels.
pub fn lock_height(&self) -> u64 {
self.body.lock_height()
}
/// Validates all relevant parts of a fully built transaction. Checks the
/// excess value against the signature as well as range proofs for each
/// output.
pub fn validate(&self, with_reward: bool) -> Result<(), Error> {
self.body.validate(with_reward)?;
if !with_reward {
self.body.verify_features()?;
self.verify_kernel_sums(self.overage(), self.offset)?;
}
Ok(())
}
/// Calculate transaction weight
pub fn tx_weight(&self) -> u32 {
self.body.body_weight()
}
/// Calculate transaction weight from transaction details
pub fn weight(input_len: usize, output_len: usize) -> u32 {
TransactionBody::weight(input_len, output_len)
}
}
/// Aggregate a vec of transactions into a multi-kernel transaction with
@@ -550,11 +708,11 @@ pub fn aggregate(
// we will sum these later to give a single aggregate offset
kernel_offsets.push(transaction.offset);
inputs.append(&mut transaction.inputs);
outputs.append(&mut transaction.outputs);
kernels.append(&mut transaction.kernels);
inputs.append(&mut transaction.body.inputs);
outputs.append(&mut transaction.body.outputs);
kernels.append(&mut transaction.body.kernels);
}
let as_block = reward.is_some();
let with_reward = reward.is_some();
if let Some((out, kernel)) = reward {
outputs.push(out);
kernels.push(kernel);
@@ -604,7 +762,7 @@ pub fn aggregate(
// The resulting tx could be invalid for a variety of reasons -
// * tx too large (too many inputs|outputs|kernels)
// * cut-through may have invalidated the sums
tx.validate(as_block)?;
tx.validate(with_reward)?;
Ok(tx)
}
@@ -622,18 +780,18 @@ pub fn deaggregate(mk_tx: Transaction, txs: Vec<Transaction>) -> Result<Transact
let tx = aggregate(txs, None)?;
for mk_input in mk_tx.inputs {
if !tx.inputs.contains(&mk_input) && !inputs.contains(&mk_input) {
for mk_input in mk_tx.body.inputs {
if !tx.body.inputs.contains(&mk_input) && !inputs.contains(&mk_input) {
inputs.push(mk_input);
}
}
for mk_output in mk_tx.outputs {
if !tx.outputs.contains(&mk_output) && !outputs.contains(&mk_output) {
for mk_output in mk_tx.body.outputs {
if !tx.body.outputs.contains(&mk_output) && !outputs.contains(&mk_output) {
outputs.push(mk_output);
}
}
for mk_kernel in mk_tx.kernels {
if !tx.kernels.contains(&mk_kernel) && !kernels.contains(&mk_kernel) {
for mk_kernel in mk_tx.body.kernels {
if !tx.body.kernels.contains(&mk_kernel) && !kernels.contains(&mk_kernel) {
kernels.push(mk_kernel);
}
}
+61 -87
View File
@@ -25,109 +25,83 @@ use global;
/// is small enough to mine it on the fly, so it does not contain its own
/// proof of work solution. Can also be easily mutated for different tests.
pub fn genesis_dev() -> core::Block {
core::Block {
header: core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(1997, 8, 4).and_hms(0, 0, 0),
nonce: global::get_genesis_nonce(),
..Default::default()
},
inputs: vec![],
outputs: vec![],
kernels: vec![],
}
core::Block::with_header(core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(1997, 8, 4).and_hms(0, 0, 0),
nonce: global::get_genesis_nonce(),
..Default::default()
})
}
/// First testnet genesis block, still subject to change (especially the date,
/// will hopefully come before Christmas).
pub fn genesis_testnet1() -> core::Block {
core::Block {
header: core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2017, 11, 16).and_hms(20, 0, 0),
nonce: 28205,
pow: core::Proof::new(vec![
0x21e, 0x7a2, 0xeae, 0x144e, 0x1b1c, 0x1fbd, 0x203a, 0x214b, 0x293b, 0x2b74,
0x2bfa, 0x2c26, 0x32bb, 0x346a, 0x34c7, 0x37c5, 0x4164, 0x42cc, 0x4cc3, 0x55af,
0x5a70, 0x5b14, 0x5e1c, 0x5f76, 0x6061, 0x60f9, 0x61d7, 0x6318, 0x63a1, 0x63fb,
0x649b, 0x64e5, 0x65a1, 0x6b69, 0x70f8, 0x71c7, 0x71cd, 0x7492, 0x7b11, 0x7db8,
0x7f29, 0x7ff8,
]),
..Default::default()
},
inputs: vec![],
outputs: vec![],
kernels: vec![],
}
core::Block::with_header(core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2017, 11, 16).and_hms(20, 0, 0),
nonce: 28205,
pow: core::Proof::new(vec![
0x21e, 0x7a2, 0xeae, 0x144e, 0x1b1c, 0x1fbd, 0x203a, 0x214b, 0x293b, 0x2b74, 0x2bfa,
0x2c26, 0x32bb, 0x346a, 0x34c7, 0x37c5, 0x4164, 0x42cc, 0x4cc3, 0x55af, 0x5a70, 0x5b14,
0x5e1c, 0x5f76, 0x6061, 0x60f9, 0x61d7, 0x6318, 0x63a1, 0x63fb, 0x649b, 0x64e5, 0x65a1,
0x6b69, 0x70f8, 0x71c7, 0x71cd, 0x7492, 0x7b11, 0x7db8, 0x7f29, 0x7ff8,
]),
..Default::default()
})
}
/// Second testnet genesis block (cuckoo30).
pub fn genesis_testnet2() -> core::Block {
core::Block {
header: core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 3, 26).and_hms(16, 0, 0),
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
nonce: 1060,
pow: core::Proof::new(vec![
0x1940730, 0x333b9d0, 0x4739d6f, 0x4c6cfb1, 0x6e3d6c3, 0x74408a3, 0x7ba2bd2,
0x83e2024, 0x8ca22b5, 0x9d39ab8, 0xb6646dd, 0xc6698b6, 0xc6f78fe, 0xc99b662,
0xcf2ae8c, 0xcf41eed, 0xdd073e6, 0xded6af8, 0xf08d1a5, 0x1156a144, 0x11d1160a,
0x131bb0a5, 0x137ad703, 0x13b0831f, 0x1421683f, 0x147e3c1f, 0x1496fda0, 0x150ba22b,
0x15cc5bc6, 0x16edf697, 0x17ced40c, 0x17d84f9e, 0x18a515c1, 0x19320d9c, 0x19da4f6d,
0x1b50bcb1, 0x1b8bc72f, 0x1c7b6964, 0x1d07b3a9, 0x1d189d4d, 0x1d1f9a15, 0x1dafcd41,
]),
..Default::default()
},
inputs: vec![],
outputs: vec![],
kernels: vec![],
}
core::Block::with_header(core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 3, 26).and_hms(16, 0, 0),
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
nonce: 1060,
pow: core::Proof::new(vec![
0x1940730, 0x333b9d0, 0x4739d6f, 0x4c6cfb1, 0x6e3d6c3, 0x74408a3, 0x7ba2bd2, 0x83e2024,
0x8ca22b5, 0x9d39ab8, 0xb6646dd, 0xc6698b6, 0xc6f78fe, 0xc99b662, 0xcf2ae8c, 0xcf41eed,
0xdd073e6, 0xded6af8, 0xf08d1a5, 0x1156a144, 0x11d1160a, 0x131bb0a5, 0x137ad703,
0x13b0831f, 0x1421683f, 0x147e3c1f, 0x1496fda0, 0x150ba22b, 0x15cc5bc6, 0x16edf697,
0x17ced40c, 0x17d84f9e, 0x18a515c1, 0x19320d9c, 0x19da4f6d, 0x1b50bcb1, 0x1b8bc72f,
0x1c7b6964, 0x1d07b3a9, 0x1d189d4d, 0x1d1f9a15, 0x1dafcd41,
]),
..Default::default()
})
}
/// Second testnet genesis block (cuckoo30). Temporary values for now.
pub fn genesis_testnet3() -> core::Block {
core::Block {
header: core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 7, 8).and_hms(18, 0, 0),
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
nonce: 4956988373127691,
pow: core::Proof::new(vec![
0xa420dc, 0xc8ffee, 0x10e433e, 0x1de9428, 0x2ed4cea, 0x52d907b, 0x5af0e3f,
0x6b8fcae, 0x8319b53, 0x845ca8c, 0x8d2a13e, 0x8d6e4cc, 0x9349e8d, 0xa7a33c5,
0xaeac3cb, 0xb193e23, 0xb502e19, 0xb5d9804, 0xc9ac184, 0xd4f4de3, 0xd7a23b8,
0xf1d8660, 0xf443756, 0x10b833d2, 0x11418fc5, 0x11b8aeaf, 0x131836ec, 0x132ab818,
0x13a46a55, 0x13df89fe, 0x145d65b5, 0x166f9c3a, 0x166fe0ef, 0x178cb36f, 0x185baf68,
0x1bbfe563, 0x1bd637b4, 0x1cfc8382, 0x1d1ed012, 0x1e391ca5, 0x1e999b4c, 0x1f7c6d21,
]),
..Default::default()
},
inputs: vec![],
outputs: vec![],
kernels: vec![],
}
core::Block::with_header(core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 7, 8).and_hms(18, 0, 0),
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
nonce: 4956988373127691,
pow: core::Proof::new(vec![
0xa420dc, 0xc8ffee, 0x10e433e, 0x1de9428, 0x2ed4cea, 0x52d907b, 0x5af0e3f, 0x6b8fcae,
0x8319b53, 0x845ca8c, 0x8d2a13e, 0x8d6e4cc, 0x9349e8d, 0xa7a33c5, 0xaeac3cb, 0xb193e23,
0xb502e19, 0xb5d9804, 0xc9ac184, 0xd4f4de3, 0xd7a23b8, 0xf1d8660, 0xf443756,
0x10b833d2, 0x11418fc5, 0x11b8aeaf, 0x131836ec, 0x132ab818, 0x13a46a55, 0x13df89fe,
0x145d65b5, 0x166f9c3a, 0x166fe0ef, 0x178cb36f, 0x185baf68, 0x1bbfe563, 0x1bd637b4,
0x1cfc8382, 0x1d1ed012, 0x1e391ca5, 0x1e999b4c, 0x1f7c6d21,
]),
..Default::default()
})
}
/// Placeholder for mainnet genesis block, will definitely change before
/// release so no use trying to pre-mine it.
pub fn genesis_main() -> core::Block {
core::Block {
header: core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 8, 14).and_hms(0, 0, 0),
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
nonce: global::get_genesis_nonce(),
pow: core::Proof::zero(consensus::PROOFSIZE),
..Default::default()
},
inputs: vec![],
outputs: vec![],
kernels: vec![],
}
core::Block::with_header(core::BlockHeader {
height: 0,
previous: core::hash::Hash([0xff; 32]),
timestamp: Utc.ymd(2018, 8, 14).and_hms(0, 0, 0),
total_difficulty: Difficulty::from_num(global::initial_block_difficulty()),
nonce: global::get_genesis_nonce(),
pow: core::Proof::zero(consensus::PROOFSIZE),
..Default::default()
})
}