rust-secp256k1-zkp breaking changes to support (#155)

Rewinding range proofs to recover transaction value from outputs
This commit is contained in:
AntiochP 2017-10-05 17:40:46 -04:00 committed by Ignotus Peverell
parent ea632076bd
commit e060b1953e
6 changed files with 124 additions and 15 deletions

View file

@ -484,9 +484,10 @@ impl Block {
let msg = secp::Message::from_slice(&[0; secp::constants::MESSAGE_SIZE])?;
let sig = keychain.sign(&msg, &pubkey)?;
let commit = keychain.commit(REWARD, &pubkey)?;
let msg = secp::pedersen::ProofMessage::empty();
// let switch_commit = keychain.switch_commit(pubkey)?;
let rproof = keychain.range_proof(REWARD, &pubkey, commit)?;
let rproof = keychain.range_proof(REWARD, &pubkey, commit, msg)?;
let output = Output {
features: COINBASE_OUTPUT,

View file

@ -55,7 +55,8 @@ pub fn input(value: u64, pubkey: Identifier) -> Box<Append> {
pub fn output(value: u64, pubkey: Identifier) -> Box<Append> {
Box::new(move |build, (tx, sum)| -> (Transaction, BlindSum) {
let commit = build.keychain.commit(value, &pubkey).unwrap();
let rproof = build.keychain.range_proof(value, &pubkey, commit).unwrap();
let msg = secp::pedersen::ProofMessage::empty();
let rproof = build.keychain.range_proof(value, &pubkey, commit, msg).unwrap();
(tx.with_output(Output {
features: DEFAULT_OUTPUT,

View file

@ -21,6 +21,7 @@ use std::ops;
use core::Committed;
use core::pmmr::Summable;
use keychain::{Identifier, Keychain};
use ser::{self, Reader, Writer, Readable, Writeable};
bitflags! {
@ -357,6 +358,21 @@ impl Output {
pub fn verify_proof(&self, secp: &Secp256k1) -> Result<(), secp::Error> {
secp.verify_range_proof(self.commit, self.proof).map(|_| ())
}
/// Given the original blinding factor we can recover the
/// value from the range proof and the commitment
pub fn recover_value(&self, keychain: &Keychain, pubkey: &Identifier) -> Option<u64> {
match keychain.rewind_range_proof(pubkey, self.commit, self.proof) {
Ok(proof_info) => {
if proof_info.success {
Some(proof_info.value)
} else {
None
}
},
Err(_) => None
}
}
}
/// Wrapper to Output commitments to provide the Summable trait.
@ -423,3 +439,38 @@ fn u64_to_32bytes(n: u64) -> [u8; 32] {
BigEndian::write_u64(&mut bytes[24..32], n);
bytes
}
#[cfg(test)]
mod test {
use super::*;
use keychain::Keychain;
use secp;
#[test]
fn test_output_value_recovery() {
let keychain = Keychain::from_random_seed().unwrap();
let pubkey = keychain.derive_pubkey(1).unwrap();
let commit = keychain.commit(1003, &pubkey).unwrap();
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(1003, &pubkey, commit, msg).unwrap();
let output = Output {
features: DEFAULT_OUTPUT,
commit: commit,
proof: proof,
};
// check we can successfully recover the value with the original blinding factor
let recovered_value = output.recover_value(&keychain, &pubkey).unwrap();
assert_eq!(recovered_value, 1003);
// check we cannot recover the value without the original blinding factor
let pubkey2 = keychain.derive_pubkey(2).unwrap();
let not_recoverable = output.recover_value(&keychain, &pubkey2);
match not_recoverable {
Some(_) => panic!("expected value to be None here"),
None => {}
}
}
}

View file

@ -17,7 +17,7 @@ use rand::{thread_rng, Rng};
use secp;
use secp::{Message, Secp256k1, Signature};
use secp::key::SecretKey;
use secp::pedersen::{Commitment, RangeProof};
use secp::pedersen::{Commitment, ProofMessage, ProofInfo, RangeProof};
use blake2;
use blind::{BlindingFactor, BlindSum};
@ -107,13 +107,23 @@ impl Keychain {
amount: u64,
pubkey: &Identifier,
commit: Commitment,
msg: ProofMessage,
) -> Result<RangeProof, Error> {
let skey = self.derived_key(pubkey)?;
let nonce = self.secp.nonce();
let range_proof = self.secp.range_proof(0, amount, skey, commit, nonce);
let range_proof = self.secp.range_proof(0, amount, skey, commit, msg);
Ok(range_proof)
}
pub fn rewind_range_proof(
&self,
pubkey: &Identifier,
commit: Commitment,
proof: RangeProof,
) -> Result<ProofInfo, Error> {
let nonce = self.derived_key(pubkey)?;
Ok(self.secp.rewind_range_proof(commit, proof, nonce))
}
pub fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error> {
let mut pos_keys: Vec<SecretKey> = blind_sum
.positive_pubkeys
@ -167,11 +177,11 @@ impl Keychain {
mod test {
use keychain::Keychain;
use secp;
use secp::pedersen::ProofMessage;
#[test]
fn test_key_derivation() {
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
let keychain = Keychain::from_random_seed().unwrap();
// use the keychain to derive a "pubkey" based on the underlying seed
@ -188,4 +198,43 @@ mod test {
let sig = keychain.sign(&msg, &pubkey).unwrap();
secp.verify_from_commit(&msg, &sig, &commit).unwrap();
}
#[test]
fn test_rewind_range_proof() {
let keychain = Keychain::from_random_seed().unwrap();
let pubkey = keychain.derive_pubkey(1).unwrap();
let commit = keychain.commit(5, &pubkey).unwrap();
let msg = ProofMessage::empty();
let proof = keychain.range_proof(5, &pubkey, commit, msg).unwrap();
let proof_info = keychain.rewind_range_proof(&pubkey, commit, proof).unwrap();
assert_eq!(proof_info.success, true);
assert_eq!(proof_info.value, 5);
// now check the recovered message is "empty" (but not truncated) i.e. all zeroes
assert_eq!(
proof_info.message,
secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::PROOF_MSG_SIZE])
);
let pubkey2 = keychain.derive_pubkey(2).unwrap();
// cannot rewind with a different nonce
let proof_info = keychain.rewind_range_proof(&pubkey2, commit, proof).unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
// cannot rewind with a commitment to the same value using a different key
let commit2 = keychain.commit(5, &pubkey2).unwrap();
let proof_info = keychain.rewind_range_proof(&pubkey, commit2, proof).unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
// cannot rewind with a commitment to a different value
let commit3 = keychain.commit(4, &pubkey).unwrap();
let proof_info = keychain.rewind_range_proof(&pubkey, commit3, proof).unwrap();
assert_eq!(proof_info.success, false);
assert_eq!(proof_info.value, 0);
}
}

View file

@ -248,23 +248,27 @@ pub fn transaction_identifier(tx: &core::transaction::Transaction) -> core::hash
#[cfg(test)]
mod tests {
use super::*;
use secp::{Secp256k1, ContextFlag};
use secp::key;
use secp;
use keychain::Keychain;
#[test]
fn test_add_entry() {
let ec = Secp256k1::with_caps(ContextFlag::Commit);
let keychain = Keychain::from_random_seed().unwrap();
let pk1 = keychain.derive_pubkey(1).unwrap();
let pk2 = keychain.derive_pubkey(2).unwrap();
let pk3 = keychain.derive_pubkey(3).unwrap();
let output_commit = ec.commit_value(70).unwrap();
let output_commit = keychain.commit(70, &pk1).unwrap();
let inputs = vec![
core::transaction::Input(ec.commit_value(50).unwrap()),
core::transaction::Input(ec.commit_value(25).unwrap()),
core::transaction::Input(keychain.commit(50, &pk2).unwrap()),
core::transaction::Input(keychain.commit(25, &pk3).unwrap()),
];
let msg = secp::pedersen::ProofMessage::empty();
let outputs = vec![
core::transaction::Output {
features: core::transaction::DEFAULT_OUTPUT,
commit: output_commit,
proof: ec.range_proof(0, 100, key::ZERO_KEY, output_commit, ec.nonce()),
proof: keychain.range_proof(100, &pk1, output_commit, msg).unwrap(),
},
];
let test_transaction = core::transaction::Transaction::new(inputs, outputs, 5);

View file

@ -561,6 +561,7 @@ mod tests {
use types::*;
use core::core::build;
use blockchain::{DummyChain, DummyChainImpl, DummyUtxoSet};
use secp;
use keychain::Keychain;
use std::sync::{Arc, RwLock};
use blake2;
@ -1074,7 +1075,8 @@ mod tests {
let keychain = keychain_for_tests();
let pubkey = keychain.derive_pubkey(value as u32).unwrap();
let commit = keychain.commit(value, &pubkey).unwrap();
let proof = keychain.range_proof(value, &pubkey, commit).unwrap();
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(value, &pubkey, commit, msg).unwrap();
transaction::Output {
features: transaction::DEFAULT_OUTPUT,
@ -1088,7 +1090,8 @@ mod tests {
let keychain = keychain_for_tests();
let pubkey = keychain.derive_pubkey(value as u32).unwrap();
let commit = keychain.commit(value, &pubkey).unwrap();
let proof = keychain.range_proof(value, &pubkey, commit).unwrap();
let msg = secp::pedersen::ProofMessage::empty();
let proof = keychain.range_proof(value, &pubkey, commit, msg).unwrap();
transaction::Output {
features: transaction::COINBASE_OUTPUT,