diff --git a/qa/cryptoconditions/README.md b/qa/cryptoconditions/README.md index d14ab5b7d..d93e8e266 100644 --- a/qa/cryptoconditions/README.md +++ b/qa/cryptoconditions/README.md @@ -2,6 +2,9 @@ These tests are for the [Crypto-Conditions](https://github.com/rfcs/crypto-conditions) functionality in Komodod, using [Hoek](https://github.com/libscott/hoek) as a tool to create and sign transactions. -The tests use Python as a scripting language and each one should "just work" given Linux and a 2.7+ Python interpreter. Each file prefixed with "test_" is a test case. +## How to run the tests -The tests require `hoek` to be on the $PATH. Included is a script to perform a complete installation of `hoek`. +1. Install hoek: https://github.com/libscott/hoek +2. Start komodod: `src/komodod -ac_name=CCTEST -ac_supply=21000000 -gen -pubkey=0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47 -ac_cc=1` +3. Set environment variable pointing to Komodo conf: `export KOMODO_CONF_PATH=~/.komodo/CCTEST/CCTEST.conf` +4. Run tests: `python test_integration.py` (you can also use a test runner such as `nose`). diff --git a/qa/cryptoconditions/test_integration.py b/qa/cryptoconditions/test_integration.py index ee266dfdf..24792cc60 100644 --- a/qa/cryptoconditions/test_integration.py +++ b/qa/cryptoconditions/test_integration.py @@ -3,6 +3,7 @@ import time import json import logging import binascii +import struct from testsupport import * @@ -20,7 +21,7 @@ def test_fulfillment_wrong_signature(inp): spend = {'inputs': [inp], 'outputs': [nospend]} signed = sign(spend) - # Set the correct pubkey, signature is wrong + # Set the correct pubkey, signature is bob's signed['tx']['inputs'][0]['script']['fulfillment']['publicKey'] = alice_pk try: @@ -32,8 +33,8 @@ def test_fulfillment_wrong_signature(inp): @fanout_input(2) def test_fulfillment_wrong_pubkey(inp): spend = {'inputs': [inp], 'outputs': [nospend]} - signed = sign(spend) + # Set the wrong pubkey, signature is correct signed['tx']['inputs'][0]['script']['fulfillment']['publicKey'] = bob_pk @@ -44,7 +45,7 @@ def test_fulfillment_wrong_pubkey(inp): @fanout_input(3) -def test_fulfillment_invalid_fulfillment(inp): +def test_invalid_fulfillment_binary(inp): # Create a valid script with an invalid fulfillment payload inp['script'] = binascii.hexlify(b"\007invalid").decode('utf-8') spend = {'inputs': [inp], 'outputs': [nospend]} @@ -55,8 +56,41 @@ def test_fulfillment_invalid_fulfillment(inp): assert 'Crypto-Condition payload is invalid' in str(e), str(e) +@fanout_input(4) +def test_invalid_condition(inp): + # Create a valid output script with an invalid cryptocondition binary + outputscript = to_hex(b"\007invalid\xcc") + spend = {'inputs': [inp], 'outputs': [{'amount': 1000, 'script': outputscript}]} + spend_txid = submit(sign(spend)) + + spend1 = { + 'inputs': [{'txid': spend_txid, 'idx': 0, 'script': {'fulfillment': cond_alice}}], + 'outputs': [nospend], + } + + 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) + + +@fanout_input(19) +def test_oversize_fulfillment(inp): + # Create oversize fulfillment script where the total length is <2000 + binscript = b'\x4d%s%s' % (struct.pack('h', 2000), b'a' * 2000) + inp['script'] = to_hex(binscript) + spend = {'inputs': [inp], 'outputs': [nospend]} + + try: + assert not submit({'tx': spend}), 'should raise an error' + except RPCError as e: + assert 'scriptsig-size' in str(e), str(e) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) for name, f in globals().items(): if name.startswith('test_'): logging.info("Running test: %s" % name) f() + logging.info("Test OK: %s" % name) diff --git a/qa/cryptoconditions/testsupport.py b/qa/cryptoconditions/testsupport.py index 5b00f8218..e6025c0bc 100644 --- a/qa/cryptoconditions/testsupport.py +++ b/qa/cryptoconditions/testsupport.py @@ -2,9 +2,11 @@ from __future__ import print_function import sys import json import time +import copy +import base64 import logging -import subprocess import functools +import subprocess class RPCError(IOError): @@ -34,6 +36,10 @@ def run_cmd(cmd): return proc.stdout.read() +def to_hex(s): + return base64.b16encode(s).decode('utf-8') + + class Hoek(JsonClient): def _exec(self, method, args): cmd = ['hoek', method, json.dumps(args[0])] @@ -111,7 +117,7 @@ def fanout_input(n): if not hasattr(fanout_input, 'txid'): fanout_input.txid = get_fanout_txid() inp = {'txid': fanout_input.txid, 'idx': n, 'script': {'fulfillment': cond_alice}} - f(inp) + f(copy.deepcopy(inp)) return functools.wraps(f)(wrapper) return decorate diff --git a/src/cryptoconditions b/src/cryptoconditions index 89325dc29..7ab93ba86 160000 --- a/src/cryptoconditions +++ b/src/cryptoconditions @@ -1 +1 @@ -Subproject commit 89325dc29391f473da75503105a4946f2f96fb76 +Subproject commit 7ab93ba86174efa32da3dac8b625ff9e1ef05fdd diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 4e03527a4..e0c2c1d01 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -949,11 +949,6 @@ bool EvalScript(vector >& stack, const CScript& script, un valtype& vchFulfillment = stacktop(-2); valtype& vchCondition = stacktop(-1); - // Hard limit fulfillment size - if (vchFulfillment.size() > 1024 * 10) { - return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT); - } - CC *cond = (CC*) calloc(1, sizeof(CC)); char *fulfillmentBin = (char*) vchFulfillment.data(); int rc = cc_readFulfillmentBinary(cond, fulfillmentBin, vchFulfillment.size()); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index f1aa1fb40..41c8a24e0 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -67,6 +67,8 @@ const char* ScriptErrorString(const ScriptError serror) return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT: + return "Crypto-Condition payload is invalid"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index e84268977..35c8fbfd8 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -54,6 +54,7 @@ typedef enum ScriptError_t SCRIPT_ERR_ERROR_COUNT, + /* crypto-condition script errors */ SCRIPT_ERR_CRYPTOCONDITION_VERIFY, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT } ScriptError;