Skip to content
Closed
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
22 changes: 21 additions & 1 deletion crates/coin-store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ categories.workspace = true
simplicityhl = { workspace = true }
contracts = { workspace = true }

simplicityhl-core = { workspace = true }

sha2 = { version = "0.10.9" }

futures = { version = "0.3" }
Expand All @@ -29,4 +31,22 @@ sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "migrate"] }
bincode = { version = "2.0.1", features = ["alloc", "derive", "serde"] }

[dev-dependencies]
hex = { version = "0.4.3" }

criterion = { version = "0.5", features = ["async_tokio"] }
tokio = { version = "1", features = ["full"] }

[[bench]]
name = "default"
harness = false

[[bench]]
name = "entropy"
harness = false

[[bench]]
name = "contracts"
harness = false

[[bench]]
name = "token"
harness = false
173 changes: 173 additions & 0 deletions crates/coin-store/benches/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
use std::collections::HashMap;
use std::fs;
use std::str::FromStr;

use coin_store::executor::UtxoStore;
use coin_store::filter::UtxoFilter;
use coin_store::store::Store;

use simplicityhl::elements::confidential::{Asset, Nonce, Value as ConfidentialValue};
use simplicityhl::elements::hashes::Hash;
use simplicityhl::elements::pset::PartiallySignedTransaction;
use simplicityhl::elements::secp256k1_zkp::SecretKey;
use simplicityhl::elements::{AddressParams, AssetId, OutPoint, Script, Transaction, TxOut, TxOutWitness, Txid};
use simplicityhl::simplicity::bitcoin::key::Keypair;
use simplicityhl::simplicity::bitcoin::secp256k1::Secp256k1;

use simplicityhl_core::{LIQUID_TESTNET_BITCOIN_ASSET, LIQUID_TESTNET_TEST_ASSET_ID_STR};

use contracts::options::OPTION_SOURCE;
use contracts::options::OptionsArguments;
use contracts::sdk::build_option_creation;
use contracts::sdk::taproot_pubkey_gen::{TaprootPubkeyGen, get_random_seed};

fn setup_tx_and_contract(
keypair: &Keypair,
start_time: u32,
expiry_time: u32,
collateral_per_contract: u64,
settlement_per_contract: u64,
) -> Result<((PartiallySignedTransaction, TaprootPubkeyGen), OptionsArguments), Box<dyn std::error::Error>> {
let option_outpoint = OutPoint::new(Txid::from_slice(&[1; 32])?, 0);
let grantor_outpoint = OutPoint::new(Txid::from_slice(&[2; 32])?, 0);

let issuance_asset_entropy = get_random_seed();

let option_arguments = OptionsArguments::new(
start_time,
expiry_time,
collateral_per_contract,
settlement_per_contract,
*LIQUID_TESTNET_BITCOIN_ASSET,
AssetId::from_str(LIQUID_TESTNET_TEST_ASSET_ID_STR)?,
issuance_asset_entropy,
(option_outpoint, false),
(grantor_outpoint, false),
);

let pst_and_tpg = build_option_creation(
&keypair.public_key(),
(
option_outpoint,
TxOut {
asset: Asset::Explicit(*LIQUID_TESTNET_BITCOIN_ASSET),
value: ConfidentialValue::Explicit(500),
nonce: Nonce::Null,
script_pubkey: Script::new(),
witness: TxOutWitness::default(),
},
),
(
grantor_outpoint,
TxOut {
asset: Asset::Explicit(*LIQUID_TESTNET_BITCOIN_ASSET),
value: ConfidentialValue::Explicit(1000),
nonce: Nonce::Null,
script_pubkey: Script::new(),
witness: TxOutWitness::default(),
},
),
&option_arguments,
issuance_asset_entropy,
100,
&AddressParams::LIQUID_TESTNET,
)?;

Ok((pst_and_tpg, option_arguments))
}

