mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
rust-secp256k1-zkp breaking changes to support (#155)
Rewinding range proofs to recover transaction value from outputs
This commit is contained in:
parent
ea632076bd
commit
e060b1953e
6 changed files with 124 additions and 15 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue