Skip to content

Commit

Permalink
opchecksigadd struct
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeanmichel7 committed Oct 18, 2024
1 parent 4fbe693 commit a5c1ef4
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 18 deletions.
87 changes: 83 additions & 4 deletions packages/engine/src/opcodes/crypto.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::engine::{Engine, EngineExtrasTrait};
use crate::transaction::{
EngineTransactionTrait, EngineTransactionInputTrait, EngineTransactionOutputTrait,
Transaction
EngineTransactionTrait, EngineTransactionInputTrait, EngineTransactionOutputTrait, Transaction
};
use crate::stack::ScriptStackTrait;
use crate::scriptflags::ScriptFlags;
Expand All @@ -10,6 +9,7 @@ use crate::signature::sighash;
use crate::signature::signature::{BaseSigVerifierTrait, TaprootSigVerifierTrait};
use starknet::secp256_trait::{is_valid_signature};
use core::sha256::compute_sha256_byte_array;
use core::num::traits::OverflowingAdd;
use crate::opcodes::utils;
use crate::scriptnum::ScriptNum;
use crate::errors::Error;
Expand Down Expand Up @@ -117,7 +117,9 @@ pub fn opcode_checksig<
return Result::Err(Error::TAPROOT_EMPTY_PUBKEY);
}

let mut verifier = TaprootSigVerifierTrait::<Transaction>::new_base(@full_sig_bytes, @pk_bytes)?;
let mut verifier = TaprootSigVerifierTrait::<
Transaction
>::new_base(@full_sig_bytes, @pk_bytes)?;
is_valid = TaprootSigVerifierTrait::<Transaction>::verify(ref verifier);
}

Expand Down Expand Up @@ -278,7 +280,8 @@ pub fn opcode_codeseparator<T, +Drop<T>>(ref engine: Engine<T>) -> Result<(), fe
if !engine.use_taproot {
// TODO: Check if this is correct
engine.taproot_context.code_sep = engine.opcode_idx;
} else if engine.witness_program.len() == 0 && engine.has_flag(ScriptFlags::ScriptVerifyConstScriptCode) {
} else if engine.witness_program.len() == 0
&& engine.has_flag(ScriptFlags::ScriptVerifyConstScriptCode) {
return Result::Err(Error::CODESEPARATOR_NON_SEGWIT);
}

Expand Down Expand Up @@ -331,3 +334,79 @@ pub fn opcode_sha1<T, +Drop<T>>(ref engine: Engine<T>) -> Result<(), felt252> {
engine.dstack.push_byte_array(h);
return Result::Ok(());
}

