run signing session

This commit is contained in:
conduition
2024-03-18 20:00:58 +00:00
parent a4206b2e22
commit 2bd8d4fed5
7 changed files with 172 additions and 5 deletions

1
Cargo.lock generated
View File

@@ -106,7 +106,6 @@ version = "0.1.0"
dependencies = [
"bitcoin",
"dlctix",
"secp",
"serde",
]

View File

@@ -8,5 +8,4 @@ edition = "2021"
[dependencies]
bitcoin = "0.31.1"
dlctix = { version = "0.0.4", path = "../.." }
secp = { version = "0.2.3", features = ["serde"] }
serde = "1.0.197"

View File

@@ -1,6 +1,7 @@
use bitcoin::Amount;
use dlctix::{ContractParameters, EventAnnouncement, Outcome};
use secp::Point;
use dlctix::musig2::AggNonce;
use dlctix::secp::Point;
use dlctix::{ContractParameters, ContractSignatures, EventAnnouncement, Outcome, SigMap};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
@@ -49,3 +50,13 @@ pub enum ServerOfferAck {
Ok,
Retry,
}
#[derive(Serialize, Deserialize)]
pub enum ServerNonceAck {
Ok(SigMap<AggNonce>),
}
#[derive(Serialize, Deserialize)]
pub enum ServerSignatureAck {
Ok(ContractSignatures),
}

View File

@@ -29,6 +29,7 @@ pub(crate) struct PlayerRegistration {
pub(crate) enum Stage {
IntentRegistry,
OfferAndAck,
SigningSession,
}
pub(crate) struct GlobalState {

View File

@@ -1,5 +1,6 @@
mod handshake;
mod offer_and_ack;
mod signing_session;
use crate::errors::WrongStageError;
use crate::global_state::{GlobalState, Stage};
@@ -42,7 +43,20 @@ fn handle_tcp_conn(
}
if let Some(accepted_offers) = offer_and_ack::offer_and_ack_cycle(&state)? {
// TODO prompt all players for signatures
{
let mut state_wlock = state.write().unwrap();
state_wlock.stage = Stage::SigningSession;
}
let (funding_tx, signed_contract) =
signing_session::run_signing_sessions(&state, accepted_offers)?;
// TODO:
// - store signed contract offline
// - accept prepayments atomically
// - broadcast funding TX and await confs
// - sell ticket preimages atomically
// - watch blockchain and resolve contract
}
}

View File

@@ -0,0 +1,142 @@
use bitcoin::{absolute::LockTime, OutPoint, Transaction};
use bitcoincore_rpc::RpcApi;
use crate::global_state::GlobalState;
use common::{PlayerID, ServerNonceAck, ServerOffer, ServerOfferAck, ServerSignatureAck};
use dlctix::musig2::{PartialSignature, PubNonce};
use dlctix::secp::Point;
use dlctix::{SigMap, SignedContract, SigningSession, TicketedDLC};
use std::{
collections::BTreeMap,
error::Error,
sync::{Arc, RwLock},
thread,
};
pub(crate) fn run_signing_sessions(
state: &Arc<RwLock<GlobalState>>,
accepted_offers: BTreeMap<PlayerID, ServerOffer>,
) -> Result<(Transaction, SignedContract), Box<dyn Error>> {
let contract_parameters = accepted_offers
.values()
.next()
.unwrap()
.contract_parameters
.clone();
// Create, fund, and sign the funding transaction using
// bitcoind's current loaded wallet.
let funding_tx = {
let skeleton_tx = Transaction {
version: bitcoin::transaction::Version::TWO,
lock_time: LockTime::ZERO,
input: vec![],
output: vec![contract_parameters.funding_output()?],
};
let state_rlock = state.read().unwrap();
let funded_tx = state_rlock
.bitcoind
.fund_raw_transaction(&skeleton_tx, None, Some(true))?
.hex;
state_rlock
.bitcoind
.sign_raw_transaction_with_wallet(&funded_tx, None, None)?
.transaction()?
};
let funding_outpoint = OutPoint {
txid: funding_tx.txid(),
vout: 0,
};
let ticketed_dlc = TicketedDLC::new(contract_parameters, funding_outpoint)?;
let signing_session = SigningSession::new(
ticketed_dlc,
&mut rand::thread_rng(),
state.read().unwrap().market_maker_seckey,
)?;
// Round 1: receive nonces.
let thread_handles: Vec<thread::JoinHandle<_>> = accepted_offers
.keys()
.map(|&player_id| {
let state = Arc::clone(state);
thread::spawn(move || -> serde_cbor::Result<(Point, SigMap<PubNonce>)> {
let state_rlock = state.read().unwrap();
let conn = &state_rlock.registrations[&player_id].connection;
let pubkey = state_rlock.registrations[&player_id].player.pubkey;
serde_cbor::to_writer(conn, &ServerOfferAck::Ok)?;
let nonces: SigMap<PubNonce> = serde_cbor::from_reader(conn)?;
Ok((pubkey, nonces))
})
})
.collect();
// TODO tell clients if someone failed to share nonces and we have to retry.
let received_nonces: BTreeMap<Point, SigMap<PubNonce>> = thread_handles
.into_iter()
.map(|handle| handle.join().unwrap())
.collect::<Result<_, serde_cbor::Error>>()?;
// TODO handle invalid (incomplete) sets of nonces
let signing_session = signing_session.compute_partial_signatures(received_nonces)?;
// Round 2: distribute agg nonces and receive partial signatures
let thread_handles: Vec<thread::JoinHandle<_>> = accepted_offers
.keys()
.map(|&player_id| {
let state = Arc::clone(state);
let agg_nonces = signing_session.aggregated_nonces().clone();
thread::spawn(
move || -> serde_cbor::Result<(Point, SigMap<PartialSignature>)> {
let state_rlock = state.read().unwrap();
let conn = &state_rlock.registrations[&player_id].connection;
let pubkey = state_rlock.registrations[&player_id].player.pubkey;
serde_cbor::to_writer(conn, &ServerNonceAck::Ok(agg_nonces))?;
let partial_sigs: SigMap<PartialSignature> = serde_cbor::from_reader(conn)?;
Ok((pubkey, partial_sigs))
},
)
})
.collect();
let received_signatures: BTreeMap<Point, SigMap<PartialSignature>> = thread_handles
.into_iter()
.map(|handle| handle.join().unwrap())
.collect::<Result<_, serde_cbor::Error>>()?;
// TODO assign blame
for (&signer, partial_sigs) in &received_signatures {
signing_session.verify_partial_signatures(signer, partial_sigs)?;
}
let signed_contract = signing_session.aggregate_all_signatures(received_signatures)?;
// Final round: distribute aggregated signatures.
let thread_handles: Vec<thread::JoinHandle<_>> = accepted_offers
.keys()
.map(|&player_id| {
let state = Arc::clone(state);
// TODO send only pruned signatures to peers
let signatures = signed_contract.all_signatures().clone();
thread::spawn(move || -> serde_cbor::Result<()> {
let state_rlock = state.read().unwrap();
let conn = &state_rlock.registrations[&player_id].connection;
serde_cbor::to_writer(conn, &ServerSignatureAck::Ok(signatures))
})
})
.collect();
// ensure all players receive their signatures
for handle in thread_handles {
handle.join().unwrap()?;
}
Ok((funding_tx, signed_contract))
}

View File

@@ -12,6 +12,7 @@ pub(crate) mod spend_info;
pub mod hashlock;
pub use musig2;
pub use secp;
use contract::{