diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40fdfe7a..ab3ec635 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,4 +48,5 @@ jobs: - nightly steps: - uses: actions/checkout@v2 - - run: GOAT_CHAIN_URL=https://rpc.testnet3.goat.network GOAT_GATEWAY_CONTRACT_ADDRESS=0xeD8AeeD334fA446FA03Aa00B28aFf02FA8aC02df GOAT_GATEWAY_CONTRACT_CREATION=0 GOAT_CHAIN_ID=48816 cargo test -r + - run: cd scripts && docker compose up -d + - run: cargo test -r --features tests diff --git a/Cargo.toml b/Cargo.toml index 67057d36..f72f91f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ libp2p-metrics = "0.16.0" libp2p-core = "0.43.0" prometheus-client = "0.22.3" once_cell = "1.5" -log = "0.4.0" bitcoin-script = { git = "https://github.com/BitVM/rust-bitcoin-script" } bitcoin = { version = "0.32.5", features = ["rand-std"] } strum = "0.27" @@ -79,4 +78,4 @@ bitvm2-lib = { path = "crates/bitvm2" } identity = { path = "crates/identity" } store = { path = "crates/store" } spv = { path = "crates/spv" } -client = { path = "crates/client" } +client = { path = "crates/client" } \ No newline at end of file diff --git a/crates/bitvm2/Cargo.toml b/crates/bitvm2/Cargo.toml index 9f685aaa..6cc240f6 100644 --- a/crates/bitvm2/Cargo.toml +++ b/crates/bitvm2/Cargo.toml @@ -22,3 +22,4 @@ bitcoin-script = { workspace = true } serde_json = { workspace = true } bincode = "1.3.3" uuid = { workspace = true } +clap = { version = "4.5.37", features = ["derive"] } diff --git a/crates/bitvm2/src/types.rs b/crates/bitvm2/src/types.rs index a25309e7..1d919a07 100644 --- a/crates/bitvm2/src/types.rs +++ b/crates/bitvm2/src/types.rs @@ -1,3 +1,5 @@ +use crate::committee::{COMMITTEE_PRE_SIGN_NUM, push_committee_pre_signatures}; +use crate::operator::push_operator_pre_signature; use anyhow::Result; use bitcoin::{Address, Amount, Network, PrivateKey, PublicKey, XOnlyPublicKey, key::Keypair}; use bitcoin::{OutPoint, TapNodeHash, Witness}; @@ -28,9 +30,6 @@ use rand::{Rng, distributions::Alphanumeric}; use secp256k1::SECP256K1; use serde::{Deserialize, Serialize}; -use crate::committee::{COMMITTEE_PRE_SIGN_NUM, push_committee_pre_signatures}; -use crate::operator::push_operator_pre_signature; - pub type VerifyingKey = ark_groth16::VerifyingKey; pub type Groth16Proof = ark_groth16::Proof; pub type PublicInputs = Vec; @@ -50,7 +49,7 @@ pub fn random_string(len: usize) -> String { rand::thread_rng().sample_iter(&Alphanumeric).take(len).map(char::from).collect() } -#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct Bitvm2Parameters { pub network: Network, pub depositor_evm_address: [u8; 20], @@ -115,7 +114,7 @@ impl Bitvm2Parameters { } } -#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct Bitvm2Graph { pub(crate) operator_pre_signed: bool, pub(crate) committee_pre_signed: bool, @@ -133,7 +132,7 @@ pub struct Bitvm2Graph { pub disprove: DisproveTransaction, } -#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)] +#[derive(Serialize, Deserialize, Clone)] pub struct SimplifiedBitvm2Graph { pub parameters: Bitvm2Parameters, pub operator_pre_sigs: Option, @@ -343,7 +342,7 @@ impl Bitvm2Graph { } } -#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct CustomInputs { pub inputs: Vec, /// stake amount / pegin_amount diff --git a/crates/bitvm2/tests/e2e.rs b/crates/bitvm2/tests/e2e.rs index aa3503c8..bf1560d3 100644 --- a/crates/bitvm2/tests/e2e.rs +++ b/crates/bitvm2/tests/e2e.rs @@ -17,290 +17,302 @@ use musig2::{AggNonce, PartialSignature, PubNonce, SecNonce}; use secp256k1::SECP256K1; use std::str::FromStr; -#[test] -#[ignore] -fn e2e_test() { - let network = Network::Testnet; - // key generation - println!("\ngenerate keypairs"); - const OPERATOR_SECRET: &str = - "3076ca1dfc1e383be26d5dd3c0c427340f96139fa8c2520862cf551ec2d670ac"; - const VERIFIER_0_SECRET: &str = - "ee0817eac0c13aa8ee2dd3256304041f09f0499d1089b56495310ae8093583e2"; - const VERIFIER_1_SECRET: &str = - "fc294c70faf210d4d0807ea7a3dba8f7e41700d90c119e1ae82a0687d89d297f"; +#[cfg(test)] +pub mod tests { + use super::*; + #[test] + #[ignore] + fn e2e_test() { + let network = Network::Testnet; + // key generation + println!("\ngenerate keypairs"); + const OPERATOR_SECRET: &str = + "3076ca1dfc1e383be26d5dd3c0c427340f96139fa8c2520862cf551ec2d670ac"; + const VERIFIER_0_SECRET: &str = + "ee0817eac0c13aa8ee2dd3256304041f09f0499d1089b56495310ae8093583e2"; + const VERIFIER_1_SECRET: &str = + "fc294c70faf210d4d0807ea7a3dba8f7e41700d90c119e1ae82a0687d89d297f"; - let verifier_0_keypair = committee::generate_keypair_from_seed(VERIFIER_0_SECRET.to_string()); - let verifier_1_keypair = committee::generate_keypair_from_seed(VERIFIER_1_SECRET.to_string()); - let operator_keypair = committee::generate_keypair_from_seed(OPERATOR_SECRET.to_string()); + let verifier_0_keypair = + committee::generate_keypair_from_seed(VERIFIER_0_SECRET.to_string()); + let verifier_1_keypair = + committee::generate_keypair_from_seed(VERIFIER_1_SECRET.to_string()); + let operator_keypair = committee::generate_keypair_from_seed(OPERATOR_SECRET.to_string()); - let verifier_0_sk = PrivateKey::new(verifier_0_keypair.secret_key(), network); - let verifier_0_public_key = PublicKey::from_private_key(SECP256K1, &verifier_0_sk); - let verifier_1_sk = PrivateKey::new(verifier_1_keypair.secret_key(), network); - let verifier_1_public_key = PublicKey::from_private_key(SECP256K1, &verifier_1_sk); - let operator_sk = PrivateKey::new(operator_keypair.secret_key(), network); - let operator_pubkey = PublicKey::from_private_key(SECP256K1, &operator_sk); + let verifier_0_sk = PrivateKey::new(verifier_0_keypair.secret_key(), network); + let verifier_0_public_key = PublicKey::from_private_key(SECP256K1, &verifier_0_sk); + let verifier_1_sk = PrivateKey::new(verifier_1_keypair.secret_key(), network); + let verifier_1_public_key = PublicKey::from_private_key(SECP256K1, &verifier_1_sk); + let operator_sk = PrivateKey::new(operator_keypair.secret_key(), network); + let operator_pubkey = PublicKey::from_private_key(SECP256K1, &operator_sk); - let committee_pubkeys: Vec = vec![verifier_0_public_key, verifier_1_public_key]; - let (committee_agg_pubkey, _) = generate_n_of_n_public_key(&committee_pubkeys); + let committee_pubkeys: Vec = vec![verifier_0_public_key, verifier_1_public_key]; + let (committee_agg_pubkey, _) = generate_n_of_n_public_key(&committee_pubkeys); - let (operator_wots_seckeys, operator_wots_pubkeys) = - operator::generate_wots_keys(OPERATOR_SECRET); + let (operator_wots_seckeys, operator_wots_pubkeys) = + operator::generate_wots_keys(OPERATOR_SECRET); - // mock groth16 proof - let mock_vk_bytes = [ - 115, 158, 251, 51, 106, 255, 102, 248, 22, 171, 229, 158, 80, 192, 240, 217, 99, 162, 65, - 107, 31, 137, 197, 79, 11, 210, 74, 65, 65, 203, 243, 14, 123, 2, 229, 125, 198, 247, 76, - 241, 176, 116, 6, 3, 241, 1, 134, 195, 39, 5, 124, 47, 31, 43, 164, 48, 120, 207, 150, 125, - 108, 100, 48, 155, 137, 132, 16, 193, 139, 74, 179, 131, 42, 119, 25, 185, 98, 13, 235, - 118, 92, 11, 154, 142, 134, 220, 191, 220, 169, 250, 244, 104, 123, 7, 247, 33, 178, 155, - 121, 59, 75, 188, 206, 198, 182, 97, 0, 64, 231, 45, 55, 92, 100, 17, 56, 159, 79, 13, 219, - 221, 33, 39, 193, 24, 36, 58, 105, 8, 70, 206, 176, 209, 146, 45, 201, 157, 226, 84, 213, - 135, 143, 178, 156, 112, 137, 246, 123, 248, 215, 168, 51, 95, 177, 47, 57, 29, 199, 224, - 98, 48, 144, 253, 15, 201, 192, 142, 62, 143, 13, 228, 89, 51, 58, 6, 226, 139, 99, 207, - 22, 113, 215, 79, 91, 158, 166, 210, 28, 90, 218, 111, 151, 4, 55, 230, 76, 90, 209, 149, - 113, 248, 245, 50, 231, 137, 51, 157, 40, 29, 184, 198, 201, 108, 199, 89, 67, 136, 239, - 96, 216, 237, 172, 29, 84, 3, 128, 240, 2, 218, 169, 217, 118, 179, 34, 226, 19, 227, 59, - 193, 131, 108, 20, 113, 46, 170, 196, 156, 45, 39, 151, 218, 22, 132, 250, 209, 183, 46, - 249, 115, 239, 14, 176, 200, 134, 158, 148, 139, 212, 167, 152, 205, 183, 236, 242, 176, - 96, 177, 187, 184, 252, 14, 226, 127, 127, 173, 147, 224, 220, 8, 29, 63, 73, 215, 92, 161, - 110, 20, 154, 131, 23, 217, 116, 145, 196, 19, 167, 84, 185, 16, 89, 175, 180, 110, 116, - 57, 198, 237, 147, 183, 164, 169, 220, 172, 52, 68, 175, 113, 244, 62, 104, 134, 215, 99, - 132, 199, 139, 172, 108, 143, 25, 238, 201, 128, 85, 24, 73, 30, 186, 142, 186, 201, 79, 3, - 176, 185, 70, 66, 89, 127, 188, 158, 209, 83, 17, 22, 187, 153, 8, 63, 58, 174, 236, 132, - 226, 43, 145, 97, 242, 198, 117, 105, 161, 21, 241, 23, 84, 32, 62, 155, 245, 172, 30, 78, - 41, 199, 219, 180, 149, 193, 163, 131, 237, 240, 46, 183, 186, 42, 201, 49, 249, 142, 188, - 59, 212, 26, 253, 23, 27, 205, 231, 163, 76, 179, 135, 193, 152, 110, 91, 5, 218, 67, 204, - 164, 128, 183, 221, 82, 16, 72, 249, 111, 118, 182, 24, 249, 91, 215, 215, 155, 2, 0, 0, 0, - 0, 0, 0, 0, 212, 110, 6, 228, 73, 146, 46, 184, 158, 58, 94, 4, 141, 241, 158, 0, 175, 140, - 72, 75, 52, 6, 72, 49, 112, 215, 21, 243, 151, 67, 106, 22, 158, 237, 80, 204, 41, 128, 69, - 52, 154, 189, 124, 203, 35, 107, 132, 241, 234, 31, 3, 165, 87, 58, 10, 92, 252, 227, 214, - 99, 176, 66, 118, 22, 177, 20, 120, 198, 252, 236, 7, 148, 207, 78, 152, 132, 94, 207, 50, - 243, 4, 169, 146, 240, 79, 98, 0, 212, 106, 137, 36, 193, 21, 175, 180, 1, 26, 107, 39, - 198, 89, 152, 26, 220, 138, 105, 243, 45, 63, 106, 163, 80, 74, 253, 176, 207, 47, 52, 7, - 84, 59, 151, 47, 178, 165, 112, 251, 161, - ] - .to_vec(); - let mock_proof_bytes: Vec = [ - 162, 50, 57, 98, 3, 171, 250, 108, 49, 206, 73, 126, 25, 35, 178, 148, 35, 219, 98, 90, - 122, 177, 16, 91, 233, 215, 222, 12, 72, 184, 53, 2, 62, 166, 50, 68, 98, 171, 218, 218, - 151, 177, 133, 223, 129, 53, 114, 236, 181, 215, 223, 91, 102, 225, 52, 122, 122, 206, 36, - 122, 213, 38, 186, 170, 235, 210, 179, 221, 122, 37, 74, 38, 79, 0, 26, 94, 59, 146, 46, - 252, 70, 153, 236, 126, 194, 169, 17, 144, 100, 218, 118, 22, 99, 226, 132, 40, 24, 248, - 232, 197, 195, 220, 254, 52, 36, 248, 18, 167, 167, 206, 108, 29, 120, 188, 18, 78, 86, 8, - 121, 217, 144, 185, 122, 58, 12, 34, 44, 6, 233, 80, 177, 183, 5, 8, 150, 74, 241, 141, 65, - 150, 35, 98, 15, 150, 137, 254, 132, 167, 228, 104, 63, 133, 11, 209, 39, 79, 138, 185, 88, - 20, 242, 102, 69, 73, 243, 88, 29, 91, 127, 157, 82, 192, 52, 95, 143, 49, 227, 83, 19, 26, - 108, 63, 232, 213, 169, 64, 221, 159, 214, 220, 246, 174, 35, 43, 143, 80, 168, 142, 29, - 103, 179, 58, 235, 33, 163, 198, 255, 188, 20, 3, 91, 47, 158, 122, 226, 201, 175, 138, 18, - 24, 178, 219, 78, 12, 96, 10, 2, 133, 35, 230, 149, 235, 206, 1, 177, 211, 245, 168, 74, - 62, 25, 115, 70, 42, 38, 131, 92, 103, 103, 176, 212, 223, 177, 242, 94, 14, - ] - .to_vec(); - let mock_scalar = [ - 232, 255, 255, 239, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88, 129, - 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48, - ] - .to_vec(); - let proof: ark_groth16::Proof = - ark_groth16::Proof::deserialize_uncompressed(&mock_proof_bytes[..]).unwrap(); - let vk: ark_groth16::VerifyingKey = - ark_groth16::VerifyingKey::deserialize_uncompressed(&mock_vk_bytes[..]).unwrap(); - let scalar: ark_bn254::Fr = ark_bn254::Fr::deserialize_uncompressed(&mock_scalar[..]).unwrap(); - let scalars = vec![scalar]; - let proof_sigs = operator::sign_proof(&vk, proof, scalars, &operator_wots_seckeys); + // mock groth16 proof + let mock_vk_bytes = [ + 115, 158, 251, 51, 106, 255, 102, 248, 22, 171, 229, 158, 80, 192, 240, 217, 99, 162, + 65, 107, 31, 137, 197, 79, 11, 210, 74, 65, 65, 203, 243, 14, 123, 2, 229, 125, 198, + 247, 76, 241, 176, 116, 6, 3, 241, 1, 134, 195, 39, 5, 124, 47, 31, 43, 164, 48, 120, + 207, 150, 125, 108, 100, 48, 155, 137, 132, 16, 193, 139, 74, 179, 131, 42, 119, 25, + 185, 98, 13, 235, 118, 92, 11, 154, 142, 134, 220, 191, 220, 169, 250, 244, 104, 123, + 7, 247, 33, 178, 155, 121, 59, 75, 188, 206, 198, 182, 97, 0, 64, 231, 45, 55, 92, 100, + 17, 56, 159, 79, 13, 219, 221, 33, 39, 193, 24, 36, 58, 105, 8, 70, 206, 176, 209, 146, + 45, 201, 157, 226, 84, 213, 135, 143, 178, 156, 112, 137, 246, 123, 248, 215, 168, 51, + 95, 177, 47, 57, 29, 199, 224, 98, 48, 144, 253, 15, 201, 192, 142, 62, 143, 13, 228, + 89, 51, 58, 6, 226, 139, 99, 207, 22, 113, 215, 79, 91, 158, 166, 210, 28, 90, 218, + 111, 151, 4, 55, 230, 76, 90, 209, 149, 113, 248, 245, 50, 231, 137, 51, 157, 40, 29, + 184, 198, 201, 108, 199, 89, 67, 136, 239, 96, 216, 237, 172, 29, 84, 3, 128, 240, 2, + 218, 169, 217, 118, 179, 34, 226, 19, 227, 59, 193, 131, 108, 20, 113, 46, 170, 196, + 156, 45, 39, 151, 218, 22, 132, 250, 209, 183, 46, 249, 115, 239, 14, 176, 200, 134, + 158, 148, 139, 212, 167, 152, 205, 183, 236, 242, 176, 96, 177, 187, 184, 252, 14, 226, + 127, 127, 173, 147, 224, 220, 8, 29, 63, 73, 215, 92, 161, 110, 20, 154, 131, 23, 217, + 116, 145, 196, 19, 167, 84, 185, 16, 89, 175, 180, 110, 116, 57, 198, 237, 147, 183, + 164, 169, 220, 172, 52, 68, 175, 113, 244, 62, 104, 134, 215, 99, 132, 199, 139, 172, + 108, 143, 25, 238, 201, 128, 85, 24, 73, 30, 186, 142, 186, 201, 79, 3, 176, 185, 70, + 66, 89, 127, 188, 158, 209, 83, 17, 22, 187, 153, 8, 63, 58, 174, 236, 132, 226, 43, + 145, 97, 242, 198, 117, 105, 161, 21, 241, 23, 84, 32, 62, 155, 245, 172, 30, 78, 41, + 199, 219, 180, 149, 193, 163, 131, 237, 240, 46, 183, 186, 42, 201, 49, 249, 142, 188, + 59, 212, 26, 253, 23, 27, 205, 231, 163, 76, 179, 135, 193, 152, 110, 91, 5, 218, 67, + 204, 164, 128, 183, 221, 82, 16, 72, 249, 111, 118, 182, 24, 249, 91, 215, 215, 155, 2, + 0, 0, 0, 0, 0, 0, 0, 212, 110, 6, 228, 73, 146, 46, 184, 158, 58, 94, 4, 141, 241, 158, + 0, 175, 140, 72, 75, 52, 6, 72, 49, 112, 215, 21, 243, 151, 67, 106, 22, 158, 237, 80, + 204, 41, 128, 69, 52, 154, 189, 124, 203, 35, 107, 132, 241, 234, 31, 3, 165, 87, 58, + 10, 92, 252, 227, 214, 99, 176, 66, 118, 22, 177, 20, 120, 198, 252, 236, 7, 148, 207, + 78, 152, 132, 94, 207, 50, 243, 4, 169, 146, 240, 79, 98, 0, 212, 106, 137, 36, 193, + 21, 175, 180, 1, 26, 107, 39, 198, 89, 152, 26, 220, 138, 105, 243, 45, 63, 106, 163, + 80, 74, 253, 176, 207, 47, 52, 7, 84, 59, 151, 47, 178, 165, 112, 251, 161, + ] + .to_vec(); + let mock_proof_bytes: Vec = [ + 162, 50, 57, 98, 3, 171, 250, 108, 49, 206, 73, 126, 25, 35, 178, 148, 35, 219, 98, 90, + 122, 177, 16, 91, 233, 215, 222, 12, 72, 184, 53, 2, 62, 166, 50, 68, 98, 171, 218, + 218, 151, 177, 133, 223, 129, 53, 114, 236, 181, 215, 223, 91, 102, 225, 52, 122, 122, + 206, 36, 122, 213, 38, 186, 170, 235, 210, 179, 221, 122, 37, 74, 38, 79, 0, 26, 94, + 59, 146, 46, 252, 70, 153, 236, 126, 194, 169, 17, 144, 100, 218, 118, 22, 99, 226, + 132, 40, 24, 248, 232, 197, 195, 220, 254, 52, 36, 248, 18, 167, 167, 206, 108, 29, + 120, 188, 18, 78, 86, 8, 121, 217, 144, 185, 122, 58, 12, 34, 44, 6, 233, 80, 177, 183, + 5, 8, 150, 74, 241, 141, 65, 150, 35, 98, 15, 150, 137, 254, 132, 167, 228, 104, 63, + 133, 11, 209, 39, 79, 138, 185, 88, 20, 242, 102, 69, 73, 243, 88, 29, 91, 127, 157, + 82, 192, 52, 95, 143, 49, 227, 83, 19, 26, 108, 63, 232, 213, 169, 64, 221, 159, 214, + 220, 246, 174, 35, 43, 143, 80, 168, 142, 29, 103, 179, 58, 235, 33, 163, 198, 255, + 188, 20, 3, 91, 47, 158, 122, 226, 201, 175, 138, 18, 24, 178, 219, 78, 12, 96, 10, 2, + 133, 35, 230, 149, 235, 206, 1, 177, 211, 245, 168, 74, 62, 25, 115, 70, 42, 38, 131, + 92, 103, 103, 176, 212, 223, 177, 242, 94, 14, + ] + .to_vec(); + let mock_scalar = [ + 232, 255, 255, 239, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88, + 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48, + ] + .to_vec(); + let proof: ark_groth16::Proof = + ark_groth16::Proof::deserialize_uncompressed(&mock_proof_bytes[..]).unwrap(); + let vk: ark_groth16::VerifyingKey = + ark_groth16::VerifyingKey::deserialize_uncompressed(&mock_vk_bytes[..]).unwrap(); + let scalar: ark_bn254::Fr = + ark_bn254::Fr::deserialize_uncompressed(&mock_scalar[..]).unwrap(); + let scalars = vec![scalar]; + let proof_sigs = operator::sign_proof(&vk, proof, scalars, &operator_wots_seckeys); - // mock graph data - println!("\ngenerate mock graph"); - let graph_index = 1; - let pegin_amount = Amount::from_btc(1.0).unwrap(); - let stake_amount = Amount::from_btc(0.2).unwrap(); - let challenge_amount = Amount::from_btc(0.1).unwrap(); - let fee_amount = Amount::from_sat(2000); - let mock_input = Input { - outpoint: OutPoint { - txid: Txid::from_str( - "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", - ) - .unwrap(), - vout: 0, - }, - amount: Amount::from_btc(10000.0).unwrap(), - }; - let mock_user_change_address = generate_burn_script_address(network); - let user_inputs = CustomInputs { - inputs: vec![mock_input.clone()], - input_amount: pegin_amount, - fee_amount, - change_address: mock_user_change_address, - }; - let mock_operator_change_address = generate_burn_script_address(network); - let operator_inputs = CustomInputs { - inputs: vec![mock_input.clone()], - input_amount: stake_amount, - fee_amount, - change_address: mock_operator_change_address, - }; - let params = Bitvm2Parameters { - network, - depositor_evm_address: [0xff; 20], - pegin_amount, - stake_amount, - challenge_amount, - committee_pubkeys, - committee_agg_pubkey, - operator_pubkey, - operator_wots_pubkeys: operator_wots_pubkeys.clone(), - user_inputs, - operator_inputs, - }; + // mock graph data + println!("\ngenerate mock graph"); + let graph_index = 1; + let pegin_amount = Amount::from_btc(1.0).unwrap(); + let stake_amount = Amount::from_btc(0.2).unwrap(); + let challenge_amount = Amount::from_btc(0.1).unwrap(); + let fee_amount = Amount::from_sat(2000); + let mock_input = Input { + outpoint: OutPoint { + txid: Txid::from_str( + "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + ) + .unwrap(), + vout: 0, + }, + amount: Amount::from_btc(10000.0).unwrap(), + }; + let mock_user_change_address = generate_burn_script_address(network); + let user_inputs = CustomInputs { + inputs: vec![mock_input.clone()], + input_amount: pegin_amount, + fee_amount, + change_address: mock_user_change_address, + }; + let mock_operator_change_address = generate_burn_script_address(network); + let operator_inputs = CustomInputs { + inputs: vec![mock_input.clone()], + input_amount: stake_amount, + fee_amount, + change_address: mock_operator_change_address, + }; + let params = Bitvm2Parameters { + network, + depositor_evm_address: [0xff; 20], + pegin_amount, + stake_amount, + challenge_amount, + committee_pubkeys, + committee_agg_pubkey, + operator_pubkey, + operator_wots_pubkeys: operator_wots_pubkeys.clone(), + user_inputs, + operator_inputs, + }; - /* - let partial_scripts = operator::generate_partial_scripts(&vk); - let disprove_scripts = operator::generate_disprove_scripts(&partial_scripts, &operator_wots_pubkeys); - */ - let mock_script = script! {OP_TRUE}; - let mock_script_bytes = mock_script.clone().compile().to_bytes(); - let mock_disprove_scripts_bytes: [Vec; NUM_TAPS] = - std::array::from_fn(|_| mock_script_bytes.clone()); + /* + let partial_scripts = operator::generate_partial_scripts(&vk); + let disprove_scripts = operator::generate_disprove_scripts(&partial_scripts, &operator_wots_pubkeys); + */ + let mock_script = script! {OP_TRUE}; + let mock_script_bytes = mock_script.clone().compile().to_bytes(); + let mock_disprove_scripts_bytes: [Vec; NUM_TAPS] = + std::array::from_fn(|_| mock_script_bytes.clone()); - let mut graph = - operator::generate_bitvm_graph(params, mock_disprove_scripts_bytes.to_vec()).unwrap(); + let mut graph = + operator::generate_bitvm_graph(params, mock_disprove_scripts_bytes.to_vec()).unwrap(); - // opeartor pre-sign - println!("\nopeartor pre-sign"); - let _ = operator::operator_pre_sign(operator_keypair, &mut graph); + // opeartor pre-sign + println!("\nopeartor pre-sign"); + let _ = operator::operator_pre_sign(operator_keypair, &mut graph); - // committee pre-sign - println!("\ncommittee pre-sign"); - let verifier_0_nonces = committee::generate_nonce_from_seed( - VERIFIER_0_SECRET.to_string(), - graph_index, - verifier_0_keypair, - ); - let verifier_1_nonces = committee::generate_nonce_from_seed( - VERIFIER_1_SECRET.to_string(), - graph_index, - verifier_1_keypair, - ); + // committee pre-sign + println!("\ncommittee pre-sign"); + let verifier_0_nonces = committee::generate_nonce_from_seed( + VERIFIER_0_SECRET.to_string(), + graph_index, + verifier_0_keypair, + ); + let verifier_1_nonces = committee::generate_nonce_from_seed( + VERIFIER_1_SECRET.to_string(), + graph_index, + verifier_1_keypair, + ); - let verifier_0_sec_nonces: [SecNonce; committee::COMMITTEE_PRE_SIGN_NUM] = - std::array::from_fn(|i| verifier_0_nonces[i].0.clone()); - let verifier_0_pub_nonces: [PubNonce; committee::COMMITTEE_PRE_SIGN_NUM] = - std::array::from_fn(|i| verifier_0_nonces[i].1.clone()); + let verifier_0_sec_nonces: [SecNonce; committee::COMMITTEE_PRE_SIGN_NUM] = + std::array::from_fn(|i| verifier_0_nonces[i].0.clone()); + let verifier_0_pub_nonces: [PubNonce; committee::COMMITTEE_PRE_SIGN_NUM] = + std::array::from_fn(|i| verifier_0_nonces[i].1.clone()); - let verifier_1_sec_nonces: [SecNonce; committee::COMMITTEE_PRE_SIGN_NUM] = - std::array::from_fn(|i| verifier_1_nonces[i].0.clone()); - let verifier_1_pub_nonces: [PubNonce; committee::COMMITTEE_PRE_SIGN_NUM] = - std::array::from_fn(|i| verifier_1_nonces[i].1.clone()); + let verifier_1_sec_nonces: [SecNonce; committee::COMMITTEE_PRE_SIGN_NUM] = + std::array::from_fn(|i| verifier_1_nonces[i].0.clone()); + let verifier_1_pub_nonces: [PubNonce; committee::COMMITTEE_PRE_SIGN_NUM] = + std::array::from_fn(|i| verifier_1_nonces[i].1.clone()); - let agg_nonces: [AggNonce; committee::COMMITTEE_PRE_SIGN_NUM] = verifier_0_pub_nonces - .iter() - .zip(verifier_1_pub_nonces) - .map(|(a, b)| committee::nonce_aggregation(&vec![a.clone(), b])) - .collect::>() - .try_into() - .unwrap(); - - let verifier_0_sigs = committee::committee_pre_sign( - verifier_0_keypair, - verifier_0_sec_nonces, - agg_nonces.clone(), - &graph, - ) - .unwrap(); - - let verifier_1_sigs = committee::committee_pre_sign( - verifier_1_keypair, - verifier_1_sec_nonces, - agg_nonces.clone(), - &graph, - ) - .unwrap(); - - let committee_partial_sigs: [Vec; committee::COMMITTEE_PRE_SIGN_NUM] = - verifier_0_sigs + let agg_nonces: [AggNonce; committee::COMMITTEE_PRE_SIGN_NUM] = verifier_0_pub_nonces .iter() - .zip(verifier_1_sigs) - .map(|(&a, b)| vec![a, b]) - .collect::>>() + .zip(verifier_1_pub_nonces) + .map(|(a, b)| committee::nonce_aggregation(&vec![a.clone(), b])) + .collect::>() .try_into() .unwrap(); - let _ = - committee::signature_aggregation_and_push(&committee_partial_sigs, &agg_nonces, &mut graph); + let verifier_0_sigs = committee::committee_pre_sign( + verifier_0_keypair, + verifier_0_sec_nonces, + agg_nonces.clone(), + &graph, + ) + .unwrap(); - // happy_path take - let withdraw_evm_txid = [0xff; 32]; - let kickoff_tx = operator::operator_sign_kickoff( - operator_keypair, - &mut graph, - &operator_wots_seckeys, - &operator_wots_pubkeys, - withdraw_evm_txid, - ) - .unwrap(); - broadcast_tx(kickoff_tx); + let verifier_1_sigs = committee::committee_pre_sign( + verifier_1_keypair, + verifier_1_sec_nonces, + agg_nonces.clone(), + &graph, + ) + .unwrap(); - let take_1_tx = operator::operator_sign_take1(operator_keypair, &mut graph).unwrap(); - broadcast_tx(take_1_tx); + let committee_partial_sigs: [Vec; committee::COMMITTEE_PRE_SIGN_NUM] = + verifier_0_sigs + .iter() + .zip(verifier_1_sigs) + .map(|(&a, b)| vec![a, b]) + .collect::>>() + .try_into() + .unwrap(); - // unhappy_path take - let (mut challenge_tx, _) = verifier::export_challenge_tx(&mut graph).unwrap(); - let mock_crowdfund_txin = TxIn { - previous_output: mock_input.outpoint, - script_sig: ScriptBuf::new(), - sequence: Sequence::MAX, - witness: Witness::default(), - }; - let mock_challenger_change_output = TxOut { - script_pubkey: generate_burn_script_address(network).script_pubkey(), - value: Amount::from_sat(1000000), - }; - challenge_tx.input.push(mock_crowdfund_txin); - challenge_tx.output.push(mock_challenger_change_output); - broadcast_tx(challenge_tx); + let _ = committee::signature_aggregation_and_push( + &committee_partial_sigs, + &agg_nonces, + &mut graph, + ); - let (assert_init_tx, assert_commit_txns, assert_final_tx) = operator::operator_sign_assert( - operator_keypair, - &mut graph, - &operator_wots_pubkeys, - proof_sigs.clone(), - ) - .unwrap(); - broadcast_tx(assert_init_tx); - assert_commit_txns.iter().for_each(|tx| broadcast_tx(tx.clone())); - broadcast_tx(assert_final_tx); + // happy_path take + let withdraw_evm_txid = [0xff; 32]; + let kickoff_tx = operator::operator_sign_kickoff( + operator_keypair, + &mut graph, + &operator_wots_seckeys, + &operator_wots_pubkeys, + withdraw_evm_txid, + ) + .unwrap(); + broadcast_tx(kickoff_tx); - let take2_tx = operator::operator_sign_take2(operator_keypair, &mut graph).unwrap(); - broadcast_tx(take2_tx); + let take_1_tx = operator::operator_sign_take1(operator_keypair, &mut graph).unwrap(); + broadcast_tx(take_1_tx); - // disprove - /* - // verify proof published by assert-txns: - let public_proof_sigs = verifier::extract_proof_sigs_from_assert_commit_txns(assert_commit_txns).unwrap(); - let disprove_witness = verifier::verify_proof( - &vk, - public_proof_sigs, - &mock_disprove_scripts, - &operator_wots_pubkeys, - ).unwrap(); - */ - let mock_disprove_witness = (0, mock_script); - let mock_challenger_reward_address = generate_burn_script_address(network); - let disprove_tx = verifier::sign_disprove( - &mut graph, - mock_disprove_witness, - mock_disprove_scripts_bytes.to_vec(), - &operator_wots_pubkeys.1, - mock_challenger_reward_address, - ) - .unwrap(); - broadcast_tx(disprove_tx); -} + // unhappy_path take + let (mut challenge_tx, _) = verifier::export_challenge_tx(&mut graph).unwrap(); + let mock_crowdfund_txin = TxIn { + previous_output: mock_input.outpoint, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::default(), + }; + let mock_challenger_change_output = TxOut { + script_pubkey: generate_burn_script_address(network).script_pubkey(), + value: Amount::from_sat(1000000), + }; + challenge_tx.input.push(mock_crowdfund_txin); + challenge_tx.output.push(mock_challenger_change_output); + broadcast_tx(challenge_tx); + + let (assert_init_tx, assert_commit_txns, assert_final_tx) = operator::operator_sign_assert( + operator_keypair, + &mut graph, + &operator_wots_pubkeys, + proof_sigs.clone(), + ) + .unwrap(); + broadcast_tx(assert_init_tx); + assert_commit_txns.iter().for_each(|tx| broadcast_tx(tx.clone())); + broadcast_tx(assert_final_tx); + + let take2_tx = operator::operator_sign_take2(operator_keypair, &mut graph).unwrap(); + broadcast_tx(take2_tx); + + // disprove + /* + // verify proof published by assert-txns: + let public_proof_sigs = verifier::extract_proof_sigs_from_assert_commit_txns(assert_commit_txns).unwrap(); + let disprove_witness = verifier::verify_proof( + &vk, + public_proof_sigs, + &mock_disprove_scripts, + &operator_wots_pubkeys, + ).unwrap(); + */ + let mock_disprove_witness = (0, mock_script); + let mock_challenger_reward_address = generate_burn_script_address(network); + let disprove_tx = verifier::sign_disprove( + &mut graph, + mock_disprove_witness, + mock_disprove_scripts_bytes.to_vec(), + &operator_wots_pubkeys.1, + mock_challenger_reward_address, + ) + .unwrap(); + broadcast_tx(disprove_tx); + } -fn broadcast_tx(_tx: Transaction) { - // broadcast transaction to bitcoin network + fn broadcast_tx(_tx: Transaction) { + // broadcast transaction to bitcoin network + } } /* diff --git a/crates/client/src/chain/goat_adaptor.rs b/crates/client/src/chain/goat_adaptor.rs index efd4b096..912e3cdc 100644 --- a/crates/client/src/chain/goat_adaptor.rs +++ b/crates/client/src/chain/goat_adaptor.rs @@ -101,7 +101,6 @@ sol!( } ); -/// testnet rpc https://rpc.testnet3.goat.network pub struct GoatInitConfig { pub rpc_url: Url, pub gateway_address: EvmAddress, @@ -110,6 +109,22 @@ pub struct GoatInitConfig { pub private_key: Option, pub chain_id: u32, } + +impl GoatInitConfig { + pub fn from_env_for_test() -> Self { + GoatInitConfig { + rpc_url: "https://rpc.testnet3.goat.network".parse::().expect("decode url"), + gateway_address: "0xeD8AeeD334fA446FA03Aa00B28aFf02FA8aC02df" + .parse() + .expect("parse contract address"), + gateway_creation_block: 0, + to_block: None, + private_key: None, + chain_id: 48816_u32, + } + } +} + pub struct GoatAdaptor { chain_id: ChainId, _gateway_address: EvmAddress, diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index fd9768db..1a109033 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -7,23 +7,13 @@ mod tests { use crate::chain::chain_adaptor::GoatNetwork; use crate::chain::goat_adaptor::GoatInitConfig; use crate::client::BitVM2Client; - use alloy::transports::http::reqwest::Url; use bitcoin::hashes::Hash; use bitcoin::{Network, Txid}; use std::str::FromStr; #[tokio::test] async fn test_spv_check() { - let global_init_config = GoatInitConfig { - rpc_url: "https://rpc.testnet3.goat.network".parse::().expect("decode url"), - gateway_address: "0xeD8AeeD334fA446FA03Aa00B28aFf02FA8aC02df" - .parse() - .expect("parse contract address"), - gateway_creation_block: 0, - to_block: None, - private_key: None, - chain_id: 48816_u32, - }; + let global_init_config = GoatInitConfig::from_env_for_test(); // let local_db = LocalDB::new(&format!("sqlite:{db_path}"), true).await; let tmp_db = tempfile::NamedTempFile::new().unwrap(); let client = BitVM2Client::new( diff --git a/node/Cargo.toml b/node/Cargo.toml index 99adbbb1..2559dd96 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -84,3 +84,7 @@ log = "0.4.27" [dev-dependencies] tempfile = "3.19.1" serial_test = "3.2.0" + +[features] +default = [] +tests = [] diff --git a/node/src/action.rs b/node/src/action.rs index f318d431..9f930b91 100644 --- a/node/src/action.rs +++ b/node/src/action.rs @@ -16,8 +16,6 @@ use goat::transactions::{assert::utils::COMMIT_TX_NUM, pre_signed::PreSignedTran use libp2p::gossipsub::MessageId; use libp2p::{PeerId, Swarm, gossipsub}; use musig2::{AggNonce, PartialSignature, PubNonce, SecNonce}; -use reqwest::Request; -use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use store::GraphStatus; use uuid::Uuid; @@ -944,26 +942,3 @@ pub(crate) fn send_to_peer( let gossipsub_topic = gossipsub::IdentTopic::new(actor); Ok(swarm.behaviour_mut().gossipsub.publish(gossipsub_topic, serde_json::to_vec(&message)?)?) } - -/// call the rpc service -/// Method::GET/POST/PUT -#[allow(dead_code)] -pub(crate) async fn inner_rpc( - addr: &str, - method: reqwest::Method, - uri: &str, - params: S, -) -> Result> -where - S: Serialize, - R: DeserializeOwned, -{ - let client = reqwest::Client::new(); - let url = reqwest::Url::parse(&format!("{addr}/{uri}"))?; - - let req = Request::new(method, url); - let req_builder = reqwest::RequestBuilder::from_parts(client, req); - let resp = req_builder.json(¶ms).send().await?; - let txt = resp.text().await?; - Ok(serde_json::from_str(txt.as_str())?) -} diff --git a/node/src/env.rs b/node/src/env.rs index 92c753a8..66fd7107 100644 --- a/node/src/env.rs +++ b/node/src/env.rs @@ -36,7 +36,7 @@ pub const PEGIN_BASE_VBYTES: u64 = 200; pub const CHALLENGE_BASE_VBYTES: u64 = 200; // reduce costs to facilitate testing -pub const MIN_SATKE_AMOUNT: u64 = 700_000; // 0.007 BTC +pub const MIN_SATKE_AMOUNT: u64 = 700_000; // 0.007 BTC pub const STAKE_RATE: u64 = 0; // 0% pub const MIN_CHALLENGE_AMOUNT: u64 = 300_000; // 0.003 BTC // pub const MIN_SATKE_AMOUNT: u64 = 20_000_000; // 0.2 BTC @@ -76,33 +76,6 @@ pub fn get_committee_member_num() -> usize { COMMITTEE_MEMBER_NUMBER } -pub fn get_bitvm2_client_config() -> GoatInitConfig { - let rpc_url_str = std::env::var(ENV_GOAT_CHAIN_URL) - .unwrap_or_else(|_| panic!("Failed to read {ENV_GOAT_CHAIN_URL} variable")); - let gateway_address_str = std::env::var(ENV_GOAT_GATEWAY_CONTRACT_ADDRESS) - .unwrap_or_else(|_| panic!("Failed to read {ENV_GOAT_GATEWAY_CONTRACT_ADDRESS} variable")); - let gateway_creation = std::env::var(ENV_GOAT_GATEWAY_CONTRACT_CREATION) - .unwrap_or_else(|_| panic!("Failed to read {ENV_GOAT_GATEWAY_CONTRACT_CREATION} variable")); - let to_block = std::env::var(ENV_GOAT_GATEWAY_CONTRACT_TO_BLOCK); - let private_key = std::env::var(ENV_GOAT_PRIVATE_KEY).ok(); - let chain_id = std::env::var(ENV_GOAT_CHAIN_ID) - .unwrap_or_else(|_| panic!("Failed to read {ENV_GOAT_CHAIN_ID} variable")); - - let rpc_url = rpc_url_str.parse::(); - let gateway_address = gateway_address_str.parse::(); - GoatInitConfig { - rpc_url: rpc_url.unwrap(), - gateway_address: gateway_address.unwrap(), - gateway_creation_block: gateway_creation.parse::().unwrap(), - to_block: match to_block { - Ok(block) => Some(BlockNumberOrTag::from_str(block.as_str()).unwrap()), - Err(_) => Some(BlockNumberOrTag::Finalized), - }, - private_key, - chain_id: chain_id.parse().expect("fail to parse int"), - } -} - pub enum IpfsTxName { AssertCommit0, AssertCommit1, @@ -136,3 +109,44 @@ impl IpfsTxName { } } } + +pub fn goat_config_from_env() -> GoatInitConfig { + if cfg!(feature = "tests") { + return GoatInitConfig::from_env_for_test(); + } + + let rpc_url_str = + std::env::var(ENV_GOAT_CHAIN_URL).expect("Failed to read {ENV_GOAT_CHAIN_URL} variable"); + let rpc_url = rpc_url_str.parse::().expect("Failed to parse {rpc_url_str} to URL"); + + let gateway_address_str = std::env::var(ENV_GOAT_GATEWAY_CONTRACT_ADDRESS) + .expect("Failed to read {ENV_GOAT_GATEWAY_CONTRACT_ADDRESS} variable"); + let gateway_address = gateway_address_str + .parse::() + .expect("Failed to parse {gateway_address_str} to address"); + + let gateway_creation = std::env::var(ENV_GOAT_GATEWAY_CONTRACT_CREATION) + .expect("Failed to read {ENV_GOAT_GATEWAY_CONTRACT_CREATION} variable"); + let gateway_creation_block = + gateway_creation.parse::().expect("{ENV_GOAT_GATEWAY_CONTRACT_CREATION} parse"); + + let to_block = match std::env::var(ENV_GOAT_GATEWAY_CONTRACT_TO_BLOCK).ok() { + Some(to_block_str) => BlockNumberOrTag::from_str(to_block_str.as_str()).ok(), + _ => None, + }; + + let private_key = std::env::var(ENV_GOAT_PRIVATE_KEY).ok(); + let chain_id = std::env::var(ENV_GOAT_CHAIN_ID) + .expect("Failed to read {ENV_GOAT_CHAIN_ID} variable") + .parse::() + .expect("Failed to parse {chain_id_str} to u32"); + + GoatInitConfig { + rpc_url, + gateway_address, + gateway_creation_block, + to_block, + private_key, + chain_id, + } +} diff --git a/node/src/main.rs b/node/src/main.rs index e6d84d0c..3a303d29 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -24,6 +24,7 @@ mod metrics_service; mod middleware; mod relayer_action; mod rpc_service; +mod tests; mod utils; use crate::action::GOATMessage; @@ -217,7 +218,7 @@ async fn main() -> Result<(), Box> { None, env::get_network(), env::get_goat_network(), - env::get_bitvm2_client_config(), + env::goat_config_from_env(), &ipfs_url, ) .await; diff --git a/node/src/rpc_service/mod.rs b/node/src/rpc_service/mod.rs index 3721c09a..83d39b43 100644 --- a/node/src/rpc_service/mod.rs +++ b/node/src/rpc_service/mod.rs @@ -4,7 +4,7 @@ use std::str::FromStr; mod handler; mod node; -use crate::env::{get_bitvm2_client_config, get_goat_network, get_network}; +use crate::env::{get_goat_network, get_network, goat_config_from_env}; use crate::metrics_service::{MetricsState, metrics_handler, metrics_middleware}; use crate::rpc_service::handler::{bitvm2_handler::*, node_handler::*}; use axum::body::Body; @@ -55,7 +55,7 @@ impl AppState { None, get_network(), get_goat_network(), - get_bitvm2_client_config(), + goat_config_from_env(), ipfs_url, ) .await; diff --git a/node/src/tests.rs b/node/src/tests.rs new file mode 100644 index 00000000..41ddc9d0 --- /dev/null +++ b/node/src/tests.rs @@ -0,0 +1,730 @@ +#[cfg(test)] +pub mod tests { + use crate::env::{ + DUST_AMOUNT, PEGIN_BASE_VBYTES, PRE_KICKOFF_BASE_VBYTES, get_committee_member_num, + }; + use crate::utils::{get_proper_utxo_set, node_p2wsh_address, node_p2wsh_script, node_sign}; + use bitcoin::key::Keypair; + use bitcoin::{CompressedPublicKey, EcdsaSighashType, ScriptBuf, Sequence, Witness}; + use bitvm2_lib::committee::{COMMITTEE_PRE_SIGN_NUM, committee_pre_sign, nonces_aggregation}; + use client::chain::chain_adaptor::GoatNetwork; + use client::chain::goat_adaptor::GoatInitConfig; + use client::client::BitVM2Client; + use esplora_client::BlockingClient; + use goat::connectors::base::generate_default_tx_in; + use goat::transactions::signing::populate_p2wsh_witness; + use musig2::secp256k1; + use std::process; + use uuid::Uuid; + + use ark_bn254::Bn254; + use ark_serialize::CanonicalDeserialize; + use bitcoin::{Address, Amount, Network, PrivateKey, PublicKey, Transaction, TxIn, TxOut}; + use bitcoin_script::script; + use bitvm::chunk::api::NUM_TAPS; + use bitvm2_lib::types::{Bitvm2Graph, Groth16WotsSignatures, WotsPublicKeys, WotsSecretKeys}; + use bitvm2_lib::verifier; + use bitvm2_lib::{ + committee, + keys::{CommitteeMasterKey, OperatorMasterKey}, + operator, + types::{Bitvm2Parameters, CustomInputs}, + }; + use goat::contexts::base::generate_n_of_n_public_key; + use goat::scripts::generate_burn_script_address; + use musig2::{PartialSignature, PubNonce, SecNonce}; + use std::str::FromStr; + + const BTCD_RPC_USER: &str = "111111"; + const BTCD_RPC_PASSWORD: &str = "111111"; + const BTCD_WALLET: &str = "alice"; + const BTCD_RPC_URL: &str = "http://127.0.0.1:3002"; + + //FIXME: The UTs should not use IPFS + const IPFS_ENDPOINT: &str = "http://44.229.236.82:5001"; + pub fn create_rpc_client() -> BlockingClient { + let builder = esplora_client::Builder::new(BTCD_RPC_URL); + let client = BlockingClient::from_builder(builder); + client + } + + fn temp_file() -> String { + let tmp_db = tempfile::NamedTempFile::new().unwrap(); + tmp_db.path().as_os_str().to_str().unwrap().to_string() + } + async fn create_bitvm2_client(network: Network) -> BitVM2Client { + let global_init_config = GoatInitConfig::from_env_for_test(); + BitVM2Client::new( + &temp_file(), + Some(BTCD_RPC_URL), + network, + GoatNetwork::Test, + global_init_config, + IPFS_ENDPOINT, + ) + .await + } + + pub fn get_regtest_address(network: Network) -> (bitcoin::key::PrivateKey, Address) { + let secp = secp256k1::Secp256k1::new(); + // Create a P2WPKH (bech32) address + let private_key = + PrivateKey::from_wif("cSWNzrM1CjFt1VZNBV7qTTr1t2fmZUgaQe2FL4jyFQRgTtrYp8Y5").unwrap(); + // Derive the public key + let address = Address::p2wpkh( + &CompressedPublicKey::from_private_key(&secp, &private_key).unwrap(), + network, + ); + let default_address = Address::from_str("bcrt1qvnhz5qn4q9vt2sgumajnm8gt53ggvmyyfwd0jg") + .unwrap() + .require_network(network) + .unwrap(); + assert_eq!(address, default_address); + let funding_address = + node_p2wsh_address(network, &PublicKey::from_private_key(&secp, &private_key)); + println!("funding address: {}", funding_address); + (private_key, funding_address) + } + + // TODO: derive sender address from depositor sk + async fn fund_address( + bitvm2_client: &BitVM2Client, + target_amount: Amount, + funding_addr: &Address, + depositor_private_key: &PrivateKey, + sender_addr: Address, + fee_rate: f64, + ) -> Transaction { + let inputs = get_proper_utxo_set( + &bitvm2_client, + PEGIN_BASE_VBYTES, + sender_addr.clone(), + target_amount, + fee_rate, + ) + .await + .unwrap() + .expect("Insufficient amount"); + let mut total_input_amount = Amount::ZERO; + let txins: Vec = inputs + .0 + .iter() + .map(|input| { + total_input_amount += input.amount; + generate_default_tx_in(input) + }) + .collect(); + let mut txouts = vec![]; + let output_0 = TxOut { value: target_amount, script_pubkey: funding_addr.script_pubkey() }; + txouts.push(output_0); + let change_amount = inputs.2; + if change_amount > Amount::from_sat(DUST_AMOUNT) { + let output_1 = + TxOut { value: change_amount, script_pubkey: sender_addr.script_pubkey() }; + txouts.push(output_1); + } + let mut tx = Transaction { + version: bitcoin::transaction::Version(2), + lock_time: bitcoin::absolute::LockTime::ZERO, + input: txins, + output: txouts, + }; + + let secp = secp256k1::Secp256k1::new(); + let script = node_p2wsh_script(&depositor_private_key.public_key(&secp)); + let keypair = Keypair::from_secret_key(&secp, &depositor_private_key.inner); + (0..tx.input.len()).into_iter().for_each(|index| { + let amount = inputs.0[index].amount; + populate_p2wsh_witness( + &mut tx, + index, + EcdsaSighashType::All, + &script, + amount, + &vec![&keypair], + ); + }); + tx + } + + fn broadcast_and_wait_for_confirming( + rpc_client: &BlockingClient, + tx: &Transaction, + confimations: u32, + ) { + let pre_current_tip = rpc_client.get_height().unwrap(); + let _ = rpc_client.broadcast(tx).unwrap(); + println!("Broadcast tx: {}", tx.compute_txid()); + let mut current_tip = rpc_client.get_height().unwrap(); + while (current_tip - pre_current_tip) < confimations { + mine_blocks(); + println!("Wait for at least {} block mined", current_tip - pre_current_tip); + std::thread::sleep(std::time::Duration::from_secs(1)); + current_tip = rpc_client.get_height().unwrap(); + } + } + + fn mine_blocks() { + let output = process::Command::new("docker") + .args([ + "exec", + "bitcoind", + "bitcoin-cli", + "-regtest", + &format!("-rpcuser={BTCD_RPC_USER}"), + &format!("-rpcpassword={BTCD_RPC_PASSWORD}"), + &format!("--rpcwallet={BTCD_WALLET}"), + "-generate", + "1", + ]) + .output() + .expect("Failed to execute docker command"); + + if output.status.success() { + let stdout = String::from_utf8_lossy(&output.stdout); + println!("Success:\n{}", stdout); + } else { + let stderr = String::from_utf8_lossy(&output.stderr); + eprintln!("Error:\n{}", stderr); + } + } + + struct E2eResult { + graph: Bitvm2Graph, + operator_keypair: Keypair, + operator_wots_seckeys: WotsSecretKeys, + operator_wots_pubkeys: WotsPublicKeys, + proof_sigs: Groth16WotsSignatures, + user_inputs: CustomInputs, + } + async fn e2e_setup( + network: Network, + rpc_client: &BlockingClient, + bitvm2_client: &BitVM2Client, + ) -> E2eResult { + let (depositor_private_key, depositor_addr) = get_regtest_address(network); + let graph_id = Uuid::new_v4(); + // key generation + let secp = secp256k1::Secp256k1::new(); + let instance_id = Uuid::new_v4(); + let committee_master_keys = (0..get_committee_member_num()) + .into_iter() + .map(|_x| { + let kp = secp.generate_keypair(&mut rand::thread_rng()); + CommitteeMasterKey::new(Keypair::from_secret_key(&secp, &kp.0)) + }) + .collect::>(); + let committee_pubkeys: Vec = committee_master_keys + .iter() + .map(|x| x.keypair_for_instance(instance_id).public_key().into()) + .collect(); + let (committee_agg_pubkey, _) = generate_n_of_n_public_key(&committee_pubkeys); + + let kp = secp.generate_keypair(&mut rand::thread_rng()); + let operator_master_key = OperatorMasterKey::new(Keypair::from_secret_key(&secp, &kp.0)); + let (operator_wots_seckeys, operator_wots_pubkeys) = + operator_master_key.wots_keypair_for_graph(graph_id.clone()); + let operator_p2wsh = node_p2wsh_address( + network.clone(), + &operator_master_key.keypair_for_graph(graph_id.clone()).public_key().into(), + ); + + let fee_rate = 1.0f64; + let pegin_amount = Amount::from_btc(0.1).unwrap(); + let stake_amount = Amount::from_btc(0.02).unwrap(); + let challenge_amount = Amount::from_btc(0.01).unwrap(); + + // fund the operator + let extra_fee = + Amount::from_sat(fee_rate as u64 * (PEGIN_BASE_VBYTES + PRE_KICKOFF_BASE_VBYTES)); + let funding_operator_txn = fund_address( + &bitvm2_client, + stake_amount + extra_fee, + &operator_p2wsh, + &depositor_private_key, + depositor_addr.clone(), + fee_rate, + ) + .await; + + println!("funding operator {}: {}", operator_p2wsh, funding_operator_txn.compute_txid()); + broadcast_and_wait_for_confirming(&rpc_client, &funding_operator_txn, 1); + + // mock groth16 proof + let mock_vk_bytes = [ + 115, 158, 251, 51, 106, 255, 102, 248, 22, 171, 229, 158, 80, 192, 240, 217, 99, 162, + 65, 107, 31, 137, 197, 79, 11, 210, 74, 65, 65, 203, 243, 14, 123, 2, 229, 125, 198, + 247, 76, 241, 176, 116, 6, 3, 241, 1, 134, 195, 39, 5, 124, 47, 31, 43, 164, 48, 120, + 207, 150, 125, 108, 100, 48, 155, 137, 132, 16, 193, 139, 74, 179, 131, 42, 119, 25, + 185, 98, 13, 235, 118, 92, 11, 154, 142, 134, 220, 191, 220, 169, 250, 244, 104, 123, + 7, 247, 33, 178, 155, 121, 59, 75, 188, 206, 198, 182, 97, 0, 64, 231, 45, 55, 92, 100, + 17, 56, 159, 79, 13, 219, 221, 33, 39, 193, 24, 36, 58, 105, 8, 70, 206, 176, 209, 146, + 45, 201, 157, 226, 84, 213, 135, 143, 178, 156, 112, 137, 246, 123, 248, 215, 168, 51, + 95, 177, 47, 57, 29, 199, 224, 98, 48, 144, 253, 15, 201, 192, 142, 62, 143, 13, 228, + 89, 51, 58, 6, 226, 139, 99, 207, 22, 113, 215, 79, 91, 158, 166, 210, 28, 90, 218, + 111, 151, 4, 55, 230, 76, 90, 209, 149, 113, 248, 245, 50, 231, 137, 51, 157, 40, 29, + 184, 198, 201, 108, 199, 89, 67, 136, 239, 96, 216, 237, 172, 29, 84, 3, 128, 240, 2, + 218, 169, 217, 118, 179, 34, 226, 19, 227, 59, 193, 131, 108, 20, 113, 46, 170, 196, + 156, 45, 39, 151, 218, 22, 132, 250, 209, 183, 46, 249, 115, 239, 14, 176, 200, 134, + 158, 148, 139, 212, 167, 152, 205, 183, 236, 242, 176, 96, 177, 187, 184, 252, 14, 226, + 127, 127, 173, 147, 224, 220, 8, 29, 63, 73, 215, 92, 161, 110, 20, 154, 131, 23, 217, + 116, 145, 196, 19, 167, 84, 185, 16, 89, 175, 180, 110, 116, 57, 198, 237, 147, 183, + 164, 169, 220, 172, 52, 68, 175, 113, 244, 62, 104, 134, 215, 99, 132, 199, 139, 172, + 108, 143, 25, 238, 201, 128, 85, 24, 73, 30, 186, 142, 186, 201, 79, 3, 176, 185, 70, + 66, 89, 127, 188, 158, 209, 83, 17, 22, 187, 153, 8, 63, 58, 174, 236, 132, 226, 43, + 145, 97, 242, 198, 117, 105, 161, 21, 241, 23, 84, 32, 62, 155, 245, 172, 30, 78, 41, + 199, 219, 180, 149, 193, 163, 131, 237, 240, 46, 183, 186, 42, 201, 49, 249, 142, 188, + 59, 212, 26, 253, 23, 27, 205, 231, 163, 76, 179, 135, 193, 152, 110, 91, 5, 218, 67, + 204, 164, 128, 183, 221, 82, 16, 72, 249, 111, 118, 182, 24, 249, 91, 215, 215, 155, 2, + 0, 0, 0, 0, 0, 0, 0, 212, 110, 6, 228, 73, 146, 46, 184, 158, 58, 94, 4, 141, 241, 158, + 0, 175, 140, 72, 75, 52, 6, 72, 49, 112, 215, 21, 243, 151, 67, 106, 22, 158, 237, 80, + 204, 41, 128, 69, 52, 154, 189, 124, 203, 35, 107, 132, 241, 234, 31, 3, 165, 87, 58, + 10, 92, 252, 227, 214, 99, 176, 66, 118, 22, 177, 20, 120, 198, 252, 236, 7, 148, 207, + 78, 152, 132, 94, 207, 50, 243, 4, 169, 146, 240, 79, 98, 0, 212, 106, 137, 36, 193, + 21, 175, 180, 1, 26, 107, 39, 198, 89, 152, 26, 220, 138, 105, 243, 45, 63, 106, 163, + 80, 74, 253, 176, 207, 47, 52, 7, 84, 59, 151, 47, 178, 165, 112, 251, 161, + ] + .to_vec(); + let mock_proof_bytes: Vec = [ + 162, 50, 57, 98, 3, 171, 250, 108, 49, 206, 73, 126, 25, 35, 178, 148, 35, 219, 98, 90, + 122, 177, 16, 91, 233, 215, 222, 12, 72, 184, 53, 2, 62, 166, 50, 68, 98, 171, 218, + 218, 151, 177, 133, 223, 129, 53, 114, 236, 181, 215, 223, 91, 102, 225, 52, 122, 122, + 206, 36, 122, 213, 38, 186, 170, 235, 210, 179, 221, 122, 37, 74, 38, 79, 0, 26, 94, + 59, 146, 46, 252, 70, 153, 236, 126, 194, 169, 17, 144, 100, 218, 118, 22, 99, 226, + 132, 40, 24, 248, 232, 197, 195, 220, 254, 52, 36, 248, 18, 167, 167, 206, 108, 29, + 120, 188, 18, 78, 86, 8, 121, 217, 144, 185, 122, 58, 12, 34, 44, 6, 233, 80, 177, 183, + 5, 8, 150, 74, 241, 141, 65, 150, 35, 98, 15, 150, 137, 254, 132, 167, 228, 104, 63, + 133, 11, 209, 39, 79, 138, 185, 88, 20, 242, 102, 69, 73, 243, 88, 29, 91, 127, 157, + 82, 192, 52, 95, 143, 49, 227, 83, 19, 26, 108, 63, 232, 213, 169, 64, 221, 159, 214, + 220, 246, 174, 35, 43, 143, 80, 168, 142, 29, 103, 179, 58, 235, 33, 163, 198, 255, + 188, 20, 3, 91, 47, 158, 122, 226, 201, 175, 138, 18, 24, 178, 219, 78, 12, 96, 10, 2, + 133, 35, 230, 149, 235, 206, 1, 177, 211, 245, 168, 74, 62, 25, 115, 70, 42, 38, 131, + 92, 103, 103, 176, 212, 223, 177, 242, 94, 14, + ] + .to_vec(); + let mock_scalar = [ + 232, 255, 255, 239, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88, + 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48, + ] + .to_vec(); + let proof: ark_groth16::Proof = + ark_groth16::Proof::deserialize_uncompressed(&mock_proof_bytes[..]).unwrap(); + let vk: ark_groth16::VerifyingKey = + ark_groth16::VerifyingKey::deserialize_uncompressed(&mock_vk_bytes[..]).unwrap(); + let scalar: ark_bn254::Fr = + ark_bn254::Fr::deserialize_uncompressed(&mock_scalar[..]).unwrap(); + let scalars = vec![scalar]; + let proof_sigs = operator::sign_proof(&vk, proof, scalars, &operator_wots_seckeys); + + let depositor_evm_address: [u8; 20] = + hex::decode("3eAC5F367F19E2E6099e897436DC17456f078609").unwrap().try_into().unwrap(); + + let inputs = get_proper_utxo_set( + &bitvm2_client, + PEGIN_BASE_VBYTES, + depositor_addr.clone(), + pegin_amount, + fee_rate, + ) + .await + .unwrap() + .expect("Insufficient amount for peg-in"); + + let user_inputs = CustomInputs { + inputs: inputs.0.clone(), + input_amount: pegin_amount, + fee_amount: inputs.1, + change_address: depositor_addr.clone(), + }; + + let inputs = get_proper_utxo_set( + &bitvm2_client, + PRE_KICKOFF_BASE_VBYTES, + operator_p2wsh, + stake_amount, + fee_rate, + ) + .await + .unwrap() + .expect("Insufficient amount for kickoff staking"); + + let operator_inputs = CustomInputs { + inputs: inputs.0.clone(), + input_amount: stake_amount, + fee_amount: inputs.1, + change_address: depositor_addr.clone(), + }; + + let operator_keypair = operator_master_key.keypair_for_graph(graph_id.clone()); + let params = Bitvm2Parameters { + network, + depositor_evm_address, + pegin_amount, + stake_amount, + challenge_amount, + committee_pubkeys, + committee_agg_pubkey, + operator_pubkey: operator_keypair.public_key().into(), + operator_wots_pubkeys: operator_wots_pubkeys.clone(), + user_inputs: user_inputs.clone(), + operator_inputs: operator_inputs.clone(), + }; + + //let partial_scripts = operator::generate_partial_scripts(&vk); + let partial_scripts = crate::utils::get_partial_scripts().unwrap(); + let disprove_scripts = + operator::generate_disprove_scripts(&partial_scripts, &operator_wots_pubkeys); + + let disprove_scripts_bytes = disprove_scripts + .iter() + .map(|sc| sc.clone().compile().to_bytes().to_vec()) + .collect::>>(); + + let mut graph = operator::generate_bitvm_graph(params, disprove_scripts_bytes).unwrap(); + + // opeartor pre-sign + println!("\nopeartor pre-sign"); + let _ = operator::operator_pre_sign(operator_keypair.clone(), &mut graph); + + // committee pre-sign + println!("\ncommittee pre-sign"); + let committee_nonce: Vec<[(_, _, _); COMMITTEE_PRE_SIGN_NUM]> = committee_master_keys + .iter() + .map(|cmk| cmk.nonces_for_graph(instance_id.clone(), graph_id.clone())) + .collect(); + let pubnonces: Vec<[PubNonce; COMMITTEE_PRE_SIGN_NUM]> = committee_nonce + .iter() + .map(|nonces| std::array::from_fn(|i| nonces[i].1.clone())) + .collect(); + let secnonces: Vec<[SecNonce; COMMITTEE_PRE_SIGN_NUM]> = committee_nonce + .iter() + .map(|nonces| std::array::from_fn(|i| nonces[i].0.clone())) + .collect(); + let agg_nonces = nonces_aggregation(pubnonces); + + let committee_partial_sigs: Vec<_> = committee_master_keys + .iter() + .enumerate() + .map(|(idx, cmk)| { + let sec_nonce = &secnonces[idx]; + committee_pre_sign( + cmk.keypair_for_instance(instance_id.clone()), + sec_nonce.clone(), + agg_nonces.clone(), + &graph, + ) + .unwrap() + }) + .collect(); + + // e.g + // [0, 1] + // [0, 1] + // [0, 1] + // [0, 1] + // [0, 1] + // ==> + // [0, 0, 0, 0, 0] + // [1, 1, 1, 1, 1] + let mut grouped_partial_sigs: [Vec; COMMITTEE_PRE_SIGN_NUM] = + Default::default(); + for partial_sigs in committee_partial_sigs { + for (i, sig) in partial_sigs.into_iter().enumerate() { + grouped_partial_sigs[i].push(sig); + } + } + + let _ = committee::signature_aggregation_and_push( + &grouped_partial_sigs, + &agg_nonces, + &mut graph, + ) + .expect("signatures aggregation and push"); + + // peg-in + let amounts = graph.pegin.input_amounts.clone(); + let keypair = Keypair::from_secret_key(&secp, &depositor_private_key.inner); + (0..graph.pegin.tx().input.len()).into_iter().for_each(|idx| { + let amount = amounts[idx].clone(); + node_sign(graph.pegin.tx_mut(), idx, amount, EcdsaSighashType::All, &keypair) + .expect("peg-in signing failed"); + }); + + println!("broadcast pegin"); + broadcast_and_wait_for_confirming(&rpc_client, &graph.pegin.tx(), 1); + + E2eResult { + graph, + operator_keypair, + operator_wots_seckeys, + operator_wots_pubkeys, + proof_sigs, + user_inputs, + } + } + ///////////// + #[tokio::test] + async fn e2e_take_1() { + let network = Network::Regtest; + let rpc_client = create_rpc_client(); + let bitvm2_client = create_bitvm2_client(network).await; + + let E2eResult { + mut graph, + operator_keypair, + operator_wots_seckeys, + operator_wots_pubkeys, + .. + } = e2e_setup(network, &rpc_client, &bitvm2_client).await; + // pre-kick-off + println!("broadcast pre-kickoff"); + let amounts = graph.pre_kickoff.input_amounts.clone(); + (0..graph.pre_kickoff.tx().input.len()).into_iter().for_each(|idx| { + let amount = amounts[idx].clone(); + node_sign( + graph.pre_kickoff.tx_mut(), + idx, + amount, + EcdsaSighashType::All, + &operator_keypair, + ) + .expect("pre kickoff signing failed"); + }); + broadcast_and_wait_for_confirming(&rpc_client, &graph.pre_kickoff.tx(), 1); + + // kick off + println!("broadcast kickoff"); + let withdraw_evm_txid = [0xff; 32]; + let kickoff_tx = operator::operator_sign_kickoff( + operator_keypair, + &mut graph, + &operator_wots_seckeys, + &operator_wots_pubkeys, + withdraw_evm_txid, + ) + .unwrap(); + broadcast_and_wait_for_confirming(&rpc_client, &kickoff_tx, 7); + + // take 1 + println!("broadcast take1"); + let take_1_tx = operator::operator_sign_take1(operator_keypair, &mut graph).unwrap(); + broadcast_and_wait_for_confirming(&rpc_client, &take_1_tx, 1); + } + + #[tokio::test] + async fn e2e_take_2() { + let network = Network::Regtest; + let rpc_client = create_rpc_client(); + let bitvm2_client = create_bitvm2_client(network).await; + + let E2eResult { + mut graph, + operator_keypair, + operator_wots_seckeys, + operator_wots_pubkeys, + proof_sigs, + user_inputs, + .. + } = e2e_setup(network, &rpc_client, &bitvm2_client).await; + + println!("broadcast pre-kickoff"); + let amounts = graph.pre_kickoff.input_amounts.clone(); + (0..graph.pre_kickoff.tx().input.len()).into_iter().for_each(|idx| { + let amount = amounts[idx].clone(); + node_sign( + graph.pre_kickoff.tx_mut(), + idx, + amount, + EcdsaSighashType::All, + &operator_keypair, + ) + .expect("pre kickoff signing failed"); + }); + broadcast_and_wait_for_confirming(&rpc_client, &graph.pre_kickoff.tx(), 1); + + // kick off + println!("broadcast kickoff"); + let withdraw_evm_txid = [0xff; 32]; + let kickoff_tx = operator::operator_sign_kickoff( + operator_keypair, + &mut graph, + &operator_wots_seckeys, + &operator_wots_pubkeys, + withdraw_evm_txid, + ) + .unwrap(); + broadcast_and_wait_for_confirming(&rpc_client, &kickoff_tx, 7); + + // unhappy_path take + let (mut challenge_tx, _) = verifier::export_challenge_tx(&mut graph).unwrap(); + let mock_crowdfund_txin = TxIn { + previous_output: user_inputs.inputs[0].outpoint.clone(), //mock_input.outpoint, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::default(), + }; + let mock_challenger_change_output = TxOut { + script_pubkey: generate_burn_script_address(network).script_pubkey(), + value: Amount::from_sat(1000000), + }; + challenge_tx.input.push(mock_crowdfund_txin); + challenge_tx.output.push(mock_challenger_change_output); + + println!("Broadcast challenge txin"); + broadcast_and_wait_for_confirming(&rpc_client, &challenge_tx, 6); + + let (assert_init_tx, assert_commit_txns, assert_final_tx) = operator::operator_sign_assert( + operator_keypair, + &mut graph, + &operator_wots_pubkeys, + proof_sigs.clone(), + ) + .unwrap(); + //broadcast_tx(assert_init_tx); + broadcast_and_wait_for_confirming(&rpc_client, &assert_init_tx, 1); + + assert_commit_txns.iter().for_each(|tx| { + //broadcast_tx(tx.clone()) + broadcast_and_wait_for_confirming(&rpc_client, tx, 1); + }); + + //broadcast_tx(assert_final_tx); + broadcast_and_wait_for_confirming(&rpc_client, &assert_final_tx, 1); + + let take2_tx = operator::operator_sign_take2(operator_keypair, &mut graph).unwrap(); + // broadcast_tx(take2_tx); + broadcast_and_wait_for_confirming(&rpc_client, &take2_tx, 7); + + // // disprove + // /* + // // verify proof published by assert-txns: + //let public_proof_sigs = verifier::extract_proof_sigs_from_assert_commit_txns(assert_commit_txns).unwrap(); + //let disprove_witness = verifier::verify_proof( + // &vk, + // public_proof_sigs, + // &mock_disprove_scripts, + // &operator_wots_pubkeys, + //).unwrap(); + //*/ + //let mock_disprove_witness = (0, mock_script); + //let mock_challenger_reward_address = generate_burn_script_address(network); + //let disprove_tx = verifier::sign_disprove( + // &mut graph, + // mock_disprove_witness, + // mock_disprove_scripts_bytes.to_vec(), + // &operator_wots_pubkeys.1, + // mock_challenger_reward_address, + //) + // .unwrap(); + //broadcast_tx(disprove_tx); + } + + #[tokio::test] + async fn e2e_disprove() { + let network = Network::Regtest; + let rpc_client = create_rpc_client(); + let bitvm2_client = create_bitvm2_client(network).await; + + let E2eResult { + mut graph, + operator_keypair, + operator_wots_seckeys, + operator_wots_pubkeys, + proof_sigs, + user_inputs, + .. + } = e2e_setup(network, &rpc_client, &bitvm2_client).await; + + println!("broadcast pre-kickoff"); + let amounts = graph.pre_kickoff.input_amounts.clone(); + (0..graph.pre_kickoff.tx().input.len()).into_iter().for_each(|idx| { + let amount = amounts[idx].clone(); + node_sign( + graph.pre_kickoff.tx_mut(), + idx, + amount, + EcdsaSighashType::All, + &operator_keypair, + ) + .expect("pre kickoff signing failed"); + }); + broadcast_and_wait_for_confirming(&rpc_client, &graph.pre_kickoff.tx(), 1); + + // kick off + println!("broadcast kickoff"); + let withdraw_evm_txid = [0xff; 32]; + let kickoff_tx = operator::operator_sign_kickoff( + operator_keypair, + &mut graph, + &operator_wots_seckeys, + &operator_wots_pubkeys, + withdraw_evm_txid, + ) + .unwrap(); + broadcast_and_wait_for_confirming(&rpc_client, &kickoff_tx, 7); + + // unhappy_path take + let (mut challenge_tx, _) = verifier::export_challenge_tx(&mut graph).unwrap(); + let mock_crowdfund_txin = TxIn { + previous_output: user_inputs.inputs[0].outpoint.clone(), //mock_input.outpoint, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::default(), + }; + let mock_challenger_change_output = TxOut { + script_pubkey: generate_burn_script_address(network).script_pubkey(), + value: Amount::from_sat(1000000), + }; + challenge_tx.input.push(mock_crowdfund_txin); + challenge_tx.output.push(mock_challenger_change_output); + + println!("Broadcast challenge txin"); + broadcast_and_wait_for_confirming(&rpc_client, &challenge_tx, 6); + + let (assert_init_tx, assert_commit_txns, assert_final_tx) = operator::operator_sign_assert( + operator_keypair, + &mut graph, + &operator_wots_pubkeys, + proof_sigs.clone(), + ) + .unwrap(); + //broadcast_tx(assert_init_tx); + broadcast_and_wait_for_confirming(&rpc_client, &assert_init_tx, 1); + + assert_commit_txns.iter().for_each(|tx| { + //broadcast_tx(tx.clone()) + broadcast_and_wait_for_confirming(&rpc_client, tx, 1); + }); + + //broadcast_tx(assert_final_tx); + broadcast_and_wait_for_confirming(&rpc_client, &assert_final_tx, 1); + + // disprove + /* + // verify proof published by assert-txns: + let public_proof_sigs = verifier::extract_proof_sigs_from_assert_commit_txns(assert_commit_txns).unwrap(); + let disprove_witness = verifier::verify_proof( + &vk, + public_proof_sigs, + &mock_disprove_scripts, + &operator_wots_pubkeys, + ).unwrap(); + */ + let mock_script = script! {OP_TRUE}; + let mock_script_bytes = mock_script.clone().compile().to_bytes(); + let mock_disprove_scripts_bytes: [Vec; NUM_TAPS] = + std::array::from_fn(|_| mock_script_bytes.clone()); + + let mock_disprove_witness = (0, mock_script); + let mock_challenger_reward_address = generate_burn_script_address(network); + let disprove_tx = verifier::sign_disprove( + &mut graph, + mock_disprove_witness, + mock_disprove_scripts_bytes.to_vec(), + &operator_wots_pubkeys.1, + mock_challenger_reward_address, + ) + .unwrap(); + broadcast_and_wait_for_confirming(&rpc_client, &disprove_tx, 1); + } +} diff --git a/node/src/utils.rs b/node/src/utils.rs index f9958aef..475aa3dd 100644 --- a/node/src/utils.rs +++ b/node/src/utils.rs @@ -445,6 +445,7 @@ pub async fn get_proper_utxo_set( }) .collect() } + println!("get utxos from: {}", address); let utxos = client.esplora.get_address_utxo(address).await?; let mut sorted_utxos = utxos; @@ -499,7 +500,7 @@ pub fn node_sign( tx, input_index, sighash_type, - &node_p2wsh_script(&node_pubkey), + &node_p2wsh_script(&node_pubkey.into()), input_value, &vec![node_keypair], ); @@ -1035,23 +1036,14 @@ mod tests { use crate::action::{CreateInstance, GOATMessageContent, KickoffReady}; use super::*; + use bitcoin::Address; use client::chain::{chain_adaptor::GoatNetwork, goat_adaptor::GoatInitConfig}; use goat::connectors::base::generate_default_tx_in; - use reqwest::Url; use serial_test::serial; use std::fmt; async fn test_client() -> BitVM2Client { - let global_init_config = GoatInitConfig { - rpc_url: "https://rpc.testnet3.goat.network".parse::().expect("decode url"), - gateway_address: "0xeD8AeeD334fA446FA03Aa00B28aFf02FA8aC02df" - .parse() - .expect("parse contract address"), - gateway_creation_block: 0, - to_block: None, - private_key: None, - chain_id: 48816_u32, - }; + let global_init_config = GoatInitConfig::from_env_for_test(); // let local_db = LocalDB::new(&format!("sqlite:{db_path}"), true).await; let tmp_db = tempfile::NamedTempFile::new().unwrap(); BitVM2Client::new( @@ -1602,16 +1594,7 @@ mod tests { #[tokio::test] #[ignore = "debug"] async fn load_graph() { - let global_init_config = GoatInitConfig { - rpc_url: "https://rpc.testnet3.goat.network".parse::().expect("decode url"), - gateway_address: "0xeD8AeeD334fA446FA03Aa00B28aFf02FA8aC02df" - .parse() - .expect("parse contract address"), - gateway_creation_block: 0, - to_block: None, - private_key: None, - chain_id: 48816_u32, - }; + let global_init_config = GoatInitConfig::from_env_for_test(); let client = BitVM2Client::new( "/tmp/bitvm2-node-0.db", None, @@ -1623,6 +1606,10 @@ mod tests { .await; let instance_id = Uuid::parse_str("85b378bc-1b2a-4c59-a116-bdf3fbdf14e0").unwrap(); let graph_id = Uuid::parse_str("ca010566-d7a7-49c8-8c62-9ddb8dd988ec").unwrap(); + + // store a graph + + // retrieve the graph let graph = get_graph(&client, instance_id, graph_id).await.unwrap(); let stake_amount = graph.parameters.stake_amount.to_sat(); diff --git a/scripts/bitcoin-init.sh b/scripts/bitcoin-init.sh new file mode 100755 index 00000000..544224a0 --- /dev/null +++ b/scripts/bitcoin-init.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +#docker rm -f bitcoin-server +#docker run --name bitcoin-server -d -v $HOME/bitcoin:/root/bitcoin -p 18443:18443 -p 8332:8332 -p 18332:18332 -it ruimarinho/bitcoin-core -regtest=1 -rpcbind='0.0.0.0' -rpcallowip='0.0.0.0/0' -fallbackfee='0.01' -txindex=1 -rpcuser=111111 -rpcpassword=111111 +#sleep 2 + bitcoind -daemon \ + -server=1 \ + -datadir=/bitcoin \ + -regtest=1 \ + -txindex=1 \ + -fallbackfee='0.01' \ + -rpcallowip=0.0.0.0/0 \ + -rpcbind=0.0.0.0 \ + -rpcuser=111111 \ + -rpcpassword=111111 + +# Wait until RPC is ready +sleep 5 +while ! bitcoin-cli -regtest -rpcuser=111111 -rpcpassword=111111 getblockchaininfo > /dev/null 2>&1; do + echo "Waiting for bitcoind..." + sleep 2 +done + +# install bitcoin-cli on MacOS: `brew install bitcoin` +#USE_DOCKER="docker exec -it bitcoin-server" +export BTC="${USE_DOCKER} bitcoin-cli -regtest -rpcuser=111111 -rpcpassword=111111" + +$BTC -named createwallet \ + wallet_name=alice \ + passphrase="btcstaker" \ + load_on_startup=true \ + descriptors=false + +$BTC loadwallet "alice" + +$BTC --rpcwallet=alice walletpassphrase "btcstaker" 600 + +$BTC --rpcwallet=alice -generate 1000 + +## prepare a funded wallet +#address=`$BTC -rpcwallet=alice getnewaddress` +#$BTC importprivkey "cSWNzrM1CjFt1VZNBV7qTTr1t2fmZUgaQe2FL4jyFQRgTtrYp8Y5" "testonly" false +#address="bcrt1qvnhz5qn4q9vt2sgumajnm8gt53ggvmyyfwd0jg" + +address="bcrt1q7tr8sl50zanztcrps35hakqpe7gmfzedhhnxcspj7n0ks5lyrnhs6m8ewg" +## fund the address +$BTC --rpcwallet=alice sendtoaddress $address 1 +$BTC --rpcwallet=alice -generate 10 + +#privkey=`$BTC --rpcwallet=alice dumpprivkey $address` +#echo $privkey > $DIR/../.key.test +tail -f /dev/null diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml new file mode 100644 index 00000000..04a01d4e --- /dev/null +++ b/scripts/docker-compose.yml @@ -0,0 +1,40 @@ +services: + bitcoind: + image: ruimarinho/bitcoin-core + container_name: bitcoind + restart: unless-stopped + volumes: + - ./bitcoin-data:/bitcoin + - ./bitcoin-init.sh:/usr/local/bin/bitcoin-init.sh + entrypoint: ["/bin/bash", "/usr/local/bin/bitcoin-init.sh"] + ports: + - "18443:18443" + - "18444:18444" + networks: + - regtest + + electrs: + image: mempool/electrs + container_name: electrs-regtest + restart: unless-stopped + ports: + - "3002:3002" + depends_on: + - bitcoind + entrypoint: > + electrs + --network regtest + --daemon-rpc-addr bitcoind:18443 + --daemon-dir /bitcoin + --http-addr 0.0.0.0:3002 + --cookie "111111:111111" + -v + volumes: + - ./electrs-data:/data + - ./bitcoin-data:/bitcoin + networks: + - regtest +networks: + regtest: + # Specify driver options + driver: bridge