Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ rustflags = [
"-Wunused-qualifications",
"-Wclippy::unwrap_used",
]

[target.wasm32-unknown-unknown]
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ members = [
"ark-bdk-wallet",
"e2e-tests",
"ark-sample",
"ark-rust-secp256k1-zkp",
"ark-rust-secp256k1",
"ark-dlc-sample",
"ark-rs",
]
Expand Down
2 changes: 1 addition & 1 deletion ark-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ bech32 = "0.11"
bitcoin = { version = "0.32.4", features = ["rand"] }
futures = "0.3.31"
jiff = "0.2.1"
musig = { package = "ark-secp256k1", path = "../ark-rust-secp256k1", features = ["serde"] }
prost = "0.13.3"
prost-types = "0.13.3"
rand = "0.8"
tokio = { version = "1.41.0", features = ["sync"] }
tracing = "0.1.37"
zkp = { package = "ark-secp256k1-zkp", version = "0.10.0", path = "../ark-rust-secp256k1-zkp", features = ["serde", "rand-std"] }

[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dependencies]
ark-grpc = { path = "../ark-grpc", version = "0.5.9" }
Expand Down
3 changes: 2 additions & 1 deletion ark-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ description = "Core types and utilities for Ark"
[dependencies]
bech32 = "0.11"
bitcoin = { version = "0.32.4", features = ["base64", "rand"] }
musig = { package = "ark-secp256k1", path = "../ark-rust-secp256k1", features = ["serde", "rand"] }
rand = "0.8"
tracing = "0.1.37"
zkp = { package = "ark-secp256k1-zkp", version = "0.10.0", path = "../ark-rust-secp256k1-zkp", features = ["serde", "rand-std"] }

