Skip to content

Commit

Permalink
feat(forced-one-time-keys): dbc name is the owner
Browse files Browse the repository at this point in the history
  • Loading branch information
davidrusu committed Sep 14, 2021
1 parent 987b65e commit 0b4e9ef
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 169 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ rand = "0.7.1"
bulletproofs = "4.0.0"
curve25519-dalek-ng = "4.0.1"
merlin = "3.0.0"
blsttc = "2.3.0"
blsttc = "3.3.0"
hex = "0.4.3"

[dependencies.rand8]
package = "rand"
version = "0.8.0"

[dependencies.bls_dkg]
version = "~0.6"
version = "~0.7"
optional = true

[dependencies.tiny-keccak]
Expand Down
20 changes: 6 additions & 14 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ use std::iter::FromIterator;
use curve25519_dalek_ng::scalar::Scalar;

use crate::{
Amount, AmountSecrets, Dbc, DbcContent, Error, Hash, NodeSignature, ReissueShare,
Amount, AmountSecrets, Dbc, DbcContent, Error, Hash, NodeSignature, PublicKey, ReissueShare,
ReissueTransaction, Result,
};

///! Unblinded data for creating sn_dbc::DbcContent
pub struct Output {
pub amount: Amount,
pub owner: blsttc::PublicKey,
pub owner: PublicKey,
}

#[derive(Default)]
Expand Down Expand Up @@ -42,11 +42,8 @@ impl TransactionBuilder {
self
}