// https://github.com/btcsuite/btcd/blob/67b8efd3ba53b60ff0eba5d79babe2c3d82f6c54/txscript/opcode.go#L2126
// opcodeCheckSigAdd implements the OP_CHECKSIGADD operation defined in BIP
// 342. This is a replacement for OP_CHECKMULTISIGVERIFY and OP_CHECKMULTISIG
// that lends better to batch sig validation, as well as a possible future of
// signature aggregation across inputs.
//
// The op code takes a public key, an integer (N) and a signature, and returns
// N if the signature was the empty vector, and n+1 otherwise.
//
// Stack transformation: [... pubkey n signature] -> [... n | n+1 ] -> [...]
pub fn opcode_checksigadd<
T,
+Drop<T>,
I,
+Drop<I>,
impl IEngineTransactionInputTrait: EngineTransactionInputTrait<I>,
O,
+Drop<O>,
impl IEngineTransactionOutputTrait: EngineTransactionOutputTrait<O>,
impl IEngineTransactionTrait: EngineTransactionTrait<
T, I, O, IEngineTransactionInputTrait, IEngineTransactionOutputTrait
>
>(
ref engine: Engine<T>
) -> Result<(), felt252> {
// This op code can only be used if tapscript execution is active.
// Before the soft fork, this opcode was marked as an invalid reserved
// op code.
if !engine.use_taproot {
return Result::Err(Error::OPCODE_RESERVED);
}

let pk_bytes: ByteArray = engine.dstack.pop_byte_array()?;
let n: i64 = engine.dstack.pop_int()?;
let sig_bytes: ByteArray = engine.dstack.pop_byte_array()?;

// Only non-empty signatures count towards the total tapscript sig op
// limit.
if sig_bytes.len() != 0 {
// Account for changes in the sig ops budget after this execution.
engine.taproot_context.use_ops_budget()?;
}

// Empty public keys immediately cause execution to fail.
if pk_bytes.len() == 0 {
return Result::Err(Error::TAPROOT_EMPTY_PUBKEY);
}

// If the signature is empty, then we'll just push the value N back
// onto the stack and continue from here.
if sig_bytes.len() == 0 {
engine.dstack.push_int(n);
return Result::Ok(());
}

// Otherwise, we'll attempt to validate the signature as normal.
//
// If the constructor fails immediately, then it's because the public
// key size is zero, so we'll fail all script execution.
let mut verifier = TaprootSigVerifierTrait::<
Transaction
>::new(@sig_bytes, @pk_bytes, engine.taproot_context.annex)?;
if !(TaprootSigVerifierTrait::<Transaction>::verify(ref verifier)) {
return Result::Err(Error::TAPROOT_INVALID_SIG);
}

// Otherwise, we increment the accumulatorInt by one, and push that
// back onto the stack.
let (n_add_1, overflow) = n.overflowing_add(1);
if overflow {
return Result::Err(Error::STACK_OVERFLOW);
}
engine.dstack.push_int(n_add_1);
Result::Ok(())
}
4 changes: 1 addition & 3 deletions packages/engine/src/signature/signature.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,8 @@ pub struct TaprootSigVerifier {
pub trait TaprootSigVerifierTrait<T> {
fn new(
sig_bytes: @ByteArray, pk_bytes: @ByteArray, annex: @ByteArray
) -> Result<TaprootSigVerifier, felt252>;
fn new_base(
sig_bytes: @ByteArray, pk_bytes: @ByteArray
) -> Result<TaprootSigVerifier, felt252>;
fn new_base(sig_bytes: @ByteArray, pk_bytes: @ByteArray) -> Result<TaprootSigVerifier, felt252>;
fn verify(ref self: TaprootSigVerifier) -> bool;
fn verify_base(ref self: TaprootSigVerifier) -> bool;
}
Expand Down
36 changes: 25 additions & 11 deletions packages/engine/src/taproot.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ pub fn serialized_compressed(pub_key: Secp256k1Point) -> ByteArray {
#[generate_trait()]
pub impl ControlBlockImpl of ControlBlockTrait {
// TODO: From parse
fn new(internal_pubkey: Secp256k1Point, output_key_y_is_odd: bool, leaf_version: u8, control_block: @ByteArray) -> ControlBlock {
fn new(
internal_pubkey: Secp256k1Point,
output_key_y_is_odd: bool,
leaf_version: u8,
control_block: @ByteArray
) -> ControlBlock {
ControlBlock {
internal_pubkey: internal_pubkey,
output_key_y_is_odd: output_key_y_is_odd,
Expand All @@ -79,7 +84,9 @@ pub impl ControlBlockImpl of ControlBlockTrait {
return "";
}

fn verify_taproot_leaf(self: @ControlBlock, witness_program: @ByteArray, script: @ByteArray) -> Result<(), felt252> {
fn verify_taproot_leaf(
self: @ControlBlock, witness_program: @ByteArray, script: @ByteArray
) -> Result<(), felt252> {
let root_hash = self.root_hash(script);
let taproot_key = compute_taproot_output_key(self.internal_pubkey, @root_hash);
let expected_witness_program = serialize_pub_key(taproot_key);
Expand All @@ -99,7 +106,8 @@ pub impl ControlBlockImpl of ControlBlockTrait {
const CONTROL_BLOCK_BASE_SIZE: u32 = 33;
const CONTROL_BLOCK_NODE_SIZE: u32 = 32;
const CONTROL_BLOCK_MAX_NODE_COUNT: u32 = 128;
const CONTROL_BLOCK_MAX_SIZE: u32 = CONTROL_BLOCK_BASE_SIZE + (CONTROL_BLOCK_MAX_NODE_COUNT * CONTROL_BLOCK_NODE_SIZE);
const CONTROL_BLOCK_MAX_SIZE: u32 = CONTROL_BLOCK_BASE_SIZE
+ (CONTROL_BLOCK_MAX_NODE_COUNT * CONTROL_BLOCK_NODE_SIZE);

const SIG_OPS_DELTA: i32 = 50;
const BASE_CODE_SEP: u32 = 0xFFFFFFFF;
Expand Down Expand Up @@ -129,14 +137,18 @@ pub impl TaprootContextImpl of TaprootContextTrait {
}
}

fn verify_taproot_spend(witness_program: @ByteArray, raw_sig: @ByteArray, tx: @Transaction, tx_idx: u32) -> Result<(), felt252> {
fn verify_taproot_spend(
witness_program: @ByteArray, raw_sig: @ByteArray, tx: @Transaction, tx_idx: u32
) -> Result<(), felt252> {
let witness: Span<ByteArray> = tx.get_transaction_inputs()[tx_idx].get_witness();
let mut annex = @"";
if is_annexed_witness(witness, witness.len()) {
annex = witness[witness.len() - 1];
}

let mut verifier = TaprootSigVerifierImpl::<Transaction>::new(raw_sig, witness_program, annex)?;
let mut verifier = TaprootSigVerifierImpl::<
Transaction
>::new(raw_sig, witness_program, annex)?;
let is_valid = TaprootSigVerifierImpl::<Transaction>::verify(ref verifier);
if !is_valid {
return Result::Err(Error::TAPROOT_INVALID_SIG);
Expand Down Expand Up @@ -174,12 +186,14 @@ pub fn parse_control_block(control_block: @ByteArray) -> Result<ControlBlock, fe
i += 1;
};
let pubkey = parse_schnorr_pub_key(@raw_pubkey);
return Result::Ok(ControlBlock {
internal_pubkey: pubkey,
output_key_y_is_odd: output_key_y_is_odd,
leaf_version: leaf_version,
control_block: control_block
});
return Result::Ok(
ControlBlock {
internal_pubkey: pubkey,
output_key_y_is_odd: output_key_y_is_odd,
leaf_version: leaf_version,
control_block: control_block
}
);
}

pub fn is_annexed_witness(witness: Span<ByteArray>, witness_len: usize) -> bool {
Expand Down

0 comments on commit a5c1ef4

Please sign in to comment.