use super::stack::{PushValue, StackOp};
const FIELD_P_MINUS_2_LOW32: u32 = 0xFFFF_FC2D;
const THREE_CURVE_N_SCRIPT_NUM: [u8; 33] = [
0xc3, 0xc3, 0xa2, 0x70, 0xa6, 0x1b, 0x77, 0x3f, 0xb3, 0xe0, 0xd9, 0x0d,
0xb4, 0x96, 0x0c, 0x30, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02,
];
const GEN_X_BYTES: [u8; 32] = [
0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95,
0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
];
const GEN_Y_BYTES: [u8; 32] = [
0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc,
0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19,
0x9c, 0x47, 0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8,
];
fn collect_ops(f: impl FnOnce(&mut dyn FnMut(StackOp))) -> Vec<StackOp> {
let mut ops = Vec::new();
f(&mut |op| ops.push(op));
ops
}
struct ECTracker<'a> {
nm: Vec<String>,
e: &'a mut dyn FnMut(StackOp),
}
#[allow(dead_code)]
impl<'a> ECTracker<'a> {
fn new(init: &[&str], emit: &'a mut dyn FnMut(StackOp)) -> Self {
ECTracker {
nm: init.iter().map(|s| s.to_string()).collect(),
e: emit,
}
}
fn depth(&self) -> usize {
self.nm.len()
}
fn find_depth(&self, name: &str) -> usize {
for i in (0..self.nm.len()).rev() {
if self.nm[i] == name {
return self.nm.len() - 1 - i;
}
}
panic!("ECTracker: '{}' not on stack {:?}", name, self.nm);
}
fn push_bytes(&mut self, n: &str, v: Vec<u8>) {
(self.e)(StackOp::Push(PushValue::Bytes(v)));
self.nm.push(n.to_string());
}
fn push_int(&mut self, n: &str, v: i128) {
(self.e)(StackOp::Push(PushValue::Int(v)));
self.nm.push(n.to_string());
}
fn dup(&mut self, n: &str) {
(self.e)(StackOp::Dup);
self.nm.push(n.to_string());
}
fn drop(&mut self) {
(self.e)(StackOp::Drop);
if !self.nm.is_empty() {
self.nm.pop();
}
}
fn nip(&mut self) {
(self.e)(StackOp::Nip);
let len = self.nm.len();
if len >= 2 {
self.nm.remove(len - 2);
}
}
fn over(&mut self, n: &str) {
(self.e)(StackOp::Over);
self.nm.push(n.to_string());
}
fn swap(&mut self) {
(self.e)(StackOp::Swap);
let len = self.nm.len();
if len >= 2 {
self.nm.swap(len - 1, len - 2);
}
}
fn rot(&mut self) {
(self.e)(StackOp::Rot);
let len = self.nm.len();
if len >= 3 {
let r = self.nm.remove(len - 3);
self.nm.push(r);
}
}
fn op(&mut self, code: &str) {
(self.e)(StackOp::Opcode(code.into()));
}
fn roll(&mut self, d: usize) {
if d == 0 {
return;
}
if d == 1 {
self.swap();
return;
}
if d == 2 {
self.rot();
return;
}
(self.e)(StackOp::Push(PushValue::Int(d as i128)));
self.nm.push(String::new());
(self.e)(StackOp::Opcode("OP_ROLL".into()));
self.nm.pop(); let idx = self.nm.len() - 1 - d;
let r = self.nm.remove(idx);
self.nm.push(r);
}
fn pick(&mut self, d: usize, n: &str) {
if d == 0 {
self.dup(n);
return;
}
if d == 1 {
self.over(n);
return;
}
(self.e)(StackOp::Push(PushValue::Int(d as i128)));
self.nm.push(String::new());
(self.e)(StackOp::Opcode("OP_PICK".into()));
self.nm.pop(); self.nm.push(n.to_string());
}
fn to_top(&mut self, name: &str) {
let d = self.find_depth(name);
self.roll(d);
}
fn copy_to_top(&mut self, name: &str, n: &str) {
let d = self.find_depth(name);
self.pick(d, n);
}
fn to_alt(&mut self) {
self.op("OP_TOALTSTACK");
if !self.nm.is_empty() {
self.nm.pop();
}
}
fn from_alt(&mut self, n: &str) {
self.op("OP_FROMALTSTACK");
self.nm.push(n.to_string());
}
fn rename(&mut self, n: &str) {
if let Some(last) = self.nm.last_mut() {
*last = n.to_string();
}
}
fn raw_block(
&mut self,
consume: &[&str],
produce: Option<&str>,
f: impl FnOnce(&mut dyn FnMut(StackOp)),
) {
for _ in consume {
if !self.nm.is_empty() {
self.nm.pop();
}
}
f(self.e);
if let Some(p) = produce {
self.nm.push(p.to_string());
}
}
fn emit_if(
&mut self,
cond_name: &str,
then_fn: impl FnOnce(&mut dyn FnMut(StackOp)),
else_fn: impl FnOnce(&mut dyn FnMut(StackOp)),
result_name: Option<&str>,
) {
self.to_top(cond_name);
self.nm.pop(); let then_ops = collect_ops(then_fn);
let else_ops = collect_ops(else_fn);
(self.e)(StackOp::If {
then_ops,
else_ops,
});
if let Some(rn) = result_name {
self.nm.push(rn.to_string());
}
}
}
const FIELD_P_SCRIPT_NUM: [u8; 33] = [
0x2f, 0xfc, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
];
fn push_field_p(t: &mut ECTracker, name: &str) {
t.push_bytes(name, FIELD_P_SCRIPT_NUM.to_vec());
}
fn field_mod(t: &mut ECTracker, a_name: &str, result_name: &str) {
t.to_top(a_name);
push_field_p(t, "_fmod_p");
t.raw_block(&[a_name, "_fmod_p"], Some(result_name), |e| {
e(StackOp::Opcode("OP_2DUP".into())); e(StackOp::Opcode("OP_MOD".into())); e(StackOp::Rot); e(StackOp::Drop); e(StackOp::Over); e(StackOp::Opcode("OP_ADD".into())); e(StackOp::Swap); e(StackOp::Opcode("OP_MOD".into())); });
}
fn field_add(t: &mut ECTracker, a_name: &str, b_name: &str, result_name: &str) {
t.to_top(a_name);
t.to_top(b_name);
t.raw_block(&[a_name, b_name], Some("_fadd_sum"), |e| {
e(StackOp::Opcode("OP_ADD".into()));
});
field_mod(t, "_fadd_sum", result_name);
}
fn field_sub(t: &mut ECTracker, a_name: &str, b_name: &str, result_name: &str) {
t.to_top(a_name);
t.to_top(b_name);
t.raw_block(&[a_name, b_name], Some("_fsub_diff"), |e| {
e(StackOp::Opcode("OP_SUB".into()));
});
field_mod(t, "_fsub_diff", result_name);
}
fn field_mul(t: &mut ECTracker, a_name: &str, b_name: &str, result_name: &str) {
t.to_top(a_name);
t.to_top(b_name);
t.raw_block(&[a_name, b_name], Some("_fmul_prod"), |e| {
e(StackOp::Opcode("OP_MUL".into()));
});
field_mod(t, "_fmul_prod", result_name);
}
fn field_sqr(t: &mut ECTracker, a_name: &str, result_name: &str) {
t.copy_to_top(a_name, "_fsqr_copy");
field_mul(t, a_name, "_fsqr_copy", result_name);
}
fn field_inv(t: &mut ECTracker, a_name: &str, result_name: &str) {
t.copy_to_top(a_name, "_inv_r");
for _i in 0..222 {
field_sqr(t, "_inv_r", "_inv_r2");
t.rename("_inv_r");
t.copy_to_top(a_name, "_inv_a");
field_mul(t, "_inv_r", "_inv_a", "_inv_m");
t.rename("_inv_r");
}
field_sqr(t, "_inv_r", "_inv_r2");
t.rename("_inv_r");
let low_bits = FIELD_P_MINUS_2_LOW32;
for i in (0..=31).rev() {
field_sqr(t, "_inv_r", "_inv_r2");
t.rename("_inv_r");
if (low_bits >> i) & 1 != 0 {
t.copy_to_top(a_name, "_inv_a");
field_mul(t, "_inv_r", "_inv_a", "_inv_m");
t.rename("_inv_r");
}
}
t.to_top(a_name);
t.drop();
t.to_top("_inv_r");
t.rename(result_name);
}
fn decompose_point(t: &mut ECTracker, point_name: &str, x_name: &str, y_name: &str) {
t.to_top(point_name);
t.raw_block(&[point_name], None, |e| {
e(StackOp::Push(PushValue::Int(32)));
e(StackOp::Opcode("OP_SPLIT".into()));
});
t.nm.push("_dp_xb".to_string());
t.nm.push("_dp_yb".to_string());
t.raw_block(&["_dp_yb"], Some(y_name), |e| {
emit_reverse_32(e);
e(StackOp::Push(PushValue::Bytes(vec![0x00])));
e(StackOp::Opcode("OP_CAT".into()));
e(StackOp::Opcode("OP_BIN2NUM".into()));
});
t.to_top("_dp_xb");
t.raw_block(&["_dp_xb"], Some(x_name), |e| {
emit_reverse_32(e);
e(StackOp::Push(PushValue::Bytes(vec![0x00])));
e(StackOp::Opcode("OP_CAT".into()));
e(StackOp::Opcode("OP_BIN2NUM".into()));
});
t.swap();
}
fn compose_point(t: &mut ECTracker, x_name: &str, y_name: &str, result_name: &str) {
t.to_top(x_name);
t.raw_block(&[x_name], Some("_cp_xb"), |e| {
e(StackOp::Push(PushValue::Int(33)));
e(StackOp::Opcode("OP_NUM2BIN".into()));
e(StackOp::Push(PushValue::Int(32)));
e(StackOp::Opcode("OP_SPLIT".into()));
e(StackOp::Drop);
emit_reverse_32(e);
});
t.to_top(y_name);
t.raw_block(&[y_name], Some("_cp_yb"), |e| {
e(StackOp::Push(PushValue::Int(33)));
e(StackOp::Opcode("OP_NUM2BIN".into()));
e(StackOp::Push(PushValue::Int(32)));
e(StackOp::Opcode("OP_SPLIT".into()));
e(StackOp::Drop);
emit_reverse_32(e);
});
t.to_top("_cp_xb");
t.to_top("_cp_yb");
t.raw_block(&["_cp_xb", "_cp_yb"], Some(result_name), |e| {
e(StackOp::Opcode("OP_CAT".into()));
});
}
fn emit_reverse_32(e: &mut dyn FnMut(StackOp)) {
e(StackOp::Opcode("OP_0".into()));
e(StackOp::Swap);
for _i in 0..32 {
e(StackOp::Push(PushValue::Int(1)));
e(StackOp::Opcode("OP_SPLIT".into()));
e(StackOp::Rot);
e(StackOp::Rot);
e(StackOp::Swap);
e(StackOp::Opcode("OP_CAT".into()));
e(StackOp::Swap);
}
e(StackOp::Drop);
}
fn affine_add(t: &mut ECTracker) {
t.copy_to_top("qy", "_qy1");
t.copy_to_top("py", "_py1");
field_sub(t, "_qy1", "_py1", "_s_num");
t.copy_to_top("qx", "_qx1");
t.copy_to_top("px", "_px1");
field_sub(t, "_qx1", "_px1", "_s_den");
field_inv(t, "_s_den", "_s_den_inv");
field_mul(t, "_s_num", "_s_den_inv", "_s");
t.copy_to_top("_s", "_s_keep");
field_sqr(t, "_s", "_s2");
t.copy_to_top("px", "_px2");
field_sub(t, "_s2", "_px2", "_rx1");
t.copy_to_top("qx", "_qx2");
field_sub(t, "_rx1", "_qx2", "rx");
t.copy_to_top("px", "_px3");
t.copy_to_top("rx", "_rx2");
field_sub(t, "_px3", "_rx2", "_px_rx");
field_mul(t, "_s_keep", "_px_rx", "_s_px_rx");
t.copy_to_top("py", "_py2");
field_sub(t, "_s_px_rx", "_py2", "ry");
t.to_top("px"); t.drop();
t.to_top("py"); t.drop();
t.to_top("qx"); t.drop();
t.to_top("qy"); t.drop();
}
fn jacobian_double(t: &mut ECTracker) {
t.copy_to_top("jy", "_jy_save");
t.copy_to_top("jx", "_jx_save");
t.copy_to_top("jz", "_jz_save");
field_sqr(t, "jy", "_A");
t.copy_to_top("_A", "_A_save");
field_mul(t, "jx", "_A", "_xA");
t.push_int("_four", 4);
field_mul(t, "_xA", "_four", "_B");
field_sqr(t, "_A_save", "_A2");
t.push_int("_eight", 8);
field_mul(t, "_A2", "_eight", "_C");
field_sqr(t, "_jx_save", "_x2");
t.push_int("_three", 3);
field_mul(t, "_x2", "_three", "_D");
t.copy_to_top("_D", "_D_save");
t.copy_to_top("_B", "_B_save");
field_sqr(t, "_D", "_D2");
t.copy_to_top("_B", "_B1");
t.push_int("_two1", 2);
field_mul(t, "_B1", "_two1", "_2B");
field_sub(t, "_D2", "_2B", "_nx");
t.copy_to_top("_nx", "_nx_copy");
field_sub(t, "_B_save", "_nx_copy", "_B_nx");
field_mul(t, "_D_save", "_B_nx", "_D_B_nx");
field_sub(t, "_D_B_nx", "_C", "_ny");
field_mul(t, "_jy_save", "_jz_save", "_yz");
t.push_int("_two2", 2);
field_mul(t, "_yz", "_two2", "_nz");
t.to_top("_B"); t.drop();
t.to_top("jz"); t.drop();
t.to_top("_nx"); t.rename("jx");
t.to_top("_ny"); t.rename("jy");
t.to_top("_nz"); t.rename("jz");
}
fn jacobian_to_affine(t: &mut ECTracker, rx_name: &str, ry_name: &str) {
field_inv(t, "jz", "_zinv");
t.copy_to_top("_zinv", "_zinv_keep");
field_sqr(t, "_zinv", "_zinv2");
t.copy_to_top("_zinv2", "_zinv2_keep");
field_mul(t, "_zinv_keep", "_zinv2", "_zinv3");
field_mul(t, "jx", "_zinv2_keep", rx_name);
field_mul(t, "jy", "_zinv3", ry_name);
}
fn build_jacobian_add_affine_inline(e: &mut dyn FnMut(StackOp), t: &ECTracker) {
let cloned_nm: Vec<String> = t.nm.clone();
let init_strs: Vec<&str> = cloned_nm.iter().map(|s| s.as_str()).collect();
let mut it = ECTracker::new(&init_strs, e);
it.copy_to_top("jz", "_jz_for_z1cu"); it.copy_to_top("jz", "_jz_for_z3"); it.copy_to_top("jy", "_jy_for_y3"); it.copy_to_top("jx", "_jx_for_u1h2");
field_sqr(&mut it, "jz", "_Z1sq");
it.copy_to_top("_Z1sq", "_Z1sq_for_u2");
field_mul(&mut it, "_jz_for_z1cu", "_Z1sq", "_Z1cu");
it.copy_to_top("ax", "_ax_c");
field_mul(&mut it, "_ax_c", "_Z1sq_for_u2", "_U2");
it.copy_to_top("ay", "_ay_c");
field_mul(&mut it, "_ay_c", "_Z1cu", "_S2");
field_sub(&mut it, "_U2", "jx", "_H");
field_sub(&mut it, "_S2", "jy", "_R");
it.copy_to_top("_H", "_H_for_h3");
it.copy_to_top("_H", "_H_for_z3");
field_sqr(&mut it, "_H", "_H2");
it.copy_to_top("_H2", "_H2_for_u1h2");
field_mul(&mut it, "_H_for_h3", "_H2", "_H3");
field_mul(&mut it, "_jx_for_u1h2", "_H2_for_u1h2", "_U1H2");
it.copy_to_top("_R", "_R_for_y3");
it.copy_to_top("_U1H2", "_U1H2_for_y3");
it.copy_to_top("_H3", "_H3_for_y3");
field_sqr(&mut it, "_R", "_R2");
field_sub(&mut it, "_R2", "_H3", "_x3_tmp");
it.push_int("_two", 2);
field_mul(&mut it, "_U1H2", "_two", "_2U1H2");
field_sub(&mut it, "_x3_tmp", "_2U1H2", "_X3");
it.copy_to_top("_X3", "_X3_c");
field_sub(&mut it, "_U1H2_for_y3", "_X3_c", "_u_minus_x");
field_mul(&mut it, "_R_for_y3", "_u_minus_x", "_r_tmp");
field_mul(&mut it, "_jy_for_y3", "_H3_for_y3", "_jy_h3");
field_sub(&mut it, "_r_tmp", "_jy_h3", "_Y3");
field_mul(&mut it, "_jz_for_z3", "_H_for_z3", "_Z3");
it.to_top("_X3"); it.rename("jx");
it.to_top("_Y3"); it.rename("jy");
it.to_top("_Z3"); it.rename("jz");
}
pub fn emit_ec_add(emit: &mut dyn FnMut(StackOp)) {
let mut t = ECTracker::new(&["_pa", "_pb"], emit);
decompose_point(&mut t, "_pa", "px", "py");
decompose_point(&mut t, "_pb", "qx", "qy");
affine_add(&mut t);
compose_point(&mut t, "rx", "ry", "_result");
}
pub fn emit_ec_mul(emit: &mut dyn FnMut(StackOp)) {
let mut t = ECTracker::new(&["_pt", "_k"], emit);
decompose_point(&mut t, "_pt", "ax", "ay");
t.to_top("_k");
t.push_bytes("_3n", THREE_CURVE_N_SCRIPT_NUM.to_vec());
t.raw_block(&["_k", "_3n"], Some("_k3n"), |e| {
e(StackOp::Opcode("OP_ADD".into()));
});
t.rename("_k");
t.copy_to_top("ax", "jx");
t.copy_to_top("ay", "jy");
t.push_int("jz", 1);
for bit in (0..=256).rev() {
jacobian_double(&mut t);
t.copy_to_top("_k", "_k_copy");
if bit > 0 {
if bit <= 126 {
t.push_int("_div", 1i128 << bit);
} else {
let divisor_bytes = script_number_pow2(bit);
t.push_bytes("_div", divisor_bytes);
}
t.raw_block(&["_k_copy", "_div"], Some("_shifted"), |e| {
e(StackOp::Opcode("OP_DIV".into()));
});
} else {
t.rename("_shifted");
}
t.push_int("_two", 2);
t.raw_block(&["_shifted", "_two"], Some("_bit"), |e| {
e(StackOp::Opcode("OP_MOD".into()));
});
t.to_top("_bit");
t.nm.pop(); let add_ops = collect_ops(|add_emit| {
build_jacobian_add_affine_inline(add_emit, &t);
});
(t.e)(StackOp::If {
then_ops: add_ops,
else_ops: vec![],
});
}
jacobian_to_affine(&mut t, "_rx", "_ry");
t.to_top("ax"); t.drop();
t.to_top("ay"); t.drop();
t.to_top("_k"); t.drop();
compose_point(&mut t, "_rx", "_ry", "_result");
}
pub fn emit_ec_mul_gen(emit: &mut dyn FnMut(StackOp)) {
let mut g_point = Vec::with_capacity(64);
g_point.extend_from_slice(&GEN_X_BYTES);
g_point.extend_from_slice(&GEN_Y_BYTES);
emit(StackOp::Push(PushValue::Bytes(g_point)));
emit(StackOp::Swap); emit_ec_mul(emit);
}
pub fn emit_ec_negate(emit: &mut dyn FnMut(StackOp)) {
let mut t = ECTracker::new(&["_pt"], emit);
decompose_point(&mut t, "_pt", "_nx", "_ny");
push_field_p(&mut t, "_fp");
field_sub(&mut t, "_fp", "_ny", "_neg_y");
compose_point(&mut t, "_nx", "_neg_y", "_result");
}
pub fn emit_ec_on_curve(emit: &mut dyn FnMut(StackOp)) {
let mut t = ECTracker::new(&["_pt"], emit);
decompose_point(&mut t, "_pt", "_x", "_y");
field_sqr(&mut t, "_y", "_y2");
t.copy_to_top("_x", "_x_copy");
field_sqr(&mut t, "_x", "_x2");
field_mul(&mut t, "_x2", "_x_copy", "_x3");
t.push_int("_seven", 7);
field_add(&mut t, "_x3", "_seven", "_rhs");
t.to_top("_y2");
t.to_top("_rhs");
t.raw_block(&["_y2", "_rhs"], Some("_result"), |e| {
e(StackOp::Opcode("OP_EQUAL".into()));
});
}
pub fn emit_ec_mod_reduce(emit: &mut dyn FnMut(StackOp)) {
emit(StackOp::Opcode("OP_2DUP".into()));
emit(StackOp::Opcode("OP_MOD".into()));
emit(StackOp::Rot);
emit(StackOp::Drop);
emit(StackOp::Over);
emit(StackOp::Opcode("OP_ADD".into()));
emit(StackOp::Swap);
emit(StackOp::Opcode("OP_MOD".into()));
}
pub fn emit_ec_encode_compressed(emit: &mut dyn FnMut(StackOp)) {
emit(StackOp::Push(PushValue::Int(32)));
emit(StackOp::Opcode("OP_SPLIT".into()));
emit(StackOp::Opcode("OP_SIZE".into()));
emit(StackOp::Push(PushValue::Int(1)));
emit(StackOp::Opcode("OP_SUB".into()));
emit(StackOp::Opcode("OP_SPLIT".into()));
emit(StackOp::Opcode("OP_BIN2NUM".into()));
emit(StackOp::Push(PushValue::Int(2)));
emit(StackOp::Opcode("OP_MOD".into()));
emit(StackOp::Swap);
emit(StackOp::Drop); emit(StackOp::If {
then_ops: vec![StackOp::Push(PushValue::Bytes(vec![0x03]))],
else_ops: vec![StackOp::Push(PushValue::Bytes(vec![0x02]))],
});
emit(StackOp::Swap);
emit(StackOp::Opcode("OP_CAT".into()));
}
pub fn emit_ec_make_point(emit: &mut dyn FnMut(StackOp)) {
emit(StackOp::Push(PushValue::Int(33)));
emit(StackOp::Opcode("OP_NUM2BIN".into()));
emit(StackOp::Push(PushValue::Int(32)));
emit(StackOp::Opcode("OP_SPLIT".into()));
emit(StackOp::Drop);
emit_reverse_32(emit);
emit(StackOp::Swap);
emit(StackOp::Push(PushValue::Int(33)));
emit(StackOp::Opcode("OP_NUM2BIN".into()));
emit(StackOp::Push(PushValue::Int(32)));
emit(StackOp::Opcode("OP_SPLIT".into()));
emit(StackOp::Drop);
emit_reverse_32(emit);
emit(StackOp::Swap);
emit(StackOp::Opcode("OP_CAT".into()));
}
pub fn emit_ec_point_x(emit: &mut dyn FnMut(StackOp)) {
emit(StackOp::Push(PushValue::Int(32)));
emit(StackOp::Opcode("OP_SPLIT".into()));
emit(StackOp::Drop);
emit_reverse_32(emit);
emit(StackOp::Push(PushValue::Bytes(vec![0x00])));
emit(StackOp::Opcode("OP_CAT".into()));
emit(StackOp::Opcode("OP_BIN2NUM".into()));
}
pub fn emit_ec_point_y(emit: &mut dyn FnMut(StackOp)) {
emit(StackOp::Push(PushValue::Int(32)));
emit(StackOp::Opcode("OP_SPLIT".into()));
emit(StackOp::Swap);
emit(StackOp::Drop);
emit_reverse_32(emit);
emit(StackOp::Push(PushValue::Bytes(vec![0x00])));
emit(StackOp::Opcode("OP_CAT".into()));
emit(StackOp::Opcode("OP_BIN2NUM".into()));
}
fn script_number_pow2(n: usize) -> Vec<u8> {
let byte_idx = n / 8;
let bit_pos = n % 8;
let min_len = byte_idx + 1;
let needs_sign_byte = bit_pos == 7;
let total_len = if needs_sign_byte { min_len + 1 } else { min_len };
let mut bytes = vec![0u8; total_len];
bytes[byte_idx] = 1 << bit_pos;
bytes
}