Skip to content

Commit

Permalink
feat: add spentbook pubkey to mint's key_manager
Browse files Browse the repository at this point in the history
* add MintNode::trust_spentbook_public_key() to make mint key_manager
  aware of spentbook pubkey
* remove spentbook verifier param from Dbc::confirm_valid()
* add to trait KeyManager fn add_known_key()
* make MintNode::reissue() accept &self instead of &mut self
* make MintNode::issue_genesis_dbc() return self (builder pattern)
* update tests to use MintNode builder pattern and trust spentbook
  pubkey.
* add input_key_image to GenesisDbcShare
* remove genesis_dbc_input static fn
* remove (ugly/temporary) SpentProof::validate_unsafe()
  • Loading branch information
dan-da authored and dirvine committed Feb 17, 2022
1 parent 36dbfbb commit c2bce8b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 111 deletions.
43 changes: 15 additions & 28 deletions src/dbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,12 @@ impl Dbc {

// Check there exists a Transaction with the output containing this Dbc
// todo: refactor so that common validation logic is shared by MintNode::reissue() and Dbc::confirm_valid()
//
// note: for spent_proofs to validate, the mint_verifier must have/know the spentbook section's public key.
pub fn confirm_valid<K: KeyManager>(
&self,
base_sk: &SecretKey,
mint_verifier: &K,
spentbook_verifier: &K,
) -> Result<(), Error> {
let tx_hash = Hash::from(self.transaction.hash());

Expand Down Expand Up @@ -97,7 +98,7 @@ impl Dbc {
{
return Err(Error::UnknownInput);
}
spent_proof.validate(tx_hash, spentbook_verifier)?;
spent_proof.validate(tx_hash, mint_verifier)?;
}

let owner = self.owner(base_sk)?;
Expand Down Expand Up @@ -267,17 +268,10 @@ mod tests {
};

let id = crate::bls_dkg_id(&mut rng);
let key_manager = SimpleKeyManager::new(SimpleSigner::from(id));

let spentbook_owner = crate::bls_dkg_id(&mut rng);
let spentbook_key_manager = SimpleKeyManager::new(SimpleSigner::from(spentbook_owner));
let mint_key_manager = SimpleKeyManager::new(SimpleSigner::from(id));

assert!(matches!(
dbc.confirm_valid(
&derived_owner.base_secret_key()?,
&key_manager,
&spentbook_key_manager
),
dbc.confirm_valid(&derived_owner.base_secret_key()?, &mint_key_manager,),
Err(Error::TransactionMustHaveAnInput)
));

Expand All @@ -298,23 +292,20 @@ mod tests {
let mut rng = rand::rngs::StdRng::from_seed([0u8; 32]);

let amount = 100;
let mint_owner = crate::bls_dkg_id(&mut rng);

let key_manager = SimpleKeyManager::new(SimpleSigner::from(mint_owner));
let mut mint_node = MintNode::new(key_manager);
let spentbook_key_manager =
SimpleKeyManager::new(SimpleSigner::from(crate::bls_dkg_id(&mut rng)));

let spentbook_owner = crate::bls_dkg_id(&mut rng);
let spentbook_key_manager = SimpleKeyManager::new(SimpleSigner::from(spentbook_owner));
let (mint_node, genesis) = MintNode::new(SimpleKeyManager::new(SimpleSigner::from(
crate::bls_dkg_id(&mut rng),
)))
.trust_spentbook_public_key(spentbook_key_manager.public_key_set()?.public_key())?
.issue_genesis_dbc(amount, &mut rng8)?;

let genesis = mint_node.issue_genesis_dbc(amount, &mut rng8)?;
let genesis_input_key_image = genesis.ringct_material.inputs[0]
.true_input
.key_image()
.to_compressed();
let mut spentbook = SpentBookMock::from((spentbook_key_manager, genesis_input_key_image));
let mut spentbook = SpentBookMock::from((spentbook_key_manager, genesis.input_key_image));

let _genesis_spent_proof_share =
spentbook.log_spent(genesis_input_key_image, genesis.transaction.clone())?;
spentbook.log_spent(genesis.input_key_image, genesis.transaction.clone())?;

let input_owners: Vec<DerivedOwner> = (0..=n_inputs.coerce())
.map(|_| {
Expand Down Expand Up @@ -491,11 +482,7 @@ mod tests {
};

let key_manager = mint_node.key_manager();
let validation_res = dbc.confirm_valid(
&derived_owner.base_secret_key()?,
key_manager,
&spentbook.key_manager,
);
let validation_res = dbc.confirm_valid(&derived_owner.base_secret_key()?, key_manager);

let dbc_owner = dbc.owner(&derived_owner.base_secret_key()?)?;

Expand Down
6 changes: 6 additions & 0 deletions src/key_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl NodeSignature {

pub trait KeyManager {
type Error: std::error::Error;
fn add_known_key(&mut self, key: PublicKey) -> Result<(), Self::Error>;
fn sign_with_child_key(&self, idx: &[u8], tx_hash: &Hash)
-> Result<NodeSignature, Self::Error>;
fn sign(&self, msg_hash: &Hash) -> Result<NodeSignature, Self::Error>;
Expand Down Expand Up @@ -106,6 +107,11 @@ impl SimpleKeyManager {
impl KeyManager for SimpleKeyManager {
type Error = crate::Error;

fn add_known_key(&mut self, key: PublicKey) -> Result<()> {
self.cache.add_known_key(key);
Ok(())
}

fn public_key_set(&self) -> Result<PublicKeySet> {
Ok(self.signer.public_key_set())
}
Expand Down
5 changes: 1 addition & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ pub use crate::{
KeyManager, NodeSignature, PublicKey, PublicKeySet, Signature, SimpleKeyManager,
SimpleSigner,
},
mint::{
genesis_dbc_input, GenesisDbcShare, MintNode, MintNodeSignatures, ReissueRequest,
ReissueShare,
},
mint::{GenesisDbcShare, MintNode, MintNodeSignatures, ReissueRequest, ReissueShare},
spent_proof::{SpentProof, SpentProofShare},
};

Expand Down
137 changes: 67 additions & 70 deletions src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

use crate::{
Amount, AmountSecrets, DbcContent, DerivedOwner, Error, Hash, KeyImage, KeyManager,
NodeSignature, OwnerBase, PublicKeySet, Result, SpentProof, SpentProofShare,
NodeSignature, OwnerBase, PublicKey, PublicKeySet, Result, SpentProof, SpentProofShare,
};
use blst_ringct::mlsag::{MlsagMaterial, TrueInput};
use blst_ringct::ringct::{RingCtMaterial, RingCtTransaction};
Expand All @@ -36,18 +36,6 @@ use std::iter::FromIterator;
pub type MintNodeSignature = (PublicKeySet, NodeSignature);
pub type MintNodeSignatures = BTreeMap<KeyImage, MintNodeSignature>;

pub fn genesis_dbc_input(share: &GenesisDbcShare) -> Result<KeyImage> {
Ok(share
.ringct_material
.inputs
.get(0)
.ok_or(Error::TransactionMustHaveAnInput)?
.true_input
.key_image()
.to_affine()
.to_compressed())
}

#[derive(Clone)]
pub struct GenesisDbcShare {
pub ringct_material: RingCtMaterial,
Expand All @@ -59,6 +47,7 @@ pub struct GenesisDbcShare {
pub public_key_set: PublicKeySet,
pub transaction_sig: NodeSignature,
pub secret_key: Scalar,
pub input_key_image: KeyImage,
}

// #[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize)]
Expand Down Expand Up @@ -89,11 +78,18 @@ impl<K: KeyManager> MintNode<K> {
Self { key_manager }
}

pub fn trust_spentbook_public_key(mut self, public_key: PublicKey) -> Result<Self> {
self.key_manager
.add_known_key(public_key)
.map_err(|e| Error::Signing(e.to_string()))?;
Ok(self)
}

pub fn issue_genesis_dbc(
&mut self,
self,
amount: Amount,
mut rng: impl RngCore + rand_core::CryptoRng,
) -> Result<GenesisDbcShare> {
) -> Result<(Self, GenesisDbcShare)> {
// Make a secret key for the input to Genesis Tx.
let input_poly = Poly::zero();
let input_secret_key_set = SecretKeySet::from(input_poly);
Expand Down Expand Up @@ -123,6 +119,8 @@ impl<K: KeyManager> MintNode<K> {
},
};

let input_key_image = true_input.key_image().to_compressed();

// note: no decoy inputs because no other DBCs exist prior to genesis DBC.
let decoy_inputs = vec![];

Expand All @@ -149,17 +147,21 @@ impl<K: KeyManager> MintNode<K> {
.sign(&Hash::from(transaction.hash()))
.map_err(|e| Error::Signing(e.to_string()))?;

Ok(GenesisDbcShare {
ringct_material,
dbc_content,
amount_secrets,
public_key_set,
derived_owner,
transaction,
revealed_commitments, // output commitments
transaction_sig,
secret_key,
})
Ok((
self,
GenesisDbcShare {
ringct_material,
dbc_content,
amount_secrets,
public_key_set,
derived_owner,
transaction,
revealed_commitments, // output commitments
transaction_sig,
secret_key,
input_key_image,
},
))
}

pub fn key_manager(&self) -> &K {
Expand Down Expand Up @@ -195,7 +197,7 @@ impl<K: KeyManager> MintNode<K> {
// 6. create SpentProofShare and return it.
}

pub fn reissue(&mut self, reissue_req: ReissueRequest) -> Result<ReissueShare> {
pub fn reissue(&self, reissue_req: ReissueRequest) -> Result<ReissueShare> {
let ReissueRequest {
transaction,
spent_proofs,
Expand Down Expand Up @@ -260,11 +262,12 @@ impl<K: KeyManager> MintNode<K> {

// Validate that each input has not yet been spent.
// iterate over mlsags. each has key_image()
//
// note: for the proofs to validate, our key_manager must have/know
// the pubkey of the spentbook section that signed the proof.
// This is a responsibility of our caller, not this crate.
for proof in spent_proofs_sorted.iter() {
// proof.validate(transaction_hash, self.key_manager())?;

// fixme: this does not validate that signing key belongs to spentbook.
proof.validate_unsafe(transaction_hash)?;
proof.validate(transaction_hash, self.key_manager())?;
}

let transaction_sigs = self.sign_transaction(&transaction)?;
Expand Down Expand Up @@ -318,23 +321,24 @@ mod tests {
let mut rng8 = rand8::rngs::StdRng::from_seed([0u8; 32]);
let mut rng = rand::rngs::StdRng::from_seed([0u8; 32]);

let mint_owner = crate::bls_dkg_id(&mut rng);
let key_manager = SimpleKeyManager::new(SimpleSigner::from(mint_owner));
let mut mint_node = MintNode::new(key_manager);
let genesis = mint_node.issue_genesis_dbc(1000, &mut rng8).unwrap();
let spentbook_key_manager =
SimpleKeyManager::new(SimpleSigner::from(crate::bls_dkg_id(&mut rng)));

let spentbook_owner = crate::bls_dkg_id(&mut rng);
let spentbook_key_manager = SimpleKeyManager::new(SimpleSigner::from(spentbook_owner));
let input_key_image = genesis_dbc_input(&genesis)?;
let mut spentbook = SpentBookMock::from((spentbook_key_manager, input_key_image));
let (mint_node, genesis) = MintNode::new(SimpleKeyManager::new(SimpleSigner::from(
crate::bls_dkg_id(&mut rng),
)))
.trust_spentbook_public_key(spentbook_key_manager.public_key_set()?.public_key())?
.issue_genesis_dbc(1000, &mut rng8)?;

let mut spentbook = SpentBookMock::from((spentbook_key_manager, genesis.input_key_image));

let mint_sig = mint_node
.key_manager()
.public_key_set()?
.combine_signatures(vec![genesis.transaction_sig.threshold_crypto()])?;

let spent_proof_share =
spentbook.log_spent(input_key_image, genesis.transaction.clone())?;
spentbook.log_spent(genesis.input_key_image, genesis.transaction.clone())?;

let spentbook_sig =
spent_proof_share
Expand All @@ -360,7 +364,7 @@ mod tests {
content: genesis.dbc_content,
transaction: genesis.transaction.clone(),
transaction_sigs: BTreeMap::from_iter([(
input_key_image,
genesis.input_key_image,
(
mint_node.key_manager().public_key_set()?.public_key(),
mint_sig,
Expand All @@ -372,7 +376,6 @@ mod tests {
let validation = genesis_dbc.confirm_valid(
&genesis.derived_owner.base_secret_key()?,
&mint_node.key_manager,
&spentbook.key_manager,
);
assert!(validation.is_ok());

Expand All @@ -389,20 +392,19 @@ mod tests {
let n_outputs = output_amounts.len();
let output_amount = output_amounts.iter().sum();

let mint_owner = crate::bls_dkg_id(&mut rng);
let key_manager = SimpleKeyManager::new(SimpleSigner::from(mint_owner));
let mut mint_node = MintNode::new(key_manager);
let genesis = mint_node
.issue_genesis_dbc(output_amount, &mut rng8)
.unwrap();
let spentbook_key_manager =
SimpleKeyManager::new(SimpleSigner::from(crate::bls_dkg_id(&mut rng)));

let spentbook_owner = crate::bls_dkg_id(&mut rng);
let spentbook_key_manager = SimpleKeyManager::new(SimpleSigner::from(spentbook_owner));
let input_key_image = genesis_dbc_input(&genesis)?;
let mut spentbook = SpentBookMock::from((spentbook_key_manager, input_key_image));
let (mint_node, genesis) = MintNode::new(SimpleKeyManager::new(SimpleSigner::from(
crate::bls_dkg_id(&mut rng),
)))
.trust_spentbook_public_key(spentbook_key_manager.public_key_set()?.public_key())?
.issue_genesis_dbc(output_amount, &mut rng8)?;

let mut spentbook = SpentBookMock::from((spentbook_key_manager, genesis.input_key_image));

let _genesis_spent_proof_share =
spentbook.log_spent(input_key_image, genesis.transaction.clone())?;
spentbook.log_spent(genesis.input_key_image, genesis.transaction.clone())?;

let owners: Vec<DerivedOwner> = (0..output_amounts.len())
.map(|_| {
Expand Down Expand Up @@ -476,7 +478,6 @@ mod tests {
.confirm_valid(
&derived_owner.base_secret_key().unwrap(),
mint_node.key_manager(),
&spentbook.key_manager
)
.is_ok());
}
Expand Down Expand Up @@ -532,20 +533,19 @@ mod tests {
// something like: genesis --> 100 outputs --> x outputs --> y outputs.
let num_decoy_inputs: usize = num_decoy_inputs.coerce::<usize>() % 2;

let mint_owner = crate::bls_dkg_id(&mut rng);
let key_manager = SimpleKeyManager::new(SimpleSigner::from(mint_owner));
let mut mint_node = MintNode::new(key_manager);
let genesis = mint_node
.issue_genesis_dbc(genesis_amount, &mut rng8)
.unwrap();
let spentbook_key_manager =
SimpleKeyManager::new(SimpleSigner::from(crate::bls_dkg_id(&mut rng)));

let (mint_node, genesis) = MintNode::new(SimpleKeyManager::new(SimpleSigner::from(
crate::bls_dkg_id(&mut rng),
)))
.trust_spentbook_public_key(spentbook_key_manager.public_key_set()?.public_key())?
.issue_genesis_dbc(genesis_amount, &mut rng8)?;

let spentbook_owner = crate::bls_dkg_id(&mut rng);
let spentbook_key_manager = SimpleKeyManager::new(SimpleSigner::from(spentbook_owner));
let input_key_image = genesis_dbc_input(&genesis)?;
let mut spentbook = SpentBookMock::from((spentbook_key_manager, input_key_image));
let mut spentbook = SpentBookMock::from((spentbook_key_manager, genesis.input_key_image));

let _genesis_spent_proof_share =
spentbook.log_spent(input_key_image, genesis.transaction.clone())?;
spentbook.log_spent(genesis.input_key_image, genesis.transaction.clone())?;

let (reissue_tx, revealed_commitments, _material, output_owners) =
crate::TransactionBuilder::default()
Expand Down Expand Up @@ -713,11 +713,8 @@ mod tests {
let output_dbcs = dbc_builder.build()?;

for (dbc, derived_owner, _amount_secrets) in output_dbcs.iter() {
let dbc_confirm_result = dbc.confirm_valid(
&derived_owner.base_secret_key()?,
&mint_node.key_manager,
&spentbook.key_manager,
);
let dbc_confirm_result = dbc
.confirm_valid(&derived_owner.base_secret_key()?, &mint_node.key_manager);
assert!(dbc_confirm_result.is_ok());
}

Expand Down
9 changes: 0 additions & 9 deletions src/spent_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,6 @@ impl SpentProof {
.map_err(|_| Error::InvalidSpentProofSignature(self.key_image))?;
Ok(())
}

// This is unsafe (and temporary) because it does not verify that
// self.spentbook_pub_key actually belongs to the spentbook.
pub(crate) fn validate_unsafe(&self, tx: Hash) -> Result<()> {
match self.spentbook_pub_key.verify(&self.spentbook_sig, &tx) {
true => Ok(()),
false => Err(Error::InvalidSpentProofSignature(self.key_image)),
}
}
}

impl PartialOrd for SpentProof {
Expand Down

0 comments on commit c2bce8b

Please sign in to comment.