Merge branch 'master' into elem-23.3.1-rc2

This commit is contained in:
Pablo Greco 2025-10-07 07:53:22 -07:00
commit afb63a43a6
63 changed files with 714 additions and 359 deletions

View file

@ -9,7 +9,6 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\..\src\simplicity\bitstream.c" />
<ClCompile Include="..\..\src\simplicity\cmr.c" />
<ClCompile Include="..\..\src\simplicity\dag.c" />
<ClCompile Include="..\..\src\simplicity\deserialize.c" />
<ClCompile Include="..\..\src\simplicity\eval.c" />
@ -20,11 +19,13 @@
<ClCompile Include="..\..\src\simplicity\sha256.c" />
<ClCompile Include="..\..\src\simplicity\type.c" />
<ClCompile Include="..\..\src\simplicity\typeInference.c" />
<ClCompile Include="..\..\src\simplicity\primitive\elements\env.c" />
<ClCompile Include="..\..\src\simplicity\primitive\elements\exec.c" />
<ClCompile Include="..\..\src\simplicity\primitive\elements\elementsJets.c" />
<ClCompile Include="..\..\src\simplicity\primitive\elements\ops.c" />
<ClCompile Include="..\..\src\simplicity\primitive\elements\primitive.c" />
<ClCompile Include="..\..\src\simplicity\elements\cmr.c" />
<ClCompile Include="..\..\src\simplicity\elements\env.c" />
<ClCompile Include="..\..\src\simplicity\elements\exec.c" />
<ClCompile Include="..\..\src\simplicity\elements\elementsJets.c" />
<ClCompile Include="..\..\src\simplicity\elements\ops.c" />
<ClCompile Include="..\..\src\simplicity\elements\primitive.c" />
<ClCompile Include="..\..\src\simplicity\elements\txEnv.c" />
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>

View file

@ -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

View file

@ -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.

View file

@ -227,6 +227,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ protected:
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 @@ public:
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 @@ public:
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;

View file

@ -134,6 +134,7 @@ public:
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 @@ protected:
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;

View file

@ -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
//
}

View file

@ -1,4 +1,5 @@
#include <chainparams.h>
#include <confidential_validation.h>
#include <issuance.h>
#include <pegins.h>
@ -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);

View file

@ -152,11 +152,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
uint160 hash;
error_str = "";
// 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 = !(bech32::Decode(str).encoding == bech32::Encoding::INVALID);
bool is_blech32 = !(blech32::Decode(str).encoding == blech32::Encoding::INVALID);
if (!is_bech32 && !is_blech32 && DecodeBase58Check(str, data, 55)) {
// Note this will be false if it is a valid Bech32 address for a different network
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 && !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();

View file

@ -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;

View file

@ -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);

View file

