|
| 1 | +#include <simplicity/bitcoin/exec.h> |
| 2 | + |
| 3 | +#include <stdalign.h> |
| 4 | +#include <string.h> |
| 5 | +#include "primitive.h" |
| 6 | +#include "../deserialize.h" |
| 7 | +#include "../eval.h" |
| 8 | +#include "../limitations.h" |
| 9 | +#include "../simplicity_alloc.h" |
| 10 | +#include "../simplicity_assert.h" |
| 11 | +#include "../typeInference.h" |
| 12 | + |
| 13 | +/* Deserialize a Simplicity 'program' with its 'witness' data and execute it in the environment of the 'ix'th input of 'tx' with `taproot`. |
| 14 | + * |
| 15 | + * If at any time malloc fails then '*error' is set to 'SIMPLICITY_ERR_MALLOC' and 'false' is returned, |
| 16 | + * meaning we were unable to determine the result of the simplicity program. |
| 17 | + * Otherwise, 'true' is returned indicating that the result was successfully computed and returned in the '*error' value. |
| 18 | + * |
| 19 | + * If deserialization, analysis, or execution fails, then '*error' is set to some simplicity_err. |
| 20 | + * |
| 21 | + * If 'amr != NULL' and the annotated Merkle root of the decoded expression doesn't match 'amr' then '*error' is set to 'SIMPLICITY_ERR_AMR'. |
| 22 | + * |
| 23 | + * Otherwise '*error' is set to 'SIMPLICITY_NO_ERROR'. |
| 24 | + * |
| 25 | + * If 'ihr != NULL' and '*error' is set to 'SIMPLICITY_NO_ERROR', then the identity hash of the root of the decoded expression is written to 'ihr'. |
| 26 | + * Otherwise if 'ihr != NULL' and '*error' is not set to 'SIMPLCITY_NO_ERROR', then 'ihr' may or may not be written to. |
| 27 | + * |
| 28 | + * Precondition: NULL != error; |
| 29 | + * NULL != ihr implies unsigned char ihr[32] |
| 30 | + * NULL != tx; |
| 31 | + * NULL != taproot; |
| 32 | + * 0 <= budget; |
| 33 | + * NULL != amr implies unsigned char amr[32] |
| 34 | + * unsigned char program[program_len] |
| 35 | + * unsigned char witness[witness_len] |
| 36 | + */ |
| 37 | +extern bool simplicity_bitcoin_execSimplicity( simplicity_err* error, unsigned char* ihr |
| 38 | + , const transaction* tx, uint_fast32_t ix, const tapEnv* taproot |
| 39 | + , int64_t budget |
| 40 | + , const unsigned char* amr |
| 41 | + , const unsigned char* program, size_t program_len |
| 42 | + , const unsigned char* witness, size_t witness_len) { |
| 43 | + simplicity_assert(NULL != error); |
| 44 | + simplicity_assert(NULL != tx); |
| 45 | + simplicity_assert(NULL != taproot); |
| 46 | + simplicity_assert(0 <= budget); |
| 47 | + simplicity_assert(NULL != program || 0 == program_len); |
| 48 | + simplicity_assert(NULL != witness || 0 == witness_len); |
| 49 | + |
| 50 | + combinator_counters census; |
| 51 | + dag_node* dag = NULL; |
| 52 | + int_fast32_t dag_len; |
| 53 | + sha256_midstate amr_hash; |
| 54 | + |
| 55 | + if (amr) sha256_toMidstate(amr_hash.s, amr); |
| 56 | + |
| 57 | + { |
| 58 | + bitstream stream = initializeBitstream(program, program_len); |
| 59 | + dag_len = simplicity_decodeMallocDag(&dag, simplicity_bitcoin_decodeJet, &census, &stream); |
| 60 | + if (dag_len <= 0) { |
| 61 | + simplicity_assert(dag_len < 0); |
| 62 | + *error = (simplicity_err)dag_len; |
| 63 | + return IS_PERMANENT(*error); |
| 64 | + } |
| 65 | + simplicity_assert(NULL != dag); |
| 66 | + simplicity_assert((uint_fast32_t)dag_len <= DAG_LEN_MAX); |
| 67 | + *error = simplicity_closeBitstream(&stream); |
| 68 | + } |
| 69 | + |
| 70 | + if (IS_OK(*error)) { |
| 71 | + if (0 != memcmp(taproot->scriptCMR.s, dag[dag_len-1].cmr.s, sizeof(uint32_t[8]))) { |
| 72 | + *error = SIMPLICITY_ERR_CMR; |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + if (IS_OK(*error)) { |
| 77 | + type* type_dag = NULL; |
| 78 | + *error = simplicity_mallocTypeInference(&type_dag, simplicity_bitcoin_mallocBoundVars, dag, (uint_fast32_t)dag_len, &census); |
| 79 | + if (IS_OK(*error)) { |
| 80 | + simplicity_assert(NULL != type_dag); |
| 81 | + if (0 != dag[dag_len-1].sourceType || 0 != dag[dag_len-1].targetType) { |
| 82 | + *error = SIMPLICITY_ERR_TYPE_INFERENCE_NOT_PROGRAM; |
| 83 | + } |
| 84 | + } |
| 85 | + if (IS_OK(*error)) { |
| 86 | + bitstream witness_stream = initializeBitstream(witness, witness_len); |
| 87 | + *error = simplicity_fillWitnessData(dag, type_dag, (uint_fast32_t)dag_len, &witness_stream); |
| 88 | + if (IS_OK(*error)) { |
| 89 | + *error = simplicity_closeBitstream(&witness_stream); |
| 90 | + if (SIMPLICITY_ERR_BITSTREAM_TRAILING_BYTES == *error) *error = SIMPLICITY_ERR_WITNESS_TRAILING_BYTES; |
| 91 | + if (SIMPLICITY_ERR_BITSTREAM_ILLEGAL_PADDING == *error) *error = SIMPLICITY_ERR_WITNESS_ILLEGAL_PADDING; |
| 92 | + } |
| 93 | + } |
| 94 | + if (IS_OK(*error)) { |
| 95 | + sha256_midstate ihr_buf; |
| 96 | + *error = simplicity_verifyNoDuplicateIdentityHashes(&ihr_buf, dag, type_dag, (uint_fast32_t)dag_len); |
| 97 | + if (IS_OK(*error) && ihr) sha256_fromMidstate(ihr, ihr_buf.s); |
| 98 | + } |
| 99 | + if (IS_OK(*error) && amr) { |
| 100 | + static_assert(DAG_LEN_MAX <= SIZE_MAX / sizeof(analyses), "analysis array too large."); |
| 101 | + static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero."); |
| 102 | + static_assert(DAG_LEN_MAX - 1 <= UINT32_MAX, "analysis array index does nto fit in uint32_t."); |
| 103 | + analyses *analysis = simplicity_malloc((size_t)dag_len * sizeof(analyses)); |
| 104 | + if (analysis) { |
| 105 | + simplicity_computeAnnotatedMerkleRoot(analysis, dag, type_dag, (uint_fast32_t)dag_len); |
| 106 | + if (0 != memcmp(amr_hash.s, analysis[dag_len-1].annotatedMerkleRoot.s, sizeof(uint32_t[8]))) { |
| 107 | + *error = SIMPLICITY_ERR_AMR; |
| 108 | + } |
| 109 | + } else { |
| 110 | + /* malloc failed which counts as a transient error. */ |
| 111 | + *error = SIMPLICITY_ERR_MALLOC; |
| 112 | + } |
| 113 | + simplicity_free(analysis); |
| 114 | + } |
| 115 | + if (IS_OK(*error)) { |
| 116 | + txEnv env = simplicity_bitcoin_build_txEnv(tx, taproot, ix); |
| 117 | + static_assert(BUDGET_MAX <= UBOUNDED_MAX, "BUDGET_MAX doesn't fit in ubounded."); |
| 118 | + *error = evalTCOProgram(dag, type_dag, (size_t)dag_len, &(ubounded){budget <= BUDGET_MAX ? (ubounded)budget : BUDGET_MAX}, &env); |
| 119 | + } |
| 120 | + simplicity_free(type_dag); |
| 121 | + } |
| 122 | + |
| 123 | + simplicity_free(dag); |
| 124 | + return IS_PERMANENT(*error); |
| 125 | +} |
0 commit comments