From bef816c38d288402b945d5e9450b320cb8326a39 Mon Sep 17 00:00:00 2001 From: Ekrem BAL Date: Fri, 8 Dec 2023 14:28:01 +0300 Subject: [PATCH 1/5] equivocation works --- src/actor.rs | 17 ++++++- src/main.rs | 130 +++++++++++++++++++++++++++------------------------ src/wire.rs | 7 ++- 3 files changed, 88 insertions(+), 66 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index a7f5c66..7d2e64a 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -1,5 +1,6 @@ use bitcoin::hashes::sha256; use bitcoin::opcodes::all::*; +use bitcoin::TapNodeHash; use bitcoin::{ hashes::Hash, script::Builder, @@ -53,20 +54,32 @@ impl Actor { .into_script() } - pub fn sign(&self, sighash: TapSighash) -> Signature { + pub fn sign_with_tweak( + &self, + sighash: TapSighash, + merkle_root: Option, + ) -> Signature { self.secp.sign_schnorr_with_rng( &Message::from_digest_slice(sighash.as_byte_array()).expect("should be hash"), &self .keypair .add_xonly_tweak( &self.secp, - &TapTweakHash::from_key_and_tweak(self.public_key, None).to_scalar(), + &TapTweakHash::from_key_and_tweak(self.public_key, merkle_root).to_scalar(), ) .unwrap(), &mut rand::thread_rng(), ) } + pub fn sign(&self, sighash: TapSighash) -> Signature { + self.secp.sign_schnorr_with_rng( + &Message::from_digest_slice(sighash.as_byte_array()).expect("should be hash"), + &self.keypair, + &mut rand::thread_rng(), + ) + } + pub fn generate_challenge_hashes(&mut self, num_gates: usize) -> Vec<[u8; 32]> { let mut challenge_hashes = Vec::new(); let mut rng = rand::thread_rng(); diff --git a/src/main.rs b/src/main.rs index 4ff0524..85602b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,17 @@ use bitcoin::absolute::{Height, LockTime}; use bitcoin::consensus::encode::serialize_hex; use bitcoin::consensus::Decodable; -use bitcoin::hash_types::Txid; -use bitcoin::secp256k1::{All, Secp256k1}; + +use bitcoin::secp256k1::{Secp256k1}; use bitcoin::sighash::SighashCache; -use bitcoin::taproot::{LeafVersion, TaprootSpendInfo}; -use bitcoin::{Amount, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Witness}; +use bitcoin::taproot::{LeafVersion}; +use bitcoin::{Amount, OutPoint, ScriptBuf, TapLeafHash, Transaction, TxIn, TxOut, Witness}; use bitcoincore_rpc::{Auth, Client, RpcApi}; use bitvm::actor::Actor; use bitvm::traits::wire::WireTrait; -use bitvm::wire::Wire; + use bitvm::{circuit::Circuit, traits::circuit::CircuitTrait}; use std::borrow::BorrowMut; @@ -28,51 +28,6 @@ pub fn parse_hex_transaction( } } -pub fn use_equivocation( - _secp: Secp256k1, - txid: Txid, - verifier: &Actor, - wire: Wire, - info: TaprootSpendInfo, -) { - let vout: u32 = 0; - - let script = wire.generate_anti_contradiction_script(verifier.public_key); - - let mut tx = Transaction { - version: bitcoin::transaction::Version::TWO, - lock_time: LockTime::from(Height::MIN), - input: vec![TxIn { - previous_output: OutPoint { txid, vout }, - script_sig: ScriptBuf::new(), - sequence: bitcoin::transaction::Sequence::ENABLE_RBF_NO_LOCKTIME, - witness: Witness::new(), - }], - output: vec![TxOut { - script_pubkey: verifier.address.script_pubkey(), - value: Amount::from_sat(9000), - }], - }; - - let mut sighash_cache = SighashCache::new(tx.borrow_mut()); - - let control_block = info - .control_block(&(script.clone(), LeafVersion::TapScript)) - .expect("Cannot create control block"); - - let witness = sighash_cache.witness_mut(0).unwrap(); - witness.push(wire.preimages.unwrap()[1]); - witness.push(wire.preimages.unwrap()[0]); - witness.push(script); - witness.push(&control_block.serialize()); - - // println!("sigHash : {:?}", sig_hash); - // println!("tx : {:?}", tx); - println!("equivocation"); - println!("txid : {:?}", tx.txid()); - println!("txid : {:?}", serialize_hex(&tx)); -} - fn main() { // if rpc feature is enabled, use the following code to connect to a bitcoin node let rpc = Client::new( @@ -117,7 +72,8 @@ fn main() { let challenge_hashes = vicky.generate_challenge_hashes(circuit.num_gates()); - let (address, info) = circuit.generate_challenge_tree(&secp, &paul, &vicky, challenge_hashes); + let (address, kickoff_taproot_info) = + circuit.generate_challenge_tree(&secp, &paul, &vicky, challenge_hashes); let mut tx = Transaction { version: bitcoin::transaction::Version::TWO, @@ -154,25 +110,79 @@ fn main() { .unwrap(); // Witness::from_slice(sigHash) - let sig = paul.sign(sig_hash); + let sig = paul.sign_with_tweak(sig_hash, None); let witness = sighash_cache.witness_mut(0).unwrap(); witness.push(sig.as_ref()); println!("txid : {:?}", serialize_hex(&tx)); - let initial_tx = rpc + let kickoff_tx = rpc .send_raw_transaction(&tx) .unwrap_or_else(|e| panic!("Failed to send raw transaction: {}", e)); - println!("initial tx = {:?}", initial_tx); + println!("initial kickoff tx = {:?}", kickoff_tx); // let mut txid_str: [u8]; // tx.consensus_encode().unwrap(); - let use_eq = 1; + let wire_rcref = &circuit.wires[0]; + let wire = wire_rcref.try_borrow_mut().unwrap(); - if use_eq > 0 { - let wire_rcref = &circuit.wires[0]; - let wire = wire_rcref.try_borrow_mut().unwrap(); - use_equivocation(secp, tx.txid(), &vicky, wire.to_owned(), info); - } + let vout: u32 = 0; + + let script = wire.generate_anti_contradiction_script(vicky.public_key); + + let mut tx = Transaction { + version: bitcoin::transaction::Version::TWO, + lock_time: LockTime::from(Height::MIN), + input: vec![TxIn { + previous_output: OutPoint { + txid: kickoff_tx, + vout, + }, + script_sig: ScriptBuf::new(), + sequence: bitcoin::transaction::Sequence::ENABLE_RBF_NO_LOCKTIME, + witness: Witness::new(), + }], + output: vec![TxOut { + script_pubkey: vicky.address.script_pubkey(), + value: Amount::from_sat(9000), + }], + }; + + let mut sighash_cache = SighashCache::new(tx.borrow_mut()); + + let prevouts = vec![TxOut { + script_pubkey: address.script_pubkey(), + value: Amount::from_sat(amt - 500), + }]; + + let sig_hash = sighash_cache + .taproot_script_spend_signature_hash( + 0, + &bitcoin::sighash::Prevouts::All(&prevouts), + TapLeafHash::from_script(&script, LeafVersion::TapScript), + bitcoin::sighash::TapSighashType::Default, + ) + .unwrap(); + + let sig = vicky.sign(sig_hash); + + let control_block = kickoff_taproot_info + .control_block(&(script.clone(), LeafVersion::TapScript)) + .expect("Cannot create control block"); + + let witness = sighash_cache.witness_mut(0).unwrap(); + witness.push(sig.as_ref()); + witness.push(wire.preimages.unwrap()[1]); + witness.push(wire.preimages.unwrap()[0]); + witness.push(script); + witness.push(&control_block.serialize()); + + println!("equivocation"); + println!("txid : {:?}", tx.txid()); + println!("txid : {:?}", serialize_hex(&tx)); + let eqv_tx = rpc + .send_raw_transaction(&tx) + .unwrap_or_else(|e| panic!("Failed to send raw transaction: {}", e)); + println!("eqv tx = {:?}", eqv_tx); } diff --git a/src/wire.rs b/src/wire.rs index f28f6e7..8911b0b 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -49,7 +49,7 @@ impl Wire { } impl WireTrait for Wire { - fn generate_anti_contradiction_script(&self, _verifier_pk: XOnlyPublicKey) -> ScriptBuf { + fn generate_anti_contradiction_script(&self, verifier_pk: XOnlyPublicKey) -> ScriptBuf { Builder::new() .push_opcode(OP_SHA256) .push_slice(self.hashes[0]) @@ -57,9 +57,8 @@ impl WireTrait for Wire { .push_opcode(OP_SHA256) .push_slice(self.hashes[1]) .push_opcode(OP_EQUALVERIFY) - //.push_x_only_key(&verifier_pk) - //.push_opcode(OP_CHECKSIGVERIFY) - .push_int(1) + .push_x_only_key(&verifier_pk) + .push_opcode(OP_CHECKSIG) .into_script() } From d93e19ea034254c48dc97b452d1b816154f441fc Mon Sep 17 00:00:00 2001 From: Ekrem BAL Date: Fri, 8 Dec 2023 14:28:12 +0300 Subject: [PATCH 2/5] Nits --- src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 85602b8..cd9a166 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,16 +2,15 @@ use bitcoin::absolute::{Height, LockTime}; use bitcoin::consensus::encode::serialize_hex; use bitcoin::consensus::Decodable; -use bitcoin::secp256k1::{Secp256k1}; +use bitcoin::secp256k1::Secp256k1; use bitcoin::sighash::SighashCache; -use bitcoin::taproot::{LeafVersion}; +use bitcoin::taproot::LeafVersion; use bitcoin::{Amount, OutPoint, ScriptBuf, TapLeafHash, Transaction, TxIn, TxOut, Witness}; use bitcoincore_rpc::{Auth, Client, RpcApi}; use bitvm::actor::Actor; use bitvm::traits::wire::WireTrait; - use bitvm::{circuit::Circuit, traits::circuit::CircuitTrait}; use std::borrow::BorrowMut; From 409ecc7cc6e90fbc51cef16993c38cfb7d510dc3 Mon Sep 17 00:00:00 2001 From: Ekrem BAL Date: Fri, 8 Dec 2023 15:02:56 +0300 Subject: [PATCH 3/5] minor --- src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index cd9a166..9a65a46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -157,13 +157,12 @@ fn main() { let sig_hash = sighash_cache .taproot_script_spend_signature_hash( - 0, + vout as usize, &bitcoin::sighash::Prevouts::All(&prevouts), TapLeafHash::from_script(&script, LeafVersion::TapScript), bitcoin::sighash::TapSighashType::Default, ) .unwrap(); - let sig = vicky.sign(sig_hash); let control_block = kickoff_taproot_info From bd04d5266f5af84bafdb6e13f974d2593529b924 Mon Sep 17 00:00:00 2001 From: Ekrem BAL Date: Fri, 8 Dec 2023 16:27:00 +0300 Subject: [PATCH 4/5] Websocket working --- Cargo.toml | 14 ++++++++++++++ src/circuit.rs | 10 ++++++++++ src/communication.rs | 35 +++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/prover.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/traits/circuit.rs | 2 ++ src/traits/wire.rs | 1 + src/verifier.rs | 37 +++++++++++++++++++++++++++++++++++++ src/wire.rs | 4 ++++ 9 files changed, 146 insertions(+) create mode 100644 src/communication.rs create mode 100644 src/prover.rs create mode 100644 src/verifier.rs diff --git a/Cargo.toml b/Cargo.toml index c886a71..6b23602 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,13 @@ bitcoin = {version = "0.31.0", features = ["rand"]} bitcoincore-rpc = {version = "0.18.0" } hex = "0.4.3" rand = "0.8.5" +tokio = { version = "1", features = ["full"] } +tokio-tungstenite = "0.15" +url = "2.2.0" +futures-util = "0.3" +serde = "1.0.193" +serde_json = "1.0.108" + [dev-dependencies] bitcoin-scriptexec = { git = "https://github.com/ekrembal/rust-bitcoin-scriptexec" } @@ -18,3 +25,10 @@ bitcoin-scriptexec = { git = "https://github.com/ekrembal/rust-bitcoin-scriptexe # rpc = ["bitcoincore-rpc"] +[[bin]] +name = "prover" +path = "src/prover.rs" + +[[bin]] +name = "verifier" +path = "src/verifier.rs" diff --git a/src/circuit.rs b/src/circuit.rs index e7bbfa3..c64f18b 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -82,6 +82,16 @@ impl CircuitTrait for Circuit { output } + fn get_wire_hashes(&self) -> Vec<[[u8; 32]; 2]> { + self.wires + .iter() + .map(|wire_rcref| { + let wire = wire_rcref.try_borrow_mut().unwrap(); + wire.get_hash_pair() + }) + .collect::>() + } + fn from_bristol(file: &str) -> Self { let mut nog: usize = 0; // number of gates let mut now: usize = 0; // number of wires diff --git a/src/communication.rs b/src/communication.rs new file mode 100644 index 0000000..3f12841 --- /dev/null +++ b/src/communication.rs @@ -0,0 +1,35 @@ +// communication.rs +use futures_util::{SinkExt, StreamExt}; +use serde::{Deserialize, Serialize}; +use std::error::Error; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_tungstenite::tungstenite::protocol::Message; +use tokio_tungstenite::WebSocketStream; + +pub async fn send_message( + ws_stream: &mut WebSocketStream, + message: &M, +) -> Result<(), Box> +where + T: AsyncRead + AsyncWrite + Unpin, + M: Serialize, +{ + let serialized = serde_json::to_string(message)?; + ws_stream.send(Message::Text(serialized)).await?; + Ok(()) +} + +pub async fn receive_message(ws_stream: &mut WebSocketStream) -> Result> +where + T: AsyncRead + AsyncWrite + Unpin, + M: for<'de> Deserialize<'de>, +{ + if let Some(msg) = ws_stream.next().await { + let msg = msg?; + if let Message::Text(text) = msg { + let deserialized: M = serde_json::from_str(&text)?; + return Ok(deserialized); + } + } + Err("Failed to receive message".into()) +} diff --git a/src/lib.rs b/src/lib.rs index 0b7c421..89f0e94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod actor; pub mod circuit; +pub mod communication; pub mod gates; pub mod traits; pub mod utils; diff --git a/src/prover.rs b/src/prover.rs new file mode 100644 index 0000000..77d723e --- /dev/null +++ b/src/prover.rs @@ -0,0 +1,42 @@ +use bitcoin::XOnlyPublicKey; + +// prover.rs +use bitvm::{ + circuit::Circuit, + communication::{receive_message, send_message}, + traits::circuit::CircuitTrait, +}; + + +use tokio_tungstenite::{connect_async}; + +// #[derive(Serialize, Deserialize, Debug)] +// struct WireHash { +// zero: [u8; 32], +// one: [u8; 32], +// } +// #[derive(Serialize, Deserialize, Debug)] +// struct WireHashes { +// wire_hashes: Vec, +// } + +#[tokio::main] +async fn main() { + let url = "ws://127.0.0.1:9000"; + let (mut ws_stream, _) = connect_async(url).await.expect("Failed to connect"); + println!("WebSocket handshake has been successfully completed"); + + send_message(&mut ws_stream, &"bristol/add.txt".to_string()) + .await + .unwrap(); + + let verifier_publickey_str: String = receive_message(&mut ws_stream).await.unwrap(); + println!("Verifier public key: {}", verifier_publickey_str); + let verifier_publickey: XOnlyPublicKey = verifier_publickey_str.parse().unwrap(); + println!("Verifier public key: {}", verifier_publickey); + + let circuit = Circuit::from_bristol("bristol/add.txt"); + let wire_hashes: Vec<[[u8; 32]; 2]> = circuit.get_wire_hashes(); + + send_message(&mut ws_stream, &wire_hashes).await.unwrap(); +} diff --git a/src/traits/circuit.rs b/src/traits/circuit.rs index 52d42fb..6e88795 100644 --- a/src/traits/circuit.rs +++ b/src/traits/circuit.rs @@ -11,6 +11,8 @@ pub trait CircuitTrait { fn evaluate(&mut self, inputs: Vec>) -> Vec>; + fn get_wire_hashes(&self) -> Vec<[[u8; 32]; 2]>; + fn from_bristol(file: &str) -> Self; fn generate_challenge_tree( diff --git a/src/traits/wire.rs b/src/traits/wire.rs index 1d6c1c4..bd836ba 100644 --- a/src/traits/wire.rs +++ b/src/traits/wire.rs @@ -1,6 +1,7 @@ use bitcoin::{script::Builder, ScriptBuf, XOnlyPublicKey}; pub trait WireTrait { + fn get_hash_pair(&self) -> [[u8; 32]; 2]; fn generate_anti_contradiction_script(&self, verifier_pk: XOnlyPublicKey) -> ScriptBuf; fn add_bit_commitment_script(&self, builder: Builder) -> Builder; } diff --git a/src/verifier.rs b/src/verifier.rs new file mode 100644 index 0000000..4dc41a3 --- /dev/null +++ b/src/verifier.rs @@ -0,0 +1,37 @@ +// verifier.rs +use bitvm::{ + actor::Actor, + communication::{receive_message, send_message}, +}; +use tokio::net::{TcpListener, TcpStream}; +use tokio_tungstenite::{accept_async}; + +#[tokio::main] +async fn main() { + let listener = TcpListener::bind("127.0.0.1:9000").await.unwrap(); + println!("Listening on: 127.0.0.1:9000"); + + while let Ok((stream, _)) = listener.accept().await { + tokio::spawn(handle_connection(stream)); + } +} + +async fn handle_connection(stream: TcpStream) { + let mut ws_stream = accept_async(stream) + .await + .expect("Error during the websocket handshake occurred"); + + let message: String = receive_message(&mut ws_stream).await.unwrap(); + println!("Received: {}", message); + + let verifier = Actor::new(); + + // send our public key to the prover + send_message(&mut ws_stream, &verifier.public_key.to_string()) + .await + .unwrap(); + + let wire_hashes: Vec<[[u8; 32]; 2]> = receive_message(&mut ws_stream).await.unwrap(); + + println!("Wire hashes: {:?}", wire_hashes); +} diff --git a/src/wire.rs b/src/wire.rs index 8911b0b..3821dc7 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -49,6 +49,10 @@ impl Wire { } impl WireTrait for Wire { + fn get_hash_pair(&self) -> [[u8; 32]; 2] { + self.hashes + } + fn generate_anti_contradiction_script(&self, verifier_pk: XOnlyPublicKey) -> ScriptBuf { Builder::new() .push_opcode(OP_SHA256) From be1fee593ffa6bd74b44055ff3f476c533a063d0 Mon Sep 17 00:00:00 2001 From: Ekrem BAL Date: Fri, 8 Dec 2023 16:27:17 +0300 Subject: [PATCH 5/5] nits --- src/prover.rs | 3 +-- src/verifier.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 77d723e..f0aa8fe 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -7,8 +7,7 @@ use bitvm::{ traits::circuit::CircuitTrait, }; - -use tokio_tungstenite::{connect_async}; +use tokio_tungstenite::connect_async; // #[derive(Serialize, Deserialize, Debug)] // struct WireHash { diff --git a/src/verifier.rs b/src/verifier.rs index 4dc41a3..bf0d083 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -4,7 +4,7 @@ use bitvm::{ communication::{receive_message, send_message}, }; use tokio::net::{TcpListener, TcpStream}; -use tokio_tungstenite::{accept_async}; +use tokio_tungstenite::accept_async; #[tokio::main] async fn main() {