@ -283,7 +283,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
CTxOut fee_out;
// Duplicate checking
std::set<CTxDestination> destinations;
std::set<std::pair<CTxDestination,CAsset>> destinations;
bool has_data{false};
std::vector<PSBTOutput> 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 {

View file

@ -2601,8 +2601,8 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
m_spent_output_spk_single_hashes = GetSpentScriptPubKeysSHA256(m_spent_outputs);
m_output_spk_single_hashes = GetOutputScriptPubKeysSHA256(txTo);
std::vector<rawBuffer> simplicityRawAnnex(txTo.witness.vtxinwit.size());
std::vector<rawInput> simplicityRawInput(txTo.vin.size());
std::vector<rawElementsBuffer> simplicityRawAnnex(txTo.witness.vtxinwit.size());
std::vector<rawElementsInput> 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<CTxOut>&& spent
}
}
std::vector<rawOutput> simplicityRawOutput(txTo.vout.size());
std::vector<rawElementsOutput> 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<CTxOut>&& spent
}
}
rawTransaction simplicityRawTx;
rawElementsTransaction simplicityRawTx;
uint256 rawHash = txTo.GetHash();
simplicityRawTx.txid = rawHash.begin();
simplicityRawTx.input = simplicityRawInput.data();
@ -3114,14 +3114,14 @@ uint32_t GenericTransactionSignatureChecker<T>::GetnIn() const
}
template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSimplicity(const valtype& program, const valtype& witness, const rawTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const
bool GenericTransactionSignatureChecker<T>::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<T>::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();

View file

@ -171,12 +171,12 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
struct SimplicityTransactionDeleter
{
void operator()(transaction* ptr)
void operator()(elementsTransaction* ptr)
{
simplicity_elements_freeTransaction(ptr);
}
};
using SimplicityTransactionUniquePtr = std::unique_ptr<transaction, SimplicityTransactionDeleter>;
using SimplicityTransactionUniquePtr = std::unique_ptr<elementsTransaction, SimplicityTransactionDeleter>;
struct PrecomputedTransactionData
{
@ -345,7 +345,7 @@ public:
return std::numeric_limits<uint32_t>::max();
}
virtual bool CheckSimplicity(const std::vector<unsigned char>& witness, const std::vector<unsigned char>& program, const rawTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const
virtual bool CheckSimplicity(const std::vector<unsigned char>& witness, const std::vector<unsigned char>& program, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const
{
return false;
}
@ -393,7 +393,7 @@ public:
const PrecomputedTransactionData* GetPrecomputedTransactionData() const override;
uint32_t GetnIn() const override;
bool CheckSimplicity(const std::vector<unsigned char>& program, const std::vector<unsigned char>& witness, const rawTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const override;
bool CheckSimplicity(const std::vector<unsigned char>& program, const std::vector<unsigned char>& witness, const rawElementsTapEnv& simplicityRawTap, int64_t budget, ScriptError* serror) const override;
};
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -2,7 +2,6 @@
#include <limits.h>
#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

View file

@ -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

View file

@ -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

View file

@ -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 <stddef.h>
#include <stdint.h>
#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

View file

@ -1,9 +1,10 @@
#include <simplicity/cmr.h>
#include <simplicity/elements/cmr.h>
#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;

View file

@ -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;
}

View file

@ -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);

View file

@ -3,12 +3,12 @@
#include <stdalign.h>
#include <stddef.h>
#include <string.h>
#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 <https://c-faq.com/ansi/constmismatch.html> 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;
}

View file

@ -3,12 +3,13 @@
#include <stdalign.h>
#include <string.h>
#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);
}

View file

@ -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,

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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 <stdbool.h>
#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

View file

@ -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. */

View file

@ -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

View file

@ -1,5 +1,5 @@
#ifndef SIMPLICITY_CMR_H
#define SIMPLICITY_CMR_H
#ifndef SIMPLICITY_ELEMENTS_CMR_H
#define SIMPLICITY_ELEMENTS_CMR_H
#include <stdbool.h>
#include <stddef.h>
@ -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

View file

