Scott Sadler
6 years ago
19 changed files with 336 additions and 465 deletions
@ -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}} |
@ -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 |
@ -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(); |
|||
} |
@ -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)); |
|||
} |
Loading…
Reference in new issue