Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ alectryon-doc
*.o
*.out
*.a
# cmake
*.cmake
CMakeCache.txt
CMakeFiles
/build/
# Coverage
*.gcda
*.gcno
Expand Down
36 changes: 36 additions & 0 deletions C/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.16)

project(BitcoinSimplicity)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_EXTENSIONS OFF)

add_library(BitcoinSimplicity STATIC
bitstream.c
dag.c
deserialize.c
eval.c
frame.c
jets-secp256k1.c
jets.c
rsort.c
sha256.c
type.c
typeInference.c
bitcoin/env.c
bitcoin/exec.c
bitcoin/bitcoinJets.c
bitcoin/ops.c
bitcoin/primitive.c
bitcoin/txEnv.c
)

option(PRODUCTION "Enable production build" ON)
if (PRODUCTION)
target_compile_definitions(BitcoinSimplicity PRIVATE "PRODUCTION")
endif()

target_include_directories(BitcoinSimplicity PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
4 changes: 2 additions & 2 deletions C/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CORE_OBJS := bitstream.o dag.o deserialize.o eval.o frame.o jets.o jets-secp256k1.o rsort.o sha256.o type.o typeInference.o
BITCOIN_OBJS := bitcoin/env.o bitcoin/ops.o bitcoin/bitcoinJets.o bitcoin/primitive.o bitcoin/txEnv.o
CORE_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
BITCOIN_OBJS := bitcoin/env.o bitcoin/exec.o bitcoin/ops.o bitcoin/bitcoinJets.o bitcoin/primitive.o bitcoin/cmr.o bitcoin/txEnv.o
ELEMENTS_OBJS := 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

Expand Down
22 changes: 22 additions & 0 deletions C/bitcoin/cmr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <simplicity/bitcoin/cmr.h>

#include "../cmr.h"
#include "primitive.h"

/* Deserialize a Simplicity 'program' and compute its CMR.
*
* Caution: no typechecking is performed, only a well-formedness check.
*
* If at any time malloc fails then '*error' is set to 'SIMPLICITY_ERR_MALLOC' and 'false' is returned,
* Otherwise, 'true' is returned indicating that the result was successfully computed and returned in the '*error' value.
*
* If the operation completes successfully then '*error' is set to 'SIMPLICITY_NO_ERROR', and the 'cmr' array is filled in with the program's computed CMR.
*
* Precondition: NULL != error;
* unsigned char cmr[32]
* unsigned char program[program_len]
*/
bool simplicity_bitcoin_computeCmr( simplicity_err* error, unsigned char* cmr
, const unsigned char* program, size_t program_len) {
return simplicity_computeCmr(error, cmr, simplicity_bitcoin_decodeJet, program, program_len);
}
136 changes: 136 additions & 0 deletions C/bitcoin/exec.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,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);
}
41 changes: 41 additions & 0 deletions C/cmr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "cmr.h"

#include "limitations.h"
#include "simplicity_alloc.h"
#include "simplicity_assert.h"

/* Deserialize a Simplicity 'program' and compute its CMR.
*
* Caution: no typechecking is performed, only a well-formedness check.
*
* If at any time malloc fails then '*error' is set to 'SIMPLICITY_ERR_MALLOC' and 'false' is returned,
* Otherwise, 'true' is returned indicating that the result was successfully computed and returned in the '*error' value.
*
* If the operation completes successfully then '*error' is set to 'SIMPLICITY_NO_ERROR', and the 'cmr' array is filled in with the program's computed CMR.
*
* Precondition: NULL != error;
* unsigned char cmr[32]
* unsigned char program[program_len]
*/
bool simplicity_computeCmr( simplicity_err* error, unsigned char* cmr, simplicity_callback_decodeJet decodeJet
, 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, decodeJet, NULL, &stream);
if (dag_len <= 0) {
simplicity_assert(dag_len < 0);
*error = (simplicity_err)dag_len;
} else {
simplicity_assert(NULL != dag);
simplicity_assert((uint_fast32_t)dag_len <= DAG_LEN_MAX);
*error = simplicity_closeBitstream(&stream);
sha256_fromMidstate(cmr, dag[dag_len-1].cmr.s);
}

