Switch commitments (#2157)

This commit is contained in:
jaspervdm 2018-12-18 12:51:44 +01:00 committed by Yeastplume
parent 590ea33484
commit 75d2c1cc56
11 changed files with 98 additions and 51 deletions

View file

@ -403,6 +403,13 @@ fn comments() -> HashMap<String, String> {
"dark_background_color_scheme".to_string(),
"
#Whether to use the black background color scheme for command line
"
.to_string(),
);
retval.insert(
"use_switch_commitments".to_string(),
"
#Whether to use switch commitments for this wallet
"
.to_string(),
);

View file

@ -251,24 +251,25 @@ pub fn verify_partial_sig(
/// let msg = kernel_sig_msg(0, height, KernelFeatures::DEFAULT_KERNEL).unwrap();
/// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap();
/// let pubkey = excess.to_pubkey(&secp).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, value, &key_id, Some(&pubkey)).unwrap();
/// ```
pub fn sign_from_key_id<K>(
secp: &Secp256k1,
k: &K,
msg: &Message,
value: u64,
key_id: &Identifier,
blind_sum: Option<&PublicKey>,
) -> Result<Signature, Error>
where
K: Keychain,
{
let skey = k.derive_key(key_id)?;
let skey = k.derive_key(value, key_id)?;
let sig = aggsig::sign_single(
secp,
&msg,
&skey.secret_key,
&skey,
None,
None,
None,
@ -324,7 +325,7 @@ where
/// let msg = kernel_sig_msg(0, height, KernelFeatures::DEFAULT_KERNEL).unwrap();
/// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap();
/// let pubkey = excess.to_pubkey(&secp).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, value, &key_id, Some(&pubkey)).unwrap();
///
/// // Verify the signature from the excess commit
/// let sig_verifies =

View file

@ -54,7 +54,7 @@ where
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
let commit = build.keychain.commit(value, &key_id).unwrap();
let input = Input::new(features, commit);
(tx.with_input(input), kern, sum.sub_key_id(key_id.to_path()))
(tx.with_input(input), kern, sum.sub_key_id(key_id.to_value_path(value)))
},
)
}
@ -102,7 +102,7 @@ where
proof: rproof,
}),
kern,
sum.add_key_id(key_id.to_path()),
sum.add_key_id(key_id.to_value_path(value)),
)
},
)

View file

@ -26,7 +26,7 @@ where
K: Keychain,
{
// hash(commit|wallet root secret key (m)) as nonce
let root_key = k.derive_key(&K::root_key_id())?.secret_key;
let root_key = k.derive_key(0, &K::root_key_id())?;
let res = blake2::blake2b::blake2b(32, &commit.0, &root_key.0[..]);
let res = res.as_bytes();
let mut ret_val = [0; 32];
@ -53,11 +53,11 @@ where
K: Keychain,
{
let commit = k.commit(amount, key_id)?;
let skey = k.derive_key(key_id)?;
let skey = k.derive_key(amount, key_id)?;
let nonce = create_nonce(k, &commit)?;
let message = ProofMessage::from_bytes(&key_id.serialize_path());
Ok(k.secp()
.bullet_proof(amount, skey.secret_key, nonce, extra_data, Some(message)))
.bullet_proof(amount, skey, nonce, extra_data, Some(message)))
}
/// Verify a proof

View file

