Support new Bulletproof rewind scheme (#2848)

* Update keychain with new rewind scheme

* Refactor: proof builder trait

* Update tests, cleanup

* rustfmt

* Move conversion of SwitchCommitmentType

* Add proof build trait to tx builders

* Cache hashes in proof builders

* Proof builder tests

* Add ViewKey struct

* Fix some warnings

* Zeroize proof builder secrets on drop
This commit is contained in:
jaspervdm 2019-06-12 11:28:55 +02:00 committed by Yeastplume
parent 6429580b0c
commit e3f3064414
30 changed files with 1399 additions and 208 deletions

10
Cargo.lock generated
View file

@ -777,6 +777,7 @@ dependencies = [
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -844,16 +845,17 @@ dependencies = [
[[package]]
name = "grin_secp256k1zkp"
version = "0.7.5"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -914,7 +916,7 @@ dependencies = [
"backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_secp256k1zkp 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_secp256k1zkp 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2739,7 +2741,7 @@ dependencies = [
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "591f8be1674b421644b6c030969520bc3fa12114d2eb467471982ed3e9584e71"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum grin_secp256k1zkp 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "75e9a265f3eeea4c204470f7262e2c6fe18f3d8ddf5fb24340cb550ac4f909c5"
"checksum grin_secp256k1zkp 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e96161c7d923bf094e7f4f583e680a03746b692523f2211bff59f642e05aa85"
"checksum h2 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "85ab6286db06040ddefb71641b50017c06874614001a134b423783e2db2920bd"
"checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac"
"checksum hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a"

View file

@ -76,7 +76,14 @@ fn data_files() {
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&pk,
0,
false,
)
.unwrap();
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
.unwrap();
@ -154,7 +161,8 @@ fn _prepare_block_nosum(
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(kc, &key_id, fees, false).unwrap();
let reward =
libtx::reward::output(kc, &libtx::ProofBuilder::new(kc), &key_id, fees, false).unwrap();
let mut b = match core::core::Block::new(
prev,
txs.into_iter().cloned().collect(),

View file

@ -19,7 +19,7 @@ use self::core::core::verifier_cache::LruVerifierCache;
use self::core::core::{Block, BlockHeader, OutputIdentifier, Transaction};
use self::core::genesis;
use self::core::global::ChainTypes;
use self::core::libtx::{self, build, reward};
use self::core::libtx::{self, build, reward, ProofBuilder};
use self::core::pow::Difficulty;
use self::core::{consensus, global, pow};
use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain};
@ -106,7 +106,14 @@ fn mine_genesis_reward_chain() {
let mut genesis = genesis::genesis_dev();
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let key_id = keychain::ExtKeychain::derive_key_id(0, 1, 0, 0, 0);
let reward = reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
genesis = genesis.with_reward(reward.0, reward.1);
let tmp_chain_dir = ".grin.tmp";
@ -143,7 +150,9 @@ where
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(keychain, &pk, 0, false).unwrap();
let reward =
libtx::reward::output(keychain, &libtx::ProofBuilder::new(keychain), &pk, 0, false)
.unwrap();
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
.unwrap();
@ -401,6 +410,7 @@ fn spend_in_fork_and_compact() {
let chain = setup(".grin6", pow::mine_genesis_block().unwrap());
let prev = chain.head_header().unwrap();
let kc = ExtKeychain::from_random_seed(false).unwrap();
let pb = ProofBuilder::new(&kc);
let mut fork_head = prev;
@ -434,6 +444,7 @@ fn spend_in_fork_and_compact() {
build::with_fee(20000),
],
&kc,
&pb,
)
.unwrap();
@ -451,6 +462,7 @@ fn spend_in_fork_and_compact() {
build::with_fee(20000),
],
&kc,
&pb,
)
.unwrap();
@ -540,7 +552,14 @@ fn output_header_mappings() {
let prev = chain.head_header().unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let pk = ExtKeychainPath::new(1, n as u32, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&pk,
0,
false,
)
.unwrap();
reward_outputs.push(reward.0.clone());
let mut b =
core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward)
@ -643,7 +662,8 @@ where
let key_id = ExtKeychainPath::new(1, diff as u32, 0, 0, 0).to_identifier();
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(kc, &key_id, fees, false).unwrap();
let reward =
libtx::reward::output(kc, &libtx::ProofBuilder::new(kc), &key_id, fees, false).unwrap();
let mut b = match core::core::Block::new(
prev,
txs.into_iter().cloned().collect(),

View file

@ -60,7 +60,14 @@ fn test_various_store_indices() {
setup_chain(&genesis, chain_store.clone()).unwrap();
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let block = Block::new(&genesis.header, vec![], Difficulty::min(), reward).unwrap();
let block_hash = block.hash();

View file

@ -16,7 +16,7 @@ use self::chain::types::NoopAdapter;
use self::chain::ErrorKind;
use self::core::core::verifier_cache::LruVerifierCache;
use self::core::global::{self, ChainTypes};
use self::core::libtx::{self, build};
use self::core::libtx::{self, build, ProofBuilder};
use self::core::pow::Difficulty;
use self::core::{consensus, pow};
use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain};
@ -59,13 +59,14 @@ fn test_coinbase_maturity() {
let prev = chain.head_header().unwrap();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
let key_id4 = ExtKeychainPath::new(1, 4, 0, 0, 0).to_identifier();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let reward = libtx::reward::output(&keychain, &key_id1, 0, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id1, 0, false).unwrap();
let mut block = core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
block.header.timestamp = prev.timestamp + Duration::seconds(60);
block.header.pow.secondary_scaling = next_header_info.secondary_scaling;
@ -104,12 +105,13 @@ fn test_coinbase_maturity() {
build::with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
let txs = vec![coinbase_txn.clone()];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id3, fees, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
block.header.timestamp = prev.timestamp + Duration::seconds(60);
@ -141,10 +143,11 @@ fn test_coinbase_maturity() {
let prev = chain.head_header().unwrap();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let reward = libtx::reward::output(&keychain, &key_id1, 0, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id1, 0, false).unwrap();
let mut block =
core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
@ -185,12 +188,13 @@ fn test_coinbase_maturity() {
build::with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
let txs = vec![coinbase_txn.clone()];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id3, fees, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
block.header.timestamp = prev.timestamp + Duration::seconds(60);
@ -222,9 +226,10 @@ fn test_coinbase_maturity() {
let prev = chain.head_header().unwrap();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let pk = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let reward = libtx::reward::output(&keychain, &pk, 0, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &pk, 0, false).unwrap();
let mut block =
core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap();
let next_header_info =
@ -254,7 +259,7 @@ fn test_coinbase_maturity() {
let txs = vec![coinbase_txn];
let fees = txs.iter().map(|tx| tx.fee()).sum();
let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap());
let reward = libtx::reward::output(&keychain, &key_id4, fees, false).unwrap();
let reward = libtx::reward::output(&keychain, &builder, &key_id4, fees, false).unwrap();
let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap();
block.header.timestamp = prev.timestamp + Duration::seconds(60);

View file

@ -27,6 +27,7 @@ siphasher = "0.2"
uuid = { version = "0.6", features = ["serde", "v4"] }
log = "0.4"
chrono = { version = "0.4.4", features = ["serde"] }
zeroize = "0.8"
grin_keychain = { path = "../keychain", version = "2.0.0-beta.1" }
grin_util = { path = "../util", version = "2.0.0-beta.1" }

View file

@ -1499,14 +1499,16 @@ mod test {
use super::*;
use crate::core::hash::Hash;
use crate::core::id::{ShortId, ShortIdentifiable};
use crate::keychain::{ExtKeychain, Keychain};
use crate::keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
use crate::util::secp;
#[test]
fn test_kernel_ser_deser() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let commit = keychain
.commit(5, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
// just some bytes for testing ser/deser
let sig = secp::Signature::from_raw_data(&[0; 64]).unwrap();
@ -1552,10 +1554,14 @@ mod test {
let keychain = ExtKeychain::from_seed(&[0; 32], false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(1003, &key_id).unwrap();
let commit = keychain
.commit(1003, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit_2 = keychain.commit(1003, &key_id).unwrap();
let commit_2 = keychain
.commit(1003, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
assert!(commit == commit_2);
}
@ -1564,7 +1570,9 @@ mod test {
fn input_short_id() {
let keychain = ExtKeychain::from_seed(&[0; 32], false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let commit = keychain
.commit(5, &key_id, &SwitchCommitmentType::Regular)
.unwrap();
let input = Input {
features: OutputFeatures::Plain,

View file

@ -36,6 +36,7 @@ extern crate log;
use failure;
#[macro_use]
extern crate failure_derive;
extern crate zeroize;
#[macro_use]
pub mod macros;

View file

@ -21,6 +21,7 @@ use crate::libtx::error::{Error, ErrorKind};
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::pedersen::Commitment;
use crate::util::secp::{self, aggsig, Message, Secp256k1, Signature};
use grin_keychain::SwitchCommitmentType;
/// Creates a new secure nonce (as a SecretKey), guaranteed to be usable during
/// aggsig creation.
@ -231,15 +232,17 @@ pub fn verify_partial_sig(
/// use core::libtx::{aggsig, proof};
/// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
/// use core::core::{Output, OutputFeatures};
/// use keychain::{Keychain, ExtKeychain};
/// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType};
///
/// let secp = Secp256k1::with_caps(ContextFlag::Commit);
/// let keychain = ExtKeychain::from_random_seed(false).unwrap();
/// let fees = 10_000;
/// let value = reward(fees);
/// let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
/// let commit = keychain.commit(value, &key_id).unwrap();
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
/// let switch = &SwitchCommitmentType::Regular;
/// let commit = keychain.commit(value, &key_id, switch).unwrap();
/// let builder = proof::ProofBuilder::new(&keychain);
/// let rproof = proof::create(&keychain, &builder, value, &key_id, switch, commit, None).unwrap();
/// let output = Output {
/// features: OutputFeatures::Coinbase,
/// commit: commit,
@ -266,7 +269,7 @@ pub fn sign_from_key_id<K>(
where
K: Keychain,
{
let skey = k.derive_key(value, key_id)?;
let skey = k.derive_key(value, key_id, &SwitchCommitmentType::Regular)?; // TODO: proper support for different switch commitment schemes
let sig = aggsig::sign_single(secp, &msg, &skey, s_nonce, None, None, blind_sum, None)?;
Ok(sig)
}
@ -296,7 +299,7 @@ where
/// use util::secp::{ContextFlag, Secp256k1};
/// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
/// use core::core::{Output, OutputFeatures};
/// use keychain::{Keychain, ExtKeychain};
/// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType};
///
/// // Create signature
/// let secp = Secp256k1::with_caps(ContextFlag::Commit);
@ -304,8 +307,10 @@ where
/// let fees = 10_000;
/// let value = reward(fees);
/// let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
/// let commit = keychain.commit(value, &key_id).unwrap();
/// let rproof = proof::create(&keychain, value, &key_id, commit, None).unwrap();
/// let switch = &SwitchCommitmentType::Regular;
/// let commit = keychain.commit(value, &key_id, switch).unwrap();
/// let builder = proof::ProofBuilder::new(&keychain);
/// let rproof = proof::create(&keychain, &builder, value, &key_id, switch, commit, None).unwrap();
/// let output = Output {
/// features: OutputFeatures::Coinbase,
/// commit: commit,

View file

@ -27,33 +27,42 @@
use crate::core::{Input, Output, OutputFeatures, Transaction, TxKernel};
use crate::keychain::{BlindSum, BlindingFactor, Identifier, Keychain};
use crate::libtx::{aggsig, proof, Error};
use crate::libtx::proof::{self, ProofBuild};
use crate::libtx::{aggsig, Error};
use grin_keychain::SwitchCommitmentType;
/// Context information available to transaction combinators.
pub struct Context<'a, K>
pub struct Context<'a, K, B>
where
K: Keychain,
B: ProofBuild,
{
/// The keychain used for key derivation
pub keychain: &'a K,
/// The bulletproof builder
pub builder: &'a B,
}
/// Function type returned by the transaction combinators. Transforms a
/// (Transaction, BlindSum) pair into another, provided some context.
pub type Append<K> = dyn for<'a> Fn(
&'a mut Context<'_, K>,
pub type Append<K, B> = dyn for<'a> Fn(
&'a mut Context<'_, K, B>,
(Transaction, TxKernel, BlindSum),
) -> (Transaction, TxKernel, BlindSum);
/// Adds an input with the provided value and blinding key to the transaction
/// being built.
fn build_input<K>(value: u64, features: OutputFeatures, key_id: Identifier) -> Box<Append<K>>
fn build_input<K, B>(value: u64, features: OutputFeatures, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
let commit = build.keychain.commit(value, &key_id).unwrap();
let commit = build
.keychain
.commit(value, &key_id, &SwitchCommitmentType::Regular)
.unwrap(); // TODO: proper support for different switch commitment schemes
let input = Input::new(features, commit);
(
tx.with_input(input),
@ -66,9 +75,10 @@ where
/// Adds an input with the provided value and blinding key to the transaction
/// being built.
pub fn input<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
pub fn input<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
debug!(
"Building input (spending regular output): {}, {}",
@ -78,9 +88,10 @@ where
}
/// Adds a coinbase input spending a coinbase output.
pub fn coinbase_input<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
pub fn coinbase_input<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
debug!("Building input (spending coinbase): {}, {}", value, key_id);
build_input(value, OutputFeatures::Coinbase, key_id)
@ -88,17 +99,30 @@ where
/// Adds an output with the provided value and key identifier from the
/// keychain.
pub fn output<K>(value: u64, key_id: Identifier) -> Box<Append<K>>
pub fn output<K, B>(value: u64, key_id: Identifier) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
let commit = build.keychain.commit(value, &key_id).unwrap();
// TODO: proper support for different switch commitment schemes
let switch = &SwitchCommitmentType::Regular;
let commit = build.keychain.commit(value, &key_id, switch).unwrap();
debug!("Building output: {}, {:?}", value, commit);
let rproof = proof::create(build.keychain, value, &key_id, commit, None).unwrap();
let rproof = proof::create(
build.keychain,
build.builder,
value,
&key_id,
switch,
commit,
None,
)
.unwrap();
(
tx.with_output(Output {
@ -114,9 +138,10 @@ where
}
/// Sets the fee on the transaction being built.
pub fn with_fee<K>(fee: u64) -> Box<Append<K>>
pub fn with_fee<K, B>(fee: u64) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -126,9 +151,10 @@ where
}
/// Sets the lock_height on the transaction being built.
pub fn with_lock_height<K>(lock_height: u64) -> Box<Append<K>>
pub fn with_lock_height<K, B>(lock_height: u64) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -140,9 +166,10 @@ where
/// Adds a known excess value on the transaction being built. Usually used in
/// combination with the initial_tx function when a new transaction is built
/// by adding to a pre-existing one.
pub fn with_excess<K>(excess: BlindingFactor) -> Box<Append<K>>
pub fn with_excess<K, B>(excess: BlindingFactor) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -152,9 +179,10 @@ where
}
/// Sets a known tx "offset". Used in final step of tx construction.
pub fn with_offset<K>(offset: BlindingFactor) -> Box<Append<K>>
pub fn with_offset<K, B>(offset: BlindingFactor) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
Box::new(
move |_build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
@ -166,9 +194,10 @@ where
/// Sets an initial transaction to add to when building a new transaction.
/// We currently only support building a tx with a single kernel with
/// build::transaction()
pub fn initial_tx<K>(mut tx: Transaction) -> Box<Append<K>>
pub fn initial_tx<K, B>(mut tx: Transaction) -> Box<Append<K, B>>
where
K: Keychain,
B: ProofBuild,
{
assert_eq!(tx.kernels().len(), 1);
let kern = tx.kernels_mut().remove(0);
@ -189,14 +218,16 @@ where
/// let (tx2, _) = build::transaction(vec![initial_tx(tx1), with_excess(sum),
/// output_rand(2)], keychain).unwrap();
///
pub fn partial_transaction<K>(
elems: Vec<Box<Append<K>>>,
pub fn partial_transaction<K, B>(
elems: Vec<Box<Append<K, B>>>,
keychain: &K,
builder: &B,
) -> Result<(Transaction, BlindingFactor), Error>
where
K: Keychain,
B: ProofBuild,
{
let mut ctx = Context { keychain };
let mut ctx = Context { keychain, builder };
let (tx, kern, sum) = elems.iter().fold(
(Transaction::empty(), TxKernel::empty(), BlindSum::new()),
|acc, elem| elem(&mut ctx, acc),
@ -212,11 +243,16 @@ where
}
/// Builds a complete transaction.
pub fn transaction<K>(elems: Vec<Box<Append<K>>>, keychain: &K) -> Result<Transaction, Error>
pub fn transaction<K, B>(
elems: Vec<Box<Append<K, B>>>,
keychain: &K,
builder: &B,
) -> Result<Transaction, Error>
where
K: Keychain,
B: ProofBuild,
{
let mut ctx = Context { keychain };
let mut ctx = Context { keychain, builder };
let (mut tx, mut kern, sum) = elems.iter().fold(
(Transaction::empty(), TxKernel::empty(), BlindSum::new()),
|acc, elem| elem(&mut ctx, acc),
@ -260,6 +296,7 @@ mod test {
use crate::core::transaction::Weighting;
use crate::core::verifier_cache::{LruVerifierCache, VerifierCache};
use crate::keychain::{ExtKeychain, ExtKeychainPath};
use crate::libtx::ProofBuilder;
fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
Arc::new(RwLock::new(LruVerifierCache::new()))
@ -268,6 +305,7 @@ mod test {
#[test]
fn blind_simple_tx() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
@ -282,6 +320,7 @@ mod test {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
@ -291,6 +330,7 @@ mod test {
#[test]
fn blind_simple_tx_with_offset() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
let key_id3 = ExtKeychainPath::new(1, 3, 0, 0, 0).to_identifier();
@ -305,6 +345,7 @@ mod test {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
@ -314,6 +355,7 @@ mod test {
#[test]
fn blind_simpler_tx() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id1 = ExtKeychainPath::new(1, 1, 0, 0, 0).to_identifier();
let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier();
@ -322,6 +364,7 @@ mod test {
let tx = transaction(
vec![input(6, key_id1), output(2, key_id2), with_fee(4)],
&keychain,
&builder,
)
.unwrap();

View file

@ -31,6 +31,7 @@ pub mod secp_ser;
use crate::consensus;
use crate::core::Transaction;
pub use self::proof::ProofBuilder;
pub use crate::libtx::error::{Error, ErrorKind};
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;

View file

@ -14,31 +14,47 @@
//! Rangeproof library functions
use crate::keychain::{Identifier, Keychain};
use crate::blake2::blake2b::blake2b;
use crate::keychain::extkey_bip32::BIP32GrinHasher;
use crate::keychain::{Identifier, Keychain, SwitchCommitmentType, ViewKey};
use crate::libtx::error::{Error, ErrorKind};
use crate::util::secp::key::SecretKey;
use crate::util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof};
use crate::util::secp::pedersen::{Commitment, ProofMessage, RangeProof};
use crate::util::secp::{self, Secp256k1};
use crate::zeroize::Zeroize;
use std::convert::TryFrom;
/// Create a bulletproof
pub fn create<K>(
pub fn create<K, B>(
k: &K,
b: &B,
amount: u64,
key_id: &Identifier,
switch: &SwitchCommitmentType,
_commit: Commitment,
extra_data: Option<Vec<u8>>,
) -> Result<RangeProof, Error>
where
K: Keychain,
B: ProofBuild,
{
let commit = k.commit(amount, key_id)?;
let skey = k.derive_key(amount, key_id)?;
let nonce = k
.create_nonce(&commit)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
let message = ProofMessage::from_bytes(&key_id.serialize_path());
Ok(k.secp()
.bullet_proof(amount, skey, nonce, extra_data, Some(message)))
// TODO: proper support for different switch commitment schemes
// The new bulletproof scheme encodes and decodes it, but
// it is not supported at the wallet level (yet).
let secp = k.secp();
let commit = k.commit(amount, key_id, switch)?;
let skey = k.derive_key(amount, key_id, switch)?;
let rewind_nonce = b.rewind_nonce(secp, &commit)?;
let private_nonce = b.private_nonce(secp, &commit)?;
let message = b.proof_message(secp, key_id, switch)?;
Ok(secp.bullet_proof(
amount,
skey,
rewind_nonce,
private_nonce,
extra_data,
Some(message),
))
}
/// Verify a proof
@ -55,35 +71,689 @@ pub fn verify(
}
}
/// Rewind a rangeproof to retrieve the amount
pub fn rewind<K>(
k: &K,
/// Rewind a rangeproof to retrieve the amount, derivation path and switch commitment type
pub fn rewind<B>(
secp: &Secp256k1,
b: &B,
commit: Commitment,
extra_data: Option<Vec<u8>>,
proof: RangeProof,
) -> Result<ProofInfo, Error>
) -> Result<Option<(u64, Identifier, SwitchCommitmentType)>, Error>
where
B: ProofBuild,
{
let nonce = b
.rewind_nonce(secp, &commit)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
let info = secp.rewind_bullet_proof(commit, nonce, extra_data, proof);
if info.is_err() {
return Ok(None);
}
let info = info.unwrap();
let amount = info.value;
let check = b
.check_output(secp, &commit, amount, info.message)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
Ok(check.map(|(id, switch)| (amount, id, switch)))
}
/// Used for building proofs and checking if the output belongs to the wallet
pub trait ProofBuild {
/// Create a BP nonce that will allow to rewind the derivation path and flags
fn rewind_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error>;
/// Create a BP nonce that blinds the private key
fn private_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error>;
/// Create a BP message
fn proof_message(
&self,
secp: &Secp256k1,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error>;
/// Check if the output belongs to this keychain
fn check_output(
&self,
secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error>;
}
/// The new, more flexible proof builder
pub struct ProofBuilder<'a, K>
where
K: Keychain,
{
let nonce = k
.create_nonce(&commit)
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
let proof_message = k
.secp()
.rewind_bullet_proof(commit, nonce, extra_data, proof);
let proof_info = match proof_message {
Ok(p) => p,
Err(_) => ProofInfo {
success: false,
value: 0,
message: ProofMessage::empty(),
blinding: SecretKey([0; secp::constants::SECRET_KEY_SIZE]),
mlen: 0,
min: 0,
max: 0,
exp: 0,
mantissa: 0,
},
};
return Ok(proof_info);
keychain: &'a K,
rewind_hash: Vec<u8>,
private_hash: Vec<u8>,
}
impl<'a, K> ProofBuilder<'a, K>
where
K: Keychain,
{
/// Creates a new instance of this proof builder
pub fn new(keychain: &'a K) -> Self {
let private_root_key = keychain
.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::None)
.unwrap();
let private_hash = blake2b(32, &[], &private_root_key.0).as_bytes().to_vec();
let public_root_key = keychain
.public_root_key()
.serialize_vec(keychain.secp(), true);
let rewind_hash = blake2b(32, &[], &public_root_key[..]).as_bytes().to_vec();
Self {
keychain,
rewind_hash,
private_hash,
}
}
fn nonce(&self, commit: &Commitment, private: bool) -> Result<SecretKey, Error> {
let hash = if private {
&self.private_hash
} else {
&self.rewind_hash
};
let res = blake2b(32, &commit.0, hash);
SecretKey::from_slice(self.keychain.secp(), res.as_bytes()).map_err(|e| {
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
})
}
}
impl<'a, K> ProofBuild for ProofBuilder<'a, K>
where
K: Keychain,
{
fn rewind_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit, false)
}
fn private_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit, true)
}
/// Message bytes:
/// 0: reserved for future use
/// 1: wallet type (0 for standard)
/// 2: switch commitment type
/// 3: path depth
/// 4-19: derivation path
fn proof_message(
&self,
_secp: &Secp256k1,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error> {
let mut msg = [0; 20];
msg[2] = u8::from(switch);
let id_bytes = id.to_bytes();
for i in 0..17 {
msg[i + 3] = id_bytes[i];
}
Ok(ProofMessage::from_bytes(&msg))
}
fn check_output(
&self,
_secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
if message.len() != 20 {
return Ok(None);
}
let msg = message.as_bytes();
let exp: [u8; 2] = [0; 2];
if msg[..2] != exp {
return Ok(None);
}
let switch = match SwitchCommitmentType::try_from(msg[2]) {
Ok(s) => s,
Err(_) => return Ok(None),
};
let depth = u8::min(msg[3], 4);
let id = Identifier::from_serialized_path(depth, &msg[4..]);
let commit_exp = self.keychain.commit(amount, &id, &switch)?;
match commit == &commit_exp {
true => Ok(Some((id, switch))),
false => Ok(None),
}
}
}
impl<'a, K> Zeroize for ProofBuilder<'a, K>
where
K: Keychain,
{
fn zeroize(&mut self) {
self.rewind_hash.zeroize();
self.private_hash.zeroize();
}
}
impl<'a, K> Drop for ProofBuilder<'a, K>
where
K: Keychain,
{
fn drop(&mut self) {
self.zeroize();
}
}
/// The legacy proof builder, used before the first hard fork
pub struct LegacyProofBuilder<'a, K>
where
K: Keychain,
{
keychain: &'a K,
root_hash: Vec<u8>,
}
impl<'a, K> LegacyProofBuilder<'a, K>
where
K: Keychain,
{
/// Creates a new instance of this proof builder
pub fn new(keychain: &'a K) -> Self {
Self {
keychain,
root_hash: keychain
.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::Regular)
.unwrap()
.0
.to_vec(),
}
}
fn nonce(&self, commit: &Commitment) -> Result<SecretKey, Error> {
let res = blake2b(32, &commit.0, &self.root_hash);
SecretKey::from_slice(self.keychain.secp(), res.as_bytes()).map_err(|e| {
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
})
}
}
impl<'a, K> ProofBuild for LegacyProofBuilder<'a, K>
where
K: Keychain,
{
fn rewind_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit)
}
fn private_nonce(&self, _secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
self.nonce(commit)
}
/// Message bytes:
/// 0-3: 0
/// 4-19: derivation path
/// All outputs with this scheme are assumed to use regular switch commitments
fn proof_message(
&self,
_secp: &Secp256k1,
id: &Identifier,
_switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error> {
let mut msg = [0; 20];
let id_ser = id.serialize_path();
for i in 0..16 {
msg[i + 4] = id_ser[i];
}
Ok(ProofMessage::from_bytes(&msg))
}
fn check_output(
&self,
_secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
if message.len() != 20 {
return Ok(None);
}
let msg = message.as_bytes();
let id = Identifier::from_serialized_path(3, &msg[4..]);
let exp: [u8; 4] = [0; 4];
if msg[..4] != exp {
return Ok(None);
}
let commit_exp = self
.keychain
.commit(amount, &id, &SwitchCommitmentType::Regular)?;
match commit == &commit_exp {
true => Ok(Some((id, SwitchCommitmentType::Regular))),
false => Ok(None),
}
}
}
impl<'a, K> Zeroize for LegacyProofBuilder<'a, K>
where
K: Keychain,
{
fn zeroize(&mut self) {
self.root_hash.zeroize();
}
}
impl<'a, K> Drop for LegacyProofBuilder<'a, K>
where
K: Keychain,
{
fn drop(&mut self) {
self.zeroize();
}
}
impl ProofBuild for ViewKey {
fn rewind_nonce(&self, secp: &Secp256k1, commit: &Commitment) -> Result<SecretKey, Error> {
let res = blake2b(32, &commit.0, &self.rewind_hash);
SecretKey::from_slice(secp, res.as_bytes()).map_err(|e| {
ErrorKind::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()).into()
})
}
fn private_nonce(&self, _secp: &Secp256k1, _commit: &Commitment) -> Result<SecretKey, Error> {
unimplemented!();
}
fn proof_message(
&self,
_secp: &Secp256k1,
_id: &Identifier,
_switch: &SwitchCommitmentType,
) -> Result<ProofMessage, Error> {
unimplemented!();
}
fn check_output(
&self,
secp: &Secp256k1,
commit: &Commitment,
amount: u64,
message: ProofMessage,
) -> Result<Option<(Identifier, SwitchCommitmentType)>, Error> {
if message.len() != 20 {
return Ok(None);
}
let msg = message.as_bytes();
let exp: [u8; 2] = [0; 2];
if msg[..2] != exp {
return Ok(None);
}
let switch = match SwitchCommitmentType::try_from(msg[2]) {
Ok(s) => s,
Err(_) => return Ok(None),
};
let depth = u8::min(msg[3], 4);
let id = Identifier::from_serialized_path(depth, &msg[4..]);
let path = id.to_path();
if self.depth > path.depth {
return Ok(None);
}
// For non-root key, check child number of current depth
if self.depth > 0
&& path.depth > 0
&& self.child_number != path.path[self.depth as usize - 1]
{
return Ok(None);
}
let mut key = self.clone();
let mut hasher = BIP32GrinHasher::new(self.is_floo);
for i in self.depth..path.depth {
let child_number = path.path[i as usize];
if child_number.is_hardened() {
return Ok(None);
}
key = key.ckd_pub(&secp, &mut hasher, child_number)?;
}
let pub_key = key.commit(secp, amount, &switch)?;
if commit.to_pubkey(&secp)? == pub_key {
Ok(Some((id, switch)))
} else {
Ok(None)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keychain::ExtKeychain;
use grin_keychain::ChildNumber;
use rand::{thread_rng, Rng};
#[test]
fn legacy_builder() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = LegacyProofBuilder::new(&keychain);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(3, rng.gen(), rng.gen(), rng.gen(), 0);
let switch = SwitchCommitmentType::Regular;
let commit = keychain.commit(amount, &id, &switch).unwrap();
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
let rewind = rewind(keychain.secp(), &builder, commit, None, proof).unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
}
#[test]
fn builder() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(3, rng.gen(), rng.gen(), rng.gen(), 0);
// With switch commitment
let commit_a = {
let switch = SwitchCommitmentType::Regular;
let commit = keychain.commit(amount, &id, &switch).unwrap();
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
let rewind = rewind(keychain.secp(), &builder, commit.clone(), None, proof).unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
commit
};
// Without switch commitment
let commit_b = {
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
assert!(verify(&keychain.secp(), commit.clone(), proof.clone(), None).is_ok());
let rewind = rewind(keychain.secp(), &builder, commit.clone(), None, proof).unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
commit
};
// The resulting pedersen commitments should be different
assert_ne!(commit_a, commit_b);
}
#[test]
fn view_key() {
// TODO
/*let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key = ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
let amount = rng.gen();
//let id = ExtKeychain::derive_key_id(3, rng.gen::<u16>() as u32, rng.gen::<u16>() as u32, rng.gen::<u16>() as u32, 0);
let id = ExtKeychain::derive_key_id(0, 0, 0, 0, 0);
let switch = SwitchCommitmentType::Regular;
println!("commit_0 = {:?}", keychain.commit(amount, &id, &SwitchCommitmentType::None).unwrap().0.to_vec());
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(&keychain, &builder, amount, &id, &switch, commit.clone(), None).unwrap();
// ..and rewind with ViewKey
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);*/
}
#[test]
fn view_key_no_switch() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key =
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with ViewKey
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
}
#[test]
fn view_key_hardened() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key =
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
rng.gen::<u16>() as u32,
u32::max_value() - 2,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with ViewKey
let rewind = rewind(keychain.secp(), &view_key, commit.clone(), None, proof);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_none());
}
#[test]
fn view_key_child() {
let rng = &mut thread_rng();
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut hasher = keychain.hasher();
let view_key =
ViewKey::create(&keychain, keychain.master.clone(), &mut hasher, false).unwrap();
assert_eq!(builder.rewind_hash, view_key.rewind_hash);
// Same child
{
let child_view_key = view_key
.ckd_pub(
keychain.secp(),
&mut hasher,
ChildNumber::from_normal_idx(10),
)
.unwrap();
assert_eq!(child_view_key.depth, 1);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
10,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with child ViewKey
let rewind = rewind(
keychain.secp(),
&child_view_key,
commit.clone(),
None,
proof,
);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_some());
let (r_amount, r_id, r_switch) = rewind.unwrap();
assert_eq!(r_amount, amount);
assert_eq!(r_id, id);
assert_eq!(r_switch, switch);
}
// Different child
{
let child_view_key = view_key
.ckd_pub(
keychain.secp(),
&mut hasher,
ChildNumber::from_normal_idx(11),
)
.unwrap();
assert_eq!(child_view_key.depth, 1);
let amount = rng.gen();
let id = ExtKeychain::derive_key_id(
3,
10,
rng.gen::<u16>() as u32,
rng.gen::<u16>() as u32,
0,
);
let switch = SwitchCommitmentType::None;
let commit = keychain.commit(amount, &id, &switch).unwrap();
// Generate proof with ProofBuilder..
let proof = create(
&keychain,
&builder,
amount,
&id,
&switch,
commit.clone(),
None,
)
.unwrap();
// ..and rewind with child ViewKey
let rewind = rewind(
keychain.secp(),
&child_view_key,
commit.clone(),
None,
proof,
);
assert!(rewind.is_ok());
let rewind = rewind.unwrap();
assert!(rewind.is_none());
}
}
}

View file

@ -19,25 +19,33 @@ use crate::core::transaction::kernel_sig_msg;
use crate::core::{KernelFeatures, Output, OutputFeatures, TxKernel};
use crate::keychain::{Identifier, Keychain};
use crate::libtx::error::Error;
use crate::libtx::{aggsig, proof};
use crate::libtx::{
aggsig,
proof::{self, ProofBuild},
};
use crate::util::{secp, static_secp_instance};
use grin_keychain::SwitchCommitmentType;
/// output a reward output
pub fn output<K>(
pub fn output<K, B>(
keychain: &K,
builder: &B,
key_id: &Identifier,
fees: u64,
test_mode: bool,
) -> Result<(Output, TxKernel), Error>
where
K: Keychain,
B: ProofBuild,
{
let value = reward(fees);
let commit = keychain.commit(value, key_id)?;
// TODO: proper support for different switch commitment schemes
let switch = &SwitchCommitmentType::Regular;
let commit = keychain.commit(value, key_id, switch)?;
trace!("Block reward - Pedersen Commit is: {:?}", commit,);
let rproof = proof::create(keychain, value, key_id, commit, None)?;
let rproof = proof::create(keychain, builder, value, key_id, switch, commit, None)?;
let output = Output {
features: OutputFeatures::Coinbase,

View file

@ -25,6 +25,7 @@ use crate::core::core::{
Block, BlockHeader, CompactBlock, HeaderVersion, KernelFeatures, OutputFeatures,
};
use crate::core::libtx::build::{self, input, output, with_fee};
use crate::core::libtx::ProofBuilder;
use crate::core::{global, ser};
use crate::keychain::{BlindingFactor, ExtKeychain, Keychain};
use crate::util::secp;
@ -45,6 +46,7 @@ fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
#[allow(dead_code)]
fn too_large_block() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let max_out = global::max_block_weight() / BLOCK_OUTPUT_WEIGHT;
let mut pks = vec![];
@ -59,12 +61,12 @@ fn too_large_block() {
let now = Instant::now();
parts.append(&mut vec![input(500000, pks.pop().unwrap()), with_fee(2)]);
let tx = build::transaction(parts, &keychain).unwrap();
let tx = build::transaction(parts, &keychain, &builder).unwrap();
println!("Build tx: {}", now.elapsed().as_secs());
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx], &keychain, &prev, &key_id);
let b = new_block(vec![&tx], &keychain, &builder, &prev, &key_id);
assert!(b
.validate(&BlindingFactor::zero(), verifier_cache())
.is_err());
@ -86,6 +88,7 @@ fn very_empty_block() {
// builds a block with a tx spending another and check that cut_through occurred
fn block_with_cut_through() {
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);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -94,17 +97,19 @@ fn block_with_cut_through() {
let mut btx2 = build::transaction(
vec![input(7, key_id1), output(5, key_id2.clone()), with_fee(2)],
&keychain,
&builder,
)
.unwrap();
// spending tx2 - reuse key_id2
let mut btx3 = txspend1i1o(5, &keychain, key_id2.clone(), key_id3);
let mut btx3 = txspend1i1o(5, &keychain, &builder, key_id2.clone(), key_id3);
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(
vec![&mut btx1, &mut btx2, &mut btx3],
&keychain,
&builder,
&prev,
&key_id,
);
@ -120,9 +125,10 @@ fn block_with_cut_through() {
#[test]
fn empty_block_with_coinbase_is_valid() {
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);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
assert_eq!(b.inputs().len(), 0);
assert_eq!(b.outputs().len(), 1);
@ -157,9 +163,10 @@ fn empty_block_with_coinbase_is_valid() {
// additionally verifying the merkle_inputs_outputs also fails
fn remove_coinbase_output_flag() {
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);
let mut b = new_block(vec![], &keychain, &prev, &key_id);
let mut b = new_block(vec![], &keychain, &builder, &prev, &key_id);
assert!(b.outputs()[0].is_coinbase());
b.outputs_mut()[0].features = OutputFeatures::Plain;
@ -179,9 +186,10 @@ fn remove_coinbase_output_flag() {
// invalidates the block and specifically it causes verify_coinbase to fail
fn remove_coinbase_kernel_flag() {
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);
let mut b = new_block(vec![], &keychain, &prev, &key_id);
let mut b = new_block(vec![], &keychain, &builder, &prev, &key_id);
assert!(b.kernels()[0].is_coinbase());
b.kernels_mut()[0].features = KernelFeatures::Plain;
@ -220,9 +228,10 @@ fn serialize_deserialize_header_version() {
#[test]
fn serialize_deserialize_block_header() {
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);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let header1 = b.header;
let mut vec = Vec::new();
@ -237,9 +246,10 @@ fn serialize_deserialize_block_header() {
fn serialize_deserialize_block() {
let tx1 = tx1i2o();
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);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
@ -255,9 +265,10 @@ fn serialize_deserialize_block() {
#[test]
fn empty_block_serialized_size() {
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);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 1_265;
@ -267,10 +278,11 @@ fn empty_block_serialized_size() {
#[test]
fn block_single_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 2_847;
@ -280,9 +292,10 @@ fn block_single_tx_serialized_size() {
#[test]
fn empty_compact_block_serialized_size() {
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);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.into();
let mut vec = Vec::new();
ser::serialize(&mut vec, &cb).expect("serialization failed");
@ -293,10 +306,11 @@ fn empty_compact_block_serialized_size() {
#[test]
fn compact_block_single_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.into();
let mut vec = Vec::new();
ser::serialize(&mut vec, &cb).expect("serialization failed");
@ -307,6 +321,7 @@ fn compact_block_single_tx_serialized_size() {
#[test]
fn block_10_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
global::set_mining_mode(global::ChainTypes::Mainnet);
let mut txs = vec![];
@ -316,7 +331,7 @@ fn block_10_tx_serialized_size() {
}
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
let b = new_block(txs.iter().collect(), &keychain, &builder, &prev, &key_id);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let target_len = 17_085;
@ -326,6 +341,7 @@ fn block_10_tx_serialized_size() {
#[test]
fn compact_block_10_tx_serialized_size() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let mut txs = vec![];
for _ in 0..10 {
@ -334,7 +350,7 @@ fn compact_block_10_tx_serialized_size() {
}
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
let b = new_block(txs.iter().collect(), &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.into();
let mut vec = Vec::new();
ser::serialize(&mut vec, &cb).expect("serialization failed");
@ -345,10 +361,11 @@ fn compact_block_10_tx_serialized_size() {
#[test]
fn compact_block_hash_with_nonce() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx], &keychain, &prev, &key_id);
let b = new_block(vec![&tx], &keychain, &builder, &prev, &key_id);
let cb1: CompactBlock = b.clone().into();
let cb2: CompactBlock = b.clone().into();
@ -375,10 +392,11 @@ fn compact_block_hash_with_nonce() {
#[test]
fn convert_block_to_compact_block() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.clone().into();
assert_eq!(cb.out_full().len(), 1);
@ -398,9 +416,10 @@ fn convert_block_to_compact_block() {
#[test]
fn hydrate_empty_compact_block() {
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);
let b = new_block(vec![], &keychain, &prev, &key_id);
let b = new_block(vec![], &keychain, &builder, &prev, &key_id);
let cb: CompactBlock = b.clone().into();
let hb = Block::hydrate_from(cb, vec![]).unwrap();
assert_eq!(hb.header, b.header);
@ -411,10 +430,11 @@ fn hydrate_empty_compact_block() {
#[test]
fn serialize_deserialize_compact_block() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let tx1 = tx1i2o();
let prev = BlockHeader::default();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
let b = new_block(vec![&tx1], &keychain, &builder, &prev, &key_id);
let mut cb1: CompactBlock = b.into();
@ -437,6 +457,7 @@ fn serialize_deserialize_compact_block() {
#[test]
fn same_amount_outputs_copy_range_proof() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
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);
@ -449,6 +470,7 @@ fn same_amount_outputs_copy_range_proof() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
@ -468,6 +490,7 @@ fn same_amount_outputs_copy_range_proof() {
kernels.clone(),
)],
&keychain,
&builder,
&prev,
&key_id,
);
@ -484,6 +507,7 @@ fn same_amount_outputs_copy_range_proof() {
#[test]
fn wrong_amount_range_proof() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
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);
@ -496,6 +520,7 @@ fn wrong_amount_range_proof() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
let tx2 = build::transaction(
@ -506,6 +531,7 @@ fn wrong_amount_range_proof() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
@ -525,6 +551,7 @@ fn wrong_amount_range_proof() {
kernels.clone(),
)],
&keychain,
&builder,
&prev,
&key_id,
);

View file

@ -21,6 +21,7 @@ use grin_core::core::{
};
use grin_core::libtx::{
build::{self, input, output, with_fee},
proof::{ProofBuild, ProofBuilder},
reward,
};
use grin_core::pow::Difficulty;
@ -29,6 +30,7 @@ use grin_keychain as keychain;
// utility producing a transaction with 2 inputs and a single outputs
pub fn tx2i1o() -> Transaction {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
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);
@ -41,6 +43,7 @@ pub fn tx2i1o() -> Transaction {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap()
}
@ -48,12 +51,14 @@ pub fn tx2i1o() -> Transaction {
// utility producing a transaction with a single input and output
pub fn tx1i1o() -> Transaction {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
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);
build::transaction(
vec![input(5, key_id1), output(3, key_id2), with_fee(2)],
&keychain,
&builder,
)
.unwrap()
}
@ -63,6 +68,7 @@ pub fn tx1i1o() -> Transaction {
// Note: this tx has an "offset" kernel
pub fn tx1i2o() -> Transaction {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
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);
@ -75,23 +81,26 @@ pub fn tx1i2o() -> Transaction {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap()
}
// utility to create a block without worrying about the key or previous
// header
pub fn new_block<K>(
pub fn new_block<K, B>(
txs: Vec<&Transaction>,
keychain: &K,
builder: &B,
previous_header: &BlockHeader,
key_id: &Identifier,
) -> Block
where
K: Keychain,
B: ProofBuild,
{
let fees = txs.iter().map(|tx| tx.fee()).sum();
let reward_output = reward::output(keychain, &key_id, fees, false).unwrap();
let reward_output = reward::output(keychain, builder, &key_id, fees, false).unwrap();
Block::new(
&previous_header,
txs.into_iter().cloned().collect(),
@ -103,13 +112,21 @@ where
// utility producing a transaction that spends an output with the provided
// value and blinding key
pub fn txspend1i1o<K>(v: u64, keychain: &K, key_id1: Identifier, key_id2: Identifier) -> Transaction
pub fn txspend1i1o<K, B>(
v: u64,
keychain: &K,
builder: &B,
key_id1: Identifier,
key_id2: Identifier,
) -> Transaction
where
K: Keychain,
B: ProofBuild,
{
build::transaction(
vec![input(v, key_id1), output(3, key_id2), with_fee(2)],
keychain,
builder,
)
.unwrap()
}

View file

@ -24,6 +24,7 @@ use self::core::core::{aggregate, deaggregate, KernelFeatures, Output, Transacti
use self::core::libtx::build::{
self, initial_tx, input, output, with_excess, with_fee, with_lock_height,
};
use self::core::libtx::ProofBuilder;
use self::core::ser;
use self::keychain::{BlindingFactor, ExtKeychain, Keychain};
use self::util::static_secp_instance;
@ -75,18 +76,15 @@ fn tx_double_ser_deser() {
#[test]
#[should_panic(expected = "Keychain Error")]
fn test_zero_commit_fails() {
let mut keychain = ExtKeychain::from_random_seed(false).unwrap();
keychain.set_use_switch_commits(false);
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);
// blinding should fail as signing with a zero r*G shouldn't work
build::transaction(
vec![
input(10, key_id1.clone()),
output(9, key_id1.clone()),
with_fee(1),
],
vec![input(10, key_id1.clone()), output(10, key_id1.clone())],
&keychain,
&builder,
)
.unwrap();
}
@ -98,6 +96,7 @@ fn verifier_cache() -> Arc<RwLock<dyn VerifierCache>> {
#[test]
fn build_tx_kernel() {
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);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -111,6 +110,7 @@ fn build_tx_kernel() {
with_fee(2),
],
&keychain,
&builder,
)
.unwrap();
@ -350,6 +350,7 @@ fn basic_transaction_deaggregation() {
#[test]
fn hash_output() {
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);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -362,6 +363,7 @@ fn hash_output() {
with_fee(1),
],
&keychain,
&builder,
)
.unwrap();
let h = tx.outputs()[0].hash();
@ -407,6 +409,7 @@ fn tx_hash_diff() {
#[test]
fn tx_build_exchange() {
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);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -419,9 +422,12 @@ fn tx_build_exchange() {
// Alice builds her transaction, with change, which also produces the sum
// of blinding factors before they're obscured.
let (tx, sum) =
build::partial_transaction(vec![in1, in2, output(1, key_id3), with_fee(2)], &keychain)
.unwrap();
let (tx, sum) = build::partial_transaction(
vec![in1, in2, output(1, key_id3), with_fee(2)],
&keychain,
&builder,
)
.unwrap();
(tx, sum)
};
@ -436,6 +442,7 @@ fn tx_build_exchange() {
output(4, key_id4),
],
&keychain,
&builder,
)
.unwrap();
@ -447,11 +454,12 @@ fn tx_build_exchange() {
#[test]
fn reward_empty_block() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let previous_header = BlockHeader::default();
let b = new_block(vec![], &keychain, &previous_header, &key_id);
let b = new_block(vec![], &keychain, &builder, &previous_header, &key_id);
b.cut_through()
.unwrap()
@ -462,6 +470,7 @@ fn reward_empty_block() {
#[test]
fn reward_with_tx_block() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let vc = verifier_cache();
@ -471,7 +480,13 @@ fn reward_with_tx_block() {
let previous_header = BlockHeader::default();
let block = new_block(vec![&mut tx1], &keychain, &previous_header, &key_id);
let block = new_block(
vec![&mut tx1],
&keychain,
&builder,
&previous_header,
&key_id,
);
block
.cut_through()
.unwrap()
@ -482,6 +497,7 @@ fn reward_with_tx_block() {
#[test]
fn simple_block() {
let keychain = keychain::ExtKeychain::from_random_seed(false).unwrap();
let builder = ProofBuilder::new(&keychain);
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let vc = verifier_cache();
@ -493,6 +509,7 @@ fn simple_block() {
let b = new_block(
vec![&mut tx1, &mut tx2],
&keychain,
&builder,
&previous_header,
&key_id,
);
@ -503,7 +520,7 @@ fn simple_block() {
#[test]
fn test_block_with_timelocked_tx() {
let keychain = 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);
let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
@ -520,12 +537,19 @@ fn test_block_with_timelocked_tx() {
with_lock_height(1),
],
&keychain,
&builder,
)
.unwrap();
let previous_header = BlockHeader::default();
let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone());
let b = new_block(
vec![&tx1],
&keychain,
&builder,
&previous_header,
&key_id3.clone(),
);
b.validate(&BlindingFactor::zero(), vc.clone()).unwrap();
// now try adding a timelocked tx where lock height is greater than current
@ -538,11 +562,18 @@ fn test_block_with_timelocked_tx() {
with_lock_height(2),
],
&keychain,
&builder,
)
.unwrap();
let previous_header = BlockHeader::default();
let b = new_block(vec![&tx1], &keychain, &previous_header, &key_id3.clone());
let b = new_block(
vec![&tx1],
&keychain,
&builder,
&previous_header,
&key_id3.clone(),
);
match b.validate(&BlindingFactor::zero(), vc.clone()) {
Err(KernelLockHeight(height)) => {

View file

@ -27,8 +27,10 @@ use grin_keychain as keychain;
fn test_output_ser_deser() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
let switch = &keychain::SwitchCommitmentType::Regular;
let commit = keychain.commit(5, &key_id, switch).unwrap();
let builder = proof::ProofBuilder::new(&keychain);
let proof = proof::create(&keychain, &builder, 5, &key_id, switch, commit, None).unwrap();
let out = Output {
features: OutputFeatures::Plain,

View file

@ -17,7 +17,7 @@ pub mod common;
use self::core::core::verifier_cache::{LruVerifierCache, VerifierCache};
use self::core::core::{Output, OutputFeatures};
use self::core::libtx::proof;
use self::keychain::{ExtKeychain, Keychain};
use self::keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
use self::util::RwLock;
use grin_core as core;
use grin_keychain as keychain;
@ -34,8 +34,10 @@ fn test_verifier_cache_rangeproofs() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let commit = keychain.commit(5, &key_id).unwrap();
let proof = proof::create(&keychain, 5, &key_id, commit, None).unwrap();
let switch = &SwitchCommitmentType::Regular;
let commit = keychain.commit(5, &key_id, switch).unwrap();
let builder = proof::ProofBuilder::new(&keychain);
let proof = proof::create(&keychain, &builder, 5, &key_id, switch, commit, None).unwrap();
let out = Output {
features: OutputFeatures::Plain,

View file

@ -149,7 +149,7 @@ impl BIP32Hasher for BIP32GrinHasher {
}
/// Extended private key
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ExtendedPrivKey {
/// The network this key is to be used on
pub network: [u8; 4],
@ -399,7 +399,7 @@ impl ExtendedPrivKey {
where
H: BIP32Hasher,
{
let mut sk: ExtendedPrivKey = *self;
let mut sk: ExtendedPrivKey = self.clone();
for cnum in cnums {
sk = sk.ckd_priv(secp, hasher, *cnum)?;
}

View file

@ -17,22 +17,33 @@
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use crate::blake2;
use crate::blake2::blake2b::blake2b;
use crate::extkey_bip32::{BIP32GrinHasher, ExtendedPrivKey};
use crate::types::{BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain};
use crate::util::secp::key::SecretKey;
use crate::extkey_bip32::{BIP32GrinHasher, ExtendedPrivKey, ExtendedPubKey};
use crate::types::{
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, SwitchCommitmentType,
};
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::pedersen::Commitment;
use crate::util::secp::{self, Message, Secp256k1, Signature};
#[derive(Clone, Debug)]
pub struct ExtKeychain {
secp: Secp256k1,
master: ExtendedPrivKey,
use_switch_commits: bool,
pub master: ExtendedPrivKey,
hasher: BIP32GrinHasher,
}
impl ExtKeychain {
pub fn pub_root_key(&mut self) -> ExtendedPubKey {
ExtendedPubKey::from_private(&self.secp, &self.master, &mut self.hasher)
}
pub fn hasher(&self) -> BIP32GrinHasher {
self.hasher.clone()
}
}
impl Keychain for ExtKeychain {
fn from_seed(seed: &[u8], is_floo: bool) -> Result<ExtKeychain, Error> {
let mut h = BIP32GrinHasher::new(is_floo);
@ -41,7 +52,6 @@ impl Keychain for ExtKeychain {
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commits: true,
hasher: h,
};
Ok(keychain)
@ -54,7 +64,6 @@ impl Keychain for ExtKeychain {
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commits: true,
hasher: h,
};
Ok(keychain)
@ -63,7 +72,7 @@ impl Keychain for ExtKeychain {
/// For testing - probably not a good idea to use outside of tests.
fn from_random_seed(is_floo: bool) -> Result<ExtKeychain, Error> {
let seed: String = thread_rng().sample_iter(&Alphanumeric).take(16).collect();
let seed = blake2::blake2b::blake2b(32, &[], seed.as_bytes());
let seed = blake2b(32, &[], seed.as_bytes());
ExtKeychain::from_seed(seed.as_bytes(), is_floo)
}
@ -75,22 +84,39 @@ impl Keychain for ExtKeychain {
ExtKeychainPath::new(depth, d1, d2, d3, d4).to_identifier()
}
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error> {
fn public_root_key(&self) -> PublicKey {
let mut hasher = self.hasher.clone();
ExtendedPubKey::from_private(&self.secp, &self.master, &mut hasher).public_key
}
fn derive_key(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<SecretKey, Error> {
let mut h = self.hasher.clone();
let p = id.to_path();
let mut ext_key = self.master;
let mut ext_key = self.master.clone();
for i in 0..p.depth {
ext_key = ext_key.ckd_priv(&self.secp, &mut h, p.path[i as usize])?;
}
match self.use_switch_commits {
true => Ok(self.secp.blind_switch(amount, ext_key.secret_key)?),
false => Ok(ext_key.secret_key),
match *switch {
SwitchCommitmentType::Regular => {
Ok(self.secp.blind_switch(amount, ext_key.secret_key)?)
}
SwitchCommitmentType::None => Ok(ext_key.secret_key),
}
}
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error> {
let key = self.derive_key(amount, id)?;
fn commit(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Commitment, Error> {
let key = self.derive_key(amount, id, switch)?;
let commit = self.secp.commit(amount, key)?;
Ok(commit)
}
@ -100,7 +126,11 @@ impl Keychain for ExtKeychain {
.positive_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
let res = self.derive_key(
k.value,
&Identifier::from_path(&k.ext_keychain_path),
&k.switch,
);
if let Ok(s) = res {
Some(s)
} else {
@ -113,7 +143,11 @@ impl Keychain for ExtKeychain {
.negative_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
let res = self.derive_key(
k.value,
&Identifier::from_path(&k.ext_keychain_path),
&k.switch,
);
if let Ok(s) = res {
Some(s)
} else {
@ -122,37 +156,32 @@ impl Keychain for ExtKeychain {
})
.collect();
pos_keys.extend(
&blind_sum
.positive_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok())
.collect::<Vec<SecretKey>>(),
);
let keys = blind_sum
.positive_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok().clone())
.collect::<Vec<SecretKey>>();
pos_keys.extend(keys);
neg_keys.extend(
&blind_sum
.negative_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok())
.collect::<Vec<SecretKey>>(),
);
let keys = blind_sum
.negative_blinding_factors
.iter()
.filter_map(|b| b.secret_key(&self.secp).ok().clone())
.collect::<Vec<SecretKey>>();
neg_keys.extend(keys);
let sum = self.secp.blind_sum(pos_keys, neg_keys)?;
Ok(BlindingFactor::from_secret_key(sum))
}
fn create_nonce(&self, commit: &Commitment) -> Result<SecretKey, Error> {
// hash(commit|wallet root secret key (m)) as nonce
let root_key = self.derive_key(0, &Self::root_key_id())?;
let res = blake2::blake2b::blake2b(32, &commit.0, &root_key.0[..]);
let res = res.as_bytes();
SecretKey::from_slice(&self.secp, &res)
.map_err(|e| Error::RangeProof(format!("Unable to create nonce: {:?}", e).to_string()))
}
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error> {
let skey = self.derive_key(amount, id)?;
fn sign(
&self,
msg: &Message,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Signature, Error> {
let skey = self.derive_key(amount, id, switch)?;
let sig = self.secp.sign(msg, &skey)?;
Ok(sig)
}
@ -167,10 +196,6 @@ impl Keychain for ExtKeychain {
Ok(sig)
}
fn set_use_switch_commits(&mut self, value: bool) {
self.use_switch_commits = value;
}
fn secp(&self) -> &Secp256k1 {
&self.secp
}
@ -182,11 +207,13 @@ mod test {
use crate::types::{BlindSum, BlindingFactor, ExtKeychainPath, Keychain};
use crate::util::secp;
use crate::util::secp::key::SecretKey;
use crate::SwitchCommitmentType;
#[test]
fn test_key_derivation() {
let keychain = ExtKeychain::from_random_seed(false).unwrap();
let secp = keychain.secp();
let switch = &SwitchCommitmentType::None;
let path = ExtKeychainPath::new(1, 1, 0, 0, 0);
let key_id = path.to_identifier();
@ -196,10 +223,10 @@ mod test {
// now create a zero commitment using the key on the keychain associated with
// the key_id
let commit = keychain.commit(0, &key_id).unwrap();
let commit = keychain.commit(0, &key_id, switch).unwrap();
// now check we can use our key to verify a signature from this zero commitment
let sig = keychain.sign(&msg, 0, &key_id).unwrap();
let sig = keychain.sign(&msg, 0, &key_id, switch).unwrap();
secp.verify_from_commit(&msg, &sig, &commit).unwrap();
}
@ -235,9 +262,9 @@ mod test {
// create commitments for secret keys 1, 2 and 3
// all committing to the value 0 (which is what we do for tx_kernels)
let commit_1 = keychain.secp.commit(0, skey1).unwrap();
let commit_2 = keychain.secp.commit(0, skey2).unwrap();
let commit_3 = keychain.secp.commit(0, skey3).unwrap();
let commit_1 = keychain.secp.commit(0, skey1.clone()).unwrap();
let commit_2 = keychain.secp.commit(0, skey2.clone()).unwrap();
let commit_3 = keychain.secp.commit(0, skey3.clone()).unwrap();
// now sum commitments for keys 1 and 2
let sum = keychain

View file

@ -25,14 +25,19 @@ extern crate serde_derive;
#[macro_use]
extern crate lazy_static;
extern crate sha2;
mod base58;
pub mod extkey_bip32;
pub mod mnemonic;
mod types;
pub mod view_key;
pub mod keychain;
pub use crate::extkey_bip32::ChildNumber;
pub use crate::keychain::ExtKeychain;
pub use crate::types::{
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, IDENTIFIER_SIZE,
BlindSum, BlindingFactor, Error, ExtKeychainPath, Identifier, Keychain, SwitchCommitmentType,
IDENTIFIER_SIZE,
};
pub use crate::view_key::ViewKey;

View file

@ -14,6 +14,7 @@
use rand::thread_rng;
use std::cmp::min;
use std::convert::TryFrom;
use std::io::Cursor;
use std::ops::Add;
/// Keychain trait and its main supporting types. The Identifier is a
@ -129,9 +130,12 @@ impl Identifier {
}
pub fn to_value_path(&self, value: u64) -> ValueExtKeychainPath {
// TODO: proper support for different switch commitment schemes
// For now it is assumed all outputs are using the regular switch commitment scheme
ValueExtKeychainPath {
value,
ext_keychain_path: self.to_path(),
switch: SwitchCommitmentType::Regular,
}
}
@ -318,7 +322,7 @@ impl BlindingFactor {
// use blind_sum to subtract skey_1 from our key (to give k = k1 + k2)
let skey = self.secret_key(secp)?;
let skey_2 = secp.blind_sum(vec![skey], vec![skey_1])?;
let skey_2 = secp.blind_sum(vec![skey], vec![skey_1.clone()])?;
let blind_1 = BlindingFactor::from_secret_key(skey_1);
let blind_2 = BlindingFactor::from_secret_key(skey_2);
@ -443,11 +447,12 @@ impl ExtKeychainPath {
}
}
/// Wrapper for amount + path
/// Wrapper for amount + switch + path
#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize)]
pub struct ValueExtKeychainPath {
pub value: u64,
pub ext_keychain_path: ExtKeychainPath,
pub switch: SwitchCommitmentType,
}
pub trait Keychain: Sync + Send + Clone {
@ -467,16 +472,61 @@ pub trait Keychain: Sync + Send + Clone {
/// Derives a key id from the depth of the keychain and the values at each
/// depth level. See `KeychainPath` for more information.
fn derive_key_id(depth: u8, d1: u32, d2: u32, d3: u32, d4: u32) -> Identifier;
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error>;
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error>;
/// The public root key
fn public_root_key(&self) -> PublicKey;
fn derive_key(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<SecretKey, Error>;
fn commit(
&self,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Commitment, Error>;
fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error>;
fn create_nonce(&self, commit: &Commitment) -> Result<SecretKey, Error>;
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error>;
fn sign(
&self,
msg: &Message,
amount: u64,
id: &Identifier,
switch: &SwitchCommitmentType,
) -> Result<Signature, Error>;
fn sign_with_blinding(&self, _: &Message, _: &BlindingFactor) -> Result<Signature, Error>;
fn set_use_switch_commits(&mut self, value: bool);
fn secp(&self) -> &Secp256k1;
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum SwitchCommitmentType {
None,
Regular,
}
impl TryFrom<u8> for SwitchCommitmentType {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(SwitchCommitmentType::None),
1 => Ok(SwitchCommitmentType::Regular),
_ => Err(()),
}
}
}
impl From<&SwitchCommitmentType> for u8 {
fn from(switch: &SwitchCommitmentType) -> Self {
match *switch {
SwitchCommitmentType::None => 0,
SwitchCommitmentType::Regular => 1,
}
}
}
#[cfg(test)]
mod test {
use rand::thread_rng;
@ -519,7 +569,7 @@ mod test {
fn split_blinding_factor() {
let secp = Secp256k1::new();
let skey_in = SecretKey::new(&secp, &mut thread_rng());
let blind = BlindingFactor::from_secret_key(skey_in);
let blind = BlindingFactor::from_secret_key(skey_in.clone());
let split = blind.split(&secp).unwrap();
// split a key, sum the split keys and confirm the sum matches the original key

195
keychain/src/view_key.rs Normal file
View file

@ -0,0 +1,195 @@
use crate::blake2::blake2b::blake2b;
use byteorder::{BigEndian, ByteOrder};
//use crate::sha2::{Digest, Sha256};
use super::extkey_bip32::{
BIP32Hasher, ChainCode, ChildNumber, Error as BIP32Error, ExtendedPrivKey, ExtendedPubKey,
Fingerprint,
};
use super::types::{Error, Keychain};
use crate::util::secp::constants::GENERATOR_PUB_J_RAW;
use crate::util::secp::ffi;
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::Secp256k1;
use crate::SwitchCommitmentType;
/*const VERSION_FLOO_NS: [u8;4] = [0x03, 0x27, 0x3E, 0x4B];
const VERSION_FLOO: [u8;4] = [0x03, 0x27, 0x3E, 0x4B];
const VERSION_MAIN_NS: [u8;4] = [0x03, 0x3C, 0x08, 0xDF];
const VERSION_MAIN: [u8;4] = [0x03, 0x3C, 0x08, 0xDF];*/
/// Key that can be used to scan the chain for owned outputs
/// This is a public key, meaning it cannot be used to spend those outputs
/// At the moment only depth 0 keys can be used
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ViewKey {
/// Whether this view key is meant for floonet or not
pub is_floo: bool,
/// How many derivations this key is from the master (which is 0)
pub depth: u8,
/// Fingerprint of the parent key
parent_fingerprint: Fingerprint,
/// Child number of the key used to derive from parent (0 for master)
pub child_number: ChildNumber,
/// Public key
public_key: PublicKey,
/// Switch public key, required to view outputs that use switch commitment
switch_public_key: Option<PublicKey>,
/// Chain code
chain_code: ChainCode,
/// Hash used to generate rewind nonce
pub rewind_hash: Vec<u8>,
}
impl ViewKey {
pub fn create<K, H>(
keychain: &K,
ext_key: ExtendedPrivKey,
hasher: &mut H,
is_floo: bool,
) -> Result<Self, Error>
where
K: Keychain,
H: BIP32Hasher,
{
let secp = keychain.secp();
let ExtendedPubKey {
network: _,
depth,
parent_fingerprint,
child_number,
public_key,
chain_code,
} = ExtendedPubKey::from_private(secp, &ext_key, hasher);
let mut switch_public_key = PublicKey(ffi::PublicKey(GENERATOR_PUB_J_RAW));
switch_public_key.mul_assign(secp, &ext_key.secret_key)?;
let switch_public_key = Some(switch_public_key);
let rewind_hash = Self::rewind_hash(secp, keychain.public_root_key());
Ok(Self {
is_floo,
depth,
parent_fingerprint,
child_number,
public_key,
switch_public_key,
chain_code,
rewind_hash,
})
}
fn rewind_hash(secp: &Secp256k1, public_root_key: PublicKey) -> Vec<u8> {
let ser = public_root_key.serialize_vec(secp, true);
blake2b(32, &[], &ser[..]).as_bytes().to_vec()
}
fn ckd_pub_tweak<H>(
&self,
secp: &Secp256k1,
hasher: &mut H,
i: ChildNumber,
) -> Result<(SecretKey, ChainCode), Error>
where
H: BIP32Hasher,
{
match i {
ChildNumber::Hardened { .. } => Err(BIP32Error::CannotDeriveFromHardenedKey.into()),
ChildNumber::Normal { index: n } => {
hasher.init_sha512(&self.chain_code[..]);
hasher.append_sha512(&self.public_key.serialize_vec(secp, true)[..]);
let mut be_n = [0; 4];
BigEndian::write_u32(&mut be_n, n);
hasher.append_sha512(&be_n);
let result = hasher.result_sha512();
let secret_key = SecretKey::from_slice(secp, &result[..32])?;
let chain_code = ChainCode::from(&result[32..]);
Ok((secret_key, chain_code))
}
}
}
pub fn ckd_pub<H>(
&self,
secp: &Secp256k1,
hasher: &mut H,
i: ChildNumber,
) -> Result<Self, Error>
where
H: BIP32Hasher,
{
let (secret_key, chain_code) = self.ckd_pub_tweak(secp, hasher, i)?;
let mut public_key = self.public_key.clone();
public_key.add_exp_assign(secp, &secret_key)?;
let switch_public_key = match &self.switch_public_key {
Some(p) => {
let mut j = PublicKey(ffi::PublicKey(GENERATOR_PUB_J_RAW));
j.mul_assign(secp, &secret_key)?;
Some(PublicKey::from_combination(secp, vec![p, &j])?)
}
None => None,
};
Ok(Self {
is_floo: self.is_floo,
depth: self.depth + 1,
parent_fingerprint: self.fingerprint(secp, hasher),
child_number: i,
public_key,
switch_public_key,
chain_code,
rewind_hash: self.rewind_hash.clone(),
})
}
pub fn commit(
&self,
secp: &Secp256k1,
amount: u64,
switch: &SwitchCommitmentType,
) -> Result<PublicKey, Error> {
let value_key = secp.commit_value(amount)?.to_pubkey(secp)?;
let pub_key = PublicKey::from_combination(secp, vec![&self.public_key, &value_key])?;
match *switch {
SwitchCommitmentType::None => Ok(pub_key),
SwitchCommitmentType::Regular => {
// TODO: replace this whole block by a libsecp function
/*let switch_pub = self.switch_public_key.ok_or(Error::SwitchCommitment)?;
let switch_ser: Vec<u8> = switch_pub.serialize_vec(secp, true)[..].to_vec();
let mut commit_ser: Vec<u8> = pub_key.serialize_vec(secp, true)[..].to_vec();
commit_ser[0] += 6; // This only works sometimes
let mut hasher = Sha256::new();
hasher.input(&commit_ser);
hasher.input(&switch_ser);
let blind = SecretKey::from_slice(secp, &hasher.result()[..])?;
let mut pub_key = pub_key;
pub_key.add_exp_assign(secp, &blind)?;
Ok(pub_key)*/
Err(Error::SwitchCommitment)
}
}
}
fn identifier<H>(&self, secp: &Secp256k1, hasher: &mut H) -> [u8; 20]
where
H: BIP32Hasher,
{
let sha2_res = hasher.sha_256(&self.public_key.serialize_vec(secp, true)[..]);
hasher.ripemd_160(&sha2_res)
}
fn fingerprint<H>(&self, secp: &Secp256k1, hasher: &mut H) -> Fingerprint
where
H: BIP32Hasher,
{
Fingerprint::from(&self.identifier(secp, hasher)[0..4])
}
}

View file

@ -47,7 +47,14 @@ fn test_transaction_pool_block_building() {
let height = prev_header.height + 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let fee = txs.iter().map(|x| x.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fee, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fee,
false,
)
.unwrap();
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -51,7 +51,14 @@ fn test_block_building_max_weight() {
let height = prev_header.height + 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let fee = txs.iter().map(|x| x.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fee, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fee,
false,
)
.unwrap();
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -45,7 +45,14 @@ fn test_transaction_pool_block_reconciliation() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let genesis = BlockHeader::default();
let mut block = Block::new(&genesis, vec![], Difficulty::min(), reward).unwrap();
@ -65,7 +72,14 @@ fn test_transaction_pool_block_reconciliation() {
let block = {
let key_id = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
let fees = initial_tx.fee();
let reward = libtx::reward::output(&keychain, &key_id, fees, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fees,
false,
)
.unwrap();
let mut block =
Block::new(&header, vec![initial_tx], Difficulty::min(), reward).unwrap();
@ -159,7 +173,14 @@ fn test_transaction_pool_block_reconciliation() {
let block = {
let key_id = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
let fees = block_txs.iter().map(|tx| tx.fee()).sum();
let reward = libtx::reward::output(&keychain, &key_id, fees, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
fees,
false,
)
.unwrap();
let mut block = Block::new(&header, block_txs, Difficulty::min(), reward).unwrap();
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).

View file

@ -195,7 +195,7 @@ where
tx_elements.push(libtx::build::with_fee(fees as u64));
libtx::build::transaction(tx_elements, keychain).unwrap()
libtx::build::transaction(tx_elements, keychain, &libtx::ProofBuilder::new(keychain)).unwrap()
}
pub fn test_transaction<K>(
@ -225,7 +225,7 @@ where
}
tx_elements.push(libtx::build::with_fee(fees as u64));
libtx::build::transaction(tx_elements, keychain).unwrap()
libtx::build::transaction(tx_elements, keychain, &libtx::ProofBuilder::new(keychain)).unwrap()
}
pub fn test_source() -> TxSource {

View file

@ -44,7 +44,14 @@ fn test_the_transaction_pool() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let block = Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();
chain.update_db_for_block(&block);
@ -246,7 +253,14 @@ fn test_the_transaction_pool() {
let header = {
let height = 1;
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
let reward = libtx::reward::output(&keychain, &key_id, 0, false).unwrap();
let reward = libtx::reward::output(
&keychain,
&libtx::ProofBuilder::new(&keychain),
&key_id,
0,
false,
)
.unwrap();
let block =
Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();

View file

@ -28,6 +28,7 @@ use crate::common::types::Error;
use crate::core::core::verifier_cache::VerifierCache;
use crate::core::core::{Output, TxKernel};
use crate::core::libtx::secp_ser;
use crate::core::libtx::ProofBuilder;
use crate::core::{consensus, core, global};
use crate::keychain::{ExtKeychain, Identifier, Keychain};
use crate::pool;
@ -223,8 +224,14 @@ fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, B
warn!("Burning block fees: {:?}", block_fees);
let keychain = ExtKeychain::from_random_seed(global::is_floonet())?;
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let (out, kernel) =
crate::core::libtx::reward::output(&keychain, &key_id, block_fees.fees, false).unwrap();
let (out, kernel) = crate::core::libtx::reward::output(
&keychain,
&ProofBuilder::new(&keychain),
&key_id,
block_fees.fees,
false,
)
.unwrap();
Ok((out, kernel, block_fees))
}

View file

@ -28,5 +28,5 @@ zeroize = "0.5.2"
#git = "https://github.com/mimblewimble/rust-secp256k1-zkp"
#tag = "grin_integration_29"
#path = "../../rust-secp256k1-zkp"
version = "0.7.5"
version = "0.7.6"
features = ["bullet-proof-sizing"]