Skip to content

Commit

Permalink
fix: avoid true input inclusion in decoys
Browse files Browse the repository at this point in the history
We've had some tests cases failing because blst-ringct now verifies
that every public_key referenced by a Tx input is unique.  But
the test cases were obtaining a set of (possibly duplicate) random
decoys for each input. So these decoys could duplicate eachother
and also any of them could also be a true input.

The fix is to refactor TransactionBuilder such that:
1. DecoyInputs are added to a decoy pool within the builder,
and the builder removes dups from the pool.
2. ::build() removes any true inputs from the decoys pool
and then distributes decoys from the pool amongst the inputs.
3. If there are not enough decoys in the pool then an error
is returned -- unless the 'require_all_decoys' setting is false,
in which case any remaining inputs receive no decoys.

Cargo changes:
* only build pprof and benches on unix/linux

Code changes:
* update tests, bench, mint-repl to use modified builder API
* add fields to TransactionBuilder
* add decoys_per_input field and set default to 10.
* rename TransactionBuilder::add_input_by_true_input to add_true_input()
* add API TransactionBuilder::set_decoys_per_input()
* add API TransactionBuilder::set_require_all_inputs()
* add API TransactionBuilder::add_decoy_inputs()
* modify TransactionBuilder::build() to filter true inputs from decoys
  and distribute decoys amongst inputs
* add Error::InsufficientDecoys
  • Loading branch information
