From 640e4ae422260d6053a0a41beebee0ba51fbec7e Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Wed, 22 May 2024 10:46:06 +0200 Subject: [PATCH 01/19] wallet: allow mintxfee=0 This check for 0 feerate was inherited from upstream, and subsequently removed in this commit: https://github.com/bitcoin/bitcoin/commit/f11eb1fe279c4a92e1bfc2c139e8838c73459d12 --- src/confidential_validation.cpp | 3 +- src/wallet/wallet.cpp | 3 +- test/functional/test_runner.py | 1 + test/functional/wallet_send_zero_fee.py | 60 +++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100755 test/functional/wallet_send_zero_fee.py diff --git a/src/confidential_validation.cpp b/src/confidential_validation.cpp index efc7b753c41..c101852fbc6 100644 --- a/src/confidential_validation.cpp +++ b/src/confidential_validation.cpp @@ -30,8 +30,9 @@ bool HasValidFee(const CTransaction& tx) { CAmount fee = 0; if (tx.vout[i].IsFee()) { fee = tx.vout[i].nValue.GetAmount(); - if (fee == 0 || !MoneyRange(fee)) + if (!MoneyRange(fee)) { return false; + } totalFee[tx.vout[i].nAsset.GetAsset()] += fee; if (!MoneyRange(totalFee)) { return false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e7444556eae..39e83f5a6b1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3012,7 +3012,8 @@ std::shared_ptr CWallet::Create(WalletContext& context, const std::stri if (args.IsArgSet("-mintxfee")) { std::optional min_tx_fee = ParseMoney(args.GetArg("-mintxfee", "")); - if (!min_tx_fee || min_tx_fee.value() == 0) { + // ELEMENTS: allow mintxfee=0 + if (!min_tx_fee) { error = AmountErrMsg("mintxfee", args.GetArg("-mintxfee", "")); return nullptr; } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) { diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 198a3eaeb1a..6806b33fedc 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -352,6 +352,7 @@ 'feature_coinstatsindex.py --legacy-wallet', 'feature_coinstatsindex.py --descriptors', 'wallet_orphanedreward.py', + 'wallet_send_zero_fee.py', 'wallet_timelock.py', 'p2p_node_network_limited.py', 'p2p_permissions.py', diff --git a/test/functional/wallet_send_zero_fee.py b/test/functional/wallet_send_zero_fee.py new file mode 100755 index 00000000000..49deea6585a --- /dev/null +++ b/test/functional/wallet_send_zero_fee.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +from decimal import Decimal + +from test_framework.blocktools import COINBASE_MATURITY +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, +) + +class WalletTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 3 + self.extra_args = [[ + "-blindedaddresses=1", + "-initialfreecoins=2100000000000000", + "-con_blocksubsidy=0", + "-con_connect_genesis_outputs=1", + "-txindex=1", + "-minrelaytxfee=0", + "-blockmintxfee=0", + "-mintxfee=0", + ]] * self.num_nodes + self.extra_args[0].append("-anyonecanspendaremine=1") + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def run_test(self): + self.generate(self.nodes[0], COINBASE_MATURITY + 1) + self.sync_all() + + self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10) + self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 20) + self.generate(self.nodes[0], 1) + assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 102) + assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('20999969.99900900')}) + assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('10.00000000')}) + assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('20.00000000')}) + + addr = self.nodes[1].getnewaddress() + txid = self.nodes[0].sendtoaddress(addr, 1, None, None, None, None, None, None, None, None, None, 0, False) + tx = self.nodes[0].gettransaction(txid) + assert_equal(tx["fee"]["bitcoin"], 0) + hex = self.nodes[0].getrawtransaction(txid) + self.generate(self.nodes[0], 1) + assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 103) + + decoded = self.nodes[0].decoderawtransaction(hex) + bitcoin = "b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23" + assert_equal(decoded["fee"][bitcoin], 0) + assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('20999968.99900900')}) + assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('11.00000000')}) + assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('20.00000000')}) + +if __name__ == '__main__': + WalletTest().main() From aef3f5a7e581883e18df6793eb6dbc8286aeb8dc Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Tue, 24 Jun 2025 11:37:54 -0400 Subject: [PATCH 02/19] Squashed 'src/simplicity/' changes from b549192109..6d503ea4f8 6d503ea4f8 Rename elements/jets.c c68063f9ef Add testcases that are an even multiple of 1000 milliWU 92887000e6 Add minCost parameter 82d6260ed8 Remove intermedite primitive directory 09b4eee340 Make raw environment struct tags elements specific a93cd359df Make environment struct tags elements specific 26de216e24 Make primitive.h functions indirect 35d188a247 rename elementsJets.* to jets.* 7fd6dbe2ca simplicity_computeCmr -> simplicity_elements_computeCmr git-subtree-dir: src/simplicity git-subtree-split: 6d503ea4f8859ec63ad22a22c0ccef067b1a0b5d --- Makefile | 6 +- deserialize.c | 13 +- deserialize.h | 14 +- elements-sources.mk | 33 ++-- .../checkSigHashAllTx1.c | 0 .../checkSigHashAllTx1.h | 6 +- cmr.c => elements/cmr.c | 17 +- .../decodeElementsJets.inc | 0 .../elements => elements}/elementsJets.c | 14 +- .../elements => elements}/elementsJets.h | 6 +- {primitive/elements => elements}/env.c | 123 ++++++-------- {primitive/elements => elements}/exec.c | 39 +++-- {primitive/elements => elements}/ops.c | 0 {primitive/elements => elements}/ops.h | 8 +- {primitive/elements => elements}/primitive.c | 17 +- primitive.h => elements/primitive.h | 14 +- .../primitiveEnumJet.inc | 0 .../elements => elements}/primitiveEnumTy.inc | 0 .../elements => elements}/primitiveInitTy.inc | 0 .../primitiveJetNode.inc | 0 elements/txEnv.c | 26 +++ .../elements/primitive.h => elements/txEnv.h | 20 +-- eval.c | 29 ++-- eval.h | 34 ++-- include/simplicity/{ => elements}/cmr.h | 8 +- include/simplicity/elements/env.h | 72 ++++----- include/simplicity/elements/exec.h | 12 +- include/simplicity/errorCodes.h | 3 + test.c | 152 ++++++++++++++---- typeInference.c | 5 +- typeInference.h | 22 ++- 31 files changed, 430 insertions(+), 263 deletions(-) rename {primitive/elements => elements}/checkSigHashAllTx1.c (100%) rename {primitive/elements => elements}/checkSigHashAllTx1.h (90%) rename cmr.c => elements/cmr.c (73%) rename {primitive/elements => elements}/decodeElementsJets.inc (100%) rename {primitive/elements => elements}/elementsJets.c (99%) rename {primitive/elements => elements}/elementsJets.h (98%) rename {primitive/elements => elements}/env.c (88%) rename {primitive/elements => elements}/exec.c (74%) rename {primitive/elements => elements}/ops.c (100%) rename {primitive/elements => elements}/ops.h (95%) rename {primitive/elements => elements}/primitive.c (89%) rename primitive.h => elements/primitive.h (78%) rename {primitive/elements => elements}/primitiveEnumJet.inc (100%) rename {primitive/elements => elements}/primitiveEnumTy.inc (100%) rename {primitive/elements => elements}/primitiveInitTy.inc (100%) rename {primitive/elements => elements}/primitiveJetNode.inc (100%) create mode 100644 elements/txEnv.c rename primitive/elements/primitive.h => elements/txEnv.h (95%) rename include/simplicity/{ => elements}/cmr.h (74%) diff --git a/Makefile b/Makefile index 06506b819c5..a0cb534209a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -OBJS := bitstream.o cmr.o dag.o deserialize.o eval.o frame.o jets.o jets-secp256k1.o rsort.o sha256.o type.o typeInference.o primitive/elements/env.o primitive/elements/exec.o primitive/elements/ops.o primitive/elements/elementsJets.o primitive/elements/primitive.o -TEST_OBJS := test.o ctx8Pruned.o ctx8Unpruned.o hashBlock.o regression4.o schnorr0.o schnorr6.o typeSkipTest.o primitive/elements/checkSigHashAllTx1.o +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 +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 CWARN := -Werror -Wall -Wextra -Wcast-qual -Wcast-align -Wstrict-aliasing -Wpointer-arith -Winit-self -Wshadow -Wswitch-enum -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wfloat-equal -Wundef -Wconversion @@ -14,7 +14,7 @@ CFLAGS := $(CFLAGS) -I include jets-secp256k1.o: jets-secp256k1.c $(CC) -c $(CFLAGS) $(CWARN) -Wno-conversion $(CPPFLAGS) -o $@ $< -primitive/elements/elementsJets.o: primitive/elements/elementsJets.c +elements/elementsJets.o: elements/elementsJets.c $(CC) -c $(CFLAGS) $(CWARN) -Wno-switch-enum -Wswitch $(CPPFLAGS) -o $@ $< sha256.o: sha256.c diff --git a/deserialize.c b/deserialize.c index d65c0d5c5cf..182f23ba16e 100644 --- a/deserialize.c +++ b/deserialize.c @@ -2,7 +2,6 @@ #include #include "limitations.h" -#include "primitive.h" #include "simplicity_alloc.h" #include "simplicity_assert.h" @@ -55,7 +54,7 @@ static simplicity_err getHash(sha256_midstate* result, bitstream* stream) { * i < 2^31 - 1 * NULL != stream */ -static simplicity_err decodeNode(dag_node* dag, uint_fast32_t i, bitstream* stream) { +static simplicity_err decodeNode(dag_node* dag, simplicity_callback_decodeJet decodeJet, uint_fast32_t i, bitstream* stream) { int32_t bit = read1Bit(stream); if (bit < 0) return (simplicity_err)bit; dag[i] = (dag_node){0}; @@ -63,7 +62,7 @@ static simplicity_err decodeNode(dag_node* dag, uint_fast32_t i, bitstream* stre bit = read1Bit(stream); if (bit < 0) return (simplicity_err)bit; if (bit) { - return simplicity_decodeJet(&dag[i], stream); + return decodeJet(&dag[i], stream); } else { /* Decode WORD. */ int32_t depth = simplicity_decodeUptoMaxInt(stream); @@ -153,9 +152,9 @@ static simplicity_err decodeNode(dag_node* dag, uint_fast32_t i, bitstream* stre * len < 2^31 * NULL != stream */ -static simplicity_err decodeDag(dag_node* dag, const uint_fast32_t len, combinator_counters* census, bitstream* stream) { +static simplicity_err decodeDag(dag_node* dag, simplicity_callback_decodeJet decodeJet, const uint_fast32_t len, combinator_counters* census, bitstream* stream) { for (uint_fast32_t i = 0; i < len; ++i) { - simplicity_err error = decodeNode(dag, i, stream); + simplicity_err error = decodeNode(dag, decodeJet, i, stream); if (!IS_OK(error)) return error; enumerator(census, dag[i].tag); @@ -186,7 +185,7 @@ static simplicity_err decodeDag(dag_node* dag, const uint_fast32_t len, combinat * of the function is positive and when NULL != census; * NULL == *dag when the return value is negative. */ -int_fast32_t simplicity_decodeMallocDag(dag_node** dag, combinator_counters* census, bitstream* stream) { +int_fast32_t simplicity_decodeMallocDag(dag_node** dag, simplicity_callback_decodeJet decodeJet, combinator_counters* census, bitstream* stream) { *dag = NULL; int32_t dagLen = simplicity_decodeUptoMaxInt(stream); if (dagLen <= 0) return dagLen; @@ -199,7 +198,7 @@ int_fast32_t simplicity_decodeMallocDag(dag_node** dag, combinator_counters* cen if (!*dag) return SIMPLICITY_ERR_MALLOC; if (census) *census = (combinator_counters){0}; - simplicity_err error = decodeDag(*dag, (uint_fast32_t)dagLen, census, stream); + simplicity_err error = decodeDag(*dag, decodeJet, (uint_fast32_t)dagLen, census, stream); if (IS_OK(error)) { error = HIDDEN == (*dag)[dagLen - 1].tag diff --git a/deserialize.h b/deserialize.h index d20561b011c..dd5515d3c8d 100644 --- a/deserialize.h +++ b/deserialize.h @@ -6,6 +6,18 @@ #include "bitstream.h" #include "dag.h" +/* Decode an application specific jet from 'stream' into 'node'. + * All jets begin with a bit prefix of '1' which needs to have already been consumed from the 'stream'. + * Returns 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' if the stream's prefix doesn't match any valid code for a jet. + * Returns 'SIMPLICITY_ERR_BITSTRING_EOF' if not enough bits are available in the 'stream'. + * In the above error cases, 'dag' may be modified. + * Returns 'SIMPLICITY_NO_ERROR' if successful. + * + * Precondition: NULL != node + * NULL != stream + */ +typedef simplicity_err (*simplicity_callback_decodeJet)(dag_node* node, bitstream* stream); + /* Decode a length-prefixed Simplicity DAG from 'stream'. * Returns 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' the length prefix's value is too large. * Returns 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' if some node's child isn't a reference to one of the preceding nodes. @@ -28,6 +40,6 @@ * of the function is positive and when NULL != census; * NULL == *dag when the return value is negative. */ -int_fast32_t simplicity_decodeMallocDag(dag_node** dag, combinator_counters* census, bitstream* stream); +int_fast32_t simplicity_decodeMallocDag(dag_node** dag, simplicity_callback_decodeJet decodeJet, combinator_counters* census, bitstream* stream); #endif diff --git a/elements-sources.mk b/elements-sources.mk index 189a04983af..1c0dc4b9a9b 100644 --- a/elements-sources.mk +++ b/elements-sources.mk @@ -5,14 +5,13 @@ ELEMENTS_SIMPLICITY_INCLUDE_DIR_INT = %reldir%/include ELEMENTS_SIMPLICITY_DIST_HEADERS_INT = -ELEMENTS_SIMPLICITY_DIST_HEADERS_INT += %reldir%/include/simplicity/cmr.h ELEMENTS_SIMPLICITY_DIST_HEADERS_INT += %reldir%/include/simplicity/errorCodes.h +ELEMENTS_SIMPLICITY_DIST_HEADERS_INT += %reldir%/include/simplicity/elements/cmr.h ELEMENTS_SIMPLICITY_DIST_HEADERS_INT += %reldir%/include/simplicity/elements/env.h ELEMENTS_SIMPLICITY_DIST_HEADERS_INT += %reldir%/include/simplicity/elements/exec.h ELEMENTS_SIMPLICITY_LIB_SOURCES_INT = ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/bitstream.c -ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/cmr.c ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/dag.c ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/deserialize.c ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/eval.c @@ -24,11 +23,13 @@ ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/sha256.c ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/type.c ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/typeInference.c -ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/primitive/elements/env.c -ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/primitive/elements/exec.c -ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/primitive/elements/elementsJets.c -ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/primitive/elements/ops.c -ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/primitive/elements/primitive.c +ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/elements/cmr.c +ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/elements/env.c +ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/elements/exec.c +ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/elements/elementsJets.c +ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/elements/ops.c +ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/elements/primitive.c +ELEMENTS_SIMPLICITY_LIB_SOURCES_INT += %reldir%/elements/txEnv.c ELEMENTS_SIMPLICITY_LIB_HEADERS_INT = ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/bitstream.h @@ -42,7 +43,6 @@ ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/frame.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/jets.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/limitations.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/precomputed.h -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/rsort.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/sha256.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/sha256_x86.inc @@ -89,11 +89,12 @@ ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/secp256k1/secp256k1.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/secp256k1/secp256k1_impl.h ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/secp256k1/util.h -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/decodeElementsJets.inc -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/elementsJets.h -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/ops.h -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/primitive.h -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/primitiveEnumJet.inc -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/primitiveEnumTy.inc -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/primitiveInitTy.inc -ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/primitive/elements/primitiveJetNode.inc +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/decodeElementsJets.inc +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/elementsJets.h +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/ops.h +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/primitive.h +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/primitiveEnumJet.inc +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/primitiveEnumTy.inc +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/primitiveInitTy.inc +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/primitiveJetNode.inc +ELEMENTS_SIMPLICITY_LIB_HEADERS_INT += %reldir%/elements/txEnv.h diff --git a/primitive/elements/checkSigHashAllTx1.c b/elements/checkSigHashAllTx1.c similarity index 100% rename from primitive/elements/checkSigHashAllTx1.c rename to elements/checkSigHashAllTx1.c diff --git a/primitive/elements/checkSigHashAllTx1.h b/elements/checkSigHashAllTx1.h similarity index 90% rename from primitive/elements/checkSigHashAllTx1.h rename to elements/checkSigHashAllTx1.h index b2bb7c5d9d0..c7a4582e095 100644 --- a/primitive/elements/checkSigHashAllTx1.h +++ b/elements/checkSigHashAllTx1.h @@ -1,9 +1,9 @@ -#ifndef SIMPLICITY_PRIMITIVE_ELEMENTS_CHECKSIGHASHALLTX1_H -#define SIMPLICITY_PRIMITIVE_ELEMENTS_CHECKSIGHASHALLTX1_H +#ifndef SIMPLICITY_ELEMENTS_CHECKSIGHASHALLTX1_H +#define SIMPLICITY_ELEMENTS_CHECKSIGHASHALLTX1_H #include #include -#include "../../bounded.h" +#include "../bounded.h" /* A length-prefixed encoding of the following Simplicity program: * Simplicity.Programs.CheckSig.Lib.checkSigVerify' Simplicity.Elements.Programs.SigHash.Lib.sigAllHash diff --git a/cmr.c b/elements/cmr.c similarity index 73% rename from cmr.c rename to elements/cmr.c index b4a94fc7de6..dd73be4ed93 100644 --- a/cmr.c +++ b/elements/cmr.c @@ -1,9 +1,10 @@ -#include +#include -#include "deserialize.h" -#include "limitations.h" -#include "simplicity_alloc.h" -#include "simplicity_assert.h" +#include "../deserialize.h" +#include "../limitations.h" +#include "../simplicity_alloc.h" +#include "../simplicity_assert.h" +#include "primitive.h" /* Deserialize a Simplicity 'program' and compute its CMR. * @@ -18,15 +19,15 @@ * unsigned char cmr[32] * unsigned char program[program_len] */ -bool simplicity_computeCmr( simplicity_err* error, unsigned char* cmr - , const unsigned char* program, size_t program_len) { +bool simplicity_elements_computeCmr( simplicity_err* error, unsigned char* cmr + , const unsigned char* program, size_t program_len) { simplicity_assert(NULL != error); simplicity_assert(NULL != cmr); simplicity_assert(NULL != program || 0 == program_len); bitstream stream = initializeBitstream(program, program_len); dag_node* dag = NULL; - int_fast32_t dag_len = simplicity_decodeMallocDag(&dag, NULL, &stream); + int_fast32_t dag_len = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, NULL, &stream); if (dag_len <= 0) { simplicity_assert(dag_len < 0); *error = (simplicity_err)dag_len; diff --git a/primitive/elements/decodeElementsJets.inc b/elements/decodeElementsJets.inc similarity index 100% rename from primitive/elements/decodeElementsJets.inc rename to elements/decodeElementsJets.inc diff --git a/primitive/elements/elementsJets.c b/elements/elementsJets.c similarity index 99% rename from primitive/elements/elementsJets.c rename to elements/elementsJets.c index e958f2fffec..dfdf5be6342 100644 --- a/primitive/elements/elementsJets.c +++ b/elements/elementsJets.c @@ -1,9 +1,9 @@ #include "elementsJets.h" #include "ops.h" -#include "primitive.h" -#include "../../taptweak.h" -#include "../../simplicity_assert.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. * @@ -145,19 +145,19 @@ static void issuanceTokenAmt(frameItem* dst, const assetIssuance* issuance) { } } -static uint_fast32_t lockHeight(const transaction* tx) { +static uint_fast32_t lockHeight(const elementsTransaction* tx) { return !tx->isFinal && tx->lockTime < 500000000U ? tx->lockTime : 0; } -static uint_fast32_t lockTime(const transaction* tx) { +static uint_fast32_t lockTime(const elementsTransaction* tx) { return !tx->isFinal && 500000000U <= tx->lockTime ? tx->lockTime : 0; } -static uint_fast16_t lockDistance(const transaction* tx) { +static uint_fast16_t lockDistance(const elementsTransaction* tx) { return 2 <= tx->version ? tx->lockDistance : 0; } -static uint_fast16_t lockDuration(const transaction* tx) { +static uint_fast16_t lockDuration(const elementsTransaction* tx) { return 2 <= tx->version ? tx->lockDuration : 0; } diff --git a/primitive/elements/elementsJets.h b/elements/elementsJets.h similarity index 98% rename from primitive/elements/elementsJets.h rename to elements/elementsJets.h index 2043fc023bb..d6412195c6c 100644 --- a/primitive/elements/elementsJets.h +++ b/elements/elementsJets.h @@ -1,9 +1,9 @@ /* This module defines primitives and jets that are specific to the Elements application for Simplicity. */ -#ifndef SIMPLICITY_PRIMITIVE_ELEMENTS_JETS_H -#define SIMPLICITY_PRIMITIVE_ELEMENTS_JETS_H +#ifndef SIMPLICITY_ELEMENTS_ELEMENTSJETS_H +#define SIMPLICITY_ELEMENTS_ELEMENTSJETS_H -#include "../../jets.h" +#include "../jets.h" /* Jets for the Elements application of Simplicity. */ bool simplicity_version(frameItem* dst, frameItem src, const txEnv* env); diff --git a/primitive/elements/env.c b/elements/env.c similarity index 88% rename from primitive/elements/env.c rename to elements/env.c index a849dc45fd4..af39d5223d9 100644 --- a/primitive/elements/env.c +++ b/elements/env.c @@ -3,12 +3,12 @@ #include #include #include -#include "primitive.h" +#include "txEnv.h" #include "ops.h" -#include "../../rsort.h" -#include "../../sha256.h" -#include "../../simplicity_assert.h" -#include "../../simplicity_alloc.h" +#include "../rsort.h" +#include "../sha256.h" +#include "../simplicity_assert.h" +#include "../simplicity_alloc.h" #define PADDING(alignType, allocated) ((alignof(alignType) - (allocated) % alignof(alignType)) % alignof(alignType)) @@ -17,13 +17,13 @@ * Precondition: NULL != result; * NULL != scriptPubKey; */ -static void hashBuffer(sha256_midstate* result, const rawBuffer* buffer) { +static void hashBuffer(sha256_midstate* result, const rawElementsBuffer* buffer) { sha256_context ctx = sha256_init(result->s); sha256_uchars(&ctx, buffer->buf, buffer->len); sha256_finalize(&ctx); } -/* Initialize a 'confidential' asset or 'confidential' nonce from an unsigned char array from a 'rawTransaction'. +/* Initialize a 'confidential' asset or 'confidential' nonce from an unsigned char array from a 'rawElementsTransaction'. * * Precondition: NULL != conf; * unsigned char rawConf[33] or rawConf == NULL; @@ -40,7 +40,7 @@ static void copyRawConfidential(confidential* conf, const unsigned char* rawConf } } -/* Initialize a 'confAmount' from an unsigned char array from a 'rawTransaction'. +/* Initialize a 'confAmount' from an unsigned char array from a 'rawElementsTransaction'. * * Precondition: NULL != amt; * unsigned char rawAmt[rawAmt[0] == 0x01 ? 9 : 33] or rawAmt == NULL @@ -60,12 +60,12 @@ static void copyRawAmt(confAmount* amt, const unsigned char* rawAmt) { } } -/* Initialize a 'sigInput' from a 'rawInput', copying or hashing the data as needed. +/* Initialize a 'sigInput' from a 'rawElementsInput', copying or hashing the data as needed. * * Precondition: NULL != result; * NULL != input; */ -static void copyInput(sigInput* result, const rawInput* input) { +static void copyInput(sigInput* result, const rawElementsInput* input) { *result = (sigInput){ .prevOutpoint = { .ix = input->prevIx } , .sequence = input->sequence , .isPegin = !!input->pegin @@ -79,8 +79,8 @@ static void copyInput(sigInput* result, const rawInput* input) { copyRawConfidential(&result->txo.asset, input->txo.asset); copyRawAmt(&result->txo.amt, input->txo.value); hashBuffer(&result->scriptSigHash, &input->scriptSig); - hashBuffer(&result->issuance.assetRangeProofHash, &(rawBuffer){0}); - hashBuffer(&result->issuance.tokenRangeProofHash, &(rawBuffer){0}); + hashBuffer(&result->issuance.assetRangeProofHash, &(rawElementsBuffer){0}); + hashBuffer(&result->issuance.tokenRangeProofHash, &(rawElementsBuffer){0}); if (input->issuance.amount || input->issuance.inflationKeys) { sha256_toMidstate(result->issuance.blindingNonce.s, input->issuance.blindingNonce); copyRawAmt(&result->issuance.assetAmt, input->issuance.amount); @@ -104,13 +104,13 @@ static void copyInput(sigInput* result, const rawInput* input) { } /* As specified in https://github.com/ElementsProject/elements/blob/de942511a67c3a3fcbdf002a8ee7e9ba49679b78/src/primitives/transaction.h#L304-L307. */ -static bool isFee(const rawOutput* output) { +static bool isFee(const rawElementsOutput* output) { return 0 == output->scriptPubKey.len && /* Empty scriptPubKey */ NULL != output->asset && 0x01 == output->asset[0] && /* Explicit asset */ NULL != output->value && 0x01 == output->value[0]; /* Explicit amount */ } -static uint_fast32_t countFeeOutputs(const rawTransaction* rawTx) { +static uint_fast32_t countFeeOutputs(const rawElementsTransaction* rawTx) { uint_fast32_t result = 0; for (uint_fast32_t i = 0; i < rawTx->numOutputs; ++i) { result += isFee(&rawTx->output[i]); @@ -126,7 +126,7 @@ static uint_fast32_t countFeeOutputs(const rawTransaction* rawTx) { * * Precondition: NULL != scriptPubKey */ -static uint_fast32_t countNullDataCodes(const rawBuffer* scriptPubKey) { +static uint_fast32_t countNullDataCodes(const rawElementsBuffer* scriptPubKey) { if (0 == scriptPubKey->len || 0x6a != scriptPubKey->buf[0] ) return 0; uint_fast32_t result = 0; @@ -161,7 +161,7 @@ static uint_fast32_t countNullDataCodes(const rawBuffer* scriptPubKey) { * * Precondition: NULL != rawTx */ -static uint_fast64_t countTotalNullDataCodes(const rawTransaction* rawTx) { +static uint_fast64_t countTotalNullDataCodes(const rawElementsTransaction* rawTx) { uint_fast64_t result = 0; for (uint_fast32_t i = 0; i < rawTx->numOutputs; ++i) { result += countNullDataCodes(&rawTx->output[i].scriptPubKey); @@ -186,7 +186,7 @@ static uint_fast64_t countTotalNullDataCodes(const rawTransaction* rawTx) { * NULL != scriptPubKey; * countNullDataCodes(scriptPubKey) <= *allocationLen */ -static void parseNullData(parsedNullData* result, opcode** allocation, size_t* allocationLen, const rawBuffer* scriptPubKey) { +static void parseNullData(parsedNullData* result, opcode** allocation, size_t* allocationLen, const rawElementsBuffer* scriptPubKey) { *result = (parsedNullData){ .op = *allocation }; if (0 == scriptPubKey->len || 0x6a != scriptPubKey->buf[0] ) { result->op = NULL; return; } @@ -233,7 +233,7 @@ static void parseNullData(parsedNullData* result, opcode** allocation, size_t* a *allocationLen -= result->len; } -/* Initialize a 'sigOutput' from a 'rawOutput', copying or hashing the data as needed. +/* Initialize a 'sigOutput' from a 'rawElementsOutput', copying or hashing the data as needed. * * '*allocation' is incremented by 'countNullDataCodes(&output->scriptPubKey)' * '*allocationLen' is decremented by 'countNullDataCodes(&output->scriptPubKey)'. @@ -245,7 +245,7 @@ static void parseNullData(parsedNullData* result, opcode** allocation, size_t* a * NULL != output; * countNullDataCodes(&output->scriptPubKey) <= *allocationLen */ -static void copyOutput(sigOutput* result, opcode** allocation, size_t* allocationLen, const rawOutput* output) { +static void copyOutput(sigOutput* result, opcode** allocation, size_t* allocationLen, const rawElementsOutput* output) { hashBuffer(&result->scriptPubKey, &output->scriptPubKey); result->emptyScript = 0 == output->scriptPubKey.len; copyRawConfidential(&result->asset, output->asset); @@ -253,8 +253,8 @@ static void copyOutput(sigOutput* result, opcode** allocation, size_t* allocatio copyRawConfidential(&result->nonce, output->nonce); parseNullData(&result->pnd, allocation, allocationLen, &output->scriptPubKey); result->isNullData = NULL != result->pnd.op; - hashBuffer(&result->surjectionProofHash, is_confidential(result->asset.prefix) ? &output->surjectionProof : &(rawBuffer){0}); - hashBuffer(&result->rangeProofHash, is_confidential(result->amt.prefix) ? &output->rangeProof : &(rawBuffer){0}); + hashBuffer(&result->surjectionProofHash, is_confidential(result->asset.prefix) ? &output->surjectionProof : &(rawElementsBuffer){0}); + hashBuffer(&result->rangeProofHash, is_confidential(result->amt.prefix) ? &output->rangeProof : &(rawElementsBuffer){0}); result->assetFee = 0; } @@ -306,15 +306,15 @@ static uint_fast32_t sumFees(sigOutput** feeOutputs, uint_fast32_t numFees) { return result + 1; } -/* Allocate and initialize a 'transaction' from a 'rawTransaction', copying or hashing the data as needed. +/* Allocate and initialize a 'elementsTransaction' from a 'rawElementsTransaction', 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 transaction* simplicity_elements_mallocTransaction(const rawTransaction* rawTx) { +extern elementsTransaction* simplicity_elements_mallocTransaction(const rawElementsTransaction* rawTx) { if (!rawTx) return NULL; - size_t allocationSize = sizeof(transaction); + size_t allocationSize = sizeof(elementsTransaction); const size_t pad1 = PADDING(sigInput, allocationSize); if (SIZE_MAX - allocationSize < pad1) return NULL; @@ -360,8 +360,8 @@ extern transaction* simplicity_elements_mallocTransaction(const rawTransaction* /* Casting through void* to avoid warning about pointer alignment. * Our padding is done carefully to ensure alignment. */ - transaction* const tx = (transaction*)(void*)allocation; - allocation += sizeof(transaction) + pad1; + elementsTransaction* const tx = (elementsTransaction*)(void*)allocation; + allocation += sizeof(elementsTransaction) + pad1; sigInput* const input = (sigInput*)(void*)allocation; allocation += rawTx->numInputs * sizeof(sigInput) + pad2; @@ -379,15 +379,15 @@ extern transaction* simplicity_elements_mallocTransaction(const rawTransaction* but C forgoes the complicated specification of C++. Therefore we must make an explicit cast of feeOutputs in C. See for details. */ - *tx = (transaction){ .input = input - , .output = output - , .feeOutputs = (sigOutput const * const *)feeOutputs - , .numInputs = rawTx->numInputs - , .numOutputs = rawTx->numOutputs - , .version = rawTx->version - , .lockTime = rawTx->lockTime - , .isFinal = true - }; + *tx = (elementsTransaction){ .input = input + , .output = output + , .feeOutputs = (sigOutput const * const *)feeOutputs + , .numInputs = rawTx->numInputs + , .numOutputs = rawTx->numOutputs + , .version = rawTx->version + , .lockTime = rawTx->lockTime + , .isFinal = true + }; sha256_toMidstate(tx->txid.s, rawTx->txid); { @@ -569,22 +569,22 @@ extern transaction* simplicity_elements_mallocTransaction(const rawTransaction* return tx; } -/* Free a pointer to 'transaction'. +/* Free a pointer to 'elementsTransaction'. */ -extern void simplicity_elements_freeTransaction(transaction* tx) { +extern void simplicity_elements_freeTransaction(elementsTransaction* tx) { simplicity_free(tx); } -/* Allocate and initialize a 'tapEnv' from a 'rawTapEnv', copying or hashing the data as needed. +/* Allocate and initialize a 'elementsTapEnv' from a 'rawElementsTapEnv', 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 tapEnv* simplicity_elements_mallocTapEnv(const rawTapEnv* rawEnv) { +extern elementsTapEnv* simplicity_elements_mallocTapEnv(const rawElementsTapEnv* rawEnv) { if (!rawEnv) return NULL; if (128 < rawEnv->pathLen) return NULL; - size_t allocationSize = sizeof(tapEnv); + size_t allocationSize = sizeof(elementsTapEnv); const size_t numMidstate = rawEnv->pathLen; const size_t pad1 = PADDING(sha256_midstate, allocationSize); @@ -604,25 +604,25 @@ extern tapEnv* simplicity_elements_mallocTapEnv(const rawTapEnv* rawEnv) { /* Casting through void* to avoid warning about pointer alignment. * Our padding is done carefully to ensure alignment. */ - tapEnv* const env = (tapEnv*)(void*)allocation; + elementsTapEnv* const env = (elementsTapEnv*)(void*)allocation; sha256_midstate* path = NULL; sha256_midstate internalKey; sha256_toMidstate(internalKey.s, &rawEnv->controlBlock[1]); if (numMidstate) { - allocation += sizeof(tapEnv) + pad1; + allocation += sizeof(elementsTapEnv) + pad1; if (rawEnv->pathLen) { path = (sha256_midstate*)(void*)allocation; } } - *env = (tapEnv){ .leafVersion = rawEnv->controlBlock[0] & 0xfe - , .internalKey = internalKey - , .path = path - , .pathLen = rawEnv->pathLen - }; + *env = (elementsTapEnv){ .leafVersion = rawEnv->controlBlock[0] & 0xfe + , .internalKey = internalKey + , .path = path + , .pathLen = rawEnv->pathLen + }; sha256_toMidstate(env->scriptCMR.s, rawEnv->scriptCMR); { @@ -646,33 +646,8 @@ extern tapEnv* simplicity_elements_mallocTapEnv(const rawTapEnv* rawEnv) { return env; } -/* Free a pointer to 'tapEnv'. +/* Free a pointer to 'elementsTapEnv'. */ -extern void simplicity_elements_freeTapEnv(tapEnv* env) { +extern void simplicity_elements_freeTapEnv(elementsTapEnv* env) { simplicity_free(env); } - -/* Construct a txEnv structure from its components. - * This function will precompute any cached values. - * - * Precondition: NULL != tx - * NULL != taproot - * NULL != genesisHash - * ix < tx->numInputs - */ -txEnv simplicity_build_txEnv(const transaction* tx, const tapEnv* taproot, const sha256_midstate* genesisHash, uint_fast32_t ix) { - txEnv result = { .tx = tx - , .taproot = taproot - , .genesisHash = *genesisHash - , .ix = ix - }; - sha256_context ctx = sha256_init(result.sigAllHash.s); - sha256_hash(&ctx, genesisHash); - sha256_hash(&ctx, genesisHash); - sha256_hash(&ctx, &tx->txHash); - sha256_hash(&ctx, &taproot->tapEnvHash); - sha256_u32be(&ctx, ix); - sha256_finalize(&ctx); - - return result; -} diff --git a/primitive/elements/exec.c b/elements/exec.c similarity index 74% rename from primitive/elements/exec.c rename to elements/exec.c index e38c9ca41a5..4dfcf421056 100644 --- a/primitive/elements/exec.c +++ b/elements/exec.c @@ -3,12 +3,13 @@ #include #include #include "primitive.h" -#include "../../deserialize.h" -#include "../../eval.h" -#include "../../limitations.h" -#include "../../simplicity_alloc.h" -#include "../../simplicity_assert.h" -#include "../../typeInference.h" +#include "txEnv.h" +#include "../deserialize.h" +#include "../eval.h" +#include "../limitations.h" +#include "../simplicity_alloc.h" +#include "../simplicity_assert.h" +#include "../typeInference.h" /* Deserialize a Simplicity 'program' with its 'witness' data and execute it in the environment of the 'ix'th input of 'tx' with `taproot`. * @@ -17,6 +18,12 @@ * Otherwise, 'true' is returned indicating that the result was successfully computed and returned in the '*error' value. * * If deserialization, analysis, or execution fails, then '*error' is set to some simplicity_err. + * In particular, if the cost analysis exceeds the budget, or exceeds BUDGET_MAX, then '*error' is set to 'SIMPLICITY_ERR_EXEC_BUDGET'. + * On the other hand, if the cost analysis is less than or equal to minCost, then '*error' is set to 'SIMPLICITY_ERR_OVERWEIGHT'. + * + * Note that minCost and budget parameters are in WU, while the cost analysis will be performed in milliWU. + * Thus the minCost and budget specify a half open interval (minCost, budget] of acceptable cost values in milliWU. + * Setting minCost to 0 effectively disables the minCost check as every Simplicity program has a non-zero cost analysis. * * If 'amr != NULL' and the annotated Merkle root of the decoded expression doesn't match 'amr' then '*error' is set to 'SIMPLICITY_ERR_AMR'. * @@ -30,15 +37,15 @@ * NULL != tx; * NULL != taproot; * unsigned char genesisBlockHash[32] - * 0 <= budget; + * 0 <= minCost <= budget; * NULL != amr implies unsigned char amr[32] * unsigned char program[program_len] * unsigned char witness[witness_len] */ extern bool simplicity_elements_execSimplicity( simplicity_err* error, unsigned char* ihr - , const transaction* tx, uint_fast32_t ix, const tapEnv* taproot + , const elementsTransaction* tx, uint_fast32_t ix, const elementsTapEnv* taproot , const unsigned char* genesisBlockHash - , int64_t budget + , int64_t minCost, int64_t budget , const unsigned char* amr , const unsigned char* program, size_t program_len , const unsigned char* witness, size_t witness_len) { @@ -46,7 +53,8 @@ extern bool simplicity_elements_execSimplicity( simplicity_err* error, unsigned simplicity_assert(NULL != tx); simplicity_assert(NULL != taproot); simplicity_assert(NULL != genesisBlockHash); - simplicity_assert(0 <= budget); + simplicity_assert(0 <= minCost); + simplicity_assert(minCost <= budget); simplicity_assert(NULL != program || 0 == program_len); simplicity_assert(NULL != witness || 0 == witness_len); @@ -60,7 +68,7 @@ extern bool simplicity_elements_execSimplicity( simplicity_err* error, unsigned { bitstream stream = initializeBitstream(program, program_len); - dag_len = simplicity_decodeMallocDag(&dag, &census, &stream); + dag_len = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, &census, &stream); if (dag_len <= 0) { simplicity_assert(dag_len < 0); *error = (simplicity_err)dag_len; @@ -79,7 +87,7 @@ extern bool simplicity_elements_execSimplicity( simplicity_err* error, unsigned if (IS_OK(*error)) { type* type_dag = NULL; - *error = simplicity_mallocTypeInference(&type_dag, dag, (uint_fast32_t)dag_len, &census); + *error = simplicity_mallocTypeInference(&type_dag, simplicity_elements_mallocBoundVars, dag, (uint_fast32_t)dag_len, &census); if (IS_OK(*error)) { simplicity_assert(NULL != type_dag); if (0 != dag[dag_len-1].sourceType || 0 != dag[dag_len-1].targetType) { @@ -117,9 +125,12 @@ extern bool simplicity_elements_execSimplicity( simplicity_err* error, unsigned simplicity_free(analysis); } if (IS_OK(*error)) { - txEnv env = simplicity_build_txEnv(tx, taproot, &genesis_hash, ix); + txEnv env = simplicity_elements_build_txEnv(tx, taproot, &genesis_hash, ix); static_assert(BUDGET_MAX <= UBOUNDED_MAX, "BUDGET_MAX doesn't fit in ubounded."); - *error = evalTCOProgram(dag, type_dag, (size_t)dag_len, &(ubounded){budget <= BUDGET_MAX ? (ubounded)budget : BUDGET_MAX}, &env); + *error = evalTCOProgram( dag, type_dag, (size_t)dag_len + , minCost <= BUDGET_MAX ? (ubounded)minCost : BUDGET_MAX + , &(ubounded){budget <= BUDGET_MAX ? (ubounded)budget : BUDGET_MAX} + , &env); } simplicity_free(type_dag); } diff --git a/primitive/elements/ops.c b/elements/ops.c similarity index 100% rename from primitive/elements/ops.c rename to elements/ops.c diff --git a/primitive/elements/ops.h b/elements/ops.h similarity index 95% rename from primitive/elements/ops.h rename to elements/ops.h index 87e9fb3a32e..d704d64e0a7 100644 --- a/primitive/elements/ops.h +++ b/elements/ops.h @@ -1,10 +1,10 @@ /* This module defines operations used in the construction the environment ('txEnv') and some jets. */ -#ifndef SIMPLICITY_PRIMITIVE_ELEMENTS_OPS_H -#define SIMPLICITY_PRIMITIVE_ELEMENTS_OPS_H +#ifndef SIMPLICITY_ELEMENTS_OPS_H +#define SIMPLICITY_ELEMENTS_OPS_H -#include "../../sha256.h" -#include "primitive.h" +#include "../sha256.h" +#include "txEnv.h" /* Add an 'confidential' value to be consumed by an ongoing SHA-256 evaluation. * If the 'confidential' value is blinded, then the 'evenPrefix' used if the y coordinate is even, diff --git a/primitive/elements/primitive.c b/elements/primitive.c similarity index 89% rename from primitive/elements/primitive.c rename to elements/primitive.c index da08b920940..72e41e5b6c6 100644 --- a/primitive/elements/primitive.c +++ b/elements/primitive.c @@ -1,12 +1,9 @@ -/* This module implements the 'primitive.h' interface for the Elements application of Simplicity. - */ #include "primitive.h" #include "elementsJets.h" -#include "../../limitations.h" -#include "../../primitive.h" -#include "../../simplicity_alloc.h" -#include "../../simplicity_assert.h" +#include "../limitations.h" +#include "../simplicity_alloc.h" +#include "../simplicity_assert.h" /* An enumeration of all the types we need to construct to specify the input and output types of all jets created by 'decodeJet'. */ enum TypeNamesForJets { @@ -32,7 +29,7 @@ enum TypeNamesForJets { * '(*bound_var)[i]' is bound to 'A' and '(*bound_var)[j]' is bound to 'B' * and, '*word256_ix < *extra_var_start' and '(*bound_var)[*word256_ix]' is bound the type 'TWO^256' */ -size_t simplicity_mallocBoundVars(unification_var** bound_var, size_t* word256_ix, size_t* extra_var_start, size_t extra_var_len) { +size_t simplicity_elements_mallocBoundVars(unification_var** bound_var, size_t* word256_ix, size_t* extra_var_start, size_t extra_var_len) { static_assert(1 <= NumberOfTypeNames, "Missing TypeNamesForJets."); static_assert(NumberOfTypeNames <= NUMBER_OF_TYPENAMES_MAX, "Too many TypeNamesForJets."); static_assert(DAG_LEN_MAX <= (SIZE_MAX - NumberOfTypeNames) / 6, "NumberOfTypeNames + 6*DAG_LEN_MAX doesn't fit in size_t"); @@ -71,7 +68,7 @@ static simplicity_err decodePrimitive(jetName* result, bitstream* stream) { if (bit < 0) return (simplicity_err)bit; if (!bit) { /* Core jets */ -#include "../../decodeCoreJets.inc" +#include "../decodeCoreJets.inc" return SIMPLICITY_ERR_DATA_OUT_OF_RANGE; } else { /* Elements jets */ @@ -94,12 +91,12 @@ static dag_node jetNode(jetName name) { * Returns 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' if the stream's prefix doesn't match any valid code for a jet. * Returns 'SIMPLICITY_ERR_BITSTRING_EOF' if not enough bits are available in the 'stream'. * In the above error cases, 'dag' may be modified. - * Returns 'SIMPLICITY_NO_ERR' if successful. + * Returns 'SIMPLICITY_NO_ERROR' if successful. * * Precondition: NULL != node * NULL != stream */ -simplicity_err simplicity_decodeJet(dag_node* node, bitstream* stream) { +simplicity_err simplicity_elements_decodeJet(dag_node* node, bitstream* stream) { jetName name; simplicity_err error = decodePrimitive(&name, stream); if (!IS_OK(error)) return error; diff --git a/primitive.h b/elements/primitive.h similarity index 78% rename from primitive.h rename to elements/primitive.h index dc17fb0b948..16b426783e9 100644 --- a/primitive.h +++ b/elements/primitive.h @@ -1,10 +1,10 @@ -/* This module defines the interface that each Simplicity application must implement. +/* Implements the required callbacks for the Elements Simplicity application. */ -#ifndef SIMPLICITY_PRIMITIVE_H -#define SIMPLICITY_PRIMITIVE_H +#ifndef SIMPLICITY_ELEMENTS_PRIMITIVE_H +#define SIMPLICITY_ELEMENTS_PRIMITIVE_H -#include "bitstream.h" -#include "typeInference.h" +#include "../bitstream.h" +#include "../typeInference.h" /* Allocate a fresh set of unification variables bound to at least all the types necessary * for all the jets that can be created by 'decodeJet', and also the type 'TWO^256', @@ -24,7 +24,7 @@ * '(*bound_var)[i]' is bound to 'A' and '(*bound_var)[j]' is bound to 'B' * and, '*word256_ix < *extra_var_start' and '(*bound_var)[*word256_ix]' is bound the type 'TWO^256' */ -size_t simplicity_mallocBoundVars(unification_var** bound_var, size_t* word256_ix, size_t* extra_var_start, size_t extra_var_len); +size_t simplicity_elements_mallocBoundVars(unification_var** bound_var, size_t* word256_ix, size_t* extra_var_start, size_t extra_var_len); /* Decode an Elements specific jet from 'stream' into 'node'. * All jets begin with a bit prefix of '1' which needs to have already been consumed from the 'stream'. @@ -36,6 +36,6 @@ size_t simplicity_mallocBoundVars(unification_var** bound_var, size_t* word256_i * Precondition: NULL != node * NULL != stream */ -simplicity_err simplicity_decodeJet(dag_node* node, bitstream* stream); +simplicity_err simplicity_elements_decodeJet(dag_node* node, bitstream* stream); #endif diff --git a/primitive/elements/primitiveEnumJet.inc b/elements/primitiveEnumJet.inc similarity index 100% rename from primitive/elements/primitiveEnumJet.inc rename to elements/primitiveEnumJet.inc diff --git a/primitive/elements/primitiveEnumTy.inc b/elements/primitiveEnumTy.inc similarity index 100% rename from primitive/elements/primitiveEnumTy.inc rename to elements/primitiveEnumTy.inc diff --git a/primitive/elements/primitiveInitTy.inc b/elements/primitiveInitTy.inc similarity index 100% rename from primitive/elements/primitiveInitTy.inc rename to elements/primitiveInitTy.inc diff --git a/primitive/elements/primitiveJetNode.inc b/elements/primitiveJetNode.inc similarity index 100% rename from primitive/elements/primitiveJetNode.inc rename to elements/primitiveJetNode.inc diff --git a/elements/txEnv.c b/elements/txEnv.c new file mode 100644 index 00000000000..a7b9f8ef12c --- /dev/null +++ b/elements/txEnv.c @@ -0,0 +1,26 @@ +#include "txEnv.h" + +/* Construct a txEnv structure from its components. + * This function will precompute any cached values. + * + * Precondition: NULL != tx + * NULL != taproot + * NULL != genesisHash + * ix < tx->numInputs + */ +txEnv simplicity_elements_build_txEnv(const elementsTransaction* tx, const elementsTapEnv* taproot, const sha256_midstate* genesisHash, uint_fast32_t ix) { + txEnv result = { .tx = tx + , .taproot = taproot + , .genesisHash = *genesisHash + , .ix = ix + }; + sha256_context ctx = sha256_init(result.sigAllHash.s); + sha256_hash(&ctx, genesisHash); + sha256_hash(&ctx, genesisHash); + sha256_hash(&ctx, &tx->txHash); + sha256_hash(&ctx, &taproot->tapEnvHash); + sha256_u32be(&ctx, ix); + sha256_finalize(&ctx); + + return result; +} diff --git a/primitive/elements/primitive.h b/elements/txEnv.h similarity index 95% rename from primitive/elements/primitive.h rename to elements/txEnv.h index b095107722f..f4493530de0 100644 --- a/primitive/elements/primitive.h +++ b/elements/txEnv.h @@ -2,11 +2,11 @@ * 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_PRIMITIVE_ELEMENTS_H -#define SIMPLICITY_PRIMITIVE_ELEMENTS_H +#ifndef SIMPLICITY_ELEMENTS_TXENV_H +#define SIMPLICITY_ELEMENTS_TXENV_H #include -#include "../../sha256.h" +#include "../sha256.h" /* An Elements 'outpoint' consists of a transaction id and output index within that transaction. * The transaction id can be a either a transaction within the chain, or the transaction id from another chain in case of a peg-in. @@ -194,7 +194,7 @@ typedef struct sigInput { /* A structure representing data from an Elements 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 transaction { +typedef struct elementsTransaction { const sigInput* input; const sigOutput* output; const sigOutput* const * feeOutputs; @@ -230,14 +230,14 @@ typedef struct transaction { uint_fast16_t lockDistance; uint_fast16_t lockDuration; /* Units of 512 seconds */ bool isFinal; -} transaction; +} elementsTransaction; /* A structure representing taproot spending data from an Elements transaction. * * Invariant: pathLen <= 128 * sha256_midstate path[pathLen]; */ -typedef struct tapEnv { +typedef struct elementsTapEnv { const sha256_midstate *path; sha256_midstate tapLeafHash; sha256_midstate tappathHash; @@ -246,7 +246,7 @@ typedef struct tapEnv { sha256_midstate scriptCMR; unsigned char pathLen; unsigned char leafVersion; -} tapEnv; +} elementsTapEnv; /* The 'txEnv' structure used by the Elements application of Simplicity. * @@ -256,8 +256,8 @@ typedef struct tapEnv { * + the hash of the genesis block for the chain, */ typedef struct txEnv { - const transaction* tx; - const tapEnv* taproot; + const elementsTransaction* tx; + const elementsTapEnv* taproot; sha256_midstate genesisHash; sha256_midstate sigAllHash; uint_fast32_t ix; @@ -271,6 +271,6 @@ typedef struct txEnv { * NULL != genesisHash * ix < tx->numInputs */ -txEnv simplicity_build_txEnv(const transaction* tx, const tapEnv* taproot, const sha256_midstate* genesisHash, uint_fast32_t ix); +txEnv simplicity_elements_build_txEnv(const elementsTransaction* tx, const elementsTapEnv* taproot, const sha256_midstate* genesisHash, uint_fast32_t ix); #endif diff --git a/eval.c b/eval.c index b1117e8b0d5..6c9e9dc05e8 100644 --- a/eval.c +++ b/eval.c @@ -571,6 +571,7 @@ typedef struct boundsAnalysis { * When maxCells < UBOUNDED_MAX, if the bounds on the number of cells needed for evaluation of 'dag' on an idealized Bit Machine exceeds maxCells, * then return SIMPLICITY_ERR_EXEC_MEMORY. * When maxCost < UBOUNDED_MAX, if the bounds on the dag's CPU cost exceeds 'maxCost', then return SIMPLICITY_ERR_EXEC_BUDGET. + * If the bounds on the dag's CPU cost is less than or equal to 'minCost', then return SIMPLICITY_ERR_OVERWEIGHT. * Otherwise returns SIMPLICITY_NO_ERR. * * Precondition: NULL != cellsBound @@ -583,11 +584,9 @@ typedef struct boundsAnalysis { * and if maxCells < UBOUNDED_MAX then '*cellsBound' bounds the number of cells needed for evaluation of 'dag' on an idealized Bit Machine * and if maxCells < UBOUNDED_MAX then '*UWORDBound' bounds the number of UWORDs needed for the frames during evaluation of 'dag' * and if maxCells < UBOUNDED_MAX then '*frameBound' bounds the number of stack frames needed during execution of 'dag'. - * - * :TODO: The cost calculations below are estimated and need to be replaced by experimental data. */ simplicity_err simplicity_analyseBounds( ubounded *cellsBound, ubounded *UWORDBound, ubounded *frameBound, ubounded *costBound - , ubounded maxCells, ubounded maxCost, const dag_node* dag, const type* type_dag, const size_t len) { + , ubounded maxCells, ubounded minCost, ubounded maxCost, const dag_node* dag, const type* type_dag, const size_t len) { static_assert(DAG_LEN_MAX <= SIZE_MAX / sizeof(boundsAnalysis), "bound array too large."); static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero."); static_assert(DAG_LEN_MAX - 1 <= UINT32_MAX, "bound array index does not fit in uint32_t."); @@ -741,6 +740,7 @@ simplicity_err simplicity_analyseBounds( ubounded *cellsBound, ubounded *UWORDBo */ return (maxCells < *cellsBound) ? SIMPLICITY_ERR_EXEC_MEMORY : (maxCost < *costBound) ? SIMPLICITY_ERR_EXEC_BUDGET + : (*costBound <= minCost) ? SIMPLICITY_ERR_OVERWEIGHT : SIMPLICITY_NO_ERROR; } @@ -748,11 +748,16 @@ simplicity_err simplicity_analyseBounds( ubounded *cellsBound, ubounded *UWORDBo * If bitSize(A) > 0, initialize the active read frame's data with 'input[ROUND_UWORD(bitSize(A))]'. * * If malloc fails, returns 'SIMPLICITY_ERR_MALLOC'. - * When a budget is given, if static analysis results determines the bound on cpu requirements exceed the allowed budget, returns 'SIMPLICITY_ERR_EXEC_BUDGET' - * If static analysis results determines the bound on memory allocation requirements exceed the allowed limits, returns 'SIMPLICITY_ERR_EXEC_MEMORY' + * When a budget is given, if static analysis results determines the bound on cpu requirements exceed the allowed budget, returns 'SIMPLICITY_ERR_EXEC_BUDGET'. + * If static analysis results determines the bound on cpu requirements is less than or equal to the minCost, returns 'SIMPLICITY_ERR_OVERWEIGHT'. + * If static analysis results determines the bound on memory allocation requirements exceed the allowed limits, returns 'SIMPLICITY_ERR_EXEC_MEMORY'. * If during execution some jet execution fails, returns 'SIMPLICITY_ERR_EXEC_JET'. * If during execution some 'assertr' or 'assertl' combinator fails, returns 'SIMPLICITY_ERR_EXEC_ASESRT'. * + * Note that minCost and budget parameters are in WU, while the cost analysis will be performed in milliWU. + * Thus the minCost and budget specify a half open interval (minCost, budget] of acceptable cost values in milliWU. + * Setting minCost to 0 effectively disables the minCost check as every Simplicity program has a non-zero cost analysis. + * * If none of the above conditions fail and bitSize(B) > 0, then a copy the final active write frame's data is written to 'output[roundWord(bitSize(B))]'. * * If 'anti_dos_checks' includes the 'CHECK_EXEC' flag, and not every non-HIDDEN dag node is executed, returns 'SIMPLICITY_ERR_ANTIDOS' @@ -764,19 +769,25 @@ simplicity_err simplicity_analyseBounds( ubounded *cellsBound, ubounded *UWORDBo * bitSize(A) == 0 or UWORD input[ROUND_UWORD(bitSize(A))]; * bitSize(B) == 0 or UWORD output[ROUND_UWORD(bitSize(B))]; * if NULL != budget then *budget <= BUDGET_MAX + * if NULL != budget then minCost <= *budget + * minCost <= BUDGET_MAX * if 'dag[len]' represents a Simplicity expression with primitives then 'NULL != env'; */ simplicity_err simplicity_evalTCOExpression( flags_type anti_dos_checks, UWORD* output, const UWORD* input - , const dag_node* dag, type* type_dag, size_t len, const ubounded* budget, const txEnv* env - ) { + , const dag_node* dag, type* type_dag, size_t len, ubounded minCost, const ubounded* budget, const txEnv* env + ) { simplicity_assert(1 <= len); simplicity_assert(len <= DAG_LEN_MAX); - if (budget) { simplicity_assert(*budget <= BUDGET_MAX); } + if (budget) { + simplicity_assert(*budget <= BUDGET_MAX); + simplicity_assert(minCost <= *budget); + } + simplicity_assert(minCost <= BUDGET_MAX); static_assert(1 <= UBOUNDED_MAX, "UBOUNDED_MAX is zero."); static_assert(BUDGET_MAX <= (UBOUNDED_MAX - 1) / 1000, "BUDGET_MAX is too large."); static_assert(CELLS_MAX < UBOUNDED_MAX, "CELLS_MAX is too large."); ubounded cellsBound, UWORDBound, frameBound, costBound; - simplicity_err result = simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, CELLS_MAX, budget ? *budget*1000 : UBOUNDED_MAX, dag, type_dag, len); + simplicity_err result = simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, CELLS_MAX, minCost*1000, budget ? *budget*1000 : UBOUNDED_MAX, dag, type_dag, len); if (!IS_OK(result)) return result; /* frameBound is at most 2*len. */ diff --git a/eval.h b/eval.h index ba6a6759057..cef5cb73b20 100644 --- a/eval.h +++ b/eval.h @@ -18,6 +18,7 @@ typedef unsigned char flags_type; * When maxCells < UBOUNDED_MAX, if the bounds on the number of cells needed for evaluation of 'dag' on an idealized Bit Machine exceeds maxCells, * then return SIMPLICITY_ERR_EXEC_MEMORY. * When maxCost < UBOUNDED_MAX, if the bounds on the dag's CPU cost exceeds 'maxCost', then return SIMPLICITY_ERR_EXEC_BUDGET. + * If the bounds on the dag's CPU cost is less than or equal to 'minCost', then return SIMPLICITY_ERR_OVERWEIGHT. * Otherwise returns SIMPLICITY_NO_ERR. * * Precondition: NULL != cellsBound @@ -32,17 +33,22 @@ typedef unsigned char flags_type; * and if maxCells < UBOUNDED_MAX then '*frameBound' bounds the number of stack frames needed during execution of 'dag'. */ simplicity_err simplicity_analyseBounds( ubounded *cellsBound, ubounded *UWORDBound, ubounded *frameBound, ubounded *costBound - , ubounded maxCells, ubounded maxCost, const dag_node* dag, const type* type_dag, const size_t len); + , ubounded maxCells, ubounded minCost, ubounded maxCost, const dag_node* dag, const type* type_dag, const size_t len); /* Run the Bit Machine on the well-typed Simplicity expression 'dag[len]' of type A |- B. * If bitSize(A) > 0, initialize the active read frame's data with 'input[ROUND_UWORD(bitSize(A))]'. * * If malloc fails, returns 'SIMPLICITY_ERR_MALLOC'. - * When a budget is given, if static analysis results determines the bound on cpu requirements exceed the allowed budget, returns 'SIMPLICITY_ERR_EXEC_BUDGET' - * If static analysis results determines the bound on memory allocation requirements exceed the allowed limits, returns 'SIMPLICITY_ERR_EXEC_MEMORY' + * When a budget is given, if static analysis results determines the bound on cpu requirements exceed the allowed budget, returns 'SIMPLICITY_ERR_EXEC_BUDGET'. + * If static analysis results determines the bound on cpu requirements is less than or equal to the minCost, returns 'SIMPLICITY_ERR_OVERWEIGHT'. + * If static analysis results determines the bound on memory allocation requirements exceed the allowed limits, returns 'SIMPLICITY_ERR_EXEC_MEMORY'. * If during execution some jet execution fails, returns 'SIMPLICITY_ERR_EXEC_JET'. * If during execution some 'assertr' or 'assertl' combinator fails, returns 'SIMPLICITY_ERR_EXEC_ASESRT'. * + * Note that minCost and budget parameters are in WU, while the cost analysis will be performed in milliWU. + * Thus the minCost and budget specify a half open interval (minCost, budget] of acceptable cost values in milliWU. + * Setting minCost to 0 effectively disables the minCost check as every Simplicity program has a non-zero cost analysis. + * * If none of the above conditions fail and bitSize(B) > 0, then a copy the final active write frame's data is written to 'output[roundWord(bitSize(B))]'. * * If 'anti_dos_checks' includes the 'CHECK_EXEC' flag, and not every non-HIDDEN dag node is executed, returns 'SIMPLICITY_ERR_ANTIDOS' @@ -54,19 +60,27 @@ simplicity_err simplicity_analyseBounds( ubounded *cellsBound, ubounded *UWORDBo * bitSize(A) == 0 or UWORD input[ROUND_UWORD(bitSize(A))]; * bitSize(B) == 0 or UWORD output[ROUND_UWORD(bitSize(B))]; * if NULL != budget then *budget <= BUDGET_MAX + * if NULL != budget then minCost <= *budget + * minCost <= BUDGET_MAX * if 'dag[len]' represents a Simplicity expression with primitives then 'NULL != env'; */ simplicity_err simplicity_evalTCOExpression( flags_type anti_dos_checks, UWORD* output, const UWORD* input - , const dag_node* dag, type* type_dag, size_t len, const ubounded* budget, const txEnv* env - ); + , const dag_node* dag, type* type_dag, size_t len, ubounded minCost, const ubounded* budget, const txEnv* env + ); /* Run the Bit Machine on the well-typed Simplicity program 'dag[len]'. * * If malloc fails, returns 'SIMPLICITY_ERR_MALLOC'. - * When a budget is given, if static analysis results determines the bound on cpu requirements exceed the allowed budget, returns 'SIMPLICITY_ERR_EXEC_BUDGET' - * If static analysis results determines the bound on memory allocation requirements exceed the allowed limits, returns 'SIMPLICITY_ERR_EXEC_MEMORY' + * When a budget is given, if static analysis results determines the bound on cpu requirements exceed the allowed budget, returns 'SIMPLICITY_ERR_EXEC_BUDGET'. + * If static analysis results determines the bound on cpu requirements is less than or equal to the minCost, returns 'SIMPLICITY_ERR_OVERWEIGHT'. + * If static analysis results determines the bound on memory allocation requirements exceed the allowed limits, returns 'SIMPLICITY_ERR_EXEC_MEMORY'. * If during execution some jet execution fails, returns 'SIMPLICITY_ERR_EXEC_JET'. * If during execution some 'assertr' or 'assertl' combinator fails, returns 'SIMPLICITY_ERR_EXEC_ASESRT'. + * + * Note that minCost and budget parameters are in WU, while the cost analysis will be performed in milliWU. + * Thus the minCost and budget specify a half open interval (minCost, budget] of acceptable cost values in milliWU. + * Setting minCost to 0 effectively disables the minCost check as every Simplicity program has a non-zero cost analysis. + * * If not every non-HIDDEN dag node is executed, returns 'SIMPLICITY_ERR_ANTIDOS' * If not every case node has both branches executed, returns 'SIMPLICITY_ERR_ANTIDOS' * @@ -74,9 +88,11 @@ simplicity_err simplicity_evalTCOExpression( flags_type anti_dos_checks, UWORD* * * Precondition: dag_node dag[len] and 'dag' is well-typed with 'type_dag' for an expression of type ONE |- ONE; * if NULL != budget then *budget <= BUDGET_MAX + * if NULL != budget then minCost <= *budget + * minCost <= BUDGET_MAX * if 'dag[len]' represents a Simplicity expression with primitives then 'NULL != env'; */ -static inline simplicity_err evalTCOProgram(const dag_node* dag, type* type_dag, size_t len, const ubounded* budget, const txEnv* env) { - return simplicity_evalTCOExpression(CHECK_ALL, NULL, NULL, dag, type_dag, len, budget, env); +static inline simplicity_err evalTCOProgram(const dag_node* dag, type* type_dag, size_t len, ubounded minCost, const ubounded* budget, const txEnv* env) { + return simplicity_evalTCOExpression(CHECK_ALL, NULL, NULL, dag, type_dag, len, minCost, budget, env); } #endif diff --git a/include/simplicity/cmr.h b/include/simplicity/elements/cmr.h similarity index 74% rename from include/simplicity/cmr.h rename to include/simplicity/elements/cmr.h index 4bde71a28e6..af3a0077940 100644 --- a/include/simplicity/cmr.h +++ b/include/simplicity/elements/cmr.h @@ -1,5 +1,5 @@ -#ifndef SIMPLICITY_CMR_H -#define SIMPLICITY_CMR_H +#ifndef SIMPLICITY_ELEMENTS_CMR_H +#define SIMPLICITY_ELEMENTS_CMR_H #include #include @@ -18,6 +18,6 @@ * unsigned char cmr[32] * unsigned char program[program_len] */ -extern bool simplicity_computeCmr( simplicity_err* error, unsigned char* cmr - , const unsigned char* program, size_t program_len); +extern bool simplicity_elements_computeCmr( simplicity_err* error, unsigned char* cmr + , const unsigned char* program, size_t program_len); #endif diff --git a/include/simplicity/elements/env.h b/include/simplicity/elements/env.h index e5c683f9f2c..ef55f31bc31 100644 --- a/include/simplicity/elements/env.h +++ b/include/simplicity/elements/env.h @@ -4,9 +4,9 @@ #include #include -/* This section builds the 'rawTransaction' structure which is the transaction data needed to build an Elements 'txEnv' environment +/* This section builds the 'rawElementsTransaction' structure which is the transaction data needed to build an Elements 'txEnv' environment * for evaluating Simplicity expressions within. - * The 'rawTransaction' is copied into an opaque 'transaction' structure that can be reused within evaluating Simplicity on multiple + * The 'rawElementsTransaction' is copied into an opaque 'elementsTransaction' structure that can be reused within evaluating Simplicity on multiple * inputs within the same transaction. */ @@ -14,10 +14,10 @@ * * Invariant: if 0 < len then unsigned char buf[len] */ -typedef struct rawBuffer { +typedef struct rawElementsBuffer { const unsigned char* buf; uint32_t len; -} rawBuffer; +} rawElementsBuffer; /* A structure representing data for one output from an Elements transaction. * @@ -25,14 +25,14 @@ typedef struct rawBuffer { * unsigned char value[value[0] == 1 ? 9 : 33] or value == NULL; * unsigned char nonce[33] or nonce == NULL; */ -typedef struct rawOutput { +typedef struct rawElementsOutput { const unsigned char* asset; const unsigned char* value; const unsigned char* nonce; - rawBuffer scriptPubKey; - rawBuffer surjectionProof; - rawBuffer rangeProof; -} rawOutput; + rawElementsBuffer scriptPubKey; + rawElementsBuffer surjectionProof; + rawElementsBuffer rangeProof; +} rawElementsOutput; /* A structure representing data for one input from an Elements transaction, including its taproot annex, * plus the TXO data of the output being redeemed. @@ -46,8 +46,8 @@ typedef struct rawOutput { * unsigned char txo.asset[33] or txo.asset == NULL; * unsigned char txo.value[txo.value[0] == 1 ? 9 : 33] or txo.value == NULL; */ -typedef struct rawInput { - const rawBuffer* annex; +typedef struct rawElementsInput { + const rawElementsBuffer* annex; const unsigned char* prevTxid; const unsigned char* pegin; struct { @@ -55,48 +55,48 @@ typedef struct rawInput { const unsigned char* assetEntropy; const unsigned char* amount; const unsigned char* inflationKeys; - rawBuffer amountRangePrf; - rawBuffer inflationKeysRangePrf; + rawElementsBuffer amountRangePrf; + rawElementsBuffer inflationKeysRangePrf; } issuance; struct { const unsigned char* asset; const unsigned char* value; - rawBuffer scriptPubKey; + rawElementsBuffer scriptPubKey; } txo; - rawBuffer scriptSig; + rawElementsBuffer scriptSig; uint32_t prevIx; uint32_t sequence; -} rawInput; +} rawElementsInput; /* A structure representing data for an Elements transaction, including the TXO data of each output being redeemed. * * Invariant: unsigned char txid[32]; - * rawInput input[numInputs]; - * rawOutput output[numOutputs]; + * rawElementsInput input[numInputs]; + * rawElementsOutput output[numOutputs]; */ -typedef struct rawTransaction { +typedef struct rawElementsTransaction { const unsigned char* txid; /* While in theory we could recompute the txid ourselves, it is easier and safer for it to be provided. */ - const rawInput* input; - const rawOutput* output; + const rawElementsInput* input; + const rawElementsOutput* output; uint32_t numInputs; uint32_t numOutputs; uint32_t version; uint32_t lockTime; -} rawTransaction; +} rawElementsTransaction; -/* A forward declaration for the structure containing a copy (and digest) of the rawTransaction data */ -typedef struct transaction transaction; +/* A forward declaration for the structure containing a copy (and digest) of the rawElementsTransaction data */ +typedef struct elementsTransaction elementsTransaction; -/* Allocate and initialize a 'transaction' from a 'rawTransaction', copying or hashing the data as needed. +/* Allocate and initialize a 'elementsTransaction' from a 'rawElementsTransaction', 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 transaction* simplicity_elements_mallocTransaction(const rawTransaction* rawTx); +extern elementsTransaction* simplicity_elements_mallocTransaction(const rawElementsTransaction* rawTx); -/* Free a pointer to 'transaction'. +/* Free a pointer to 'elementsTransaction'. */ -extern void simplicity_elements_freeTransaction(transaction* tx); +extern void simplicity_elements_freeTransaction(elementsTransaction* tx); /* A structure representing taproot spending data for an Elements transaction. * @@ -104,23 +104,23 @@ extern void simplicity_elements_freeTransaction(transaction* tx); * unsigned char controlBlock[33+pathLen*32]; * unsigned char scriptCMR[32]; */ -typedef struct rawTapEnv { +typedef struct rawElementsTapEnv { const unsigned char* controlBlock; const unsigned char* scriptCMR; unsigned char pathLen; -} rawTapEnv; +} rawElementsTapEnv; -/* A forward declaration for the structure containing a copy (and digest) of the rawTapEnv data */ -typedef struct tapEnv tapEnv; +/* A forward declaration for the structure containing a copy (and digest) of the rawElementsTapEnv data */ +typedef struct elementsTapEnv elementsTapEnv; -/* Allocate and initialize a 'tapEnv' from a 'rawTapEnv', copying or hashing the data as needed. +/* Allocate and initialize a 'elementsTapEnv' from a 'rawElementsTapEnv', 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 tapEnv* simplicity_elements_mallocTapEnv(const rawTapEnv* rawEnv); +extern elementsTapEnv* simplicity_elements_mallocTapEnv(const rawElementsTapEnv* rawEnv); -/* Free a pointer to 'tapEnv'. +/* Free a pointer to 'elementsTapEnv'. */ -extern void simplicity_elements_freeTapEnv(tapEnv* env); +extern void simplicity_elements_freeTapEnv(elementsTapEnv* env); #endif diff --git a/include/simplicity/elements/exec.h b/include/simplicity/elements/exec.h index 758df913a16..18f70709e1f 100644 --- a/include/simplicity/elements/exec.h +++ b/include/simplicity/elements/exec.h @@ -14,6 +14,12 @@ * Otherwise, 'true' is returned indicating that the result was successfully computed and returned in the '*error' value. * * If deserialization, analysis, or execution fails, then '*error' is set to some simplicity_err. + * In particular, if the cost analysis exceeds the budget, or exceeds BUDGET_MAX, then '*error' is set to 'SIMPLICITY_ERR_EXEC_BUDGET'. + * On the other hand, if the cost analysis is less than or equal to minCost, then '*error' is set to 'SIMPLICITY_ERR_OVERWEIGHT'. + * + * Note that minCost and budget parameters are in WU, while the cost analysis will be performed in milliWU. + * Thus the minCost and budget specify a half open interval (minCost, budget] of acceptable cost values in milliWU. + * Setting minCost to 0 effectively disables the minCost check as every Simplicity program has a non-zero cost analysis. * * If 'amr != NULL' and the annotated Merkle root of the decoded expression doesn't match 'amr' then '*error' is set to 'SIMPLICITY_ERR_AMR'. * @@ -27,15 +33,15 @@ * NULL != tx; * NULL != taproot; * unsigned char genesisBlockHash[32] - * 0 <= budget; + * 0 <= minCost <= budget; * NULL != amr implies unsigned char amr[32] * unsigned char program[program_len] * unsigned char witness[witness_len] */ extern bool simplicity_elements_execSimplicity( simplicity_err* error, unsigned char* ihr - , const transaction* tx, uint_fast32_t ix, const tapEnv* taproot + , const elementsTransaction* tx, uint_fast32_t ix, const elementsTapEnv* taproot , const unsigned char* genesisBlockHash - , int64_t budget + , int64_t minCost, int64_t budget , const unsigned char* amr , const unsigned char* program, size_t program_len , const unsigned char* witness, size_t witness_len); diff --git a/include/simplicity/errorCodes.h b/include/simplicity/errorCodes.h index 00ffee51c07..cea6e7dbc77 100644 --- a/include/simplicity/errorCodes.h +++ b/include/simplicity/errorCodes.h @@ -36,6 +36,7 @@ typedef enum { SIMPLICITY_ERR_ANTIDOS = -42, SIMPLICITY_ERR_HIDDEN_ROOT = -44, SIMPLICITY_ERR_AMR = -46, + SIMPLICITY_ERR_OVERWEIGHT = -48, } simplicity_err; /* Check if failure is permanent (or success which is always permanent). */ @@ -102,6 +103,8 @@ static inline const char * SIMPLICITY_ERR_MSG(simplicity_err err) { return "Program's root is HIDDEN"; case SIMPLICITY_ERR_AMR: return "Program's AMR does not match"; + case SIMPLICITY_ERR_OVERWEIGHT: + return "Program's budget is too large"; default: return "Unknown error code"; } diff --git a/test.c b/test.c index 445b5726786..4ec33387d56 100644 --- a/test.c +++ b/test.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include "ctx8Pruned.h" #include "ctx8Unpruned.h" #include "dag.h" @@ -20,7 +20,8 @@ #include "typeSkipTest.h" #include "simplicity_alloc.h" #include "typeInference.h" -#include "primitive/elements/checkSigHashAllTx1.h" +#include "elements/checkSigHashAllTx1.h" +#include "elements/primitive.h" _Static_assert(CHAR_BIT == 8, "Buffers passed to fmemopen presume 8 bit chars"); @@ -78,7 +79,7 @@ static void test_hashBlock(void) { simplicity_err error; { bitstream stream = initializeBitstream(hashBlock, sizeof_hashBlock); - len = simplicity_decodeMallocDag(&dag, &census, &stream); + len = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, &census, &stream); if (!dag) { simplicity_assert(len < 0); error = (simplicity_err)len; @@ -105,7 +106,7 @@ static void test_hashBlock(void) { type* type_dag; bitstream witness = initializeBitstream(hashBlock_witness, sizeof_hashBlock_witness); - if (!IS_OK(simplicity_mallocTypeInference(&type_dag, dag, (uint_fast32_t)len, &census)) || !type_dag || + if (!IS_OK(simplicity_mallocTypeInference(&type_dag, simplicity_elements_mallocBoundVars, dag, (uint_fast32_t)len, &census)) || !type_dag || type_dag[dag[len-1].sourceType].bitSize != 768 || type_dag[dag[len-1].targetType].bitSize != 256) { failures++; printf("Unexpected failure of type inference for hashblock\n"); @@ -152,7 +153,7 @@ static void test_hashBlock(void) { } { ubounded cellsBound, UWORDBound, frameBound, costBound; - if (IS_OK(simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len)) + if (IS_OK(simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, 0, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len)) && hashBlock_cost == costBound) { successes++; } else { @@ -160,7 +161,7 @@ static void test_hashBlock(void) { printf("Expected %d for cost, but got %d instead.\n", hashBlock_cost, costBound); } } - simplicity_err err = simplicity_evalTCOExpression(CHECK_NONE, output, input, dag, type_dag, (uint_fast32_t)len, NULL, NULL); + simplicity_err err = simplicity_evalTCOExpression(CHECK_NONE, output, input, dag, type_dag, (uint_fast32_t)len, 0, NULL, NULL); if (IS_OK(err)) { /* The expected result is the value 'SHA256("abc")'. */ const uint32_t expectedHash[8] = { 0xba7816bful, 0x8f01cfeaul, 0x414140deul, 0x5dae2223ul @@ -195,7 +196,7 @@ static void test_program(char* name, const unsigned char* program, size_t progra simplicity_err error; { bitstream stream = initializeBitstream(program, program_len); - len = simplicity_decodeMallocDag(&dag, &census, &stream); + len = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, &census, &stream); if (!dag) { simplicity_assert(len < 0); error = (simplicity_err)len; @@ -231,7 +232,7 @@ static void test_program(char* name, const unsigned char* program, size_t progra } type* type_dag; bitstream witness_stream = initializeBitstream(witness, witness_len); - if (!IS_OK(simplicity_mallocTypeInference(&type_dag, dag, (uint_fast32_t)len, &census)) || !type_dag || + if (!IS_OK(simplicity_mallocTypeInference(&type_dag, simplicity_elements_mallocBoundVars, dag, (uint_fast32_t)len, &census)) || !type_dag || dag[len-1].sourceType != 0 || dag[len-1].targetType != 0) { failures++; printf("Unexpected failure of type inference.\n"); @@ -264,7 +265,8 @@ static void test_program(char* name, const unsigned char* program, size_t progra } if (expectedCost) { ubounded cellsBound, UWORDBound, frameBound, costBound; - if (IS_OK(simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len)) + if (IS_OK(simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, 0, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len)) + && 0 < costBound && *expectedCost == costBound) { successes++; } else { @@ -272,7 +274,8 @@ static void test_program(char* name, const unsigned char* program, size_t progra printf("Expected %u for cost, but got %u instead.\n", *expectedCost, costBound); } /* Analysis should pass when computed bounds are used. */ - if (IS_OK(simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, cellsBound, costBound, dag, type_dag, (uint_fast32_t)len))) { + if (IS_OK(simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, cellsBound, costBound-1, costBound, dag, type_dag, (uint_fast32_t)len)) + && *expectedCost == costBound) { successes++; } else { failures++; @@ -280,7 +283,7 @@ static void test_program(char* name, const unsigned char* program, size_t progra } /* if cellsBound is non-zero, analysis should fail when smaller cellsBound is used. */ if (0 < cellsBound) { - if (SIMPLICITY_ERR_EXEC_MEMORY == simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, cellsBound-1, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len)) { + if (SIMPLICITY_ERR_EXEC_MEMORY == simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, cellsBound-1, 0, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len)) { successes++; } else { failures++; @@ -289,15 +292,24 @@ static void test_program(char* name, const unsigned char* program, size_t progra } /* Analysis should fail when smaller costBound is used. */ if (0 < *expectedCost && - SIMPLICITY_ERR_EXEC_BUDGET == simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, *expectedCost-1, dag, type_dag, (uint_fast32_t)len) + SIMPLICITY_ERR_EXEC_BUDGET == simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, 0, *expectedCost-1, dag, type_dag, (uint_fast32_t)len) ) { successes++; } else { failures++; printf("Analysis with too small cost bounds failed.\n"); } + /* Analysis should fail when overweight. */ + if (0 < *expectedCost && + SIMPLICITY_ERR_OVERWEIGHT == simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, *expectedCost, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len) + ) { + successes++; + } else { + failures++; + printf("Analysis with too large minCost failed.\n"); + } } - simplicity_err actualResult = evalTCOProgram(dag, type_dag, (uint_fast32_t)len, NULL, NULL); + simplicity_err actualResult = evalTCOProgram(dag, type_dag, (uint_fast32_t)len, 0, NULL, NULL); if (expectedResult == actualResult) { successes++; } else { @@ -319,7 +331,7 @@ static void test_occursCheck(void) { int_fast32_t len; { bitstream stream = initializeBitstream(buf, sizeof(buf)); - len = simplicity_decodeMallocDag(&dag, &census, &stream); + len = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, &census, &stream); } if (!dag) { simplicity_assert(len < 0); @@ -327,7 +339,7 @@ static void test_occursCheck(void) { } else { type* type_dag; simplicity_assert(0 < len); - if (SIMPLICITY_ERR_TYPE_INFERENCE_OCCURS_CHECK == simplicity_mallocTypeInference(&type_dag, dag, (uint_fast32_t)len, &census) && + if (SIMPLICITY_ERR_TYPE_INFERENCE_OCCURS_CHECK == simplicity_mallocTypeInference(&type_dag, simplicity_elements_mallocBoundVars, dag, (uint_fast32_t)len, &census) && !type_dag) { successes++; } else { @@ -345,18 +357,18 @@ static void test_elements(void) { sha256_fromMidstate(amr, elementsCheckSigHashAllTx1_amr); unsigned char genesisHash[32] = "\x0f\x91\x88\xf1\x3c\xb7\xb2\xc7\x1f\x2a\x33\x5e\x3a\x4f\xc3\x28\xbf\x5b\xeb\x43\x60\x12\xaf\xca\x59\x0b\x1a\x11\x46\x6e\x22\x06"; - rawTapEnv rawTaproot = (rawTapEnv) + rawElementsTapEnv rawTaproot = (rawElementsTapEnv) { .controlBlock = (unsigned char [33]){"\xbe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3b\x78\xce\x56\x3f\x89\xa0\xed\x94\x14\xf5\xaa\x28\xad\x0d\x96\xd6\x79\x5f\x9c\x63"} , .pathLen = 0 , .scriptCMR = cmr }; - tapEnv* taproot = simplicity_elements_mallocTapEnv(&rawTaproot); + elementsTapEnv* taproot = simplicity_elements_mallocTapEnv(&rawTaproot); printf("Test elements\n"); { - rawTransaction testTx1 = (rawTransaction) + rawElementsTransaction testTx1 = (rawElementsTransaction) { .txid = (unsigned char[32]){"\xdb\x9a\x3d\xe0\xb6\xb8\xcc\x74\x1e\x4d\x6c\x8f\x19\xce\x75\xec\x0d\xfd\x01\x02\xdb\x9c\xb5\xcd\x27\xa4\x1a\x66\x91\x66\x3a\x07"} - , .input = (rawInput[]) + , .input = (rawElementsInput[]) { { .annex = NULL , .prevTxid = (unsigned char[32]){"\xeb\x04\xb6\x8e\x9a\x26\xd1\x16\x04\x6c\x76\xe8\xff\x47\x33\x2f\xb7\x1d\xda\x90\xff\x4b\xef\x53\x70\xf2\x52\x26\xd3\xbc\x09\xfc"} , .prevIx = 0 @@ -367,7 +379,7 @@ static void test_elements(void) { , .value = (unsigned char[9]){"\x01\x00\x00\x00\x02\x54\x0b\xe4\x00"} , .scriptPubKey = {0} } } } - , .output = (rawOutput[]) + , .output = (rawElementsOutput[]) { { .asset = (unsigned char[33]){"\x01\x23\x0f\x4f\x5d\x4b\x7c\x6f\xa8\x45\x80\x6e\xe4\xf6\x77\x13\x45\x9e\x1b\x69\xe8\xe6\x0f\xce\xe2\xe4\x94\x0c\x7a\x0d\x5d\xe1\xb2"} , .value = (unsigned char[9]){"\x01\x00\x00\x00\x02\x54\x0b\xd7\x1c"} , .nonce = NULL @@ -385,13 +397,13 @@ static void test_elements(void) { , .version = 0x00000002 , .lockTime = 0x00000000 }; - transaction* tx1 = simplicity_elements_mallocTransaction(&testTx1); + elementsTransaction* tx1 = simplicity_elements_mallocTransaction(&testTx1); if (tx1) { successes++; simplicity_err execResult; { unsigned char cmrResult[32]; - if (simplicity_computeCmr(&execResult, cmrResult, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1) && IS_OK(execResult)) { + if (simplicity_elements_computeCmr(&execResult, cmrResult, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1) && IS_OK(execResult)) { if (0 == memcmp(cmrResult, cmr, sizeof(unsigned char[8]))) { successes++; } else { @@ -400,12 +412,12 @@ static void test_elements(void) { } } else { failures++; - printf("simplicity_computeCmr of elementsCheckSigHashAllTx1 unexpectedly produced %d.\n", execResult); + printf("simplicity_elements_computeCmr of elementsCheckSigHashAllTx1 unexpectedly produced %d.\n", execResult); } } { unsigned char ihrResult[32]; - if (simplicity_elements_execSimplicity(&execResult, ihrResult, tx1, 0, taproot, genesisHash, (elementsCheckSigHashAllTx1_cost + 999)/1000, amr, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, elementsCheckSigHashAllTx1_witness, sizeof_elementsCheckSigHashAllTx1_witness) && IS_OK(execResult)) { + if (simplicity_elements_execSimplicity(&execResult, ihrResult, tx1, 0, taproot, genesisHash, 0, (elementsCheckSigHashAllTx1_cost + 999)/1000, amr, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, elementsCheckSigHashAllTx1_witness, sizeof_elementsCheckSigHashAllTx1_witness) && IS_OK(execResult)) { sha256_midstate ihr; sha256_toMidstate(ihr.s, ihrResult); if (0 == memcmp(ihr.s, elementsCheckSigHashAllTx1_ihr, sizeof(uint32_t[8]))) { @@ -421,7 +433,7 @@ static void test_elements(void) { if (elementsCheckSigHashAllTx1_cost){ /* test the same transaction without adequate budget. */ simplicity_assert(elementsCheckSigHashAllTx1_cost); - if (simplicity_elements_execSimplicity(&execResult, ihrResult, tx1, 0, taproot, genesisHash, (elementsCheckSigHashAllTx1_cost - 1)/1000, amr, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, elementsCheckSigHashAllTx1_witness, sizeof_elementsCheckSigHashAllTx1_witness) && SIMPLICITY_ERR_EXEC_BUDGET == execResult) { + if (simplicity_elements_execSimplicity(&execResult, ihrResult, tx1, 0, taproot, genesisHash, 0, (elementsCheckSigHashAllTx1_cost - 1)/1000, amr, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, elementsCheckSigHashAllTx1_witness, sizeof_elementsCheckSigHashAllTx1_witness) && SIMPLICITY_ERR_EXEC_BUDGET == execResult) { successes++; } else { failures++; @@ -434,7 +446,7 @@ static void test_elements(void) { unsigned char brokenSig[sizeof_elementsCheckSigHashAllTx1_witness]; memcpy(brokenSig, elementsCheckSigHashAllTx1_witness, sizeof_elementsCheckSigHashAllTx1_witness); brokenSig[sizeof_elementsCheckSigHashAllTx1_witness - 1] ^= 0x80; - if (simplicity_elements_execSimplicity(&execResult, NULL, tx1, 0, taproot, genesisHash, BUDGET_MAX, NULL, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, brokenSig, sizeof_elementsCheckSigHashAllTx1_witness) && SIMPLICITY_ERR_EXEC_JET == execResult) { + if (simplicity_elements_execSimplicity(&execResult, NULL, tx1, 0, taproot, genesisHash, 0, BUDGET_MAX, NULL, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, brokenSig, sizeof_elementsCheckSigHashAllTx1_witness) && SIMPLICITY_ERR_EXEC_JET == execResult) { successes++; } else { failures++; @@ -449,9 +461,9 @@ static void test_elements(void) { } /* test a modified transaction with the same signature. */ { - rawTransaction testTx2 = (rawTransaction) + rawElementsTransaction testTx2 = (rawElementsTransaction) { .txid = (unsigned char[32]){"\xdb\x9a\x3d\xe0\xb6\xb8\xcc\x74\x1e\x4d\x6c\x8f\x19\xce\x75\xec\x0d\xfd\x01\x02\xdb\x9c\xb5\xcd\x27\xa4\x1a\x66\x91\x66\x3a\x07"} - , .input = (rawInput[]) + , .input = (rawElementsInput[]) { { .prevTxid = (unsigned char[32]){"\xeb\x04\xb6\x8e\x9a\x26\xd1\x16\x04\x6c\x76\xe8\xff\x47\x33\x2f\xb7\x1d\xda\x90\xff\x4b\xef\x53\x70\xf2\x52\x26\xd3\xbc\x09\xfc"} , .prevIx = 0 , .sequence = 0xffffffff /* Here is the modification. */ @@ -460,7 +472,7 @@ static void test_elements(void) { , .value = (unsigned char[9]){"\x01\x00\x00\x00\x02\x54\x0b\xe4\x00"} , .scriptPubKey = {0} } } } - , .output = (rawOutput[]) + , .output = (rawElementsOutput[]) { { .asset = (unsigned char[33]){"\x01\x23\x0f\x4f\x5d\x4b\x7c\x6f\xa8\x45\x80\x6e\xe4\xf6\x77\x13\x45\x9e\x1b\x69\xe8\xe6\x0f\xce\xe2\xe4\x94\x0c\x7a\x0d\x5d\xe1\xb2"} , .value = (unsigned char[9]){"\x01\x00\x00\x00\x02\x54\x0b\xd7\x1c"} , .nonce = NULL @@ -478,12 +490,12 @@ static void test_elements(void) { , .version = 0x00000002 , .lockTime = 0x00000000 }; - transaction* tx2 = simplicity_elements_mallocTransaction(&testTx2); + elementsTransaction* tx2 = simplicity_elements_mallocTransaction(&testTx2); if (tx2) { successes++; simplicity_err execResult; { - if (simplicity_elements_execSimplicity(&execResult, NULL, tx2, 0, taproot, genesisHash, BUDGET_MAX, NULL, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, elementsCheckSigHashAllTx1_witness, sizeof_elementsCheckSigHashAllTx1_witness) && SIMPLICITY_ERR_EXEC_JET == execResult) { + if (simplicity_elements_execSimplicity(&execResult, NULL, tx2, 0, taproot, genesisHash, 0, BUDGET_MAX, NULL, elementsCheckSigHashAllTx1, sizeof_elementsCheckSigHashAllTx1, elementsCheckSigHashAllTx1_witness, sizeof_elementsCheckSigHashAllTx1_witness) && SIMPLICITY_ERR_EXEC_JET == execResult) { successes++; } else { failures++; @@ -625,6 +637,83 @@ static void iden8mebi_test(void) { } } } +static void exactBudget_test(void) { + /* Core Simplicity program with a cost that is exactly 410000 milliWU */ + const unsigned char program[] = { + 0xe0, 0x09, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x81, 0x02, + 0x05, 0xb4, 0x6d, 0xa0, 0x80 + }; + const ubounded expectedCost = 410; /* in WU */ + + printf("Test exactBudget\n"); + + dag_node* dag; + type* type_dag; + combinator_counters census; + int_fast32_t len; + simplicity_err error, expected; + sha256_midstate ihr; + { + bitstream stream = initializeBitstream(program, sizeof(program)); + len = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, &census, &stream); + simplicity_assert(dag); + simplicity_assert(0 < len); + error = simplicity_closeBitstream(&stream); + simplicity_assert(IS_OK(error)); + } + error = simplicity_mallocTypeInference(&type_dag, simplicity_elements_mallocBoundVars, dag, (uint_fast32_t)len, &census); + simplicity_assert(IS_OK(error)); + simplicity_assert(type_dag); + simplicity_assert(!dag[len-1].sourceType); + simplicity_assert(!dag[len-1].targetType); + { + bitstream stream = initializeBitstream(NULL, 0); + error = simplicity_fillWitnessData(dag, type_dag, (uint_fast32_t)len, &stream); + simplicity_assert(IS_OK(error)); + } + error = simplicity_verifyNoDuplicateIdentityHashes(&ihr, dag, type_dag, (uint_fast32_t)len); + simplicity_assert(IS_OK(error)); + { + ubounded cellsBound, UWORDBound, frameBound, costBound; + error = simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, UBOUNDED_MAX, 0, UBOUNDED_MAX, dag, type_dag, (uint_fast32_t)len); + simplicity_assert(IS_OK(error)); + simplicity_assert(1000*expectedCost == costBound); + } + error = evalTCOProgram(dag, type_dag, (uint_fast32_t)len, expectedCost, &expectedCost, NULL); + expected = SIMPLICITY_ERR_OVERWEIGHT; + if (expected == error) { + successes++; + } else { + failures++; + printf("Expected %d from evaluation, but got %d instead.\n", expected, error); + } + error = evalTCOProgram(dag, type_dag, (uint_fast32_t)len, expectedCost-1, &(ubounded){expectedCost-1}, NULL); + expected = SIMPLICITY_ERR_EXEC_BUDGET; + if (expected == error) { + successes++; + } else { + failures++; + printf("Expected %d from evaluation, but got %d instead.\n", expected, error); + } + error = evalTCOProgram(dag, type_dag, (uint_fast32_t)len, expectedCost, &(ubounded){expectedCost+1}, NULL); + expected = SIMPLICITY_ERR_OVERWEIGHT; + if (expected == error) { + successes++; + } else { + failures++; + printf("Expected %d from evaluation, but got %d instead.\n", expected, error); + } + error = evalTCOProgram(dag, type_dag, (uint_fast32_t)len, expectedCost-1, &expectedCost, NULL); + expected = SIMPLICITY_NO_ERROR; + if (expected == error) { + successes++; + } else { + failures++; + printf("Expected %d from evaluation, but got %d instead.\n", expected, error); + } + simplicity_free(type_dag); + simplicity_free(dag); +} int main(int argc, char **argv) { while (true) { @@ -676,6 +765,7 @@ int main(int argc, char **argv) { test_program("schnorr6", schnorr6, sizeof_schnorr6, schnorr6_witness, sizeof_schnorr6_witness, SIMPLICITY_ERR_EXEC_JET, schnorr6_cmr, schnorr6_ihr, schnorr6_amr, &schnorr0_cost); test_program("typeSkipTest", typeSkipTest, sizeof_typeSkipTest, typeSkipTest_witness, sizeof_typeSkipTest_witness, SIMPLICITY_NO_ERROR, NULL, NULL, NULL, NULL); test_elements(); + exactBudget_test(); regression_tests(); iden8mebi_test(); diff --git a/typeInference.c b/typeInference.c index 89f09232626..e8359d5e934 100644 --- a/typeInference.c +++ b/typeInference.c @@ -3,7 +3,6 @@ #include #include "bounded.h" #include "limitations.h" -#include "primitive.h" #include "simplicity_alloc.h" #include "simplicity_assert.h" @@ -559,7 +558,7 @@ static simplicity_err freezeTypes(type* type_dag, dag_node* dag, unification_arr * or 'dag' is well-typed with '*type_dag' and without witness values * if the return value is not 'SIMPLICITY_NO_ERROR' then 'NULL == *type_dag' */ -simplicity_err simplicity_mallocTypeInference(type** type_dag, dag_node* dag, const uint_fast32_t len, const combinator_counters* census) { +simplicity_err simplicity_mallocTypeInference(type** type_dag, simplicity_callback_mallocBoundVars mallocBoundVars, dag_node* dag, const uint_fast32_t len, const combinator_counters* census) { *type_dag = NULL; static_assert(DAG_LEN_MAX <= SIZE_MAX / sizeof(unification_arrow), "arrow array too large."); static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero."); @@ -569,7 +568,7 @@ simplicity_err simplicity_mallocTypeInference(type** type_dag, dag_node* dag, co unification_arrow* arrow = simplicity_malloc(len * sizeof(unification_arrow)); unification_var* bound_var = NULL; size_t word256_ix, extra_var_start; - const size_t orig_bindings_used = simplicity_mallocBoundVars(&bound_var, &word256_ix, &extra_var_start, max_extra_vars(census)); + const size_t orig_bindings_used = mallocBoundVars(&bound_var, &word256_ix, &extra_var_start, max_extra_vars(census)); size_t bindings_used = orig_bindings_used; static_assert(1 <= NUMBER_OF_TYPENAMES_MAX, "NUMBER_OF_TYPENAMES_MAX is zero."); diff --git a/typeInference.h b/typeInference.h index dd8ccf35fde..3988d08b461 100644 --- a/typeInference.h +++ b/typeInference.h @@ -71,6 +71,26 @@ struct unification_var { bool isBound; }; +/* Allocate a fresh set of unification variables bound to at least all the types necessary + * for all the jets that can be created by 'simplicity_callbac_decodeJet', and also the type 'TWO^256', + * and also allocate space for 'extra_var_len' many unification variables. + * Return the number of non-trivial bindings created. + * + * However, if malloc fails, then return 0. + * + * Precondition: NULL != bound_var; + * NULL != word256_ix; + * NULL != extra_var_start; + * extra_var_len <= 6*DAG_LEN_MAX; + * + * Postcondition: Either '*bound_var == NULL' and the function returns 0 + * or 'unification_var (*bound_var)[*extra_var_start + extra_var_len]' is an array of unification variables + * such that for any 'jet : A |- B' there is some 'i < *extra_var_start' and 'j < *extra_var_start' such that + * '(*bound_var)[i]' is bound to 'A' and '(*bound_var)[j]' is bound to 'B' + * and, '*word256_ix < *extra_var_start' and '(*bound_var)[*word256_ix]' is bound the type 'TWO^256' + */ +typedef size_t (*simplicity_callback_mallocBoundVars)(unification_var** bound_var, size_t* word256_ix, size_t* extra_var_start, size_t extra_var_len); + /* If the Simplicity DAG, 'dag', has a principal type (including constraints due to sharing of subexpressions), * then allocate a well-formed type DAG containing all the types needed for all the subexpressions of 'dag', * with all free type variables instantiated at ONE, and set '*type_dag' to this allocation, @@ -91,6 +111,6 @@ struct unification_var { * or 'dag' is well-typed with '*type_dag' and without witness values * if the return value is not 'SIMPLICITY_NO_ERROR' then 'NULL == *type_dag' */ -simplicity_err simplicity_mallocTypeInference(type** type_dag, dag_node* dag, const uint_fast32_t len, const combinator_counters* census); +simplicity_err simplicity_mallocTypeInference(type** type_dag, simplicity_callback_mallocBoundVars mallocBoundVars, dag_node* dag, const uint_fast32_t len, const combinator_counters* census); #endif From 0a0a69f315893cc2e91301ad1f6ed47b46ded642 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Wed, 18 Jun 2025 11:41:44 -0400 Subject: [PATCH 03/19] Update Simplicity integeration This Simplicity update has - renamed a few types - moved some files around - added a minCost parameter, currently set to 0 - added a new error code --- .../libelementssimplicity.vcxproj | 13 ++++++----- src/script/interpreter.cpp | 17 +++++++------- src/script/interpreter.h | 8 +++---- src/script/script_error.cpp | 2 ++ src/script/script_error.h | 1 + src/test/fuzz/simplicity.cpp | 22 +++++++++---------- src/test/fuzz/simplicity_compute_amr.c | 13 ++++++----- src/test/fuzz/simplicity_tx.cpp | 4 ++-- test/sanitizer_suppressions/ubsan | 2 +- 9 files changed, 44 insertions(+), 38 deletions(-) diff --git a/build_msvc/libelementssimplicity/libelementssimplicity.vcxproj b/build_msvc/libelementssimplicity/libelementssimplicity.vcxproj index f975f8316dd..068b9316ada 100644 --- a/build_msvc/libelementssimplicity/libelementssimplicity.vcxproj +++ b/build_msvc/libelementssimplicity/libelementssimplicity.vcxproj @@ -9,7 +9,6 @@ - @@ -20,11 +19,13 @@ - - - - - + + + + + + + diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 81b72882e40..d01d4a1a874 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -2601,8 +2601,8 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector&& spent m_spent_output_spk_single_hashes = GetSpentScriptPubKeysSHA256(m_spent_outputs); m_output_spk_single_hashes = GetOutputScriptPubKeysSHA256(txTo); - std::vector simplicityRawAnnex(txTo.witness.vtxinwit.size()); - std::vector simplicityRawInput(txTo.vin.size()); + std::vector simplicityRawAnnex(txTo.witness.vtxinwit.size()); + std::vector simplicityRawInput(txTo.vin.size()); for (size_t i = 0; i < txTo.vin.size(); ++i) { simplicityRawInput[i].prevTxid = txTo.vin[i].prevout.hash.begin(); simplicityRawInput[i].prevIx = txTo.vin[i].prevout.n; @@ -2639,7 +2639,7 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector&& spent } } - std::vector simplicityRawOutput(txTo.vout.size()); + std::vector simplicityRawOutput(txTo.vout.size()); for (size_t i = 0; i < txTo.vout.size(); ++i) { simplicityRawOutput[i].asset = txTo.vout[i].nAsset.vchCommitment.empty() ? NULL : txTo.vout[i].nAsset.vchCommitment.data(); simplicityRawOutput[i].value = txTo.vout[i].nValue.vchCommitment.empty() ? NULL : txTo.vout[i].nValue.vchCommitment.data(); @@ -2659,7 +2659,7 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector&& spent } } - rawTransaction simplicityRawTx; + rawElementsTransaction simplicityRawTx; uint256 rawHash = txTo.GetHash(); simplicityRawTx.txid = rawHash.begin(); simplicityRawTx.input = simplicityRawInput.data(); @@ -3114,14 +3114,14 @@ uint32_t GenericTransactionSignatureChecker::GetnIn() const } template -bool GenericTransactionSignatureChecker::CheckSimplicity(const valtype& program, const valtype& witness, const rawTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const +bool GenericTransactionSignatureChecker::CheckSimplicity(const valtype& program, const valtype& witness, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const { simplicity_err error; - tapEnv* simplicityTapEnv = simplicity_elements_mallocTapEnv(&simplicityRawTap); + elementsTapEnv* simplicityTapEnv = simplicity_elements_mallocTapEnv(&simplicityRawTap); assert(txdata->m_simplicity_tx_data); assert(simplicityTapEnv); - if (!simplicity_elements_execSimplicity(&error, 0, txdata->m_simplicity_tx_data.get(), nIn, simplicityTapEnv, txdata->m_hash_genesis_block.data(), budget, 0, program.data(), program.size(), witness.data(), witness.size())) { + if (!simplicity_elements_execSimplicity(&error, 0, txdata->m_simplicity_tx_data.get(), nIn, simplicityTapEnv, txdata->m_hash_genesis_block.data(), 0, budget, 0, program.data(), program.size(), witness.data(), witness.size())) { assert(!"simplicity_elements_execSimplicity internal error"); } simplicity_elements_freeTapEnv(simplicityTapEnv); @@ -3154,6 +3154,7 @@ bool GenericTransactionSignatureChecker::CheckSimplicity(const valtype& progr case SIMPLICITY_ERR_ANTIDOS: return set_error(serror, SCRIPT_ERR_SIMPLICITY_ANTIDOS); case SIMPLICITY_ERR_HIDDEN_ROOT: return set_error(serror, SCRIPT_ERR_SIMPLICITY_HIDDEN_ROOT); case SIMPLICITY_ERR_AMR: return set_error(serror, SCRIPT_ERR_SIMPLICITY_AMR); + case SIMPLICITY_ERR_OVERWEIGHT: return set_error(serror, SCRIPT_ERR_SIMPLICITY_OVERWEIGHT); default: return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); } } @@ -3312,7 +3313,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const valtype& simplicity_program = SpanPopBack(stack); const valtype& simplicity_witness = SpanPopBack(stack); const int64_t budget = ::GetSerializeSize(witness.stack, PROTOCOL_VERSION) + VALIDATION_WEIGHT_OFFSET; - rawTapEnv simplicityRawTap; + rawElementsTapEnv simplicityRawTap; simplicityRawTap.controlBlock = control.data(); simplicityRawTap.pathLen = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE; simplicityRawTap.scriptCMR = script_bytes.data(); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e54e15678e6..6ec8339d9c4 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -171,12 +171,12 @@ bool CheckSignatureEncoding(const std::vector &vchSig, unsigned i struct SimplicityTransactionDeleter { - void operator()(transaction* ptr) + void operator()(elementsTransaction* ptr) { simplicity_elements_freeTransaction(ptr); } }; -using SimplicityTransactionUniquePtr = std::unique_ptr; +using SimplicityTransactionUniquePtr = std::unique_ptr; struct PrecomputedTransactionData { @@ -345,7 +345,7 @@ class BaseSignatureChecker return std::numeric_limits::max(); } - virtual bool CheckSimplicity(const std::vector& witness, const std::vector& program, const rawTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const + virtual bool CheckSimplicity(const std::vector& witness, const std::vector& program, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const { return false; } @@ -393,7 +393,7 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker const PrecomputedTransactionData* GetPrecomputedTransactionData() const override; uint32_t GetnIn() const override; - bool CheckSimplicity(const std::vector& program, const std::vector& witness, const rawTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const override; + bool CheckSimplicity(const std::vector& program, const std::vector& witness, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const override; }; using TransactionSignatureChecker = GenericTransactionSignatureChecker; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index a50888dd8f9..b79c9e7b347 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -180,6 +180,8 @@ std::string ScriptErrorString(const ScriptError serror) return SIMPLICITY_ERR_MSG(SIMPLICITY_ERR_HIDDEN_ROOT); case SCRIPT_ERR_SIMPLICITY_AMR: return SIMPLICITY_ERR_MSG(SIMPLICITY_ERR_AMR); + case SCRIPT_ERR_SIMPLICITY_OVERWEIGHT: + return SIMPLICITY_ERR_MSG(SIMPLICITY_ERR_OVERWEIGHT); case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index 8fac6496d08..0285255e1e7 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -122,6 +122,7 @@ typedef enum ScriptError_t SCRIPT_ERR_SIMPLICITY_ANTIDOS, SCRIPT_ERR_SIMPLICITY_HIDDEN_ROOT, SCRIPT_ERR_SIMPLICITY_AMR, + SCRIPT_ERR_SIMPLICITY_OVERWEIGHT, /* Must go last */ SCRIPT_ERR_ERROR_COUNT diff --git a/src/test/fuzz/simplicity.cpp b/src/test/fuzz/simplicity.cpp index 25722a5f3ce..363ee93ac52 100644 --- a/src/test/fuzz/simplicity.cpp +++ b/src/test/fuzz/simplicity.cpp @@ -5,7 +5,7 @@ #include #include extern "C" { -#include +#include #include #include } @@ -31,9 +31,9 @@ static std::vector TAPROOT_ANNEX(99, 0x50); // Defined in simplicity_compute_amr.c extern "C" { -bool simplicity_computeAmr( simplicity_err* error, unsigned char* amr - , const unsigned char* program, size_t program_len - , const unsigned char* witness, size_t witness_len); +bool simplicity_elements_computeAmr( simplicity_err* error, unsigned char* amr + , const unsigned char* program, size_t program_len + , const unsigned char* witness, size_t witness_len); } void initialize_simplicity() @@ -149,8 +149,8 @@ FUZZ_TARGET_INIT(simplicity, initialize_simplicity) simplicity_err error; unsigned char cmr[32]; unsigned char amr[32]; - assert(simplicity_computeAmr(&error, amr, prog_data, prog_data_len, wit_data, wit_data_len)); - assert(simplicity_computeCmr(&error, cmr, prog_data, prog_data_len)); + assert(simplicity_elements_computeAmr(&error, amr, prog_data, prog_data_len, wit_data, wit_data_len)); + assert(simplicity_elements_computeCmr(&error, cmr, prog_data, prog_data_len)); // The remainder is just copy/pasted from the original fuzztest @@ -196,7 +196,7 @@ FUZZ_TARGET_INIT(simplicity, initialize_simplicity) } // 5. Set up Simplicity environment and tx environment - rawTapEnv simplicityRawTap; + rawElementsTapEnv simplicityRawTap; simplicityRawTap.controlBlock = TAPROOT_CONTROL.data(); simplicityRawTap.pathLen = (TAPROOT_CONTROL.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE; simplicityRawTap.scriptCMR = cmr; @@ -210,15 +210,15 @@ FUZZ_TARGET_INIT(simplicity, initialize_simplicity) unsigned char imr_out[32]; unsigned char *imr = mtx.vin[0].prevout.hash.data()[2] & 2 ? imr_out : NULL; - const transaction* tx = txdata.m_simplicity_tx_data.get(); - tapEnv* taproot = simplicity_elements_mallocTapEnv(&simplicityRawTap); - simplicity_elements_execSimplicity(&error, imr, tx, nIn, taproot, GENESIS_HASH.data(), budget, amr, prog_bytes.data(), prog_bytes.size(), wit_bytes.data(), wit_bytes.size()); + const elementsTransaction* tx = txdata.m_simplicity_tx_data.get(); + elementsTapEnv* taproot = simplicity_elements_mallocTapEnv(&simplicityRawTap); + simplicity_elements_execSimplicity(&error, imr, tx, nIn, taproot, GENESIS_HASH.data(), 0, budget, amr, prog_bytes.data(), prog_bytes.size(), wit_bytes.data(), wit_bytes.size()); // 5. Secondary test -- try flipping a bunch of bits and check that this doesn't mess things up for (size_t j = 0; j < 8 * prog_bytes.size(); j++) { if (j > 32 && j % 23 != 0) continue; // skip most bits so this test doesn't overwhelm the fuzz time prog_bytes.data()[j / 8] ^= (1 << (j % 8)); - simplicity_elements_execSimplicity(&error, imr, tx, nIn, taproot, GENESIS_HASH.data(), budget, amr, prog_bytes.data(), prog_bytes.size(), wit_bytes.data(), wit_bytes.size()); + simplicity_elements_execSimplicity(&error, imr, tx, nIn, taproot, GENESIS_HASH.data(), 0, budget, amr, prog_bytes.data(), prog_bytes.size(), wit_bytes.data(), wit_bytes.size()); } // 6. Cleanup diff --git a/src/test/fuzz/simplicity_compute_amr.c b/src/test/fuzz/simplicity_compute_amr.c index dfa0450e174..3e8d9f93818 100644 --- a/src/test/fuzz/simplicity_compute_amr.c +++ b/src/test/fuzz/simplicity_compute_amr.c @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include +#include #include #include // simplicity_decodeMallocDag #include // DAG_LEN_MAX @@ -10,11 +10,12 @@ #include // simplicity_mallocTypeInference #include #include +#include // Copy of computeCmr used for AMR -bool simplicity_computeAmr( simplicity_err* error, unsigned char* amr - , const unsigned char* program, size_t program_len - , const unsigned char* witness, size_t witness_len) { +bool simplicity_elements_computeAmr( simplicity_err* error, unsigned char* amr + , const unsigned char* program, size_t program_len + , const unsigned char* witness, size_t witness_len) { simplicity_assert(NULL != error); simplicity_assert(NULL != amr); simplicity_assert(NULL != program || 0 == program_len); @@ -23,7 +24,7 @@ bool simplicity_computeAmr( simplicity_err* error, unsigned char* amr bitstream stream = initializeBitstream(program, program_len); dag_node* dag = NULL; combinator_counters census; - int_fast32_t dag_len = simplicity_decodeMallocDag(&dag, &census, &stream); + int_fast32_t dag_len = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, &census, &stream); if (dag_len <= 0) { simplicity_assert(dag_len < 0); *error = (simplicity_err)dag_len; @@ -34,7 +35,7 @@ bool simplicity_computeAmr( simplicity_err* error, unsigned char* amr type* type_dag = NULL; if (IS_OK(*error)) { - *error = simplicity_mallocTypeInference(&type_dag, dag, (uint_fast32_t)dag_len, &census); + *error = simplicity_mallocTypeInference(&type_dag, simplicity_elements_mallocBoundVars, dag, (uint_fast32_t)dag_len, &census); } bitstream witness_stream; if (IS_OK(*error)) { diff --git a/src/test/fuzz/simplicity_tx.cpp b/src/test/fuzz/simplicity_tx.cpp index a5bf99552ba..f5b8b4aa849 100644 --- a/src/test/fuzz/simplicity_tx.cpp +++ b/src/test/fuzz/simplicity_tx.cpp @@ -7,7 +7,7 @@ #include