@ -4,9 +4,9 @@
#include <stdbool.h>
#include <stdint.h>
/* 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

View file

@ -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);

View file

@ -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";
}

View file

@ -5,7 +5,7 @@
#include <time.h>
#include <getopt.h>
#include <simplicity/elements/exec.h>
#include <simplicity/cmr.h>
#include <simplicity/elements/cmr.h>
#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();

View file

@ -3,7 +3,6 @@
#include <stdbool.h>
#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.");

View file

@ -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

View file

@ -5,7 +5,7 @@
#include <cstdio>
#include <primitives/transaction.h>
extern "C" {
#include <simplicity/cmr.h>
#include <simplicity/elements/cmr.h>
#include <simplicity/elements/env.h>
#include <simplicity/elements/exec.h>
}
@ -31,9 +31,9 @@ static std::vector<unsigned char> 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

View file

@ -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 <simplicity/cmr.h>
#include <simplicity/elements/cmr.h>
#include <simplicity/dag.h>
#include <simplicity/deserialize.h> // simplicity_decodeMallocDag
#include <simplicity/limitations.h> // DAG_LEN_MAX
@ -10,11 +10,12 @@
#include <simplicity/typeInference.h> // simplicity_mallocTypeInference
#include <simplicity/elements/env.h>
#include <simplicity/elements/exec.h>
#include <simplicity/elements/primitive.h>
// 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)) {

View file

@ -7,7 +7,7 @@
#include <script/sigcache.h>
#include <validation.h>
extern "C" {
#include <simplicity/cmr.h>
#include <simplicity/elements/cmr.h>
#include <simplicity/elements/env.h>
#include <simplicity/elements/exec.h>
}
@ -151,7 +151,7 @@ FUZZ_TARGET_INIT(simplicity_tx, initialize_simplicity_tx)
// valid transactions will work with this code).
// Compute CMR and do some sanity checks on it (and the program)
std::vector<unsigned char> cmr(32, 0);
assert(simplicity_computeCmr(&error, cmr.data(), program.data(), program.size()));
assert(simplicity_elements_computeCmr(&error, cmr.data(), program.data(), program.size()));
if (error == SIMPLICITY_NO_ERROR) {
if (memcmp(last_cmr, cmr.data(), sizeof(last_cmr)) == 0) {
// If we have already seen this CMR this transaction, try mangling

View file

@ -722,6 +722,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
// Check unblinded issuance is in MoneyRange if configured
if (!chainparams.GetAcceptUnlimitedIssuances() && !IsIssuanceInMoneyRange(tx)) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "issuance-out-of-range", "Issuance is greater than 21 million and acceptunlimitedissuances is not enabled.");
}
// Do not work on transactions that are too small.
// A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
// Transactions smaller than this are not relayed to mitigate CVE-2017-12842 by not relaying
@ -1072,10 +1077,6 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
scriptVerifyFlags |= SCRIPT_SIGHASH_RANGEPROOF;
}
if (DeploymentActiveAfter(m_active_chainstate.m_chain.Tip(), args.m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SIMPLICITY)) {
scriptVerifyFlags |= SCRIPT_VERIFY_SIMPLICITY;
}
// Check input scripts and signatures.
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata)) {
@ -2152,12 +2153,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
nBlocksTotal++;
// Check that all non-zero coinbase outputs pay to the required destination
// Check that all non-zero policyAsset coinbase outputs pay to the required destination
const CScript& mandatory_coinbase_destination = m_params.GetConsensus().mandatory_coinbase_destination;
if (mandatory_coinbase_destination != CScript()) {
for (auto& txout : block.vtx[0]->vout) {
bool mustPay = !txout.nValue.IsExplicit() || txout.nValue.GetAmount() != 0;
if (mustPay && txout.scriptPubKey != mandatory_coinbase_destination) {
if (mustPay && txout.nAsset.GetAsset() == policyAsset && txout.scriptPubKey != mandatory_coinbase_destination) {
LogPrintf("ERROR: ConnectBlock(): Coinbase outputs didn't match required scriptPubKey\n");
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-coinbase-txos");
}

View file

@ -1322,11 +1322,14 @@ static bool CreateTransactionInternal(
// Add fee output.
if (g_con_elementsmode) {
CTxOut fee(::policyAsset, 0, CScript());
assert(fee.IsFee());
txNew.vout.push_back(fee);
if (blind_details) {
blind_details->o_pubkeys.push_back(CPubKey());
// only create fee output if non-zero fee
if (coin_selection_params.m_effective_feerate > CFeeRate()) {
CTxOut fee(::policyAsset, 0, CScript());
assert(fee.IsFee());
txNew.vout.push_back(fee);
if (blind_details) {
blind_details->o_pubkeys.push_back(CPubKey());
}
}
}
assert(nChangePosInOut != -1);
@ -1448,7 +1451,6 @@ static bool CreateTransactionInternal(
txNew = tx_blinded; // sigh, `fillBlindDetails` may have modified txNew
// Update the change position to the new tx
change_position = txNew.vout.begin() + nChangePosInOut;
int ret = BlindTransaction(blind_details->i_amount_blinds, blind_details->i_asset_blinds, blind_details->i_assets, blind_details->i_amounts, blind_details->o_amount_blinds, blind_details->o_asset_blinds, blind_details->o_pubkeys, issuance_asset_keys, issuance_token_keys, tx_blinded);
assert(ret != -1);
if (ret != blind_details->num_to_blind) {

View file

@ -3029,7 +3029,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
if (args.IsArgSet("-mintxfee")) {
std::optional<CAmount> min_tx_fee = ParseMoney(args.GetArg("-mintxfee", ""));
if (!min_tx_fee || min_tx_fee.value() == 0) {
if (!min_tx_fee) {
error = AmountErrMsg("mintxfee", args.GetArg("-mintxfee", ""));
return nullptr;
} else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) {

View file

@ -305,6 +305,7 @@ class DynaFedTest(BitcoinTestFramework):
assert comb_result["complete"]
self.nodes[0].submitblock(comb_result["hex"])
assert_equal(self.nodes[0].getblockcount(), cur_height+11)
self.sync_blocks()
def test_transition_mempool_eject(self):
self.log.info("Testing mempool (r)ejection policy on transitions...")

View file

@ -502,5 +502,29 @@ class IssuanceTest(BitcoinTestFramework):
# an "impossible" case and a confusing/generic error message.
self.nodes[1].issueasset(0, 1, False)["txid"]
# Send different assets to the same address in a transaction with createrawtransaction
# Unblinded issuance of asset
contract_hash_1 = "deadbee1"*8
contract_hash_2 = "deadbee2"*8
issued_1 = self.nodes[0].issueasset(1, 1, False, contract_hash_1)
issued_2 = self.nodes[0].issueasset(1, 1, False, contract_hash_2)
self.generate(self.nodes[0], 1)
self.sync_all()
balance = self.nodes[0].getwalletinfo()["balance"]
assert_equal(balance[issued_1["asset"]], 1)
assert_equal(balance[issued_2["asset"]], 1)
send_address = self.nodes[0].getnewaddress()
# error generated if duplicated address:asset pair
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address and asset", self.nodes[0].createrawtransaction, [], [{send_address: 1, "asset": issued_1["asset"]}, {send_address: 1, "asset": issued_1["asset"]}], 0, False)
# repeated address with different asset accepted
raw_tx = self.nodes[0].createrawtransaction([], [{send_address: 1, "asset": issued_1["asset"]}, {send_address: 1, "asset": issued_2["asset"]}], 0, False)
funded_tx = self.nodes[0].fundrawtransaction(raw_tx)["hex"]
blind_tx = self.nodes[0].blindrawtransaction(funded_tx)
signed_tx = self.nodes[0].signrawtransactionwithwallet(blind_tx)
self.nodes[0].sendrawtransaction(signed_tx["hex"])
if __name__ == '__main__':
IssuanceTest ().main ()

View file

@ -109,7 +109,6 @@ class PAKTest (BitcoinTestFramework):
# Node 1 will now make a PAK peg-out, accepted in all mempools and blocks
pegout_info = self.nodes[1].sendtomainchain("", 1)
print(pegout_info)
raw_node1_pegout = self.nodes[1].gettransaction(pegout_info["txid"])["hex"]
self.sync_all() # mempool sync
self.generatetoaddress(self.nodes[1], 1, self.nodes[0].getnewaddress(), sync_fun=self.no_op)
@ -224,7 +223,6 @@ class PAKTest (BitcoinTestFramework):
peg_out_found = False
for output in wpkh_raw["vout"]:
print(output)
if "pegout_address" in output["scriptPubKey"]:
if output["scriptPubKey"]["pegout_address"] == wpkh_info["address_lookahead"][0]:
peg_out_found = True
@ -248,6 +246,8 @@ class PAKTest (BitcoinTestFramework):
for tx_id in [wpkh_txid, sh_wpkh_txid]:
assert_greater_than(self.nodes[1].gettransaction(tx_id)["confirmations"], 0)
self.sync_blocks()
self.log.info("Test that pak-less pegouts are rejected")
# Last test of a pak-less peg-out failing to get into mempool/block
@ -256,7 +256,10 @@ class PAKTest (BitcoinTestFramework):
# node 0 will now create a pegout, will fail to enter mempool of node 1 or 2
# since it's pak-less
nopak_pegout_txid = self.nodes[0].sendtomainchain("n3NkSZqoPMCQN5FENxUBw4qVATbytH6FDK", 1)
pegout_address = "n3NkSZqoPMCQN5FENxUBw4qVATbytH6FDK"
nopak_pegout_txid = self.nodes[0].sendtomainchain(pegout_address, 1)
pegout_tx = self.nodes[0].gettransaction(nopak_pegout_txid,True,True)
assert_equal(pegout_tx["decoded"]["vout"][[d["value"] for d in pegout_tx["decoded"]["vout"]].index(1)]["scriptPubKey"]["pegout_address"],pegout_address)
raw_pakless_pegout = self.nodes[0].gettransaction(nopak_pegout_txid)["hex"]
assert nopak_pegout_txid in self.nodes[0].getrawmempool()

View file

@ -41,6 +41,10 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
# generate a wallet txn
addr = node.getnewaddress()
wallet_tx_hsh = node.sendtoaddress(addr, 0.0001)
wallet_tx = node.gettransaction(wallet_tx_hsh,True,True)
# remove UTXO spent
utxos = [utxo for utxo in utxos if utxo['txid'] != wallet_tx['decoded']['vin'][0]['txid']]
# generate a txn using sendrawtransaction
us0 = utxos.pop()

View file

@ -13,7 +13,7 @@ from test_framework.util import (
BECH32_VALID = 'ert1qtmp74ayg7p24uslctssvjm06q5phz4yr7gdkdv'
BECH32_VALID_CAPITALS = 'ERT1QTMP74AYG7P24USLCTSSVJM06Q5PHZ4YR7GDKDV'
BECH32_VALID_MULTISIG = 'ert1qdg3myrgvzw7ml9q0ejxhlkyxm7vl9r56yzkfgvzclrf4hkpx9yfqhpsuks'
BECH32_VALID_MULTISIG = 'ert1qmzm84udpuz6axdstxlpwafca7g7nh5w2yu8c7vqhe0rhjkcrfcfqwymvhe'
BECH32_INVALID_BECH32 = 'ert1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqugsf3u'
BECH32_INVALID_BECH32M = 'ert1qw508d6qejxtdg4y5r3zarvary0c5xw7kfqwaud'
@ -21,19 +21,19 @@ BECH32_INVALID_VERSION = 'ert130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz
BECH32_INVALID_SIZE = 'ert1s0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav25pltc58'
BECH32_INVALID_V0_SIZE = 'ert1qw508d6qejxtdg4y5r3zarvary0c5xw7kqq2287l0'
BECH32_INVALID_PREFIX = 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'
BECH32_TOO_LONG = 'bcrt1q049edschfnwystcqnsvyfpj23mpsg3jcedq9xv049edschfnwystcqnsvyfpj23mpsg3jcedq9xv049edschfnwystcqnsvyfpj23m'
BECH32_ONE_ERROR = 'bcrt1q049edschfnwystcqnsvyfpj23mpsg3jcedq9xv'
BECH32_ONE_ERROR_CAPITALS = 'BCRT1QPLMTZKC2XHARPPZDLNPAQL78RSHJ68U32RAH7R'
BECH32_TWO_ERRORS = 'bcrt1qax9suht3qv95sw33xavx8crpxduefdrsvgsklu' # should be bcrt1qax9suht3qv95sw33wavx8crpxduefdrsvgsklx
BECH32_NO_SEPARATOR = 'bcrtq049ldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
BECH32_INVALID_CHAR = 'bcrt1q04oldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
BECH32_MULTISIG_TWO_ERRORS = 'bcrt1qdg3myrgvzw7ml8q0ejxhlkyxn7vl9r56yzkfgvzclrf4hkpx9yfqhpsuks'
BECH32_WRONG_VERSION = 'bcrt1ptmp74ayg7p24uslctssvjm06q5phz4yrxucgnv'
BECH32_TOO_LONG = 'ert1q049edschfnwystcqnsvyfpj23mpsg3jcedq9xv049edschfnwystcqnsvyfpj23mpsg3jcedq9xv049edschfnwystcqnsvyfpj23m'
BECH32_ONE_ERROR = 'ert1qtmp7aayg7p24uslctssvjm06q5phz4yr7gdkdv'
BECH32_ONE_ERROR_CAPITALS = 'ERT1QTMP7AAYG7P24USLCTSSVJM06Q5PHZ4YR7GDKDV'
BECH32_TWO_ERRORS = 'ert1qtmp74syg7p24uslctsavjm06q5phz4yr7gdkdv'
BECH32_NO_SEPARATOR = 'ertq049ldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
BECH32_INVALID_CHAR = 'ert1q04oldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
BECH32_MULTISIG_TWO_ERRORS = 'ert1qmzm84udpua6axdstxlpwafca7g7na5w2yu8c7vqhe0rhjkcrfcfqwymvhe'
BECH32_WRONG_VERSION = 'ert1ptmp74ayg7p24uslctssvjm06q5phz4yr7gdkdv'
BASE58_VALID = '2dcjQH4DQC3pMcSQkMkSQyPPEr7rZ6Ga4GR'
BASE58_INVALID_PREFIX = '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'
BASE58_INVALID_CHECKSUM = 'mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJJfn'
BASE58_INVALID_LENGTH = '2VKf7XKMrp4bVNVmuRbyCewkP8FhGLP2E54LHDPakr9Sq5mtU2'
BASE58_INVALID_LENGTH = '2dcjQH4DQC3pMcSQkMkSQyPPEr7rZ6Ga4GR7rZ6Ga4GR'
INVALID_ADDRESS = 'asfah14i8fajz0123f'
INVALID_ADDRESS_2 = '1q049ldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
@ -71,36 +71,36 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
def test_validateaddress(self):
# Invalid Bech32
self.check_invalid(BECH32_INVALID_SIZE, 'Invalid Bech32 address data size')
# self.check_invalid(BECH32_INVALID_PREFIX, 'Not a valid Bech32 or Base58 encoding') # ELEMENTS: FIXME
self.check_invalid(BECH32_INVALID_PREFIX, 'Invalid prefix for Bech32 address')
self.check_invalid(BECH32_INVALID_BECH32, 'Version 1+ witness address must use Bech32m checksum')
self.check_invalid(BECH32_INVALID_BECH32M, 'Version 0 witness address must use Bech32 checksum')
self.check_invalid(BECH32_INVALID_VERSION, 'Invalid Bech32 address witness version')
self.check_invalid(BECH32_INVALID_V0_SIZE, 'Invalid Bech32 v0 address data size')
self.check_invalid(BECH32_TOO_LONG, 'Bech32 string too long', list(range(90, 108)))
self.check_invalid(BECH32_TOO_LONG, 'Bech32 string too long', list(range(90, 107)))
self.check_invalid(BECH32_ONE_ERROR, 'Invalid Bech32 checksum', [9])
self.check_invalid(BECH32_TWO_ERRORS, 'Invalid Bech32 checksum', [22, 43])
self.check_invalid(BECH32_ONE_ERROR_CAPITALS, 'Invalid Bech32 checksum', [38])
self.check_invalid(BECH32_TWO_ERRORS, 'Invalid Bech32 checksum', [10, 23])
self.check_invalid(BECH32_ONE_ERROR_CAPITALS, 'Invalid Bech32 checksum', [9])
self.check_invalid(BECH32_NO_SEPARATOR, 'Missing separator')
self.check_invalid(BECH32_INVALID_CHAR, 'Invalid Base 32 character', [8])
self.check_invalid(BECH32_MULTISIG_TWO_ERRORS, 'Invalid Bech32 checksum', [19, 30])
self.check_invalid(BECH32_WRONG_VERSION, 'Invalid Bech32 checksum', [5])
self.check_invalid(BECH32_INVALID_CHAR, 'Invalid Base 32 character', [7])
self.check_invalid(BECH32_MULTISIG_TWO_ERRORS, 'Invalid Bech32 checksum', [14, 33])
self.check_invalid(BECH32_WRONG_VERSION, 'Invalid Bech32 checksum', [4])
# Valid Bech32
self.check_valid(BECH32_VALID)
self.check_valid(BECH32_VALID_CAPITALS)
# self.check_valid(BECH32_VALID_MULTISIG) # ELEMENTS: FIXME
self.check_valid(BECH32_VALID_MULTISIG)
# Invalid Base58
self.check_invalid(BASE58_INVALID_PREFIX, 'Invalid prefix for Base58-encoded address')
# self.check_invalid(BASE58_INVALID_CHECKSUM, 'Invalid checksum or length of Base58 address') # ELEMENTS: FIXME
# self.check_invalid(BASE58_INVALID_LENGTH, 'Invalid checksum or length of Base58 address') # ELEMENTS: FIXME
self.check_invalid(BASE58_INVALID_CHECKSUM, 'Invalid checksum or length of Base58 address')
self.check_invalid(BASE58_INVALID_LENGTH, 'Invalid checksum or length of Base58 address')
# Valid Base58
self.check_valid(BASE58_VALID)
# Invalid address format
# self.check_invalid(INVALID_ADDRESS, 'Not a valid Bech32 or Base58 encoding') # ELEMENTS: FIXME
# self.check_invalid(INVALID_ADDRESS_2, 'Not a valid Bech32 or Base58 encoding') # ELEMENTS: FIXME
self.check_invalid(INVALID_ADDRESS, 'Not a valid Bech32 or Base58 encoding')
self.check_invalid(INVALID_ADDRESS_2, 'Not a valid Bech32 or Base58 encoding')
# ELEMENTS
info = self.nodes[0].validateaddress(BLECH32_INVALID_SIZE)

View file

@ -221,7 +221,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], [{'foo': 0}])
assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], [{address: 'foo'}])
assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], [{address: -1}])
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}])
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address and asset: %s" % address, self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}])
assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], [{"data": 'aa'}, {"data": "bb"}])
assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']])

View file

@ -356,6 +356,7 @@ BASE_SCRIPTS = [
'feature_coinstatsindex.py --legacy-wallet',
'feature_coinstatsindex.py --descriptors',
'wallet_orphanedreward.py',
'wallet_send_zero_fee.py',
'wallet_timelock.py',
'p2p_node_network_limited.py',
'p2p_permissions.py',

View file

@ -7,13 +7,21 @@ from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
self.extra_args = [['-blindedaddresses=1']] * self.num_nodes
args = [
"-blindedaddresses=1"
]
self.extra_args = [
args + ["-acceptunlimitedissuances=1"],
args + ["-acceptunlimitedissuances=1"],
args, # node 2 blocks unblinded issuances out of moneyrange
]
def setup_network(self, split=False):
self.setup_nodes()
@ -42,6 +50,17 @@ class WalletTest(BitcoinTestFramework):
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance()[asset], 200_000_000)
self.log.info("Issue more than 21 million of a unblinded non-policy asset")
issuance = self.nodes[0].issueasset(300_000_000, 100, False)
unblinded_asset = issuance['asset']
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance()[unblinded_asset], 300_000_000)
self.log.info("Reissue more than 21 million of a unblinded non-policy asset")
self.nodes[0].reissueasset(unblinded_asset, 200_000_000)
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance()[unblinded_asset], 500_000_000)
# send more than 21 million of that asset
addr = self.nodes[1].getnewaddress()
self.nodes[0].sendtoaddress(address=addr, amount=22_000_000, assetlabel=asset)
@ -90,5 +109,26 @@ class WalletTest(BitcoinTestFramework):
self.nodes[2].loadwallet(self.default_wallet_name)
assert_equal(self.nodes[2].getbalance()[asset], 200_000_000)
# send some policy asset to node 2 for fees
addr = self.nodes[2].getnewaddress()
self.nodes[0].sendtoaddress(address=addr, amount=1)
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[2].getbalance()['bitcoin'], 1)
self.log.info("Issue more than 21 million of a non-policy asset on node 2 - rejected from mempool")
issuance = self.nodes[2].issueasset(300_000_000, 100, False)
asset = issuance['asset']
issuance_tx = self.nodes[2].gettransaction(issuance["txid"])
assert_raises_rpc_error(-26, "issuance-out-of-range", self.nodes[2].sendrawtransaction, issuance_tx['hex'])
self.generate(self.nodes[0], 1)
assert(asset not in self.nodes[2].getbalance())
# transaction should be accepted on node 0
self.nodes[0].sendrawtransaction(issuance_tx["hex"])
assert(issuance['txid'] in self.nodes[0].getrawmempool())
assert(issuance['txid'] not in self.nodes[2].getrawmempool())
self.generate(self.nodes[0], 1)
assert(asset not in self.nodes[0].getbalance())
assert_equal(self.nodes[2].getbalance()[asset], 300_000_000)
if __name__ == '__main__':
WalletTest().main()

View file

@ -56,6 +56,7 @@ class WalletTest(BitcoinTestFramework):
for i in range(num_utxos):
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), value, "", "", False, False, None, None, None, asset, False, fee_rate, True)
self.generate(self.nodes[0], 1)
self.sync_all()
# create a raw tx on node 1 consolidating the asset utxos
self.log.info(f"Create the raw consolidation transaction")

View file

@ -0,0 +1,60 @@
#!/usr/bin/env python3
# Copyright (c) 2017-2020 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
self.extra_args = [[
"-blindedaddresses=1",
"-minrelaytxfee=0",
"-blockmintxfee=0",
"-mintxfee=0",
]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
# initial state when setup_clean_chain is False
assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('1250')})
assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('1250')})
assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('1250')})
assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 200)
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 20)
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 201)
assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('1269.99897200')})
assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('1260')})
assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('1270')})
# send a zero fee_rate transaction, which should not add a fee output
addr = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(address=addr, amount=1, fee_rate=0)
tx = self.nodes[0].getrawtransaction(txid, 2)
# there should be no fees
assert "bitcoin" not in tx["fee"]
assert_equal(tx["fee"], {})
# and no fee output
assert_equal(len(tx["vout"]),2)
for output in tx["vout"]:
assert output["scriptPubKey"]["type"] != "fee"
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getblockchaininfo()["blocks"], 202)
assert_equal(self.nodes[0].getbalance(), {'bitcoin': Decimal('1318.99897200')})
assert_equal(self.nodes[1].getbalance(), {'bitcoin': Decimal('1261')})
assert_equal(self.nodes[2].getbalance(), {'bitcoin': Decimal('1270')})
if __name__ == '__main__':
WalletTest().main()

View file

@ -99,4 +99,4 @@ unsigned-integer-overflow:simplicity/secp256k1/group_impl.h
# This one involves careful roconnor code
shift-base:simplicity/frame.c
# See comment in simplicity/primitive/elements/env.c line 303
unsigned-integer-overflow:simplicity/primitive/elements/env.c
unsigned-integer-overflow:simplicity/elements/env.c