pub fn inputs_hashes(&self) -> BTreeSet<Hash> {
self.inputs
.iter()
.map(|(dbc, _)| dbc.name())
.collect::<BTreeSet<_>>()
pub fn input_owners(&self) -> BTreeSet<PublicKey> {
BTreeSet::from_iter(self.inputs.keys().map(Dbc::name))
}

pub fn inputs_amount_sum(&self) -> Amount {
Expand All @@ -57,7 +54,7 @@ impl TransactionBuilder {
self.outputs.iter().map(|o| o.amount).sum()
}

pub fn build(self) -> Result<(ReissueTransaction, HashMap<crate::Hash, blsttc::PublicKey>)> {
pub fn build(self) -> Result<ReissueTransaction> {
let parents = BTreeSet::from_iter(self.inputs.keys().map(Dbc::name));
let inputs_bf_sum = self
.inputs
Expand Down Expand Up @@ -89,13 +86,8 @@ impl TransactionBuilder {
.collect::<Result<Vec<_>>>()?;

let inputs = HashSet::from_iter(self.inputs.into_keys());
let output_owners = HashMap::from_iter(
outputs_and_owners
.iter()
.map(|(dbc_content, owner)| (dbc_content.hash(), *owner)),
);
let outputs = HashSet::from_iter(outputs_and_owners.into_iter().map(|(o, _)| o));
Ok((ReissueTransaction { inputs, outputs }, output_owners))
Ok(ReissueTransaction { inputs, outputs })
}
}

Expand Down
67 changes: 33 additions & 34 deletions src/dbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,21 @@
// 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::{
DbcContent, DbcContentHash, DbcTransaction, Error, Hash, KeyManager, PublicKey, Result,
Signature,
};
use crate::{DbcContent, DbcTransaction, Error, KeyManager, PublicKey, Result, Signature};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct Dbc {
pub content: DbcContent,
pub transaction: DbcTransaction,
pub transaction_sigs: BTreeMap<DbcContentHash, (PublicKey, Signature)>,
pub transaction_sigs: BTreeMap<PublicKey, (PublicKey, Signature)>,
}

impl Dbc {
pub fn name(&self) -> Hash {
self.content.hash()
// TODO: rename Dbc::name to Dbc::owner
pub fn name(&self) -> PublicKey {
self.content.owner
}

// Check there exists a DbcTransaction with the output containing this Dbc
Expand All @@ -43,7 +41,7 @@ impl Dbc {
Err(Error::MissingSignatureForInput)
} else if self.transaction.inputs != self.content.parents {
Err(Error::DbcContentParentsDifferentFromTransactionInputs)
} else if !self.transaction.outputs.contains(&self.content.hash()) {
} else if !self.transaction.outputs.contains(&self.content.owner) {
Err(Error::DbcContentNotPresentInTransactionOutput)
} else {
Ok(())
Expand All @@ -62,8 +60,8 @@ mod tests {

use crate::tests::{NonZeroTinyInt, TinyInt};
use crate::{
Amount, DbcBuilder, DbcHelper, KeyManager, Mint, ReissueRequest, SimpleKeyManager,
SimpleSigner, SimpleSpendBook,
Amount, DbcBuilder, DbcHelper, Hash, KeyManager, Mint, OwnerKey, ReissueRequest,
SimpleKeyManager, SimpleSigner, SimpleSpendBook,
};

fn divide(amount: Amount, n_ways: u8) -> impl Iterator<Item = Amount> {
Expand All @@ -84,7 +82,7 @@ mod tests {
) -> Result<ReissueRequest, Error> {
let amount_secrets = DbcHelper::decrypt_amount_secrets(dbc_owner, &dbc.content)?;

let (reissue_tx, _) = crate::TransactionBuilder::default()
let reissue_tx = crate::TransactionBuilder::default()
.add_input(dbc.clone(), amount_secrets)
.add_outputs(
divide(amount_secrets.amount, n_ways).map(|amount| crate::Output {
Expand All @@ -103,11 +101,9 @@ mod tests {
.combine_signatures(vec![(dbc_owner.index, &sig_share)])
.unwrap();

let dbc_owner_key = dbc_owner.public_key_set.public_key();

let request = ReissueRequest {
transaction: reissue_tx,
input_ownership_proofs: HashMap::from_iter([(dbc.name(), (dbc_owner_key, sig))]),
input_ownership_proofs: HashMap::from_iter([(dbc.name(), sig)]),
};

Ok(request)
Expand All @@ -122,13 +118,13 @@ mod tests {
DbcContent::random_blinding_factor(),
)?;

let input_content_hashes = BTreeSet::from_iter([input_content.hash()]);
let input_owners = BTreeSet::from_iter([input_content.owner]);

let dbc = Dbc {
content: input_content,
transaction: DbcTransaction {
inputs: BTreeSet::new(),
outputs: input_content_hashes,
outputs: input_owners,
},
transaction_sigs: Default::default(),
};
Expand Down Expand Up @@ -180,7 +176,7 @@ mod tests {
content: gen_dbc_content,
transaction: gen_dbc_trans,
transaction_sigs: BTreeMap::from_iter([(
crate::mint::GENESIS_DBC_INPUT,
crate::genesis_dbc_input(),
(genesis_key, genesis_sig),
)]),
};
Expand Down Expand Up @@ -211,7 +207,7 @@ mod tests {
(dbc, amount_secrets)
});

let (reissue_tx, _) = crate::TransactionBuilder::default()
let reissue_tx = crate::TransactionBuilder::default()
.add_inputs(inputs)
.add_output(crate::Output {
amount,
Expand All @@ -228,12 +224,12 @@ mod tests {
.combine_signatures(vec![(input_owner.index, &sig_share)])
.unwrap();

let input_ownership_proofs = HashMap::from_iter(reissue_tx.inputs.iter().map(|input| {
(
input.name(),
(input_owner_key_set.public_key(), sig.clone()),
)
}));
let input_ownership_proofs = HashMap::from_iter(
reissue_tx
.inputs
.iter()
.map(|input| (input.name(), sig.clone())),
);

let reissue_request = ReissueRequest {
transaction: reissue_tx,
Expand Down Expand Up @@ -268,7 +264,7 @@ mod tests {
// add some random parents
(0..n_add_random_parents.coerce())
.into_iter()
.map(|_| rand::random()),
.map(|_| rand::random::<OwnerKey>().0),
),
);

Expand All @@ -279,15 +275,16 @@ mod tests {
DbcContent::random_blinding_factor(),
)?;

let mut fuzzed_transaction_sigs: BTreeMap<Hash, (PublicKey, Signature)> = BTreeMap::new();
let mut fuzzed_transaction_sigs: BTreeMap<PublicKey, (PublicKey, Signature)> =
BTreeMap::new();

// Add valid sigs
fuzzed_transaction_sigs.extend(
reissue_share
.mint_node_signatures
.iter()
.keys()
.take(n_valid_sigs.coerce())
.map(|(in_hash, _)| (*in_hash, (genesis_key, mint_sig.clone()))),
.map(|in_owner| (*in_owner, (genesis_key, mint_sig.clone()))),
);
let mut repeating_inputs = reissue_request
.transaction
Expand All @@ -297,7 +294,7 @@ mod tests {
// skip the valid sigs so that we don't immediately overwrite them
.skip(n_valid_sigs.coerce());

// Valid mint signatures BUT signing wrong message
// Invalid mint signatures BUT signing correct message
for _ in 0..n_wrong_signer_sigs.coerce() {
if let Some(input) = repeating_inputs.next() {
let id = crate::bls_dkg_id();
Expand Down Expand Up @@ -332,7 +329,10 @@ mod tests {

// Valid mint signatures for inputs not present in the transaction
for _ in 0..n_extra_input_sigs.coerce() {
fuzzed_transaction_sigs.insert(rand::random(), (genesis_key, mint_sig.clone()));
fuzzed_transaction_sigs.insert(
rand::random::<OwnerKey>().0,
(genesis_key, mint_sig.clone()),
);
}

let dbc = Dbc {
Expand All @@ -347,10 +347,9 @@ mod tests {

let dbc_amount = DbcHelper::decrypt_amount(&input_owner, &dbc.content)?;

println!("Validation Result: {:#?}", validation_res);
match validation_res {
Ok(()) => {
assert!(dbc.transaction.outputs.contains(&dbc.content.hash()));
assert!(dbc.transaction.outputs.contains(&dbc.content.owner));
assert!(n_inputs.coerce::<u8>() > 0);
assert!(n_valid_sigs.coerce::<u8>() >= n_inputs.coerce::<u8>());
assert_eq!(dbc_amount, amount);
Expand All @@ -376,10 +375,10 @@ mod tests {
n_add_random_parents.coerce::<u8>() > 0 || n_drop_parents.coerce::<u8>() > 0
);
assert!(dbc.transaction.inputs != dbc.content.parents);
assert!(!dbc.transaction.outputs.contains(&dbc.content.hash()));
assert!(!dbc.transaction.outputs.contains(&dbc.content.owner));
}
Err(Error::DbcContentNotPresentInTransactionOutput) => {
assert!(!dbc.transaction.outputs.contains(&dbc.content.hash()));
assert!(!dbc.transaction.outputs.contains(&dbc.content.owner));
}
Err(Error::TransactionMustHaveAnInput) => {
assert_eq!(n_inputs.coerce::<u8>(), 0);
Expand Down
7 changes: 4 additions & 3 deletions src/dbc_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ impl AmountSecrets {
}
}

// TODO: delete DbcContentHash
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct DbcContent {
pub parents: BTreeSet<DbcContentHash>, // Parent DBC's, acts as a nonce
pub parents: BTreeSet<PublicKey>, // Parent DBC's, acts as a nonce
pub amount_secrets_cipher: Ciphertext,
pub commitment: CompressedRistretto,
pub range_proof_bytes: Vec<u8>, // RangeProof::to_bytes() -> (2 lg n + 9) 32-byte elements, where n is # of secret bits, or 64 in our case. Gives 21 32-byte elements.
Expand All @@ -101,7 +102,7 @@ pub struct DbcContent {
impl DbcContent {
// Create a new DbcContent for signing.
pub fn new(
parents: BTreeSet<DbcContentHash>,
parents: BTreeSet<PublicKey>,
amount: Amount,
owner: PublicKey,
blinding_factor: Scalar,
Expand Down Expand Up @@ -144,7 +145,7 @@ impl DbcContent {
let mut sha3 = Sha3::v256();

for parent in self.parents.iter() {
sha3.update(parent);
sha3.update(&parent.to_bytes());
}

sha3.update(&self.amount_secrets_cipher.to_bytes());
Expand Down
22 changes: 11 additions & 11 deletions src/dbc_transaction.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::{DbcContentHash, Hash};
use crate::{Hash, PublicKey};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use tiny_keccak::{Hasher, Sha3};
Expand All @@ -16,23 +16,23 @@ use tiny_keccak::{Hasher, Sha3};
/// i.e. a Dbc can be stored anywhere, even offline.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct DbcTransaction {
pub inputs: BTreeSet<DbcContentHash>,
pub outputs: BTreeSet<DbcContentHash>,
pub inputs: BTreeSet<PublicKey>,
pub outputs: BTreeSet<PublicKey>,
}

impl DbcTransaction {
pub fn new(inputs: BTreeSet<DbcContentHash>, outputs: BTreeSet<DbcContentHash>) -> Self {
pub fn new(inputs: BTreeSet<PublicKey>, outputs: BTreeSet<PublicKey>) -> Self {
Self { inputs, outputs }
}

pub fn hash(&self) -> Hash {
let mut sha3 = Sha3::v256();
for input in self.inputs.iter() {
sha3.update(input);
sha3.update(&input.to_bytes());
}

for output in self.outputs.iter() {
sha3.update(output);
sha3.update(&output.to_bytes());
}

let mut hash = [0; 32];
Expand All @@ -48,18 +48,18 @@ mod tests {
use super::*;
use quickcheck_macros::quickcheck;

use crate::sha3_256;
use crate::OwnerKey;

#[quickcheck]
fn prop_hash_is_independent_of_order(inputs: Vec<u64>, outputs: Vec<u64>) {
// This test is here to protect us in the case that someone swaps out the BTreeSet for inputs/outputs for something else
let input_hashes: Vec<DbcContentHash> = inputs
let input_hashes: Vec<PublicKey> = inputs
.iter()
.map(|i| Hash(sha3_256(&i.to_be_bytes())))
.map(|_| rand::random::<OwnerKey>().0)
.collect();
let output_hashes: Vec<DbcContentHash> = outputs
let output_hashes: Vec<PublicKey> = outputs
.iter()
.map(|i| Hash(sha3_256(&i.to_be_bytes())))
.map(|_| rand::random::<OwnerKey>().0)
.collect();

let forward_hash = DbcTransaction::new(
Expand Down
3 changes: 1 addition & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ pub enum Error {
#[error("DBC already spent in transaction: {transaction:?}")]
DbcAlreadySpent {
transaction: crate::DbcTransaction,
transaction_sigs:
BTreeMap<crate::DbcContentHash, (crate::PublicKeySet, crate::NodeSignature)>,
transaction_sigs: BTreeMap<crate::PublicKey, (crate::PublicKeySet, crate::NodeSignature)>,
},
#[error("Genesis Input has already been spent in a different transaction")]
GenesisInputAlreadySpent,
Expand Down
Loading

0 comments on commit 0b4e9ef

Please sign in to comment.