diff --git a/config/src/comments.rs b/config/src/comments.rs index 305dff9ec..b8a62bb46 100644 --- a/config/src/comments.rs +++ b/config/src/comments.rs @@ -403,6 +403,13 @@ fn comments() -> HashMap { "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(), ); diff --git a/core/src/libtx/aggsig.rs b/core/src/libtx/aggsig.rs index 6815e8348..50e213710 100644 --- a/core/src/libtx/aggsig.rs +++ b/core/src/libtx/aggsig.rs @@ -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( secp: &Secp256k1, k: &K, msg: &Message, + value: u64, key_id: &Identifier, blind_sum: Option<&PublicKey>, ) -> Result 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 = diff --git a/core/src/libtx/build.rs b/core/src/libtx/build.rs index 267010d85..d4e75d710 100644 --- a/core/src/libtx/build.rs +++ b/core/src/libtx/build.rs @@ -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)), ) }, ) diff --git a/core/src/libtx/proof.rs b/core/src/libtx/proof.rs index 28142607e..aa32f4983 100644 --- a/core/src/libtx/proof.rs +++ b/core/src/libtx/proof.rs @@ -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 diff --git a/core/src/libtx/reward.rs b/core/src/libtx/reward.rs index 094696abb..e114e4272 100644 --- a/core/src/libtx/reward.rs +++ b/core/src/libtx/reward.rs @@ -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, diff --git a/keychain/src/keychain.rs b/keychain/src/keychain.rs index c5ea067e9..a99ef88d7 100644 --- a/keychain/src/keychain.rs +++ b/keychain/src/keychain.rs @@ -29,16 +29,18 @@ use crate::util::secp::{self, Message, Secp256k1, Signature}; pub struct ExtKeychain { secp: Secp256k1, master: ExtendedPrivKey, + use_switch_commitments: Option } impl Keychain for ExtKeychain { - fn from_seed(seed: &[u8]) -> Result { + fn from_seed(seed: &[u8], use_switch_commitments: bool) -> Result { 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 { + fn from_random_seed(use_switch_commitments: bool) -> Result { 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 { + fn derive_key(&self, amount: u64, id: &Identifier) -> Result { 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 { - 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 { - 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 { + 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 { + 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, diff --git a/keychain/src/types.rs b/keychain/src/types.rs index d23fbc409..5081e063d 100644 --- a/keychain/src/types.rs +++ b/keychain/src/types.rs @@ -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 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, - pub negative_key_ids: Vec, + pub positive_key_ids: Vec, + pub negative_key_ids: Vec, pub positive_blinding_factors: Vec, pub negative_blinding_factors: Vec, } @@ -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; + fn from_seed(seed: &[u8], use_switch_commitments: bool) -> Result; fn from_mnemonic(word_list: &str, extension_word: &str) -> Result; - fn from_random_seed() -> Result; + fn from_random_seed(use_switch_commitments: bool) -> Result; 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; + fn derive_key(&self, amount: u64, id: &Identifier) -> Result; fn commit(&self, amount: u64, id: &Identifier) -> Result; fn blind_sum(&self, blind_sum: &BlindSum) -> Result; - fn sign(&self, msg: &Message, id: &Identifier) -> Result; + fn sign(&self, msg: &Message, amount: u64, id: &Identifier) -> Result; fn sign_with_blinding(&self, _: &Message, _: &BlindingFactor) -> Result; fn secp(&self) -> &Secp256k1; + fn use_switch_commitments(&self) -> Option; } #[cfg(test)] diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index 3c4ccf115..28b84be2b 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -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) diff --git a/wallet/src/lmdb_wallet.rs b/wallet/src/lmdb_wallet.rs index acadf3651..99ad35bf9 100644 --- a/wallet/src/lmdb_wallet.rs +++ b/wallet/src/lmdb_wallet.rs @@ -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(()) } diff --git a/wallet/src/types.rs b/wallet/src/types.rs index 01678b308..2055ea7b0 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -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, - /// TLS ceritificate private key file + /// TLS certificate private key file pub tls_certificate_key: Option, /// 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, + /// 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(&self) -> Result { - let result = K::from_seed(&self.0)?; + pub fn derive_keychain(&self, use_switch_commitments: bool) -> Result { + let result = K::from_seed(&self.0, use_switch_commitments)?; Ok(result) } diff --git a/wallet/tests/libwallet.rs b/wallet/tests/libwallet.rs index 0fb73e6f1..fcb942f21 100644 --- a/wallet/tests/libwallet.rs +++ b/wallet/tests/libwallet.rs @@ -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());