2020-01-20 14:40:58 +03:00
|
|
|
// Copyright 2020 The Grin Developers
|
2018-05-09 12:15:58 +03:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
//! Rangeproof library functions
|
|
|
|
|
2018-12-08 02:59:40 +03:00
|
|
|
use crate::libtx::error::{Error, ErrorKind};
|
2019-11-14 18:27:30 +03:00
|
|
|
use blake2::blake2b::blake2b;
|
|
|
|
use keychain::extkey_bip32::BIP32GrinHasher;
|
|
|
|
use keychain::{Identifier, Keychain, SwitchCommitmentType, ViewKey};
|
2019-06-27 11:19:17 +03:00
|
|
|
use std::convert::TryFrom;
|
2019-11-14 18:27:30 +03:00
|
|
|
use util::secp::key::SecretKey;
|
|
|
|
use util::secp::pedersen::{Commitment, ProofMessage, RangeProof};
|
|
|
|
use util::secp::{self, Secp256k1};
|
|
|
|
use zeroize::Zeroize;
|
2018-05-09 12:15:58 +03:00
|
|
|
|
2018-06-25 14:28:56 +03:00
|
|
|
/// Create a bulletproof
|
2019-06-27 11:19:17 +03:00
|
|
|
pub fn create<K, B>(
|
2018-06-08 08:21:54 +03:00
|
|
|
k: &K,
|
2019-06-27 11:19:17 +03:00
|
|
|
b: &B,
|
2018-05-09 12:15:58 +03:00
|
|
|
amount: u64,
|
|
|
|
key_id: &Identifier,
|
2019-06-27 11:19:17 +03:00
|
|
|
switch: &SwitchCommitmentType,
|
2018-05-09 12:15:58 +03:00
|
|
|
_commit: Commitment,
|
|
|
|
extra_data: Option<Vec<u8>>,
|
2018-06-08 08:21:54 +03:00
|
|
|
) -> Result<RangeProof, Error>
|
|
|
|
where
|
|
|
|
K: Keychain,
|
2019-06-27 11:19:17 +03:00
|
|
|
B: ProofBuild,
|
2018-06-08 08:21:54 +03:00
|
|
|
{
|
2019-06-27 11:19:17 +03:00
|
|
|
// 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),
|
|
|
|
))
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|
|
|
|
|
2018-05-24 18:27:26 +03:00
|
|
|
/// Verify a proof
|
2018-05-09 12:15:58 +03:00
|
|
|
pub fn verify(
|
|
|
|
secp: &Secp256k1,
|
|
|
|
commit: Commitment,
|
|
|
|
proof: RangeProof,
|
|
|
|
extra_data: Option<Vec<u8>>,
|
|
|
|
) -> Result<(), secp::Error> {
|
|
|
|
let result = secp.verify_bullet_proof(commit, proof, extra_data);
|
|
|
|
match result {
|
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(e) => Err(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-27 11:19:17 +03:00
|
|
|
/// Rewind a rangeproof to retrieve the amount, derivation path and switch commitment type
|
|
|
|
pub fn rewind<B>(
|
|
|
|
secp: &Secp256k1,
|
|
|
|
b: &B,
|
2018-05-09 12:15:58 +03:00
|
|
|
commit: Commitment,
|
|
|
|
extra_data: Option<Vec<u8>>,
|
|
|
|
proof: RangeProof,
|
2019-06-27 11:19:17 +03:00
|
|
|
) -> Result<Option<(u64, Identifier, SwitchCommitmentType)>, Error>
|
2018-06-08 08:21:54 +03:00
|
|
|
where
|
2019-06-27 11:19:17 +03:00
|
|
|
B: ProofBuild,
|
2018-06-08 08:21:54 +03:00
|
|
|
{
|
2019-06-27 11:19:17 +03:00
|
|
|
let nonce = b
|
|
|
|
.rewind_nonce(secp, &commit)
|
2019-03-19 19:13:49 +03:00
|
|
|
.map_err(|e| ErrorKind::RangeProof(e.to_string()))?;
|
2019-06-27 11:19:17 +03:00
|
|
|
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,
|
|
|
|
{
|
|
|
|
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::*;
|
2019-11-14 18:27:30 +03:00
|
|
|
use keychain::ChildNumber;
|
|
|
|
use keychain::ExtKeychain;
|
2019-06-27 11:19:17 +03:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
2018-05-09 12:15:58 +03:00
|
|
|
}
|