upgrade secp256kfun (#1466)

* ci: add cargo check on rust stable

* refactor: upgrade secp256kfun and fix resulting issues

* build(deps): update sigma_fun and ecdsa_fun to a52142cf7f

 #1520
 #1521

* chore: fix clippy issue

* update to 91112f80b24

* bump to 294de1721add

* chore(deps): remove spectral

spectral fails to compile on rust stable 1.76 due to dep on deprecated
rustc-serialize

* secp256kfun: update to 7da9d277 and set rev in manifest

* update to 6fdc5d8

* switch to crates.io versions of ecdsa_fun and sigma_fun

* ci: update toolchain to 1.74 and fix draft action

* clippy fixes

---------

Co-authored-by: binarybaron <86064887+binarybaron@users.noreply.github.com>
This commit is contained in:
Byron Hambly 2024-05-27 11:03:20 +02:00 committed by GitHub
parent 1e2dddb61b
commit 796863359f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 229 additions and 220 deletions

View file

@ -27,7 +27,7 @@ data-encoding = "2.6"
dialoguer = "0.11"
digest = "0.10.7"
directories-next = "2"
ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "libsecp_compat", "serde", "adaptor" ] }
ecdsa_fun = { version = "0.10", default-features = false, features = [ "libsecp_compat", "serde", "adaptor" ] }
ed25519-dalek = "1"
futures = { version = "0.3", default-features = false }
hex = "0.4"
@ -50,7 +50,7 @@ serde_cbor = "0.11"
serde_json = "1"
serde_with = { version = "1", features = [ "macros" ] }
sha2 = "0.10"
sigma_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "ed25519", "serde", "secp256k1", "alloc" ] }
sigma_fun = { version = "0.7", default-features = false, features = [ "ed25519", "serde", "secp256k1", "alloc" ] }
sqlx = { version = "0.6.3", features = [ "sqlite", "runtime-tokio-rustls", "offline" ] }
structopt = "0.3"
strum = { version = "0.26", features = [ "derive" ] }
@ -88,7 +88,6 @@ proptest = "1"
sequential-test = "0.2.4"
serde_cbor = "0.11"
serial_test = "3.0"
spectral = "0.6"
tempfile = "3"
testcontainers = "0.14"

View file