pub async fn setup_db() -> Result<
(
Store,
(Vec<UtxoFilter>, Vec<UtxoFilter>, Vec<UtxoFilter>, Vec<UtxoFilter>),
String,
),
Box<dyn std::error::Error>,
> {
let path = "/tmp/benchmark_stress.db";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: hardcoded database path could cause conflicts if multiple benchmarks run concurrently - consider using a unique path per benchmark or test run

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/coin-store/benches/common.rs
Line: 84:84

Comment:
**style:** hardcoded database path could cause conflicts if multiple benchmarks run concurrently - consider using a unique path per benchmark or test run

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, the main way to optimize a database is to configure it to use one (possibly large) function and only configure filters. So if I have one database with a dataset, I don't need to store it in different places.

let _ = fs::remove_file(path);

let mut filters_default = vec![];
let mut filters_contracts = vec![];
let mut filters_tokens = vec![];
let mut filters_entropy = vec![];
let store = Store::create(path).await?;

for _i in 0..10 {
let secp = Secp256k1::new();
let keypair = Keypair::from_secret_key(&secp, &SecretKey::from_slice(&[1u8; 32])?);
let secret_key = keypair.secret_key();

let ((pst, tpg), opts_args) = setup_tx_and_contract(&keypair, 0, 0, 20, 25)?;
let tx: Transaction = pst.extract_tx()?;

let mut blinder_keys = HashMap::new();
for (i, _output) in tx.output.iter().enumerate() {
blinder_keys.insert(i, keypair);
}
store
.insert_transaction(&tx, blinder_keys)
.await
.expect("Failed to insert Tx");

let source_code = OPTION_SOURCE;

let args = opts_args.build_option_arguments();

let tpg_for_db = tpg.clone();
let tpg_for_filter = tpg.clone();
let tpg_for_token = tpg;

store
.add_contract(source_code, args, tpg_for_db, None)
.await
.expect("Failed to insert contract token");

let option_asset_id = opts_args.option_token();

store
.insert_contract_token(&tpg_for_token.clone(), option_asset_id, "Name of token")
.await
.expect("Failed to insert contract token");

for output in tx.output.iter() {
let asset_id = match output.asset {
Asset::Explicit(id) => id,

Asset::Confidential(_) => {
let unblinded = output.unblind(&secp, secret_key).expect("Missing blinding key");
unblinded.asset
}

_ => panic!("Match error"),
};

filters_default.push(UtxoFilter::new().asset_id(asset_id).required_value(1));

filters_entropy.push(UtxoFilter::new().asset_id(asset_id).required_value(1).include_entropy());

filters_contracts.push(
UtxoFilter::new()
.asset_id(asset_id)
.required_value(1)
.include_entropy()
.taproot_pubkey_gen(tpg_for_filter.clone()),
);

filters_tokens.push(
UtxoFilter::new()
.asset_id(asset_id)
.required_value(1)
.include_entropy()
.taproot_pubkey_gen(tpg_for_filter.clone())
.token_tag("Name of token"),
);
}
}

Ok((
store,
(filters_default, filters_entropy, filters_contracts, filters_tokens),
path.to_string(),
))
}
30 changes: 30 additions & 0 deletions crates/coin-store/benches/contracts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::fs;
use tokio::runtime::Runtime;

use coin_store::executor::UtxoStore;

mod common;

fn criterion_benchmark(c: &mut Criterion) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to make this function async?

let rt = Runtime::new().unwrap();

let (store, filters, db_path) = rt.block_on(async { common::setup_db().await.expect("DB Setup failed") });

let mut group = c.benchmark_group("UTXO Queries (with contracts)");
group.sample_size(10);
group.measurement_time(std::time::Duration::from_secs(10));

group.bench_function("current_implementation", |b| {
b.to_async(&rt).iter(|| async {
store.query_utxos(black_box(&filters.2)).await.unwrap();
})
});

group.finish();

let _ = fs::remove_file(db_path);
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
30 changes: 30 additions & 0 deletions crates/coin-store/benches/default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::fs;
use tokio::runtime::Runtime;

use coin_store::executor::UtxoStore;

mod common;

fn criterion_benchmark(c: &mut Criterion) {
let rt = Runtime::new().unwrap();

let (store, filters, db_path) = rt.block_on(async { common::setup_db().await.expect("DB Setup failed") });

let mut group = c.benchmark_group("UTXO Queries (default)");
group.sample_size(10);
group.measurement_time(std::time::Duration::from_secs(10));

group.bench_function("current_implementation", |b| {
b.to_async(&rt).iter(|| async {
store.query_utxos(black_box(&filters.0)).await.unwrap();
})
});

group.finish();

let _ = fs::remove_file(db_path);
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
30 changes: 30 additions & 0 deletions crates/coin-store/benches/entropy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::fs;
use tokio::runtime::Runtime;

use coin_store::executor::UtxoStore;

mod common;

fn criterion_benchmark(c: &mut Criterion) {
let rt = Runtime::new().unwrap();

let (store, filters, db_path) = rt.block_on(async { common::setup_db().await.expect("DB Setup failed") });

let mut group = c.benchmark_group("UTXO Queries (with entropy)");
group.sample_size(10);
group.measurement_time(std::time::Duration::from_secs(10));

group.bench_function("current_implementation", |b| {
b.to_async(&rt).iter(|| async {
store.query_utxos(black_box(&filters.1)).await.unwrap();
})
});

group.finish();

let _ = fs::remove_file(db_path);
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
30 changes: 30 additions & 0 deletions crates/coin-store/benches/token.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::fs;
use tokio::runtime::Runtime;

use coin_store::executor::UtxoStore;

mod common;

fn criterion_benchmark(c: &mut Criterion) {
let rt = Runtime::new().unwrap();

let (store, filters, db_path) = rt.block_on(async { common::setup_db().await.expect("DB Setup failed") });

let mut group = c.benchmark_group("UTXO Queries (with token)");
group.sample_size(10);
group.measurement_time(std::time::Duration::from_secs(10));

group.bench_function("current_implementation", |b| {
b.to_async(&rt).iter(|| async {
store.query_utxos(black_box(&filters.3)).await.unwrap();
})
});

group.finish();

let _ = fs::remove_file(db_path);
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);