Merge pull request #1459 from ElementsProject/simplicity

Update Simplicity integeration
This commit is contained in:
Byron Hambly 2025-07-17 17:04:48 +02:00 committed by GitHub
commit b379bb2465
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 474 additions and 301 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

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

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