@ -279,10 +279,10 @@ where
SwarmEvent::IncomingConnectionError { send_back_addr: address, error, .. } => {
tracing::warn!(%address, "Failed to set up connection with peer: {:#}", error);
}
SwarmEvent::ConnectionClosed { peer_id: peer, num_established, endpoint, cause: Some(error) } if num_established == 0 => {
SwarmEvent::ConnectionClosed { peer_id: peer, num_established: 0, endpoint, cause: Some(error) } => {
tracing::debug!(%peer, address = %endpoint.get_remote_address(), "Lost connection to peer: {:#}", error);
}
SwarmEvent::ConnectionClosed { peer_id: peer, num_established, endpoint, cause: None } if num_established == 0 => {
SwarmEvent::ConnectionClosed { peer_id: peer, num_established: 0, endpoint, cause: None } => {
tracing::info!(%peer, address = %endpoint.get_remote_address(), "Successfully closed connection");
}
SwarmEvent::NewListenAddr{address, ..} => {
@ -296,7 +296,7 @@ where
Some(Ok((peer, transfer_proof, responder))) => {
if !self.swarm.behaviour_mut().transfer_proof.is_connected(&peer) {
tracing::warn!(%peer, "No active connection to peer, buffering transfer proof");
self.buffered_transfer_proofs.entry(peer).or_insert_with(Vec::new).push((transfer_proof, responder));
self.buffered_transfer_proofs.entry(peer).or_default().push((transfer_proof, responder));
continue;
}

View file

@ -302,7 +302,7 @@ pub mod rendezvous {
fn inject_disconnected(&mut self, peer_id: &PeerId) {
for i in 0..self.rendezvous_nodes.len() {
let mut node = &mut self.rendezvous_nodes[i];
let node = &mut self.rendezvous_nodes[i];
if peer_id == &node.peer_id {
node.connection_status = ConnectionStatus::Disconnected;
}
@ -325,7 +325,7 @@ pub mod rendezvous {
_error: &DialError,
) {
for i in 0..self.rendezvous_nodes.len() {
let mut node = &mut self.rendezvous_nodes[i];
let node = &mut self.rendezvous_nodes[i];
if let Some(id) = peer_id {
if id == node.peer_id {
node.connection_status = ConnectionStatus::Disconnected;

View file

@ -16,6 +16,7 @@ pub use crate::bitcoin::timelocks::{BlockHeight, ExpiredTimelocks};
pub use ::bitcoin::util::amount::Amount;
pub use ::bitcoin::util::psbt::PartiallySignedTransaction;
pub use ::bitcoin::{Address, AddressType, Network, Transaction, Txid};
use bitcoin::secp256k1::ecdsa;
pub use ecdsa_fun::adaptor::EncryptedSignature;
pub use ecdsa_fun::fun::Scalar;
pub use ecdsa_fun::Signature;
@ -25,9 +26,8 @@ pub use wallet::Wallet;
pub use wallet::WalletBuilder;
use crate::bitcoin::wallet::ScriptStatus;
use ::bitcoin::hashes::hex::ToHex;
use ::bitcoin::hashes::Hash;
use ::bitcoin::{secp256k1, Sighash};
use ::bitcoin::Sighash;
use anyhow::{bail, Context, Result};
use bdk::miniscript::descriptor::Wsh;
use bdk::miniscript::{Descriptor, Segwitv0};
@ -206,20 +206,21 @@ pub fn verify_encsig(
#[error("encrypted signature is invalid")]
pub struct InvalidEncryptedSignature;
pub fn build_shared_output_descriptor(A: Point, B: Point) -> Descriptor<bitcoin::PublicKey> {
pub fn build_shared_output_descriptor(
A: Point,
B: Point,
) -> Result<Descriptor<bitcoin::PublicKey>> {
const MINISCRIPT_TEMPLATE: &str = "c:and_v(v:pk(A),pk_k(B))";
// NOTE: This shouldn't be a source of error, but maybe it is
let A = ToHex::to_hex(&secp256k1::PublicKey::from(A));
let B = ToHex::to_hex(&secp256k1::PublicKey::from(B));
let miniscript = MINISCRIPT_TEMPLATE.replace('A', &A).replace('B', &B);
let miniscript = MINISCRIPT_TEMPLATE
.replace('A', &A.to_string())
.replace('B', &B.to_string());
let miniscript =
bdk::miniscript::Miniscript::<bitcoin::PublicKey, Segwitv0>::from_str(&miniscript)
.expect("a valid miniscript");
Descriptor::Wsh(Wsh::new(miniscript).expect("a valid descriptor"))
Ok(Descriptor::Wsh(Wsh::new(miniscript)?))
}
pub fn recover(S: PublicKey, sig: Signature, encsig: EncryptedSignature) -> Result<SecretKey> {
@ -305,6 +306,13 @@ pub mod bitcoin_address {
}
}
// Transform the ecdsa der signature bytes into a secp256kfun ecdsa signature type.
pub fn extract_ecdsa_sig(sig: &[u8]) -> Result<Signature> {
let data = &sig[..sig.len() - 1];
let sig = ecdsa::Signature::from_der(data)?.serialize_compact();
Signature::from_bytes(sig).ok_or(anyhow::anyhow!("invalid signature"))
}
/// Bitcoin error codes: https://github.com/bitcoin/bitcoin/blob/97d3500601c1d28642347d014a6de1e38f53ae4e/src/rpc/protocol.h#L23
pub enum RpcErrorCode {
/// Transaction or block was rejected by network rules. Error code -26.
@ -378,6 +386,8 @@ mod tests {
use super::*;
use crate::env::{GetConfig, Regtest};
use crate::protocol::{alice, bob};
use bitcoin::secp256k1;
use ecdsa_fun::fun::marker::{NonZero, Public};
use rand::rngs::OsRng;
use std::matches;
use uuid::Uuid;
@ -524,4 +534,16 @@ mod tests {
transaction
)
}
#[test]
fn compare_point_hex() {
// secp256kfun Point and secp256k1 PublicKey should have the same bytes and hex representation
let secp = secp256k1::Secp256k1::default();
let keypair = secp256k1::KeyPair::new(&secp, &mut OsRng);
let pubkey = keypair.public_key();
let point: Point<_, Public, NonZero> = Point::from_bytes(pubkey.serialize()).unwrap();
assert_eq!(pubkey.to_string(), point.to_string());
}
}

View file

@ -5,7 +5,8 @@ use crate::bitcoin::{
};
use ::bitcoin::util::sighash::SighashCache;
use ::bitcoin::{
EcdsaSighashType, OutPoint, PackedLockTime, Script, Sequence, Sighash, TxIn, TxOut, Txid,
secp256k1, EcdsaSighashType, OutPoint, PackedLockTime, Script, Sequence, Sighash, TxIn, TxOut,
Txid,
};
use anyhow::Result;
use bdk::miniscript::Descriptor;
@ -117,8 +118,8 @@ impl TxCancel {
A: PublicKey,
B: PublicKey,
spending_fee: Amount,
) -> Self {
let cancel_output_descriptor = build_shared_output_descriptor(A.0, B.0);
) -> Result<Self> {
let cancel_output_descriptor = build_shared_output_descriptor(A.0, B.0)?;
let tx_in = TxIn {
previous_output: tx_lock.as_outpoint(),
@ -148,12 +149,12 @@ impl TxCancel {
)
.expect("sighash");
Self {
Ok(Self {
inner: transaction,
digest,
output_descriptor: cancel_output_descriptor,
lock_output_descriptor: tx_lock.output_descriptor.clone(),
}
})
}
pub fn txid(&self) -> Txid {
@ -214,25 +215,27 @@ impl TxCancel {
let A = ::bitcoin::PublicKey {
compressed: true,
inner: A.0.into(),
inner: secp256k1::PublicKey::from_slice(&A.0.to_bytes())?,
};
let B = ::bitcoin::PublicKey {
compressed: true,
inner: B.0.into(),
inner: secp256k1::PublicKey::from_slice(&B.0.to_bytes())?,
};
// The order in which these are inserted doesn't matter
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);

View file

@ -32,7 +32,7 @@ impl TxLock {
C: EstimateFeeRate,
D: BatchDatabase,
{
let lock_output_descriptor = build_shared_output_descriptor(A.0, B.0);
let lock_output_descriptor = build_shared_output_descriptor(A.0, B.0)?;
let address = lock_output_descriptor
.address(wallet.get_network())
.expect("can derive address from descriptor");
@ -84,7 +84,7 @@ impl TxLock {
}
};
let descriptor = build_shared_output_descriptor(A.0, B.0);
let descriptor = build_shared_output_descriptor(A.0, B.0)?;
let legit_shared_output_script = descriptor.script_pubkey();
if shared_output_candidate.script_pubkey != legit_shared_output_script {
@ -263,7 +263,7 @@ mod tests {
fn estimated_tx_lock_script_size_never_changes(a in crate::proptest::ecdsa_fun::point(), b in crate::proptest::ecdsa_fun::point()) {
proptest::prop_assume!(a != b);
let computed_size = build_shared_output_descriptor(a, b).script_pubkey().len();
let computed_size = build_shared_output_descriptor(a, b).unwrap().script_pubkey().len();
assert_eq!(computed_size, SCRIPT_SIZE);
}

View file

@ -1,7 +1,7 @@
use crate::bitcoin::wallet::Watchable;
use crate::bitcoin::{self, Address, Amount, PunishTimelock, Transaction, TxCancel, Txid};
use ::bitcoin::util::sighash::SighashCache;
use ::bitcoin::{EcdsaSighashType, Sighash};
use ::bitcoin::{secp256k1, EcdsaSighashType, Sighash};
use anyhow::{Context, Result};
use bdk::bitcoin::Script;
use bdk::miniscript::Descriptor;
@ -64,18 +64,20 @@ impl TxPunish {
let A = a.public().try_into()?;
let B = B.try_into()?;
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
// The order in which these are inserted doesn't matter
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);

View file

@ -6,7 +6,7 @@ use crate::bitcoin::{
use ::bitcoin::{Sighash, Txid};
use anyhow::{bail, Context, Result};
use bdk::miniscript::Descriptor;
use bitcoin::secp256k1::ecdsa;
use bitcoin::secp256k1;
use bitcoin::util::sighash::SighashCache;
use bitcoin::{EcdsaSighashType, Script};
use ecdsa_fun::adaptor::{Adaptor, HashTranscript};
@ -16,6 +16,8 @@ use ecdsa_fun::Signature;
use sha2::Sha256;
use std::collections::HashMap;
use super::extract_ecdsa_sig;
#[derive(Clone, Debug)]
pub struct TxRedeem {
inner: Transaction,
@ -64,7 +66,7 @@ impl TxRedeem {
) -> Result<Transaction> {
verify_encsig(
B,
PublicKey::from(s_a.clone()),
PublicKey::from(s_a),
&self.digest(),
&encrypted_signature,
)
@ -79,25 +81,27 @@ impl TxRedeem {
let A = ::bitcoin::PublicKey {
compressed: true,
inner: a.public.into(),
inner: secp256k1::PublicKey::from_slice(&a.public.to_bytes())?,
};
let B = ::bitcoin::PublicKey {
compressed: true,
inner: B.0.into(),
inner: secp256k1::PublicKey::from_slice(&B.0.to_bytes())?,
};
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
// The order in which these are inserted doesn't matter
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);
@ -120,16 +124,16 @@ impl TxRedeem {
let input = match candidate_transaction.input.as_slice() {
[input] => input,
[] => bail!(NoInputs),
[inputs @ ..] => bail!(TooManyInputs(inputs.len())),
inputs => bail!(TooManyInputs(inputs.len())),
};
let sigs = match input.witness.iter().collect::<Vec<_>>().as_slice() {
let sigs = match input.witness.to_vec().as_slice() {
[sig_1, sig_2, _script] => [sig_1, sig_2]
.iter()
.map(|sig| ecdsa::Signature::from_der(&sig[..sig.len() - 1]).map(Signature::from))
.collect::<std::result::Result<Vec<_>, _>>(),
.into_iter()
.map(|sig| extract_ecdsa_sig(sig))
.collect::<Result<Vec<_>, _>>(),
[] => bail!(EmptyWitnessStack),
[witnesses @ ..] => bail!(NotThreeWitnesses(witnesses.len())),
witnesses => bail!(NotThreeWitnesses(witnesses.len())),
}?;
let sig = sigs

View file

@ -4,7 +4,7 @@ use crate::bitcoin::{
TooManyInputs, Transaction, TxCancel,
};
use crate::{bitcoin, monero};
use ::bitcoin::secp256k1::ecdsa;
use ::bitcoin::secp256k1;
use ::bitcoin::util::sighash::SighashCache;
use ::bitcoin::{EcdsaSighashType, Script, Sighash, Txid};
use anyhow::{bail, Context, Result};
@ -12,6 +12,8 @@ use bdk::miniscript::Descriptor;
use ecdsa_fun::Signature;
use std::collections::HashMap;
use super::extract_ecdsa_sig;
#[derive(Debug)]
pub struct TxRefund {
inner: Transaction,
@ -62,25 +64,27 @@ impl TxRefund {
let A = ::bitcoin::PublicKey {
compressed: true,
inner: A.0.into(),
inner: secp256k1::PublicKey::from_slice(&A.0.to_bytes())?,
};
let B = ::bitcoin::PublicKey {
compressed: true,
inner: B.0.into(),
inner: secp256k1::PublicKey::from_slice(&B.0.to_bytes())?,
};
let sig_a = secp256k1::ecdsa::Signature::from_compact(&sig_a.to_bytes())?;
let sig_b = secp256k1::ecdsa::Signature::from_compact(&sig_b.to_bytes())?;
// The order in which these are inserted doesn't matter
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
sig: sig_a,
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
sig: sig_b,
hash_ty: EcdsaSighashType::All,
},
);
@ -127,16 +131,16 @@ impl TxRefund {
let input = match candidate_transaction.input.as_slice() {
[input] => input,
[] => bail!(NoInputs),
[inputs @ ..] => bail!(TooManyInputs(inputs.len())),
inputs => bail!(TooManyInputs(inputs.len())),
};
let sigs = match input.witness.iter().collect::<Vec<_>>().as_slice() {
let sigs = match input.witness.to_vec().as_slice() {
[sig_1, sig_2, _script] => [sig_1, sig_2]
.iter()
.map(|sig| ecdsa::Signature::from_der(&sig[..sig.len() - 1]).map(Signature::from))
.collect::<std::result::Result<Vec<_>, _>>(),
.into_iter()
.map(|sig| extract_ecdsa_sig(sig))
.collect::<Result<Vec<_>, _>>(),
[] => bail!(EmptyWitnessStack),
[witnesses @ ..] => bail!(NotThreeWitnesses(witnesses.len())),
witnesses => bail!(NotThreeWitnesses(witnesses.len())),
}?;
let sig = sigs

View file

@ -65,9 +65,7 @@ impl Wallet {
database,
) {
Ok(w) => w,
Err(e) if matches!(e, bdk::Error::ChecksumMismatch) => {
Self::migrate(data_dir, xprivkey, network)?
}
Err(bdk::Error::ChecksumMismatch) => Self::migrate(data_dir, xprivkey, network)?,
err => err?,
};

View file

@ -168,7 +168,7 @@ impl EventLoop {
tracing::info!("Successfully closed connection to Alice");
return;
}
SwarmEvent::OutgoingConnectionError { peer_id, error } if matches!(peer_id, Some(alice_peer_id) if alice_peer_id == self.alice_peer_id) => {
SwarmEvent::OutgoingConnectionError { peer_id: Some(alice_peer_id), error } if alice_peer_id == self.alice_peer_id => {
tracing::warn!(%error, "Failed to dial Alice");
if let Some(duration) = self.swarm.behaviour_mut().redial.until_next_redial() {

View file

@ -19,7 +19,7 @@ pub struct CborCodec<P, Req, Res> {
impl<P, Req, Res> Default for CborCodec<P, Req, Res> {
fn default() -> Self {
Self {
phantom: PhantomData::default(),
phantom: PhantomData,
}
}
}

View file

@ -25,7 +25,7 @@ pub struct JsonPullCodec<P, Res> {
impl<P, Res> Default for JsonPullCodec<P, Res> {
fn default() -> Self {
Self {
phantom: PhantomData::default(),
phantom: PhantomData,
}
}
}

View file

@ -40,7 +40,7 @@ where
.expect("failed to create dh_keys");
let noise = NoiseConfig::xx(dh_keys).into_authenticated();
let transport = MemoryTransport::default()
let transport = MemoryTransport
.or_transport(TokioTcpConfig::new())
.upgrade(Version::V1)
.authenticate(noise)

View file

@ -310,7 +310,8 @@ impl State2 {
self.a.public(),
self.B,
self.tx_cancel_fee,
);
)
.expect("valid cancel tx");
let tx_refund =
bitcoin::TxRefund::new(&tx_cancel, &self.refund_address, self.tx_refund_fee);
@ -335,7 +336,7 @@ impl State2 {
self.a.public(),
self.B,
self.tx_cancel_fee,
);
)?;
bitcoin::verify_sig(&self.B, &tx_cancel.digest(), &msg.tx_cancel_sig)
.context("Failed to verify cancel transaction")?;
let tx_punish = bitcoin::TxPunish::new(
@ -458,6 +459,7 @@ impl State3 {
self.B,
self.tx_cancel_fee,
)
.expect("valid cancel tx")
}
pub fn tx_refund(&self) -> TxRefund {

View file

@ -243,7 +243,7 @@ impl State1 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_refund =
bitcoin::TxRefund::new(&tx_cancel, &self.refund_address, self.tx_refund_fee);
@ -316,7 +316,8 @@ impl State2 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)
.expect("valid cancel tx");
let tx_cancel_sig = self.b.sign(tx_cancel.digest());
let tx_punish = bitcoin::TxPunish::new(
&tx_cancel,
@ -450,7 +451,7 @@ impl State3 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?;
let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?;
@ -531,7 +532,7 @@ impl State4 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?;
let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?;
@ -612,7 +613,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_lock_status = bitcoin_wallet.status_of_script(&self.tx_lock).await?;
let tx_cancel_status = bitcoin_wallet.status_of_script(&tx_cancel).await?;
@ -635,7 +636,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx = bitcoin_wallet.get_raw_transaction(tx_cancel.txid()).await?;
@ -652,7 +653,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
)
)?
.complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone())
.context("Failed to complete Bitcoin cancel transaction")?;
@ -675,7 +676,7 @@ impl State6 {
self.A,
self.b.public(),
self.tx_cancel_fee,
);
)?;
let tx_refund =
bitcoin::TxRefund::new(&tx_cancel, &self.refund_address, self.tx_refund_fee);