mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 11:31:08 +03:00
iterate once over txs in a block (#233)
Split out verify_sig and build_kernel to make kernel explicit.
This commit is contained in:
parent
81bd764fd0
commit
ad07866275
2 changed files with 62 additions and 42 deletions
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue