Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: increase simulation likelihood of low probability events #68

Merged
merged 7 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- run: rustup update nightly && rustup default nightly
- run: RUSTDOCFLAGS="-D warnings --cfg doc_cfg" cargo doc --workspace --all-features --no-deps
- run: RUSTDOCFLAGS="-D warnings --cfg doc_cfg" cargo doc --workspace --all-features --no-deps --document-private-items
bench:
name: Bench (${{ matrix.os }})
strategy:
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/simulation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Simulation
on:
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -D warnings
RUST_BACKTRACE: full
jobs:
simulation:
name: Simulation (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
agents:
- 1
- 2
- 3
- 10
- 100
rounds:
- 10
- 100
- 1000
- 10000
os:
- ubuntu-latest
channel:
- nightly
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- run: rustup update ${{ matrix.channel }} --no-self-update && rustup default ${{ matrix.channel }}
- run: cargo run --package manta-pay --all-features --release --bin simulation ${{ matrix.agents }} ${{ matrix.rounds }} 10 100000
3 changes: 1 addition & 2 deletions manta-accounting/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ std = ["manta-crypto/std", "manta-util/std"]
test = [
"futures",
"indexmap",
"manta-crypto/rand",
"parking_lot",
"rand/alloc",
"statrs"
]

Expand All @@ -62,7 +62,6 @@ indexmap = { version = "1.8.0", optional = true, default-features = false }
manta-crypto = { path = "../manta-crypto", default-features = false }
manta-util = { path = "../manta-util", default-features = false, features = ["alloc"] }
parking_lot = { version = "0.12.0", optional = true, default-features = false }
rand = { version = "0.8.4", optional = true, default-features = false }
rand_chacha = { version = "0.3.1", optional = true, default-features = false }
statrs = { version = "0.15.0", optional = true, default-features = false }

Expand Down
11 changes: 11 additions & 0 deletions manta-accounting/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ impl AssetId {
pub const fn into_bytes(self) -> [u8; Self::SIZE] {
self.0.to_le_bytes()
}

/// Samples an [`Asset`] by uniformly choosing between zero and `maximum` when selecting coins.
#[cfg(feature = "test")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "test")))]
#[inline]
pub fn sample_up_to<R>(self, maximum: AssetValue, rng: &mut R) -> Asset
where
R: CryptoRng + RngCore + ?Sized,
{
self.value(rng.gen_range(0..maximum.0))
}
}

impl From<AssetId> for [u8; AssetId::SIZE] {
Expand Down
10 changes: 10 additions & 0 deletions manta-accounting/src/transfer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,16 @@ where
}
}

/// Returns `true` if `self` is a [`Transaction`] which transfers zero value.
#[inline]
pub fn is_zero(&self) -> bool {
match self {
Self::Mint(asset) => asset.is_zero(),
Self::PrivateTransfer(asset, _) => asset.is_zero(),
Self::Reclaim(asset) => asset.is_zero(),
}
}

/// Returns a transaction summary given the asset `metadata`.
#[inline]
pub fn display<F>(&self, metadata: &AssetMetadata, f: F) -> String
Expand Down
119 changes: 95 additions & 24 deletions manta-accounting/src/wallet/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,6 @@ impl BalanceState for AssetList {
}
}

/// Performs a withdraw on `balance` returning `false` if it would overflow.
#[inline]
fn withdraw(balance: Option<&mut AssetValue>, withdraw: AssetValue) -> bool {
match balance {
Some(balance) => {
*balance = match balance.checked_sub(withdraw) {
Some(balance) => balance,
_ => return false,
};
true
}
_ => false,
}
}

/// Adds implementation of [`BalanceState`] for a map type with the given `$entry` type.
macro_rules! impl_balance_state_map_body {
($entry:tt) => {
Expand All @@ -138,7 +123,18 @@ macro_rules! impl_balance_state_map_body {
#[inline]
fn withdraw(&mut self, asset: Asset) -> bool {
if !asset.is_zero() {
withdraw(self.get_mut(&asset.id), asset.value)
if let $entry::Occupied(mut entry) = self.entry(asset.id) {
let balance = entry.get_mut();
if let Some(next_balance) = balance.checked_sub(asset.value) {
if next_balance == 0 {
entry.remove();
} else {
*balance = next_balance;
}
return true;
}
}
false
} else {
true
}
Expand Down Expand Up @@ -205,22 +201,97 @@ pub mod test {
);
}

/// Tests valid withdrawals for an [`AssetList`] balance state.
#[test]
fn asset_list_valid_withdraw() {
assert_valid_withdraw(&mut AssetList::new(), &mut OsRng);
/// Asserts that a maximal withdraw that leaves the state with no value should delete its memory
/// for this process.
#[inline]
pub fn assert_full_withdraw_should_remove_entry<S, R>(rng: &mut R)
where
S: BalanceState,
for<'s> &'s S: IntoIterator,
for<'s> <&'s S as IntoIterator>::IntoIter: ExactSizeIterator,
R: CryptoRng + RngCore + ?Sized,
{
let mut state = S::default();
let asset = Asset::gen(rng);
let initial_length = state.into_iter().len();
state.deposit(asset);
assert_eq!(
initial_length + 1,
state.into_iter().len(),
"Length should have increased by one after depositing a new asset."
);
let balance = state.balance(asset.id);
state.withdraw(asset.id.with(balance));
assert_eq!(
state.balance(asset.id),
0,
"Balance in the removed AssetId should be zero."
);
assert_eq!(
initial_length,
state.into_iter().len(),
"Removed AssetId should remove its entry in the database."
);
}

/// Tests valid withdrawals for a [`BTreeMapBalanceState`] balance state.
#[test]
fn btree_map_valid_withdraw() {
assert_valid_withdraw(&mut BTreeMapBalanceState::new(), &mut OsRng);
/// Defines the tests across multiple different [`BalanceState`] types.
macro_rules! define_tests {
($((
$type:ty,
$doc:expr,
$valid_withdraw:ident,
$full_withdraw:ident
$(,)?)),*$(,)?) => {
$(
#[doc = "Tests valid withdrawals for an"]
#[doc = $doc]
#[doc = "balance state."]
#[test]
fn $valid_withdraw() {
let mut state = <$type>::default();
let mut rng = OsRng;
for _ in 0..0xFFFF {
assert_valid_withdraw(&mut state, &mut rng);
}
}

#[doc = "Tests that there are no empty entries in"]
#[doc = $doc]
#[doc = "with no value stored in them."]
#[test]
fn $full_withdraw() {
assert_full_withdraw_should_remove_entry::<$type, _>(&mut OsRng);
}
)*
}
}

define_tests!(
(
AssetList,
"[`AssetList`]",
asset_list_valid_withdraw,
asset_list_full_withdraw,
),
(
BTreeMapBalanceState,
"[`BTreeMapBalanceState`]",
btree_map_valid_withdraw,
btree_map_full_withdraw,
),
);

/// Tests valid withdrawals for a [`HashMapBalanceState`] balance state.
#[cfg(feature = "std")]
#[test]
fn hash_map_valid_withdraw() {
assert_valid_withdraw(&mut HashMapBalanceState::new(), &mut OsRng);
}

///
#[cfg(feature = "std")]
#[test]
fn hash_map_full_withdraw() {
assert_full_withdraw_should_remove_entry::<HashMapBalanceState, _>(&mut OsRng);
}
}
Loading