dan-da committed Apr 5, 2022
1 parent a08aa89 commit 7dd204e
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 159 deletions.
18 changes: 5 additions & 13 deletions benches/reissue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ fn bench_reissue_1_to_100(c: &mut Criterion) {
generate_dbc_of_value(N_OUTPUTS as Amount, &mut rng).unwrap();

let mut dbc_builder = sn_dbc::TransactionBuilder::default()
.set_require_all_decoys(false) // no decoys!
.add_input_by_secrets(
starting_dbc
.owner_once_bearer()
.unwrap()
.secret_key()
.unwrap(),
starting_dbc.amount_secrets_bearer().unwrap(),
vec![], // never any decoys for genesis
&mut rng,
)
.add_outputs_by_amount((0..N_OUTPUTS).into_iter().map(|_| {
let owner_once =
Expand Down Expand Up @@ -69,21 +68,19 @@ fn bench_reissue_1_to_100(c: &mut Criterion) {

fn bench_reissue_100_to_1(c: &mut Criterion) {
let mut rng = rng::from_seed([0u8; 32]);
let num_decoys = 0;

let (mut spentbook_node, starting_dbc) =
generate_dbc_of_value(N_OUTPUTS as Amount, &mut rng).unwrap();

let mut dbc_builder = sn_dbc::TransactionBuilder::default()
.set_require_all_decoys(false) // no decoys!
.add_input_by_secrets(
starting_dbc
.owner_once_bearer()
.unwrap()
.secret_key()
.unwrap(),
starting_dbc.amount_secrets_bearer().unwrap(),
vec![], // never any decoy inputs for genesis
&mut rng,
)
.add_outputs_by_amount((0..N_OUTPUTS).into_iter().map(|_| {
let owner_once =
Expand All @@ -103,17 +100,13 @@ fn bench_reissue_100_to_1(c: &mut Criterion) {
OwnerOnce::from_owner_base(Owner::from_random_secret_key(&mut rng), &mut rng);

let mut merge_dbc_builder = sn_dbc::TransactionBuilder::default()
.set_require_all_decoys(false) // no decoys!
.add_inputs_by_secrets(
dbcs.into_iter()
.map(|(_dbc, owner_once, amount_secrets)| {
(
owner_once.as_owner().secret_key().unwrap(),
amount_secrets,
spentbook_node.random_decoys(num_decoys, &mut rng),
)
(owner_once.as_owner().secret_key().unwrap(), amount_secrets)
})
.collect(),
&mut rng,
)
.add_output_by_amount(N_OUTPUTS as Amount, output_owner_once)
.build(&mut rng)
Expand Down Expand Up @@ -153,11 +146,10 @@ fn generate_dbc_of_value(
let output_amounts = vec![amount, sn_dbc::GenesisMaterial::GENESIS_AMOUNT - amount];

let mut dbc_builder = sn_dbc::TransactionBuilder::default()
.set_require_all_decoys(false) // no decoys!
.add_input_by_secrets(
genesis_dbc.owner_once_bearer()?.secret_key()?,
genesis_dbc.amount_secrets_bearer()?,
vec![], // never any decoys for genesis
rng,
)
.add_outputs_by_amount(output_amounts.into_iter().map(|amount| {
let owner_once = OwnerOnce::from_owner_base(Owner::from_random_secret_key(rng), rng);
Expand Down
35 changes: 20 additions & 15 deletions examples/mint-repl/mint-repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ use std::os::unix::{io::AsRawFd, prelude::RawFd};
#[cfg(unix)]
use termios::{tcsetattr, Termios, ICANON, TCSADRAIN};

const STD_DECOYS: usize = 3; // how many decoys to use (when available in spentbook)
const STD_DECOYS_TO_FETCH: usize = 1000; // how many decoys to fetch from spentbook (if available)
const STD_DECOYS_PER_INPUT: usize = 3; // how many decoys to use per input (when available)

/// Holds information about the Mint, which may be comprised
/// of 1 or more nodes.
Expand Down Expand Up @@ -556,7 +557,14 @@ fn verify(mintinfo: &MintInfo) -> Result<()> {

/// Implements prepare_tx command.
fn prepare_tx(mintinfo: &MintInfo) -> Result<DbcBuilder> {
let mut tx_builder: TransactionBuilder = Default::default();
let decoy_inputs = mintinfo
.spentbook()?
.random_decoys(STD_DECOYS_TO_FETCH, &mut rng::thread_rng());

let mut tx_builder = TransactionBuilder::default()
.set_decoys_per_input(STD_DECOYS_PER_INPUT)
.set_require_all_decoys(false)
.add_decoy_inputs(decoy_inputs);

// Get DBC inputs from user
loop {
Expand Down Expand Up @@ -585,15 +593,7 @@ fn prepare_tx(mintinfo: &MintInfo) -> Result<DbcBuilder> {
}
};

let decoy_inputs = mintinfo
.spentbook()?
.random_decoys(STD_DECOYS, &mut rng::thread_rng());
tx_builder = tx_builder.add_input_dbc(
&dbc,
&base_secret_key,
decoy_inputs,
&mut rng::thread_rng(),
)?;
tx_builder = tx_builder.add_input_dbc(&dbc, &base_secret_key)?;
}

let mut i = 0u32;
Expand Down Expand Up @@ -717,13 +717,18 @@ fn reissue_auto_cli(mintinfo: &mut MintInfo) -> Result<()> {
.map(|(_, d)| (*d).clone())
.collect();

let mut tx_builder = TransactionBuilder::default();
let decoy_inputs = mintinfo
.spentbook()?
.random_decoys(STD_DECOYS_TO_FETCH, &mut rng);

let mut tx_builder = TransactionBuilder::default()
.set_decoys_per_input(STD_DECOYS_PER_INPUT)
.set_require_all_decoys(false)
.add_decoy_inputs(decoy_inputs);

for dbc in input_dbcs.iter() {
let base_sk = dbc.owner_base().secret_key()?;
let decoy_inputs = mintinfo.spentbook()?.random_decoys(STD_DECOYS, &mut rng);

tx_builder = tx_builder.add_input_dbc(dbc, &base_sk, decoy_inputs, &mut rng)?;
tx_builder = tx_builder.add_input_dbc(dbc, &base_sk)?;
}

let inputs_sum = tx_builder.inputs_amount_sum();
Expand Down

0 comments on commit 7dd204e

Please sign in to comment.