diff --git a/C/Makefile b/C/Makefile index e9038369..59156ec9 100644 --- a/C/Makefile +++ b/C/Makefile @@ -1,4 +1,6 @@ -OBJS := bitstream.o dag.o deserialize.o eval.o frame.o jets.o jets-secp256k1.o rsort.o sha256.o type.o typeInference.o elements/env.o elements/exec.o elements/ops.o elements/elementsJets.o elements/primitive.o elements/cmr.o elements/txEnv.o +CORE_OBJS := bitstream.o dag.o deserialize.o eval.o frame.o jets.o jets-secp256k1.o rsort.o sha256.o type.o typeInference.o +BITCOIN_OBJS := bitcoin/env.o bitcoin/ops.o bitcoin/bitcoinJets.o bitcoin/txEnv.o +ELEMENTS_OBJS := elements/env.o elements/exec.o elements/ops.o elements/elementsJets.o elements/primitive.o elements/cmr.o elements/txEnv.o TEST_OBJS := test.o ctx8Pruned.o ctx8Unpruned.o hashBlock.o regression4.o schnorr0.o schnorr6.o typeSkipTest.o elements/checkSigHashAllTx1.o # From https://fastcompression.blogspot.com/2019/01/compiler-warnings.html @@ -23,13 +25,16 @@ sha256.o: sha256.c %.o: %.c $(CC) -c $(CFLAGS) $(CWARN) $(CPPFLAGS) -o $@ $< -libElementsSimplicity.a: $(OBJS) +libBitcoinSimplicity.a: $(CORE_OBJS) $(BITCOIN_OBJS) + ar rcs $@ $^ + +libElementsSimplicity.a: $(CORE_OBJS) $(ELEMENTS_OBJS) ar rcs $@ $^ test: $(TEST_OBJS) libElementsSimplicity.a $(CC) $^ -o $@ $(LDFLAGS) -install: libElementsSimplicity.a +install: libBitcoinSimplicity.a libElementsSimplicity.a mkdir -p $(out)/lib cp $^ $(out)/lib/ cp -R include $(out)/include diff --git a/C/bitcoin/bitcoinJets.c b/C/bitcoin/bitcoinJets.c new file mode 100644 index 00000000..850b20e1 --- /dev/null +++ b/C/bitcoin/bitcoinJets.c @@ -0,0 +1,592 @@ +#include "bitcoinJets.h" + +#include "ops.h" +#include "txEnv.h" +#include "../taptweak.h" +#include "../simplicity_assert.h" + +/* Read a 256-bit hash value from the 'src' frame, advancing the cursor 256 cells. + * + * Precondition: '*src' is a valid read frame for 256 more cells; + * NULL != h; + */ +static void readHash(sha256_midstate* h, frameItem *src) { + read32s(h->s, 8, src); +} + +/* Write a 256-bit hash value to the 'dst' frame, advancing the cursor 256 cells. + * + * Precondition: '*dst' is a valid write frame for 256 more cells; + * NULL != h; + */ +static void writeHash(frameItem* dst, const sha256_midstate* h) { + write32s(dst, h->s, 8); +} + +/* Write an outpoint value to the 'dst' frame, advancing the cursor 288 cells. + * + * Precondition: '*dst' is a valid write frame for 288 more cells; + * NULL != op; + */ +static void prevOutpoint(frameItem* dst, const outpoint* op) { + writeHash(dst, &op->txid); + simplicity_write32(dst, op->ix); +} + +static uint_fast32_t lockHeight(const bitcoinTransaction* tx) { + return !tx->isFinal && tx->lockTime < 500000000U ? tx->lockTime : 0; +} + +static uint_fast32_t lockTime(const bitcoinTransaction* tx) { + return !tx->isFinal && 500000000U <= tx->lockTime ? tx->lockTime : 0; +} + +static uint_fast16_t lockDistance(const bitcoinTransaction* tx) { + return 2 <= tx->version ? tx->lockDistance : 0; +} + +static uint_fast16_t lockDuration(const bitcoinTransaction* tx) { + return 2 <= tx->version ? tx->lockDuration : 0; +} + +/* version : ONE |- TWO^32 */ +bool simplicity_bitcoin_version(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write32(dst, env->tx->version); + return true; +} + +/* lock_time : ONE |- TWO^32 */ +bool simplicity_bitcoin_lock_time(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write32(dst, env->tx->lockTime); + return true; +} + +/* input_prev_outpoint : TWO^32 |- S (TWO^256 * TWO^32) */ +bool simplicity_bitcoin_input_prev_outpoint(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + prevOutpoint(dst, &env->tx->input[i].prevOutpoint); + } else { + skipBits(dst, 288); + } + return true; +} + +/* input_value : TWO^32 |- S TWO^64 */ +bool simplicity_bitcoin_input_value(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + simplicity_write64(dst, env->tx->input[i].txo.value); + } else { + skipBits(dst, 64); + } + return true; +} + +/* input_script_hash : TWO^32 |- S TWO^256 */ +bool simplicity_bitcoin_input_script_hash(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + writeHash(dst, &env->tx->input[i].txo.scriptPubKey); + } else { + skipBits(dst, 256); + } + return true; +} + +/* input_sequence : TWO^32 |- S TWO^32 */ +bool simplicity_bitcoin_input_sequence(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + simplicity_write32(dst, env->tx->input[i].sequence); + } else { + skipBits(dst, 32); + } + return true; +} + +/* input_annex_hash : TWO^32 |- S (S (TWO^256)) */ +bool simplicity_bitcoin_input_annex_hash(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + if (writeBit(dst, env->tx->input[i].hasAnnex)) { + writeHash(dst, &env->tx->input[i].annexHash); + } else { + skipBits(dst, 256); + } + } else { + skipBits(dst, 257); + } + return true; +} + +/* input_script_sig_hash : TWO^32 |- (S (TWO^256) */ +bool simplicity_bitcoin_input_script_sig_hash(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + writeHash(dst, &env->tx->input[i].scriptSigHash); + } else { + skipBits(dst, 256); + } + return true; +} + +/* output_value : TWO^32 |- S (TWO^64) */ +bool simplicity_bitcoin_output_value(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numOutputs)) { + simplicity_write64(dst, env->tx->output[i].value); + } else { + skipBits(dst, 64); + } + return true; +} + +/* output_script_hash : TWO^32 |- S TWO^256 */ +bool simplicity_bitcoin_output_script_hash(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numOutputs)) { + writeHash(dst, &env->tx->output[i].scriptPubKey); + } else { + skipBits(dst, 256); + } + return true; +} + +/* fee : ONE |- TWO^64 */ +bool simplicity_bitcoin_fee(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write64(dst, env->tx->totalInputValue - env->tx->totalOutputValue); + return true; +} + +/* total_input_value : ONE |- TWO^64 */ +bool simplicity_bitcoin_total_input_value(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write64(dst, env->tx->totalInputValue); + return true; +} + +/* total_output_value : ONE |- TWO^64 */ +bool simplicity_bitcoin_total_output_value(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write64(dst, env->tx->totalOutputValue); + return true; +} + +/* script_cmr : ONE |- TWO^256 */ +bool simplicity_bitcoin_script_cmr(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + write32s(dst, env->taproot->scriptCMR.s, 8); + return true; +} + +/* transaction_id : ONE |- TWO^256 */ +bool simplicity_bitcoin_transaction_id(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + write32s(dst, env->tx->txid.s, 8); + return true; +} + +/* current_index : ONE |- TWO^32 */ +bool simplicity_bitcoin_current_index(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write32(dst, env->ix); + return true; +} + +/* current_prev_outpoint : ONE |- TWO^256 * TWO^32 */ +bool simplicity_bitcoin_current_prev_outpoint(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + if (env->tx->numInputs <= env->ix) return false; + prevOutpoint(dst, &env->tx->input[env->ix].prevOutpoint); + return true; +} + +/* current_value : ONE |- (TWO^64) */ +bool simplicity_bitcoin_current_value(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + if (env->tx->numInputs <= env->ix) return false; + simplicity_write64(dst, env->tx->input[env->ix].txo.value); + return true; +} + +/* current_script_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_current_script_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + if (env->tx->numInputs <= env->ix) return false; + writeHash(dst, &env->tx->input[env->ix].txo.scriptPubKey); + return true; +} + +/* current_sequence : ONE |- TWO^32 */ +bool simplicity_bitcoin_current_sequence(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + if (env->tx->numInputs <= env->ix) return false; + simplicity_write32(dst, env->tx->input[env->ix].sequence); + return true; +} + +/* current_script_sig_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_current_script_sig_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + if (env->tx->numInputs <= env->ix) return false; + writeHash(dst, &env->tx->input[env->ix].scriptSigHash); + return true; +} + +/* current_annex_hash : ONE |- S (TWO^256) */ +bool simplicity_bitcoin_current_annex_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + if (env->tx->numInputs <= env->ix) return false; + if (writeBit(dst, env->tx->input[env->ix].hasAnnex)) { + writeHash(dst, &env->tx->input[env->ix].annexHash); + } else { + skipBits(dst, 256); + } + return true; +} + +/* tapleaf_version : ONE |- TWO^8 */ +bool simplicity_bitcoin_tapleaf_version(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write8(dst, env->taproot->leafVersion); + return true; +} + +/* tappath : TWO^8 |- S (TWO^256) */ +bool simplicity_bitcoin_tappath(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast8_t i = simplicity_read8(&src); + if (writeBit(dst, i < env->taproot->pathLen)) { + writeHash(dst, &env->taproot->path[i]); + } else { + skipBits(dst, 256); + } + return true; +} + +/* internal_key : ONE |- TWO^256 */ +bool simplicity_bitcoin_internal_key(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->taproot->internalKey); + return true; +} + +/* num_inputs : ONE |- TWO^32 */ +bool simplicity_bitcoin_num_inputs(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write32(dst, env->tx->numInputs); + return true; +} + +/* num_outputs : ONE |- TWO^32 */ +bool simplicity_bitcoin_num_outputs(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write32(dst, env->tx->numOutputs); + return true; +} + +/* tx_is_final : ONE |- TWO */ +bool simplicity_bitcoin_tx_is_final(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeBit(dst, env->tx->isFinal); + return true; +} + +/* tx_lock_height : ONE |- TWO^32 */ +bool simplicity_bitcoin_tx_lock_height(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write32(dst, lockHeight(env->tx)); + return true; +} + +/* tx_lock_time : ONE |- TWO^32 */ +bool simplicity_bitcoin_tx_lock_time(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write32(dst, lockTime(env->tx)); + return true; +} + +/* tx_lock_distance : ONE |- TWO^16 */ +bool simplicity_bitcoin_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write16(dst, lockDistance(env->tx)); + return true; +} + +/* tx_lock_duration : ONE |- TWO^16 */ +bool simplicity_bitcoin_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + simplicity_write16(dst, lockDuration(env->tx)); + return true; +} + +/* check_lock_height : TWO^32 |- ONE */ +bool simplicity_bitcoin_check_lock_height(frameItem* dst, frameItem src, const txEnv* env) { + (void) dst; // dst is unused; + uint_fast32_t x = simplicity_read32(&src); + return x <= lockHeight(env->tx); +} + +/* check_lock_time : TWO^32 |- ONE */ +bool simplicity_bitcoin_check_lock_time(frameItem* dst, frameItem src, const txEnv* env) { + (void) dst; // dst is unused; + uint_fast32_t x = simplicity_read32(&src); + return x <= lockTime(env->tx); +} + +/* check_lock_distance : TWO^16 |- ONE */ +bool simplicity_bitcoin_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { + (void) dst; // dst is unused; + uint_fast16_t x = simplicity_read16(&src); + return x <= lockDistance(env->tx); +} + +/* check_lock_duration : TWO^16 |- ONE */ +bool simplicity_bitcoin_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { + (void) dst; // dst is unused; + uint_fast16_t x = simplicity_read16(&src); + return x <= lockDuration(env->tx); +} + +/* build_tapleaf_simplicity : TWO^256 |- TWO^256 */ +bool simplicity_bitcoin_build_tapleaf_simplicity(frameItem* dst, frameItem src, const txEnv* env) { + (void) env; // env is unused. + sha256_midstate cmr; + readHash(&cmr, &src); + sha256_midstate result = simplicity_bitcoin_make_tapleaf(0xbe, &cmr); + writeHash(dst, &result); + return true; +} + +/* build_tapbranch : TWO^256 * TWO^256 |- TWO^256 */ +bool simplicity_bitcoin_build_tapbranch(frameItem* dst, frameItem src, const txEnv* env) { + (void) env; // env is unused. + sha256_midstate a, b; + readHash(&a, &src); + readHash(&b, &src); + + sha256_midstate result = simplicity_bitcoin_make_tapbranch(&a, &b); + writeHash(dst, &result); + return true; +} + +/* build_taptweak : PUBKEY * TWO^256 |- PUBKEY */ +bool simplicity_bitcoin_build_taptweak(frameItem* dst, frameItem src, const txEnv* env) { + (void) env; // env is unused. + static unsigned char taptweak[] = "TapTweak"; + return simplicity_generic_taptweak(dst, &src, taptweak, sizeof(taptweak)-1); +} + +/* outpoint_hash : CTX8 * TWO^256 * TWO^32 |- CTX8 */ +bool simplicity_bitcoin_outpoint_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) env; // env is unused. + sha256_midstate midstate; + unsigned char buf[36]; + sha256_context ctx = {.output = midstate.s}; + + /* Read a SHA-256 context. */ + if (!simplicity_read_sha256_context(&ctx, &src)) return false; + + /* Read an outpoint (hash and index). */ + read8s(buf, 36, &src); + sha256_uchars(&ctx, buf, 36); + + return simplicity_write_sha256_context(dst, &ctx); +} + +/* annex_hash : CTX8 * S TWO^256 |- CTX8 */ +bool simplicity_bitcoin_annex_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) env; // env is unused. + sha256_midstate midstate; + unsigned char buf[32]; + sha256_context ctx = {.output = midstate.s}; + + /* Read a SHA-256 context. */ + if (!simplicity_read_sha256_context(&ctx, &src)) return false; + + /* Read an optional hash. (257 bits) */ + if (readBit(&src)) { + /* Read a hash. (256 bits) */ + read8s(buf, 32, &src); + sha256_uchar(&ctx, 0x01); + sha256_uchars(&ctx, buf, 32); + } else { + /* No hash. */ + sha256_uchar(&ctx, 0x00); + } + + return simplicity_write_sha256_context(dst, &ctx); +} + +/* output_amounts_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_output_values_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->outputValuesHash); + return true; +} + +/* output_scripts_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_output_scripts_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->outputScriptsHash); + return true; +} + +/* outputs_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_outputs_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->outputsHash); + return true; +} + +/* output_hash : TWO^32 |- S TWO^256 */ +bool simplicity_bitcoin_output_hash(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numOutputs)) { + const sigOutput* output = &env->tx->output[i]; + sha256_midstate midstate; + sha256_context ctx = sha256_init(midstate.s); + sha256_u64be(&ctx, output->value); + sha256_hash(&ctx, &output->scriptPubKey); + sha256_finalize(&ctx); + writeHash(dst, &midstate); + } else { + skipBits(dst, 256); + } + return true; +} + +/* input_outpoints_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_input_outpoints_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputOutpointsHash); + return true; +} + +/* input_values_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_input_values_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputValuesHash); + return true; +} + +/* input_scripts_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_input_scripts_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputScriptsHash); + return true; +} + +/* input_utxos_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_input_utxos_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputUTXOsHash); + return true; +} + +/* input_utxo_hash : TWO^32 |- S TWO^256 */ +bool simplicity_bitcoin_input_utxo_hash(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + const sigOutput* txo = &env->tx->input[i].txo; + sha256_midstate midstate; + sha256_context ctx = sha256_init(midstate.s); + sha256_u64be(&ctx, txo->value); + sha256_hash(&ctx, &txo->scriptPubKey); + sha256_finalize(&ctx); + writeHash(dst, &midstate); + } else { + skipBits(dst, 256); + } + return true; +} + +/* input_sequences_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_input_sequences_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputSequencesHash); + return true; +} + +/* input_annexes_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_input_annexes_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputAnnexesHash); + return true; +} + +/* input_script_sigs_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_input_script_sigs_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputScriptSigsHash); + return true; +} + +/* inputs_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_inputs_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->inputsHash); + return true; +} + +/* input_hash : TWO^32 |- S TWO^256 */ +bool simplicity_bitcoin_input_hash(frameItem* dst, frameItem src, const txEnv* env) { + uint_fast32_t i = simplicity_read32(&src); + if (writeBit(dst, i < env->tx->numInputs)) { + const sigInput* input = &env->tx->input[i]; + sha256_midstate midstate; + sha256_context ctx = sha256_init(midstate.s); + sha256_hash(&ctx, &input->prevOutpoint.txid); + sha256_u32be(&ctx, input->prevOutpoint.ix); + sha256_u32be(&ctx, input->sequence); + if (input->hasAnnex) { + sha256_uchar(&ctx, 1); + sha256_hash(&ctx, &input->annexHash); + } else { + sha256_uchar(&ctx, 0); + } + sha256_finalize(&ctx); + writeHash(dst, &midstate); + } else { + skipBits(dst, 256); + } + return true; +} + +/* tx_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_tx_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->tx->txHash); + return true; +} + +/* tapleaf_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_tapleaf_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->taproot->tapLeafHash); + return true; +} + +/* tappath_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_tappath_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->taproot->tappathHash); + return true; +} + +/* tap_env_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_tap_env_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->taproot->tapEnvHash); + return true; +} + +/* sig_all_hash : ONE |- TWO^256 */ +bool simplicity_bitcoin_sig_all_hash(frameItem* dst, frameItem src, const txEnv* env) { + (void) src; // src is unused; + writeHash(dst, &env->sigAllHash); + return true; +} diff --git a/C/bitcoin/bitcoinJets.h b/C/bitcoin/bitcoinJets.h new file mode 100644 index 00000000..1143298e --- /dev/null +++ b/C/bitcoin/bitcoinJets.h @@ -0,0 +1,70 @@ +/* This module defines primitives and jets that are specific to the Bitcoin application for Simplicity. + */ +#ifndef SIMPLICITY_BITCOIN_BITCOINJETS_H +#define SIMPLICITY_BITCOIN_BITCOINJETS_H + +#include "../jets.h" + +/* Jets for the Bitcoin application of Simplicity. */ +bool simplicity_bitcoin_version(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_lock_time(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_prev_outpoint(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_value(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_script_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_sequence(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_annex_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_script_sig_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_output_value(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_output_script_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_fee(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_total_input_value(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_total_output_value(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_script_cmr(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_transaction_id(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_current_index(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_current_prev_outpoint(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_current_value(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_current_script_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_current_sequence(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_current_annex_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_current_script_sig_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tapleaf_version(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tappath(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_internal_key(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_num_inputs(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_num_outputs(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tx_is_final(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tx_lock_height(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tx_lock_time(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_check_lock_height(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_check_lock_time(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_build_tapleaf_simplicity(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_build_tapbranch(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_build_taptweak(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_outpoint_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_annex_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_output_values_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_output_scripts_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_outputs_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_output_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_outpoints_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_values_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_scripts_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_utxos_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_utxo_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_sequences_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_annexes_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_script_sigs_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_inputs_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_input_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tx_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tapleaf_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tappath_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_tap_env_hash(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_bitcoin_sig_all_hash(frameItem* dst, frameItem src, const txEnv* env); + +#endif diff --git a/C/bitcoin/env.c b/C/bitcoin/env.c new file mode 100644 index 00000000..5d4bcd6b --- /dev/null +++ b/C/bitcoin/env.c @@ -0,0 +1,268 @@ +#include + +#include +#include +#include +#include "txEnv.h" +#include "ops.h" +#include "../sha256.h" +#include "../simplicity_assert.h" +#include "../simplicity_alloc.h" + +#define PADDING(alignType, allocated) ((alignof(alignType) - (allocated) % alignof(alignType)) % alignof(alignType)) + +/* Compute the SHA-256 hash of a scriptPubKey and write it into 'result'. + * + * Precondition: NULL != result; + * NULL != scriptPubKey; + */ +static void hashBuffer(sha256_midstate* result, const rawBitcoinBuffer* buffer) { + sha256_context ctx = sha256_init(result->s); + sha256_uchars(&ctx, buffer->buf, buffer->len); + sha256_finalize(&ctx); +} + +/* Initialize a 'sigOutput' from a 'rawOutput', copying or hashing the data as needed. + * + * Precondition: NULL != result; + * NULL != output; + */ +static void copyOutput(sigOutput* result, const rawBitcoinOutput* output) { + hashBuffer(&result->scriptPubKey, &output->scriptPubKey); + result->value = output->value; +} + +/* Initialize a 'sigInput' from a 'rawBitcoinInput', copying or hashing the data as needed. + * + * Precondition: NULL != result; + * NULL != input; + */ +static void copyInput(sigInput* result, const rawBitcoinInput* input) { + *result = (sigInput){ .prevOutpoint = { .ix = input->prevIx } + , .sequence = input->sequence + , .hasAnnex = !!input->annex + }; + + if (input->annex) hashBuffer(&result->annexHash, input->annex); + sha256_toMidstate(result->prevOutpoint.txid.s, input->prevTxid); + copyOutput(&result->txo, &input->txo); + hashBuffer(&result->scriptSigHash, &input->scriptSig); +} + +/* Allocate and initialize a 'bitcoinTransaction' from a 'rawBitcoinTransaction', copying or hashing the data as needed. + * Returns NULL if malloc fails (or if malloc cannot be called because we require an allocation larger than SIZE_MAX). + * + * Precondition: NULL != rawTx + */ +extern bitcoinTransaction* simplicity_bitcoin_mallocTransaction(const rawBitcoinTransaction* rawTx) { + if (!rawTx) return NULL; + + size_t allocationSize = sizeof(bitcoinTransaction); + + const size_t pad1 = PADDING(sigInput, allocationSize); + if (SIZE_MAX - allocationSize < pad1) return NULL; + allocationSize += pad1; + + /* Multiply by (size_t)1 to disable type-limits warning. */ + if (SIZE_MAX / sizeof(sigInput) < (size_t)1 * rawTx->numInputs) return NULL; + if (SIZE_MAX - allocationSize < rawTx->numInputs * sizeof(sigInput)) return NULL; + allocationSize += rawTx->numInputs * sizeof(sigInput); + + const size_t pad2 = PADDING(sigOutput, allocationSize); + if (SIZE_MAX - allocationSize < pad2) return NULL; + allocationSize += pad2; + + /* Multiply by (size_t)1 to disable type-limits warning. */ + if (SIZE_MAX / sizeof(sigOutput) < (size_t)1 * rawTx->numOutputs) return NULL; + if (SIZE_MAX - allocationSize < rawTx->numOutputs * sizeof(sigOutput)) return NULL; + allocationSize += rawTx->numOutputs * sizeof(sigOutput); + + char *allocation = simplicity_malloc(allocationSize); + if (!allocation) return NULL; + + /* Casting through void* to avoid warning about pointer alignment. + * Our padding is done carefully to ensure alignment. + */ + bitcoinTransaction* const tx = (bitcoinTransaction*)(void*)allocation; + allocation += sizeof(bitcoinTransaction) + pad1; + + sigInput* const input = (sigInput*)(void*)allocation; + allocation += rawTx->numInputs * sizeof(sigInput) + pad2; + + sigOutput* const output = (sigOutput*)(void*)allocation; + + *tx = (bitcoinTransaction){ .input = input + , .output = output + , .numInputs = rawTx->numInputs + , .numOutputs = rawTx->numOutputs + , .version = rawTx->version + , .lockTime = rawTx->lockTime + , .isFinal = true + }; + + sha256_toMidstate(tx->txid.s, rawTx->txid); + { + sha256_context ctx_inputOutpointsHash = sha256_init(tx->inputOutpointsHash.s); + sha256_context ctx_inputValuesHash = sha256_init(tx->inputValuesHash.s); + sha256_context ctx_inputScriptsHash = sha256_init(tx->inputScriptsHash.s); + sha256_context ctx_inputUTXOsHash = sha256_init(tx->inputUTXOsHash.s); + sha256_context ctx_inputSequencesHash = sha256_init(tx->inputSequencesHash.s); + sha256_context ctx_inputAnnexesHash = sha256_init(tx->inputAnnexesHash.s); + sha256_context ctx_inputScriptSigsHash = sha256_init(tx->inputScriptSigsHash.s); + sha256_context ctx_inputsHash = sha256_init(tx->inputsHash.s); + for (uint_fast32_t i = 0; i < tx->numInputs; ++i) { + copyInput(&input[i], &rawTx->input[i]); + tx->totalInputValue += input[i].txo.value; + if (input[i].sequence < 0xffffffff) { tx->isFinal = false; } + if (input[i].sequence < 0x80000000) { + const uint_fast16_t maskedSequence = input[i].sequence & 0xffff; + if (input[i].sequence & ((uint_fast32_t)1 << 22)) { + if (tx->lockDuration < maskedSequence) tx->lockDuration = maskedSequence; + } else { + if (tx->lockDistance < maskedSequence) tx->lockDistance = maskedSequence; + } + } + sha256_hash(&ctx_inputOutpointsHash, &input[i].prevOutpoint.txid); + sha256_u32be(&ctx_inputOutpointsHash, input[i].prevOutpoint.ix); + sha256_u64be(&ctx_inputValuesHash, input[i].txo.value); + sha256_hash(&ctx_inputScriptsHash, &input[i].txo.scriptPubKey); + sha256_u32be(&ctx_inputSequencesHash, input[i].sequence); + if (input[i].hasAnnex) { + sha256_uchar(&ctx_inputAnnexesHash, 1); + sha256_hash(&ctx_inputAnnexesHash, &input[i].annexHash); + } else { + sha256_uchar(&ctx_inputAnnexesHash, 0); + } + sha256_hash(&ctx_inputScriptSigsHash, &input[i].scriptSigHash); + } + sha256_finalize(&ctx_inputOutpointsHash); + sha256_finalize(&ctx_inputValuesHash); + sha256_finalize(&ctx_inputScriptsHash); + sha256_finalize(&ctx_inputSequencesHash); + sha256_finalize(&ctx_inputAnnexesHash); + sha256_finalize(&ctx_inputScriptSigsHash); + + sha256_hash(&ctx_inputUTXOsHash, &tx->inputValuesHash); + sha256_hash(&ctx_inputUTXOsHash, &tx->inputScriptsHash); + sha256_finalize(&ctx_inputUTXOsHash); + + sha256_hash(&ctx_inputsHash, &tx->inputOutpointsHash); + sha256_hash(&ctx_inputsHash, &tx->inputSequencesHash); + sha256_hash(&ctx_inputsHash, &tx->inputAnnexesHash); + sha256_finalize(&ctx_inputsHash); + } + + { + sha256_context ctx_outputValuesHash = sha256_init(tx->outputValuesHash.s); + sha256_context ctx_outputScriptsHash = sha256_init(tx->outputScriptsHash.s); + sha256_context ctx_outputsHash = sha256_init(tx->outputsHash.s); + + for (uint_fast32_t i = 0; i < tx->numOutputs; ++i) { + copyOutput(&output[i], &rawTx->output[i]); + tx->totalOutputValue += output[i].value; + sha256_u64be(&ctx_outputValuesHash, output[i].value); + sha256_hash(&ctx_outputScriptsHash, &output[i].scriptPubKey); + } + + sha256_finalize(&ctx_outputValuesHash); + sha256_finalize(&ctx_outputScriptsHash); + + sha256_hash(&ctx_outputsHash, &tx->outputValuesHash); + sha256_hash(&ctx_outputsHash, &tx->outputScriptsHash); + sha256_finalize(&ctx_outputsHash); + } + { + sha256_context ctx_txHash = sha256_init(tx->txHash.s); + sha256_u32be(&ctx_txHash, tx->version); + sha256_u32be(&ctx_txHash, tx->lockTime); + sha256_hash(&ctx_txHash, &tx->inputsHash); + sha256_hash(&ctx_txHash, &tx->outputsHash); + sha256_hash(&ctx_txHash, &tx->inputUTXOsHash); + sha256_finalize(&ctx_txHash); + } + + return tx; +} + +/* Free a pointer to 'bitcoinTransaction'. + */ +extern void simplicity_bitcoin_freeTransaction(bitcoinTransaction* tx) { + simplicity_free(tx); +} + +/* Allocate and initialize a 'bitcoinTapEnv' from a 'rawBitcoinTapEnv', copying or hashing the data as needed. + * Returns NULL if malloc fails (or if malloc cannot be called because we require an allocation larger than SIZE_MAX). + * + * Precondition: *rawEnv is well-formed (i.e. rawEnv->pathLen <= 128.) + */ +extern bitcoinTapEnv* simplicity_bitcoin_mallocTapEnv(const rawBitcoinTapEnv* rawEnv) { + if (!rawEnv) return NULL; + if (128 < rawEnv->pathLen) return NULL; + + size_t allocationSize = sizeof(bitcoinTapEnv); + + const size_t numMidstate = rawEnv->pathLen; + const size_t pad1 = PADDING(sha256_midstate, allocationSize); + + if (numMidstate) { + if (SIZE_MAX - allocationSize < pad1) return NULL; + allocationSize += pad1; + + if (SIZE_MAX / sizeof(sha256_midstate) < numMidstate) return NULL; + if (SIZE_MAX - allocationSize < numMidstate * sizeof(sha256_midstate)) return NULL; + allocationSize += numMidstate * sizeof(sha256_midstate); + } + + char *allocation = simplicity_malloc(allocationSize); + if (!allocation) return NULL; + + /* Casting through void* to avoid warning about pointer alignment. + * Our padding is done carefully to ensure alignment. + */ + bitcoinTapEnv* const env = (bitcoinTapEnv*)(void*)allocation; + sha256_midstate* path = NULL; + sha256_midstate internalKey; + + sha256_toMidstate(internalKey.s, &rawEnv->controlBlock[1]); + + if (numMidstate) { + allocation += sizeof(bitcoinTapEnv) + pad1; + + if (rawEnv->pathLen) { + path = (sha256_midstate*)(void*)allocation; + } + } + + *env = (bitcoinTapEnv){ .leafVersion = rawEnv->controlBlock[0] & 0xfe + , .internalKey = internalKey + , .path = path + , .pathLen = rawEnv->pathLen + }; + sha256_toMidstate(env->scriptCMR.s, rawEnv->scriptCMR); + + { + sha256_context ctx = sha256_init(env->tappathHash.s); + for (int i = 0; i < env->pathLen; ++i) { + sha256_toMidstate(path[i].s, &rawEnv->controlBlock[33+32*i]); + sha256_hash(&ctx, &path[i]); + } + sha256_finalize(&ctx); + } + + env->tapLeafHash = simplicity_bitcoin_make_tapleaf(env->leafVersion, &env->scriptCMR); + + { + sha256_context ctx = sha256_init(env->tapEnvHash.s); + sha256_hash(&ctx, &env->tapLeafHash); + sha256_hash(&ctx, &env->tappathHash); + sha256_hash(&ctx, &env->internalKey); + sha256_finalize(&ctx); + } + return env; +} + +/* Free a pointer to 'bitcoinTapEnv'. + */ +extern void simplicity_bitcoin_freeTapEnv(bitcoinTapEnv* env) { + simplicity_free(env); +} diff --git a/C/bitcoin/ops.c b/C/bitcoin/ops.c new file mode 100644 index 00000000..37daef6f --- /dev/null +++ b/C/bitcoin/ops.c @@ -0,0 +1,56 @@ +#include "ops.h" + +/* Compute Bitcoins's tapleaf hash from a tapleaf version and a 256-bit script value. + * A reimplementation of ComputeTapleafHash from Bitcoin's 'interpreter.cpp'. + * Only 256-bit script values are supported as that is the size used for Simplicity CMRs. + * + * Precondition: NULL != cmr; + */ +sha256_midstate simplicity_bitcoin_make_tapleaf(unsigned char version, const sha256_midstate* cmr) { + sha256_midstate result; + sha256_midstate tapleafTag; + { + static unsigned char tagName[] = "TapLeaf"; + sha256_context ctx = sha256_init(tapleafTag.s); + sha256_uchars(&ctx, tagName, sizeof(tagName) - 1); + sha256_finalize(&ctx); + } + sha256_context ctx = sha256_init(result.s); + sha256_hash(&ctx, &tapleafTag); + sha256_hash(&ctx, &tapleafTag); + sha256_uchar(&ctx, version); + sha256_uchar(&ctx, 32); + sha256_hash(&ctx, cmr); + sha256_finalize(&ctx); + + return result; +} + +/* Compute Bitcoins's tapbrach hash from two branches. + * + * Precondition: NULL != a; + * NULL != b; + */ +sha256_midstate simplicity_bitcoin_make_tapbranch(const sha256_midstate* a, const sha256_midstate* b) { + sha256_midstate result; + sha256_midstate tapbranchTag; + { + static unsigned char tagName[] = "TapBranch"; + sha256_context ctx = sha256_init(tapbranchTag.s); + sha256_uchars(&ctx, tagName, sizeof(tagName) - 1); + sha256_finalize(&ctx); + } + sha256_context ctx = sha256_init(result.s); + sha256_hash(&ctx, &tapbranchTag); + sha256_hash(&ctx, &tapbranchTag); + if (sha256_cmp_be(a, b) < 0) { + sha256_hash(&ctx, a); + sha256_hash(&ctx, b); + } else { + sha256_hash(&ctx, b); + sha256_hash(&ctx, a); + } + sha256_finalize(&ctx); + + return result; +} diff --git a/C/bitcoin/ops.h b/C/bitcoin/ops.h new file mode 100644 index 00000000..02664caa --- /dev/null +++ b/C/bitcoin/ops.h @@ -0,0 +1,23 @@ +/* This module defines operations used in the construction the environment ('txEnv') and some jets. + */ +#ifndef SIMPLICITY_BITCOIN_OPS_H +#define SIMPLICITY_BITCOIN_OPS_H + +#include "../sha256.h" + +/* Compute Bitcoin's tapleaf hash from a tapleaf version and a 256-bit script value. + * A reimplementation of ComputeTapleafHash from Bitcoin's 'interpreter.cpp'. + * Only 256-bit script values are supported as that is the size used for Simplicity CMRs. + * + * Precondition: NULL != cmr; + */ +sha256_midstate simplicity_bitcoin_make_tapleaf(unsigned char version, const sha256_midstate* cmr); + +/* Compute an Bitcoin's tapbrach hash from two branches. + * + * Precondition: NULL != a; + * NULL != b; + */ +sha256_midstate simplicity_bitcoin_make_tapbranch(const sha256_midstate* a, const sha256_midstate* b); + +#endif diff --git a/C/bitcoin/txEnv.c b/C/bitcoin/txEnv.c new file mode 100644 index 00000000..040626ad --- /dev/null +++ b/C/bitcoin/txEnv.c @@ -0,0 +1,22 @@ +#include "txEnv.h" + +/* Construct a txEnv structure from its components. + * This function will precompute any cached values. + * + * Precondition: NULL != tx + * NULL != taproot + * ix < tx->numInputs + */ +txEnv simplicity_bitcoin_build_txEnv(const bitcoinTransaction* tx, const bitcoinTapEnv* taproot, uint_fast32_t ix) { + txEnv result = { .tx = tx + , .taproot = taproot + , .ix = ix + }; + sha256_context ctx = sha256_init(result.sigAllHash.s); + sha256_hash(&ctx, &tx->txHash); + sha256_hash(&ctx, &taproot->tapEnvHash); + sha256_u32be(&ctx, ix); + sha256_finalize(&ctx); + + return result; +} diff --git a/C/bitcoin/txEnv.h b/C/bitcoin/txEnv.h new file mode 100644 index 00000000..1095e05b --- /dev/null +++ b/C/bitcoin/txEnv.h @@ -0,0 +1,109 @@ +/* This module defines the environment ('txEnv') for Simplicity evaluation for Bitcoin. + * It includes the transaction data and input index of the input whose Simplicity program is being executed. + * It also includes the commitment Merkle root of the program being executed. + */ +#ifndef SIMPLICITY_BITCOIN_TXENV_H +#define SIMPLICITY_BITCOIN_TXENV_H + +#include +#include "../sha256.h" + +/* An Bitcoin 'outpoint' consists of a transaction id and output index within that transaction. + */ +typedef struct outpoint { + sha256_midstate txid; + uint_fast32_t ix; +} outpoint; + +/* A structure representing data from one output from a Bitcoin transaction. + * 'scriptPubKey' is the SHA-256 hash of the outputs scriptPubKey. + */ +typedef struct sigOutput { + uint_fast64_t value; + sha256_midstate scriptPubKey; +} sigOutput; + +/* A structure representing data from one input from a Bitcoin transaction along with the utxo data of the output being redeemed. + * When 'hasAnnex' then 'annexHash' is a cache of the hash of the input's segwit annex. + */ +typedef struct sigInput { + sha256_midstate annexHash; + sha256_midstate scriptSigHash; + outpoint prevOutpoint; + sigOutput txo; + uint_fast32_t sequence; + bool hasAnnex; +} sigInput; + +/* A structure representing data from a Bitcoin transaction (along with the utxo data of the outputs being redeemed). + * Includes a variety of cached hash values that are used in signature hash jets. + */ +typedef struct bitcoinTransaction { + const sigInput* input; + const sigOutput* output; + sha256_midstate outputValuesHash; + sha256_midstate outputScriptsHash; + sha256_midstate outputsHash; + sha256_midstate inputOutpointsHash; + sha256_midstate inputValuesHash; + sha256_midstate inputScriptsHash; + sha256_midstate inputUTXOsHash; + sha256_midstate inputSequencesHash; + sha256_midstate inputAnnexesHash; + sha256_midstate inputScriptSigsHash; + sha256_midstate inputsHash; + sha256_midstate txHash; + sha256_midstate txid; + uint_fast64_t totalInputValue; + uint_fast64_t totalOutputValue; + uint_fast32_t numInputs; + uint_fast32_t numOutputs; + uint_fast32_t version; + uint_fast32_t lockTime; + /* lockDuration and lockDistance values are set even when the version is 0 or 1. + * This is similar to lockTime whose value is also set, even when the transaction is final. + */ + uint_fast16_t lockDistance; + uint_fast16_t lockDuration; /* Units of 512 seconds */ + bool isFinal; +} bitcoinTransaction; + +/* A structure representing taproot spending data from a Bitcoin transaction. + * + * Invariant: pathLen <= 128 + * sha256_midstate path[pathLen]; + */ +typedef struct bitcoinTapEnv { + const sha256_midstate *path; + sha256_midstate tapLeafHash; + sha256_midstate tappathHash; + sha256_midstate tapEnvHash; + sha256_midstate internalKey; + sha256_midstate scriptCMR; + unsigned char pathLen; + unsigned char leafVersion; +} bitcoinTapEnv; + +/* The 'txEnv' structure used by the Bitcoin application of Simplicity. + * + * It includes + * + the transaction data, which may be shared when Simplicity expressions are used for multiple inputs in the same transaction), + * + the input index under consideration, + */ +typedef struct txEnv { + const bitcoinTransaction* tx; + const bitcoinTapEnv* taproot; + sha256_midstate sigAllHash; + uint_fast32_t ix; +} txEnv; + +/* Construct a txEnv structure from its components. + * This function will precompute any cached values. + * + * Precondition: NULL != tx + * NULL != taproot + * ix < tx->numInputs + */ +txEnv simplicity_bitcoin_build_txEnv(const bitcoinTransaction* tx, const bitcoinTapEnv* taproot, uint_fast32_t ix); + +#endif diff --git a/C/include/simplicity/bitcoin/env.h b/C/include/simplicity/bitcoin/env.h new file mode 100644 index 00000000..0a7debef --- /dev/null +++ b/C/include/simplicity/bitcoin/env.h @@ -0,0 +1,98 @@ +#ifndef SIMPLICITY_BITCOIN_ENV_H +#define SIMPLICITY_BITCOIN_ENV_H + +#include +#include + +/* This section builds the 'rawBitcoinTransaction' structure which is the transaction data needed to build a Bitcoin 'txEnv' environment + * for evaluating Simplicity expressions within. + * The 'rawBitcoinTransaction' is copied into an opaque 'bitcoinTransaction' structure that can be reused within evaluating Simplicity on multiple + * inputs within the same transaction. + */ + +/* A type for an unparsed buffer + * + * Invariant: if 0 < len then unsigned char buf[len] + */ +typedef struct rawBitcoinBuffer { + const unsigned char* buf; + uint32_t len; +} rawBitcoinBuffer; + +/* A structure representing data for one output from a Bitcoin transaction. + */ +typedef struct rawBitcoinOutput { + uint64_t value; + rawBitcoinBuffer scriptPubKey; +} rawBitcoinOutput; + +/* A structure representing data for one input from a Bitcoin transaction, including its taproot annex, + * plus the TXO data of the output being redeemed. + * + * Invariant: unsigned char prevTxid[32]; + */ +typedef struct rawInput { + const rawBitcoinBuffer* annex; + const unsigned char* prevTxid; + rawBitcoinOutput txo; + rawBitcoinBuffer scriptSig; + uint32_t prevIx; + uint32_t sequence; +} rawBitcoinInput; + +/* A structure representing data for a Bitcoin transaction, including the TXO data of each output being redeemed. + * + * Invariant: unsigned char txid[32]; + * rawBitcoinInput input[numInputs]; + * rawBitcoinOutput output[numOutputs]; + */ +typedef struct rawBitcoinTransaction { + const unsigned char* txid; /* While in theory we could recompute the txid ourselves, it is easier and safer for it to be provided. */ + const rawBitcoinInput* input; + const rawBitcoinOutput* output; + uint32_t numInputs; + uint32_t numOutputs; + uint32_t version; + uint32_t lockTime; +} rawBitcoinTransaction; + +/* A forward declaration for the structure containing a copy (and digest) of the rawTransaction data */ +typedef struct bitcoinTransaction bitcoinTransaction; + +/* Allocate and initialize a 'bitcoinTransaction' from a 'rawBitcoinTransaction', copying or hashing the data as needed. + * Returns NULL if malloc fails (or if malloc cannot be called because we require an allocation larger than SIZE_MAX). + * + * Precondition: NULL != rawTx + */ +extern bitcoinTransaction* simplicity_bitcoin_mallocTransaction(const rawBitcoinTransaction* rawTx); + +/* Free a pointer to 'bitcoinTransaction'. + */ +extern void simplicity_bitcoin_freeTransaction(bitcoinTransaction* tx); + +/* A structure representing taproot spending data for a Bitcoin transaction. + * + * Invariant: pathLen <= 128; + * unsigned char controlBlock[33+pathLen*32]; + * unsigned char scriptCMR[32]; + */ +typedef struct rawBitcoinTapEnv { + const unsigned char* controlBlock; + const unsigned char* scriptCMR; + unsigned char pathLen; +} rawBitcoinTapEnv; + +/* A forward declaration for the structure containing a copy (and digest) of the rawBitcoinTapEnv data */ +typedef struct bitcoinTapEnv bitcoinTapEnv; + +/* Allocate and initialize a 'bitcoinTapEnv' from a 'rawBitcoinTapEnv', copying or hashing the data as needed. + * Returns NULL if malloc fails (or if malloc cannot be called because we require an allocation larger than SIZE_MAX). + * + * Precondition: *rawEnv is well-formed (i.e. rawEnv->pathLen <= 128.) + */ +extern bitcoinTapEnv* simplicity_bitcoin_mallocTapEnv(const rawBitcoinTapEnv* rawEnv); + +/* Free a pointer to 'bitcoinTapEnv'. + */ +extern void simplicity_bitcoin_freeTapEnv(bitcoinTapEnv* env); +#endif diff --git a/Haskell/Simplicity/Bitcoin/FFI/Env.hs b/Haskell/Simplicity/Bitcoin/FFI/Env.hs new file mode 100644 index 00000000..eefcb371 --- /dev/null +++ b/Haskell/Simplicity/Bitcoin/FFI/Env.hs @@ -0,0 +1,164 @@ +-- | This module binds the C implementation of jets for Simplicity for assertions. +{-# LANGUAGE ForeignFunctionInterface #-} +module Simplicity.Bitcoin.FFI.Env + ( CTransaction, CTapEnv, CTxEnv + , marshallTransaction, marshallTapEnv + , withEnv, withPrimEnv + ) where + +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BSL +import Data.Foldable (toList) +import Data.Serialize (Serialize, encode, runPut) +import Data.Vector (Vector) +import Data.Word (Word32) +import Foreign.C.Types (CSize(..), CChar(..), CUChar(..), CUInt(..), CULong(..)) +import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr) +import Foreign.Marshal.Alloc (allocaBytes) +import Foreign.Marshal.Array (withArray, withArrayLen) +import Foreign.Marshal.Unsafe (unsafeLocalState) +import Foreign.Ptr (FunPtr, Ptr, nullPtr, plusPtr) +import Foreign.Storable (Storable(..)) +import Lens.Family2 ((^.), under) + +import Simplicity.Digest +import Simplicity.Bitcoin.DataTypes +import Simplicity.Bitcoin.Primitive + +-- Abstract representative for the C txEnv types. +newtype RawBuffer = RawBuffer RawBuffer +newtype RawOutput = RawOutput RawOutput +newtype RawInput = RawInput RawInput +newtype RawTransaction = RawTransaction RawTransaction +newtype RawTapEnv = RawTapEnv RawTapEnv +newtype CTransaction = CTransaction CTransaction +newtype CTapEnv = CTapEnv CTapEnv +newtype CTxEnv = CTxEnv CTxEnv + +foreign import ccall unsafe "&" c_sizeof_rawBitcoinBuffer :: Ptr CSize +foreign import ccall unsafe "&" c_sizeof_rawBitcoinOutput :: Ptr CSize +foreign import ccall unsafe "&" c_sizeof_rawBitcoinInput :: Ptr CSize +foreign import ccall unsafe "&" c_sizeof_rawBitcoinTransaction :: Ptr CSize +foreign import ccall unsafe "&" c_sizeof_rawBitcoinTapEnv :: Ptr CSize +foreign import ccall unsafe "&" c_bitcoin_sizeof_txEnv :: Ptr CSize + +foreign import ccall unsafe "" c_set_rawBitcoinBuffer :: Ptr RawBuffer -> Ptr CChar -> CUInt -> IO () +foreign import ccall unsafe "" c_set_rawBitcoinOutput :: Ptr RawOutput -> CULong -> Ptr RawBuffer -> IO () +foreign import ccall unsafe "" c_set_rawBitcoinInput :: Ptr RawInput -> Ptr RawBuffer -> Ptr RawBuffer + -> Ptr CChar -> CUInt + -> CULong -> Ptr RawBuffer + -> CUInt -> IO () +foreign import ccall unsafe "" c_set_rawBitcoinTransaction :: Ptr RawTransaction -> Ptr CChar -> CUInt + -> Ptr RawInput -> CUInt + -> Ptr RawOutput -> CUInt + -> CUInt -> IO () +foreign import ccall unsafe "" c_set_rawBitcoinTapEnv :: Ptr RawTapEnv -> Ptr CChar -> CUChar -> Ptr CChar -> IO () +foreign import ccall unsafe "" c_bitcoin_set_txEnv :: Ptr CTxEnv -> Ptr CTransaction -> Ptr CTapEnv -> CUInt -> IO () + +foreign import ccall unsafe "" simplicity_bitcoin_mallocTransaction :: Ptr RawTransaction -> IO (Ptr CTransaction) +foreign import ccall unsafe "&" simplicity_bitcoin_freeTransaction :: FunPtr (Ptr CTransaction -> IO ()) +foreign import ccall unsafe "" simplicity_bitcoin_mallocTapEnv :: Ptr RawTapEnv -> IO (Ptr CTapEnv) +foreign import ccall unsafe "&" simplicity_bitcoin_freeTapEnv :: FunPtr (Ptr CTapEnv -> IO ()) + +sizeof_rawBuffer :: Int +sizeof_rawBuffer = fromIntegral . unsafeLocalState $ peek c_sizeof_rawBitcoinBuffer + +sizeof_rawOutput :: Int +sizeof_rawOutput = fromIntegral . unsafeLocalState $ peek c_sizeof_rawBitcoinOutput + +sizeof_rawInput :: Int +sizeof_rawInput = fromIntegral . unsafeLocalState $ peek c_sizeof_rawBitcoinInput + +sizeof_rawTransaction :: Int +sizeof_rawTransaction = fromIntegral . unsafeLocalState $ peek c_sizeof_rawBitcoinTransaction + +sizeof_rawTapEnv :: Int +sizeof_rawTapEnv = fromIntegral . unsafeLocalState $ peek c_sizeof_rawBitcoinTapEnv + +sizeof_txEnv :: Int +sizeof_txEnv = fromIntegral . unsafeLocalState $ peek c_bitcoin_sizeof_txEnv + +withRawBuffer :: BSL.ByteString -> (Ptr RawBuffer -> IO b) -> IO b +withRawBuffer str k = + allocaBytes sizeof_rawBuffer $ \pRawBuffer -> + BS.useAsCStringLen (BSL.toStrict str) $ \(pCharStr, len) -> do + c_set_rawBitcoinBuffer pRawBuffer pCharStr (fromIntegral len) + k pRawBuffer + +withRawOutputs :: Vector TxOutput -> (Ptr RawOutput -> IO b) -> IO b +withRawOutputs txos k = + allocaBytes (len * sizeof_rawOutput) $ \pRawOutput -> + foldr ($) (k pRawOutput) [pokeRawOutput txo (pRawOutput `plusPtr` (i*sizeof_rawOutput)) | (i, txo) <- zip [0..] (toList txos)] + where + len = fromIntegral $ length txos + pokeRawOutput :: TxOutput -> Ptr RawOutput -> IO b -> IO b + pokeRawOutput txo pRawOutput k = + withRawBuffer (txoScript txo) $ \pScript -> do + c_set_rawBitcoinOutput pRawOutput (fromIntegral $ txoValue txo) pScript + k + +withRawInputs :: Vector SigTxInput -> (Ptr RawInput -> IO b) -> IO b +withRawInputs txis k = + allocaBytes (len * sizeof_rawInput) $ \pRawInput -> do + foldr ($) (k pRawInput) [pokeRawInput txo (pRawInput `plusPtr` (i*sizeof_rawInput)) | (i, txo) <- zip [0..] (toList txis)] + where + len = fromIntegral $ length txis + withMaybeRawBuffer Nothing = ($ nullPtr) + withMaybeRawBuffer (Just buf) = withRawBuffer buf + pokeRawInput :: SigTxInput -> Ptr RawInput -> IO b -> IO b + pokeRawInput txi pRawInput k = + withMaybeRawBuffer (sigTxiAnnex txi) $ \pAnnex -> + withRawBuffer (sigTxiScriptSig txi) $ \pScriptSig -> + BS.useAsCString (encode . opHash $ sigTxiPreviousOutpoint txi) $ \pPrevTxid -> + withRawBuffer (txoScript $ sigTxiTxo txi) $ \pScript -> do + c_set_rawBitcoinInput pRawInput pAnnex pScriptSig + pPrevTxid (fromIntegral . opIndex . sigTxiPreviousOutpoint $ txi) + (fromIntegral . txoValue $ sigTxiTxo txi) pScript + (fromIntegral . sigTxiSequence $ txi) + k + +withRawTransaction :: SigTx -> (Ptr RawTransaction -> IO b) -> IO b +withRawTransaction tx k = + allocaBytes sizeof_rawTransaction $ \pRawTransaction -> + withRawInputs (sigTxIn tx) $ \pInput -> + withRawOutputs (sigTxOut tx) $ \pOutput -> do + BS.useAsCString (encode $ txid tx) $ \pTxid -> do + c_set_rawBitcoinTransaction pRawTransaction pTxid version pInput numInputs pOutput numOutputs lockTime + k pRawTransaction + where + version = fromIntegral (sigTxVersion tx) + numInputs = fromIntegral $ length (sigTxIn tx) + numOutputs = fromIntegral $ length (sigTxOut tx) + lockTime = fromIntegral (sigTxLock tx) + +withRawTapEnv :: TapEnv -> (Ptr RawTapEnv -> IO b) -> IO b +withRawTapEnv tapEnv k | length (tappath tapEnv) <= 128 = + allocaBytes sizeof_rawTapEnv $ \pRawTapEnv -> + BS.useAsCString encodePath $ \pControlBlock -> do + BS.useAsCString (encode $ tapScriptCMR tapEnv) $ \pCmr -> do + c_set_rawBitcoinTapEnv pRawTapEnv pControlBlock (fromIntegral . length $ tappath tapEnv) pCmr + k pRawTapEnv + where + encodePath = BS.cons (tapleafVersion tapEnv) (BS.concat (encode (tapInternalKey tapEnv) : map encode (tappath tapEnv))) + +marshallTransaction :: SigTx -> IO (ForeignPtr CTransaction) +marshallTransaction tx = withRawTransaction tx + $ \pRawTransaction -> simplicity_bitcoin_mallocTransaction pRawTransaction >>= newForeignPtr simplicity_bitcoin_freeTransaction + +marshallTapEnv :: TapEnv -> IO (ForeignPtr CTapEnv) +marshallTapEnv env = withRawTapEnv env + $ \pRawTapEnv -> simplicity_bitcoin_mallocTapEnv pRawTapEnv >>= newForeignPtr simplicity_bitcoin_freeTapEnv + +withEnv :: ForeignPtr CTransaction -> Word32 -> ForeignPtr CTapEnv -> (Ptr CTxEnv -> IO b) -> IO b +withEnv cTransaction ix cTapEnv k = + allocaBytes sizeof_txEnv $ \pTxEnv -> + withForeignPtr cTransaction $ \pTransaction -> + withForeignPtr cTapEnv $ \pTapEnv -> do + c_bitcoin_set_txEnv pTxEnv pTransaction pTapEnv (fromIntegral ix) + k pTxEnv + +withPrimEnv :: PrimEnv -> (Ptr CTxEnv -> IO b) -> IO b +withPrimEnv env k = do + cTransaction <- marshallTransaction (envTx env) + cTapEnv <- marshallTapEnv (envTap env) + withEnv cTransaction (envIx env) cTapEnv k diff --git a/Haskell/Simplicity/Bitcoin/FFI/Jets.hs b/Haskell/Simplicity/Bitcoin/FFI/Jets.hs new file mode 100644 index 00000000..f842d182 --- /dev/null +++ b/Haskell/Simplicity/Bitcoin/FFI/Jets.hs @@ -0,0 +1,321 @@ +-- | This module binds the C implementation of jets for Simplicity for assertions. +{-# LANGUAGE ForeignFunctionInterface #-} +module Simplicity.Bitcoin.FFI.Jets + ( version + , lock_time + , input_prev_outpoint + , input_value + , input_script_hash + , input_sequence + , input_annex_hash + , input_script_sig_hash + , output_value + , output_script_hash + , script_cmr + , transaction_id + , current_index + , current_prev_outpoint + , current_value + , current_script_hash + , current_sequence + , current_annex_hash + , current_script_sig_hash + , tapleaf_version + , tappath + , internal_key + , num_inputs + , num_outputs + , tx_is_final + , tx_lock_height + , tx_lock_time + , tx_lock_distance + , tx_lock_duration + , check_lock_height + , check_lock_time + , check_lock_distance + , check_lock_duration + , outpoint_hash + , annex_hash + , build_tapleaf_simplicity + , build_tapbranch + , build_taptweak + , output_values_hash + , output_scripts_hash + , outputs_hash + , output_hash + , total_output_value + , input_outpoints_hash + , input_values_hash + , input_scripts_hash + , input_utxos_hash + , input_utxo_hash + , input_sequences_hash + , input_annexes_hash + , input_script_sigs_hash + , inputs_hash + , input_hash + , total_input_value + , fee + , tx_hash + , tapleaf_hash + , tappath_hash + , tap_env_hash + , sig_all_hash + ) where + +import Foreign.Ptr (Ptr) +import Foreign.C.Types (CBool(..)) + +import Simplicity.Bitcoin.FFI.Env +import Simplicity.Bitcoin.Primitive +import Simplicity.FFI.Frame +import Simplicity.Programs.Elements +import Simplicity.Programs.LibSecp256k1 +import Simplicity.Ty +import Simplicity.Ty.Word + +-- | This cannot be used with jets that access global variables. +unsafeLocalJet :: (TyC a, TyC b) => (Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool) -> PrimEnv -> a -> Maybe b +unsafeLocalJet jet env = unsafeLocalCoreJet (\dst src -> withPrimEnv env (jet dst src)) + +foreign import ccall unsafe "" c_bitcoin_version :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_lock_time :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_prev_outpoint :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_value :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_script_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_sequence :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_annex_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_script_sig_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_output_value :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_output_script_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_script_cmr :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_transaction_id :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_current_index :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_current_prev_outpoint :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_current_value :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_current_script_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_current_sequence :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_current_annex_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_current_script_sig_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tapleaf_version :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tappath :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_internal_key :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_num_inputs :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_num_outputs :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tx_is_final :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tx_lock_height :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tx_lock_time :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tx_lock_distance :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tx_lock_duration :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_check_lock_height :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_check_lock_time :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_check_lock_distance :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_check_lock_duration :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_outpoint_hash :: Ptr FrameItem -> Ptr FrameItem -> IO CBool +foreign import ccall unsafe "" c_bitcoin_annex_hash :: Ptr FrameItem -> Ptr FrameItem -> IO CBool +foreign import ccall unsafe "" c_bitcoin_build_tapleaf_simplicity :: Ptr FrameItem -> Ptr FrameItem -> IO CBool +foreign import ccall unsafe "" c_bitcoin_build_tapbranch :: Ptr FrameItem -> Ptr FrameItem -> IO CBool +foreign import ccall unsafe "" c_bitcoin_build_taptweak :: Ptr FrameItem -> Ptr FrameItem -> IO CBool +foreign import ccall unsafe "" c_bitcoin_output_values_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_output_nonces_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_output_scripts_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_outputs_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_output_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_total_output_value :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_outpoints_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_values_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_scripts_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_utxos_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_utxo_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_sequences_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_annexes_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_script_sigs_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_inputs_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_input_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_total_input_value :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_fee :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tx_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tapleaf_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tappath_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_tap_env_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_bitcoin_sig_all_hash :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool + +version :: PrimEnv -> () -> Maybe Word32 +version = unsafeLocalJet c_bitcoin_version + +lock_time :: PrimEnv -> () -> Maybe Word32 +lock_time = unsafeLocalJet c_bitcoin_lock_time + +num_inputs :: PrimEnv -> () -> Maybe Word32 +num_inputs = unsafeLocalJet c_bitcoin_num_inputs + +input_prev_outpoint :: PrimEnv -> Word32 -> Maybe (S (Word256, Word32)) +input_prev_outpoint = unsafeLocalJet c_bitcoin_input_prev_outpoint + +input_value :: PrimEnv -> Word32 -> Maybe (S Word64) +input_value = unsafeLocalJet c_bitcoin_input_value + +input_script_hash :: PrimEnv -> Word32 -> Maybe (S Word256) +input_script_hash = unsafeLocalJet c_bitcoin_input_script_hash + +input_sequence :: PrimEnv -> Word32 -> Maybe (S Word32) +input_sequence = unsafeLocalJet c_bitcoin_input_sequence + +input_annex_hash :: PrimEnv -> Word32 -> Maybe (S (S Word256)) +input_annex_hash = unsafeLocalJet c_bitcoin_input_annex_hash + +input_script_sig_hash :: PrimEnv -> Word32 -> Maybe (S Word256) +input_script_sig_hash = unsafeLocalJet c_bitcoin_input_script_sig_hash + +current_index :: PrimEnv -> () -> Maybe Word32 +current_index = unsafeLocalJet c_bitcoin_current_index + +current_prev_outpoint :: PrimEnv -> () -> Maybe (Word256, Word32) +current_prev_outpoint = unsafeLocalJet c_bitcoin_current_prev_outpoint + +current_value :: PrimEnv -> () -> Maybe Word64 +current_value = unsafeLocalJet c_bitcoin_current_value + +current_script_hash :: PrimEnv -> () -> Maybe Word256 +current_script_hash = unsafeLocalJet c_bitcoin_current_script_hash + +current_sequence :: PrimEnv -> () -> Maybe Word32 +current_sequence = unsafeLocalJet c_bitcoin_current_sequence + +current_annex_hash :: PrimEnv -> () -> Maybe (S Word256) +current_annex_hash = unsafeLocalJet c_bitcoin_current_annex_hash + +current_script_sig_hash :: PrimEnv -> () -> Maybe Word256 +current_script_sig_hash = unsafeLocalJet c_bitcoin_current_script_sig_hash + +tapleaf_version :: PrimEnv -> () -> Maybe Word8 +tapleaf_version = unsafeLocalJet c_bitcoin_tapleaf_version + +tappath :: PrimEnv -> Word8 -> Maybe (S Word256) +tappath = unsafeLocalJet c_bitcoin_tappath + +internal_key :: PrimEnv -> () -> Maybe PubKey +internal_key = unsafeLocalJet c_bitcoin_internal_key + +num_outputs :: PrimEnv -> () -> Maybe Word32 +num_outputs = unsafeLocalJet c_bitcoin_num_outputs + +output_value :: PrimEnv -> Word32 -> Maybe (S Word64) +output_value = unsafeLocalJet c_bitcoin_output_value + +output_script_hash :: PrimEnv -> Word32 -> Maybe (S Word256) +output_script_hash = unsafeLocalJet c_bitcoin_output_script_hash + +script_cmr :: PrimEnv -> () -> Maybe Word256 +script_cmr = unsafeLocalJet c_bitcoin_script_cmr + +transaction_id :: PrimEnv -> () -> Maybe Word256 +transaction_id = unsafeLocalJet c_bitcoin_transaction_id + +tx_is_final :: PrimEnv -> () -> Maybe Bit +tx_is_final = unsafeLocalJet c_bitcoin_tx_is_final + +tx_lock_height :: PrimEnv -> () -> Maybe Word32 +tx_lock_height = unsafeLocalJet c_bitcoin_tx_lock_height + +tx_lock_time :: PrimEnv -> () -> Maybe Word32 +tx_lock_time = unsafeLocalJet c_bitcoin_tx_lock_time + +tx_lock_distance :: PrimEnv -> () -> Maybe Word16 +tx_lock_distance = unsafeLocalJet c_bitcoin_tx_lock_distance + +tx_lock_duration :: PrimEnv -> () -> Maybe Word16 +tx_lock_duration = unsafeLocalJet c_bitcoin_tx_lock_duration + +check_lock_height :: PrimEnv -> Word32 -> Maybe () +check_lock_height = unsafeLocalJet c_bitcoin_check_lock_height + +check_lock_time :: PrimEnv -> Word32 -> Maybe () +check_lock_time = unsafeLocalJet c_bitcoin_check_lock_time + +check_lock_distance :: PrimEnv -> Word16 -> Maybe () +check_lock_distance = unsafeLocalJet c_bitcoin_check_lock_distance + +check_lock_duration :: PrimEnv -> Word16 -> Maybe () +check_lock_duration = unsafeLocalJet c_bitcoin_check_lock_duration + +outpoint_hash :: (Ctx8, (Word256, Word32)) -> Maybe Ctx8 +outpoint_hash = unsafeLocalCoreJet c_bitcoin_outpoint_hash + +annex_hash :: (Ctx8, S Word256) -> Maybe Ctx8 +annex_hash = unsafeLocalCoreJet c_bitcoin_annex_hash + +build_tapleaf_simplicity :: Word256 -> Maybe Word256 +build_tapleaf_simplicity = unsafeLocalCoreJet c_bitcoin_build_tapleaf_simplicity + +build_tapbranch :: (Word256, Word256) -> Maybe Word256 +build_tapbranch = unsafeLocalCoreJet c_bitcoin_build_tapbranch + +build_taptweak :: (Word256, Word256) -> Maybe Word256 +build_taptweak = unsafeLocalCoreJet c_bitcoin_build_taptweak + +output_values_hash :: PrimEnv -> () -> Maybe Word256 +output_values_hash = unsafeLocalJet c_bitcoin_output_values_hash + +output_scripts_hash :: PrimEnv -> () -> Maybe Word256 +output_scripts_hash = unsafeLocalJet c_bitcoin_output_scripts_hash + +outputs_hash :: PrimEnv -> () -> Maybe Word256 +outputs_hash = unsafeLocalJet c_bitcoin_outputs_hash + +output_hash :: PrimEnv -> Word32 -> Maybe (S Word256) +output_hash = unsafeLocalJet c_bitcoin_output_hash + +total_output_value :: PrimEnv -> () -> Maybe Word64 +total_output_value = unsafeLocalJet c_bitcoin_total_output_value + +input_outpoints_hash :: PrimEnv -> () -> Maybe Word256 +input_outpoints_hash = unsafeLocalJet c_bitcoin_input_outpoints_hash + +input_values_hash :: PrimEnv -> () -> Maybe Word256 +input_values_hash = unsafeLocalJet c_bitcoin_input_values_hash + +input_scripts_hash :: PrimEnv -> () -> Maybe Word256 +input_scripts_hash = unsafeLocalJet c_bitcoin_input_scripts_hash + +input_utxos_hash :: PrimEnv -> () -> Maybe Word256 +input_utxos_hash = unsafeLocalJet c_bitcoin_input_utxos_hash + +input_utxo_hash :: PrimEnv -> Word32 -> Maybe (S Word256) +input_utxo_hash = unsafeLocalJet c_bitcoin_input_utxo_hash + +input_sequences_hash :: PrimEnv -> () -> Maybe Word256 +input_sequences_hash = unsafeLocalJet c_bitcoin_input_sequences_hash + +input_annexes_hash :: PrimEnv -> () -> Maybe Word256 +input_annexes_hash = unsafeLocalJet c_bitcoin_input_annexes_hash + +input_script_sigs_hash :: PrimEnv -> () -> Maybe Word256 +input_script_sigs_hash = unsafeLocalJet c_bitcoin_input_script_sigs_hash + +inputs_hash :: PrimEnv -> () -> Maybe Word256 +inputs_hash = unsafeLocalJet c_bitcoin_inputs_hash + +input_hash :: PrimEnv -> Word32 -> Maybe (S Word256) +input_hash = unsafeLocalJet c_bitcoin_input_hash + +total_input_value :: PrimEnv -> () -> Maybe Word64 +total_input_value = unsafeLocalJet c_bitcoin_total_input_value + +fee :: PrimEnv -> () -> Maybe Word64 +fee = unsafeLocalJet c_bitcoin_fee + +tx_hash :: PrimEnv -> () -> Maybe Word256 +tx_hash = unsafeLocalJet c_bitcoin_tx_hash + +tapleaf_hash :: PrimEnv -> () -> Maybe Word256 +tapleaf_hash = unsafeLocalJet c_bitcoin_tapleaf_hash + +tappath_hash :: PrimEnv -> () -> Maybe Word256 +tappath_hash = unsafeLocalJet c_bitcoin_tappath_hash + +tap_env_hash :: PrimEnv -> () -> Maybe Word256 +tap_env_hash = unsafeLocalJet c_bitcoin_tap_env_hash + +sig_all_hash :: PrimEnv -> () -> Maybe Word256 +sig_all_hash = unsafeLocalJet c_bitcoin_sig_all_hash diff --git a/Haskell/Tests/Simplicity/Bitcoin/FFI/Tests.hs b/Haskell/Tests/Simplicity/Bitcoin/FFI/Tests.hs new file mode 100644 index 00000000..a5015ab5 --- /dev/null +++ b/Haskell/Tests/Simplicity/Bitcoin/FFI/Tests.hs @@ -0,0 +1,410 @@ +module Simplicity.Bitcoin.FFI.Tests (tests) where + +import Control.Arrow ((***), (+++)) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe, isJust) +import Data.Vector ((!?)) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (Assertion, (@?=), testCase) +import Test.Tasty.QuickCheck (NonNegative(..), Property, classify, forAll, testProperty) +import Lens.Family2 (under, view) + +import Simplicity.Arbitrary +import Simplicity.Digest +import Simplicity.Bitcoin.Arbitrary +import qualified Simplicity.Bitcoin.DataTypes as Prim +import Simplicity.Bitcoin.FFI.Jets +import Simplicity.Bitcoin.Jets +import qualified Simplicity.Bitcoin.Primitive as Prim +import Simplicity.Bitcoin.Semantics +import Simplicity.Bitcoin.TestEval +import Simplicity.FFI.Jets +import qualified Simplicity.Programs.Bitcoin.Lib as Prog +import Simplicity.TestCoreEval +import Simplicity.Ty.Arbitrary +import Simplicity.Ty.Word +import qualified Simplicity.Word as Word + +toW32 :: Word.Word32 -> Word32 +toW32 = toWord32 . fromIntegral + +toW8 :: Word.Word8 -> Word8 +toW8 = toWord8 . fromIntegral + +tests :: TestTree +tests = testGroup "Bitcoin" + [ testGroup "Jets" + [ testProperty "tx_is_final" prop_tx_is_final + , testProperty "tx_lock_height" prop_tx_lock_height + , testProperty "tx_lock_time" prop_tx_lock_time + , testProperty "tx_lock_distance" prop_tx_lock_distance + , testProperty "tx_lock_duration" prop_tx_lock_duration + , testProperty "check_lock_height" prop_check_lock_height + , testProperty "check_lock_time" prop_check_lock_time + , testProperty "check_lock_distance" prop_check_lock_distance + , testProperty "check_lock_duration" prop_check_lock_duration + , testProperty "outpoint_hash" prop_outpoint_hash + , testProperty "annex_hash" prop_annex_hash + , testProperty "build_tapleaf_simplicity" prop_build_tapleaf_simplicity + , testProperty "build_tapbranch" prop_build_tapbranch + , testProperty "build_taptweak" prop_build_taptweak + , testProperty "output_values_hash" prop_output_values_hash + , testProperty "output_scripts_hash" prop_output_scripts_hash + , testProperty "outputs_hash" prop_outputs_hash + , testProperty "output_hash" prop_output_hash + , testProperty "input_outpoints_hash" prop_input_outpoints_hash + , testProperty "input_values_hash" prop_input_values_hash + , testProperty "input_scripts_hash" prop_input_scripts_hash + , testProperty "input_utxos_hash" prop_input_utxos_hash + , testProperty "input_utxo_hash" prop_input_utxo_hash + , testProperty "input_sequences_hash" prop_input_sequences_hash + , testProperty "input_annexes_hash" prop_input_annexes_hash + , testProperty "input_script_sigs_hash" prop_input_script_sigs_hash + , testProperty "inputs_hash" prop_inputs_hash + , testProperty "input_hash" prop_input_hash + , testProperty "tx_hash" prop_tx_hash + , testProperty "tapleaf_hash" prop_tapleaf_hash + , testProperty "tappath_hash" prop_tappath_hash + , testProperty "tap_env_hash" prop_tap_env_hash + , testProperty "sig_all_hash" prop_sig_all_hash + ] + , testGroup "Transaction" + [ testProperty "script_cmr" prop_script_cmr + , testProperty "internal_key" prop_internal_key + , testProperty "current_index" prop_current_index + , testProperty "num_inputs" prop_num_inputs + , testProperty "num_outputs" prop_num_outputs + , testProperty "lock_time" prop_lock_time + , testProperty "output_value" prop_output_value + , testProperty "output_script_hash" prop_output_script_hash + , testProperty "total_output_value" prop_total_output_value + , testProperty "current_prev_outpoint" prop_current_prev_outpoint + , testProperty "current_value" prop_current_value + , testProperty "current_script_hash" prop_current_script_hash + , testProperty "current_sequence" prop_current_sequence + , testProperty "current_annex_hash" prop_current_annex_hash + , testProperty "current_script_sig_hash" prop_current_script_sig_hash + , testProperty "input_prev_outpoint" prop_input_prev_outpoint + , testProperty "input_value" prop_input_value + , testProperty "input_script_hash" prop_input_script_hash + , testProperty "input_sequence" prop_input_sequence + , testProperty "input_annex_hash" prop_input_annex_hash + , testProperty "input_script_sig_hash" prop_input_script_sig_hash + , testProperty "total_input_value" prop_total_input_value + , testProperty "fee" prop_fee + , testProperty "tapleaf_version" prop_tapleaf_version + , testProperty "tappath" prop_tappath + , testProperty "version" prop_version + , testProperty "transaction_id" prop_transaction_id + ] + ] + +prop_tx_is_final :: Property +prop_tx_is_final = forallPrimEnv $ \env -> fast_tx_is_final env () == tx_is_final env () + where + fast_tx_is_final = testEval (specification (BitcoinJet (TimeLockJet TxIsFinal))) + +prop_tx_lock_height :: Property +prop_tx_lock_height = forallPrimEnv $ \env -> fast_tx_lock_height env () == tx_lock_height env () + where + fast_tx_lock_height = testEval (specification (BitcoinJet (TimeLockJet TxLockHeight))) + +prop_tx_lock_time :: Property +prop_tx_lock_time = forallPrimEnv $ \env -> fast_tx_lock_time env () == tx_lock_time env () + where + fast_tx_lock_time = testEval (specification (BitcoinJet (TimeLockJet TxLockTime))) + +prop_tx_lock_distance :: Property +prop_tx_lock_distance = forallPrimEnv $ \env -> fast_tx_lock_distance env () == tx_lock_distance env () + where + fast_tx_lock_distance = testEval (specification (BitcoinJet (TimeLockJet TxLockDistance))) + +prop_tx_lock_duration :: Property +prop_tx_lock_duration = forallPrimEnv $ \env -> fast_tx_lock_duration env () == tx_lock_duration env () + where + fast_tx_lock_duration = testEval (specification (BitcoinJet (TimeLockJet TxLockDuration))) + +prop_check_lock_height :: Word32 -> Property +prop_check_lock_height = \w -> forallPrimEnv $ \env -> fast_check_lock_height env w == check_lock_height env w + where + fast_check_lock_height = testEval (specification (BitcoinJet (TimeLockJet CheckLockHeight))) + +prop_check_lock_time :: Word32 -> Property +prop_check_lock_time = \w -> forallPrimEnv $ \env -> fast_check_lock_time env w == check_lock_time env w + where + fast_check_lock_time = testEval (specification (BitcoinJet (TimeLockJet CheckLockTime))) + +prop_check_lock_distance :: Word16 -> Property +prop_check_lock_distance = \w -> forallPrimEnv $ \env -> fast_check_lock_distance env w == check_lock_distance env w + where + fast_check_lock_distance = testEval (specification (BitcoinJet (TimeLockJet CheckLockDistance))) + +prop_check_lock_duration :: Word16 -> Property +prop_check_lock_duration = \w -> forallPrimEnv $ \env -> fast_check_lock_duration env w == check_lock_duration env w + where + fast_check_lock_duration = testEval (specification (BitcoinJet (TimeLockJet CheckLockDuration))) + +prop_outpoint_hash :: Sha256CtxElement -> (Word256, Word32) -> Bool +prop_outpoint_hash = \ctx op -> + let input = (ctxAsTy ctx, op) + in outpoint_hash input == fast_outpoint_hash input + where + fast_outpoint_hash = testCoreEval Prog.outpointHash + +prop_annex_hash :: Sha256CtxElement -> Maybe Word256 -> Bool +prop_annex_hash = \ctx mw256 -> + let input = (ctxAsTy ctx, cast mw256) + in annex_hash input == fast_annex_hash input + where + fast_annex_hash = testCoreEval Prog.annexHash + cast = maybe (Left ()) Right + +prop_build_tapleaf_simplicity :: Word256 -> Bool +prop_build_tapleaf_simplicity = \w -> + build_tapleaf_simplicity w == fast_build_tapleaf_simplicity w + where + fast_build_tapleaf_simplicity = testCoreEval Prog.buildTapleafSimplicity + +prop_build_tapbranch :: Word256 -> Word256 -> Bool +prop_build_tapbranch = \a b -> + build_tapbranch (a, b) == fast_build_tapbranch (a, b) + where + fast_build_tapbranch = testCoreEval Prog.buildTapbranch + +prop_build_taptweak :: FieldElement -> Word256 -> Bool +prop_build_taptweak = \a b -> + let input = (feAsTy a, b) in + build_taptweak input == fast_build_taptweak input + where + fast_build_taptweak = testCoreEval Prog.buildTaptweak + +prop_output_values_hash :: Property +prop_output_values_hash = forallPrimEnv $ \env -> fast_output_values_hash env () == output_values_hash env () + where + fast_output_values_hash = testEval (specification (BitcoinJet (SigHashJet OutputValuesHash))) + +prop_output_scripts_hash :: Property +prop_output_scripts_hash = forallPrimEnv $ \env -> fast_output_scripts_hash env () == output_scripts_hash env () + where + fast_output_scripts_hash = testEval (specification (BitcoinJet (SigHashJet OutputScriptsHash))) + +prop_outputs_hash :: Property +prop_outputs_hash = forallPrimEnv $ \env -> fast_outputs_hash env () == outputs_hash env () + where + fast_outputs_hash = testEval (specification (BitcoinJet (SigHashJet OutputsHash))) + +prop_output_hash :: Property +prop_output_hash = forallOutPrimEnv $ \env i -> fast_output_hash env (toW32 i) == output_hash env (toW32 i) + where + fast_output_hash = testEval (specification (BitcoinJet (SigHashJet OutputHash))) + +prop_input_outpoints_hash :: Property +prop_input_outpoints_hash = forallPrimEnv $ \env -> fast_input_outpoints_hash env () == input_outpoints_hash env () + where + fast_input_outpoints_hash = testEval (specification (BitcoinJet (SigHashJet InputOutpointsHash))) + +prop_input_values_hash :: Property +prop_input_values_hash = forallPrimEnv $ \env -> fast_input_values_hash env () == input_values_hash env () + where + fast_input_values_hash = testEval (specification (BitcoinJet (SigHashJet InputValuesHash))) + +prop_input_scripts_hash :: Property +prop_input_scripts_hash = forallPrimEnv $ \env -> fast_input_scripts_hash env () == input_scripts_hash env () + where + fast_input_scripts_hash = testEval (specification (BitcoinJet (SigHashJet InputScriptsHash))) + +prop_input_utxos_hash :: Property +prop_input_utxos_hash = forallPrimEnv $ \env -> fast_input_utxos_hash env () == input_utxos_hash env () + where + fast_input_utxos_hash = testEval (specification (BitcoinJet (SigHashJet InputUtxosHash))) + +prop_input_utxo_hash :: Property +prop_input_utxo_hash = forallInPrimEnv $ \env i -> fast_input_utxo_hash env (toW32 i) == input_utxo_hash env (toW32 i) + where + fast_input_utxo_hash = testEval (specification (BitcoinJet (SigHashJet InputUtxoHash))) + +prop_input_sequences_hash :: Property +prop_input_sequences_hash = forallPrimEnv $ \env -> fast_input_sequences_hash env () == input_sequences_hash env () + where + fast_input_sequences_hash = testEval (specification (BitcoinJet (SigHashJet InputSequencesHash))) + +prop_input_annexes_hash :: Property +prop_input_annexes_hash = forallPrimEnv $ \env -> fast_input_annexes_hash env () == input_annexes_hash env () + where + fast_input_annexes_hash = testEval (specification (BitcoinJet (SigHashJet InputAnnexesHash))) + +prop_input_script_sigs_hash :: Property +prop_input_script_sigs_hash = forallPrimEnv $ \env -> fast_input_script_sigs_hash env () == input_script_sigs_hash env () + where + fast_input_script_sigs_hash = testEval (specification (BitcoinJet (SigHashJet InputScriptSigsHash))) + +prop_inputs_hash :: Property +prop_inputs_hash = forallPrimEnv $ \env -> fast_inputs_hash env () == inputs_hash env () + where + fast_inputs_hash = testEval (specification (BitcoinJet (SigHashJet InputsHash))) + +prop_input_hash :: Property +prop_input_hash = forallInPrimEnv $ \env i -> fast_input_hash env (toW32 i) == input_hash env (toW32 i) + where + fast_input_hash = testEval (specification (BitcoinJet (SigHashJet InputHash))) + +prop_tx_hash :: Property +prop_tx_hash = forallPrimEnv $ \env -> fast_tx_hash env () == tx_hash env () + where + fast_tx_hash = testEval (specification (BitcoinJet (SigHashJet TxHash))) + +prop_tapleaf_hash :: Property +prop_tapleaf_hash = forallPrimEnv $ \env -> fast_tapleaf_hash env () == tapleaf_hash env () + where + fast_tapleaf_hash = testEval (specification (BitcoinJet (SigHashJet TapleafHash))) + +prop_tappath_hash :: Property +prop_tappath_hash = forallPrimEnv $ \env -> fast_tappath_hash env () == tappath_hash env () + where + fast_tappath_hash = testEval (specification (BitcoinJet (SigHashJet TappathHash))) + +prop_tap_env_hash :: Property +prop_tap_env_hash = forallPrimEnv $ \env -> fast_tap_env_hash env () == tap_env_hash env () + where + fast_tap_env_hash = testEval (specification (BitcoinJet (SigHashJet TapEnvHash))) + +prop_sig_all_hash :: Property +prop_sig_all_hash = forallPrimEnv $ \env -> fast_sig_all_hash env () == sig_all_hash env () + where + fast_sig_all_hash = testEval (specification (BitcoinJet (SigHashJet SigAllHash))) + +prop_script_cmr :: Property +prop_script_cmr = forallPrimEnv $ \env -> fast_script_cmr env () == script_cmr env () + where + fast_script_cmr = testEval (specification (BitcoinJet (TransactionJet ScriptCMR))) + +prop_internal_key :: Property +prop_internal_key = forallPrimEnv $ \env -> fast_internal_key env () == internal_key env () + where + fast_internal_key = testEval (specification (BitcoinJet (TransactionJet InternalKey))) + +prop_current_index :: Property +prop_current_index = forallPrimEnv $ \env -> fast_current_index env () == current_index env () + where + fast_current_index = testEval (specification (BitcoinJet (TransactionJet CurrentIndex))) + +prop_num_inputs :: Property +prop_num_inputs = forallPrimEnv $ \env -> fast_num_inputs env () == num_inputs env () + where + fast_num_inputs = testEval (specification (BitcoinJet (TransactionJet NumInputs))) + +prop_num_outputs :: Property +prop_num_outputs = forallPrimEnv $ \env -> fast_num_outputs env () == num_outputs env () + where + fast_num_outputs = testEval (specification (BitcoinJet (TransactionJet NumOutputs))) + +prop_lock_time :: Property +prop_lock_time = forallPrimEnv $ \env -> fast_lock_time env () == lock_time env () + where + fast_lock_time = testEval (specification (BitcoinJet (TransactionJet LockTime))) + +prop_output_value :: Property +prop_output_value = forallOutPrimEnv $ \env i -> fast_output_value env (toW32 i) == output_value env (toW32 i) + where + fast_output_value = testEval (specification (BitcoinJet (TransactionJet OutputValue))) + +prop_output_script_hash :: Property +prop_output_script_hash = forallOutPrimEnv $ \env i -> fast_output_script_hash env (toW32 i) == output_script_hash env (toW32 i) + where + fast_output_script_hash = testEval (specification (BitcoinJet (TransactionJet OutputScriptHash))) + +prop_total_output_value :: Property +prop_total_output_value = forallPrimEnv $ \env -> fast_total_output_value env () == total_output_value env () + where + fast_total_output_value = testEval (specification (BitcoinJet (TransactionJet TotalOutputValue))) + +prop_current_prev_outpoint :: Property +prop_current_prev_outpoint = forallPrimEnv $ \env -> fast_current_prev_outpoint env () == current_prev_outpoint env () + where + fast_current_prev_outpoint = testEval (specification (BitcoinJet (TransactionJet CurrentPrevOutpoint))) + +prop_current_value :: Property +prop_current_value = forallPrimEnv $ \env -> fast_current_value env () == current_value env () + where + fast_current_value = testEval (specification (BitcoinJet (TransactionJet CurrentValue))) + +prop_current_script_hash :: Property +prop_current_script_hash = forallPrimEnv $ \env -> fast_current_script_hash env () == current_script_hash env () + where + fast_current_script_hash = testEval (specification (BitcoinJet (TransactionJet CurrentScriptHash))) + +prop_current_sequence :: Property +prop_current_sequence = forallPrimEnv $ \env -> fast_current_sequence env () == current_sequence env () + where + fast_current_sequence = testEval (specification (BitcoinJet (TransactionJet CurrentSequence))) + +prop_current_annex_hash :: Property +prop_current_annex_hash = forallPrimEnv $ \env -> fast_current_annex_hash env () == current_annex_hash env () + where + fast_current_annex_hash = testEval (specification (BitcoinJet (TransactionJet CurrentAnnexHash))) + +prop_current_script_sig_hash :: Property +prop_current_script_sig_hash = forallPrimEnv $ \env -> fast_current_script_sig_hash env () == current_script_sig_hash env () + where + fast_current_script_sig_hash = testEval (specification (BitcoinJet (TransactionJet CurrentScriptSigHash))) + +prop_input_prev_outpoint :: Property +prop_input_prev_outpoint = forallInPrimEnv $ \env i -> fast_input_prev_outpoint env (toW32 i) == input_prev_outpoint env (toW32 i) + where + fast_input_prev_outpoint = testEval (specification (BitcoinJet (TransactionJet InputPrevOutpoint))) + +prop_input_value :: Property +prop_input_value = forallInPrimEnv $ \env i -> fast_input_value env (toW32 i) == input_value env (toW32 i) + where + fast_input_value = testEval (specification (BitcoinJet (TransactionJet InputValue))) + +prop_input_script_hash :: Property +prop_input_script_hash = forallInPrimEnv $ \env i -> fast_input_script_hash env (toW32 i) == input_script_hash env (toW32 i) + where + fast_input_script_hash = testEval (specification (BitcoinJet (TransactionJet InputScriptHash))) + +prop_input_sequence :: Property +prop_input_sequence = forallInPrimEnv $ \env i -> fast_input_sequence env (toW32 i) == input_sequence env (toW32 i) + where + fast_input_sequence = testEval (specification (BitcoinJet (TransactionJet InputSequence))) + +prop_input_annex_hash :: Property +prop_input_annex_hash = forallInPrimEnv $ \env i -> fast_input_annex_hash env (toW32 i) == input_annex_hash env (toW32 i) + where + fast_input_annex_hash = testEval (specification (BitcoinJet (TransactionJet InputAnnexHash))) + +prop_input_script_sig_hash :: Property +prop_input_script_sig_hash = forallInPrimEnv $ \env i -> fast_input_script_sig_hash env (toW32 i) == input_script_sig_hash env (toW32 i) + where + fast_input_script_sig_hash = testEval (specification (BitcoinJet (TransactionJet InputScriptSigHash))) + +prop_total_input_value :: Property +prop_total_input_value = forallPrimEnv $ \env -> fast_total_input_value env () == total_input_value env () + where + fast_total_input_value = testEval (specification (BitcoinJet (TransactionJet TotalInputValue))) + +prop_fee :: Property +prop_fee = forallPrimEnv $ \env -> fast_fee env () == fee env () + where + fast_fee = testEval (specification (BitcoinJet (TransactionJet Fee))) + +prop_tapleaf_version :: Property +prop_tapleaf_version = forallPrimEnv $ \env -> fast_tapleaf_version env () == tapleaf_version env () + where + fast_tapleaf_version = testEval (specification (BitcoinJet (TransactionJet TapleafVersion))) + +prop_tappath :: Property +prop_tappath = forallPrimEnv $ \env -> forAll (genTappathIx env) $ \i -> fast_tappath env (toW8 i) == tappath env (toW8 i) + where + fast_tappath = testEval (specification (BitcoinJet (TransactionJet Tappath))) + genTappathIx = genBoundaryCases . fromIntegral . length . Prim.tappath . Prim.envTap + +prop_version :: Property +prop_version = forallPrimEnv $ \env -> fast_version env () == version env () + where + fast_version = testEval (specification (BitcoinJet (TransactionJet Version))) + +prop_transaction_id :: Property +prop_transaction_id = forallPrimEnv $ \env -> fast_transaction_id env () == transaction_id env () + where + fast_transaction_id = testEval (specification (BitcoinJet (TransactionJet TransactionId))) diff --git a/Haskell/Tests/Tests.hs b/Haskell/Tests/Tests.hs index 48334a67..5d12ff50 100644 --- a/Haskell/Tests/Tests.hs +++ b/Haskell/Tests/Tests.hs @@ -8,6 +8,7 @@ import qualified Simplicity.BitMachine.StaticAnalysis.Tests as StaticAnalysis import qualified Simplicity.FFI.Tests as FFI import qualified Simplicity.Programs.Tests as Programs import qualified Simplicity.Bitcoin.Tests as Bitcoin +import qualified Simplicity.Bitcoin.FFI.Tests as BitcoinFFI import qualified Simplicity.Bitcoin.Serialization.Tests as BitcoinSerialization import qualified Simplicity.Elements.Tests as Elements import qualified Simplicity.Elements.FFI.Tests as ElementsFFI @@ -25,6 +26,7 @@ tests = testGroup "Tests" , BitMachine.tests , StaticAnalysis.tests , Bitcoin.tests + , BitcoinFFI.tests , BitcoinSerialization.tests , Ty.tests , Elements.tests diff --git a/Haskell/cbits/bitcoin/env.c b/Haskell/cbits/bitcoin/env.c new file mode 100644 index 00000000..42108fa8 --- /dev/null +++ b/Haskell/cbits/bitcoin/env.c @@ -0,0 +1,53 @@ +#include "simplicity_alloc.h" +#include "simplicity/bitcoin/env.h" +#include "bitcoin/txEnv.h" + +const size_t c_sizeof_rawBitcoinBuffer = sizeof(rawBitcoinBuffer); +const size_t c_sizeof_rawBitcoinOutput = sizeof(rawBitcoinOutput); +const size_t c_sizeof_rawBitcoinInput = sizeof(rawBitcoinInput); +const size_t c_sizeof_rawBitcoinTransaction = sizeof(rawBitcoinTransaction); +const size_t c_sizeof_rawBitcoinTapEnv = sizeof(rawBitcoinTapEnv); +const size_t c_bitcoin_sizeof_txEnv = sizeof(txEnv); + +void c_set_rawBitcoinBuffer(rawBitcoinBuffer* result, const char* buf, unsigned int len) { + *result = (rawBitcoinBuffer){ .buf = buf, .len = len }; +} + +void c_set_rawBitcoinOutput(rawBitcoinOutput* result, unsigned long value, const rawBitcoinBuffer* scriptPubKey) { + *result = (rawBitcoinOutput){ .value = value + , .scriptPubKey = *scriptPubKey + }; +} + +void c_set_rawBitcoinInput(rawBitcoinInput* result, const rawBitcoinBuffer* annex, const rawBitcoinBuffer* scriptSig, + const char* prevTxid, unsigned int prevIx, + unsigned long value, const rawBitcoinBuffer* scriptPubKey, + unsigned int sequence) { + *result = (rawBitcoinInput){ .annex = annex + , .scriptSig = *scriptSig + , .prevTxid = prevTxid + , .txo = {.value = value, .scriptPubKey = *scriptPubKey} + , .prevIx = prevIx + , .sequence = sequence + }; +} + +void c_set_rawBitcoinTransaction(rawBitcoinTransaction* result, const unsigned char* txid, unsigned int version, + const rawBitcoinInput* input, unsigned int numInputs, + const rawBitcoinOutput* output, unsigned int numOutputs, + unsigned int lockTime) { + *result = (rawBitcoinTransaction){ .txid = txid + , .version = version + , .input = input, .numInputs = numInputs + , .output = output, .numOutputs = numOutputs + , .lockTime = lockTime, + }; +} + +void c_set_rawBitcoinTapEnv(rawBitcoinTapEnv* result, const char* controlBlock, unsigned char pathLen, const char* scriptCMR) { + *result = (rawBitcoinTapEnv){ .controlBlock = controlBlock, .pathLen = pathLen, .scriptCMR = scriptCMR }; +} + +void c_bitcoin_set_txEnv(txEnv* result, const bitcoinTransaction* tx, const bitcoinTapEnv* taproot, unsigned int ix) { + *result = simplicity_bitcoin_build_txEnv(tx, taproot, ix); +} diff --git a/Haskell/cbits/bitcoin/jets.c b/Haskell/cbits/bitcoin/jets.c new file mode 100644 index 00000000..6e598d85 --- /dev/null +++ b/Haskell/cbits/bitcoin/jets.c @@ -0,0 +1,63 @@ +#include "bitcoin/bitcoinJets.h" +#include "../wrappers.h" + +WRAP_(bitcoin_version) +WRAP_(bitcoin_lock_time) +WRAP_(bitcoin_input_prev_outpoint) +WRAP_(bitcoin_input_value) +WRAP_(bitcoin_input_script_hash) +WRAP_(bitcoin_input_sequence) +WRAP_(bitcoin_input_annex_hash) +WRAP_(bitcoin_input_script_sig_hash) +WRAP_(bitcoin_output_value) +WRAP_(bitcoin_output_script_hash) +WRAP_(bitcoin_fee) +WRAP_(bitcoin_total_input_value) +WRAP_(bitcoin_total_output_value) +WRAP_(bitcoin_script_cmr) +WRAP_(bitcoin_transaction_id) +WRAP_(bitcoin_current_index) +WRAP_(bitcoin_current_prev_outpoint) +WRAP_(bitcoin_current_value) +WRAP_(bitcoin_current_script_hash) +WRAP_(bitcoin_current_sequence) +WRAP_(bitcoin_current_annex_hash) +WRAP_(bitcoin_current_script_sig_hash) +WRAP_(bitcoin_tapleaf_version) +WRAP_(bitcoin_tappath) +WRAP_(bitcoin_internal_key) +WRAP_(bitcoin_num_inputs) +WRAP_(bitcoin_num_outputs) +WRAP_(bitcoin_tx_is_final) +WRAP_(bitcoin_tx_lock_height) +WRAP_(bitcoin_tx_lock_time) +WRAP_(bitcoin_tx_lock_distance) +WRAP_(bitcoin_tx_lock_duration) +WRAP_(bitcoin_check_lock_height) +WRAP_(bitcoin_check_lock_time) +WRAP_(bitcoin_check_lock_distance) +WRAP_(bitcoin_check_lock_duration) +COREWRAP_(bitcoin_outpoint_hash) +COREWRAP_(bitcoin_annex_hash) +COREWRAP_(bitcoin_build_tapleaf_simplicity) +COREWRAP_(bitcoin_build_tapbranch) +COREWRAP_(bitcoin_build_taptweak) +WRAP_(bitcoin_output_values_hash) +WRAP_(bitcoin_output_scripts_hash) +WRAP_(bitcoin_outputs_hash) +WRAP_(bitcoin_output_hash) +WRAP_(bitcoin_input_outpoints_hash) +WRAP_(bitcoin_input_values_hash) +WRAP_(bitcoin_input_scripts_hash) +WRAP_(bitcoin_input_utxos_hash) +WRAP_(bitcoin_input_utxo_hash) +WRAP_(bitcoin_input_sequences_hash) +WRAP_(bitcoin_input_annexes_hash) +WRAP_(bitcoin_input_script_sigs_hash) +WRAP_(bitcoin_inputs_hash) +WRAP_(bitcoin_input_hash) +WRAP_(bitcoin_tx_hash) +WRAP_(bitcoin_tapleaf_hash) +WRAP_(bitcoin_tappath_hash) +WRAP_(bitcoin_tap_env_hash) +WRAP_(bitcoin_sig_all_hash) diff --git a/Simplicity.cabal b/Simplicity.cabal index f1f9cd62..ed8c6494 100644 --- a/Simplicity.cabal +++ b/Simplicity.cabal @@ -126,6 +126,8 @@ library Simplicity-Indef library C-sources: C/rsort.c C/elements/elementsJets.c C/elements/ops.c C/elements/env.c C/elements/txEnv.c + C/bitcoin/bitcoinJets.c C/bitcoin/ops.c C/bitcoin/env.c C/bitcoin/txEnv.c + Haskell/cbits/bitcoin/jets.c Haskell/cbits/bitcoin/env.c Haskell/cbits/elements/jets.c Haskell/cbits/elements/env.c Include-dirs: C C/include Includes: elements/elementsJets.h elements/primitive.h simplicity/elements/env.h @@ -136,6 +138,7 @@ library Simplicity.Elements.Programs.SigHash, Simplicity.Elements.Programs.SigHash.Lib, Simplicity.Elements.Programs.Transaction, Simplicity.Elements.Programs.Transaction.Lib, Simplicity.Bitcoin.Jets, Simplicity.Elements.Jets, + Simplicity.Bitcoin.FFI.Env, Simplicity.Bitcoin.FFI.Jets, Simplicity.Elements.FFI.Env, Simplicity.Elements.FFI.Jets reexported-modules: Simplicity.Ty, Simplicity.Ty.Bit, Simplicity.Ty.Word, Simplicity.Ty.LibSecp256k1, Simplicity.Ty.Sha256, Simplicity.Term.Core, @@ -229,7 +232,7 @@ Test-Suite testsuite Simplicity.Bip0340, Simplicity.Arbitrary, Simplicity.Ty.Arbitrary, Simplicity.Bitcoin.Arbitrary, Simplicity.Elements.Arbitrary, Simplicity.BitMachine.StaticAnalysis.Tests, Simplicity.BitMachine.Tests, - Simplicity.Bitcoin.TestEval, Simplicity.Bitcoin.Tests, + Simplicity.Bitcoin.TestEval, Simplicity.Bitcoin.Tests, Simplicity.Bitcoin.FFI.Tests, Simplicity.Bitcoin.Serialization.Tests, Simplicity.FFI.Bitstream, Simplicity.FFI.Dag, Simplicity.Elements.FFI.Primitive, @@ -240,7 +243,8 @@ Test-Suite testsuite Simplicity.Serialization.Tests, Simplicity.TestCoreEval, Simplicity.Ty.Tests - C-sources: C/rsort.c, C/dag.c, C/elements/primitive.c, C/bitstream.c + C-sources: C/rsort.c, C/dag.c, C/bitstream.c + C/elements/primitive.c Haskell/cbits/bitstream.c, Haskell/cbits/dag.c build-depends: Simplicity, base >=4.9 && <4.20,