mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 11:31:08 +03:00
move verify_kernel_sums into committed trait (#1131)
This commit is contained in:
parent
6fd2afccb4
commit
0ecadd3486
10 changed files with 302 additions and 210 deletions
|
@ -23,6 +23,7 @@ use std::time::{Duration, Instant};
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
use core::core::pmmr::MerkleProof;
|
use core::core::pmmr::MerkleProof;
|
||||||
use core::core::target::Difficulty;
|
use core::core::target::Difficulty;
|
||||||
|
use core::core::Committed;
|
||||||
use core::core::{Block, BlockHeader, Output, OutputIdentifier, Transaction, TxKernel};
|
use core::core::{Block, BlockHeader, Output, OutputIdentifier, Transaction, TxKernel};
|
||||||
use core::global;
|
use core::global;
|
||||||
use grin_store::Error::NotFoundErr;
|
use grin_store::Error::NotFoundErr;
|
||||||
|
@ -200,7 +201,14 @@ impl Chain {
|
||||||
header.height,
|
header.height,
|
||||||
header.hash()
|
header.hash()
|
||||||
);
|
);
|
||||||
let (output_sum, kernel_sum) = extension.validate_sums(&header)?;
|
|
||||||
|
let (output_sum, kernel_sum) = extension.verify_kernel_sums(
|
||||||
|
header.total_overage(),
|
||||||
|
header.total_kernel_offset(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
store.save_block_sums(
|
store.save_block_sums(
|
||||||
&header.hash(),
|
&header.hash(),
|
||||||
&BlockSums {
|
&BlockSums {
|
||||||
|
|
|
@ -24,11 +24,11 @@ use std::time::Instant;
|
||||||
|
|
||||||
use util::secp::pedersen::{Commitment, RangeProof};
|
use util::secp::pedersen::{Commitment, RangeProof};
|
||||||
|
|
||||||
use core::consensus::REWARD;
|
use core::core::committed::Committed;
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
use core::core::pmmr::{self, MerkleProof, PMMR};
|
use core::core::pmmr::{self, MerkleProof, PMMR};
|
||||||
use core::core::{Block, BlockHeader, Committed, Input, Output, OutputFeatures, OutputIdentifier,
|
use core::core::{Block, BlockHeader, Input, Output, OutputFeatures, OutputIdentifier, Transaction,
|
||||||
Transaction, TxKernel};
|
TxKernel};
|
||||||
use core::global;
|
use core::global;
|
||||||
use core::ser::{PMMRIndexHashable, PMMRable};
|
use core::ser::{PMMRIndexHashable, PMMRable};
|
||||||
|
|
||||||
|
@ -773,35 +773,6 @@ impl<'a> Extension<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The real magicking: the sum of all kernel excess should equal the sum
|
|
||||||
/// of all output commitments, minus the total supply.
|
|
||||||
pub fn validate_sums(&self, header: &BlockHeader) -> Result<((Commitment, Commitment)), Error> {
|
|
||||||
let now = Instant::now();
|
|
||||||
|
|
||||||
// Treat the total "supply" as one huge overage that needs to be accounted for.
|
|
||||||
// If we have a supply of 6,000 grin then we should
|
|
||||||
// have a corresponding 6,000 grin in unspent outputs.
|
|
||||||
let supply = ((header.height * REWARD) as i64).checked_neg().unwrap_or(0);
|
|
||||||
let output_sum = self.sum_commitments(supply, None)?;
|
|
||||||
|
|
||||||
let (kernel_sum, kernel_sum_plus_offset) =
|
|
||||||
self.sum_kernel_excesses(&header.total_kernel_offset, None)?;
|
|
||||||
|
|
||||||
if output_sum != kernel_sum_plus_offset {
|
|
||||||
return Err(Error::InvalidTxHashSet(
|
|
||||||
"Differing Output commitment and kernel excess sums.".to_owned(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
LOGGER,
|
|
||||||
"txhashset: validated sums, took (total) {}s",
|
|
||||||
now.elapsed().as_secs(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok((output_sum, kernel_sum))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate the txhashset state against the provided block header.
|
/// Validate the txhashset state against the provided block header.
|
||||||
pub fn validate(
|
pub fn validate(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -816,13 +787,21 @@ impl<'a> Extension<'a> {
|
||||||
return Ok((zero_commit.clone(), zero_commit.clone()));
|
return Ok((zero_commit.clone(), zero_commit.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (output_sum, kernel_sum) = self.validate_sums(header)?;
|
// The real magicking happens here.
|
||||||
|
// Sum of kernel excesses should equal sum of
|
||||||
|
// unspent outputs minus total supply.
|
||||||
|
let (output_sum, kernel_sum) = self.verify_kernel_sums(
|
||||||
|
header.total_overage(),
|
||||||
|
header.total_kernel_offset(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
// this is a relatively expensive verification step
|
// This is an expensive verification step.
|
||||||
self.verify_kernel_signatures()?;
|
self.verify_kernel_signatures()?;
|
||||||
|
|
||||||
// verify the rangeproof for each output in the sum above
|
// Verify the rangeproof for each output in the sum above.
|
||||||
// this is an expensive operation (only verified if requested)
|
// This is an expensive verification step (skip for faster verification).
|
||||||
if !skip_rproofs {
|
if !skip_rproofs {
|
||||||
self.verify_rangeproofs()?;
|
self.verify_rangeproofs()?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ use util::secp;
|
||||||
use util::secp::pedersen::Commitment;
|
use util::secp::pedersen::Commitment;
|
||||||
use util::secp_static;
|
use util::secp_static;
|
||||||
|
|
||||||
|
use core::core::committed;
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
use core::core::target::Difficulty;
|
use core::core::target::Difficulty;
|
||||||
use core::core::{block, transaction, Block, BlockHeader};
|
use core::core::{block, transaction, Block, BlockHeader};
|
||||||
|
@ -107,6 +108,8 @@ pub enum Error {
|
||||||
Transaction(transaction::Error),
|
Transaction(transaction::Error),
|
||||||
/// Anything else
|
/// Anything else
|
||||||
Other(String),
|
Other(String),
|
||||||
|
/// Error from summing and verifying kernel sums via committed trait.
|
||||||
|
Committed(committed::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for Error {
|
impl error::Error for Error {
|
||||||
|
@ -155,6 +158,12 @@ impl From<secp::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<committed::Error> for Error {
|
||||||
|
fn from(e: committed::Error) -> Error {
|
||||||
|
Error::Committed(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
/// Whether the error is due to a block that was intrinsically wrong
|
/// Whether the error is due to a block that was intrinsically wrong
|
||||||
pub fn is_bad_data(&self) -> bool {
|
pub fn is_bad_data(&self) -> bool {
|
||||||
|
|
|
@ -21,12 +21,14 @@ use time;
|
||||||
|
|
||||||
use consensus;
|
use consensus;
|
||||||
use consensus::{exceeds_weight, reward, VerifySortOrder, REWARD};
|
use consensus::{exceeds_weight, reward, VerifySortOrder, REWARD};
|
||||||
|
use core::committed;
|
||||||
|
use core::committed::Committed;
|
||||||
use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH};
|
use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH};
|
||||||
use core::id::ShortIdentifiable;
|
use core::id::ShortIdentifiable;
|
||||||
use core::target::Difficulty;
|
use core::target::Difficulty;
|
||||||
use core::transaction;
|
use core::transaction;
|
||||||
use core::{Commitment, Committed, Input, KernelFeatures, Output, OutputFeatures, Proof, ShortId,
|
use core::{Commitment, Input, KernelFeatures, Output, OutputFeatures, Proof, ShortId, Transaction,
|
||||||
Transaction, TxKernel};
|
TxKernel};
|
||||||
use global;
|
use global;
|
||||||
use keychain;
|
use keychain;
|
||||||
use keychain::BlindingFactor;
|
use keychain::BlindingFactor;
|
||||||
|
@ -66,10 +68,18 @@ pub enum Error {
|
||||||
},
|
},
|
||||||
/// Underlying Merkle proof error
|
/// Underlying Merkle proof error
|
||||||
MerkleProof,
|
MerkleProof,
|
||||||
|
/// Error when verifying kernel sums via committed trait.
|
||||||
|
Committed(committed::Error),
|
||||||
/// Other unspecified error condition
|
/// Other unspecified error condition
|
||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<committed::Error> for Error {
|
||||||
|
fn from(e: committed::Error) -> Error {
|
||||||
|
Error::Committed(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<transaction::Error> for Error {
|
impl From<transaction::Error> for Error {
|
||||||
fn from(e: transaction::Error) -> Error {
|
fn from(e: transaction::Error) -> Error {
|
||||||
Error::Transaction(e)
|
Error::Transaction(e)
|
||||||
|
@ -219,6 +229,23 @@ impl BlockHeader {
|
||||||
hasher.finalize(&mut ret);
|
hasher.finalize(&mut ret);
|
||||||
Hash(ret)
|
Hash(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The "overage" to use when verifying the kernel sums.
|
||||||
|
/// For a block header the overage is 0 - reward.
|
||||||
|
pub fn overage(&self) -> i64 {
|
||||||
|
(REWARD as i64).checked_neg().unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The "total overage" to use when verifying the kernel sums for a full
|
||||||
|
/// chain state. For a full chain state this is 0 - (height * reward).
|
||||||
|
pub fn total_overage(&self) -> i64 {
|
||||||
|
((self.height * REWARD) as i64).checked_neg().unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Total kernel offset for the chain state up to and including this block.
|
||||||
|
pub fn total_kernel_offset(&self) -> BlindingFactor {
|
||||||
|
self.total_kernel_offset
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compact representation of a full block.
|
/// Compact representation of a full block.
|
||||||
|
@ -658,7 +685,17 @@ impl Block {
|
||||||
self.verify_coinbase()?;
|
self.verify_coinbase()?;
|
||||||
self.verify_inputs()?;
|
self.verify_inputs()?;
|
||||||
self.verify_kernel_lock_heights()?;
|
self.verify_kernel_lock_heights()?;
|
||||||
self.verify_sums(prev_output_sum, prev_kernel_sum)
|
|
||||||
|
let sums = self.verify_kernel_sums(
|
||||||
|
self.header.overage(),
|
||||||
|
self.header.total_kernel_offset(),
|
||||||
|
Some(prev_output_sum),
|
||||||
|
Some(prev_kernel_sum),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.verify_rangeproofs()?;
|
||||||
|
self.verify_kernel_signatures()?;
|
||||||
|
Ok(sums)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_weight(&self) -> Result<(), Error> {
|
fn verify_weight(&self) -> Result<(), Error> {
|
||||||
|
@ -707,37 +744,22 @@ impl Block {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify sums
|
/// Verify the kernel signatures.
|
||||||
pub fn verify_sums(
|
/// Note: this is expensive.
|
||||||
&self,
|
fn verify_kernel_signatures(&self) -> Result<(), Error> {
|
||||||
prev_output_sum: &Commitment,
|
|
||||||
prev_kernel_sum: &Commitment,
|
|
||||||
) -> Result<((Commitment, Commitment)), Error> {
|
|
||||||
// Verify the output rangeproofs.
|
|
||||||
// Note: this is expensive.
|
|
||||||
for x in &self.outputs {
|
|
||||||
x.verify_proof()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the kernel signatures.
|
|
||||||
// Note: this is expensive.
|
|
||||||
for x in &self.kernels {
|
for x in &self.kernels {
|
||||||
x.verify()?;
|
x.verify()?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
// Sum all input|output|overage commitments.
|
|
||||||
let overage = (REWARD as i64).checked_neg().unwrap_or(0);
|
|
||||||
let io_sum = self.sum_commitments(overage, Some(prev_output_sum))?;
|
|
||||||
|
|
||||||
// Sum the kernel excesses accounting for the kernel offset.
|
|
||||||
let (kernel_sum, kernel_sum_plus_offset) =
|
|
||||||
self.sum_kernel_excesses(&self.header.total_kernel_offset, Some(prev_kernel_sum))?;
|
|
||||||
|
|
||||||
if io_sum != kernel_sum_plus_offset {
|
|
||||||
return Err(Error::KernelSumMismatch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((io_sum, kernel_sum))
|
/// 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. Entails 2 main
|
/// Validate the coinbase outputs generated by miners. Entails 2 main
|
||||||
|
|
169
core/src/core/committed.rs
Normal file
169
core/src/core/committed.rs
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// Copyright 2018 The Grin Developers
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! The Committed trait and associated errors.
|
||||||
|
|
||||||
|
use keychain;
|
||||||
|
use keychain::BlindingFactor;
|
||||||
|
|
||||||
|
use util::secp::pedersen::*;
|
||||||
|
use util::{secp, secp_static, static_secp_instance};
|
||||||
|
|
||||||
|
/// Errors from summing and verifying kernel excesses via committed trait.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Keychain related error.
|
||||||
|
Keychain(keychain::Error),
|
||||||
|
/// Secp related error.
|
||||||
|
Secp(secp::Error),
|
||||||
|
/// Kernel sums do not equal output sums.
|
||||||
|
KernelSumMismatch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<secp::Error> for Error {
|
||||||
|
fn from(e: secp::Error) -> Error {
|
||||||
|
Error::Secp(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<keychain::Error> for Error {
|
||||||
|
fn from(e: keychain::Error) -> Error {
|
||||||
|
Error::Keychain(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implemented by types that hold inputs and outputs (and kernels)
|
||||||
|
/// containing Pedersen commitments.
|
||||||
|
/// Handles the collection of the commitments as well as their
|
||||||
|
/// summing, taking potential explicit overages of fees into account.
|
||||||
|
pub trait Committed {
|
||||||
|
/// Gather the kernel excesses and sum them.
|
||||||
|
fn sum_kernel_excesses(
|
||||||
|
&self,
|
||||||
|
offset: &BlindingFactor,
|
||||||
|
extra_excess: Option<&Commitment>,
|
||||||
|
) -> Result<(Commitment, Commitment), Error> {
|
||||||
|
let zero_commit = secp_static::commit_to_zero_value();
|
||||||
|
|
||||||
|
// then gather the kernel excess commitments
|
||||||
|
let mut kernel_commits = self.kernels_committed();
|
||||||
|
|
||||||
|
if let Some(extra) = extra_excess {
|
||||||
|
kernel_commits.push(*extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle "zero commit" values by filtering them out here
|
||||||
|
kernel_commits.retain(|x| *x != zero_commit);
|
||||||
|
|
||||||
|
// sum the commitments
|
||||||
|
let kernel_sum = {
|
||||||
|
let secp = static_secp_instance();
|
||||||
|
let secp = secp.lock().unwrap();
|
||||||
|
secp.commit_sum(kernel_commits, vec![])?
|
||||||
|
};
|
||||||
|
|
||||||
|
// sum the commitments along with the
|
||||||
|
// commit to zero built from the offset
|
||||||
|
let kernel_sum_plus_offset = {
|
||||||
|
let secp = static_secp_instance();
|
||||||
|
let secp = secp.lock().unwrap();
|
||||||
|
let mut commits = vec![kernel_sum];
|
||||||
|
if *offset != BlindingFactor::zero() {
|
||||||
|
let key = offset.secret_key(&secp)?;
|
||||||
|
let offset_commit = secp.commit(0, key)?;
|
||||||
|
commits.push(offset_commit);
|
||||||
|
}
|
||||||
|
secp.commit_sum(commits, vec![])?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((kernel_sum, kernel_sum_plus_offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gathers commitments and sum them.
|
||||||
|
fn sum_commitments(
|
||||||
|
&self,
|
||||||
|
overage: i64,
|
||||||
|
extra_commit: Option<&Commitment>,
|
||||||
|
) -> Result<Commitment, Error> {
|
||||||
|
let zero_commit = secp_static::commit_to_zero_value();
|
||||||
|
|
||||||
|
// then gather the commitments
|
||||||
|
let mut input_commits = self.inputs_committed();
|
||||||
|
let mut output_commits = self.outputs_committed();
|
||||||
|
|
||||||
|
// add the overage as output commitment if positive,
|
||||||
|
// or as an input commitment if negative
|
||||||
|
if overage != 0 {
|
||||||
|
let over_commit = {
|
||||||
|
let secp = static_secp_instance();
|
||||||
|
let secp = secp.lock().unwrap();
|
||||||
|
secp.commit_value(overage.abs() as u64).unwrap()
|
||||||
|
};
|
||||||
|
if overage < 0 {
|
||||||
|
input_commits.push(over_commit);
|
||||||
|
} else {
|
||||||
|
output_commits.push(over_commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(extra) = extra_commit {
|
||||||
|
output_commits.push(*extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle "zero commit" values by filtering them out here
|
||||||
|
output_commits.retain(|x| *x != zero_commit);
|
||||||
|
input_commits.retain(|x| *x != zero_commit);
|
||||||
|
|
||||||
|
// sum all that stuff
|
||||||
|
{
|
||||||
|
let secp = static_secp_instance();
|
||||||
|
let secp = secp.lock().unwrap();
|
||||||
|
let res = secp.commit_sum(output_commits, input_commits)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vector of input commitments to verify.
|
||||||
|
fn inputs_committed(&self) -> Vec<Commitment>;
|
||||||
|
|
||||||
|
/// Vector of output commitments to verify.
|
||||||
|
fn outputs_committed(&self) -> Vec<Commitment>;
|
||||||
|
|
||||||
|
/// Vector of kernel excesses to verify.
|
||||||
|
fn kernels_committed(&self) -> Vec<Commitment>;
|
||||||
|
|
||||||
|
/// Verify the sum of the kernel excesses equals the
|
||||||
|
/// sum of the outputs, taking into account both
|
||||||
|
/// the kernel_offset and overage.
|
||||||
|
fn verify_kernel_sums(
|
||||||
|
&self,
|
||||||
|
overage: i64,
|
||||||
|
kernel_offset: BlindingFactor,
|
||||||
|
prev_output_sum: Option<&Commitment>,
|
||||||
|
prev_kernel_sum: Option<&Commitment>,
|
||||||
|
) -> Result<((Commitment, Commitment)), Error> {
|
||||||
|
// Sum all input|output|overage commitments.
|
||||||
|
let utxo_sum = self.sum_commitments(overage, prev_output_sum)?;
|
||||||
|
|
||||||
|
// Sum the kernel excesses accounting for the kernel offset.
|
||||||
|
let (kernel_sum, kernel_sum_plus_offset) =
|
||||||
|
self.sum_kernel_excesses(&kernel_offset, prev_kernel_sum)?;
|
||||||
|
|
||||||
|
if utxo_sum != kernel_sum_plus_offset {
|
||||||
|
return Err(Error::KernelSumMismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((utxo_sum, kernel_sum))
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
//! Core types
|
//! Core types
|
||||||
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod committed;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod id;
|
pub mod id;
|
||||||
pub mod pmmr;
|
pub mod pmmr;
|
||||||
|
@ -23,122 +24,19 @@ pub mod transaction;
|
||||||
use consensus::GRIN_BASE;
|
use consensus::GRIN_BASE;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::num::ParseFloatError;
|
use std::num::ParseFloatError;
|
||||||
use std::{fmt, iter};
|
use std::{fmt, iter};
|
||||||
|
|
||||||
use util::secp::pedersen::*;
|
use util::secp::pedersen::*;
|
||||||
use util::{secp, secp_static, static_secp_instance};
|
|
||||||
|
|
||||||
pub use self::block::*;
|
pub use self::block::*;
|
||||||
|
pub use self::committed::Committed;
|
||||||
pub use self::id::ShortId;
|
pub use self::id::ShortId;
|
||||||
pub use self::transaction::*;
|
pub use self::transaction::*;
|
||||||
use core::hash::Hashed;
|
use core::hash::Hashed;
|
||||||
use global;
|
use global;
|
||||||
use keychain;
|
|
||||||
use keychain::BlindingFactor;
|
|
||||||
use ser::{Error, Readable, Reader, Writeable, Writer};
|
use ser::{Error, Readable, Reader, Writeable, Writer};
|
||||||
|
|
||||||
/// Implemented by types that hold inputs and outputs (and kernels)
|
|
||||||
/// containing Pedersen commitments.
|
|
||||||
/// Handles the collection of the commitments as well as their
|
|
||||||
/// summing, taking potential explicit overages of fees into account.
|
|
||||||
pub trait Committed {
|
|
||||||
/// Gather the kernel excesses and sum them.
|
|
||||||
fn sum_kernel_excesses(
|
|
||||||
&self,
|
|
||||||
offset: &BlindingFactor,
|
|
||||||
extra_excess: Option<&Commitment>,
|
|
||||||
) -> Result<(Commitment, Commitment), keychain::Error> {
|
|
||||||
let zero_commit = secp_static::commit_to_zero_value();
|
|
||||||
|
|
||||||
// then gather the kernel excess commitments
|
|
||||||
let mut kernel_commits = self.kernels_committed();
|
|
||||||
|
|
||||||
if let Some(extra) = extra_excess {
|
|
||||||
kernel_commits.push(*extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle "zero commit" values by filtering them out here
|
|
||||||
kernel_commits.retain(|x| *x != zero_commit);
|
|
||||||
|
|
||||||
// sum the commitments
|
|
||||||
let kernel_sum = {
|
|
||||||
let secp = static_secp_instance();
|
|
||||||
let secp = secp.lock().unwrap();
|
|
||||||
secp.commit_sum(kernel_commits, vec![])?
|
|
||||||
};
|
|
||||||
|
|
||||||
// sum the commitments along with the
|
|
||||||
// commit to zero built from the offset
|
|
||||||
let kernel_sum_plus_offset = {
|
|
||||||
let secp = static_secp_instance();
|
|
||||||
let secp = secp.lock().unwrap();
|
|
||||||
let mut commits = vec![kernel_sum];
|
|
||||||
if *offset != BlindingFactor::zero() {
|
|
||||||
let key = offset.secret_key(&secp)?;
|
|
||||||
let offset_commit = secp.commit(0, key)?;
|
|
||||||
commits.push(offset_commit);
|
|
||||||
}
|
|
||||||
secp.commit_sum(commits, vec![])?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((kernel_sum, kernel_sum_plus_offset))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gathers commitments and sum them.
|
|
||||||
fn sum_commitments(
|
|
||||||
&self,
|
|
||||||
overage: i64,
|
|
||||||
extra_commit: Option<&Commitment>,
|
|
||||||
) -> Result<Commitment, secp::Error> {
|
|
||||||
let zero_commit = secp_static::commit_to_zero_value();
|
|
||||||
|
|
||||||
// then gather the commitments
|
|
||||||
let mut input_commits = self.inputs_committed();
|
|
||||||
let mut output_commits = self.outputs_committed();
|
|
||||||
|
|
||||||
// add the overage as output commitment if positive,
|
|
||||||
// or as an input commitment if negative
|
|
||||||
if overage != 0 {
|
|
||||||
let over_commit = {
|
|
||||||
let secp = static_secp_instance();
|
|
||||||
let secp = secp.lock().unwrap();
|
|
||||||
secp.commit_value(overage.abs() as u64).unwrap()
|
|
||||||
};
|
|
||||||
if overage < 0 {
|
|
||||||
input_commits.push(over_commit);
|
|
||||||
} else {
|
|
||||||
output_commits.push(over_commit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(extra) = extra_commit {
|
|
||||||
output_commits.push(*extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle "zero commit" values by filtering them out here
|
|
||||||
output_commits.retain(|x| *x != zero_commit);
|
|
||||||
input_commits.retain(|x| *x != zero_commit);
|
|
||||||
|
|
||||||
// sum all that stuff
|
|
||||||
{
|
|
||||||
let secp = static_secp_instance();
|
|
||||||
let secp = secp.lock().unwrap();
|
|
||||||
secp.commit_sum(output_commits, input_commits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Vector of input commitments to verify.
|
|
||||||
fn inputs_committed(&self) -> Vec<Commitment>;
|
|
||||||
|
|
||||||
/// Vector of output commitments to verify.
|
|
||||||
fn outputs_committed(&self) -> Vec<Commitment>;
|
|
||||||
|
|
||||||
/// Vector of kernel excesses to verify.
|
|
||||||
fn kernels_committed(&self) -> Vec<Commitment>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Proof of work
|
/// Proof of work
|
||||||
#[derive(Clone, PartialOrd, PartialEq)]
|
#[derive(Clone, PartialOrd, PartialEq)]
|
||||||
pub struct Proof {
|
pub struct Proof {
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
//! Transactions
|
//! Transactions
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
@ -26,11 +26,12 @@ use util::{kernel_sig_msg, static_secp_instance};
|
||||||
|
|
||||||
use consensus;
|
use consensus;
|
||||||
use consensus::VerifySortOrder;
|
use consensus::VerifySortOrder;
|
||||||
use core::BlockHeader;
|
use core::committed;
|
||||||
use core::Committed;
|
use core::committed::Committed;
|
||||||
use core::global;
|
use core::global;
|
||||||
use core::hash::{Hash, Hashed, ZERO_HASH};
|
use core::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
use core::pmmr::MerkleProof;
|
use core::pmmr::MerkleProof;
|
||||||
|
use core::BlockHeader;
|
||||||
use keychain;
|
use keychain;
|
||||||
use keychain::BlindingFactor;
|
use keychain::BlindingFactor;
|
||||||
use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable,
|
use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable,
|
||||||
|
@ -75,9 +76,8 @@ pub enum Error {
|
||||||
/// Returns if the value hidden within the a RangeProof message isn't
|
/// Returns if the value hidden within the a RangeProof message isn't
|
||||||
/// repeated 3 times, indicating it's incorrect
|
/// repeated 3 times, indicating it's incorrect
|
||||||
InvalidProofMessage,
|
InvalidProofMessage,
|
||||||
/// Error when sums do not verify correctly during tx aggregation.
|
/// Error when verifying kernel sums via committed trait.
|
||||||
/// Likely a "double spend" across two unconfirmed txs.
|
Committed(committed::Error),
|
||||||
AggregationError,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for Error {
|
impl error::Error for Error {
|
||||||
|
@ -114,6 +114,12 @@ impl From<keychain::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<committed::Error> for Error {
|
||||||
|
fn from(e: committed::Error) -> Error {
|
||||||
|
Error::Committed(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A proof that a transaction sums to zero. Includes both the transaction's
|
/// A proof that a transaction sums to zero. Includes both the transaction's
|
||||||
/// Pedersen commitment and the signature, that guarantees that the commitments
|
/// Pedersen commitment and the signature, that guarantees that the commitments
|
||||||
/// amount to zero.
|
/// amount to zero.
|
||||||
|
@ -387,6 +393,10 @@ impl Transaction {
|
||||||
self.kernels.iter().fold(0, |acc, ref x| acc + x.fee)
|
self.kernels.iter().fold(0, |acc, ref x| acc + x.fee)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overage(&self) -> i64 {
|
||||||
|
self.fee() as i64
|
||||||
|
}
|
||||||
|
|
||||||
/// Lock height of a transaction is the max lock height of the kernels.
|
/// Lock height of a transaction is the max lock height of the kernels.
|
||||||
pub fn lock_height(&self) -> u64 {
|
pub fn lock_height(&self) -> u64 {
|
||||||
self.kernels
|
self.kernels
|
||||||
|
@ -394,46 +404,24 @@ impl Transaction {
|
||||||
.fold(0, |acc, ref x| max(acc, x.lock_height))
|
.fold(0, |acc, ref x| max(acc, x.lock_height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify the kernel signatures.
|
||||||
|
/// Note: this is expensive.
|
||||||
fn verify_kernel_signatures(&self) -> Result<(), Error> {
|
fn verify_kernel_signatures(&self) -> Result<(), Error> {
|
||||||
// Verify the kernel signatures.
|
|
||||||
// Note: this is expensive.
|
|
||||||
for x in &self.kernels {
|
for x in &self.kernels {
|
||||||
x.verify()?;
|
x.verify()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify all the output rangeproofs.
|
||||||
|
/// Note: this is expensive.
|
||||||
fn verify_rangeproofs(&self) -> Result<(), Error> {
|
fn verify_rangeproofs(&self) -> Result<(), Error> {
|
||||||
// Verify all the output rangeproofs.
|
|
||||||
// Note: this is expensive.
|
|
||||||
for x in &self.outputs {
|
for x in &self.outputs {
|
||||||
x.verify_proof()?;
|
x.verify_proof()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// To verify transaction kernels we check that -
|
|
||||||
/// * all kernels have an even fee
|
|
||||||
/// * sum of input/output commitments matches sum of kernel commitments
|
|
||||||
/// after applying offset * each kernel sig is valid (i.e. tx commitments
|
|
||||||
/// sum to zero, given above is true)
|
|
||||||
fn verify_kernel_sums(&self) -> Result<(), Error> {
|
|
||||||
// Sum all input|output|overage commitments.
|
|
||||||
let overage = self.fee() as i64;
|
|
||||||
let io_sum = self.sum_commitments(overage, None)?;
|
|
||||||
|
|
||||||
// Sum the kernel excesses accounting for the kernel offset.
|
|
||||||
let (_, kernel_sum) = self.sum_kernel_excesses(&self.offset, None)?;
|
|
||||||
|
|
||||||
// sum of kernel commitments (including the offset) must match
|
|
||||||
// the sum of input/output commitments (minus fee)
|
|
||||||
if io_sum != kernel_sum {
|
|
||||||
return Err(Error::KernelSumMismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
|
@ -442,7 +430,7 @@ impl Transaction {
|
||||||
return Err(Error::TooManyInputs);
|
return Err(Error::TooManyInputs);
|
||||||
}
|
}
|
||||||
self.verify_sorted()?;
|
self.verify_sorted()?;
|
||||||
self.verify_kernel_sums()?;
|
self.verify_kernel_sums(self.overage(), self.offset, None, None)?;
|
||||||
self.verify_rangeproofs()?;
|
self.verify_rangeproofs()?;
|
||||||
self.verify_kernel_signatures()?;
|
self.verify_kernel_signatures()?;
|
||||||
|
|
||||||
|
@ -553,10 +541,9 @@ pub fn aggregate(transactions: Vec<Transaction>) -> Result<Transaction, Error> {
|
||||||
|
|
||||||
let tx = Transaction::new(new_inputs, new_outputs, kernels).with_offset(total_kernel_offset);
|
let tx = Transaction::new(new_inputs, new_outputs, kernels).with_offset(total_kernel_offset);
|
||||||
|
|
||||||
// We need to check sums here as aggregation/cut-through may have created an
|
// We need to check sums here as aggregation/cut-through
|
||||||
// invalid tx.
|
// may have created an invalid tx.
|
||||||
tx.verify_kernel_sums()
|
tx.verify_kernel_sums(tx.overage(), tx.offset, None, None)?;
|
||||||
.map_err(|_| Error::AggregationError)?;
|
|
||||||
|
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ use grin_core::consensus::{BLOCK_OUTPUT_WEIGHT, MAX_BLOCK_WEIGHT};
|
||||||
use grin_core::core::block::Error;
|
use grin_core::core::block::Error;
|
||||||
use grin_core::core::hash::Hashed;
|
use grin_core::core::hash::Hashed;
|
||||||
use grin_core::core::id::{ShortId, ShortIdentifiable};
|
use grin_core::core::id::{ShortId, ShortIdentifiable};
|
||||||
|
use grin_core::core::Committed;
|
||||||
use grin_core::core::{Block, BlockHeader, CompactBlock, KernelFeatures, OutputFeatures};
|
use grin_core::core::{Block, BlockHeader, CompactBlock, KernelFeatures, OutputFeatures};
|
||||||
use grin_core::global;
|
use grin_core::global;
|
||||||
use grin_core::ser;
|
use grin_core::ser;
|
||||||
|
@ -167,7 +168,12 @@ fn remove_coinbase_output_flag() {
|
||||||
.remove(OutputFeatures::COINBASE_OUTPUT);
|
.remove(OutputFeatures::COINBASE_OUTPUT);
|
||||||
|
|
||||||
assert_eq!(b.verify_coinbase(), Err(Error::CoinbaseSumMismatch));
|
assert_eq!(b.verify_coinbase(), Err(Error::CoinbaseSumMismatch));
|
||||||
assert!(b.verify_sums(&zero_commit, &zero_commit).is_ok());
|
assert!(b.verify_kernel_sums(
|
||||||
|
b.header.overage(),
|
||||||
|
b.header.total_kernel_offset(),
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
).is_ok());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.validate(&zero_commit, &zero_commit),
|
b.validate(&zero_commit, &zero_commit),
|
||||||
Err(Error::CoinbaseSumMismatch)
|
Err(Error::CoinbaseSumMismatch)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
use failure::{Backtrace, Context, Fail};
|
use failure::{Backtrace, Context, Fail};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
|
use core::core::committed;
|
||||||
use core::core::transaction;
|
use core::core::transaction;
|
||||||
use keychain::{self, extkey};
|
use keychain::{self, extkey};
|
||||||
use util::secp;
|
use util::secp;
|
||||||
|
@ -50,6 +51,9 @@ pub enum ErrorKind {
|
||||||
/// Fee error
|
/// Fee error
|
||||||
#[fail(display = "Fee Error")]
|
#[fail(display = "Fee Error")]
|
||||||
Fee(String),
|
Fee(String),
|
||||||
|
/// Error from summing commitments via committed trait.
|
||||||
|
#[fail(display = "Committed Error")]
|
||||||
|
Committed(committed::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fail for Error {
|
impl Fail for Error {
|
||||||
|
@ -97,6 +101,14 @@ impl From<secp::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<committed::Error> for Error {
|
||||||
|
fn from(error: committed::Error) -> Error {
|
||||||
|
Error {
|
||||||
|
inner: Context::new(ErrorKind::Committed(error)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<keychain::Error> for Error {
|
impl From<keychain::Error> for Error {
|
||||||
fn from(error: keychain::Error) -> Error {
|
fn from(error: keychain::Error) -> Error {
|
||||||
Error {
|
Error {
|
||||||
|
|
|
@ -17,13 +17,15 @@
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use core::core::{amount_to_hr_string, Committed, Transaction};
|
use core::core::committed;
|
||||||
|
use core::core::committed::Committed;
|
||||||
|
use core::core::{amount_to_hr_string, Transaction};
|
||||||
use keychain::{BlindSum, BlindingFactor, Keychain};
|
use keychain::{BlindSum, BlindingFactor, Keychain};
|
||||||
use libtx::error::{Error, ErrorKind};
|
use libtx::error::{Error, ErrorKind};
|
||||||
use libtx::{aggsig, build, tx_fee};
|
use libtx::{aggsig, build, tx_fee};
|
||||||
|
|
||||||
use util::secp::Signature;
|
|
||||||
use util::secp::key::{PublicKey, SecretKey};
|
use util::secp::key::{PublicKey, SecretKey};
|
||||||
|
use util::secp::Signature;
|
||||||
use util::{secp, LOGGER};
|
use util::{secp, LOGGER};
|
||||||
|
|
||||||
/// Public data for each participant in the slate
|
/// Public data for each participant in the slate
|
||||||
|
|
Loading…
Reference in a new issue