extra tests for failure modes and remove CRYPTOCONDITION_OVERSIZE error state

This commit is contained in:
Scott Sadler
2018-02-19 18:24:12 -03:00
parent 456c9e72fd
commit f5cf215f71
7 changed files with 54 additions and 13 deletions

View File

@@ -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. 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`).

View File

@@ -3,6 +3,7 @@ import time
import json import json
import logging import logging
import binascii import binascii
import struct
from testsupport import * from testsupport import *
@@ -20,7 +21,7 @@ def test_fulfillment_wrong_signature(inp):
spend = {'inputs': [inp], 'outputs': [nospend]} spend = {'inputs': [inp], 'outputs': [nospend]}
signed = sign(spend) 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 signed['tx']['inputs'][0]['script']['fulfillment']['publicKey'] = alice_pk
try: try:
@@ -32,8 +33,8 @@ def test_fulfillment_wrong_signature(inp):
@fanout_input(2) @fanout_input(2)
def test_fulfillment_wrong_pubkey(inp): def test_fulfillment_wrong_pubkey(inp):
spend = {'inputs': [inp], 'outputs': [nospend]} spend = {'inputs': [inp], 'outputs': [nospend]}
signed = sign(spend) signed = sign(spend)
# Set the wrong pubkey, signature is correct # Set the wrong pubkey, signature is correct
signed['tx']['inputs'][0]['script']['fulfillment']['publicKey'] = bob_pk signed['tx']['inputs'][0]['script']['fulfillment']['publicKey'] = bob_pk
@@ -44,7 +45,7 @@ def test_fulfillment_wrong_pubkey(inp):
@fanout_input(3) @fanout_input(3)
def test_fulfillment_invalid_fulfillment(inp): def test_invalid_fulfillment_binary(inp):
# Create a valid script with an invalid fulfillment payload # Create a valid script with an invalid fulfillment payload
inp['script'] = binascii.hexlify(b"\007invalid").decode('utf-8') inp['script'] = binascii.hexlify(b"\007invalid").decode('utf-8')
spend = {'inputs': [inp], 'outputs': [nospend]} 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) 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__': if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
for name, f in globals().items(): for name, f in globals().items():
if name.startswith('test_'): if name.startswith('test_'):
logging.info("Running test: %s" % name) logging.info("Running test: %s" % name)
f() f()
logging.info("Test OK: %s" % name)

View File

@@ -2,9 +2,11 @@ from __future__ import print_function
import sys import sys
import json import json
import time import time
import copy
import base64
import logging import logging
import subprocess
import functools import functools
import subprocess
class RPCError(IOError): class RPCError(IOError):
@@ -34,6 +36,10 @@ def run_cmd(cmd):
return proc.stdout.read() return proc.stdout.read()
def to_hex(s):
return base64.b16encode(s).decode('utf-8')
class Hoek(JsonClient): class Hoek(JsonClient):
def _exec(self, method, args): def _exec(self, method, args):
cmd = ['hoek', method, json.dumps(args[0])] cmd = ['hoek', method, json.dumps(args[0])]
@@ -111,7 +117,7 @@ def fanout_input(n):
if not hasattr(fanout_input, 'txid'): if not hasattr(fanout_input, 'txid'):
fanout_input.txid = get_fanout_txid() fanout_input.txid = get_fanout_txid()
inp = {'txid': fanout_input.txid, 'idx': n, 'script': {'fulfillment': cond_alice}} inp = {'txid': fanout_input.txid, 'idx': n, 'script': {'fulfillment': cond_alice}}
f(inp) f(copy.deepcopy(inp))
return functools.wraps(f)(wrapper) return functools.wraps(f)(wrapper)
return decorate return decorate

View File

@@ -949,11 +949,6 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
valtype& vchFulfillment = stacktop(-2); valtype& vchFulfillment = stacktop(-2);
valtype& vchCondition = stacktop(-1); 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)); CC *cond = (CC*) calloc(1, sizeof(CC));
char *fulfillmentBin = (char*) vchFulfillment.data(); char *fulfillmentBin = (char*) vchFulfillment.data();
int rc = cc_readFulfillmentBinary(cond, fulfillmentBin, vchFulfillment.size()); int rc = cc_readFulfillmentBinary(cond, fulfillmentBin, vchFulfillment.size());

View File

@@ -67,6 +67,8 @@ const char* ScriptErrorString(const ScriptError serror)
return "NOPx reserved for soft-fork upgrades"; return "NOPx reserved for soft-fork upgrades";
case SCRIPT_ERR_PUBKEYTYPE: case SCRIPT_ERR_PUBKEYTYPE:
return "Public key is neither compressed or uncompressed"; 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_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT: case SCRIPT_ERR_ERROR_COUNT:
default: break; default: break;

View File

@@ -54,6 +54,7 @@ typedef enum ScriptError_t
SCRIPT_ERR_ERROR_COUNT, SCRIPT_ERR_ERROR_COUNT,
/* crypto-condition script errors */
SCRIPT_ERR_CRYPTOCONDITION_VERIFY, SCRIPT_ERR_CRYPTOCONDITION_VERIFY,
SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT
} ScriptError; } ScriptError;