cpp test suite for cryptoconditions integration
This commit is contained in:
@@ -3,9 +3,9 @@ rust_packages := rust librustzcash
|
|||||||
proton_packages := proton
|
proton_packages := proton
|
||||||
zcash_packages := libgmp libsodium
|
zcash_packages := libgmp libsodium
|
||||||
ifeq ($(host_os),linux)
|
ifeq ($(host_os),linux)
|
||||||
packages := boost openssl libevent zeromq $(zcash_packages) #googletest googlemock
|
packages := boost openssl libevent zeromq $(zcash_packages) googletest # googlemock
|
||||||
else
|
else
|
||||||
packages := boost openssl libevent zeromq $(zcash_packages) libcurl # googletest googlemock libcurl
|
packages := boost openssl libevent zeromq $(zcash_packages) libcurl googletest # googlemock
|
||||||
endif
|
endif
|
||||||
|
|
||||||
native_packages := native_ccache
|
native_packages := native_ccache
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
# Integration tests for Crypto-Conditions
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## How to run the tests
|
|
||||||
|
|
||||||
1. Install hoek: https://github.com/libscott/hoek
|
|
||||||
1. Allow peer-less block creation: set "fMiningRequiresPeers = false" in src/chainparams.cpp, then build komodod.
|
|
||||||
1. Start komodod: `src/komodod -ac_name=CCTEST -ac_supply=21000000 -gen -pubkey=0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47 -ac_cc=1`
|
|
||||||
1. Set environment variable pointing to Komodo conf: `export KOMODO_CONF_PATH=~/.komodo/CCTEST/CCTEST.conf`
|
|
||||||
1. Run tests: `python test_integration.py` (you can also use a test runner such as `nose`).
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
import sys
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import binascii
|
|
||||||
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]}
|
|
||||||
spend_txid = submit(sign(spend))
|
|
||||||
assert rpc.getrawtransaction(spend_txid)
|
|
||||||
|
|
||||||
|
|
||||||
@fanout_input(1)
|
|
||||||
def test_fulfillment_wrong_signature(inp):
|
|
||||||
# Set other pubkey and sign
|
|
||||||
inp['script']['fulfillment']['publicKey'] = bob_pk
|
|
||||||
spend = {'inputs': [inp], 'outputs': [nospend]}
|
|
||||||
signed = sign(spend)
|
|
||||||
|
|
||||||
# Set the correct pubkey, signature is bob's
|
|
||||||
signed['inputs'][0]['script']['fulfillment']['publicKey'] = alice_pk
|
|
||||||
|
|
||||||
try:
|
|
||||||
assert not submit(signed), 'should raise an error'
|
|
||||||
except RPCError as e:
|
|
||||||
assert SCRIPT_FALSE in str(e), str(e)
|
|
||||||
|
|
||||||
|
|
||||||
@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['inputs'][0]['script']['fulfillment']['publicKey'] = bob_pk
|
|
||||||
|
|
||||||
try:
|
|
||||||
assert not submit(signed), 'should raise an error'
|
|
||||||
except RPCError as e:
|
|
||||||
assert SCRIPT_FALSE in str(e), str(e)
|
|
||||||
|
|
||||||
|
|
||||||
@fanout_input(3)
|
|
||||||
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]}
|
|
||||||
|
|
||||||
try:
|
|
||||||
assert not submit(spend), 'should raise an error'
|
|
||||||
except RPCError as 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_FALSE in str(e), str(e)
|
|
||||||
|
|
||||||
|
|
||||||
@fanout_input(5)
|
|
||||||
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(spend), 'should raise an error'
|
|
||||||
except RPCError as e:
|
|
||||||
assert 'scriptsig-size' in str(e), str(e)
|
|
||||||
|
|
||||||
|
|
||||||
@fanout_input(6)
|
|
||||||
def test_aux_basic(inp):
|
|
||||||
aux_cond = {
|
|
||||||
'type': 'aux-sha-256',
|
|
||||||
'method': 'equals',
|
|
||||||
'conditionAux': 'LTE',
|
|
||||||
'fulfillmentAux': 'LTE'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Setup some aux outputs
|
|
||||||
spend0 = {
|
|
||||||
'inputs': [inp],
|
|
||||||
'outputs': [
|
|
||||||
{'amount': 500, 'script': {'condition': aux_cond}},
|
|
||||||
{'amount': 500, 'script': {'condition': aux_cond}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
spend0_txid = submit(sign(spend0))
|
|
||||||
assert rpc.getrawtransaction(spend0_txid)
|
|
||||||
|
|
||||||
# Test a good fulfillment
|
|
||||||
spend1 = {
|
|
||||||
'inputs': [{'txid': spend0_txid, 'idx': 0, 'script': {'fulfillment': aux_cond}}],
|
|
||||||
'outputs': [{'amount': 500, 'script': {'condition': aux_cond}}]
|
|
||||||
}
|
|
||||||
spend1_txid = submit(sign(spend1))
|
|
||||||
assert rpc.getrawtransaction(spend1_txid)
|
|
||||||
|
|
||||||
# Test a bad fulfillment
|
|
||||||
aux_cond['fulfillmentAux'] = 'WYW'
|
|
||||||
spend2 = {
|
|
||||||
'inputs': [{'txid': spend0_txid, 'idx': 1, 'script': {'fulfillment': aux_cond}}],
|
|
||||||
'outputs': [{'amount': 500, 'script': {'condition': aux_cond}}]
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
assert not submit(sign(spend2)), 'should raise an error'
|
|
||||||
except RPCError as e:
|
|
||||||
assert SCRIPT_FALSE in str(e), str(e)
|
|
||||||
|
|
||||||
|
|
||||||
@fanout_input(7)
|
|
||||||
def test_aux_complex(inp):
|
|
||||||
aux_cond = {
|
|
||||||
'type': 'aux-sha-256',
|
|
||||||
'method': 'inputIsReturn',
|
|
||||||
'conditionAux': '',
|
|
||||||
'fulfillmentAux': 'AQ' # \1 (tx.vout[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
# Setup some aux outputs
|
|
||||||
spend0 = {
|
|
||||||
'inputs': [inp],
|
|
||||||
'outputs': [
|
|
||||||
{'amount': 500, 'script': {'condition': aux_cond}},
|
|
||||||
{'amount': 500, 'script': {'condition': aux_cond}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
spend0_txid = submit(sign(spend0))
|
|
||||||
assert rpc.getrawtransaction(spend0_txid)
|
|
||||||
|
|
||||||
# Test a good fulfillment
|
|
||||||
spend1 = {
|
|
||||||
'inputs': [{'txid': spend0_txid, 'idx': 0, 'script': {'fulfillment': aux_cond}}],
|
|
||||||
'outputs': [
|
|
||||||
{'amount': 250, 'script': {'condition': aux_cond}},
|
|
||||||
{'amount': 250, 'script': "6A0B68656C6C6F207468657265"} # OP_RETURN somedata
|
|
||||||
]
|
|
||||||
}
|
|
||||||
spend1_txid = submit(sign(spend1))
|
|
||||||
assert rpc.getrawtransaction(spend1_txid)
|
|
||||||
|
|
||||||
# Test a bad fulfillment
|
|
||||||
spend2 = {
|
|
||||||
'inputs': [{'txid': spend0_txid, 'idx': 1, 'script': {'fulfillment': aux_cond}}],
|
|
||||||
'outputs': [
|
|
||||||
{'amount': 500, 'script': "6A0B68656C6C6F207468657265"} # OP_RETURN somedata
|
|
||||||
]
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
assert not submit(sign(spend2)), 'should raise an error'
|
|
||||||
except RPCError as 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['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__':
|
|
||||||
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)
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
from __future__ import print_function
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import copy
|
|
||||||
import base64
|
|
||||||
import logging
|
|
||||||
import functools
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
|
|
||||||
class RPCError(IOError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class JsonClient(object):
|
|
||||||
def __getattr__(self, method):
|
|
||||||
if method[0] == '_':
|
|
||||||
return getattr(super(JsonClient, self), method)
|
|
||||||
def inner(*args):
|
|
||||||
return self._exec(method, args)
|
|
||||||
return inner
|
|
||||||
|
|
||||||
def load_response(self, data):
|
|
||||||
data = json.loads(data.decode("utf-8"))
|
|
||||||
if data.get('error'):
|
|
||||||
raise RPCError(data['error'])
|
|
||||||
if 'result' in data:
|
|
||||||
return data['result']
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmd(cmd):
|
|
||||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
|
||||||
assert proc.wait() == 0
|
|
||||||
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])]
|
|
||||||
return self.load_response(run_cmd(cmd))
|
|
||||||
|
|
||||||
|
|
||||||
class Komodod(JsonClient):
|
|
||||||
def _exec(self, method, args):
|
|
||||||
if not hasattr(self, '_url'):
|
|
||||||
urltpl = 'http://$rpcuser:$rpcpassword@${rpchost:-127.0.0.1}:$rpcport'
|
|
||||||
cmd = ['bash', '-c', '. $KOMODO_CONF_PATH && echo -n %s' % urltpl]
|
|
||||||
self._url = run_cmd(cmd)
|
|
||||||
|
|
||||||
req = {'method': method, 'params': args, 'id': 1}
|
|
||||||
cmd = ['curl', '-s', '-H', 'Content-Type: application/json', '-d', json.dumps(req), self._url]
|
|
||||||
return self.load_response(run_cmd(cmd))
|
|
||||||
|
|
||||||
|
|
||||||
rpc = Komodod()
|
|
||||||
hoek = Hoek()
|
|
||||||
|
|
||||||
|
|
||||||
def wait_for_block(height):
|
|
||||||
logging.info("Waiting for block height %s" % height)
|
|
||||||
for i in range(100):
|
|
||||||
try:
|
|
||||||
return rpc.getblock(str(height))
|
|
||||||
except RPCError as e:
|
|
||||||
time.sleep(3)
|
|
||||||
raise Exception('Time out waiting for block at height %s' % height)
|
|
||||||
|
|
||||||
|
|
||||||
def sign(tx):
|
|
||||||
signed = hoek.signTxBitcoin({'tx': tx, 'privateKeys': [notary_sk]})
|
|
||||||
signed = hoek.signTxEd25519({'tx': signed, 'privateKeys': [alice_sk, bob_sk]})
|
|
||||||
return hoek.signTxSecp256k1({'tx': signed, 'privateKeys': [notary_sk]})
|
|
||||||
|
|
||||||
|
|
||||||
def submit(tx):
|
|
||||||
encoded = hoek.encodeTx(tx)
|
|
||||||
try:
|
|
||||||
rpc.getrawtransaction(encoded['txid'])
|
|
||||||
logging.info("Transaction already in chain: %s" % encoded['txid'])
|
|
||||||
return encoded['txid']
|
|
||||||
except RPCError:
|
|
||||||
pass
|
|
||||||
logging.info("submit transaction: %s:%s" % (encoded['txid'], json.dumps(tx)))
|
|
||||||
return rpc.sendrawtransaction(encoded['hex'])
|
|
||||||
|
|
||||||
|
|
||||||
def get_fanout_txid():
|
|
||||||
block = wait_for_block(1)
|
|
||||||
reward_txid = block['tx'][0]
|
|
||||||
reward_tx_raw = rpc.getrawtransaction(reward_txid)
|
|
||||||
reward_tx = hoek.decodeTx({'hex': reward_tx_raw})
|
|
||||||
balance = reward_tx['outputs'][0]['amount']
|
|
||||||
|
|
||||||
n_outs = 16
|
|
||||||
remainder = balance - n_outs * 1000
|
|
||||||
|
|
||||||
fanout = {
|
|
||||||
'inputs': [
|
|
||||||
{'txid': reward_txid, 'idx': 0, 'script': {'pubkey': notary_pk}}
|
|
||||||
],
|
|
||||||
"outputs": (n_outs * [
|
|
||||||
{"amount": 1000, "script": {"condition": cond_alice}}
|
|
||||||
] + [{"amount": remainder, 'script': {'address': notary_addr}}])
|
|
||||||
}
|
|
||||||
|
|
||||||
return submit(sign(fanout))
|
|
||||||
|
|
||||||
|
|
||||||
def fanout_input(n):
|
|
||||||
def decorate(f):
|
|
||||||
def wrapper():
|
|
||||||
if not hasattr(fanout_input, 'txid'):
|
|
||||||
fanout_input.txid = get_fanout_txid()
|
|
||||||
inp = {'txid': fanout_input.txid, 'idx': n, 'script': {'fulfillment': cond_alice}}
|
|
||||||
f(copy.deepcopy(inp))
|
|
||||||
return functools.wraps(f)(wrapper)
|
|
||||||
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'
|
|
||||||
notary_sk = 'UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU'
|
|
||||||
alice_pk = '8ryTLBMnozUK4xUz7y49fjzZhxDDMK7c4mucLdbVY6jW'
|
|
||||||
alice_sk = 'E4ER7uYvaSTdpQFzTXNNSTkR6jNRJyqhZPJMGuU899nY'
|
|
||||||
cond_alice = {"type": "ed25519-sha-256", "publicKey": alice_pk}
|
|
||||||
bob_pk = 'C8MfEjKiFxDguacHvcM7MV83cRQ55RAHacC73xqg8qeu'
|
|
||||||
bob_sk = 'GrP1fZdUxUc1NYmu7kiNkJV4p7PKpshp1yBY7hogPUWT'
|
|
||||||
cond_bob = {"type": "ed25519-sha-256", "publicKey": bob_pk}
|
|
||||||
nospend = {"amount": 1000, "script": {"address": notary_addr}}
|
|
||||||
@@ -620,9 +620,10 @@ endif
|
|||||||
@test -f $(PROTOC)
|
@test -f $(PROTOC)
|
||||||
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $(<D) $<)
|
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $(<D) $<)
|
||||||
|
|
||||||
#if ENABLE_TESTS
|
if ENABLE_TESTS
|
||||||
|
include Makefile.ktest.include
|
||||||
#include Makefile.test.include
|
#include Makefile.test.include
|
||||||
#include Makefile.gtest.include
|
#include Makefile.gtest.include
|
||||||
#endif
|
endif
|
||||||
|
|
||||||
include Makefile.zcash.include
|
include Makefile.zcash.include
|
||||||
|
|||||||
14
src/Makefile.ktest.include
Normal file
14
src/Makefile.ktest.include
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
TESTS += komodo-test
|
||||||
|
bin_PROGRAMS += komodo-test
|
||||||
|
|
||||||
|
# tool for generating our public parameters
|
||||||
|
komodo_test_SOURCES = \
|
||||||
|
test-komodo/main.cpp \
|
||||||
|
test-komodo/test_cryptoconditions.cpp
|
||||||
|
|
||||||
|
komodo_test_CPPFLAGS = $(komodod_CPPFLAGS)
|
||||||
|
|
||||||
|
komodo_test_LDADD = -lgtest $(komodod_LDADD)
|
||||||
|
|
||||||
|
komodo_test_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
*/
|
*/
|
||||||
bool EvalConditionValidity(const CC *cond, const CTransaction *txTo, int nIn)
|
bool EvalConditionValidity(const CC *cond, const CTransaction *txTo, int nIn)
|
||||||
{
|
{
|
||||||
if (strcmp(cond->method, "testEval") == 0) {
|
if (strcmp(cond->method, "TestEval") == 0) {
|
||||||
return cond->paramsBinLength == 8 &&
|
return cond->paramsBinLength == 8 &&
|
||||||
memcmp(cond->paramsBin, "testEval", 8) == 0;
|
memcmp(cond->paramsBin, "TestEval", 8) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(cond->method, "ImportPayout") == 0) {
|
if (strcmp(cond->method, "ImportPayout") == 0) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ enum CCTypeId {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaliliary verification callback
|
* Evaliliary verification callback
|
||||||
*/
|
*/
|
||||||
@@ -83,17 +84,17 @@ int cc_signTreeSecp256k1Msg32(CC *cond, const unsigned char *private
|
|||||||
size_t cc_conditionBinary(const CC *cond, unsigned char *buf);
|
size_t cc_conditionBinary(const CC *cond, unsigned char *buf);
|
||||||
size_t cc_fulfillmentBinary(const CC *cond, unsigned char *buf, size_t bufLength);
|
size_t cc_fulfillmentBinary(const CC *cond, unsigned char *buf, size_t bufLength);
|
||||||
static int cc_secp256k1VerifyTreeMsg32(const CC *cond, const unsigned char *msg32);
|
static int cc_secp256k1VerifyTreeMsg32(const CC *cond, const unsigned char *msg32);
|
||||||
struct CC* cc_conditionFromJSON(cJSON *params, unsigned char *err);
|
struct CC* cc_conditionFromJSON(cJSON *params, char *err);
|
||||||
struct CC* cc_conditionFromJSONString(const unsigned char *json, unsigned char *err);
|
struct CC* cc_conditionFromJSONString(const char *json, char *err);
|
||||||
struct CC* cc_readConditionBinary(const unsigned char *cond_bin, size_t cond_bin_len);
|
struct CC* cc_readConditionBinary(const unsigned char *cond_bin, size_t cond_bin_len);
|
||||||
struct CC* cc_readFulfillmentBinary(const unsigned char *ffill_bin, size_t ffill_bin_len);
|
struct CC* cc_readFulfillmentBinary(const unsigned char *ffill_bin, size_t ffill_bin_len);
|
||||||
struct cJSON* cc_conditionToJSON(const CC *cond);
|
struct cJSON* cc_conditionToJSON(const CC *cond);
|
||||||
unsigned char* cc_conditionToJSONString(const CC *cond);
|
char* cc_conditionToJSONString(const CC *cond);
|
||||||
unsigned char* cc_conditionUri(const CC *cond);
|
char* cc_conditionUri(const CC *cond);
|
||||||
unsigned char* cc_jsonRPC(unsigned char *request);
|
char* cc_jsonRPC(char *request);
|
||||||
unsigned long cc_getCost(const CC *cond);
|
|
||||||
enum CCTypeId cc_typeId(const CC *cond);
|
|
||||||
char* cc_typeName(const CC *cond);
|
char* cc_typeName(const CC *cond);
|
||||||
|
enum CCTypeId cc_typeId(const CC *cond);
|
||||||
|
unsigned long cc_getCost(const CC *cond);
|
||||||
uint32_t cc_typeMask(const CC *cond);
|
uint32_t cc_typeMask(const CC *cond);
|
||||||
void cc_free(struct CC *cond);
|
void cc_free(struct CC *cond);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
|
||||||
|
|
||||||
static struct CCType *typeRegistry[] = {
|
struct CCType *CCTypeRegistry[] = {
|
||||||
&cc_preimageType,
|
&cc_preimageType,
|
||||||
&cc_prefixType,
|
&cc_prefixType,
|
||||||
&cc_thresholdType,
|
&cc_thresholdType,
|
||||||
@@ -28,7 +28,7 @@ static struct CCType *typeRegistry[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int typeRegistryLength = sizeof(typeRegistry) / sizeof(typeRegistry[0]);
|
int CCTypeRegistryLength = sizeof(CCTypeRegistry) / sizeof(CCTypeRegistry[0]);
|
||||||
|
|
||||||
|
|
||||||
void appendUriSubtypes(uint32_t mask, unsigned char *buf) {
|
void appendUriSubtypes(uint32_t mask, unsigned char *buf) {
|
||||||
@@ -37,10 +37,10 @@ void appendUriSubtypes(uint32_t mask, unsigned char *buf) {
|
|||||||
if (mask & 1 << i) {
|
if (mask & 1 << i) {
|
||||||
if (append) {
|
if (append) {
|
||||||
strcat(buf, ",");
|
strcat(buf, ",");
|
||||||
strcat(buf, typeRegistry[i]->name);
|
strcat(buf, CCTypeRegistry[i]->name);
|
||||||
} else {
|
} else {
|
||||||
strcat(buf, "&subtypes=");
|
strcat(buf, "&subtypes=");
|
||||||
strcat(buf, typeRegistry[i]->name);
|
strcat(buf, CCTypeRegistry[i]->name);
|
||||||
append = 1;
|
append = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ void appendUriSubtypes(uint32_t mask, unsigned char *buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned char *cc_conditionUri(const CC *cond) {
|
char *cc_conditionUri(const CC *cond) {
|
||||||
unsigned char *fp = cond->type->fingerprint(cond);
|
unsigned char *fp = cond->type->fingerprint(cond);
|
||||||
if (!fp) return NULL;
|
if (!fp) return NULL;
|
||||||
|
|
||||||
@@ -158,9 +158,9 @@ unsigned long cc_getCost(const CC *cond) {
|
|||||||
|
|
||||||
|
|
||||||
CCType *getTypeByAsnEnum(Condition_PR present) {
|
CCType *getTypeByAsnEnum(Condition_PR present) {
|
||||||
for (int i=0; i<typeRegistryLength; i++) {
|
for (int i=0; i<CCTypeRegistryLength; i++) {
|
||||||
if (typeRegistry[i] != NULL && typeRegistry[i]->asnType == present) {
|
if (CCTypeRegistry[i] != NULL && CCTypeRegistry[i]->asnType == present) {
|
||||||
return typeRegistry[i];
|
return CCTypeRegistry[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -245,7 +245,7 @@ CC *cc_readConditionBinary(const unsigned char *cond_bin, size_t length) {
|
|||||||
asn_dec_rval_t rval;
|
asn_dec_rval_t rval;
|
||||||
rval = ber_decode(0, &asn_DEF_Condition, (void **)&asnCond, cond_bin, length);
|
rval = ber_decode(0, &asn_DEF_Condition, (void **)&asnCond, cond_bin, length);
|
||||||
if (rval.code != RC_OK) {
|
if (rval.code != RC_OK) {
|
||||||
printf("Failed reading condition binary\n");
|
fprintf(stderr, "Failed reading condition binary\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
CC *cond = mkAnon(asnCond);
|
CC *cond = mkAnon(asnCond);
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ extern "C" {
|
|||||||
* Condition Type */
|
* Condition Type */
|
||||||
typedef struct CCType {
|
typedef struct CCType {
|
||||||
int typeId;
|
int typeId;
|
||||||
unsigned char name[100];
|
char name[100];
|
||||||
Condition_PR asnType;
|
Condition_PR asnType;
|
||||||
int (*visitChildren)(CC *cond, CCVisitor visitor);
|
int (*visitChildren)(CC *cond, CCVisitor visitor);
|
||||||
unsigned char *(*fingerprint)(const CC *cond);
|
unsigned char *(*fingerprint)(const CC *cond);
|
||||||
unsigned long (*getCost)(const CC *cond);
|
unsigned long (*getCost)(const CC *cond);
|
||||||
uint32_t (*getSubtypes)(const CC *cond);
|
uint32_t (*getSubtypes)(const CC *cond);
|
||||||
CC *(*fromJSON)(const cJSON *params, unsigned char *err);
|
CC *(*fromJSON)(const cJSON *params, char *err);
|
||||||
void (*toJSON)(const CC *cond, cJSON *params);
|
void (*toJSON)(const CC *cond, cJSON *params);
|
||||||
CC *(*fromFulfillment)(const Fulfillment_t *ffill);
|
CC *(*fromFulfillment)(const Fulfillment_t *ffill);
|
||||||
Fulfillment_t *(*toFulfillment)(const CC *cond);
|
Fulfillment_t *(*toFulfillment)(const CC *cond);
|
||||||
@@ -38,8 +38,8 @@ typedef struct CCType {
|
|||||||
/*
|
/*
|
||||||
* Globals
|
* Globals
|
||||||
*/
|
*/
|
||||||
static struct CCType *typeRegistry[];
|
struct CCType *CCTypeRegistry[];
|
||||||
static int typeRegistryLength;
|
int CCTypeRegistryLength;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -50,7 +50,7 @@ static CC *mkAnon(const Condition_t *asnCond);
|
|||||||
static void asnCondition(const CC *cond, Condition_t *asn);
|
static void asnCondition(const CC *cond, Condition_t *asn);
|
||||||
static Condition_t *asnConditionNew(const CC *cond);
|
static Condition_t *asnConditionNew(const CC *cond);
|
||||||
static Fulfillment_t *asnFulfillmentNew(const CC *cond);
|
static Fulfillment_t *asnFulfillmentNew(const CC *cond);
|
||||||
static cJSON *jsonEncodeCondition(cJSON *params, unsigned char *err);
|
static cJSON *jsonEncodeCondition(cJSON *params, char *err);
|
||||||
static struct CC *fulfillmentToCC(Fulfillment_t *ffill);
|
static struct CC *fulfillmentToCC(Fulfillment_t *ffill);
|
||||||
static struct CCType *getTypeByAsnEnum(Condition_PR present);
|
static struct CCType *getTypeByAsnEnum(Condition_PR present);
|
||||||
|
|
||||||
@@ -62,11 +62,11 @@ unsigned char *base64_encode(const unsigned char *data, size_t input_length);
|
|||||||
unsigned char *base64_decode(const unsigned char *data_, size_t *output_length);
|
unsigned char *base64_decode(const unsigned char *data_, size_t *output_length);
|
||||||
unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp);
|
unsigned char *hashFingerprintContents(asn_TYPE_descriptor_t *asnType, void *fp);
|
||||||
void dumpStr(unsigned char *str, size_t len);
|
void dumpStr(unsigned char *str, size_t len);
|
||||||
int checkString(const cJSON *value, unsigned char *key, unsigned char *err);
|
int checkString(const cJSON *value, char *key, char *err);
|
||||||
int checkDecodeBase64(const cJSON *value, unsigned char *key, unsigned char *err, unsigned char **data, size_t *size);
|
int checkDecodeBase64(const cJSON *value, char *key, char *err, unsigned char **data, size_t *size);
|
||||||
int jsonGetBase64(const cJSON *params, unsigned char *key, unsigned char *err, unsigned char **data, size_t *size);
|
int jsonGetBase64(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size);
|
||||||
int jsonGetBase64Optional(const cJSON *params, unsigned char *key, unsigned char *err, unsigned char **data, size_t *size);
|
int jsonGetBase64Optional(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size);
|
||||||
void jsonAddBase64(cJSON *params, unsigned char *key, unsigned char *bin, size_t size);
|
void jsonAddBase64(cJSON *params, char *key, unsigned char *bin, size_t size);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ static cJSON *jsonFulfillment(CC *cond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CC *cc_conditionFromJSON(cJSON *params, unsigned char *err) {
|
CC *cc_conditionFromJSON(cJSON *params, char *err) {
|
||||||
if (!params || !cJSON_IsObject(params)) {
|
if (!params || !cJSON_IsObject(params)) {
|
||||||
strcpy(err, "Condition params must be an object");
|
strcpy(err, "Condition params must be an object");
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -44,10 +44,10 @@ CC *cc_conditionFromJSON(cJSON *params, unsigned char *err) {
|
|||||||
strcpy(err, "\"type\" must be a string");
|
strcpy(err, "\"type\" must be a string");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
for (int i=0; i<typeRegistryLength; i++) {
|
for (int i=0; i<CCTypeRegistryLength; i++) {
|
||||||
if (typeRegistry[i] != NULL) {
|
if (CCTypeRegistry[i] != NULL) {
|
||||||
if (0 == strcmp(typeName->valuestring, typeRegistry[i]->name)) {
|
if (0 == strcmp(typeName->valuestring, CCTypeRegistry[i]->name)) {
|
||||||
return typeRegistry[i]->fromJSON(params, err);
|
return CCTypeRegistry[i]->fromJSON(params, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ CC *cc_conditionFromJSON(cJSON *params, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CC *cc_conditionFromJSONString(const unsigned char *data, unsigned char *err) {
|
CC *cc_conditionFromJSONString(const char *data, char *err) {
|
||||||
cJSON *params = cJSON_Parse(data);
|
cJSON *params = cJSON_Parse(data);
|
||||||
CC *out = cc_conditionFromJSON(params, err);
|
CC *out = cc_conditionFromJSON(params, err);
|
||||||
cJSON_Delete(params);
|
cJSON_Delete(params);
|
||||||
@@ -64,7 +64,7 @@ CC *cc_conditionFromJSONString(const unsigned char *data, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonEncodeCondition(cJSON *params, unsigned char *err) {
|
static cJSON *jsonEncodeCondition(cJSON *params, char *err) {
|
||||||
CC *cond = cc_conditionFromJSON(params, err);
|
CC *cond = cc_conditionFromJSON(params, err);
|
||||||
cJSON *out = NULL;
|
cJSON *out = NULL;
|
||||||
if (cond != NULL) {
|
if (cond != NULL) {
|
||||||
@@ -75,7 +75,7 @@ static cJSON *jsonEncodeCondition(cJSON *params, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonEncodeFulfillment(cJSON *params, unsigned char *err) {
|
static cJSON *jsonEncodeFulfillment(cJSON *params, char *err) {
|
||||||
CC *cond = cc_conditionFromJSON(params, err);
|
CC *cond = cc_conditionFromJSON(params, err);
|
||||||
cJSON *out = NULL;
|
cJSON *out = NULL;
|
||||||
if (cond != NULL) {
|
if (cond != NULL) {
|
||||||
@@ -86,14 +86,14 @@ static cJSON *jsonEncodeFulfillment(cJSON *params, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonErr(unsigned char *err) {
|
static cJSON *jsonErr(char *err) {
|
||||||
cJSON *out = cJSON_CreateObject();
|
cJSON *out = cJSON_CreateObject();
|
||||||
cJSON_AddItemToObject(out, "error", cJSON_CreateString(err));
|
cJSON_AddItemToObject(out, "error", cJSON_CreateString(err));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonVerifyFulfillment(cJSON *params, unsigned char *err) {
|
static cJSON *jsonVerifyFulfillment(cJSON *params, char *err) {
|
||||||
unsigned char *ffill_bin = 0, *msg = 0, *cond_bin = 0;
|
unsigned char *ffill_bin = 0, *msg = 0, *cond_bin = 0;
|
||||||
size_t ffill_bin_len, msg_len, cond_bin_len;
|
size_t ffill_bin_len, msg_len, cond_bin_len;
|
||||||
cJSON *out = 0;
|
cJSON *out = 0;
|
||||||
@@ -121,7 +121,7 @@ END:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonDecodeFulfillment(cJSON *params, unsigned char *err) {
|
static cJSON *jsonDecodeFulfillment(cJSON *params, char *err) {
|
||||||
size_t ffill_bin_len;
|
size_t ffill_bin_len;
|
||||||
unsigned char *ffill_bin;
|
unsigned char *ffill_bin;
|
||||||
if (!jsonGetBase64(params, "fulfillment", err, &ffill_bin, &ffill_bin_len))
|
if (!jsonGetBase64(params, "fulfillment", err, &ffill_bin, &ffill_bin_len))
|
||||||
@@ -139,7 +139,7 @@ static cJSON *jsonDecodeFulfillment(cJSON *params, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonDecodeCondition(cJSON *params, unsigned char *err) {
|
static cJSON *jsonDecodeCondition(cJSON *params, char *err) {
|
||||||
size_t cond_bin_len;
|
size_t cond_bin_len;
|
||||||
unsigned char *cond_bin;
|
unsigned char *cond_bin;
|
||||||
if (!jsonGetBase64(params, "bin", err, &cond_bin, &cond_bin_len))
|
if (!jsonGetBase64(params, "bin", err, &cond_bin, &cond_bin_len))
|
||||||
@@ -160,7 +160,7 @@ static cJSON *jsonDecodeCondition(cJSON *params, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonSignTreeEd25519(cJSON *params, unsigned char *err) {
|
static cJSON *jsonSignTreeEd25519(cJSON *params, char *err) {
|
||||||
cJSON *out = 0;
|
cJSON *out = 0;
|
||||||
unsigned char *msg = 0, *sk = 0;
|
unsigned char *msg = 0, *sk = 0;
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ END:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonSignTreeSecp256k1(cJSON *params, unsigned char *err) {
|
static cJSON *jsonSignTreeSecp256k1(cJSON *params, char *err) {
|
||||||
cJSON *out = 0;
|
cJSON *out = 0;
|
||||||
unsigned char *msg = 0, *sk = 0;
|
unsigned char *msg = 0, *sk = 0;
|
||||||
|
|
||||||
@@ -244,22 +244,22 @@ cJSON *cc_conditionToJSON(const CC *cond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned char *cc_conditionToJSONString(const CC *cond) {
|
char *cc_conditionToJSONString(const CC *cond) {
|
||||||
assert(cond != NULL);
|
assert(cond != NULL);
|
||||||
cJSON *params = cc_conditionToJSON(cond);
|
cJSON *params = cc_conditionToJSON(cond);
|
||||||
unsigned char *out = cJSON_Print(params);
|
char *out = cJSON_Print(params);
|
||||||
cJSON_Delete(params);
|
cJSON_Delete(params);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonListMethods(cJSON *params, unsigned char *err);
|
static cJSON *jsonListMethods(cJSON *params, char *err);
|
||||||
|
|
||||||
|
|
||||||
typedef struct JsonMethod {
|
typedef struct JsonMethod {
|
||||||
unsigned char *name;
|
char *name;
|
||||||
cJSON* (*method)(cJSON *params, unsigned char *err);
|
cJSON* (*method)(cJSON *params, char *err);
|
||||||
unsigned char *description;
|
char *description;
|
||||||
} JsonMethod;
|
} JsonMethod;
|
||||||
|
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ static JsonMethod cc_jsonMethods[] = {
|
|||||||
static int nJsonMethods = sizeof(cc_jsonMethods) / sizeof(*cc_jsonMethods);
|
static int nJsonMethods = sizeof(cc_jsonMethods) / sizeof(*cc_jsonMethods);
|
||||||
|
|
||||||
|
|
||||||
static cJSON *jsonListMethods(cJSON *params, unsigned char *err) {
|
static cJSON *jsonListMethods(cJSON *params, char *err) {
|
||||||
cJSON *list = cJSON_CreateArray();
|
cJSON *list = cJSON_CreateArray();
|
||||||
for (int i=0; i<nJsonMethods; i++) {
|
for (int i=0; i<nJsonMethods; i++) {
|
||||||
JsonMethod method = cc_jsonMethods[i];
|
JsonMethod method = cc_jsonMethods[i];
|
||||||
@@ -293,7 +293,7 @@ static cJSON *jsonListMethods(cJSON *params, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static cJSON* execJsonRPC(cJSON *root, unsigned char *err) {
|
static cJSON* execJsonRPC(cJSON *root, char *err) {
|
||||||
cJSON *method_item = cJSON_GetObjectItem(root, "method");
|
cJSON *method_item = cJSON_GetObjectItem(root, "method");
|
||||||
|
|
||||||
if (!cJSON_IsString(method_item)) {
|
if (!cJSON_IsString(method_item)) {
|
||||||
@@ -316,8 +316,8 @@ static cJSON* execJsonRPC(cJSON *root, unsigned char *err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned char *cc_jsonRPC(unsigned char* input) {
|
char *cc_jsonRPC(char* input) {
|
||||||
unsigned char err[1000] = "\0";
|
char err[1000] = "\0";
|
||||||
cJSON *out;
|
cJSON *out;
|
||||||
cJSON *root = cJSON_Parse(input);
|
cJSON *root = cJSON_Parse(input);
|
||||||
if (!root) out = jsonErr("Error parsing JSON request");
|
if (!root) out = jsonErr("Error parsing JSON request");
|
||||||
@@ -325,7 +325,7 @@ unsigned char *cc_jsonRPC(unsigned char* input) {
|
|||||||
out = execJsonRPC(root, err);
|
out = execJsonRPC(root, err);
|
||||||
if (NULL == out) out = jsonErr(err);
|
if (NULL == out) out = jsonErr(err);
|
||||||
}
|
}
|
||||||
unsigned char *res = cJSON_Print(out);
|
char *res = cJSON_Print(out);
|
||||||
cJSON_Delete(out);
|
cJSON_Delete(out);
|
||||||
cJSON_Delete(root);
|
cJSON_Delete(root);
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ typedef struct CCSecp256k1SigningData {
|
|||||||
* Visitor that signs an secp256k1 condition if it has a matching public key
|
* Visitor that signs an secp256k1 condition if it has a matching public key
|
||||||
*/
|
*/
|
||||||
static int secp256k1Sign(CC *cond, CCVisitor visitor) {
|
static int secp256k1Sign(CC *cond, CCVisitor visitor) {
|
||||||
if (cond->type->typeId != cc_secp256k1Type.typeId) return 1;
|
if (cond->type->typeId != CC_Secp256k1) return 1;
|
||||||
CCSecp256k1SigningData *signing = (CCSecp256k1SigningData*) visitor.context;
|
CCSecp256k1SigningData *signing = (CCSecp256k1SigningData*) visitor.context;
|
||||||
if (0 != memcmp(cond->publicKey, signing->pk, SECP256K1_PK_SIZE)) return 1;
|
if (0 != memcmp(cond->publicKey, signing->pk, SECP256K1_PK_SIZE)) return 1;
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ static int secp256k1Sign(CC *cond, CCVisitor visitor) {
|
|||||||
* Sign secp256k1 conditions in a tree
|
* Sign secp256k1 conditions in a tree
|
||||||
*/
|
*/
|
||||||
int cc_signTreeSecp256k1Msg32(CC *cond, const unsigned char *privateKey, const unsigned char *msg32) {
|
int cc_signTreeSecp256k1Msg32(CC *cond, const unsigned char *privateKey, const unsigned char *msg32) {
|
||||||
if (cc_typeMask(cond) & (1 << cc_preimageType.typeId)) {
|
if (cc_typeMask(cond) & (1 << CC_Prefix)) {
|
||||||
// No support for prefix currently, due to pending protocol decision on
|
// No support for prefix currently, due to pending protocol decision on
|
||||||
// how to combine message and prefix into 32 byte hash
|
// how to combine message and prefix into 32 byte hash
|
||||||
return 0;
|
return 0;
|
||||||
@@ -159,7 +159,10 @@ int cc_signTreeSecp256k1Msg32(CC *cond, const unsigned char *privateKey, const u
|
|||||||
lockSign();
|
lockSign();
|
||||||
int rc = secp256k1_ec_pubkey_create(ec_ctx_sign, &spk, privateKey);
|
int rc = secp256k1_ec_pubkey_create(ec_ctx_sign, &spk, privateKey);
|
||||||
unlockSign();
|
unlockSign();
|
||||||
if (rc != 1) return 0;
|
if (rc != 1) {
|
||||||
|
fprintf(stderr, "Cryptoconditions couldn't derive secp256k1 pubkey\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// serialize pubkey
|
// serialize pubkey
|
||||||
unsigned char *publicKey = calloc(1, SECP256K1_PK_SIZE);
|
unsigned char *publicKey = calloc(1, SECP256K1_PK_SIZE);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ static unsigned long thresholdCost(const CC *cond) {
|
|||||||
|
|
||||||
|
|
||||||
static int thresholdVisitChildren(CC *cond, CCVisitor visitor) {
|
static int thresholdVisitChildren(CC *cond, CCVisitor visitor) {
|
||||||
for (int i=0; i<cond->threshold; i++) {
|
for (int i=0; i<cond->size; i++) {
|
||||||
if (!cc_visit(cond->subconditions[i], visitor)) {
|
if (!cc_visit(cond->subconditions[i], visitor)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ static void thresholdToJSON(const CC *cond, cJSON *params) {
|
|||||||
static int thresholdIsFulfilled(const CC *cond) {
|
static int thresholdIsFulfilled(const CC *cond) {
|
||||||
int nFulfilled = 0;
|
int nFulfilled = 0;
|
||||||
for (int i=0; i<cond->threshold; i++) {
|
for (int i=0; i<cond->threshold; i++) {
|
||||||
if (!cc_isFulfilled(cond->subconditions[i])) {
|
if (cc_isFulfilled(cond->subconditions[i])) {
|
||||||
nFulfilled++;
|
nFulfilled++;
|
||||||
}
|
}
|
||||||
if (nFulfilled == cond->threshold) {
|
if (nFulfilled == cond->threshold) {
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ void dumpStr(unsigned char *str, size_t len) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int checkString(const cJSON *value, unsigned char *key, unsigned char *err) {
|
int checkString(const cJSON *value, char *key, char *err) {
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
sprintf(err, "%s is required", key);
|
sprintf(err, "%s is required", key);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -153,7 +153,7 @@ int checkString(const cJSON *value, unsigned char *key, unsigned char *err) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int checkDecodeBase64(const cJSON *value, unsigned char *key, unsigned char *err, unsigned char **data, size_t *size) {
|
int checkDecodeBase64(const cJSON *value, char *key, char *err, unsigned char **data, size_t *size) {
|
||||||
if (!checkString(value, key, err)) {
|
if (!checkString(value, key, err)) {
|
||||||
sprintf(err, "%s must be valid base64 string", key);
|
sprintf(err, "%s must be valid base64 string", key);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -168,7 +168,7 @@ int checkDecodeBase64(const cJSON *value, unsigned char *key, unsigned char *err
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int jsonGetBase64(const cJSON *params, unsigned char *key, unsigned char *err, unsigned char **data, size_t *size)
|
int jsonGetBase64(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size)
|
||||||
{
|
{
|
||||||
cJSON *item = cJSON_GetObjectItem(params, key);
|
cJSON *item = cJSON_GetObjectItem(params, key);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
@@ -179,7 +179,7 @@ int jsonGetBase64(const cJSON *params, unsigned char *key, unsigned char *err, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int jsonGetBase64Optional(const cJSON *params, unsigned char *key, unsigned char *err, unsigned char **data, size_t *size) {
|
int jsonGetBase64Optional(const cJSON *params, char *key, char *err, unsigned char **data, size_t *size) {
|
||||||
cJSON *item = cJSON_GetObjectItem(params, key);
|
cJSON *item = cJSON_GetObjectItem(params, key);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -188,7 +188,7 @@ int jsonGetBase64Optional(const cJSON *params, unsigned char *key, unsigned char
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void jsonAddBase64(cJSON *params, unsigned char *key, unsigned char *bin, size_t size) {
|
void jsonAddBase64(cJSON *params, char *key, unsigned char *bin, size_t size) {
|
||||||
unsigned char *b64 = base64_encode(bin, size);
|
unsigned char *b64 = base64_encode(bin, size);
|
||||||
cJSON_AddItemToObject(params, key, cJSON_CreateString(b64));
|
cJSON_AddItemToObject(params, key, cJSON_CreateString(b64));
|
||||||
free(b64);
|
free(b64);
|
||||||
|
|||||||
@@ -7,18 +7,6 @@ bool IsCryptoConditionsEnabled()
|
|||||||
return 0 != ASSETCHAINS_CC;
|
return 0 != ASSETCHAINS_CC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit acceptable condition types
|
|
||||||
// Prefix not enabled because no current use case, ambiguity on how to combine with secp256k1
|
|
||||||
// RSA not enabled because no current use case, not implemented
|
|
||||||
int CCEnabledTypes = 1 << CC_Secp256k1 | \
|
|
||||||
1 << CC_Threshold | \
|
|
||||||
1 << CC_Eval | \
|
|
||||||
1 << CC_Preimage | \
|
|
||||||
1 << CC_Ed25519;
|
|
||||||
|
|
||||||
|
|
||||||
int CCSigningNodes = 1 << CC_Ed25519 | 1 << CC_Secp256k1;
|
|
||||||
|
|
||||||
|
|
||||||
bool IsSupportedCryptoCondition(const CC *cond)
|
bool IsSupportedCryptoCondition(const CC *cond)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,17 @@
|
|||||||
extern int32_t ASSETCHAINS_CC;
|
extern int32_t ASSETCHAINS_CC;
|
||||||
bool IsCryptoConditionsEnabled();
|
bool IsCryptoConditionsEnabled();
|
||||||
|
|
||||||
|
// Limit acceptable condition types
|
||||||
|
// Prefix not enabled because no current use case, ambiguity on how to combine with secp256k1
|
||||||
|
// RSA not enabled because no current use case, not implemented
|
||||||
|
const int CCEnabledTypes = 1 << CC_Secp256k1 | \
|
||||||
|
1 << CC_Threshold | \
|
||||||
|
1 << CC_Eval | \
|
||||||
|
1 << CC_Preimage | \
|
||||||
|
1 << CC_Ed25519;
|
||||||
|
|
||||||
|
const int CCSigningNodes = 1 << CC_Ed25519 | 1 << CC_Secp256k1;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the server can accept the condition based on it's structure / types
|
* Check if the server can accept the condition based on it's structure / types
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ public:
|
|||||||
const std::vector<unsigned char>& ffillBin,
|
const std::vector<unsigned char>& ffillBin,
|
||||||
const CScript& scriptCode,
|
const CScript& scriptCode,
|
||||||
uint32_t consensusBranchId) const;
|
uint32_t consensusBranchId) const;
|
||||||
VerifyEval GetCCEval() const;
|
virtual VerifyEval GetCCEval() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MutableTransactionSignatureChecker : public TransactionSignatureChecker
|
class MutableTransactionSignatureChecker : public TransactionSignatureChecker
|
||||||
|
|||||||
12
src/test-komodo/main.cpp
Normal file
12
src/test-komodo/main.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "key.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "crypto/common.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
assert(init_and_check_sodium() != -1);
|
||||||
|
ECC_Start();
|
||||||
|
|
||||||
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
226
src/test-komodo/test_cryptoconditions.cpp
Normal file
226
src/test-komodo/test_cryptoconditions.cpp
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
#include <cryptoconditions.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "base58.h"
|
||||||
|
#include "key.h"
|
||||||
|
#include "komodo_cc.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
|
#include "script/serverchecker.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define VCH(a,b) std::vector<unsigned char>(a, a + b)
|
||||||
|
|
||||||
|
std::string pubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47";
|
||||||
|
std::string secret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU";
|
||||||
|
CKey notaryKey;
|
||||||
|
|
||||||
|
|
||||||
|
char ccjsonerr[1000] = "\0";
|
||||||
|
#define CCFromJson(o,s) \
|
||||||
|
o = cc_conditionFromJSONString(s, ccjsonerr); \
|
||||||
|
if (!o) FAIL() << "bad json: " << ccjsonerr;
|
||||||
|
|
||||||
|
|
||||||
|
CScript CCPubKey(const CC *cond) {
|
||||||
|
unsigned char buf[1000];
|
||||||
|
size_t len = cc_conditionBinary(cond, buf);
|
||||||
|
return CScript() << VCH(buf, len) << OP_CHECKCRYPTOCONDITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CScript CCSig(const CC *cond) {
|
||||||
|
unsigned char buf[1000];
|
||||||
|
size_t len = cc_fulfillmentBinary(cond, buf, 1000);
|
||||||
|
auto ffill = VCH(buf, len);
|
||||||
|
ffill.push_back(SIGHASH_ALL);
|
||||||
|
return CScript() << ffill;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CCSign(CMutableTransaction &tx, CC *cond) {
|
||||||
|
tx.vin.resize(1);
|
||||||
|
PrecomputedTransactionData txdata(tx);
|
||||||
|
uint256 sighash = SignatureHash(CCPubKey(cond), tx, 0, SIGHASH_ALL, 0, 0, &txdata);
|
||||||
|
|
||||||
|
int out = cc_signTreeSecp256k1Msg32(cond, notaryKey.begin(), sighash.begin());
|
||||||
|
tx.vin[0].scriptSig = CCSig(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CCTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
SelectParams(CBaseChainParams::REGTEST);
|
||||||
|
// Notary key
|
||||||
|
CBitcoinSecret vchSecret;
|
||||||
|
// this returns false due to network prefix mismatch but works anyway
|
||||||
|
vchSecret.SetString(secret);
|
||||||
|
notaryKey = vchSecret.GetKey();
|
||||||
|
}
|
||||||
|
virtual void SetUp() {
|
||||||
|
// enable CC
|
||||||
|
ASSETCHAINS_CC = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(CCTest, testIsPayToCryptoCondition)
|
||||||
|
{
|
||||||
|
CScript s = CScript() << VCH("a", 1);
|
||||||
|
ASSERT_FALSE(s.IsPayToCryptoCondition());
|
||||||
|
|
||||||
|
s = CScript() << VCH("a", 1) << OP_CHECKCRYPTOCONDITION;
|
||||||
|
ASSERT_TRUE(s.IsPayToCryptoCondition());
|
||||||
|
|
||||||
|
s = CScript() << OP_CHECKCRYPTOCONDITION;
|
||||||
|
ASSERT_FALSE(s.IsPayToCryptoCondition());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(CCTest, testMayAcceptCryptoCondition)
|
||||||
|
{
|
||||||
|
CC *cond;
|
||||||
|
|
||||||
|
// ok
|
||||||
|
CCFromJson(cond, R"!!(
|
||||||
|
{ "type": "threshold-sha-256",
|
||||||
|
"threshold": 2,
|
||||||
|
"subfulfillments": [
|
||||||
|
{ "type": "secp256k1-sha-256", "publicKey": "AgWorQwdvFFfFJrzd5gaq1i4Nq8AjU16shvXb6+AVQtH" }
|
||||||
|
]
|
||||||
|
})!!");
|
||||||
|
ASSERT_TRUE(CCPubKey(cond).MayAcceptCryptoCondition());
|
||||||
|
|
||||||
|
|
||||||
|
// prefix not allowed
|
||||||
|
CCFromJson(cond, R"!!(
|
||||||
|
{ "type": "prefix-sha-256",
|
||||||
|
"prefix": "abc",
|
||||||
|
"maxMessageLength": 10,
|
||||||
|
"subfulfillment":
|
||||||
|
{ "type": "secp256k1-sha-256", "publicKey": "AgWorQwdvFFfFJrzd5gaq1i4Nq8AjU16shvXb6+AVQtH" }
|
||||||
|
})!!");
|
||||||
|
ASSERT_FALSE(CCPubKey(cond).MayAcceptCryptoCondition());
|
||||||
|
|
||||||
|
|
||||||
|
// has no signature nodes
|
||||||
|
CCFromJson(cond, R"!!(
|
||||||
|
{ "type": "threshold-sha-256",
|
||||||
|
"threshold": 1,
|
||||||
|
"subfulfillments": [
|
||||||
|
{ "type": "eval-sha-256", "method": "test", "params": "" },
|
||||||
|
{ "type": "eval-sha-256", "method": "test", "params": "" }
|
||||||
|
]
|
||||||
|
})!!");
|
||||||
|
ASSERT_FALSE(CCPubKey(cond).MayAcceptCryptoCondition());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(CCTest, testVerifyCryptoCondition)
|
||||||
|
{
|
||||||
|
CC *cond;
|
||||||
|
ScriptError error;
|
||||||
|
CMutableTransaction mtxTo;
|
||||||
|
|
||||||
|
auto Verify = [&] (const CC *cond) {
|
||||||
|
CAmount amount;
|
||||||
|
CTransaction txTo(mtxTo);
|
||||||
|
PrecomputedTransactionData txdata(txTo);
|
||||||
|
auto checker = ServerTransactionSignatureChecker(&txTo, 0, amount, false, txdata);
|
||||||
|
return VerifyScript(CCSig(cond), CCPubKey(cond), 0, checker, 0, &error);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ok
|
||||||
|
CCFromJson(cond, R"!!({
|
||||||
|
"type": "secp256k1-sha-256",
|
||||||
|
"publicKey": "AgWorQwdvFFfFJrzd5gaq1i4Nq8AjU16shvXb6+AVQtH"
|
||||||
|
})!!");
|
||||||
|
CCSign(mtxTo, cond);
|
||||||
|
ASSERT_TRUE(Verify(cond));
|
||||||
|
|
||||||
|
|
||||||
|
// has signature nodes
|
||||||
|
CCFromJson(cond, R"!!({
|
||||||
|
"type": "threshold-sha-256",
|
||||||
|
"threshold": 1,
|
||||||
|
"subfulfillments": [
|
||||||
|
{ "type": "preimage-sha-256", "preimage": "" },
|
||||||
|
{ "type": "secp256k1-sha-256", "publicKey": "AgWorQwdvFFfFJrzd5gaq1i4Nq8AjU16shvXb6+AVQtH" }
|
||||||
|
]
|
||||||
|
})!!");
|
||||||
|
cond->threshold = 2;
|
||||||
|
CCSign(mtxTo, cond);
|
||||||
|
ASSERT_TRUE(Verify(cond));
|
||||||
|
|
||||||
|
// no signatures; the preimage will get encoded as a fulfillment because it's cheaper
|
||||||
|
// and the secp256k1 node will get encoded as a condition
|
||||||
|
cond->threshold = 1;
|
||||||
|
ASSERT_FALSE(Verify(cond));
|
||||||
|
|
||||||
|
// here the signature is set wrong
|
||||||
|
cond->threshold = 2;
|
||||||
|
ASSERT_TRUE(Verify(cond));
|
||||||
|
memset(cond->subconditions[1]->signature, 0, 32);
|
||||||
|
ASSERT_FALSE(Verify(cond));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(CCTest, testVerifyEvalCondition)
|
||||||
|
{
|
||||||
|
CC *cond;
|
||||||
|
ScriptError error;
|
||||||
|
CMutableTransaction mtxTo;
|
||||||
|
|
||||||
|
auto Verify = [&] (const CC *cond) {
|
||||||
|
CAmount amount;
|
||||||
|
CTransaction txTo(mtxTo);
|
||||||
|
PrecomputedTransactionData txdata(txTo);
|
||||||
|
auto checker = ServerTransactionSignatureChecker(&txTo, 0, amount, false, txdata);
|
||||||
|
return VerifyScript(CCSig(cond), CCPubKey(cond), 0, checker, 0, &error);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ok
|
||||||
|
CCFromJson(cond, R"!!({
|
||||||
|
"type": "threshold-sha-256",
|
||||||
|
"threshold": 2,
|
||||||
|
"subfulfillments": [
|
||||||
|
{ "type": "secp256k1-sha-256", "publicKey": "AgWorQwdvFFfFJrzd5gaq1i4Nq8AjU16shvXb6+AVQtH" },
|
||||||
|
{ "type": "eval-sha-256", "method": "TestEval", "params": "" }
|
||||||
|
]})!!");
|
||||||
|
CC *ecCond = cond->subconditions[1];
|
||||||
|
ecCond->paramsBin = (unsigned char*) "TestEval";
|
||||||
|
ecCond->paramsBinLength = 8;
|
||||||
|
CCSign(mtxTo, cond); // will reorder subconditions
|
||||||
|
ASSERT_TRUE(Verify(cond));
|
||||||
|
|
||||||
|
ecCond->paramsBin = (unsigned char*) "FailEval";
|
||||||
|
ASSERT_FALSE(Verify(cond));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(CCTest, testCryptoConditionsDisabled)
|
||||||
|
{
|
||||||
|
CC *cond;
|
||||||
|
ScriptError error;
|
||||||
|
CMutableTransaction mtxTo;
|
||||||
|
|
||||||
|
auto Verify = [&] (const CC *cond) {
|
||||||
|
CAmount amount;
|
||||||
|
CTransaction txTo(mtxTo);
|
||||||
|
PrecomputedTransactionData txdata(txTo);
|
||||||
|
auto checker = ServerTransactionSignatureChecker(&txTo, 0, amount, false, txdata);
|
||||||
|
return VerifyScript(CCSig(cond), CCPubKey(cond), 0, checker, 0, &error);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ok
|
||||||
|
CCFromJson(cond, R"!!({
|
||||||
|
"type": "secp256k1-sha-256",
|
||||||
|
"publicKey": "AgWorQwdvFFfFJrzd5gaq1i4Nq8AjU16shvXb6+AVQtH"
|
||||||
|
})!!");
|
||||||
|
CCSign(mtxTo, cond);
|
||||||
|
ASSERT_TRUE(Verify(cond));
|
||||||
|
|
||||||
|
ASSETCHAINS_CC = 0;
|
||||||
|
ASSERT_FALSE(Verify(cond));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user