@ -59,7 +59,7 @@ where
// This output will not be spendable earlier than lock_height (and we sign this
// here).
let msg = kernel_sig_msg(0, height, KernelFeatures::COINBASE_KERNEL)?;
let sig = aggsig::sign_from_key_id(&secp, keychain, &msg, &key_id, Some(&pubkey))?;
let sig = aggsig::sign_from_key_id(&secp, keychain, &msg, value, &key_id, Some(&pubkey))?;
let proof = TxKernel {
features: KernelFeatures::COINBASE_KERNEL,

View file

@ -29,16 +29,18 @@ use crate::util::secp::{self, Message, Secp256k1, Signature};
pub struct ExtKeychain {
secp: Secp256k1,
master: ExtendedPrivKey,
use_switch_commitments: Option<bool>
}
impl Keychain for ExtKeychain {
fn from_seed(seed: &[u8]) -> Result<ExtKeychain, Error> {
fn from_seed(seed: &[u8], use_switch_commitments: bool) -> Result<ExtKeychain, Error> {
let mut h = BIP32GrinHasher::new();
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
let master = ExtendedPrivKey::new_master(&secp, &mut h, seed)?;
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commitments: Some(use_switch_commitments)
};
Ok(keychain)
}
@ -49,15 +51,16 @@ impl Keychain for ExtKeychain {
let keychain = ExtKeychain {
secp: secp,
master: master,
use_switch_commitments: None
};
Ok(keychain)
}
/// For testing - probably not a good idea to use outside of tests.
fn from_random_seed() -> Result<ExtKeychain, Error> {
fn from_random_seed(use_switch_commitments: bool) -> Result<ExtKeychain, Error> {
let seed: String = thread_rng().sample_iter(&Alphanumeric).take(16).collect();
let seed = blake2::blake2b::blake2b(32, &[], seed.as_bytes());
ExtKeychain::from_seed(seed.as_bytes())
ExtKeychain::from_seed(seed.as_bytes(), use_switch_commitments)
}
fn root_key_id() -> Identifier {
@ -68,19 +71,27 @@ impl Keychain for ExtKeychain {
ExtKeychainPath::new(depth, d1, d2, d3, d4).to_identifier()
}
fn derive_key(&self, id: &Identifier) -> Result<ExtendedPrivKey, Error> {
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error> {
let mut h = BIP32GrinHasher::new();
let p = id.to_path();
let mut sk = self.master;
let mut ext_key = self.master;
for i in 0..p.depth {
sk = sk.ckd_priv(&self.secp, &mut h, p.path[i as usize])?;
ext_key = ext_key.ckd_priv(&self.secp, &mut h, p.path[i as usize])?;
}
// Switch commitments have to be explicitly turned on or off
let use_switch = self.use_switch_commitments.ok_or(Error::SwitchCommitment)?;
if use_switch {
Ok(self.secp.blind_switch(amount, ext_key.secret_key)?)
}
else {
Ok(ext_key.secret_key)
}
Ok(sk)
}
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error> {
let key = self.derive_key(id)?;
let commit = self.secp.commit(amount, key.secret_key)?;
let key = self.derive_key(amount, id)?;
let commit = self.secp.commit(amount, key)?;
Ok(commit)
}
@ -89,9 +100,9 @@ impl Keychain for ExtKeychain {
.positive_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(&Identifier::from_path(&k));
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
if let Ok(s) = res {
Some(s.secret_key)
Some(s)
} else {
None
}
@ -102,9 +113,9 @@ impl Keychain for ExtKeychain {
.negative_key_ids
.iter()
.filter_map(|k| {
let res = self.derive_key(&Identifier::from_path(&k));
let res = self.derive_key(k.value, &Identifier::from_path(&k.ext_keychain_path));
if let Ok(s) = res {
Some(s.secret_key)
Some(s)
} else {
None
}
@ -131,9 +142,9 @@ impl Keychain for ExtKeychain {
Ok(BlindingFactor::from_secret_key(sum))
}
fn sign(&self, msg: &Message, id: &Identifier) -> Result<Signature, Error> {
let skey = self.derive_key(id)?;
let sig = self.secp.sign(msg, &skey.secret_key)?;
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error> {
let skey = self.derive_key(amount, id)?;
let sig = self.secp.sign(msg, &skey)?;
Ok(sig)
}
@ -150,6 +161,10 @@ impl Keychain for ExtKeychain {
fn secp(&self) -> &Secp256k1 {
&self.secp
}
fn use_switch_commitments(&self) -> Option<bool> {
self.use_switch_commitments
}
}
#[cfg(test)]
@ -162,6 +177,7 @@ mod test {
#[test]
fn test_key_derivation() {
let keychain = ExtKeychain::from_random_seed().unwrap();
keychain.set_use_switch_commitments(true);
let secp = keychain.secp();
let path = ExtKeychainPath::new(1, 1, 0, 0, 0);
@ -175,7 +191,7 @@ mod test {
let commit = keychain.commit(0, &key_id).unwrap();
// now check we can use our key to verify a signature from this zero commitment
let sig = keychain.sign(&msg, &key_id).unwrap();
let sig = keychain.sign(&msg, 0, &key_id).unwrap();
secp.verify_from_commit(&msg, &sig, &commit).unwrap();
}
@ -186,6 +202,7 @@ mod test {
#[test]
fn secret_key_addition() {
let keychain = ExtKeychain::from_random_seed().unwrap();
keychain.set_use_switch_commitments(false);
let skey1 = SecretKey::from_slice(
&keychain.secp,

View file

@ -23,7 +23,7 @@ use std::ops::Add;
use std::{error, fmt};
use crate::blake2::blake2b::blake2b;
use crate::extkey_bip32::{self, ChildNumber, ExtendedPrivKey};
use crate::extkey_bip32::{self, ChildNumber};
use serde::{de, ser}; //TODO: Convert errors to use ErrorKind
use crate::util;
@ -44,6 +44,7 @@ pub enum Error {
KeyDerivation(extkey_bip32::Error),
Transaction(String),
RangeProof(String),
SwitchCommitment
}
impl From<secp::Error> for Error {
@ -126,6 +127,13 @@ impl Identifier {
ExtKeychainPath::from_identifier(&self)
}
pub fn to_value_path(&self, value: u64) -> ValueExtKeychainPath {
ValueExtKeychainPath {
value,
ext_keychain_path: self.to_path()
}
}
/// output the path itself, for insertion into bulletproof
/// recovery processes can grind through possiblities to find the
/// correct length if required
@ -327,8 +335,8 @@ pub struct SplitBlindingFactor {
/// factor as well as the "sign" with which they should be combined.
#[derive(Clone, Debug, PartialEq)]
pub struct BlindSum {
pub positive_key_ids: Vec<ExtKeychainPath>,
pub negative_key_ids: Vec<ExtKeychainPath>,
pub positive_key_ids: Vec<ValueExtKeychainPath>,
pub negative_key_ids: Vec<ValueExtKeychainPath>,
pub positive_blinding_factors: Vec<BlindingFactor>,
pub negative_blinding_factors: Vec<BlindingFactor>,
}
@ -344,12 +352,12 @@ impl BlindSum {
}
}
pub fn add_key_id(mut self, path: ExtKeychainPath) -> BlindSum {
pub fn add_key_id(mut self, path: ValueExtKeychainPath) -> BlindSum {
self.positive_key_ids.push(path);
self
}
pub fn sub_key_id(mut self, path: ExtKeychainPath) -> BlindSum {
pub fn sub_key_id(mut self, path: ValueExtKeychainPath) -> BlindSum {
self.negative_key_ids.push(path);
self
}
@ -430,18 +438,26 @@ impl ExtKeychainPath {
}
}
/// Wrapper for amount + path
#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize)]
pub struct ValueExtKeychainPath {
pub value: u64,
pub ext_keychain_path: ExtKeychainPath
}
pub trait Keychain: Sync + Send + Clone {
fn from_seed(seed: &[u8]) -> Result<Self, Error>;
fn from_seed(seed: &[u8], use_switch_commitments: bool) -> Result<Self, Error>;
fn from_mnemonic(word_list: &str, extension_word: &str) -> Result<Self, Error>;
fn from_random_seed() -> Result<Self, Error>;
fn from_random_seed(use_switch_commitments: bool) -> Result<Self, Error>;
fn root_key_id() -> Identifier;
fn derive_key_id(depth: u8, d1: u32, d2: u32, d3: u32, d4: u32) -> Identifier;
fn derive_key(&self, id: &Identifier) -> Result<ExtendedPrivKey, Error>;
fn derive_key(&self, amount: u64, id: &Identifier) -> Result<SecretKey, Error>;
fn commit(&self, amount: u64, id: &Identifier) -> Result<Commitment, Error>;
fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error>;
fn sign(&self, msg: &Message, id: &Identifier) -> Result<Signature, Error>;
fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result<Signature, Error>;
fn sign_with_blinding(&self, _: &Message, _: &BlindingFactor) -> Result<Signature, Error>;
fn secp(&self) -> &Secp256k1;
fn use_switch_commitments(&self) -> Option<bool>;
}
#[cfg(test)]

View file

@ -170,7 +170,7 @@ fn build_block(
///
fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, BlockFees), Error> {
warn!("Burning block fees: {:?}", block_fees);
let keychain = ExtKeychain::from_random_seed().unwrap();
let keychain = ExtKeychain::from_random_seed(true).unwrap();
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, block_fees.height)

View file

@ -140,8 +140,7 @@ where
fn open_with_credentials(&mut self) -> Result<(), Error> {
let wallet_seed = WalletSeed::from_file(&self.config, &self.passphrase)
.context(ErrorKind::CallbackImpl("Error opening wallet"))?;
let keychain = wallet_seed.derive_keychain();
self.keychain = Some(keychain.context(ErrorKind::CallbackImpl("Error deriving keychain"))?);
self.keychain = Some(wallet_seed.derive_keychain(self.config.use_switch_commitments).context(ErrorKind::CallbackImpl("Error deriving keychain"))?);
Ok(())
}

View file

@ -50,13 +50,15 @@ pub struct WalletConfig {
pub check_node_api_http_addr: String,
// The directory in which wallet files are stored
pub data_file_dir: String,
/// TLS ceritificate file
/// TLS certificate file
pub tls_certificate_file: Option<String>,
/// TLS ceritificate private key file
/// TLS certificate private key file
pub tls_certificate_key: Option<String>,
/// Whether to use the black background color scheme for command line
/// if enabled, wallet command output color will be suitable for black background terminal
pub dark_background_color_scheme: Option<bool>,
/// Whether we want to use switch commitments for this wallet
pub use_switch_commitments: bool
}
impl Default for WalletConfig {
@ -72,6 +74,7 @@ impl Default for WalletConfig {
tls_certificate_file: None,
tls_certificate_key: None,
dark_background_color_scheme: Some(true),
use_switch_commitments: false // TODO: possibly change to true when we want it on by default
}
}
}
@ -121,8 +124,8 @@ impl WalletSeed {
seed.as_bytes().to_vec()
}
pub fn derive_keychain<K: Keychain>(&self) -> Result<K, Error> {
let result = K::from_seed(&self.0)?;
pub fn derive_keychain<K: Keychain>(&self, use_switch_commitments: bool) -> Result<K, Error> {
let result = K::from_seed(&self.0, use_switch_commitments)?;
Ok(result)
}

View file

@ -32,14 +32,16 @@ fn kernel_sig_msg() -> secp::Message {
#[test]
fn aggsig_sender_receiver_interaction() {
let sender_keychain = ExtKeychain::from_random_seed().unwrap();
sender_keychain.set_use_switch_commitments(false);
let receiver_keychain = ExtKeychain::from_random_seed().unwrap();
receiver_keychain.set_use_switch_commitments(false);
// Calculate the kernel excess here for convenience.
// Normally this would happen during transaction building.
let kernel_excess = {
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey1 = sender_keychain.derive_key(&id1).unwrap().secret_key;
let skey2 = receiver_keychain.derive_key(&id1).unwrap().secret_key;
let skey1 = sender_keychain.derive_key(0, &id1).unwrap().secret_key;
let skey2 = receiver_keychain.derive_key(0, &id1).unwrap().secret_key;
let keychain = ExtKeychain::from_random_seed().unwrap();
let blinding_factor = keychain
@ -62,7 +64,7 @@ fn aggsig_sender_receiver_interaction() {
let (sender_pub_excess, _sender_pub_nonce) = {
let keychain = sender_keychain.clone();
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey = keychain.derive_key(&id1).unwrap().secret_key;
let skey = keychain.derive_key(0, &id1).unwrap().secret_key;
// dealing with an input here so we need to negate the blinding_factor
// rather than use it as is
@ -85,7 +87,7 @@ fn aggsig_sender_receiver_interaction() {
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
// let blind = blind_sum.secret_key(&keychain.secp())?;
let blind = keychain.derive_key(&key_id).unwrap().secret_key;
let blind = keychain.derive_key(0, &key_id).unwrap().secret_key;
rx_cx = Context::new(&keychain.secp(), blind);
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
@ -236,7 +238,9 @@ fn aggsig_sender_receiver_interaction() {
#[test]
fn aggsig_sender_receiver_interaction_offset() {
let sender_keychain = ExtKeychain::from_random_seed().unwrap();
sender_keychain.set_use_switch_commitments(false);
let receiver_keychain = ExtKeychain::from_random_seed().unwrap();
receiver_keychain.set_use_switch_commitments(false);
// This is the kernel offset that we use to split the key
// Summing these at the block level prevents the
@ -247,8 +251,8 @@ fn aggsig_sender_receiver_interaction_offset() {
// Normally this would happen during transaction building.
let kernel_excess = {
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey1 = sender_keychain.derive_key(&id1).unwrap().secret_key;
let skey2 = receiver_keychain.derive_key(&id1).unwrap().secret_key;
let skey1 = sender_keychain.derive_key(0, &id1).unwrap().secret_key;
let skey2 = receiver_keychain.derive_key(0, &id1).unwrap().secret_key;
let keychain = ExtKeychain::from_random_seed().unwrap();
let blinding_factor = keychain
@ -274,7 +278,7 @@ fn aggsig_sender_receiver_interaction_offset() {
let (sender_pub_excess, _sender_pub_nonce) = {
let keychain = sender_keychain.clone();
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let skey = keychain.derive_key(&id1).unwrap().secret_key;
let skey = keychain.derive_key(0, &id1).unwrap().secret_key;
// dealing with an input here so we need to negate the blinding_factor
// rather than use it as is
@ -301,7 +305,7 @@ fn aggsig_sender_receiver_interaction_offset() {
let keychain = receiver_keychain.clone();
let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
let blind = keychain.derive_key(&key_id).unwrap().secret_key;
let blind = keychain.derive_key(0, &key_id).unwrap().secret_key;
rx_cx = Context::new(&keychain.secp(), blind);
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());