1use binius_core::Word;
6use binius_frontend::{CircuitBuilder, Wire};
7
8use crate::{
9 bignum::{self, BigUint},
10 bitcoin::double_sha256::double_sha256,
11};
12
13pub fn header_chain(
35 builder: &CircuitBuilder,
36 headers: &[[Wire; 10]],
38) -> [Wire; 4] {
39 let latest_digest = double_sha256(builder, &headers[0]);
40 assert_fulfills_target(builder, latest_digest, headers[0][9]);
41
42 for i in 1..headers.len() {
43 let digest = double_sha256(builder, &headers[i]);
45 builder.assert_eq_v("hash chain", digest, previous_block_hash(builder, &headers[i - 1]));
46
47 assert_fulfills_target(builder, digest, headers[i][9]);
50
51 }
54
55 latest_digest
56}
57
58fn assert_fulfills_target(builder: &CircuitBuilder, digest: [Wire; 4], bits: Wire) {
60 let target = target(builder, bits);
61 let digest_as_uint = BigUint {
62 limbs: digest.to_vec(),
63 };
64 let fulfills_target = bignum::biguint_lt(builder, &digest_as_uint, &target);
65 builder.assert_true("PoW fulfills target", fulfills_target);
66}
67
68fn previous_block_hash(builder: &CircuitBuilder, header: &[Wire; 10]) -> [Wire; 4] {
70 [
71 join(builder, header[1], header[0]),
72 join(builder, header[2], header[1]),
73 join(builder, header[3], header[2]),
74 join(builder, header[4], header[3]),
75 ]
76}
77
78fn join(builder: &CircuitBuilder, b0: Wire, b1: Wire) -> Wire {
79 let c0 = builder.shl(b0, 32);
80 let c1 = builder.shr(b1, 32);
81 builder.bxor(c0, c1)
82}
83
84fn target(builder: &CircuitBuilder, bits: Wire) -> BigUint {
86 let mantissa = builder.band(builder.add_constant_64(0x0000000000ffffff), bits);
87 let exponent = builder.band(builder.add_constant_64(0x00000000000000ff), builder.shr(bits, 24));
88
89 let (shift_val, _) = builder.isub_bin_bout(
92 exponent,
93 builder.add_constant_64(3),
94 builder.add_constant(Word::ZERO),
95 );
96
97 let cond_1 = builder.shl(shift_val, 63);
99 let v0_1 = builder.select(cond_1, builder.shl(mantissa, 8), mantissa);
100
101 let cond_2 = builder.shl(shift_val, 62);
103 let v0_2 = builder.select(cond_2, builder.shl(v0_1, 2 * 8), v0_1);
104
105 let cond_4 = builder.shl(shift_val, 61);
107 let v0_4 = builder.select(cond_4, builder.shl(v0_2, 4 * 8), v0_2);
108 let v1_4 = builder.select(cond_4, builder.shr(v0_2, 4 * 8), builder.add_constant(Word::ZERO));
109
110 let cond_8 = builder.shl(shift_val, 60);
112 let v0_8 = builder.select(cond_8, builder.add_constant(Word::ZERO), v0_4);
113 let v1_8 = builder.select(cond_8, v0_4, v1_4);
114 let v2_8 = builder.select(cond_8, v1_4, builder.add_constant(Word::ZERO));
115
116 let cond_16 = builder.shl(shift_val, 59);
118 let v0_16 = builder.select(cond_16, builder.add_constant(Word::ZERO), v0_8);
119 let v1_16 = builder.select(cond_16, builder.add_constant(Word::ZERO), v1_8);
120 let v2_16 = builder.select(cond_16, v0_8, v2_8);
121 let v3_16 = builder.select(cond_16, v1_8, builder.add_constant(Word::ZERO));
122
123 BigUint {
124 limbs: vec![v0_16, v1_16, v2_16, v3_16],
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use hex_literal::hex;
131
132 use super::*;
133 use crate::bignum;
134
135 fn test_target_fixed_exponent(exponent: usize) {
137 let builder = CircuitBuilder::new();
139 let bits = builder.add_witness();
140 let asserted_target = BigUint::new_witness(&builder, 4);
141 let target = target(&builder, bits);
142 bignum::assert_eq(&builder, "target matches asserted target", &target, &asserted_target);
143 let circuit = builder.build();
144
145 let mut filler = circuit.new_witness_filler();
147 let bits_value: u64 = 0x4444444400aabbcc | ((exponent as u64) << 24);
148 filler[bits] = Word(bits_value);
149 let shift_val = exponent - 3;
150 let asserted_target_value = num_bigint::BigUint::from_bytes_be(&hex!(
151 "0000000000000000000000000000000000000000000000000000000000aabbcc"
152 )) << (shift_val * 8);
153 let mut asserted_target_digits = asserted_target_value.to_u64_digits();
154 asserted_target_digits.truncate(4);
155 while asserted_target_digits.len() < 4 {
156 asserted_target_digits.push(0);
157 }
158 asserted_target.populate_limbs(&mut filler, &asserted_target_digits);
159 circuit.populate_wire_witness(&mut filler).unwrap();
160
161 let constraint_system = circuit.constraint_system();
163 constraint_system.verify(&filler.into_value_vec()).unwrap();
164 }
165
166 #[test]
167 fn test_target() {
168 for exponent in 3..35 {
170 test_target_fixed_exponent(exponent);
171 }
172 }
173
174 #[test]
175 fn test_valid() {
176 let builder = CircuitBuilder::new();
178 let headers: Vec<[Wire; 10]> =
179 std::iter::repeat_with(|| std::array::from_fn(|_| builder.add_witness()))
180 .take(3)
181 .collect();
182 let latest_digest: [Wire; 4] = std::array::from_fn(|_| builder.add_witness());
183 builder.assert_eq_v("latest digest", header_chain(&builder, &headers), latest_digest);
184 let circuit = builder.build();
185
186 let mut filler = circuit.new_witness_filler();
188 let headers_value = vec![
189 hex!(
190 "000000264a14e21adad047d981c06a26446e345eda3d8beb807401000000000000000000fc01df2139954b36cebc3fa6fbf6a7160a67d34b67e5c4aa2a7ce46f5bb42a83642ea468b32c0217d14ba4d1"
191 ),
192 hex!(
193 "00606a3190af271dec0197c6b87f218070c5611cf0c506f6671c0200000000000000000021f21732014796558e6736b15de9b132ca65318b42fe5759b153c63dd4306e38842da468b32c021737421730"
194 ),
195 hex!(
196 "00800020e77cf8eb3114116cc2e6d4aca9b27d35bb5402a5054a010000000000000000005b41d83c40c8226c48807401b0b08aa8e4e2eb053bf91d580f62cd49f5c2a99f802ba468b32c0217cea24c34"
197 ),
198 ];
199 let latest_digest_value =
200 hex!("228561b085b7524957e515605725901238299ff2793300000000000000000000");
201 for (header, header_value) in headers.iter().zip(&headers_value) {
202 filler.pack_bytes_le(header, header_value);
203 }
204 filler.pack_bytes_le(&latest_digest, &latest_digest_value);
205 circuit.populate_wire_witness(&mut filler).unwrap();
206
207 let constraint_system = circuit.constraint_system();
209 constraint_system.verify(&filler.into_value_vec()).unwrap();
210 }
211}