diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 943eedba57..24631f8450 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -219,6 +219,7 @@ endif if ENABLE_FUZZ_BINARY test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) test_fuzz_fuzz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_fuzz_CFLAGS = $(AM_CFLAGS) $(PIE_FLAGS) test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_fuzz_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) $(RUNTIME_LDFLAGS) test_fuzz_fuzz_SOURCES = \ @@ -312,6 +313,9 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp \ test/fuzz/signature_checker.cpp \ test/fuzz/signet.cpp \ + test/fuzz/simplicity_compute_amr.c \ + test/fuzz/simplicity.cpp \ + test/fuzz/simplicity_tx.cpp \ test/fuzz/socks5.cpp \ test/fuzz/span.cpp \ test/fuzz/spanparsing.cpp \ diff --git a/src/test/fuzz/simplicity.cpp b/src/test/fuzz/simplicity.cpp new file mode 100644 index 0000000000..02644e6990 --- /dev/null +++ b/src/test/fuzz/simplicity.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +extern "C" { +#include +#include +#include +} +#include +#include +#include + +#include +#include +#include +#include + +static uint256 GENESIS_HASH; + +static CConfidentialAsset INPUT_ASSET_UNCONF{}; +static CConfidentialAsset INPUT_ASSET_CONF{}; +static CConfidentialValue INPUT_VALUE_UNCONF{}; +static CConfidentialValue INPUT_VALUE_CONF{}; +static CScript TAPROOT_SCRIPT_PUB_KEY{}; +static std::vector TAPROOT_CONTROL{}; +static std::vector TAPROOT_ANNEX(99, 0x50); +//CMutableTransaction MTX_TEMPLATE{}; + +// 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); +} + +void initialize_simplicity() +{ + g_con_elementsmode = true; + + GENESIS_HASH = uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"); + + INPUT_VALUE_UNCONF.SetToAmount(12345678); + INPUT_VALUE_CONF.vchCommitment = { + 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + }; + + INPUT_ASSET_UNCONF.vchCommitment = INPUT_VALUE_CONF.vchCommitment; + INPUT_ASSET_UNCONF.vchCommitment[0] = 0x01; + INPUT_ASSET_CONF.vchCommitment = INPUT_VALUE_CONF.vchCommitment; + INPUT_ASSET_CONF.vchCommitment[0] = 0x0a; + + XOnlyPubKey intkey = XOnlyPubKey{uint256::ONE}; + XOnlyPubKey extkey = XOnlyPubKey{uint256::ONE}; + TAPROOT_SCRIPT_PUB_KEY = CScript{} << OP_1 << std::vector(extkey.begin(), extkey.end()); + // TODO have control block of nontrivial path length + TAPROOT_CONTROL.push_back(TAPROOT_LEAF_TAPSIMPLICITY | 1); // 1 is parity + TAPROOT_CONTROL.insert(TAPROOT_CONTROL.end(), intkey.begin(), intkey.end()); +} + +uint32_t read_u32(const unsigned char **buf) { + uint32_t ret; + memcpy(&ret, *buf, 4); + *buf += 4; + return le32toh(ret); +} + +#define MAX_LEN (1024 * 1024) + +FUZZ_TARGET_INIT(simplicity, initialize_simplicity) +{ + const unsigned char *buf = buffer.data(); + + uint32_t budget; + uint32_t tx_data_len; + uint32_t prog_data_len; + uint32_t wit_data_len; + + // 1. Sanitize and parse the buffer + if (buffer.size() < 8) { + return; + } + budget = read_u32(&buf); + + tx_data_len = read_u32(&buf); + if (tx_data_len > MAX_LEN || buffer.size() < tx_data_len + 12) { + return; + } + const unsigned char *tx_data = buf; + buf += tx_data_len; + + prog_data_len = read_u32(&buf); + if (prog_data_len > MAX_LEN || buffer.size() < tx_data_len + prog_data_len + 16) { + return; + } + const unsigned char *prog_data = buf; + buf += prog_data_len; + + wit_data_len = read_u32(&buf); + if (wit_data_len > MAX_LEN || buffer.size() != tx_data_len + prog_data_len + wit_data_len + 16) { + return; + } + const unsigned char *wit_data = buf; + + //printf("OK going\n"); + + // 2. Parse the transaction (the program and witness are just raw bytes) + CMutableTransaction mtx; + CDataStream txds{Span{tx_data, tx_data_len}, SER_NETWORK, INIT_PROTO_VERSION}; + try { + txds >> mtx; + mtx.witness.vtxinwit.resize(mtx.vin.size()); + mtx.witness.vtxoutwit.resize(mtx.vout.size()); + + // We use the first vin as a "random oracle" rather than reading more from + // the fuzzer, because we want our fuzz seeds to have as simple a structure + // as possible. This means we must reject 0-input transactions, which are + // invalid on-chain anyway. + if (mtx.vin.size() == 0) { + return; + } + + // This is an assertion in the Simplicity interpreter. It is guaranteed + // to hold for anything on the network since (even if validatepegin is off) + // pegins are validated for well-formedness long before the script interpreter + // is invoked. But in this code we just call the interpreter directly without + // these checks. + for (unsigned i = 0; i < mtx.vin.size(); i++) { + if (mtx.vin[i].m_is_pegin && (mtx.witness.vtxinwit[i].m_pegin_witness.stack.size() < 4 || mtx.witness.vtxinwit[i].m_pegin_witness.stack[2].size() != 32)) { + return; + } + } + } catch (const std::ios_base::failure&) { + return; + } + + // 2a. Pull the program and witness into vectors so they can be pushed onto the stack. + std::vector prog_bytes; + std::vector wit_bytes; + prog_bytes.assign(prog_data, prog_data + prog_data_len); + wit_bytes.assign(wit_data, wit_data + wit_data_len); + + 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)); + + // The remainder is just copy/pasted from the original fuzztest + + // 3. Construct `nIn` and `spent_outs` array. + // + // Here we extract data from the first input's txid, since the fuzzer already + // produced that as a random string which has no other meaning. So to avoid + // complicating our seed encoding beyond "transaction then simplicity code" + // we just use it as a random source. + // + // We do skip the first byte since that has pegin/issuance flag in it and + // therefore already has semantic information. + size_t nIn = mtx.vin[0].prevout.hash.data()[1] % mtx.vin.size(); + std::vector spent_outs{}; + for (unsigned int i = 0; i < mtx.vin.size(); i++) { + // Null asset or value would assert in the interpreter, and are impossible + // to hit in real transactions. Nonces are not included in the UTXO set and + // therefore don't matter. + CConfidentialValue value = i & 1 ? INPUT_VALUE_CONF : INPUT_VALUE_UNCONF; + CConfidentialAsset asset = i & 2 ? INPUT_ASSET_CONF : INPUT_ASSET_UNCONF; + CScript scriptPubKey; + if (i != nIn) { + // For scriptPubKeys we can use arbitrary scripts. We include the empty + // script even though in a real transaction this would be impossible, + // because it shouldn't break anything. + for (unsigned int j = 0; j < i; j++) { + scriptPubKey << OP_TRUE; + } + } else { + scriptPubKey = TAPROOT_SCRIPT_PUB_KEY; + } + + spent_outs.push_back(CTxOut{asset, value, scriptPubKey}); + } + assert(spent_outs.size() == mtx.vin.size()); + + // 4. Set up witness data + mtx.witness.vtxinwit[nIn].scriptWitness.stack.clear(); + mtx.witness.vtxinwit[nIn].scriptWitness.stack.push_back(prog_bytes); + mtx.witness.vtxinwit[nIn].scriptWitness.stack.push_back(TAPROOT_CONTROL); + if (mtx.vin[0].prevout.hash.data()[2] & 1) { + mtx.witness.vtxinwit[nIn].scriptWitness.stack.push_back(TAPROOT_ANNEX); + } + + // 5. Set up Simplicity environment and tx environment + rawTapEnv simplicityRawTap; + simplicityRawTap.controlBlock = TAPROOT_CONTROL.data(); + simplicityRawTap.pathLen = (TAPROOT_CONTROL.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE; + simplicityRawTap.scriptCMR = cmr; + + PrecomputedTransactionData txdata{GENESIS_HASH}; + std::vector spent_outs_copy{spent_outs}; + txdata.Init(mtx, std::move(spent_outs_copy)); + assert(txdata.m_simplicity_tx_data != NULL); + + // 4. Main test + 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; + 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()); + + // 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()); + } + + // 6. Cleanup + free(taproot); +} diff --git a/src/test/fuzz/simplicity_compute_amr.c b/src/test/fuzz/simplicity_compute_amr.c new file mode 100644 index 0000000000..dfa0450e17 --- /dev/null +++ b/src/test/fuzz/simplicity_compute_amr.c @@ -0,0 +1,61 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include // simplicity_decodeMallocDag +#include // DAG_LEN_MAX +#include // simplicity_free +#include // simplicity_mallocTypeInference +#include +#include + +// 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) { + simplicity_assert(NULL != error); + simplicity_assert(NULL != amr); + simplicity_assert(NULL != program || 0 == program_len); + simplicity_assert(NULL != witness || 0 == witness_len); + + bitstream stream = initializeBitstream(program, program_len); + dag_node* dag = NULL; + combinator_counters census; + int_fast32_t dag_len = simplicity_decodeMallocDag(&dag, &census, &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); + + type* type_dag = NULL; + if (IS_OK(*error)) { + *error = simplicity_mallocTypeInference(&type_dag, dag, (uint_fast32_t)dag_len, &census); + } + bitstream witness_stream; + if (IS_OK(*error)) { + 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)) { + analyses *analysis = (analyses*) simplicity_malloc((size_t)dag_len * sizeof(analyses)); + simplicity_assert(NULL != analysis); + simplicity_computeAnnotatedMerkleRoot(analysis, dag, type_dag, (uint_fast32_t)dag_len); + sha256_fromMidstate(amr, analysis[dag_len-1].annotatedMerkleRoot.s); + simplicity_free(analysis); + } + simplicity_free(type_dag); + } + + simplicity_free(dag); + return IS_PERMANENT(*error); +} diff --git a/src/test/fuzz/simplicity_tx.cpp b/src/test/fuzz/simplicity_tx.cpp new file mode 100644 index 0000000000..7a1b927004 --- /dev/null +++ b/src/test/fuzz/simplicity_tx.cpp @@ -0,0 +1,227 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include