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/simplicity/Makefile b/src/simplicity/Makefile index 06506b819c5..a0cb534209a 100644 --- a/src/simplicity/Makefile +++ b/src/simplicity/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/src/simplicity/deserialize.c b/src/simplicity/deserialize.c index d65c0d5c5cf..182f23ba16e 100644 --- a/src/simplicity/deserialize.c +++ b/src/simplicity/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/src/simplicity/deserialize.h b/src/simplicity/deserialize.h index d20561b011c..dd5515d3c8d 100644 --- a/src/simplicity/deserialize.h +++ b/src/simplicity/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/src/simplicity/elements-sources.mk b/src/simplicity/elements-sources.mk index 189a04983af..1c0dc4b9a9b 100644 --- a/src/simplicity/elements-sources.mk +++ b/src/simplicity/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/src/simplicity/primitive/elements/checkSigHashAllTx1.c b/src/simplicity/elements/checkSigHashAllTx1.c similarity index 100% rename from src/simplicity/primitive/elements/checkSigHashAllTx1.c rename to src/simplicity/elements/checkSigHashAllTx1.c diff --git a/src/simplicity/primitive/elements/checkSigHashAllTx1.h b/src/simplicity/elements/checkSigHashAllTx1.h similarity index 90% rename from src/simplicity/primitive/elements/checkSigHashAllTx1.h rename to src/simplicity/elements/checkSigHashAllTx1.h index b2bb7c5d9d0..c7a4582e095 100644 --- a/src/simplicity/primitive/elements/checkSigHashAllTx1.h +++ b/src/simplicity/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/src/simplicity/cmr.c b/src/simplicity/elements/cmr.c similarity index 73% rename from src/simplicity/cmr.c rename to src/simplicity/elements/cmr.c index b4a94fc7de6..dd73be4ed93 100644 --- a/src/simplicity/cmr.c +++ b/src/simplicity/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/src/simplicity/primitive/elements/decodeElementsJets.inc b/src/simplicity/elements/decodeElementsJets.inc similarity index 100% rename from src/simplicity/primitive/elements/decodeElementsJets.inc rename to src/simplicity/elements/decodeElementsJets.inc diff --git a/src/simplicity/primitive/elements/elementsJets.c b/src/simplicity/elements/elementsJets.c similarity index 99% rename from src/simplicity/primitive/elements/elementsJets.c rename to src/simplicity/elements/elementsJets.c index e958f2fffec..dfdf5be6342 100644 --- a/src/simplicity/primitive/elements/elementsJets.c +++ b/src/simplicity/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/src/simplicity/primitive/elements/elementsJets.h b/src/simplicity/elements/elementsJets.h similarity index 98% rename from src/simplicity/primitive/elements/elementsJets.h rename to src/simplicity/elements/elementsJets.h index 2043fc023bb..d6412195c6c 100644 --- a/src/simplicity/primitive/elements/elementsJets.h +++ b/src/simplicity/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/src/simplicity/primitive/elements/env.c b/src/simplicity/elements/env.c similarity index 88% rename from src/simplicity/primitive/elements/env.c rename to src/simplicity/elements/env.c index a849dc45fd4..af39d5223d9 100644 --- a/src/simplicity/primitive/elements/env.c +++ b/src/simplicity/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/src/simplicity/primitive/elements/exec.c b/src/simplicity/elements/exec.c similarity index 74% rename from src/simplicity/primitive/elements/exec.c rename to src/simplicity/elements/exec.c index e38c9ca41a5..4dfcf421056 100644 --- a/src/simplicity/primitive/elements/exec.c +++ b/src/simplicity/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/src/simplicity/primitive/elements/ops.c b/src/simplicity/elements/ops.c similarity index 100% rename from src/simplicity/primitive/elements/ops.c rename to src/simplicity/elements/ops.c diff --git a/src/simplicity/primitive/elements/ops.h b/src/simplicity/elements/ops.h similarity index 95% rename from src/simplicity/primitive/elements/ops.h rename to src/simplicity/elements/ops.h index 87e9fb3a32e..d704d64e0a7 100644 --- a/src/simplicity/primitive/elements/ops.h +++ b/src/simplicity/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/src/simplicity/primitive/elements/primitive.c b/src/simplicity/elements/primitive.c similarity index 89% rename from src/simplicity/primitive/elements/primitive.c rename to src/simplicity/elements/primitive.c index da08b920940..72e41e5b6c6 100644 --- a/src/simplicity/primitive/elements/primitive.c +++ b/src/simplicity/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/src/simplicity/primitive.h b/src/simplicity/elements/primitive.h similarity index 78% rename from src/simplicity/primitive.h rename to src/simplicity/elements/primitive.h index dc17fb0b948..16b426783e9 100644 --- a/src/simplicity/primitive.h +++ b/src/simplicity/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/src/simplicity/primitive/elements/primitiveEnumJet.inc b/src/simplicity/elements/primitiveEnumJet.inc similarity index 100% rename from src/simplicity/primitive/elements/primitiveEnumJet.inc rename to src/simplicity/elements/primitiveEnumJet.inc diff --git a/src/simplicity/primitive/elements/primitiveEnumTy.inc b/src/simplicity/elements/primitiveEnumTy.inc similarity index 100% rename from src/simplicity/primitive/elements/primitiveEnumTy.inc rename to src/simplicity/elements/primitiveEnumTy.inc diff --git a/src/simplicity/primitive/elements/primitiveInitTy.inc b/src/simplicity/elements/primitiveInitTy.inc similarity index 100% rename from src/simplicity/primitive/elements/primitiveInitTy.inc rename to src/simplicity/elements/primitiveInitTy.inc diff --git a/src/simplicity/primitive/elements/primitiveJetNode.inc b/src/simplicity/elements/primitiveJetNode.inc similarity index 100% rename from src/simplicity/primitive/elements/primitiveJetNode.inc rename to src/simplicity/elements/primitiveJetNode.inc diff --git a/src/simplicity/elements/txEnv.c b/src/simplicity/elements/txEnv.c new file mode 100644 index 00000000000..a7b9f8ef12c --- /dev/null +++ b/src/simplicity/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/src/simplicity/primitive/elements/primitive.h b/src/simplicity/elements/txEnv.h similarity index 95% rename from src/simplicity/primitive/elements/primitive.h rename to src/simplicity/elements/txEnv.h index b095107722f..f4493530de0 100644 --- a/src/simplicity/primitive/elements/primitive.h +++ b/src/simplicity/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/src/simplicity/eval.c b/src/simplicity/eval.c index b1117e8b0d5..6c9e9dc05e8 100644 --- a/src/simplicity/eval.c +++ b/src/simplicity/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/src/simplicity/eval.h b/src/simplicity/eval.h index ba6a6759057..cef5cb73b20 100644 --- a/src/simplicity/eval.h +++ b/src/simplicity/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/src/simplicity/include/simplicity/cmr.h b/src/simplicity/include/simplicity/elements/cmr.h similarity index 74% rename from src/simplicity/include/simplicity/cmr.h rename to src/simplicity/include/simplicity/elements/cmr.h index 4bde71a28e6..af3a0077940 100644 --- a/src/simplicity/include/simplicity/cmr.h +++ b/src/simplicity/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/src/simplicity/include/simplicity/elements/env.h b/src/simplicity/include/simplicity/elements/env.h index e5c683f9f2c..ef55f31bc31 100644 --- a/src/simplicity/include/simplicity/elements/env.h +++ b/src/simplicity/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/src/simplicity/include/simplicity/elements/exec.h b/src/simplicity/include/simplicity/elements/exec.h index 758df913a16..18f70709e1f 100644 --- a/src/simplicity/include/simplicity/elements/exec.h +++ b/src/simplicity/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/src/simplicity/include/simplicity/errorCodes.h b/src/simplicity/include/simplicity/errorCodes.h index 00ffee51c07..cea6e7dbc77 100644 --- a/src/simplicity/include/simplicity/errorCodes.h +++ b/src/simplicity/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/src/simplicity/test.c b/src/simplicity/test.c index 445b5726786..4ec33387d56 100644 --- a/src/simplicity/test.c +++ b/src/simplicity/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/src/simplicity/typeInference.c b/src/simplicity/typeInference.c index 89f09232626..e8359d5e934 100644 --- a/src/simplicity/typeInference.c +++ b/src/simplicity/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/src/simplicity/typeInference.h b/src/simplicity/typeInference.h index dd8ccf35fde..3988d08b461 100644 --- a/src/simplicity/typeInference.h +++ b/src/simplicity/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 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