simplicity_free(dag);
return IS_PERMANENT(*error);
}
24 changes: 24 additions & 0 deletions C/cmr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef SIMPLICITY_CMR_H
#define SIMPLICITY_CMR_H

#include <stdbool.h>
#include <stddef.h>
#include <simplicity/errorCodes.h>
#include "deserialize.h"

/* Deserialize a Simplicity 'program' and compute its CMR.
*
* Caution: no typechecking is performed, only a well-formedness check.
*
* If at any time malloc fails then '*error' is set to 'SIMPLICITY_ERR_MALLOC' and 'false' is returned,
* Otherwise, 'true' is returned indicating that the result was successfully computed and returned in the '*error' value.
*
* If the operation completes successfully then '*error' is set to 'SIMPLICITY_NO_ERROR', and the 'cmr' array is filled in with the program's computed CMR.
*
* Precondition: NULL != error;
* unsigned char cmr[32]
* unsigned char program[program_len]
*/
extern bool simplicity_computeCmr( simplicity_err* error, unsigned char* cmr, simplicity_callback_decodeJet decodeJet
, const unsigned char* program, size_t program_len);
#endif
25 changes: 2 additions & 23 deletions C/elements/cmr.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#include <simplicity/elements/cmr.h>

#include "../deserialize.h"
#include "../limitations.h"
#include "../simplicity_alloc.h"
#include "../simplicity_assert.h"
#include "../cmr.h"
#include "primitive.h"

/* Deserialize a Simplicity 'program' and compute its CMR.
Expand All @@ -21,23 +18,5 @@
*/
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, simplicity_elements_decodeJet, NULL, &stream);
if (dag_len <= 0) {
simplicity_assert(dag_len < 0);
*error = (simplicity_err)dag_len;
} else {
simplicity_assert(NULL != dag);
simplicity_assert((uint_fast32_t)dag_len <= DAG_LEN_MAX);
*error = simplicity_closeBitstream(&stream);
sha256_fromMidstate(cmr, dag[dag_len-1].cmr.s);
}

simplicity_free(dag);
return IS_PERMANENT(*error);
return simplicity_computeCmr(error, cmr, simplicity_elements_decodeJet, program, program_len);
}
23 changes: 23 additions & 0 deletions C/include/simplicity/bitcoin/cmr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef SIMPLICITY_BITCOIN_CMR_H
#define SIMPLICITY_BITCOIN_CMR_H

#include <stdbool.h>
#include <stddef.h>
#include <simplicity/errorCodes.h>

/* Deserialize a Simplicity 'program' and compute its CMR.
*
* Caution: no typechecking is performed, only a well-formedness check.
*
* If at any time malloc fails then '*error' is set to 'SIMPLICITY_ERR_MALLOC' and 'false' is returned,
* Otherwise, 'true' is returned indicating that the result was successfully computed and returned in the '*error' value.
*
* If the operation completes successfully then '*error' is set to 'SIMPLICITY_NO_ERROR', and the 'cmr' array is filled in with the program's computed CMR.
*
* Precondition: NULL != error;
* unsigned char cmr[32]
* unsigned char program[program_len]
*/
extern bool simplicity_bitcoin_computeCmr( simplicity_err* error, unsigned char* cmr
, const unsigned char* program, size_t program_len);
#endif
41 changes: 41 additions & 0 deletions C/include/simplicity/bitcoin/exec.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef SIMPLICITY_BITCOIN_EXEC_H
#define SIMPLICITY_BITCOIN_EXEC_H

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <simplicity/errorCodes.h>
#include <simplicity/bitcoin/env.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.
*
* 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 <= budget;
* 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);
#endif
Loading
Loading