diff --git a/.gitmodules b/.gitmodules index 83942a053..bf76cc359 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "cryptoconditions"] path = src/cryptoconditions url = https://github.com/libscott/libcryptoconditions.git - branch = komodo-integration + branch = komodo diff --git a/qa/cryptoconditions/test_integration.py b/qa/cryptoconditions/test_integration.py index 6d60ee921..f764fcb81 100644 --- a/qa/cryptoconditions/test_integration.py +++ b/qa/cryptoconditions/test_integration.py @@ -7,6 +7,9 @@ import struct from testsupport import * +SCRIPT_FALSE = 'Script evaluated without error but finished with a false/empty top stack element' + + @fanout_input(0) def test_basic_spend(inp): spend = {'inputs': [inp], "outputs": [nospend]} @@ -27,7 +30,7 @@ def test_fulfillment_wrong_signature(inp): try: assert not submit(signed), 'should raise an error' except RPCError as e: - assert 'Script evaluated without error but finished with a false/empty top stack element' in str(e), str(e) + assert SCRIPT_FALSE in str(e), str(e) @fanout_input(2) @@ -41,7 +44,7 @@ def test_fulfillment_wrong_pubkey(inp): try: assert not submit(signed), 'should raise an error' except RPCError as e: - assert 'Script evaluated without error but finished with a false/empty top stack element' in str(e), str(e) + assert SCRIPT_FALSE in str(e), str(e) @fanout_input(3) @@ -71,7 +74,7 @@ def test_invalid_condition(inp): try: assert not submit(sign(spend1)), 'should raise an error' except RPCError as e: - assert 'Script evaluated without error but finished with a false/empty top stack element' in str(e), str(e) + assert SCRIPT_FALSE in str(e), str(e) @fanout_input(5) @@ -124,7 +127,7 @@ def test_aux_basic(inp): try: assert not submit(sign(spend2)), 'should raise an error' except RPCError as e: - assert 'Script evaluated without error but finished with a false/empty top stack element' in str(e), str(e) + assert SCRIPT_FALSE in str(e), str(e) @fanout_input(7) @@ -168,7 +171,47 @@ def test_aux_complex(inp): try: assert not submit(sign(spend2)), 'should raise an error' except RPCError as e: - assert 'Script evaluated without error but finished with a false/empty top stack element' in str(e), str(e) + assert SCRIPT_FALSE in str(e), str(e) + + +@fanout_input(8) +def test_secp256k1_condition(inp): + ec_cond = { + 'type': 'secp256k1-sha-256', + 'publicKey': notary_pk + } + + # Create some secp256k1 outputs + spend0 = { + 'inputs': [inp], + 'outputs': [ + {'amount': 500, 'script': {'condition': ec_cond}}, + {'amount': 500, 'script': {'condition': ec_cond}} + ] + } + spend0_txid = submit(sign(spend0)) + assert rpc.getrawtransaction(spend0_txid) + + # Test a good fulfillment + spend1 = { + 'inputs': [{'txid': spend0_txid, 'idx': 0, 'script': {'fulfillment': ec_cond}}], + 'outputs': [{'amount': 500, 'script': {'condition': ec_cond}}] + } + spend1_txid = submit(sign(spend1)) + assert rpc.getrawtransaction(spend1_txid) + + # Test a bad fulfillment + spend2 = { + 'inputs': [{'txid': spend0_txid, 'idx': 1, 'script': {'fulfillment': ec_cond}}], + 'outputs': [{'amount': 500, 'script': {'condition': ec_cond}}] + } + signed = sign(spend2) + signed['tx']['inputs'][0]['script']['fulfillment']['publicKey'] = \ + '0275cef12fc5c49be64f5aab3d1fbba08cd7b0d02908b5112fbd8504218d14bc7d' + try: + assert not submit(signed), 'should raise an error' + except RPCError as e: + assert SCRIPT_FALSE in str(e), str(e) if __name__ == '__main__': diff --git a/qa/cryptoconditions/testsupport.py b/qa/cryptoconditions/testsupport.py index e6025c0bc..35b7ecdf6 100644 --- a/qa/cryptoconditions/testsupport.py +++ b/qa/cryptoconditions/testsupport.py @@ -74,7 +74,8 @@ def wait_for_block(height): def sign(tx): signed = hoek.signTxBitcoin({'tx': tx, 'privateKeys': [notary_sk]}) - return hoek.signTxEd25519({'tx': signed['tx'], 'privateKeys': [alice_sk, bob_sk]}) + signed = hoek.signTxEd25519({'tx': signed['tx'], 'privateKeys': [alice_sk, bob_sk]}) + return hoek.signTxSecp256k1({'tx': signed['tx'], 'privateKeys': [notary_sk]}) def submit(tx): @@ -96,14 +97,14 @@ def get_fanout_txid(): reward_tx = hoek.decodeTx({'hex': reward_tx_raw}) balance = reward_tx['tx']['outputs'][0]['amount'] - n_outs = 100 + n_outs = 16 remainder = balance - n_outs * 1000 fanout = { 'inputs': [ {'txid': reward_txid, 'idx': 0, 'script': {'pubkey': notary_pk}} ], - "outputs": (100 * [ + "outputs": (n_outs * [ {"amount": 1000, "script": {"condition": cond_alice}} ] + [{"amount": remainder, 'script': {'address': notary_addr}}]) } @@ -122,6 +123,21 @@ def fanout_input(n): return decorate +def decode_base64(data): + """Decode base64, padding being optional. + + :param data: Base64 data as an ASCII byte string + :returns: The decoded byte string. + """ + missing_padding = len(data) % 4 + if missing_padding: + data += '=' * (4 - missing_padding) + return base64.urlsafe_b64decode(data) + + +def encode_base64(data): + return base64.urlsafe_b64encode(data).rstrip(b'=') + notary_addr = 'RXSwmXKtDURwXP7sdqNfsJ6Ga8RaxTchxE' notary_pk = '0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47' diff --git a/src/cryptoconditions b/src/cryptoconditions index 0dcac79cf..863d92661 160000 --- a/src/cryptoconditions +++ b/src/cryptoconditions @@ -1 +1 @@ -Subproject commit 0dcac79cf9ffeda8aff9d3e9e41fa23ac8208486 +Subproject commit 863d9266141667b469a6574ce7285134665427c6 diff --git a/src/komodo_cryptoconditions.cpp b/src/komodo_cryptoconditions.cpp index c807df7c9..4c516b320 100644 --- a/src/komodo_cryptoconditions.cpp +++ b/src/komodo_cryptoconditions.cpp @@ -3,8 +3,8 @@ #include "script/interpreter.h" -int TransactionSignatureChecker::CheckAuxCondition(const CC *cond) const { - +int TransactionSignatureChecker::CheckAuxCondition(const CC *cond) const +{ // Check that condition is equal to fulfillment if (0 == strcmp((const char*)cond->method, "equals")) { return (cond->conditionAuxLength == cond->fulfillmentAuxLength) && diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 709b566f6..91b22039e 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1164,7 +1164,7 @@ static int komodoCCAux(CC *cond, void *checker) { bool TransactionSignatureChecker::CheckCryptoCondition(const CC *cond, const std::vector& condBin, const CScript& scriptCode) const { uint256 message = SignatureHash(scriptCode, *txTo, nIn, SIGHASH_ALL); - return cc_verify(cond, (const unsigned char*)&message, 32, + return cc_verify(cond, (const unsigned char*)&message, 32, 0, condBin.data(), condBin.size(), komodoCCAux, (void*)this); } diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h index ed1d22818..8d97db946 100644 --- a/src/secp256k1/src/ecdsa_impl.h +++ b/src/secp256k1/src/ecdsa_impl.h @@ -47,12 +47,20 @@ static const secp256k1_fe_t secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_C ); static int secp256k1_ecdsa_sig_parse(secp256k1_ecdsa_sig_t *r, const unsigned char *sig, int size) { + + /* libscott had to add this to get this version of the library to read compact sigs */ + int overflow = 0; + if (size == 64) { + secp256k1_scalar_set_b32(&r->r, sig, &overflow); + secp256k1_scalar_set_b32(&r->s, sig+32, &overflow); + return 1; + } + unsigned char ra[32] = {0}, sa[32] = {0}; const unsigned char *rp; const unsigned char *sp; int lenr; int lens; - int overflow; if (sig[0] != 0x30) { return 0; }