Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 120 additions & 80 deletions goat/src/disprove_scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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: &[<Wots32 as Wots>::PublicKey; NUM_GUEST],
groth16_pubin_wots_pubkeys: &[<Wots32 as Wots>::PublicKey; NUM_PUBS],
Expand Down Expand Up @@ -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() }

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand All @@ -660,25 +641,41 @@ fn test_verify_guest_pubin() {
.map(|s| Wots32::generate_public_key(s))
.collect::<Vec<_>>();
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::<ark_bn254::Bn254>::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::<ark_bn254::Bn254>::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<u8>> = 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::<Vec<u8>>()));
}

let lock_scr = script! {
Expand All @@ -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) }
Expand All @@ -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) }
Expand All @@ -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) }
Expand All @@ -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) }
Expand All @@ -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);
}
3 changes: 3 additions & 0 deletions goat/src/scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ pub fn extract_data_from_commitment_outputs(txouts: &[TxOut]) -> Vec<u8> {
if let bitcoin::blockdata::script::Instruction::PushBytes(bytes) = &instructions[1] {
data.extend_from_slice(bytes.as_bytes());
}
if script.is_op_return() {
break;
}
}
data
}
Expand Down
1 change: 1 addition & 0 deletions goat/ziren/proof.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
^Rjçuè_–¯m ‘¿œ”Dä’"âÀ ˆéH ãoSž³RÅ^Û 9«Öó§ ¡œáÞâU6 c‡¢Y½Ì RÛ¿£€@ëã3×j¾fF‚ñ{ÚiLeï‹Ñ/qü€þÍʪ®©h×I×?Þ®õŸ‡ÎøÌE. öëxýáj¢u$Qµá
Binary file added goat/ziren/public_inputs.bin
Binary file not shown.
Binary file added goat/ziren/vk.bin
Binary file not shown.
Loading