Skip to content

Commit

Permalink
feat: add Dbc::key_image() for checking if spent
Browse files Browse the repository at this point in the history
Some changes to support mint-repl and general cleanup:

* add KeyImage::to_bytes()
* normalize key fn names to *_secret_key() and *_public_key()
* add Dbc::key_image() fn, needed to check if spent
* add Dbc::amount_secrets() convenience fn
* add Dbc key getter convenience fns.
* remove DbcContent::derive_owner()
* add DbcContent::derivation_index()
* add fns to get blst format keys from DerivedOwner
* fix wrong assertion in SpentBookMock::log_spent()
* simplify tests a bit using new key convenience fns.
  • Loading branch information
dan-da authored and dirvine committed Feb 17, 2022
1 parent ff32395 commit f72fe8f
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 103 deletions.
14 changes: 10 additions & 4 deletions src/blst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub type BlindingFactor = Scalar;
#[derive(Debug, Clone, Default)]
pub struct KeyImage(G1Affine);

impl KeyImage {
pub fn to_bytes(&self) -> [u8; 48] {
self.0.to_compressed()
}
}

impl PartialEq for KeyImage {
fn eq(&self, other: &Self) -> bool {
self.0.to_compressed() == other.0.to_compressed()
Expand Down Expand Up @@ -86,24 +92,24 @@ pub struct BlsHelper {}

impl BlsHelper {
#[allow(dead_code)]
pub fn blsttc_to_blstrs_sk(sk: SecretKey) -> SecretKeyBlst {
pub fn blsttc_to_blstrs_secret_key(sk: SecretKey) -> SecretKeyBlst {
let bytes = sk.to_bytes();
SecretKeyBlst::from_bytes_be(&bytes).unwrap()
}

pub fn blsttc_to_blstrs_pubkey(pk: &PublicKey) -> PublicKeyBlst {
pub fn blsttc_to_blstrs_public_key(pk: &PublicKey) -> PublicKeyBlst {
let bytes = pk.to_bytes();
// fixme: unwrap
PublicKeyBlst::from_compressed(&bytes).unwrap()
}

pub fn blstrs_to_blsttc_pubkey(pk: &PublicKeyBlst) -> PublicKey {
pub fn blstrs_to_blsttc_public_key(pk: &PublicKeyBlst) -> PublicKey {
let bytes = pk.to_compressed();
// fixme: unwrap
PublicKey::from_bytes(bytes).unwrap()
}

pub fn blstrs_to_blsttc_sk(sk: SecretKeyBlst) -> SecretKey {
pub fn blstrs_to_blsttc_secret_key(sk: SecretKeyBlst) -> SecretKey {
let bytes = sk.to_bytes_be();
// fixme: unwrap
SecretKey::from_bytes(bytes).unwrap()
Expand Down
125 changes: 93 additions & 32 deletions src/dbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use crate::{AmountSecrets, BlsHelper, KeyImage, PublicKeyBlst, SpentProof, TransactionValidator};
use crate::{DbcContent, Error, KeyManager, Result};
use crate::{
AmountSecrets, KeyImage, PublicKeyBlst, SecretKeyBlst, SpentProof, TransactionValidator,
};
use crate::{DbcContent, DerivationIndex, DerivedOwner, Error, KeyManager, Owner, Result};
use blst_ringct::ringct::{OutputProof, RingCtTransaction};
use blst_ringct::RevealedCommitment;
use blstrs::group::Curve;
use blstrs::G1Projective;
use blsttc::{PublicKey, SecretKey, Signature};
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryFrom;
use tiny_keccak::{Hasher, Sha3};

#[cfg(feature = "serde")]
Expand All @@ -28,20 +32,87 @@ pub struct Dbc {
}

impl Dbc {
/// Read the DBC owner
pub fn owner(&self, base_sk: &SecretKey) -> Result<PublicKey> {
self.content.derive_owner(base_sk)
/// returns owner base from which one-time-use keypair is derived.
pub fn owner_base(&self) -> &Owner {
&self.content.owner_base
}

// convenience fn, can go away once blsttc integrated with blst_ringct.
pub(crate) fn owner_blst(&self, base_sk: &SecretKey) -> Result<PublicKeyBlst> {
Ok(BlsHelper::blsttc_to_blstrs_pubkey(&self.owner(base_sk)?))
/// returns derived_owner
pub fn derived_owner(&self, base_sk: &SecretKey) -> Result<DerivedOwner> {
Ok(DerivedOwner {
owner_base: self.owner_base().clone(),
derivation_index: self.derivation_index(base_sk)?,
})
}

/// returns derivation index used to derive one-time-use keypair from owner base
pub fn derivation_index(&self, base_sk: &SecretKey) -> Result<DerivationIndex> {
self.content.derivation_index(base_sk)
}

/// returns owner base SecretKey if available.
pub fn owner_base_secret_key(&self) -> Result<SecretKey> {
self.content.owner_base.secret_key()
}

/// returbs owner base PublicKey.
pub fn owner_base_public_key(&self) -> PublicKey {
self.content.owner_base.public_key()
}

/// returns owner SecretKey derived from supplied owner base SecretKey
pub fn owner_secret_key(&self, base_sk: &SecretKey) -> Result<SecretKey> {
Ok(base_sk.derive_child(&self.derivation_index(base_sk)?))
}

/// returns owner PublicKey derived from owner base PublicKey
pub fn owner_public_key(&self, base_sk: &SecretKey) -> Result<PublicKey> {
Ok(self
.content
.owner_base
.derive(&self.derivation_index(base_sk)?)
.public_key())
}

/// returns owner BLST SecretKey derived from owner base SecrtKey, if available.
// note: can go away once blsttc integrated with blst_ringct.
pub fn owner_secret_key_blst(&self, base_sk: &SecretKey) -> Result<SecretKeyBlst> {
self.derived_owner(base_sk)?.derive_secret_key_blst()
}

/// returns owner BLST PublicKey derived from owner base PublicKey
// note: can go away once blsttc integrated with blst_ringct.
pub fn owner_public_key_blst(&self, base_sk: &SecretKey) -> Result<PublicKeyBlst> {
Ok(self.derived_owner(base_sk)?.derive_public_key_blst())
}

/// returns true if owner base includes a SecretKey.
///
/// If the SecretKey is present, this Dbc can be spent by anyone in
/// possession of it, making it a true "Bearer" instrument.
///
/// If the SecretKey is not present, then only the person(s) holding
/// the SecretKey matching the PublicKey can spend it.
pub fn has_secret_key(&self) -> bool {
self.content.owner_base.has_secret_key()
}

/// decypts and returns the AmountSecrets
pub fn amount_secrets(&self, base_sk: &SecretKey) -> Result<AmountSecrets> {
let sk = self.owner_secret_key(base_sk)?;
AmountSecrets::try_from((&sk, &self.content.amount_secrets_cipher))
}

/// returns KeyImage for the owner's derived public key
/// This is useful for checking if a Dbc has been spent.
pub fn key_image(&self, base_sk: &SecretKey) -> Result<KeyImage> {
let public_key: G1Projective = self.owner_public_key_blst(base_sk)?.into();
let secret_key = self.owner_secret_key_blst(base_sk)?;
Ok((blst_ringct::hash_to_curve(public_key) * secret_key)
.to_affine()
.into())
}

/// Generate hash of this DBC
pub fn hash(&self) -> [u8; 32] {
let mut sha3 = Sha3::v256();
Expand All @@ -50,7 +121,7 @@ impl Dbc {
sha3.update(&self.transaction.hash());

for (in_key, (mint_key, mint_sig)) in self.transaction_sigs.iter() {
sha3.update(&in_key.as_ref().to_compressed());
sha3.update(&in_key.to_bytes());
sha3.update(&mint_key.to_bytes());
sha3.update(&mint_sig.to_bytes());
}
Expand All @@ -65,7 +136,7 @@ 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: 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>(
Expand All @@ -80,7 +151,7 @@ impl Dbc {
&self.spent_proofs,
)?;

let owner = self.owner_blst(base_sk)?;
let owner = self.owner_public_key_blst(base_sk)?;

if !self
.transaction
Expand Down Expand Up @@ -135,7 +206,7 @@ impl Dbc {
}

fn my_output_proof(&self, base_sk: &SecretKey) -> Result<&OutputProof> {
let owner = self.owner_blst(base_sk)?;
let owner = self.owner_public_key_blst(base_sk)?;
self.transaction
.outputs
.iter()
Expand All @@ -154,12 +225,11 @@ mod tests {

use crate::tests::{init_genesis, NonZeroTinyInt, SpentBookMock, TinyInt};
use crate::{
Amount, AmountSecrets, BlsHelper, DbcBuilder, DerivedOwner, Hash, KeyManager, Owner,
ReissueRequest, ReissueRequestBuilder, SecretKeyBlst, SimpleKeyManager, SimpleSigner,
Amount, AmountSecrets, DbcBuilder, DerivedOwner, Hash, KeyManager, Owner, ReissueRequest,
ReissueRequestBuilder, SecretKeyBlst, SimpleKeyManager, SimpleSigner,
};
use blst_ringct::ringct::RingCtMaterial;
use blst_ringct::{Output, RevealedCommitment};
use blsttc::SecretKey;
use rand::SeedableRng;
use rand_core::RngCore;
use rand_core::SeedableRng as SeedableRngCore;
Expand All @@ -175,7 +245,7 @@ mod tests {
}

fn prepare_even_split(
dbc_owner: SecretKey,
dbc_owner_blst: SecretKeyBlst,
amount_secrets: AmountSecrets,
n_ways: u8,
output_owners: Vec<DerivedOwner>,
Expand All @@ -192,20 +262,13 @@ mod tests {

let (reissue_tx, revealed_commitments, _material, output_owners) =
crate::TransactionBuilder::default()
.add_input_by_secrets(
BlsHelper::blsttc_to_blstrs_sk(dbc_owner),
amount_secrets,
decoy_inputs,
&mut rng8,
)
.add_input_by_secrets(dbc_owner_blst, amount_secrets, decoy_inputs, &mut rng8)
.add_outputs(divide(amount, n_ways).zip(output_owners.into_iter()).map(
|(amount, derived_owner)| {
(
Output {
amount,
public_key: BlsHelper::blsttc_to_blstrs_pubkey(
&derived_owner.derive_public_key(),
),
public_key: derived_owner.derive_public_key_blst(),
},
derived_owner,
)
Expand Down Expand Up @@ -235,7 +298,7 @@ mod tests {
let ringct_material = RingCtMaterial {
inputs: vec![],
outputs: vec![Output {
public_key: BlsHelper::blsttc_to_blstrs_pubkey(&derived_owner.derive_public_key()),
public_key: derived_owner.derive_public_key_blst(),
amount,
}],
};
Expand Down Expand Up @@ -295,7 +358,7 @@ mod tests {
.collect();

let (reissue_request, revealed_commitments, output_owners) = prepare_even_split(
BlsHelper::blstrs_to_blsttc_sk(genesis.secret_key),
genesis.secret_key,
genesis.amount_secrets,
n_inputs.coerce(),
input_owners,
Expand All @@ -321,7 +384,7 @@ mod tests {
.map(|(_dbc, derived_owner, amount_secrets)| {
let decoy_inputs = vec![]; // todo
(
BlsHelper::blsttc_to_blstrs_sk(derived_owner.derive_secret_key().unwrap()),
derived_owner.derive_secret_key_blst().unwrap(),
amount_secrets,
decoy_inputs,
)
Expand All @@ -337,9 +400,7 @@ mod tests {
.add_output(
Output {
amount,
public_key: BlsHelper::blsttc_to_blstrs_pubkey(
&derived_owner.derive_public_key(),
),
public_key: derived_owner.derive_public_key_blst(),
},
derived_owner.clone(),
)
Expand Down Expand Up @@ -457,7 +518,7 @@ mod tests {
let key_manager = mint_node.key_manager();
let validation_res = dbc.confirm_valid(&derived_owner.base_secret_key()?, key_manager);

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

// Check if commitment in AmountSecrets matches the commitment in tx OutputProof
let commitments_match = dbc
Expand Down
6 changes: 3 additions & 3 deletions src/dbc_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// permissions and limitations relating to use of the SAFE Network Software.

use crate::{AmountSecrets, DerivationIndex, Owner};
use blsttc::{Ciphertext, PublicKey, SecretKey};
use blsttc::{Ciphertext, SecretKey};
use tiny_keccak::{Hasher, Sha3};

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -89,7 +89,7 @@ impl From<(Owner, DerivationIndex, AmountSecrets)> for DbcContent {
}

impl DbcContent {
pub fn derive_owner(&self, base_sk: &SecretKey) -> Result<PublicKey> {
pub(crate) fn derivation_index(&self, base_sk: &SecretKey) -> Result<DerivationIndex> {
let bytes = base_sk
.decrypt(&self.owner_derivation_cipher)
.ok_or(Error::DecryptionBySecretKeyFailed)?;
Expand All @@ -98,7 +98,7 @@ impl DbcContent {

let mut idx = [0u8; 32];
idx.copy_from_slice(&bytes[0..32]);
Ok(self.owner_base.derive(&idx).public_key())
Ok(idx)
}

pub fn to_bytes(&self) -> Vec<u8> {
Expand Down
16 changes: 15 additions & 1 deletion src/derived_owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use crate::{Error, PublicKey, Result};
use crate::{BlsHelper, Error, PublicKey, PublicKeyBlst, Result, SecretKeyBlst};
use blsttc::{serde_impl::SerdeSecret, SecretKey};
use std::fmt;

Expand Down Expand Up @@ -132,6 +132,20 @@ impl DerivedOwner {
self.owner_base.derive(&self.derivation_index).secret_key()
}

/// returns owner BLST PublicKey derived from owner base PublicKey
// note: can go away once blsttc integrated with blst_ringct.
pub fn derive_public_key_blst(&self) -> PublicKeyBlst {
BlsHelper::blsttc_to_blstrs_public_key(&self.derive_public_key())
}

/// returns owner BLST SecretKey derived from owner base SecretKey, if available.
// note: can go away once blsttc integrated with blst_ringct.
pub fn derive_secret_key_blst(&self) -> Result<SecretKeyBlst> {
Ok(BlsHelper::blsttc_to_blstrs_secret_key(
self.derive_secret_key()?,
))
}

pub fn from_owner_base(owner_base: Owner, mut rng: impl rand8::RngCore) -> Self {
Self {
owner_base,
Expand Down
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,14 @@ mod tests {
.find(|proof| proof.public_key() == pk)
})
.collect();
assert_eq!(output_proofs.len(), 1);

// note: all inputs to a tx will store the same Tx. As such,
// we will can get multiple matches. But they should/must
// be from the same Tx. So we use only the first one.
// A better impl would store only a single Tx with multiple
// KeyImage pointers to it.

assert!(!output_proofs.is_empty());
output_proofs[0].commitment()
})
.collect();
Expand Down
Loading

0 comments on commit f72fe8f

Please sign in to comment.