[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2", features = ["wasm-bindgen", "js"] }
getrandom2 = { package = "getrandom", version = "0.3.1", features = ["wasm_js"] }
6 changes: 3 additions & 3 deletions ark-core/src/conversions.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use bitcoin::secp256k1::PublicKey;
use bitcoin::XOnlyPublicKey;

pub fn to_zkp_pk(pk: PublicKey) -> zkp::PublicKey {
zkp::PublicKey::from_slice(&pk.serialize()).expect("valid conversion")
pub fn to_musig_pk(pk: PublicKey) -> musig::PublicKey {
musig::PublicKey::from_slice(&pk.serialize()).expect("valid conversion")
}

pub fn from_zkp_xonly(pk: zkp::XOnlyPublicKey) -> XOnlyPublicKey {
pub fn from_musig_xonly(pk: musig::XOnlyPublicKey) -> XOnlyPublicKey {
XOnlyPublicKey::from_slice(&pk.serialize()).expect("valid conversion")
}
91 changes: 45 additions & 46 deletions ark-core/src/round.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::conversions::from_zkp_xonly;
use crate::conversions::to_zkp_pk;
use crate::conversions::from_musig_xonly;
use crate::conversions::to_musig_pk;
use crate::forfeit_fee::compute_forfeit_min_relay_fee;
use crate::internal_node::VtxoTreeInternalNodeScript;
use crate::server::TxTree;
Expand Down Expand Up @@ -30,17 +30,11 @@ use bitcoin::Transaction;
use bitcoin::TxIn;
use bitcoin::TxOut;
use bitcoin::XOnlyPublicKey;
use musig::musig;
use rand::CryptoRng;
use rand::Rng;
use std::collections::BTreeMap;
use std::collections::HashMap;
use zkp::MusigAggNonce;
use zkp::MusigKeyAggCache;
use zkp::MusigPartialSignature;
use zkp::MusigPubNonce;
use zkp::MusigSecNonce;
use zkp::MusigSession;
use zkp::MusigSessionId;

/// The cosigner PKs that sign a VTXO TX input are included in the `unknown` key-value map field of
/// that input in the VTXO PSBT. Since the `unknown` field can be used for any purpose, we know that
Expand Down Expand Up @@ -119,14 +113,14 @@ impl VtxoInput {
/// copied. We use the [`Option`] to move it into the [`NonceTree`] during nonce generation, and out
/// of the [`NonceTree`] when signing the VTXO tree.
#[allow(clippy::type_complexity)]
pub struct NonceTree(Vec<Vec<Option<(Option<MusigSecNonce>, MusigPubNonce)>>>);
pub struct NonceTree(Vec<Vec<Option<(Option<musig::SecretNonce>, musig::PublicNonce)>>>);

impl NonceTree {
/// Take ownership of the [`MusigSecNonce`] at level `i` and branch `j` in the tree.
///
/// The caller must take ownership because the [`MusigSecNonce`] ensures that it can only be
/// used once, to avoid nonce reuse.
pub fn take_sk(&mut self, i: usize, j: usize) -> Option<MusigSecNonce> {
pub fn take_sk(&mut self, i: usize, j: usize) -> Option<musig::SecretNonce> {
self.0
.get_mut(i)
.and_then(|level| {
Expand Down Expand Up @@ -156,11 +150,11 @@ impl NonceTree {

/// A public nonce per shared internal (non-leaf) node in the VTXO tree.
#[derive(Debug)]
pub struct PubNonceTree(Vec<Vec<Option<MusigPubNonce>>>);
pub struct PubNonceTree(Vec<Vec<Option<musig::PublicNonce>>>);

impl PubNonceTree {
/// Get the [`MusigPubNonce`] at level `i` and branch `j` in the tree.
pub fn get(&self, i: usize, j: usize) -> Option<MusigPubNonce> {
pub fn get(&self, i: usize, j: usize) -> Option<musig::PublicNonce> {
self.0
.get(i)
.and_then(|level| level.get(j))
Expand All @@ -169,13 +163,13 @@ impl PubNonceTree {
}

/// Get the underlying matrix of [`MusigPubNonce`]s.
pub fn into_inner(self) -> Vec<Vec<Option<MusigPubNonce>>> {
pub fn into_inner(self) -> Vec<Vec<Option<musig::PublicNonce>>> {
self.0
}
}

impl From<Vec<Vec<Option<MusigPubNonce>>>> for PubNonceTree {
fn from(value: Vec<Vec<Option<MusigPubNonce>>>) -> Self {
impl From<Vec<Vec<Option<musig::PublicNonce>>>> for PubNonceTree {
fn from(value: Vec<Vec<Option<musig::PublicNonce>>>) -> Self {
Self(value)
}
}
Expand All @@ -190,7 +184,7 @@ pub fn generate_nonce_tree<R>(
where
R: Rng + CryptoRng,
{
let secp_zkp = zkp::Secp256k1::new();
let secp_musig = ::musig::Secp256k1::new();

let nonce_tree = unsigned_vtxo_tree
.levels
Expand All @@ -207,28 +201,32 @@ where
return Ok(None);
}

let session_id = MusigSessionId::new(rng);
// TODO: We would like to use our own RNG here, but this library is using a
// different version of `rand`. I think it's not worth the hassle at this stage,
// particularly because this duplicated dependency will go away anyway.
let session_id = musig::SessionSecretRand::new();
let extra_rand = rng.gen();

let msg = virtual_tx_sighash(node, unsigned_vtxo_tree, i, round_tx)?;

let key_agg_cache = {
let cosigner_pks = cosigner_pks
.iter()
.map(|pk| to_zkp_pk(*pk))
.map(|pk| to_musig_pk(*pk))
.collect::<Vec<_>>();
MusigKeyAggCache::new(&secp_zkp, &cosigner_pks)
musig::KeyAggCache::new(
&secp_musig,
&cosigner_pks.iter().collect::<Vec<_>>(),
)
};

let (nonce, pub_nonce) = key_agg_cache
.nonce_gen(
&secp_zkp,
session_id,
to_zkp_pk(own_cosigner_pk),
msg,
extra_rand,
)
.map_err(Error::crypto)?;
let (nonce, pub_nonce) = key_agg_cache.nonce_gen(
&secp_musig,
session_id,
to_musig_pk(own_cosigner_pk),
msg,
extra_rand,
);

Ok(Some((Some(nonce), pub_nonce)))
})
Expand All @@ -248,7 +246,7 @@ fn virtual_tx_sighash(
level: usize,
// The round transaction, in case it is the parent VTXO.
round_tx: &Psbt,
) -> Result<zkp::Message, Error> {
) -> Result<::musig::Message, Error> {
let tx = &node.tx.unsigned_tx;

// We expect a single input to a VTXO.
Expand Down Expand Up @@ -277,17 +275,17 @@ fn virtual_tx_sighash(
let tap_sighash = SighashCache::new(tx)
.taproot_key_spend_signature_hash(VTXO_INPUT_INDEX, &prevouts, TapSighashType::Default)
.map_err(Error::crypto)?;
let msg = zkp::Message::from_digest(tap_sighash.to_raw_hash().to_byte_array());
let msg = ::musig::Message::from_digest(tap_sighash.to_raw_hash().to_byte_array());

Ok(msg)
}

/// A Musig partial signature per shared internal (non-leaf) node in the VTXO tree.
pub struct PartialSigTree(Vec<Vec<Option<MusigPartialSignature>>>);
pub struct PartialSigTree(Vec<Vec<Option<musig::PartialSignature>>>);

impl PartialSigTree {
/// Get the underlying matrix of [`MusigPartialSignature`]s.
pub fn into_inner(self) -> Vec<Vec<Option<MusigPartialSignature>>> {
pub fn into_inner(self) -> Vec<Vec<Option<musig::PartialSignature>>> {
self.0
}
}
Expand All @@ -309,13 +307,13 @@ pub fn sign_vtxo_tree(
let internal_node_script = VtxoTreeInternalNodeScript::new(vtxo_tree_expiry, server_pk);

let secp = Secp256k1::new();
let secp_zkp = zkp::Secp256k1::new();
let secp_musig = ::musig::Secp256k1::new();

let own_cosigner_kp =
zkp::Keypair::from_seckey_slice(&secp_zkp, &own_cosigner_kp.secret_bytes())
::musig::Keypair::from_seckey_slice(&secp_musig, &own_cosigner_kp.secret_bytes())
.expect("valid keypair");

let mut partial_sig_tree: Vec<Vec<Option<MusigPartialSignature>>> = Vec::new();
let mut partial_sig_tree: Vec<Vec<Option<musig::PartialSignature>>> = Vec::new();
for (i, level) in vtxo_tree.levels.iter().enumerate() {
let mut sigs_level = Vec::new();
for (j, node) in level.nodes.iter().enumerate() {
Expand All @@ -332,37 +330,38 @@ pub fn sign_vtxo_tree(
let mut key_agg_cache = {
let cosigner_pks = cosigner_pks
.iter()
.map(|pk| to_zkp_pk(*pk))
.map(|pk| to_musig_pk(*pk))
.collect::<Vec<_>>();
MusigKeyAggCache::new(&secp_zkp, &cosigner_pks)
musig::KeyAggCache::new(&secp_musig, &cosigner_pks.iter().collect::<Vec<_>>())
};

let sweep_tap_tree = internal_node_script
.sweep_spend_leaf(&secp, from_zkp_xonly(key_agg_cache.agg_pk()));
.sweep_spend_leaf(&secp, from_musig_xonly(key_agg_cache.agg_pk()));

let tweak = zkp::SecretKey::from_slice(sweep_tap_tree.tap_tweak().as_byte_array())
.expect("valid conversion");
let tweak = ::musig::Scalar::from(
::musig::SecretKey::from_slice(sweep_tap_tree.tap_tweak().as_byte_array())
.expect("valid conversion"),
);

key_agg_cache
.pubkey_xonly_tweak_add(&secp_zkp, tweak)
.pubkey_xonly_tweak_add(&secp_musig, &tweak)
.map_err(Error::crypto)?;

let agg_pub_nonce = aggregate_pub_nonce_tree
.get(i, j)
.ok_or_else(|| Error::crypto(format!("missing pub nonce {i}, {j}")))?;

// Equivalent to parsing the individual `MusigAggNonce` from a slice.
let agg_nonce = MusigAggNonce::new(&secp_zkp, &[agg_pub_nonce]);
let agg_nonce = musig::AggregatedNonce::new(&secp_musig, &[&agg_pub_nonce]);

let msg = virtual_tx_sighash(node, vtxo_tree, i, round_tx)?;

let nonce_sk = our_nonce_tree
.take_sk(i, j)
.ok_or(Error::crypto("missing nonce {i}, {j}"))?;

let sig = MusigSession::new(&secp_zkp, &key_agg_cache, agg_nonce, msg)
.partial_sign(&secp_zkp, nonce_sk, &own_cosigner_kp, &key_agg_cache)
.map_err(Error::crypto)?;
let sig = musig::Session::new(&secp_musig, &key_agg_cache, agg_nonce, msg)
.partial_sign(&secp_musig, nonce_sk, &own_cosigner_kp, &key_agg_cache);

sigs_level.push(Some(sig));
}
Expand Down
3 changes: 2 additions & 1 deletion ark-core/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bitcoin::OutPoint;
use bitcoin::Psbt;
use bitcoin::ScriptBuf;
use bitcoin::Txid;
use musig::musig;
use std::collections::HashMap;

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -207,7 +208,7 @@ pub struct RoundSigningEvent {
#[derive(Debug, Clone)]
pub struct RoundSigningNoncesGeneratedEvent {
pub id: String,
pub tree_nonces: Vec<Vec<Option<zkp::MusigPubNonce>>>,
pub tree_nonces: Vec<Vec<Option<musig::PublicNonce>>>,
}

#[derive(Debug, Clone)]
Expand Down
3 changes: 2 additions & 1 deletion ark-dlc-sample/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ ark-grpc = { path = "../ark-grpc" }
bitcoin = { version = "0.32" }
esplora-client = { version = "0.10", features = ["async-https"] }
futures = "0.3"
musig = { package = "ark-secp256k1", path = "../ark-rust-secp256k1" }
rand = "0.8"
regex = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "time", "tracing-log", "json"] }
zkp = { package = "ark-secp256k1-zkp", version = "0.10.0", path = "../ark-rust-secp256k1-zkp" }
zkp = { package = "secp256k1-zkp", git = "https://github.com/sanket1729/rust-secp256k1-zkp", rev = "60e631c24588a0c9e271badd61959294848c665d", features = ["rand-std"] }
32 changes: 19 additions & 13 deletions ark-dlc-sample/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use anyhow::Context;
use anyhow::Result;
use ark_core::boarding_output::list_boarding_outpoints;
use ark_core::boarding_output::BoardingOutpoints;
use ark_core::conversions::from_zkp_xonly;
use ark_core::conversions::to_zkp_pk;
use ark_core::redeem;
use ark_core::redeem::build_redeem_transaction;
use ark_core::redeem::sign_redeem_transaction;
Expand Down Expand Up @@ -50,11 +48,11 @@ use std::collections::HashMap;
use std::process::Command;
use std::time::Duration;
use tokio::task::block_in_place;
use zkp::musig;
use zkp::new_musig_nonce_pair;
use zkp::MusigAggNonce;
use zkp::MusigSession;
use zkp::MusigSessionId;
use zkp::musig::new_musig_nonce_pair;
use zkp::musig::MusigAggNonce;
use zkp::musig::MusigKeyAggCache;
use zkp::musig::MusigSession;
use zkp::musig::MusigSessionId;

#[tokio::main]
async fn main() -> Result<()> {
Expand Down Expand Up @@ -113,7 +111,7 @@ async fn main() -> Result<()> {

// Using Musig2, the server is not even aware that this is a shared VTXO.
let musig_key_agg_cache =
musig::MusigKeyAggCache::new(&zkp, &[to_zkp_pk(alice_pk), to_zkp_pk(bob_pk)]);
MusigKeyAggCache::new(&zkp, &[to_zkp_pk(alice_pk), to_zkp_pk(bob_pk)]);
let shared_pk = musig_key_agg_cache.agg_pk();
let shared_pk = from_zkp_xonly(shared_pk);

Expand Down Expand Up @@ -283,7 +281,7 @@ async fn main() -> Result<()> {

// Complete the adaptor signature, producing a valid signature for this CET.

let sig = musig::adapt(adaptor_sig, adaptor, musig_nonce_parity);
let sig = zkp::musig::adapt(adaptor_sig, adaptor, musig_nonce_parity);
let sig = schnorr::Signature::from_slice(sig.as_ref()).expect("valid sig");

input_sig.signature = sig;
Expand Down Expand Up @@ -433,7 +431,7 @@ fn sign_cet_redeem_tx(
mut cet_redeem_psbt: Psbt,
alice_kp: &Keypair,
bob_kp: &Keypair,
musig_key_agg_cache: &musig::MusigKeyAggCache,
musig_key_agg_cache: &MusigKeyAggCache,
adaptor_pk: zkp::PublicKey,
dlc_vtxo_input: &redeem::VtxoInput,
) -> Result<(Psbt, zkp::Parity)> {
Expand Down Expand Up @@ -823,7 +821,7 @@ pub struct SpendStatus {
}

impl EsploraClient {
pub fn new(url: &str) -> Result<Self> {
fn new(url: &str) -> Result<Self> {
let builder = esplora_client::Builder::new(url);
let esplora_client = builder.build_async()?;

Expand Down Expand Up @@ -895,7 +893,7 @@ impl EsploraClient {
}
}

pub async fn faucet_fund(address: &bitcoin::Address, amount: Amount) -> Result<OutPoint> {
async fn faucet_fund(address: &bitcoin::Address, amount: Amount) -> Result<OutPoint> {
let res = Command::new("nigiri")
.args(["faucet", &address.to_string(), &amount.to_btc().to_string()])
.output()?;
Expand Down Expand Up @@ -942,7 +940,15 @@ pub async fn faucet_fund(address: &bitcoin::Address, amount: Amount) -> Result<O
})
}

pub fn init_tracing() {
fn to_zkp_pk(pk: secp256k1::PublicKey) -> zkp::PublicKey {
zkp::PublicKey::from_slice(&pk.serialize()).expect("valid conversion")
}

pub fn from_zkp_xonly(pk: zkp::XOnlyPublicKey) -> XOnlyPublicKey {
XOnlyPublicKey::from_slice(&pk.serialize()).expect("valid conversion")
}

fn init_tracing() {
tracing_subscriber::fmt()
.with_env_filter(
"debug,\
Expand Down
Loading
Loading