-
Notifications
You must be signed in to change notification settings - Fork 50
Expand file tree
/
Copy pathexec.c
More file actions
136 lines (128 loc) · 6.32 KB
/
exec.c
File metadata and controls
136 lines (128 loc) · 6.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <simplicity/bitcoin/exec.h>
#include <stdalign.h>
#include <string.h>
#include "primitive.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`.
*
* If at any time malloc fails then '*error' is set to 'SIMPLICITY_ERR_MALLOC' and 'false' is returned,
* meaning we were unable to determine the result of the simplicity program.
* 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'.
*
* Otherwise '*error' is set to 'SIMPLICITY_NO_ERROR'.
*
* 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'.
* Otherwise if 'ihr != NULL' and '*error' is not set to 'SIMPLCITY_NO_ERROR', then 'ihr' may or may not be written to.
*
* Precondition: NULL != error;
* NULL != ihr implies unsigned char ihr[32]
* NULL != tx;
* NULL != taproot;
* 0 <= minCost <= budget;
* NULL != amr implies unsigned char amr[32]
* unsigned char program[program_len]
* unsigned char witness[witness_len]
*/
extern bool simplicity_bitcoin_execSimplicity( simplicity_err* error, unsigned char* ihr
, const bitcoinTransaction* tx, uint_fast32_t ix, const bitcoinTapEnv* taproot
, 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) {
simplicity_assert(NULL != error);
simplicity_assert(NULL != tx);
simplicity_assert(NULL != taproot);
simplicity_assert(0 <= minCost);
simplicity_assert(minCost <= budget);
simplicity_assert(NULL != program || 0 == program_len);
simplicity_assert(NULL != witness || 0 == witness_len);
combinator_counters census;
dag_node* dag = NULL;
int_fast32_t dag_len;
sha256_midstate amr_hash;
if (amr) sha256_toMidstate(amr_hash.s, amr);
{
bitstream stream = initializeBitstream(program, program_len);
dag_len = simplicity_decodeMallocDag(&dag, simplicity_bitcoin_decodeJet, &census, &stream);
if (dag_len <= 0) {
simplicity_assert(dag_len < 0);
*error = (simplicity_err)dag_len;
return IS_PERMANENT(*error);
}
simplicity_assert(NULL != dag);
simplicity_assert((uint_fast32_t)dag_len <= DAG_LEN_MAX);
*error = simplicity_closeBitstream(&stream);
}
if (IS_OK(*error)) {
if (0 != memcmp(taproot->scriptCMR.s, dag[dag_len-1].cmr.s, sizeof(uint32_t[8]))) {
*error = SIMPLICITY_ERR_CMR;
}
}
if (IS_OK(*error)) {
type* type_dag = NULL;
*error = simplicity_mallocTypeInference(&type_dag, simplicity_bitcoin_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) {
*error = SIMPLICITY_ERR_TYPE_INFERENCE_NOT_PROGRAM;
}
}
if (IS_OK(*error)) {
bitstream witness_stream = initializeBitstream(witness, witness_len);
*error = simplicity_fillWitnessData(dag, type_dag, (uint_fast32_t)dag_len, &witness_stream);
if (IS_OK(*error)) {
*error = simplicity_closeBitstream(&witness_stream);
if (SIMPLICITY_ERR_BITSTREAM_TRAILING_BYTES == *error) *error = SIMPLICITY_ERR_WITNESS_TRAILING_BYTES;
if (SIMPLICITY_ERR_BITSTREAM_ILLEGAL_PADDING == *error) *error = SIMPLICITY_ERR_WITNESS_ILLEGAL_PADDING;
}
}
if (IS_OK(*error)) {
sha256_midstate ihr_buf;
*error = simplicity_verifyNoDuplicateIdentityHashes(&ihr_buf, dag, type_dag, (uint_fast32_t)dag_len);
if (IS_OK(*error) && ihr) sha256_fromMidstate(ihr, ihr_buf.s);
}
if (IS_OK(*error) && amr) {
static_assert(DAG_LEN_MAX <= SIZE_MAX / sizeof(analyses), "analysis array too large.");
static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero.");
static_assert(DAG_LEN_MAX - 1 <= UINT32_MAX, "analysis array index does nto fit in uint32_t.");
analyses *analysis = simplicity_malloc((size_t)dag_len * sizeof(analyses));
if (analysis) {
simplicity_computeAnnotatedMerkleRoot(analysis, dag, type_dag, (uint_fast32_t)dag_len);
if (0 != memcmp(amr_hash.s, analysis[dag_len-1].annotatedMerkleRoot.s, sizeof(uint32_t[8]))) {
*error = SIMPLICITY_ERR_AMR;
}
} else {
/* malloc failed which counts as a transient error. */
*error = SIMPLICITY_ERR_MALLOC;
}
simplicity_free(analysis);
}
if (IS_OK(*error)) {
txEnv env = simplicity_bitcoin_build_txEnv(tx, taproot, ix);
static_assert(BUDGET_MAX <= UBOUNDED_MAX, "BUDGET_MAX doesn't fit in ubounded.");
*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);
}
simplicity_free(dag);
return IS_PERMANENT(*error);
}