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/ci/test/04_install.sh b/ci/test/04_install.sh index a555d94c185..5e778f10e3b 100755 --- a/ci/test/04_install.sh +++ b/ci/test/04_install.sh @@ -11,7 +11,7 @@ if [[ $QEMU_USER_CMD == qemu-s390* ]]; then fi if [ "$CI_OS_NAME" == "macos" ]; then - sudo -H pip3 install --upgrade --break-system-packages pip + sudo -H pip3 install --upgrade --break-system-packages --ignore-installed pip # shellcheck disable=SC2086 IN_GETOPT_BIN="$(brew --prefix gnu-getopt)/bin/getopt" ${CI_RETRY_EXE} pip3 install --user $PIP_PACKAGES fi diff --git a/configure.ac b/configure.ac index af56c9eb70f..4f3990469b3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.69]) define(_CLIENT_VERSION_MAJOR, 23) define(_CLIENT_VERSION_MINOR, 3) define(_CLIENT_VERSION_BUILD, 1) -define(_CLIENT_VERSION_RC, 1) +define(_CLIENT_VERSION_RC, 2) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2025) define(_COPYRIGHT_HOLDERS,[The %s developers]) diff --git a/doc/tapscript_opcodes.md b/doc/tapscript_opcodes.md index 7e673e1af8e..7e365d46c66 100644 --- a/doc/tapscript_opcodes.md +++ b/doc/tapscript_opcodes.md @@ -17,7 +17,7 @@ Taproot already increases a lot of resource limitations from segwitv0, so there 1. **Streaming Opcodes for streaming hashes**: There is an existing limitation of `MAX_SCRIPT_ELEMENT_SIZE`(520 bytes) because of which we cannot operate hash functions like `OP_SHA256` on messages more than 520 bytes. This allows hashing on more than 520 bytes while still preserving the existing security against resource exhaustion attacks. The proposal for this by Russell O'Connor can be found in the description [here](https://github.com/ElementsProject/elements/pull/817). 1. Define `OP_SUCCESS196` as `OP_SHA256INITIALIZE` which pops a bytestring and push SHA256 context creating by adding the bytestring to the initial SHA256 context. 1. Define `OP_SUCCESS197` as `OP_SHA256UPDATE` which first pops a bytestring followed by another pop for SHA256 context and pushes an updated context by adding the bytestring to the data stream being hashed. - 1. Define `OP_SUCCESS198` as `OP_SHA256FINALIZE` which first pops a pops a bytestring followed by another pop for SHA256 context and finally pushes a SHA256 hash value after adding the bytestring and completing the padding. + 1. Define `OP_SUCCESS198` as `OP_SHA256FINALIZE` which first pops a bytestring followed by another pop for SHA256 context and finally pushes a SHA256 hash value after adding the bytestring and completing the padding. 2. **Transaction Introspection codes**: Transaction introspection is already possible in elements script by use of `OP_CHECKSIGFROMSTACKVERIFY`, however the current solutions are really expensive in applications like [covenants](https://github.com/sanket1729/covenants-demo). Therefore, we are not adding any new functionality by supporting introspection, only making it easier to use. The warning still remains the same as with covenants, if the user is inspecting data from parts of the transaction that are not signed, the script can cause unexpected behavior. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 02ee9145476..a2b4c418af2 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -227,6 +227,7 @@ class CMainParams : public CChainParams { consensus.connect_genesis_outputs = false; consensus.subsidy_asset = CAsset(); anyonecanspend_aremine = false; + accept_unlimited_issuances = false; enforce_pak = false; multi_data_permitted = false; accept_discount_ct = false; @@ -374,6 +375,7 @@ class CTestNetParams : public CChainParams { consensus.subsidy_asset = CAsset(); anyonecanspend_aremine = false; enforce_pak = false; + accept_unlimited_issuances = false; multi_data_permitted = false; accept_discount_ct = false; create_discount_ct = false; @@ -538,6 +540,7 @@ class SigNetParams : public CChainParams { consensus.subsidy_asset = CAsset(); anyonecanspend_aremine = false; enforce_pak = false; + accept_unlimited_issuances = false; multi_data_permitted = false; accept_discount_ct = false; create_discount_ct = false; @@ -641,6 +644,7 @@ class CRegTestParams : public CChainParams { consensus.subsidy_asset = CAsset(); anyonecanspend_aremine = false; enforce_pak = false; + accept_unlimited_issuances = false; multi_data_permitted = false; accept_discount_ct = false; create_discount_ct = false; @@ -909,6 +913,8 @@ class CCustomParams : public CRegTestParams { enforce_pak = args.GetBoolArg("-enforce_pak", false); + accept_unlimited_issuances = args.GetBoolArg("-acceptunlimitedissuances", accept_unlimited_issuances); + // Allow multiple op_return outputs by relay policy multi_data_permitted = args.GetBoolArg("-multi_data_permitted", enforce_pak); @@ -1167,6 +1173,8 @@ class CLiquidV1Params : public CChainParams { enforce_pak = true; + accept_unlimited_issuances = false; + multi_data_permitted = true; create_discount_ct = args.GetBoolArg("-creatediscountct", false); accept_discount_ct = args.GetBoolArg("-acceptdiscountct", true) || create_discount_ct; @@ -1525,6 +1533,8 @@ class CLiquidV1TestParams : public CLiquidV1Params { enforce_pak = args.GetBoolArg("-enforce_pak", enforce_pak); + accept_unlimited_issuances = args.GetBoolArg("-acceptunlimitedissuances", accept_unlimited_issuances); + multi_data_permitted = args.GetBoolArg("-multi_data_permitted", multi_data_permitted); create_discount_ct = args.GetBoolArg("-creatediscountct", create_discount_ct); accept_discount_ct = args.GetBoolArg("-acceptdiscountct", accept_discount_ct) || create_discount_ct; diff --git a/src/chainparams.h b/src/chainparams.h index a80807a8eba..840a22ba5c4 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -134,6 +134,7 @@ class CChainParams const std::string& ParentBech32HRP() const { return parent_bech32_hrp; } const std::string& ParentBlech32HRP() const { return parent_blech32_hrp; } bool GetEnforcePak() const { return enforce_pak; } + bool GetAcceptUnlimitedIssuances() const { return accept_unlimited_issuances; } bool GetMultiDataPermitted() const { return multi_data_permitted; } bool GetAcceptDiscountCT() const { return accept_discount_ct; } bool GetCreateDiscountCT() const { return create_discount_ct; } @@ -168,6 +169,7 @@ class CChainParams std::string parent_bech32_hrp; std::string parent_blech32_hrp; bool enforce_pak; + bool accept_unlimited_issuances; bool multi_data_permitted; bool accept_discount_ct; bool create_discount_ct; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index e3eb7dd45bf..40a1078dbd9 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -61,6 +61,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-total_valid_epochs", "Per-chain parameter that sets how long a particular fedpegscript is in effect for.", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS); argsman.AddArg("-evbparams=deployment:start:end:period:threshold", "Use given start/end times for specified version bits deployment (regtest or custom only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::ELEMENTS); argsman.AddArg("-con_start_p2wsh_script", "Create p2wsh addresses when starting in dynafed mode (regtest or custom only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::ELEMENTS); + argsman.AddArg("-acceptunlimitedissuances", "Allow unblinded issuance amounts to exceed 21 million units", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS); // END ELEMENTS // } diff --git a/src/confidential_validation.cpp b/src/confidential_validation.cpp index 62190a92fd6..a1230c609bb 100644 --- a/src/confidential_validation.cpp +++ b/src/confidential_validation.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -30,8 +31,9 @@ bool HasValidFee(const CTransaction& tx) { CAmount fee = 0; if (tx.vout[i].IsFee()) { fee = tx.vout[i].nValue.GetAmount(); - if (fee == 0 || !MoneyRange(fee)) + if (fee == 0 || !MoneyRange(fee)) { return false; + } totalFee[tx.vout[i].nAsset.GetAsset()] += fee; if (!MoneyRange(totalFee)) { return false; @@ -102,14 +104,13 @@ static bool VerifyIssuanceAmount(secp256k1_pedersen_commitment& value_commit, se // Build value commitment if (value.IsExplicit()) { - if (!MoneyRange(value.GetAmount()) || value.GetAmount() == 0) { + if ((asset == Params().GetConsensus().pegged_asset && !MoneyRange(value.GetAmount())) || value.GetAmount() <= 0) { return false; } if (!rangeproof.empty()) { return false; } - ret = secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &value_commit, explicit_blinds, value.GetAmount(), &asset_gen); // The explicit_blinds are all 0, and the amount is not 0. So secp256k1_pedersen_commit does not fail. assert(ret == 1); diff --git a/src/key_io.cpp b/src/key_io.cpp index fb6d7719b38..c6064aa25a4 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -152,11 +152,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par uint160 hash; error_str = ""; + bool is_bech32 = !(bech32::Decode(str).encoding == bech32::Encoding::INVALID); + bool is_blech32 = !(blech32::Decode(str).encoding == blech32::Encoding::INVALID); + // Note this will be false if it is a valid Bech32 address for a different network - bool is_bech32 = (ToLower(str.substr(0, params.Bech32HRP().size())) == params.Bech32HRP()); - bool is_blech32 = (ToLower(str.substr(0, params.Blech32HRP().size())) == params.Blech32HRP()); + bool is_bech32_hrp = (ToLower(str.substr(0, params.Bech32HRP().size())) == params.Bech32HRP()); + bool is_blech32_hrp = (ToLower(str.substr(0, params.Blech32HRP().size())) == params.Blech32HRP()); - if (!is_bech32 && !is_blech32 && DecodeBase58Check(str, data, 55)) { + if (!is_bech32 && !is_blech32 && !is_bech32_hrp && !is_blech32_hrp && DecodeBase58Check(str, data, 55)) { // base58-encoded Bitcoin addresses. // Public-key-hash-addresses have version 0 (or 111 testnet). // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. @@ -206,14 +209,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par error_str = "Invalid prefix for Base58-encoded address"; } return CNoDestination(); - } else if (!is_bech32 && !is_blech32) { + } else if (!is_bech32 && !is_blech32 && !is_bech32_hrp && !is_blech32_hrp) { // Try Base58 decoding without the checksum, using a much larger max length if (!DecodeBase58(str, data, 100)) { error_str = "Not a valid Bech32 or Base58 encoding"; } else { error_str = "Invalid checksum or length of Base58 address"; } - // return CNoDestination(); // ELEMENTS: FIXME + return CNoDestination(); } data.clear(); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 99435154d2b..65d7bae3a55 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -308,6 +308,20 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return true; } +bool IsIssuanceInMoneyRange(const CTransaction& tx) +{ + for (size_t i = 0; i < tx.vin.size(); ++i) { + const CAssetIssuance& issuance = tx.vin[i].assetIssuance; + if (issuance.IsNull()) { + continue; + } + if (issuance.nAmount.IsExplicit() && !MoneyRange(issuance.nAmount.GetAmount())) { + return false; + } + } + return true; +} + int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop) { return (std::max(nWeight, nSigOpCost * bytes_per_sigop) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR; diff --git a/src/policy/policy.h b/src/policy/policy.h index 944d65d0c50..14390dcf540 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -80,7 +80,9 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE SCRIPT_VERIFY_TAPROOT | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION | SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE; + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE | + SCRIPT_VERIFY_SIMPLICITY; + /** For convenience, standard but not mandatory verify flags. */ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; @@ -92,6 +94,9 @@ static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_S // ELEMENTS: keep a copy of the upstream default dust relay fee rate static const unsigned int DUST_RELAY_TX_FEE_BITCOIN = 3000; +// ELEMENTS: allow unblinded issuances/reissuances greater than MAX_MONEY +static const bool DEFAULT_ACCEPT_UNLIMITED_ISSUANCES = true; + CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee); bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee); @@ -124,6 +129,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) */ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); +/* ELEMENTS +* Check if unblinded issuance/reissuance is in MoneyRange +*/ +bool IsIssuanceInMoneyRange(const CTransaction& tx); + /** Compute the virtual transaction size (weight reinterpreted as bytes). */ int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop); int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop); diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index 56a472a44c5..e41679401ac 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -283,7 +283,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal CTxOut fee_out; // Duplicate checking - std::set destinations; + std::set> destinations; bool has_data{false}; std::vector psbt_outs; @@ -297,6 +297,8 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal CTxOut out(::policyAsset, 0, CScript()); bool is_fee = false; + CTxDestination destination; + std::string dest; for (const std::string& name_ : output.getKeys()) { if (name_ == "data") { if (has_data) { @@ -337,14 +339,11 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal // For PSET psbt_out.m_blinder_index = find_value(output, name_).get_int(); } else { - CTxDestination destination = DecodeDestination(name_); + destination = DecodeDestination(name_); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); } - - if (!destinations.insert(destination).second) { - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); - } + dest = name_; CScript scriptPubKey = GetScriptForDestination(destination); CAmount nAmount = AmountFromValue(output[name_]); @@ -362,6 +361,11 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal psbt_out.m_blinding_pubkey = blind_pub; } } + + if (!destinations.emplace(destination, out.nAsset.GetAsset()).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address and asset: " + dest + " " + out.nAsset.GetHex())); + } + if (is_fee) { fee_out = out; } else { 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