From 8f84330b5dc95772e20df8fedb6a75404b5b795e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orkun=20Mahir=20K=C4=B1l=C4=B1=C3=A7?= Date: Fri, 2 Feb 2024 14:48:28 +0300 Subject: [PATCH] Clean Up (#37) * fix file structure * fix file structure * Revert "fix file structure" This reverts commit 7ff9b607dde4faf79061584f5b817f822060c365. * lint * fix inputs * remove unnecessary make * clean prints * lint * Exit after slashing --------- Co-authored-by: Ekrem BAL --- Cargo.toml | 6 +- LICENSE | 21 +++ Makefile | 12 +- README.md | 20 ++- src/actor.rs | 2 +- src/{ => circuit}/gates.rs | 10 +- src/{circuit.rs => circuit/mod.rs} | 11 +- src/{ => circuit}/wire.rs | 0 src/communication.rs | 1 - src/lib.rs | 2 - src/main.rs | 179 ------------------- src/prover.rs | 34 ++-- src/traits/gate.rs | 2 +- src/{transactions.rs => transactions/mod.rs} | 2 +- src/verifier.rs | 42 +++-- 15 files changed, 98 insertions(+), 246 deletions(-) create mode 100644 LICENSE rename src/{ => circuit}/gates.rs (98%) rename src/{circuit.rs => circuit/mod.rs} (97%) rename src/{ => circuit}/wire.rs (100%) delete mode 100644 src/main.rs rename src/{transactions.rs => transactions/mod.rs} (99%) diff --git a/Cargo.toml b/Cargo.toml index 6b23602..f260146 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "bitvm" +name = "toy-bitvm" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bitcoin = {version = "0.31.0", features = ["rand"]} -bitcoincore-rpc = {version = "0.18.0" } +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"] } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..69271ae --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Chainway Limited + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile index d949082..232a49f 100644 --- a/Makefile +++ b/Makefile @@ -12,4 +12,14 @@ fix: check: cargo fmt -- --check - cargo clippy -- -D warnings \ No newline at end of file + cargo clippy -- -D warnings + +lint: + cargo +nightly fmt --all --check + cargo check --all-targets --all-features + cargo clippy --all-targets --all-features + +lint-fix: + cargo +nightly fmt --all + cargo fix --allow-dirty + cargo clippy --fix --allow-dirty \ No newline at end of file diff --git a/README.md b/README.md index 7f76746..4b694ef 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,29 @@ -# BitVM.rs +# Toy BitVM in Rust -Experimental BitVM implementation in Rust. +Experimental toy BitVM implementation in Rust. It is recommended to always use [cargo-crev](https://github.com/crev-dev/cargo-crev) to verify the trustworthiness of each of your dependencies, including this one. - +Run regtest with the following command: ``` bitcoind -regtest -rpcuser=admin -rpcpassword=admin -rpcport=18443 -fallbackfee=0.00001 -wallet=admin ``` +Then run the following command to generate blocks continuously: ``` -bitcoin-cli -regtest -rpcuser=admin -rpcpassword=admin createwallet "admin" +sh ./regtest-commands.sh ``` +Then start the verifier binary with the following command: +``` +cargo run --bin verifier ``` -bitcoin-cli -regtest -rpcuser=admin -rpcpassword=admin generatetoaddress 101 $(bitcoin-cli -regtest -rpcuser=admin -rpcpassword=admin getnewaddress) + +Start the prover binary with the following command in another terminal: ``` +cargo run --bin prover +``` + +From now on, you can start challenging gates and waiting for the prover to respond. +There is a fraud hardcoded in the code. Challenge `64` for first, then `63` to see the fraud and slash the prover. diff --git a/src/actor.rs b/src/actor.rs index 14f48a7..75603ef 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -9,7 +9,7 @@ use bitcoin::{ }; use rand::Rng; -use crate::wire::{HashValue, PreimageValue}; +use crate::circuit::wire::{HashValue, PreimageValue}; pub struct Actor { secp: Secp256k1, diff --git a/src/gates.rs b/src/circuit/gates.rs similarity index 98% rename from src/gates.rs rename to src/circuit/gates.rs index 92abe0c..b7685f3 100644 --- a/src/gates.rs +++ b/src/circuit/gates.rs @@ -4,12 +4,9 @@ use bitcoin::opcodes::all::{ use bitcoin::script::Builder; use bitcoin::ScriptBuf; +use super::wire::{HashValue, Wire}; +use crate::traits::gate::{GateTrait, Wires}; use crate::transactions::add_bit_commitment_script; -use crate::wire::HashValue; -use crate::{ - traits::gate::{GateTrait, Wires}, - wire::Wire, -}; use std::sync::{Arc, Mutex}; @@ -225,8 +222,7 @@ pub fn create_gate( #[cfg(test)] mod tests { - use crate::wire::PreimageValue; - + use super::super::wire::PreimageValue; use super::*; use bitcoin::hashes::sha256; use bitcoin::hashes::Hash; diff --git a/src/circuit.rs b/src/circuit/mod.rs similarity index 97% rename from src/circuit.rs rename to src/circuit/mod.rs index b41fa08..b25e551 100644 --- a/src/circuit.rs +++ b/src/circuit/mod.rs @@ -1,12 +1,15 @@ +pub mod gates; +pub mod wire; + use std::collections::BTreeMap; use std::iter::zip; use std::sync::{Arc, Mutex}; -use crate::gates::create_gate; -use crate::wire::HashTuple; +use gates::create_gate; +use wire::{HashTuple, Wire}; -use crate::{traits::gate::GateTrait, utils::read_lines, wire::Wire}; +use crate::{traits::gate::GateTrait, utils::read_lines}; pub struct Circuit { pub input_sizes: Vec, @@ -212,7 +215,7 @@ mod tests { let b2 = number_to_bool_array(a2, 64); let o = circuit.evaluate(vec![b1, b2]); - let output = bool_array_to_number(o.get(0).unwrap().to_vec()); + let output = bool_array_to_number(o.first().unwrap().to_vec()); assert_eq!(output, a1 + a2); } } diff --git a/src/wire.rs b/src/circuit/wire.rs similarity index 100% rename from src/wire.rs rename to src/circuit/wire.rs diff --git a/src/communication.rs b/src/communication.rs index 3f12841..5960970 100644 --- a/src/communication.rs +++ b/src/communication.rs @@ -1,4 +1,3 @@ -// communication.rs use futures_util::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; use std::error::Error; diff --git a/src/lib.rs b/src/lib.rs index ce1edc2..32cfa61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,6 @@ pub mod actor; pub mod circuit; pub mod communication; -pub mod gates; pub mod traits; pub mod transactions; pub mod utils; -pub mod wire; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index b264481..0000000 --- a/src/main.rs +++ /dev/null @@ -1,179 +0,0 @@ -use bitcoin::consensus::Decodable; - -use bitcoin::Transaction; - -pub fn parse_hex_transaction( - tx_hex: &str, -) -> Result { - if let Ok(reader) = hex::decode(tx_hex) { - Transaction::consensus_decode(&mut &reader[..]) - } else { - Err(bitcoin::consensus::encode::Error::ParseFailed( - "Could not decode hex", - )) - } -} - -fn main() { - // if rpc feature is enabled, use the following code to connect to a bitcoin node - // let rpc = Client::new( - // "http://localhost:18443/wallet/admin", - // Auth::UserPass("admin".to_string(), "admin".to_string()), - // ) - // .unwrap_or_else(|e| panic!("Failed to connect to Bitcoin RPC: {}", e)); - - // let circuit = Circuit::from_bristol("bristol/add.txt", None); - - // let paul = Actor::new(); - // let mut vicky = Actor::new(); - // let secp = Secp256k1::new(); - // let amt = 10_000; - - // let initial_fund = rpc - // .send_to_address( - // &paul.address, - // Amount::from_sat(amt), - // None, - // None, - // None, - // None, - // None, - // None, - // ) - // .unwrap_or_else(|e| panic!("Failed to send to address: {}", e)); - // let initial_tx = rpc - // .get_transaction(&initial_fund, None) - // .unwrap_or_else(|e| panic!("Failed to get transaction: {}", e)); - - // println!("initial tx = {:?}", initial_tx); - - // // println!("Send {} satoshis to Public Key: {}", amt, paul.address); - // // let txid: Txid = take_stdin("Enter txid: ") - // // .parse() - // // .expect("invalid txid format"); - // // let vout: u32 = take_stdin("Enter vout: ") - // // .trim() - // // .parse() - // // .expect("invalid vout format"); - - // let challenge_hashes = vicky.generate_challenge_hashes(circuit.num_gates()); - - // let (address, kickoff_taproot_info) = generate_challenge_address_and_info( - // &secp, - // &circuit, - // paul.public_key, - // vicky.public_key, - // &challenge_hashes, - // ); - - // let mut tx = Transaction { - // version: bitcoin::transaction::Version::TWO, - // lock_time: LockTime::from(Height::MIN), - // input: vec![TxIn { - // previous_output: OutPoint { - // txid: initial_fund, - // vout: initial_tx.details[0].vout, - // }, - // script_sig: ScriptBuf::new(), - // sequence: bitcoin::transaction::Sequence::ENABLE_RBF_NO_LOCKTIME, - // witness: Witness::new(), - // }], - // output: vec![TxOut { - // script_pubkey: address.script_pubkey(), - // value: Amount::from_sat(amt - 500), - // }], - // }; - - // let prevouts = vec![TxOut { - // script_pubkey: paul.address.script_pubkey(), - // value: Amount::from_sat(amt), - // }]; - - // println!("prevout: {:?}", prevouts); - // let mut sighash_cache = SighashCache::new(tx.borrow_mut()); - // // TODO: add support for signing with a keypair - // let sig_hash = sighash_cache - // .taproot_key_spend_signature_hash( - // 0, - // &bitcoin::sighash::Prevouts::All(&prevouts), - // bitcoin::sighash::TapSighashType::Default, - // ) - // .unwrap(); - - // // Witness::from_slice(sigHash) - // 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 kickoff_tx = rpc - // .send_raw_transaction(&tx) - // .unwrap_or_else(|e| panic!("Failed to send raw transaction: {}", e)); - // println!("initial kickoff tx = {:?}", kickoff_tx); - - // // let mut txid_str: [u8]; - // // tx.consensus_encode().unwrap(); - - // let wire_rcref = &circuit.wires[0]; - // let wire = wire_rcref.lock().unwrap(); - - // let vout: u32 = 0; - - // let script = generate_anti_contradiction_script(wire.get_hash_pair(), 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( - // 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 - // .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().one); - // witness.push(wire.preimages.unwrap().zero); - // 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); - println!("Hello, world!"); -} diff --git a/src/prover.rs b/src/prover.rs index 0315a18..833edd3 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -12,20 +12,18 @@ use bitcoin::{secp256k1::Secp256k1, Amount, Transaction, XOnlyPublicKey}; use bitcoin::{OutPoint, ScriptBuf, TapLeafHash, TxIn, TxOut, Witness}; use bitcoincore_rpc::{Auth, Client, RpcApi}; -use bitvm::transactions::{ - generate_2_of_2_script, generate_equivoation_address_and_info, generate_gate_response_script, - generate_response_second_address_and_info, watch_transaction, -}; - -use bitvm::utils::number_to_bool_array; -use bitvm::wire::PreimageValue; -// prover.rs -use bitvm::{ +use toy_bitvm::{ actor::Actor, + circuit::wire::{HashTuple, HashValue, PreimageValue}, circuit::Circuit, communication::{receive_message, send_message}, + transactions::{ + generate_2_of_2_script, generate_equivoation_address_and_info, + generate_gate_response_script, generate_response_second_address_and_info, + watch_transaction, + }, transactions::{generate_challenge_address_and_info, generate_response_address_and_info}, - wire::{HashTuple, HashValue}, + utils::number_to_bool_array, }; use tokio_tungstenite::connect_async; @@ -114,7 +112,7 @@ async fn main() { }; for i in 0..bisection_length as u64 { - println!("Bisection iteration {}", i); + println!("Bisection iteration: {}", i); let challenge_hashes: Vec = receive_message(&mut ws_stream).await.unwrap(); prover.add_challenge_hashes(challenge_hashes.clone()); let (challenge_address, _) = generate_challenge_address_and_info( @@ -261,14 +259,14 @@ async fn main() { ) .unwrap(); let challenge_sig = prover.sign(sig_hash); - println!("challenge sig: {:?}", challenge_sig); + println!("Challenge Sig: {:?}", challenge_sig); send_message(&mut ws_stream, &challenge_sig).await.unwrap(); last_output = outputs2; last_txid = response_tx.txid(); } - println!("Bisection complete"); + println!("Bisection completed"); // now we send the funding let prevouts = vec![TxOut { @@ -278,7 +276,6 @@ async fn main() { // if kickoff_tx uninitialized, then panic - println!("prevout: {:?}", prevouts); let mut sighash_cache = SighashCache::new(kickoff_tx.borrow_mut()); // TODO: add support for signing with a keypair let sig_hash = sighash_cache @@ -299,7 +296,7 @@ async fn main() { let kickoff_txid = rpc .send_raw_transaction(&kickoff_tx) .unwrap_or_else(|e| panic!("Failed to send raw transaction: {}", e)); - println!("initial kickoff txid = {:?}", kickoff_txid); + println!("Initial kickoff txid: {:?}", kickoff_txid); send_message(&mut ws_stream, &kickoff_txid).await.unwrap(); let a1 = 633; @@ -461,11 +458,10 @@ async fn main() { .send_raw_transaction(&challenge_tx) .unwrap_or_else(|e| panic!("Failed to send raw transaction: {}", e)); - println!("Our response to the challenge"); - println!("txid : {:?}", challenge_txid); + println!("Responsing to the challenge, txid: {:?}", challenge_txid); // let _sig = verifier.sign(sig_hash); - println!("NOW WE GIVE THE RESPONSEEE"); + println!("Response given"); let a1 = 32; let a2 = 70; @@ -505,7 +501,7 @@ async fn main() { // println!("response txid: {:?}", response_tx.txid()); // Prover waits for challenge - println!("Waiting for challenge"); + println!("Waiting for a challenge..."); let challenge_tx = watch_transaction(&rpc, &response_tx.txid(), watch_interval).unwrap(); let preimage: &[u8; 32] = challenge_tx.input[0] .witness diff --git a/src/traits/gate.rs b/src/traits/gate.rs index a383182..cad83f9 100644 --- a/src/traits/gate.rs +++ b/src/traits/gate.rs @@ -1,4 +1,4 @@ -use crate::wire::{HashValue, PreimageValue, Wire}; +use crate::circuit::wire::{HashValue, PreimageValue, Wire}; use bitcoin::ScriptBuf; use std::{ iter::zip, diff --git a/src/transactions.rs b/src/transactions/mod.rs similarity index 99% rename from src/transactions.rs rename to src/transactions/mod.rs index 900f71f..184ba24 100644 --- a/src/transactions.rs +++ b/src/transactions/mod.rs @@ -10,8 +10,8 @@ use bitcoin::blockdata::script::Builder; use bitcoin::opcodes::all::*; use bitcoincore_rpc::{Client, RpcApi}; +use crate::circuit::wire::{HashTuple, HashValue}; use crate::traits::gate::GateTrait; -use crate::wire::{HashTuple, HashValue}; use crate::circuit::Circuit; diff --git a/src/verifier.rs b/src/verifier.rs index b4a15f2..39b36d8 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -10,23 +10,21 @@ use bitcoin::taproot::LeafVersion; use bitcoin::{secp256k1::Secp256k1, Transaction, Txid, XOnlyPublicKey}; use bitcoin::{Amount, OutPoint, ScriptBuf, TapLeafHash, TxIn, TxOut, Witness}; use bitcoincore_rpc::{Auth, Client, RpcApi}; -use bitvm::transactions::{ - generate_2_of_2_script, generate_anti_contradiction_script, generate_challenge_script, - generate_equivoation_address_and_info, generate_response_second_address_and_info, - watch_transaction, -}; -use bitvm::utils::take_stdin; -use bitvm::wire::{PreimageValue, Wire}; -// verifier.rs -use bitvm::{ +use tokio::net::{TcpListener, TcpStream}; +use tokio_tungstenite::accept_async; +use toy_bitvm::{ actor::Actor, + circuit::wire::{HashTuple, HashValue, PreimageValue, Wire}, circuit::Circuit, communication::{receive_message, send_message}, - transactions::{generate_challenge_address_and_info, generate_response_address_and_info}, - wire::{HashTuple, HashValue}, + transactions::{ + generate_2_of_2_script, generate_anti_contradiction_script, + generate_challenge_address_and_info, generate_challenge_script, + generate_equivoation_address_and_info, generate_response_address_and_info, + generate_response_second_address_and_info, watch_transaction, + }, + utils::take_stdin, }; -use tokio::net::{TcpListener, TcpStream}; -use tokio_tungstenite::accept_async; #[tokio::main] async fn main() { @@ -103,7 +101,7 @@ async fn handle_connection(stream: TcpStream) { }; for i in 0..bisection_length as u64 { - println!("Bisection iteration {}", i); + println!("Bisection iteration: {}", i); let challenge_hashes: Vec = verifier.generate_challenge_hashes(circuit.num_gates()); send_message(&mut ws_stream, &challenge_hashes) @@ -236,7 +234,7 @@ async fn handle_connection(stream: TcpStream) { // Prover needs to give signature to verifier so that verifier can start a challenge let challenge_sig: Signature = receive_message(&mut ws_stream).await.unwrap(); verifier.add_signature(challenge_sig); - println!("challenge sig: {:?}", challenge_sig); + println!("Challenge Sig: {:?}", challenge_sig); // Verify needs to verify the signature let mut sighash_cache = SighashCache::new(response_tx.borrow_mut()); @@ -261,7 +259,7 @@ async fn handle_connection(stream: TcpStream) { last_output = outputs2; last_txid = response_tx.txid(); } - println!("Bisection complete!"); + println!("Bisection completed"); let kickoff_txid: Txid = receive_message(&mut ws_stream).await.unwrap(); if kickoff_tx.txid() != kickoff_txid { panic!("Kickoff txid mismatch!"); @@ -353,7 +351,7 @@ async fn handle_connection(stream: TcpStream) { if i != 0 { // Verifier needs needs to give signature to prover so that prover can give a response - println!("Waiting for provers response"); + println!("Waiting for prover's response..."); let provers_response = watch_transaction(&rpc, &challenge_tx.txid(), watch_interval).unwrap(); let num_wires = circuit.gates[challenge_gate_num].get_input_size() @@ -418,7 +416,7 @@ async fn handle_connection(stream: TcpStream) { value: Amount::from_sat(amt - (2 * i + 2) * (fee + dust_limit)), }], }; - println!("CONTRADICTION FOUND"); + println!("Contraditcion found! Slashing the prover!"); let mut sighash_cache = SighashCache::new(steal_tx.borrow_mut()); let equivocation_script = generate_anti_contradiction_script( @@ -450,7 +448,8 @@ async fn handle_connection(stream: TcpStream) { let steal_txid = rpc .send_raw_transaction(&steal_tx) .unwrap_or_else(|e| panic!("Failed to send raw transaction: {}", e)); - println!("VERIFIER STOLE ALL THE MONEY: {:?}", steal_txid); + println!("Verifier slashed the prover: {:?}", steal_txid); + std::process::exit(0); } // Prover needs to give signature to verifier so that verifier can start a challenge challenge_gate_num = @@ -514,11 +513,10 @@ async fn handle_connection(stream: TcpStream) { .send_raw_transaction(&response_tx) .unwrap_or_else(|e| panic!("Failed to send raw transaction: {}", e)); - println!("CHALLENGE SENTTTTTTT"); - println!("txid : {:?}", response_txid); + println!("Challenge transaction sent! txid: {:?}", response_txid); last_output = outputs2; last_txid = response_tx.txid(); } - println!("{:?}", last_output); + println!("Last output: {:?}", last_output); }