iterate once over txs in a block (#233)

Split out verify_sig and build_kernel to make kernel explicit.
This commit is contained in:
AntiochP 2017-11-03 14:17:09 -04:00 committed by Ignotus Peverell
parent 81bd764fd0
commit ad07866275
2 changed files with 62 additions and 42 deletions

View file

@ -25,6 +25,7 @@ use core::{Input, Output, Proof, SwitchCommitHash, Transaction, TxKernel, COINBA
use consensus::{exceeds_weight, reward, MINIMUM_DIFFICULTY, REWARD}; use consensus::{exceeds_weight, reward, MINIMUM_DIFFICULTY, REWARD};
use core::hash::{Hash, Hashed, ZERO_HASH}; use core::hash::{Hash, Hashed, ZERO_HASH};
use core::target::Difficulty; use core::target::Difficulty;
use core::transaction;
use ser::{self, read_and_verify_sorted, Readable, Reader, Writeable, WriteableSorted, Writer}; use ser::{self, read_and_verify_sorted, Readable, Reader, Writeable, WriteableSorted, Writer};
use util::LOGGER; use util::LOGGER;
use global; use global;
@ -47,8 +48,18 @@ pub enum Error {
/// The lock_height causing this validation error /// The lock_height causing this validation error
lock_height: u64, lock_height: u64,
}, },
/// Underlying tx related error
Transaction(transaction::Error),
/// Underlying Secp256k1 error (signature validation or invalid public key typically) /// Underlying Secp256k1 error (signature validation or invalid public key typically)
Secp(secp::Error), Secp(secp::Error),
/// Underlying keychain related error
Keychain(keychain::Error),
}
impl From<transaction::Error> for Error {
fn from(e: transaction::Error) -> Error {
Error::Transaction(e)
}
} }
impl From<secp::Error> for Error { impl From<secp::Error> for Error {
@ -57,6 +68,12 @@ impl From<secp::Error> for Error {
} }
} }
impl From<keychain::Error> for Error {
fn from(e: keychain::Error) -> Error {
Error::Keychain(e)
}
}
/// Block header, fairly standard compared to other blockchains. /// Block header, fairly standard compared to other blockchains.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct BlockHeader { pub struct BlockHeader {
@ -266,7 +283,7 @@ impl Block {
txs: Vec<&Transaction>, txs: Vec<&Transaction>,
keychain: &keychain::Keychain, keychain: &keychain::Keychain,
key_id: &keychain::Identifier, key_id: &keychain::Identifier,
) -> Result<Block, keychain::Error> { ) -> Result<Block, Error> {
let fees = txs.iter().map(|tx| tx.fee).sum(); let fees = txs.iter().map(|tx| tx.fee).sum();
let (reward_out, reward_proof) = Block::reward_output(keychain, key_id, fees)?; 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)?;
@ -281,30 +298,37 @@ impl Block {
txs: Vec<&Transaction>, txs: Vec<&Transaction>,
reward_out: Output, reward_out: Output,
reward_kern: TxKernel, reward_kern: TxKernel,
) -> Result<Block, secp::Error> { ) -> Result<Block, Error> {
// note: the following reads easily but may not be the most efficient due to
// repeated iterations, revisit if a problem
let secp = Secp256k1::with_caps(secp::ContextFlag::Commit); let secp = Secp256k1::with_caps(secp::ContextFlag::Commit);
// validate each transaction and gather their kernels let mut kernels = vec![];
let mut kernels = try_map_vec!(txs, |tx| tx.verify_sig(&secp)); let mut inputs = vec![];
kernels.push(reward_kern); let mut outputs = vec![];
// build vectors with all inputs and all outputs, ordering them by hash // iterate over the all the txs
// needs to be a fold so we don't end up with a vector of vectors and we // build the kernel for each
// want to fully own the refs (not just a pointer like flat_map). // and collect all the kernels, inputs and outputs
let mut inputs = txs.iter().fold(vec![], |mut acc, ref tx| { // to build the block (which we can sort of think of as one big tx?)
let mut inputs = tx.inputs.clone(); for tx in txs {
acc.append(&mut inputs); // validate each transaction and gather their kernels
acc let excess = tx.validate(&secp)?;
}); let kernel = tx.build_kernel(excess);
let mut outputs = txs.iter().fold(vec![], |mut acc, ref tx| { kernels.push(kernel);
let mut outputs = tx.outputs.clone();
acc.append(&mut outputs); for input in tx.inputs.clone() {
acc inputs.push(input);
}); }
for output in tx.outputs.clone() {
outputs.push(output);
}
}
// also include the reward kernel and output
kernels.push(reward_kern);
outputs.push(reward_out); outputs.push(reward_out);
// now sort everything to the block is built deterministically
inputs.sort(); inputs.sort();
outputs.sort(); outputs.sort();
kernels.sort(); kernels.sort();

View file

@ -26,7 +26,6 @@ use core::hash::Hashed;
use core::pmmr::Summable; use core::pmmr::Summable;
use keychain::{Identifier, Keychain}; use keychain::{Identifier, Keychain};
use ser::{self, read_and_verify_sorted, Readable, Reader, Writeable, WriteableSorted, Writer}; use ser::{self, read_and_verify_sorted, Readable, Reader, Writeable, WriteableSorted, Writer};
use util::LOGGER;
/// The size to use for the stored blake2 hash of a switch_commitment /// The size to use for the stored blake2 hash of a switch_commitment
pub const SWITCH_COMMIT_HASH_SIZE: usize = 20; pub const SWITCH_COMMIT_HASH_SIZE: usize = 20;
@ -65,7 +64,7 @@ macro_rules! hashable_ord {
} }
/// Errors thrown by Block validation /// Errors thrown by Block validation
#[derive(Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Error { pub enum Error {
/// Transaction fee can't be odd, due to half fee burning /// Transaction fee can't be odd, due to half fee burning
OddFee, OddFee,
@ -305,7 +304,7 @@ impl Transaction {
/// sum to zero as they should in r.G + v.H then only k.G the excess /// sum to zero as they should in r.G + v.H then only k.G the excess
/// of the sum of r.G should be left. And r.G is the definition of a /// of the sum of r.G should be left. And r.G is the definition of a
/// public key generated using r as a private key. /// public key generated using r as a private key.
pub fn verify_sig(&self, secp: &Secp256k1) -> Result<TxKernel, secp::Error> { pub fn verify_sig(&self, secp: &Secp256k1) -> Result<Commitment, secp::Error> {
let rsum = self.sum_commitments(secp)?; let rsum = self.sum_commitments(secp)?;
let msg = Message::from_slice(&kernel_sig_msg(self.fee, self.lock_height))?; let msg = Message::from_slice(&kernel_sig_msg(self.fee, self.lock_height))?;
@ -320,34 +319,31 @@ impl Transaction {
// of generating a public key from a commitment behind verify_from_commit // of generating a public key from a commitment behind verify_from_commit
secp.verify_from_commit(&msg, &sig, &rsum)?; secp.verify_from_commit(&msg, &sig, &rsum)?;
let kernel = TxKernel { Ok(rsum)
}
pub fn build_kernel(&self, excess: Commitment) -> TxKernel {
TxKernel {
features: DEFAULT_KERNEL, features: DEFAULT_KERNEL,
excess: rsum, excess: excess,
excess_sig: self.excess_sig.clone(), excess_sig: self.excess_sig.clone(),
fee: self.fee, fee: self.fee,
lock_height: self.lock_height, lock_height: self.lock_height,
}; }
debug!(
LOGGER,
"tx verify_sig: fee - {}, lock_height - {}",
kernel.fee,
kernel.lock_height
);
Ok(kernel)
} }
/// Validates all relevant parts of a fully built transaction. Checks the /// Validates all relevant parts of a fully built transaction. Checks the
/// excess value against the signature as well as range proofs for each /// excess value against the signature as well as range proofs for each
/// output. /// output.
pub fn validate(&self, secp: &Secp256k1) -> Result<TxKernel, Error> { pub fn validate(&self, secp: &Secp256k1) -> Result<Commitment, Error> {
if self.fee & 1 != 0 { if self.fee & 1 != 0 {
return Err(Error::OddFee); return Err(Error::OddFee);
} }
for out in &self.outputs { for out in &self.outputs {
out.verify_proof(secp)?; out.verify_proof(secp)?;
} }
self.verify_sig(secp).map_err(&From::from) let excess = self.verify_sig(secp)?;
Ok(excess)
} }
} }