diff --git a/goat/src/disprove_scripts.rs b/goat/src/disprove_scripts.rs index b69396ff..fbfa8b4f 100644 --- a/goat/src/disprove_scripts.rs +++ b/goat/src/disprove_scripts.rs @@ -7,12 +7,14 @@ use bitvm::chunk::api::{ }; use bitvm::hash::sha256_u4::sha256 as sha256_u4; use bitvm::signatures::{Wots, Wots16, Wots32}; +#[allow(unused_imports)] use bitvm::u4::u4_std::u4_hex_to_nibbles; use bitvm::{treepp::*, FmtStack}; pub type ChallengeHashType = [u8; 20]; // OP_HASH160 -pub const GUEST_PUBIN_COMMITMENT_INDEX: usize = 1; +pub const GUEST_PUBIN_G16_PUBIN_INDEX: usize = 1; +pub const GUEST_PUBIN_COMMITMENT_INDEX: usize = 0; // public inputs are reversed in assertions pub const NUM_GUEST_PUBS_ASSERT: usize = 2; // commit [constants, watchtower-inclued-map] pub const NUM_GUEST_PUBS_EXTRA: usize = 1; // commit blockhash pub const NUM_GUEST: usize = NUM_GUEST_PUBS_ASSERT + NUM_GUEST_PUBS_EXTRA; @@ -54,22 +56,34 @@ pub fn validate_guest_assertions( } pub fn generate_guest_pubin_commitment(guest_pubin_num: u32) -> Script { - fn push_length_prefix_rev_u4() -> Script { - script! { - { u4_hex_to_nibbles("0000000000000020")} - } - } script! { for i in 0..guest_pubin_num as usize { - { push_length_prefix_rev_u4() } - { lift_and_reverse_bytes_u4(80 * i + 16, 32) } + { lift_and_reverse_bytes_u4(64 * i, 32) } } - { sha256_u4(guest_pubin_num * 40) } + { sha256_u4(guest_pubin_num * 32) } { reverse_bytes_u4(32) } OP_SWAP { mod2_u4() } OP_SWAP } } +// // with 8-bytes length prefix (deprecated) +// pub fn generate_guest_pubin_commitment(guest_pubin_num: u32) -> Script { +// fn push_length_prefix_rev_u4() -> Script { +// script! { +// { u4_hex_to_nibbles("0000000000000020")} +// } +// } +// script! { +// for i in 0..guest_pubin_num as usize { +// { push_length_prefix_rev_u4() } +// { lift_and_reverse_bytes_u4(80 * i + 16, 32) } +// } +// { sha256_u4(guest_pubin_num * 40) } +// { reverse_bytes_u4(32) } +// OP_SWAP { mod2_u4() } OP_SWAP +// } +// } + pub fn verify_guest_pubin( guest_pubin_wots_pubkeys: &[::PublicKey; NUM_GUEST], groth16_pubin_wots_pubkeys: &[::PublicKey; NUM_PUBS], @@ -98,11 +112,11 @@ pub fn verify_guest_pubin( { Wots32::checksig_verify(&groth16_pubin_wots_pubkeys[GUEST_PUBIN_COMMITMENT_INDEX]) } - { zip_nibbles_bytes32() } + { reverse_zip_nibbles_bytes32() } { roll_n(zipped_wots32_msg_stack_items_num, wots32_msg_stack_items_num * 3) } - { generate_guest_pubin_commitment(NUM_GUEST as u32) } + { generate_guest_pubin_commitment(NUM_GUEST as u32) } // guest-pubin-commitment = hash(blockhash || constant || included_watchtowers_map) { zip_nibbles_bytes32() } @@ -354,6 +368,16 @@ fn zip_nibbles_bytes32() -> Script { } } +fn reverse_zip_nibbles_bytes32() -> Script { + script! { + for _ in 0..32 { + { roll(62) } + { roll(63) } + } + { zip_nibbles_bytes32() } + } +} + pub fn bits_to_bytes32(bits: &[bool]) -> [u8; 32] { let mut out = [0u8; 32]; let len = bits.len().min(256); @@ -442,6 +466,17 @@ pub fn parse_u4_stack(start_index: usize, num_bytes: usize, stack: &FmtStack) -> v } +pub fn reverse_each_byte(input: [u8; 32]) -> [u8; 32] { + let mut output = [0u8; 32]; + for i in 0..32 { + let byte = input[i]; + let high_nibble = (byte & 0xF0) >> 4; + let low_nibble = byte & 0x0F; + output[i] = (low_nibble << 4) | high_nibble; + } + output +} + #[test] fn test_hash160() { let input = "hello world".as_bytes().to_vec(); @@ -596,61 +631,7 @@ fn test_verify_hashlock_pubin_script() { } #[test] -fn test_generate_guest_pubin_commitment() { - fn u4_bytes_to_nibbles(bytes: &[u8]) -> Script { - let mut rev_bytes = bytes.to_vec(); - rev_bytes.reverse(); - // u4_hex_to_nibbles takes little-endian hex strings - script! { - { u4_hex_to_nibbles(&hex::encode(rev_bytes)) } - } - } - let pubins: Vec<[u8; 32]> = vec![ - hex::decode("395bf727f9aac5e80911591073fcf9c826f428804131ca089beba3869421749a") - .unwrap() - .try_into() - .unwrap(), - hex::decode("7517017f1ed26c3a0eeeeff8cd8415a0f90f4f8ece83f639a625e62f399c9ac1") - .unwrap() - .try_into() - .unwrap(), - hex::decode("0fd7e7b3810d822c9804b48d0e1334ed98690f3a78c3baceeefb88fb416b49b9") - .unwrap() - .try_into() - .unwrap(), - ]; - let pubin_bytes = hex::decode("0000000000000020395bf727f9aac5e80911591073fcf9c826f428804131ca089beba3869421749a00000000000000207517017f1ed26c3a0eeeeff8cd8415a0f90f4f8ece83f639a625e62f399c9ac100000000000000200fd7e7b3810d822c9804b48d0e1334ed98690f3a78c3baceeefb88fb416b49b9").unwrap(); - let _sha256_digest: [u8; 32] = - hex::decode("7702d98383c9971311fc63deed6edb20a1a7d19a01c7587fdd9c8b76a98ae798") - .unwrap() - .try_into() - .unwrap(); - let commit_value: [u8; 32] = - hex::decode("1702d98383c9971311fc63deed6edb20a1a7d19a01c7587fdd9c8b76a98ae798") - .unwrap() - .try_into() - .unwrap(); - let _pubin_bytes_rev = { - let mut v = pubin_bytes.clone(); - v.reverse(); - v - }; - - let s = script! { - { u4_bytes_to_nibbles(&pubins[2]) } - { u4_bytes_to_nibbles(&pubins[1]) } - { u4_bytes_to_nibbles(&pubins[0]) } - { generate_guest_pubin_commitment(3) } - }; - let result = execute_script(s); - assert_eq!( - commit_value.to_vec(), - parse_u4_stack(0, 32, &result.final_stack) - ); -} - -#[test] -fn test_verify_guest_pubin() { +fn test_verify_guest_pubin_ziren() { use hex::FromHex; let secrets = std::iter::repeat(Wots32::generate_secret_key()) .take(NUM_GUEST + NUM_PUBS) @@ -660,25 +641,41 @@ fn test_verify_guest_pubin() { .map(|s| Wots32::generate_public_key(s)) .collect::>(); let blockhash = - <[u8; 32]>::from_hex("395bf727f9aac5e80911591073fcf9c826f428804131ca089beba3869421749a") + <[u8; 32]>::from_hex("5a690bba0ba076d621f77665398f4b1ddbfc2349bbb3e8880307625ac5cfa900") .unwrap(); let constant = - <[u8; 32]>::from_hex("7517017f1ed26c3a0eeeeff8cd8415a0f90f4f8ece83f639a625e62f399c9ac1") + <[u8; 32]>::from_hex("2df7bde0605973f5809a1338094cb47a309bc38363dd35ce178c088cd3cda79f") .unwrap(); - // 1011010100011010 let included_bitmap = - <[u8; 32]>::from_hex("b51a000000000000000000000000000000000000000000000000000000000000") - .unwrap(); - let groth16_pubin = - <[u8; 32]>::from_hex("1351297318a8febcdabb450df8e3ea4c61c1c2218d36a3df3ed887ca548d38a9") + <[u8; 32]>::from_hex("0100000000000000000000000000000000000000000000000000000000000000") .unwrap(); - let hashes_len = 16; + // let groth16_pubin = + // <[u8; 32]>::from_hex("1a5605834864faf9cb10055606d9ae06425ea5cf8cf757f996182cd1da196158") + // .unwrap(); + + use ark_serialize::CanonicalDeserialize; + use ark_ff::{BigInteger, PrimeField}; + // let proof_file = "ziren/proof.bin"; + // let proof_bin = std::fs::read(proof_file).unwrap(); + // let groth16_proof = ark_groth16::Proof::::deserialize_compressed(&*proof_bin).unwrap(); + // let vk_file = "ziren/vk.bin"; + // let vk_bin = std::fs::read(vk_file).unwrap(); + // let groth16_vkey = ark_groth16::VerifyingKey::::deserialize_compressed(&*vk_bin).unwrap(); + let pubin_file = "ziren/public_inputs.bin"; + let pubin_bin = std::fs::read(pubin_file).unwrap(); + let groth16_public_inputs = <[ark_bn254::Fr; 2]>::deserialize_compressed(&*pubin_bin).unwrap(); + let guest_pubin_commitment = groth16_public_inputs[GUEST_PUBIN_G16_PUBIN_INDEX]; + let guest_pubin_commitment_bytes = guest_pubin_commitment.into_bigint().to_bytes_be(); + let mut groth16_pubin = [0u8; 32]; + groth16_pubin[32 - guest_pubin_commitment_bytes.len()..] + .copy_from_slice(&guest_pubin_commitment_bytes); + + let hashes_len = 2; let mut preimages: Vec> = vec![]; let mut hashes: Vec<[u8; 20]> = vec![]; for i in 0..hashes_len { preimages.push(format!("preimage_{:02x}", i).into_bytes()); hashes.push(hash160(&preimages[i].clone())); - // println!("hash {}: {:?}", i, hex::encode(hashes[i].iter().rev().cloned().collect::>())); } let lock_scr = script! { @@ -696,7 +693,7 @@ fn test_verify_guest_pubin() { let mut input_preimages = vec![vec![]; hashes_len]; input_preimages[1] = preimages[1].clone(); let full_scr = script! { - { Wots32::sign_to_raw_witness(&secrets[NUM_GUEST + GUEST_PUBIN_COMMITMENT_INDEX], &groth16_pubin) } + { Wots32::sign_to_raw_witness(&secrets[NUM_GUEST + GUEST_PUBIN_COMMITMENT_INDEX], &reverse_each_byte(groth16_pubin)) } { Wots32::sign_to_raw_witness(&secrets[0], &blockhash) } { Wots32::sign_to_raw_witness(&secrets[1], &constant) } { push_preimage_to_stack(&input_preimages) } @@ -715,7 +712,7 @@ fn test_verify_guest_pubin() { let mut mismatched_constant = constant.clone(); mismatched_constant[0] ^= 0xFF; let full_scr = script! { - { Wots32::sign_to_raw_witness(&secrets[NUM_PUBS + GUEST_PUBIN_COMMITMENT_INDEX], &groth16_pubin) } + { Wots32::sign_to_raw_witness(&secrets[NUM_PUBS + GUEST_PUBIN_COMMITMENT_INDEX], &reverse_each_byte(groth16_pubin)) } { Wots32::sign_to_raw_witness(&secrets[0], &blockhash) } { Wots32::sign_to_raw_witness(&secrets[1], &mismatched_constant) } { push_preimage_to_stack(&input_preimages) } @@ -733,7 +730,7 @@ fn test_verify_guest_pubin() { let mut mismatched_groth16_pubin = groth16_pubin.clone(); mismatched_groth16_pubin[1] ^= 0xFF; let full_scr = script! { - { Wots32::sign_to_raw_witness(&secrets[NUM_PUBS + GUEST_PUBIN_COMMITMENT_INDEX], &mismatched_groth16_pubin) } + { Wots32::sign_to_raw_witness(&secrets[NUM_PUBS + GUEST_PUBIN_COMMITMENT_INDEX], &reverse_each_byte(mismatched_groth16_pubin)) } { Wots32::sign_to_raw_witness(&secrets[0], &blockhash) } { Wots32::sign_to_raw_witness(&secrets[1], &constant) } { push_preimage_to_stack(&input_preimages) } @@ -747,9 +744,10 @@ fn test_verify_guest_pubin() { { // TEST FAILURE case: everything correct - let input_preimages = vec![vec![]; hashes_len]; + let mut input_preimages = vec![vec![]; hashes_len]; + input_preimages[0] = preimages[0].clone(); let full_scr = script! { - { Wots32::sign_to_raw_witness(&secrets[NUM_PUBS + GUEST_PUBIN_COMMITMENT_INDEX], &groth16_pubin) } + { Wots32::sign_to_raw_witness(&secrets[NUM_PUBS + GUEST_PUBIN_COMMITMENT_INDEX], &reverse_each_byte(groth16_pubin)) } { Wots32::sign_to_raw_witness(&secrets[0], &blockhash) } { Wots32::sign_to_raw_witness(&secrets[1], &constant) } { push_preimage_to_stack(&input_preimages) } @@ -761,3 +759,45 @@ fn test_verify_guest_pubin() { assert_eq!(result.final_stack.len(), 1); } } + +#[test] +fn test_generate_guest_pubin_commitment_ziren() { + fn u4_bytes_to_nibbles(bytes: &[u8]) -> Script { + let mut rev_bytes = bytes.to_vec(); + rev_bytes.reverse(); + // u4_hex_to_nibbles takes little-endian hex strings + script! { + { u4_hex_to_nibbles(&hex::encode(rev_bytes)) } + } + } + let pubins: Vec<[u8; 32]> = vec![ + hex::decode("5a690bba0ba076d621f77665398f4b1ddbfc2349bbb3e8880307625ac5cfa900") + .unwrap() + .try_into() + .unwrap(), + hex::decode("2df7bde0605973f5809a1338094cb47a309bc38363dd35ce178c088cd3cda79f") + .unwrap() + .try_into() + .unwrap(), + hex::decode("0100000000000000000000000000000000000000000000000000000000000000") + .unwrap() + .try_into() + .unwrap(), + ]; + // expected_commit_value with length prefix: 001df6fc84f5d020f1453744b865e29750c935c11c89e3c1605e27db449d8821 + let expected_commit_value: [u8; 32] = + hex::decode("1a5605834864faf9cb10055606d9ae06425ea5cf8cf757f996182cd1da196158") + .unwrap() + .try_into() + .unwrap(); + + let s = script! { + { u4_bytes_to_nibbles(&pubins[2]) } + { u4_bytes_to_nibbles(&pubins[1]) } + { u4_bytes_to_nibbles(&pubins[0]) } + { generate_guest_pubin_commitment(3) } + }; + let result = execute_script(s); + let commit_values = parse_u4_stack(0, 32, &result.final_stack); + assert_eq!(expected_commit_value.to_vec(), commit_values); +} diff --git a/goat/src/scripts.rs b/goat/src/scripts.rs index ccd1811c..de7dc9d7 100644 --- a/goat/src/scripts.rs +++ b/goat/src/scripts.rs @@ -243,6 +243,9 @@ pub fn extract_data_from_commitment_outputs(txouts: &[TxOut]) -> Vec { if let bitcoin::blockdata::script::Instruction::PushBytes(bytes) = &instructions[1] { data.extend_from_slice(bytes.as_bytes()); } + if script.is_op_return() { + break; + } } data } diff --git a/goat/ziren/proof.bin b/goat/ziren/proof.bin new file mode 100644 index 00000000..2716c496 --- /dev/null +++ b/goat/ziren/proof.bin @@ -0,0 +1 @@ +^Rju_mD"H oSR^ 9󧠡U6 cY̠R@3jfF{iLe/qʪhI?E.xju$Q \ No newline at end of file diff --git a/goat/ziren/public_inputs.bin b/goat/ziren/public_inputs.bin new file mode 100644 index 00000000..a496a6f7 Binary files /dev/null and b/goat/ziren/public_inputs.bin differ diff --git a/goat/ziren/vk.bin b/goat/ziren/vk.bin new file mode 100644 index 00000000..858944da Binary files /dev/null and b/goat/ziren/vk.bin differ