diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index b1284c1a67..09c2b052cb 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -114,9 +114,8 @@ 'policytransactions.py', 'onboard.py', 'onboard_cit.py', - 'onboardencrypted.py', 'onboardmanual.py', - 'onboardmanualencrypted.py', + 'onboardmanual_cit.py', 'fixedfee.py', # Accounts not supported #'wallet-accounts.py', diff --git a/qa/rpc-tests/onboard.py b/qa/rpc-tests/onboard.py index a1bf17ac51..61c7f73589 100755 --- a/qa/rpc-tests/onboard.py +++ b/qa/rpc-tests/onboard.py @@ -417,7 +417,7 @@ def run_test (self): multiAddress2=self.nodes[1].createmultisig(2,[clientAddress1['pubkey'],clientAddress2['pubkey'],clientAddress4['pubkey']]) self.nodes[1].addmultitowhitelist(multiAddress2['address'],[clientAddress1['derivedpubkey'],clientAddress2['derivedpubkey'],clientAddress3['derivedpubkey']],2) except JSONRPCException as e: - assert("add_multisig_whitelist: address does not derive from public keys when tweaked with contract hash" in e.error['message']) + assert("TryValid: address does not derive from public keys when tweaked with contract hash" in e.error['message']) else: raise AssertionError("P2SH multisig with a different third pubkey has been validated and accepted to the whitelist.") @@ -425,7 +425,7 @@ def run_test (self): multiAddress2=self.nodes[1].createmultisig(2,[clientAddress1['pubkey'],clientAddress2['pubkey'],clientAddress3['derivedpubkey']]) self.nodes[1].addmultitowhitelist(multiAddress2['address'],[clientAddress1['derivedpubkey'],clientAddress2['derivedpubkey'],clientAddress3['derivedpubkey']],2) except JSONRPCException as e: - assert("add_multisig_whitelist: address does not derive from public keys when tweaked with contract hash" in e.error['message']) + assert("TryValid: address does not derive from public keys when tweaked with contract hash" in e.error['message']) else: raise AssertionError("P2SH multisig with an untweaked third pubkey has been validated and accepted to the whitelist.") @@ -433,7 +433,7 @@ def run_test (self): multiAddress2=self.nodes[1].createmultisig(2,[clientAddress1['pubkey'],clientAddress2['pubkey'],clientAddress3['pubkey'],clientAddress4['pubkey']]) self.nodes[1].addmultitowhitelist(multiAddress2['address'],[clientAddress1['derivedpubkey'],clientAddress2['derivedpubkey'],clientAddress3['derivedpubkey']],2) except JSONRPCException as e: - assert("add_multisig_whitelist: address does not derive from public keys when tweaked with contract hash" in e.error['message']) + assert("TryValid: address does not derive from public keys when tweaked with contract hash" in e.error['message']) else: raise AssertionError("P2SH multisig with more pubkeys in redeem script than rpc has been validated and accepted to the whitelist.") diff --git a/qa/rpc-tests/onboard_cit.py b/qa/rpc-tests/onboard_cit.py index f01be7d428..ab833c9889 100755 --- a/qa/rpc-tests/onboard_cit.py +++ b/qa/rpc-tests/onboard_cit.py @@ -253,11 +253,18 @@ def run_test (self): #Onboard node0 kycfile0=self.initfile(os.path.join(self.options.tmpdir,"kycfile0.dat")) userOnboardPubKey=self.nodes[0].dumpkycfile(kycfile0) + kycfile0_plain=self.initfile(os.path.join(self.options.tmpdir,"kycfile0_plain.dat")) + print("onboard node 0 \n") + print("read kycfile0 node 0 \n") + self.nodes[0].readkycfile(kycfile0, kycfile0_plain) self.nodes[0].onboarduser(kycfile0) + print("generate block \n") self.nodes[0].generate(101) + print("sync all \n") self.sync_all() wl1file=self.initfile(os.path.join(self.options.tmpdir,"wl1.dat")) + print("dump node1 wl \n") self.nodes[1].dumpwhitelist(wl1file) nlines=self.linecount(wl1file) nlines_empty=self.linecount(wl1file_empty) @@ -434,6 +441,9 @@ def run_test (self): except JSONRPCException as e: assert("Not implemented for unencrypted whitelist" in e.error['message']) + + print("Multisig address: ") + print(multiAddress1['address']) #Adding the created p2sh to the whitelist via addmultitowhitelist rpc try: self.nodes[1].addmultitowhitelist(multiAddress1['address'],[clientAddress1['derivedpubkey'],clientAddress2['derivedpubkey'],clientAddress3['derivedpubkey']],2,"") diff --git a/qa/rpc-tests/onboardencrypted.py b/qa/rpc-tests/onboardencrypted.py index 332fdeab62..a0db395955 100755 --- a/qa/rpc-tests/onboardencrypted.py +++ b/qa/rpc-tests/onboardencrypted.py @@ -264,7 +264,11 @@ def run_test (self): wl1file=self.initfile(os.path.join(self.options.tmpdir,"wl1.dat")) self.nodes[1].dumpwhitelist(wl1file) nadd=100 - saveres=self.nodes[1].sendaddtowhitelisttx(nadd,"CBT") + try: + saveres=self.nodes[1].sendaddtowhitelisttx(nadd,"CBT") + except Exception as e: + print(e.error['message']) + assert False time.sleep(5) self.nodes[0].generate(101) self.sync_all() diff --git a/qa/rpc-tests/onboardmanual.py b/qa/rpc-tests/onboardmanual.py index 4de53e9fb7..c1b285b130 100755 --- a/qa/rpc-tests/onboardmanual.py +++ b/qa/rpc-tests/onboardmanual.py @@ -108,6 +108,16 @@ def run_test (self): self.nodes[0].readwhitelist("keys.main") os.remove("keys.main") + #Try and create kycfile when no KYC pub keys available + kycfile_test="kycfile_test.dat" + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test,[], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert('No unassigned KYC public keys available.' in message) + #Register a KYC public key policyaddr=self.nodes[0].getnewaddress() assert(self.nodes[0].querywhitelist(policyaddr)) @@ -135,13 +145,25 @@ def run_test (self): self.sync_all() #Onboard node1 - kycfile="kycfile.dat" - kycfile_normal="kycfile_normal.dat" - kycfile_multisig="kycfile_multisig.dat" - kycfile_empty="kycfile_empty.dat" + kycfile=os.path.join(self.options.tmpdir,"kycfile.dat") + kycfile_normal=os.path.join(self.options.tmpdir,"kycfile_normal.dat") + kycfile_normal_plain=os.path.join(self.options.tmpdir,"kycfile_normal_plain.dat") + kycfile_multisig=os.path.join(self.options.tmpdir,"kycfile_multisig.dat") + kycfile_multisig_plain=os.path.join(self.options.tmpdir,"kycfile_multisig_plain.dat") + kycfile_empty=os.path.join(self.options.tmpdir,"kycfile_empty.dat") #userOnboardPubKey=self.nodes[1].dumpkycfile(kycfile) - #Create empty file and checck validity + + #Test invalid parameters + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test,None, None); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid parameters, arguments 2 and 3 can't both be null" in message) + + #Create empty file and check validity try: userOnboardPubKey=self.nodes[1].createkycfile(kycfile_empty,[], []); except JSONRPCException as e: @@ -159,11 +181,15 @@ def run_test (self): onboardAddress1=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) onboardAddress2=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) try: - userOnboardPubKey=self.nodes[1].createkycfile(kycfile_normal, [{"address":onboardAddress1['address'],"pubkey":onboardAddress1['derivedpubkey']},{"address":onboardAddress2['address'],"pubkey":onboardAddress2['derivedpubkey']}], []); + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_normal, [{"address":onboardAddress1['address'],"pubkey":onboardAddress1['derivedpubkey']},{"address":onboardAddress2['address'], + "pubkey":onboardAddress2['derivedpubkey']}], + []); except JSONRPCException as e: print(e.error['message']) assert(False) + self.nodes[0].readkycfile(kycfile_normal, kycfile_normal_plain) + valkyc=self.nodes[0].validatekycfile(kycfile_normal) print(valkyc) assert(valkyc["iswhitelisted"] == False) @@ -172,6 +198,43 @@ def run_test (self): self.nodes[0].generate(101) self.sync_all() + #Test invalid parameters + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [{"address":onboardAddress1['address']},{"address":onboardAddress2['address'],"pubkey":onboardAddress2['derivedpubkey']}], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + print(message) + assert("Pubkey missing in pubkeylist" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [{"pubkey":onboardAddress1['derivedpubk\ +ey']},{"address":onboardAddress2['address'],"pubkey":onboardAddress2['derivedpubkey']}], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Address missing in pubkeylist" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [{"address":"myInvalidAddress","pubkey":onboardAddress1['derivedpubk\ +ey']},{"address":onboardAddress2['address'],"pubkey":onboardAddress2['derivedpubkey']}], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid address in pubkeylist: myInvalidAddress" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [{"address":onboardAddress1['address'],"pubkey":"myInvalidPubKey"},{"address":onboardAddress2['address'], + "pubkey":onboardAddress2['derivedpubkey']}], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid pubkey in pubkeylist: myInvalidPubKey" in message) + balance_1=self.nodes[0].getwalletinfo()["balance"]["WHITELIST"] try: self.nodes[0].onboarduser(kycfile_normal) @@ -197,11 +260,12 @@ def run_test (self): untweakedPubkeys2=[onboardAddress2['derivedpubkey'],onboardAddress3['derivedpubkey'],onboardAddress4['derivedpubkey']] untweakedPubkeys3=[onboardAddress3['derivedpubkey'],onboardAddress4['derivedpubkey']] try: - userOnboardPubKey=self.nodes[1].createkycfile(kycfile_multisig, [], [{"nmultisig":2,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + createResult=self.nodes[1].createkycfile(kycfile_multisig, [], [{"nmultisig":2,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); except JSONRPCException as e: print(e.error['message']) assert(False) + self.nodes[0].readkycfile(kycfile_multisig, kycfile_multisig_plain) valkyc=self.nodes[0].validatekycfile(kycfile_multisig) print(valkyc) assert(valkyc["iswhitelisted"] == False) @@ -216,12 +280,77 @@ def run_test (self): self.nodes[0].generate(101) self.sync_all() + valkyc=self.nodes[0].validatekycfile(kycfile_multisig) print(valkyc) + + print("querying whitelist for addresses...") + for addr in valkyc["addresses"]: + print(addr) + assert(self.nodes[0].querywhitelist(addr)) + assert(valkyc["iswhitelisted"] == True) os.remove(kycfile_multisig) + + + #Test invalid parameters + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + print(message) + assert("nmultisig missing in multisiglist" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":2},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + print(message) + assert("pubkeys missing in multisiglist" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":2.1,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + print(message) + assert("JSON integer out of range" in message) + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":16,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + print(message) + assert("nmultisig must be an integer between 1 and 15") + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":0,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("nmultisig must be an integer between 1 and 15") + + + invalidPubkeys=['myInvalidPubKey',onboardAddress2['derivedpubkey'],onboardAddress3['derivedpubkey']] + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":2,"pubkeys":invalidPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + print(message) + assert("Invalid pubkey in multisiglist: " in message) + onboardAddress1=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) onboardAddress2=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) onboardAddress3=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) diff --git a/qa/rpc-tests/onboardmanual_cit.py b/qa/rpc-tests/onboardmanual_cit.py new file mode 100755 index 0000000000..8979bb2371 --- /dev/null +++ b/qa/rpc-tests/onboardmanual_cit.py @@ -0,0 +1,573 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import filecmp +import time +import string + +class OnboardManualTest (BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 + self.extra_args = [['-txindex'] for i in range(3)] + self.extra_args[0].append("-contractintx=1") + self.extra_args[0].append("-pkhwhitelist=1") + self.extra_args[0].append("-pkhwhitelist-encrypt=0") + self.extra_args[0].append("-rescan=1") + self.extra_args[0].append("-initialfreecoins=2100000000000000") + self.extra_args[0].append("-policycoins=50000000000000") + self.extra_args[0].append("-regtest=0") + self.extra_args[0].append("-initialfreecoinsdestination=76a914bc835aff853179fa88f2900f9003bb674e17ed4288ac") + self.extra_args[0].append("-whitelistcoinsdestination=76a914427bf8530a3962ed77fd3c07d17fd466cb31c2fd88ac") + self.extra_args[1].append("-contractintx=1") + self.extra_args[1].append("-rescan=1") + self.extra_args[1].append("-regtest=0") + self.extra_args[1].append("-pkhwhitelist=1") + self.extra_args[1].append("-pkhwhitelist-encrypt=0") + self.extra_args[1].append("-initialfreecoins=2100000000000000") + self.extra_args[1].append("-policycoins=50000000000000") + self.extra_args[1].append("-initialfreecoinsdestination=76a914bc835aff853179fa88f2900f9003bb674e17ed4288ac") + self.extra_args[1].append("-whitelistcoinsdestination=76a914427bf8530a3962ed77fd3c07d17fd466cb31c2fd88ac") + self.extra_args[2].append("-contractintx=1") + self.extra_args[2].append("-rescan=1") + self.extra_args[2].append("-regtest=0") + self.extra_args[2].append("-pkhwhitelist=1") + self.extra_args[2].append("-pkhwhitelist-encrypt=0") + self.extra_args[2].append("-initialfreecoins=2100000000000000") + self.extra_args[2].append("-policycoins=50000000000000") + self.extra_args[2].append("-initialfreecoinsdestination=76a914bc835aff853179fa88f2900f9003bb674e17ed4288ac") + self.extra_args[2].append("-whitelistcoinsdestination=76a914427bf8530a3962ed77fd3c07d17fd466cb31c2fd88ac") + self.files=[] + + def setup_network(self, split=False): + self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args[:3]) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.is_network_split=False + self.sync_all() + + def linecount(self, file): + nlines=0 + with open(file) as f: + for nlines, l in enumerate(f): + pass + return nlines + + def initfile(self, filename): + self.files.append(filename) + self.removefileifexists(filename) + return filename + + def removefileifexists(self, filename): + if(os.path.isfile(filename)): + os.remove(filename) + + def cleanup_files(self): + for file in self.files: + self.removefileifexists(file) + + def run_test (self): + keypool=1 + + # import the policy keys into node 0 + self.nodes[0].importprivkey("cS29UJMQrpnee7UaUHo6NqJVpGr35TEqUDkKXStTnxSZCGUWavgE") + self.nodes[0].importprivkey("cNCQhCnpnzyeYh48NszsTJC2G4HPoFMZguUnUgBpJ5X9Vf2KaPYx") + + self.nodes[0].generate(101) + self.sync_all() + + #find txouts for the freezelistasset and burnlistasset + pascript = "76a914bc835aff853179fa88f2900f9003bb674e17ed4288ac" + wlscript = "76a914427bf8530a3962ed77fd3c07d17fd466cb31c2fd88ac" + genhash = self.nodes[0].getblockhash(0) + genblock = self.nodes[0].getblock(genhash) + + for txid in genblock["tx"]: + rawtx = self.nodes[0].getrawtransaction(txid,True) + if rawtx["vout"][0]["scriptPubKey"]["hex"] == pascript: + paasset = rawtx["vout"][0]["asset"] + patxid = txid + pavalue = rawtx["vout"][0]["value"] + if "assetlabel" in rawtx["vout"][0]: + if rawtx["vout"][0]["assetlabel"] == "WHITELIST": + wlasset = rawtx["vout"][0]["asset"] + wltxid = txid + wlvalue = rawtx["vout"][0]["value"] + + #Initial WHITELIST token balance + wb0_1=float(self.nodes[0].getbalance("", 1, False, "WHITELIST")) + coin=float(1e8) + assert_equal(wb0_1*coin,float(50000000000000)) + + #Whitelist node 0 addresses + self.nodes[0].dumpderivedkeys("keys.main") + self.nodes[0].readwhitelist("keys.main") + os.remove("keys.main") + + #Try and create kycfile when no KYC pub keys available + kycfile_test="kycfile_test.dat" + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test,[], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert('No unassigned KYC public keys available.' in message) + + #Register a KYC public key + policyaddr=self.nodes[0].getnewaddress() + assert(self.nodes[0].querywhitelist(policyaddr)) + policypubkey=self.nodes[0].validateaddress(policyaddr)["pubkey"] + kycaddr=self.nodes[0].getnewaddress() + kycpubkey=self.nodes[0].validateaddress(kycaddr)["pubkey"] + + inputs=[] + vin = {} + vin["txid"]= wltxid + vin["vout"]= 0 + inputs.append(vin) + outputs = [] + outp = {} + outp["pubkey"]=policypubkey + outp["value"]=wlvalue + outp["userkey"]=kycpubkey + outputs.append(outp) + wltx=self.nodes[0].createrawpolicytx(inputs, outputs, 0, wlasset) + wltx_signed=self.nodes[0].signrawtransaction(wltx) + assert(wltx_signed["complete"]) + wltx_send = self.nodes[0].sendrawtransaction(wltx_signed["hex"]) + + self.nodes[0].generate(101) + self.sync_all() + + #Onboard node1 + kycfile="kycfile.dat" + kycfile_normal="kycfile_normal.dat" + kycfile_p2sh="kycfile_p2sh.dat" + kycfile_p2pkh="kycfile_p2pkh.dat" + kycfile_multisig="kycfile_multisig.dat" + kycfile_empty="kycfile_empty.dat" + #userOnboardPubKey=self.nodes[1].dumpkycfile(kycfile) + + #P2SH + onboardAddress1=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress2=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress3=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress4=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress5=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + multisigAddress1=self.nodes[1].createmultisig(2,[onboardAddress1['address'],onboardAddress2['address'],onboardAddress3['address']])['address']; + multisigAddress2=self.nodes[1].createmultisig(2,[onboardAddress3['address'],onboardAddress4['address'],onboardAddress5['address']])['address']; + + witnessAddress1=self.nodes[1].addwitnessaddress(self.nodes[1].getnewaddress()) + + #P2SH + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_p2sh, [{"address":multisigAddress1},{"address":multisigAddress2},{"address":witnessAddress1}],[]); + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + valkyc=self.nodes[0].validatekycfile(kycfile_p2sh) + print(valkyc) + assert(valkyc["iswhitelisted"] == False) + assert(len(valkyc["addresses"]) == 3) + + assert(self.nodes[0].querywhitelist(multisigAddress1) == False) + assert(self.nodes[0].querywhitelist(multisigAddress2) == False) + assert(self.nodes[0].querywhitelist(witnessAddress1) == False) + + try: + self.nodes[0].onboarduser(kycfile_p2sh) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + self.nodes[0].generate(101) + self.sync_all() + + valkyc=self.nodes[0].validatekycfile(kycfile_p2sh) + print(valkyc) + assert(valkyc["iswhitelisted"] == True) + + assert(self.nodes[0].querywhitelist(multisigAddress1) == True) + assert(self.nodes[0].querywhitelist(multisigAddress2) == True) + assert(self.nodes[0].querywhitelist(witnessAddress1) == True) + + os.remove(kycfile_p2sh) + + #P2PkH + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_p2pkh, [{"address":onboardAddress4['address']},{"address":onboardAddress5['address']}],[]); + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + valkyc=self.nodes[0].validatekycfile(kycfile_p2pkh) + print(valkyc) + assert(valkyc["iswhitelisted"] == False) + assert(len(valkyc["addresses"]) == 2) + + assert(self.nodes[0].querywhitelist(onboardAddress4['address']) == False) + assert(self.nodes[0].querywhitelist(onboardAddress5['address']) == False) + + try: + self.nodes[0].onboarduser(kycfile_p2pkh) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + self.nodes[0].generate(101) + self.sync_all() + + valkyc=self.nodes[0].validatekycfile(kycfile_p2pkh) + print(valkyc) + assert(valkyc["iswhitelisted"] == True) + + assert(self.nodes[0].querywhitelist(onboardAddress4['address']) == True) + assert(self.nodes[0].querywhitelist(onboardAddress5['address']) == True) + + os.remove(kycfile_p2pkh) + + + #Test invalid parameters + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test,None, None); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid parameters, arguments 2 and 3 can't both be null" in message) + + #Create empty file and check validity + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_empty,[], []); + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + try: + self.nodes[0].validatekycfile(kycfile_empty) + except JSONRPCException as e: + print(e.error['message']) + assert('no address data in file' in e.error['message']) + + os.remove(kycfile_empty) + + onboardAddress1=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress2=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_normal, [{"address":onboardAddress1['address'],"pubkey":onboardAddress1['derivedpubkey']},{"address":onboardAddress2['address'], + "pubkey":onboardAddress2['derivedpubkey']}], + []); + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + valkyc=self.nodes[0].validatekycfile(kycfile_normal) + print(valkyc) + assert(valkyc["iswhitelisted"] == False) + assert(len(valkyc["addresses"]) == 2) + + self.nodes[0].generate(101) + self.sync_all() + + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [{"address":"myInvalidAddress","pubkey":onboardAddress1['derivedpubkey']},{"address":onboardAddress2['address'],"pubkey":onboardAddress2['derivedpubkey']}], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid address in pubkeylist: myInvalidAddress" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [{"address":onboardAddress1['address'],"pubkey":"myInvalidPubKey"},{"address":onboardAddress2['address'], + "pubkey":onboardAddress2['derivedpubkey']}], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid pubkey in pubkeylist: myInvalidPubKey" in message) + + balance_1=self.nodes[0].getwalletinfo()["balance"]["WHITELIST"] + try: + self.nodes[0].onboarduser(kycfile_normal) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + self.nodes[0].generate(101) + self.sync_all() + + valkyc=self.nodes[0].validatekycfile(kycfile_normal) + print(valkyc) + assert(valkyc["iswhitelisted"] == True) + + os.remove(kycfile_normal) + + #P2SH + onboardAddress3=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + multisigAddress1=self.nodes[1].createmultisig(2,[onboardAddress1['address'],onboardAddress2['address'],onboardAddress3['address']])['address']; + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_p2sh, [{"address":multisigAddress1}], + []); + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + valkyc=self.nodes[0].validatekycfile(kycfile_p2sh) + print(valkyc) + assert(valkyc["iswhitelisted"] == False) + assert(len(valkyc["addresses"]) == 1) + + self.nodes[0].generate(101) + self.sync_all() + + #Test invalid parameters + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [{"address":"myInvalidAddress"}], []); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid address in pubkeylist: myInvalidAddress" in message) + + balance_1=self.nodes[0].getwalletinfo()["balance"]["WHITELIST"] + try: + self.nodes[0].onboarduser(kycfile_p2sh) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + self.nodes[0].generate(101) + self.sync_all() + + valkyc=self.nodes[0].validatekycfile(kycfile_p2sh) + print(valkyc) + assert(valkyc["iswhitelisted"] == True) + + os.remove(kycfile_p2sh) + + + onboardAddress1=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress2=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress3=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress4=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + untweakedPubkeys=[onboardAddress1['derivedpubkey'],onboardAddress2['derivedpubkey'],onboardAddress3['derivedpubkey']] + untweakedPubkeys2=[onboardAddress2['derivedpubkey'],onboardAddress3['derivedpubkey'],onboardAddress4['derivedpubkey']] + untweakedPubkeys3=[onboardAddress3['derivedpubkey'],onboardAddress4['derivedpubkey']] + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_multisig, [], [{"nmultisig":2,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + valkyc=self.nodes[0].validatekycfile(kycfile_multisig) + print(valkyc) + assert(valkyc["iswhitelisted"] == False) + assert(len(valkyc["addresses"]) == 3) + + try: + self.nodes[0].onboarduser(kycfile_multisig) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + self.nodes[0].generate(101) + self.sync_all() + + + #Test invalid parameters + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("nmultisig missing in multisiglist" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":2},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("pubkeys missing in multisiglist" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":2.1,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("JSON integer out of range" in message) + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":16,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("nmultisig must be an integer between 1 and 15") + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":0,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("nmultisig must be an integer between 1 and 15") + + + invalidPubkeys=['myInvalidPubKey',onboardAddress2['derivedpubkey'],onboardAddress3['derivedpubkey']] + + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile_test, [], [{"nmultisig":2,"pubkeys":invalidPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + #expect an exception + assert(False) + except JSONRPCException as e: + message=e.error['message'] + assert("Invalid pubkey in multisiglist: " in message) + + + valkyc=self.nodes[0].validatekycfile(kycfile_multisig) + print(valkyc) + assert(valkyc["iswhitelisted"] == True) + + os.remove(kycfile_multisig) + + onboardAddress1=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress2=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress3=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + onboardAddress4=self.nodes[1].validateaddress(self.nodes[1].getnewaddress()) + untweakedPubkeys=[onboardAddress1['derivedpubkey'],onboardAddress2['derivedpubkey'],onboardAddress3['derivedpubkey']] + untweakedPubkeys2=[onboardAddress2['derivedpubkey'],onboardAddress3['derivedpubkey'],onboardAddress4['derivedpubkey']] + untweakedPubkeys3=[onboardAddress3['derivedpubkey'],onboardAddress4['derivedpubkey']] + try: + userOnboardPubKey=self.nodes[1].createkycfile(kycfile, [{"address":onboardAddress1['address'],"pubkey":onboardAddress1['derivedpubkey']},{"address":onboardAddress2['address'],"pubkey":onboardAddress2['derivedpubkey']}], [{"nmultisig":2,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + try: + result=userOnboardPubKey=self.nodes[1].createkycfile("", [{"address":onboardAddress1['address'],"pubkey":onboardAddress1['derivedpubkey']},{"address":onboardAddress2['address'],"pubkey":onboardAddress2['derivedpubkey']}], [{"nmultisig":2,"pubkeys":untweakedPubkeys},{"nmultisig":2,"pubkeys":untweakedPubkeys2},{"nmultisig":2,"pubkeys":untweakedPubkeys3}]); + kycstring=result["kycfile"] + okey=result["onboardpubkey"] + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + valkyc=self.nodes[0].validatekycfile(kycfile) + print(valkyc) + assert(len(valkyc["addresses"]) == 5) + assert(valkyc["iswhitelisted"] == False) + + kycfile_plain="kycfile_plain.dat" + self.nodes[0].readkycfile(kycfile,kycfile_plain) + + kycfile_fromstr="kycfile_fromstr.dat" + kycfile_fromstr_plain="kycfile_fromstr_plain.dat" + + with open(kycfile_fromstr, "w") as f: + f.write(kycstring) + + self.nodes[0].readkycfile(kycfile_fromstr,kycfile_fromstr_plain) + + with open(kycfile_plain) as f1: + with open(kycfile_fromstr_plain) as f2: + different = set(f1).difference(f2) + + discard=set() + for line in different: + if line[0] == '#': + discard.add(line) + + different=different.difference(discard) + + discard=set() + for line in different: + sline=line.split(' ') + if len(sline) == 3 and sline[0] == okey: + discard.add(line) + + different=different.difference(discard) + + assert(len(different) == 0) + + + try: + self.nodes[0].onboarduser(kycfile) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + + self.nodes[0].generate(101) + self.sync_all() + + valkyc=self.nodes[0].validatekycfile(kycfile) + print(valkyc) + assert(valkyc["iswhitelisted"] == True) + + os.remove(kycfile) + + + balance_2=self.nodes[0].getwalletinfo()["balance"]["WHITELIST"] + #Make sure the onboard transaction fee was zero + assert((balance_1-balance_2) == 0) + + node1addr=self.nodes[1].getnewaddress() + try: + iswl=self.nodes[0].querywhitelist(onboardAddress1['address']) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + assert(iswl) + + try: + iswl=self.nodes[0].querywhitelist(onboardAddress2['address']) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + assert(iswl) + + multiAdr=self.nodes[1].createmultisig(2,[onboardAddress1['pubkey'],onboardAddress2['pubkey'],onboardAddress3['pubkey']]) + try: + iswl2=self.nodes[0].querywhitelist(multiAdr['address']) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + assert(iswl2) + + multiAdr=self.nodes[1].createmultisig(2,[onboardAddress2['pubkey'],onboardAddress3['pubkey'],onboardAddress4['pubkey']]) + try: + iswl2=self.nodes[0].querywhitelist(multiAdr['address']) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + assert(iswl2) + + multiAdr=self.nodes[1].createmultisig(2,[onboardAddress3['pubkey'],onboardAddress4['pubkey']]) + try: + iswl2=self.nodes[0].querywhitelist(multiAdr['address']) + except JSONRPCException as e: + print(e.error['message']) + assert(False) + assert(iswl2) + + #Test that txs do not send WHITELIST tokens + wb0_2=float(self.nodes[0].getbalance("", 1, False, "WHITELIST")) + assert_equal(wb0_1-float(wlvalue), wb0_2) + + self.cleanup_files() + return + +if __name__ == '__main__': + OnboardManualTest().main() diff --git a/src/policy/kycfile.cpp b/src/policy/kycfile.cpp index 497f1253fb..31139947e0 100644 --- a/src/policy/kycfile.cpp +++ b/src/policy/kycfile.cpp @@ -14,8 +14,8 @@ CKYCFile::~CKYCFile(){ } void CKYCFile::clear(){ - _addressKeys.clear(); - _addressKeyIds.clear(); + _pubKeyPairs.clear(); + _destinations.clear(); _decryptedStream.clear(); _errorStream.clear(); _multisigData.clear(); @@ -89,9 +89,6 @@ bool CKYCFile::read(){ throw std::invalid_argument( std::string(std::string(__func__) + ": invalid kyc pub key in KYC file")); - if(!pwalletMain->GetKey(_onboardPubKey->GetID(), onboardPrivKey)) - throw std::invalid_argument( - std::string(std::string(__func__) + ": cannot get onboard private key")); std::vector userPubKeyData(ParseHex(vstr[1])); _onboardUserPubKey = new CPubKey(userPubKeyData.begin(), userPubKeyData.end()); @@ -99,6 +96,11 @@ bool CKYCFile::read(){ throw std::invalid_argument( std::string(std::string(__func__) + ": invalid onboard user pub key in kyc file")); + if(!pwalletMain->GetKey(_onboardPubKey->GetID(), onboardPrivKey)) + if(!pwalletMain->GetKey(_onboardUserPubKey->GetID(), onboardPrivKey)) + throw std::invalid_argument( + std::string(std::string(__func__) + ": cannot get onboard private key")); + std::stringstream ssNBytes; ssNBytes << vstr[2]; ssNBytes >> nBytesTotal; @@ -120,10 +122,12 @@ bool CKYCFile::read(){ std::string str=ss.str(); std::vector vch(str.begin(), str.end()); std::vector vdata; - if(!encryptor.Decrypt(vdata, vch, onboardPrivKey)) - throw std::invalid_argument( - std::string(std::string(__func__) + ": KYC file decryption failed")); - + if(!encryptor.Decrypt(vdata, vch, onboardPrivKey)){ + if((_onboardPubKey == nullptr) |! encryptor.Decrypt(vdata, vch, onboardPrivKey, *_onboardPubKey)) + throw std::invalid_argument( + std::string(std::string(__func__) + ": KYC file decryption failed")); + } + data = std::string(vdata.begin(), vdata.end()); std::stringstream ss_data; ss_data << data; @@ -135,7 +139,11 @@ bool CKYCFile::read(){ continue; } boost::split(vstr, line, boost::is_any_of(" ")); - if (vstr.size() < 2){ + if (vstr.size() < 1){ + continue; + } + else if (vstr.size() == 1){ + if(!parseAddress(vstr,line)) _fAddressesValid = false; continue; } else if (vstr.size() == 2){ @@ -189,6 +197,21 @@ void CKYCFile::appendOutStream(std::string line, std::string error){ appendOutStream(ss.str()); } +bool CKYCFile::parseAddress(const std::vector vstr, const std::string line){ + CBitcoinAddress address; + if (!address.SetString(vstr[0])) { + std::stringstream ss(": invalid base58check address: "); + ss << vstr[0]; + appendOutStream(line, ss.str()); + return false; + } + + //Addresses valid, write to map + _destinations.push_back(address.Get()); + appendOutStream(line); + return true; +} + bool CKYCFile::parsePubkeyPair(const std::vector vstr, const std::string line){ CBitcoinAddress address; if (!address.SetString(vstr[0])) { @@ -207,9 +230,11 @@ bool CKYCFile::parsePubkeyPair(const std::vector vstr, const std::s //Check the key tweaking CKeyID addressKeyId; + pubKeyPair p; if(address.GetKeyID(addressKeyId)){ + p = pubKeyPair(addressKeyId, pubKey); if(!Params().ContractInTx()){ - if(!Consensus::CheckValidTweakedAddress(addressKeyId, pubKey)){ + if(!Consensus::CheckValidTweakedAddress(p)){ appendOutStream(line, ": invalid key tweaking"); return false; } @@ -222,8 +247,7 @@ bool CKYCFile::parsePubkeyPair(const std::vector vstr, const std::s //Addresses valid, write to map - _addressKeys.push_back(pubKey); - _addressKeyIds.push_back(addressKeyId); + _pubKeyPairs.push_back(p); appendOutStream(line); return true; } @@ -286,7 +310,6 @@ bool CKYCFile::parseMultisig(const std::vector vstr, const std::str //Multi Address is valid, write to map _multisigData.push_back(OnboardMultisig(nMultisig, multiKeyId, pubKeys)); - _addressKeyIds.push_back(multiKeyId); appendOutStream(line); return true; } @@ -295,13 +318,13 @@ bool CKYCFile::is_valid(){ CScript script; if(is_empty()) throw std::invalid_argument(std::string(std::string(__func__) + - "no address data in file")); + ": no address data in file")); return getOnboardingScript(script); } bool CKYCFile::is_empty(){ - if(_addressKeys.size() > 0) return false; - if(_addressKeyIds.size() > 0) return false; + if(_pubKeyPairs.size() > 0) return false; + if(_destinations.size() > 0) return false; if(_multisigData.size() > 0) return false; return true; } @@ -320,11 +343,11 @@ bool CKYCFile::getOnboardingScript(CScript& script, bool fBlacklist){ if(!_fAddressesValid) { std::stringstream ss(std::string(std::string(__func__))); - ss << "invalid addresses in kycfile: " << std::endl; - ss << _errorStream.str(); + ss << "invalid addresses in kycfile: " << _errorStream.str(); throw std::invalid_argument(ss.str()); } + COnboardingScript obScript; obScript.SetDeregister(fBlacklist); @@ -334,12 +357,15 @@ bool CKYCFile::getOnboardingScript(CScript& script, bool fBlacklist){ if (!pwalletMain->IsLocked()) pwalletMain->TopUpKeyPool(); - if(_addressKeys.size() != 0) - if(!obScript.Append(_addressKeys)) return false; - if(_multisigData.size() != 0) if(!obScript.Append(_multisigData)) return false; + if(_pubKeyPairs.size() != 0) + if(!obScript.Append(_pubKeyPairs)) return false; + + if(_destinations.size() != 0) + if(!obScript.Append(_destinations)) return false; + // Get an unassigned KYC key from the addressWhitelist if(fWhitelistEncrypt){ @@ -373,12 +399,18 @@ std::ostream& operator<<(std::ostream& os, const CKYCFile& fl){ bool CKYCFile::is_whitelisted(){ bool fOk = true; - for(auto k : _addressKeyIds){ + for(auto k : _destinations){ if(!addressWhitelist->is_whitelisted(k)){ fOk = false; break; } } + for(auto k : _pubKeyPairs){ + if(!addressWhitelist->is_whitelisted(k.first)){ + fOk = false; + break; + } + } for(auto k : _multisigData){ if(!addressWhitelist->is_whitelisted(k.scriptID)){ fOk = false; @@ -388,4 +420,19 @@ bool CKYCFile::is_whitelisted(){ return fOk; } +std::vector CKYCFile::getAddresses() const{ + std::vector result = _destinations; + for(auto k : _pubKeyPairs){ + result.push_back(k.first); + } + for(auto k : _multisigData){ + result.push_back(k.scriptID); + } + return result; +} + + + + + diff --git a/src/policy/kycfile.h b/src/policy/kycfile.h index 2f7b748471..225d7fd5f2 100644 --- a/src/policy/kycfile.h +++ b/src/policy/kycfile.h @@ -14,9 +14,11 @@ #include #include #include +#include #include "script/onboardingscript.h" using uc_vec=std::vector; +using pubKeyPair=std::pair; class CKYCFile{ public: @@ -35,12 +37,13 @@ class CKYCFile{ bool initEncryptor(); - std::vector getAddressKeys() const {return _addressKeys;} - std::vector getAddressKeyIds() const {return _addressKeyIds;} + + std::vector getAddresses() const; const CPubKey* getOnboardPubKey() const {return _onboardPubKey;} const CPubKey* getOnboardUserPubKey() const {return _onboardUserPubKey;} + bool parseAddress(const std::vector vstr, const std::string line); bool parsePubkeyPair(const std::vector vstr, const std::string line); bool parseContractHash(const std::vector vstr, const std::string line); bool parseMultisig(const std::vector vstr, const std::string line); @@ -64,8 +67,9 @@ class CKYCFile{ CWhiteList* _whitelist=nullptr; // The user address keys to be whitelisted - std::vector _addressKeys; - std::vector _addressKeyIds; + std::vector _pubKeyPairs; + + std::vector _destinations; std::vector _multisigData; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 8f7fc9a3c8..8e1ff07060 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -62,7 +62,8 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) } else if (whichType == TX_NULL_DATA && (!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes)) return false; - else if ( (whichType == TX_REGISTERADDRESS || whichType == TX_DEREGISTERADDRESS) && + else if ( (whichType == TX_REGISTERADDRESS_V0 || whichType == TX_DEREGISTERADDRESS_V0 || + whichType == TX_REGISTERADDRESS_V1 || whichType == TX_DEREGISTERADDRESS_V1) && (!fAcceptRegisteraddress || scriptPubKey.size() > nMaxRegisteraddressBytes)) return false; else if (whichType == TX_TRUE) @@ -141,7 +142,8 @@ bool IsAnyBurn(const CTransaction &tx) { vector> vSolutions; for (CTxOut const &txout : tx.vout) { if(Solver(txout.scriptPubKey, whichType, vSolutions)) { - if ((whichType == TX_NULL_DATA || whichType == TX_REGISTERADDRESS || whichType == TX_DEREGISTERADDRESS) && txout.nValue.GetAmount() != 0) return true; + if ((whichType == TX_NULL_DATA || whichType == TX_REGISTERADDRESS_V0 || whichType == TX_DEREGISTERADDRESS_V0 + || whichType == TX_REGISTERADDRESS_V1 || whichType == TX_DEREGISTERADDRESS_V1) && txout.nValue.GetAmount() != 0) return true; } else { return true; } @@ -255,7 +257,8 @@ bool IsWhitelisted(CTransaction const &tx) { // skip whitelist check if output is OP_RETURN // skip whitelist check if output is OP_REGISTERADDRESS or OP_DEREGISTERADDRESS if (!tx.vin[0].assetIssuance.IsNull() || whichType == TX_FEE || - whichType == TX_NULL_DATA || whichType == TX_REGISTERADDRESS || whichType == TX_DEREGISTERADDRESS ) + whichType == TX_NULL_DATA || whichType == TX_REGISTERADDRESS_V0 || whichType == TX_DEREGISTERADDRESS_V0 + || whichType == TX_REGISTERADDRESS_V1 || whichType == TX_DEREGISTERADDRESS_V1 ) continue; // return false if not P2PKH or P2SH if (whichType == TX_PUBKEYHASH) { @@ -288,7 +291,8 @@ bool IsRedemption(CTransaction const &tx) { vector> vSolutions; for (uint32_t itr = 0; itr < tx.vout.size(); ++itr) { if (Solver(tx.vout[itr].scriptPubKey, whichType, vSolutions)) { - if (whichType == TX_FEE || whichType == TX_REGISTERADDRESS || whichType == TX_DEREGISTERADDRESS) + if (whichType == TX_FEE || whichType == TX_REGISTERADDRESS_V0 || whichType == TX_DEREGISTERADDRESS_V0 + || whichType == TX_REGISTERADDRESS_V1 || whichType == TX_DEREGISTERADDRESS_V1) continue; //set freeze-flag key uint160 frzInt; @@ -341,7 +345,8 @@ bool IsFreezelisted(CTransaction const &tx, CCoinsViewCache const &mapInputs) { CKeyID keyId = CKeyID(uint160(vSolutions[0])); // search in freezelist for the presence of keyid if (!addressFreezelist.find(keyId)) return false; - } else if (whichType == TX_FEE || whichType == TX_REGISTERADDRESS || whichType == TX_DEREGISTERADDRESS) { + } else if (whichType == TX_FEE || whichType == TX_REGISTERADDRESS_V0 || whichType == TX_DEREGISTERADDRESS_V0 + || whichType == TX_REGISTERADDRESS_V1 || whichType == TX_DEREGISTERADDRESS_V1 ) { continue; } else { return false; diff --git a/src/policy/policylist.h b/src/policy/policylist.h index a18623c6f9..d6662928f4 100644 --- a/src/policy/policylist.h +++ b/src/policy/policylist.h @@ -18,10 +18,10 @@ class CPolicyList : private std::set { + public: using base = std::set; using baseIter = base::iterator; - public: CPolicyList(); virtual ~CPolicyList(); void lock(){_mtx.lock();} diff --git a/src/policy/whitelist.cpp b/src/policy/whitelist.cpp index dfe429acac..554239b61b 100644 --- a/src/policy/whitelist.cpp +++ b/src/policy/whitelist.cpp @@ -4,15 +4,19 @@ #include "chain.h" #include "whitelist.h" -#include "validation.h" #ifdef ENABLE_WALLET #endif #include "ecies_hex.h" -#include "policy/policy.h" #include "rpc/server.h" #include "random.h" #include #include +#include +#include "validation.h" + +const unsigned int CWhiteList::nMultisigSize=1; +const unsigned int CWhiteList::addrSize=20; +const unsigned int CWhiteList::_minDataSize=CWhiteList::addrSize; CWhiteList::CWhiteList(){ _asset=whitelistAsset; @@ -80,10 +84,12 @@ bool CWhiteList::Load(CCoinsView *view) COutPoint outPoint(key, i); add_unassigned_kyc(kycPubKey, outPoint); } - } else if ((whichType == TX_REGISTERADDRESS || - whichType == TX_DEREGISTERADDRESS) + } else if ((whichType == TX_REGISTERADDRESS_V1 || + whichType == TX_REGISTERADDRESS_V0 || + whichType == TX_DEREGISTERADDRESS_V1 || + whichType == TX_DEREGISTERADDRESS_V0) &! fReindex &! fReindexChainState ) { - ParseRegisterAddressOutput(out, whichType == TX_DEREGISTERADDRESS); + ParseRegisterAddressOutput(whichType, vSolutions); } } } @@ -147,24 +153,11 @@ void CWhiteList::add_derived(const CBitcoinAddress& address, const CPubKey& pubK void CWhiteList::add_derived(const CBitcoinAddress& address, const CPubKey& pubKey){ boost::recursive_mutex::scoped_lock scoped_lock(_mtx); - if (!pubKey.IsFullyValid()) - throw std::invalid_argument(std::string(std::string(__func__) + - ": invalid public key")); - - //Will throw an error if address is not a valid derived address. - CTxDestination keyId; - keyId = address.Get(); - - if (keyId.which() == ((CTxDestination)CNoDestination()).which()) - throw std::invalid_argument(std::string(std::string(__func__) + - ": invalid key id")); - if(!Params().ContractInTx() && !Consensus::CheckValidTweakedAddress(keyId, pubKey)) - throw std::invalid_argument(std::string(std::string(__func__) + - ": address does not derive from public key when tweaked with contract hash")); + CDerivedData dat; + dat.Set(pubKeyPair(address.Get(), pubKey)); - //insert new address into sorted CWhiteList vector - add_sorted(keyId); + add_sorted(dat.GetDest()); } void CWhiteList::add_derived(const std::string& sAddress, const std::string& sPubKey, @@ -192,28 +185,17 @@ void CWhiteList::add_multisig_whitelist(const CBitcoinAddress& address, const st } void CWhiteList::add_multisig_whitelist(const CBitcoinAddress& address, const std::vector& pubKeys, - const uint8_t nMultisig){ + const uint8_t m){ boost::recursive_mutex::scoped_lock scoped_lock(_mtx); - for(unsigned int i = 0; i < pubKeys.size(); ++i) { - if (!pubKeys[i].IsFullyValid()) - throw std::invalid_argument(std::string(std::string(__func__) + - ": invalid public key")); - } - - //Will throw an error if address is not a valid derived address. - CTxDestination keyId; - keyId = address.Get(); - if (keyId.which() == ((CTxDestination)CNoDestination()).which()) - throw std::invalid_argument(std::string(std::string(__func__) + - ": invalid key id")); - - if(!Params().ContractInTx() && !Consensus::CheckValidTweakedAddress(keyId, pubKeys, nMultisig)) - throw std::invalid_argument(std::string(std::string(__func__) + - ": address does not derive from public keys when tweaked with contract hash")); + CMultisigData dat; + + CTxDestination dest = address.Get(); + + dat.Set(dest, pubKeys, m); //insert new address into sorted CWhiteList vector - add_sorted(keyId); + add_sorted(dat.GetDest()); } void CWhiteList::add_multisig_whitelist(const std::string& addressIn, const UniValue& keys, @@ -222,7 +204,7 @@ void CWhiteList::add_multisig_whitelist(const std::string& addressIn, const UniV } void CWhiteList::add_multisig_whitelist(const std::string& sAddress, const UniValue& sPubKeys, - const uint8_t nMultisig){ + const uint8_t mMultisig){ boost::recursive_mutex::scoped_lock scoped_lock(_mtx); CBitcoinAddress address; @@ -238,7 +220,7 @@ void CWhiteList::add_multisig_whitelist(const std::string& sAddress, const UniVa pubKeyVec.push_back(pubKey); } - add_multisig_whitelist(address, pubKeyVec, nMultisig); + add_multisig_whitelist(address, pubKeyVec, mMultisig); } bool CWhiteList::RegisterAddress(const CTransaction& tx, const CBlockIndex* pindex){ @@ -280,12 +262,16 @@ bool CWhiteList::RegisterAddress(const std::vector& vout){ //Check if this is a TX_REGISTERADDRESS or TX_DEREGISTERADDRESS. If so, read the data into a byte vector. txnouttype whichType; - // For each TXOUT, if a TX_REGISTERADDRESS, read the data + // For each TXOUT, if a TX_REGISTERADDRESS, read the istdata BOOST_FOREACH (const CTxOut& txout, vout) { + if (!IsWhitelistAsset(txout)) continue; std::vector > vSolutions; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; - if(whichType == TX_REGISTERADDRESS || whichType == TX_DEREGISTERADDRESS) { - return ParseRegisterAddressOutput(txout, whichType == TX_DEREGISTERADDRESS); + if(whichType == TX_REGISTERADDRESS_V1 || + whichType == TX_REGISTERADDRESS_V0 || + whichType == TX_DEREGISTERADDRESS_V1 || + whichType == TX_DEREGISTERADDRESS_V0) { + return ParseRegisterAddressOutput(whichType, vSolutions); } } return false; @@ -295,27 +281,18 @@ bool CWhiteList::RegisterAddress(const std::vector& vout){ #endif //#ifdef ENABLE_WALLET } -bool CWhiteList::ParseRegisterAddressOutput(const CTxOut& txout, bool fBlacklist){ +bool CWhiteList::ParseRegisterAddressOutput(const txnouttype& whichType, const std::vector& solutions){ #ifdef ENABLE_WALLET boost::recursive_mutex::scoped_lock scoped_lock(_mtx); - if (!IsWhitelistAsset(txout)) return false; - - opcodetype opcode; - std::vector bytes; - CScript::const_iterator pc = txout.scriptPubKey.begin(); - if (!txout.scriptPubKey.GetOp(++pc, opcode, bytes)) return false; + const uc_vec& bytes = solutions[0]; //Confirm data read from the TX_REGISTERADDRESS - unsigned int minDataSize=CPubKey::COMPRESSED_PUBLIC_KEY_SIZE+addrSize; - if(bytes.size() inputPubKeys; + if(bytes.size()<_minDataSize) return false; //Read the message data std::vector data(bytes.begin(), bytes.end()); - return RegisterDecryptedAddresses(data, fBlacklist); + return RegisterDecryptedAddresses(whichType, data); #else //#ifdef ENABLE_WALLET LogPrintf("POLICY: wallet not enabled - unable to process registeraddress transaction.\n"); @@ -325,202 +302,57 @@ bool CWhiteList::ParseRegisterAddressOutput(const CTxOut& txout, bool fBlacklist -bool CWhiteList::RegisterDecryptedAddresses(const std::vector& data, - const bool bBlacklist){ - //Interpret the data - //First 20 bytes: keyID - std::vector::const_iterator itData2 = data.begin(); - std::vector::const_iterator itData1 = itData2; - - std::vector::const_iterator pend = data.end(); - - bool bEnd=false; - bool bSuccess=false; - - while(!bEnd){ - bool isMultisig = IsRegisterAddressMulti(itData1, pend); - - //REGISTERADDRESS for pubkeys - if(isMultisig == false){ - bool fEnd = false; - int pairsAdded = 0; - while(!fEnd){ - std::vector::const_iterator itStart = itData1; - for(unsigned int i=0; i addrChars(itData1,itData2); - CTxDestination addr = CKeyID(uint160(addrChars)); - itData1 = itData2; - for(unsigned int i=0; i mMultisigChars(itData1,itData2); - - mMultisig = mMultisigChars[0]; - - itData1 = itData2; - - itData2 += nMultisigSize; - - uint8_t nMultisig = 0; - std::vector nMultisigChars(itData1,itData2); - - nMultisig = nMultisigChars[0]; - - itData1 = itData2; - - itData2 += addrSize; - - CBitcoinAddress addrMultiNew; - std::vector addrTestChars(itData1,itData2); - addrMultiNew.Set(CScriptID(uint160(addrTestChars))); - - std::vector vPubKeys; - - itData1=itData2; - - unsigned int pubkeyNr = static_cast(nMultisig); +bool CWhiteList::RegisterDecryptedAddresses(const txnouttype& whichType, const std::vector& data){ + boost::recursive_mutex::scoped_lock scoped_lock(_mtx); - for (unsigned int j=0; j < pubkeyNr; ++j){ + CRegisterAddressData* dat; + std::vector vDat; - if(bEnd == true) - break; + CRegisterAddressDataFactory* fact; - for(unsigned int i=0; i::const_iterator start, const std::vector::const_iterator vend){ - - std::vector::const_iterator point1 = start; - std::vector::const_iterator point2 = start; - - for(unsigned int i=0; iGetNext()){ + vDat.push_back(dat); } - uint8_t mMultisig = *start; - - if(mMultisig > MAX_P2SH_SIGOPS || mMultisig == 0) + //Everything was valid if the factory reached the end of the byte vector + if (!fact->IsEnd()) { + delete fact; return false; - - point1 = point2; - - for(unsigned int i=0; i MAX_P2SH_SIGOPS || nMultisig == 0) - return false; - - point1 = point2; - - for(unsigned int i=0; i addrTestChars(point1,point2); - addrTestNew.Set(CScriptID(uint160(addrTestChars))); - - if(!addrTestNew.IsValid()) - return false; - - point1=point2; - - for(unsigned int i=0; iGetDest(); + if(dest != _noDest) CPolicyList::add_sorted(dest); +} + +void CWhiteList::remove(CRegisterAddressData* d){ + CPolicyList::remove(d->GetDest()); +} + //Update from transaction bool CWhiteList::Update(const CTransaction& tx, const CCoinsViewCache& mapInputs) { @@ -701,3 +533,267 @@ bool CWhiteList::get_kycpubkey_outpoint(const CPubKey& pubKey, COutPoint& outPoi +//=============================================== +//CRegisterAddressDataFactory +//----------------------------------------------- + +CRegisterAddressData* CRegisterAddressDataFactory::GetNext(){ + CRegisterAddressData* data; + if( data = GetNextMultisig() ) return data; + return GetNextDerived(); +} + + +CMultisigData* CRegisterAddressDataFactory::GetNextMultisig(){ + + MarkReset(); + + ucvec_it cursor = _cursor; + if(!AdvanceCursor(cursor, CWhiteList::nMultisigSize)){ + return nullptr; + } + + std::vector mMultisigChars(_cursor,cursor); + + uint8_t mMultisig = mMultisigChars[0]; + + CMultisigData* data = new CMultisigData(); + + try{ + data->SetM(mMultisig); + } catch (std::invalid_argument e) { + ResetCursor(); + delete data; + return nullptr; + } + + _cursor = cursor; + + if(!AdvanceCursor(cursor, CWhiteList::nMultisigSize)){ + ResetCursor(); + delete data; + return nullptr; + } + + std::vector nMultisigChars(_cursor,cursor); + + uint8_t nMultisig = nMultisigChars[0]; + + _cursor = cursor; + + if(!AdvanceCursor(cursor, CWhiteList::addrSize)){ + ResetCursor(); + delete data; + return nullptr; + } + + std::vector addrChars(_cursor,cursor); + + //Try and set the multisig dest + try{ + data->SetDest(CScriptID(uint160(addrChars))); + } catch (std::invalid_argument e) { + ResetCursor(); + delete data; + return nullptr; + } + + _cursor=cursor; + + unsigned int pubkeyNr = static_cast(nMultisig); + + for (unsigned int j=0; j < pubkeyNr; ++j){ + + if(!AdvanceCursor(cursor, _pubkeySize)){ + ResetCursor(); + delete data; + return nullptr; + } + + CPubKey pubKeyNew = CPubKey(_cursor,cursor); + try{ + data->AddPubKey(pubKeyNew); + } catch (std::invalid_argument e) { + ResetCursor(); + delete data; + return nullptr; + } + + _cursor=cursor; + } + + try{ + data->TryValid(pubkeyNr); + } catch (std::invalid_argument e) { + ResetCursor(); + delete data; + return nullptr; + } + + return data; +} + +CDerivedData* CRegisterAddressDataFactory::GetNextDerived(){ + + MarkReset(); + + CDerivedData* data = new CDerivedData(); + + ucvec_it cursor = _cursor; + + if(!AdvanceCursor(cursor, CWhiteList::addrSize)){ + ResetCursor(); + delete data; + return nullptr; + } + + std::vector addrChars(_cursor,cursor); + CTxDestination addr = CKeyID(uint160(addrChars)); + + _cursor = cursor; + + if(!AdvanceCursor(cursor, _pubkeySize)){ + ResetCursor(); + delete data; + return nullptr; + } + + CPubKey pubKeyNew = CPubKey(_cursor,cursor); + _cursor=cursor; + + pubKeyPair p(addr, pubKeyNew); + + try{ + data->Set(p); + } catch (std::invalid_argument e) { + ResetCursor(); + delete data; + return nullptr; + } + return data; +} + +CP2SHData* CRegisterAddressDataFactory::GetNextP2SH(){ + return nullptr; +} + +CRegisterAddressData* CRegisterAddressDataFactory_v1::GetNext(){ + CWhiteList::AddrType nAddrType; + if(!GetNextAddrType(nAddrType)) + return nullptr; + switch(nAddrType){ + case CWhiteList::AddrType::MULTI: + return GetNextMultisig(); + case CWhiteList::AddrType::DERIVED: + return GetNextDerived(); + case CWhiteList::AddrType::P2SH: + return GetNextP2SH(); + case CWhiteList::AddrType::P2PKH: + return GetNextP2PKH(); + default: + return nullptr;; + } +} + +bool CRegisterAddressDataFactory_v1::GetNextAddrType( CWhiteList::AddrType& type){ + unsigned int t = *_cursor; + if (t >= CWhiteList::AddrType::LAST) + return false; + type = CWhiteList::AddrType(t); + return AdvanceCursor(); +} + +CP2PKHData* CRegisterAddressDataFactory_v1::GetNextP2PKH(){ + MarkReset(); + + CP2PKHData* data = new CP2PKHData(); + + ucvec_it cursor = GetCursor(); + + if(!AdvanceCursor(cursor, CWhiteList::addrSize)){ + ResetCursor(); + delete data; + return nullptr; + } + + std::vector addrChars(_cursor,cursor); + CTxDestination addr = CKeyID(uint160(addrChars)); + + try{ + data->Set(addr); + } catch (std::invalid_argument e) { + ResetCursor(); + delete data; + return nullptr; + } + + _cursor = cursor; + + return data; +} + +CP2SHData* CRegisterAddressDataFactory_v1::GetNextP2SH(){ + MarkReset(); + + CP2SHData* data = new CP2SHData(); + + ucvec_it cursor = GetCursor(); + + if(!AdvanceCursor(cursor, CWhiteList::addrSize)){ + ResetCursor(); + delete data; + return nullptr; + } + + std::vector addrChars(_cursor,cursor); + CTxDestination addr = CScriptID(uint160(addrChars)); + + try{ + data->Set(addr); + } catch (std::invalid_argument e) { + ResetCursor(); + delete data; + return nullptr; + } + + _cursor = cursor; + + return data; +} + +void CDerivedData::Set(const pubKeyPair& pair){ + pubKeyPair p = pair; + if (!p.second.IsFullyValid()) + throw std::invalid_argument(std::string(std::string(__func__) + + ": invalid public key")); + + + CBitcoinAddress addr(p.first); + if(!addr.IsValid()) + throw std::invalid_argument(std::string(std::string(__func__) + + ": invalid base58check address\n")); + + if(!Params().ContractInTx() && + !::Consensus::CheckValidTweakedAddress(p)) + throw std::invalid_argument(std::string(std::string(__func__) + + ": address does not derive from public key when tweaked with contract hash")); + + _pubKeyPair = p; + } + +void CMultisigData::TryValid(const uint8_t& n){ + if(Params().ContractInTx()) return; + + //Check m-of-n validity + if (_m > n || n > MAX_P2SH_SIGOPS || n == 0){ + throw std::invalid_argument(std::string(std::string(__func__) + + ": invalid m-of-n\n")); + } + + //Check dest built from pubkeys, m, n + if(!Params().ContractInTx() && !::Consensus::CheckValidTweakedAddress(_dest, _pubKeys, _m)){ + throw std::invalid_argument(std::string(std::string(__func__) + + ": address does not derive from public keys when tweaked with contract hash\n")); + } +} + diff --git a/src/policy/whitelist.h b/src/policy/whitelist.h index 0de600f5bc..fcb2caf6a8 100644 --- a/src/policy/whitelist.h +++ b/src/policy/whitelist.h @@ -11,15 +11,44 @@ #endif #include "chain.h" #include +#include +#include "policy/policy.h" +#include "pubkey.h" + +using uc_vec=std::vector; +using ucvec_it = std::vector::const_iterator; +using pubKeyPair = std::pair; + +class CRegisterAddressData{ + public: + CRegisterAddressData(){;} + virtual ~CRegisterAddressData(){;} + + virtual CTxDestination GetDest() = 0; + +}; class CWhiteList : public CPolicyList{ public: CWhiteList(); virtual ~CWhiteList(); - static const int64_t MAX_UNASSIGNED_KYCPUBKEYS=1000; - static const int64_t MAX_KYCPUBKEY_GAP=MAX_UNASSIGNED_KYCPUBKEYS; + static constexpr int64_t MAX_UNASSIGNED_KYCPUBKEYS=1000; + static constexpr int64_t MAX_KYCPUBKEY_GAP=MAX_UNASSIGNED_KYCPUBKEYS; + //The written code behaviour expects nMultisigSize to be of length 1 at the moment. If it is changed in the future the code needs to be adjusted accordingly. + + static const unsigned int nMultisigSize; + static const unsigned int addrSize; + static const unsigned int _minDataSize; + + enum AddrType : unsigned int{ + DERIVED = 0, + MULTI, + P2SH, + P2PKH, + LAST + }; void init_defaults(); @@ -51,9 +80,7 @@ class CWhiteList : public CPolicyList{ virtual void add_multisig_whitelist(const CBitcoinAddress& address, const std::vector& pubKeys, const uint8_t nMultisig); - bool RegisterDecryptedAddresses(const std::vector& data, const bool bBlacklist=false); - - virtual bool IsRegisterAddressMulti(const std::vector::const_iterator start,const std::vector::const_iterator vend); + bool RegisterDecryptedAddresses(const txnouttype& whichType, const std::vector& data); virtual bool RegisterAddress(const CTransaction& tx, const CBlockIndex* pindex); @@ -61,8 +88,15 @@ class CWhiteList : public CPolicyList{ virtual bool RegisterAddress(const std::vector& vout); - bool ParseRegisterAddressOutput(const CTxOut& txout, bool fBlacklist); + bool ParseRegisterAddressOutput(const txnouttype& whichType, const std::vector& solutions); + void add(CRegisterAddressData* d); + + void remove(CRegisterAddressData* d); + + CPolicyList::baseIter remove(CTxDestination d){ + return CPolicyList::remove(d); + } //Update from transaction virtual bool Update(const CTransaction& tx, const CCoinsViewCache& mapInputs); @@ -114,6 +148,8 @@ class CWhiteList : public CPolicyList{ void sync_whitelist_wallet(); + const CTxDestination _noDest = CNoDestination(); + protected: std::set _myPending; @@ -128,15 +164,289 @@ class CWhiteList : public CPolicyList{ using CPolicyList::find; - const unsigned int addrSize=20; - //The written code behaviour expects nMultisigSize to be of length 1 at the moment. If it is changed in the future the code needs to be adjusted accordingly. - const unsigned int nMultisigSize=1; const unsigned int minPayloadSize=2; std::map _kycPubkeyOutPointMap; + enum class RAType { single, pair, multisig, none }; + RAType GetRAType(const std::vector::const_iterator start, + const std::vector::const_iterator vend); private: void add_unassigned_kyc(const CPubKey& pubKey, const COutPoint& outPoint); }; + +class CP2SHData : public CRegisterAddressData{ + public: + CP2SHData(){ + Clear(); + } + virtual ~CP2SHData(){;} + + virtual CTxDestination GetDest(){ + return _dest; + } + + void Set(const CTxDestination& dest){ + if (!Params().ContractInTx()) + throw std::invalid_argument( + std::string(std::string(__func__) + + "attempting to register address without key tweaking checks for contractintx=0") + ); + + CBitcoinAddress addr(dest); + if(!addr.IsValid()) + throw std::invalid_argument(std::string(std::string(__func__) + + std::string(": invalid base58check address\n"))); + + if(!addr.IsScript()) + throw std::invalid_argument(std::string(std::string(__func__) + + std::string(": non-p2sh address: ") + addr.ToString())); + + + _dest = dest; + } + + void Clear(){ + _dest = CNoDestination(); + } + + private: + CTxDestination _dest; +}; + +class CP2PKHData : public CRegisterAddressData{ + public: + CP2PKHData(){ + Clear(); + } + virtual ~CP2PKHData(){;} + + virtual CTxDestination GetDest(){ + return _dest; + } + + void Set(const CTxDestination& dest){ + if (!Params().ContractInTx()) + throw std::invalid_argument( + std::string(std::string(__func__) + + "attempting to register address without key tweaking checks for contractintx=0") + ); + + CBitcoinAddress addr(dest); + if(!addr.IsValid()) + throw std::invalid_argument(std::string(std::string(__func__) + + std::string(": invalid base58check address\n"))); + + if(addr.IsScript()) + throw std::invalid_argument(std::string(std::string(__func__) + + std::string(": p2sh address: ") + addr.ToString())); + + + _dest = dest; + } + + void Clear(){ + _dest = CNoDestination(); + } + + private: + CTxDestination _dest; +}; + +class CDerivedData : public CRegisterAddressData{ + public: + CDerivedData(){;} + CDerivedData(const pubKeyPair& p){ + pubKeyPair pc = p; + Set(pc); + } + virtual ~CDerivedData(){;} + + pubKeyPair Get(){ + return _pubKeyPair; + } + + virtual CTxDestination GetDest(){ + return _pubKeyPair.first; + } + + + void Set(const pubKeyPair& p); + + private: + pubKeyPair _pubKeyPair; +}; + +class CMultisigData : public CRegisterAddressData{ + public: + CMultisigData(){;} + virtual ~CMultisigData(){;} + + virtual CTxDestination GetDest(){ + TryValid(); + return _dest; + } + + void Set(const CTxDestination& dest, const std::vector& pubKeys, + uint8_t m){ + Clear(); + SetM(m); + SetDest(dest); + for (auto k: pubKeys){ + AddPubKey(k); + } + TryValid(); + } + + void SetM(const uint8_t& m){ + if (m > MAX_P2SH_SIGOPS || m == 0) { + throw std::invalid_argument(std::string(std::string(__func__) + + ": invalid m\n")); + } + _m=m; + } + + void SetDest(const CTxDestination& dest){ + CBitcoinAddress addr(dest); + if(!addr.IsValid()) { + throw std::invalid_argument(std::string(std::string(__func__) + + ": invalid dest: " + addr.ToString() + "\n")); + } + + if(!addr.IsScript()) { + throw std::invalid_argument(std::string(std::string(__func__) + + ": invalid non-p2sh address for multisig: " + addr.ToString() + "\n")); + } + + _dest = dest; + } + + void AddPubKey(const CPubKey& k){ + if(!k.IsFullyValid()){ + throw std::invalid_argument(std::string(std::string(__func__) + + ": invalid public key\n")); + } + _pubKeys.push_back(k); + } + + void TryValid(){ + if(Params().ContractInTx()) return; + + if(_pubKeys.size() > MAX_P2SH_SIGOPS){ + throw std::invalid_argument(std::string(std::string(__func__) + + ": n > MAX_P2SH_SIGOPS\n")); + } + uint8_t n = _pubKeys.size(); + TryValid(n); + } + + void TryValid(const uint8_t& n); + + virtual void Clear(){ + _pubKeys.clear(); + } + + private: + CTxDestination _dest; + std::vector _pubKeys; + uint8_t _m; + +}; + +class CRegisterAddressDataFactory{ + public: + CRegisterAddressDataFactory(){ + ; + } + + CRegisterAddressDataFactory(const uc_vec& data){ + CRegisterAddressDataFactory(); + InitCursor(data.begin(), data.end()); + } + virtual ~CRegisterAddressDataFactory(){;} + + + + void InitCursor(const ucvec_it& start, + const ucvec_it& end){ + _start=start; + _cursor=start; + _end=end; + _prev_cursor=_cursor; + } + + void SetCursor(const ucvec_it& c){ + _cursor = c; + } + + ucvec_it GetCursor() const{ + return _cursor; + } + + //Returns the next data object + virtual CRegisterAddressData* GetNext(); + + bool IsEnd(){ + return (_cursor == _end); + } + + protected: + virtual CMultisigData* GetNextMultisig(); + virtual CDerivedData* GetNextDerived(); + virtual CP2SHData* GetNextP2SH(); + + void ResetCursor(){_cursor=_prev_cursor;} + + void MarkReset(const ucvec_it& cursor){_prev_cursor = cursor;} + + void MarkReset(){MarkReset(_cursor);} + + bool AdvanceCursor(ucvec_it& cursor, const unsigned int& nSteps=1){ + if ( std::distance(cursor, _end) < nSteps) + return false; + std::advance(cursor, nSteps); + return true; + } + + bool AdvanceCursor(const unsigned int& nSteps=1){ + return AdvanceCursor(_cursor, nSteps); + } + + ucvec_it _cursor; + + private: + unsigned int _pubkeySize=CPubKey::COMPRESSED_PUBLIC_KEY_SIZE; + + unsigned int _version=0; + + ucvec_it _start; + ucvec_it _end; + ucvec_it _prev_cursor; + +}; + +class CRegisterAddressDataFactory_v1 : public CRegisterAddressDataFactory{ + public: + CRegisterAddressDataFactory_v1(){ + ; + } + + CRegisterAddressDataFactory_v1(const uc_vec& data) + : CRegisterAddressDataFactory(data){ + ; + } + virtual ~CRegisterAddressDataFactory_v1(){;} + + //Returns the next data object + virtual CRegisterAddressData* GetNext(); + + protected: + virtual CP2SHData* GetNextP2SH(); + virtual CP2PKHData* GetNextP2PKH(); + + private: + bool GetNextAddrType(CWhiteList::AddrType& type); + +}; \ No newline at end of file diff --git a/src/policy/whitelistEncrypted.cpp b/src/policy/whitelistEncrypted.cpp index 9f1b5d63e9..7cc9c9f122 100644 --- a/src/policy/whitelistEncrypted.cpp +++ b/src/policy/whitelistEncrypted.cpp @@ -298,9 +298,11 @@ bool CWhiteListEncrypted::RegisterAddress(const CTransaction& tx, const CCoinsVi std::vector > vSolutions; txnouttype whichType; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; - if(whichType == TX_REGISTERADDRESS) { - CScript::const_iterator pc = txout.scriptPubKey.begin(); - if (!txout.scriptPubKey.GetOp(++pc, opcode, bytes)) return false; + if(whichType == TX_REGISTERADDRESS_V0) { + //CScript::const_iterator pc = txout.scriptPubKey.begin(); + //if (!txout.scriptPubKey.GetOp(++pc, opcode, bytes)) return false; + bytes.clear(); + bytes.insert(bytes.end(), vSolutions[0].begin(), vSolutions[0].end()); break; } } diff --git a/src/policy/whitelistEncrypted.h b/src/policy/whitelistEncrypted.h index 022945d172..792a9bbbb2 100644 --- a/src/policy/whitelistEncrypted.h +++ b/src/policy/whitelistEncrypted.h @@ -22,6 +22,7 @@ class CWhiteListEncrypted : public CWhiteList{ black }; + virtual bool Load(CCoinsView *view); virtual void add_destination(const CTxDestination& dest); diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 1c7f3a0082..9728830ace 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -62,8 +62,10 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& switch (whichType) { case TX_NONSTANDARD: - case TX_REGISTERADDRESS: - case TX_DEREGISTERADDRESS: + case TX_REGISTERADDRESS_V0: + case TX_DEREGISTERADDRESS_V0: + case TX_REGISTERADDRESS_V1: + case TX_DEREGISTERADDRESS_V1: case TX_NULL_DATA: case TX_FEE: break; diff --git a/src/script/onboardingscript.cpp b/src/script/onboardingscript.cpp index 5253762e03..ac8c3675d5 100644 --- a/src/script/onboardingscript.cpp +++ b/src/script/onboardingscript.cpp @@ -26,7 +26,7 @@ bool COnboardingScript::Finalize(CScript& script, const CPubKey& onboardPubKey, const CKey& kycPrivKey){ - if(whitelistType != RA_ONBOARDING) + if(_whitelistType != RA_ONBOARDING) return false; _encrypted.clear(); CECIES_hex encryptor; @@ -43,23 +43,19 @@ bool COnboardingScript::Finalize(CScript& script, //Append the encrypted addresses sendData.insert(sendData.end(), _encrypted.begin(), _encrypted.end()); - //Assemble the script and return - script.clear(); - script << _opcode << sendData; - return true; + return BuildScript(script, sendData); } bool COnboardingScript::FinalizeUnencrypted(CScript& script){ - if(whitelistType != RA_ONBOARDING) + if(_whitelistType != RA_ONBOARDING) return false; - ucvec sendData = ToByteVector(_kycPubKey); + ucvec sendData;// = ToByteVector(_kycPubKey); //Append the addresses (unencrypted) sendData.insert(sendData.end(), _payload.begin(), _payload.end()); - script.clear(); - script << _opcode << sendData; - return true; + + return BuildScript(script, sendData); } - + diff --git a/src/script/onboardingscript.h b/src/script/onboardingscript.h index bb04a60991..bc1476f749 100644 --- a/src/script/onboardingscript.h +++ b/src/script/onboardingscript.h @@ -9,7 +9,6 @@ #include "registeraddressscript.h" - class COnboardingScript : public CRegisterAddressScript{ public: COnboardingScript(); diff --git a/src/script/registeraddressscript.cpp b/src/script/registeraddressscript.cpp index c7842bc57c..39cdee4319 100644 --- a/src/script/registeraddressscript.cpp +++ b/src/script/registeraddressscript.cpp @@ -7,14 +7,50 @@ #include "registeraddressscript.h" #include "util.h" +namespace +{ + class CByteVecVisitor : public boost::static_visitor{ + private: + CRegisterAddressScript* script; + bool bAppendType=true; + + public: + CByteVecVisitor(CRegisterAddressScript* scriptIn, + bool bAppendTypeIn=true) : script(scriptIn), + bAppendType(bAppendTypeIn) {} + + bool operator()(const CKeyID& id) const { + if(bAppendType) + script->Append(AddrType::P2PKH); + std::vector v = ToByteVector(id); + script->Append(v); + return true; + } + + bool operator()(const CScriptID& id) const { + if(bAppendType) + script->Append(AddrType::P2SH); + std::vector v = ToByteVector(id); + script->Append(v); + return true; + } + + bool operator()(const CNoDestination& no) const { return false; } + }; +} //anon namespace + + + CRegisterAddressScript::CRegisterAddressScript(RegisterAddressType type){ - whitelistType = type; + + _whitelistType = type; + _nScriptVersion = fWhitelistEncrypt ? 0 : 1; } -CRegisterAddressScript::CRegisterAddressScript(const CRegisterAddressScript* script, RegisterAddressType type){ +CRegisterAddressScript::CRegisterAddressScript(const CRegisterAddressScript* script, + RegisterAddressType type) : CRegisterAddressScript(type){ _payload = script->_payload; _encrypted = script->_encrypted; - whitelistType = type; } CRegisterAddressScript::~CRegisterAddressScript(){ @@ -30,90 +66,177 @@ bool CRegisterAddressScript::Finalize(CScript& script, const CPubKey& ePubKey, c ucvec sendData; sendData.insert(sendData.end(), _encrypted.begin(), _encrypted.end()); //Assemble the script and return - script.clear(); - script << _opcode << sendData; - return true; + return BuildScript(script, sendData); } bool CRegisterAddressScript::FinalizeUnencrypted(CScript& script){ ucvec sendData; sendData.resize(AES_BLOCKSIZE); sendData.insert(sendData.end(), _payload.begin(), _payload.end()); + return BuildScript(script, sendData); +} + +bool CRegisterAddressScript::BuildScript(CScript& script, const ucvec& sendData){ script.clear(); - script << _opcode << sendData; + switch(_nScriptVersion){ + case 0: + script << _opcode << sendData; + break; + case 1: + script << _opcode << OP_1 << sendData; + break; + default: + script << _opcode << sendData; + break; + }; return true; } +bool CRegisterAddressScript::Append(const AddrType& addr){ + unsigned char t; + switch(_nScriptVersion){ + case 1: + t = (unsigned char)addr; + AppendChar(t); + return true; + default: + return false; + }; +} + bool CRegisterAddressScript::Append(const CPubKey& pubKey){ - if(whitelistType != RA_PUBLICKEY && whitelistType != RA_ONBOARDING) - return false; + if(_whitelistType != RA_PUBLICKEY && _whitelistType != RA_ONBOARDING) + return false; + std::vector v = ToByteVector(pubKey); + Append(v); + return true; +} + + - uint256 contract = chainActive.Tip() ? chainActive.Tip()->hashContract : GetContractHash(); - CPubKey tweakedPubKey(pubKey); - if (!contract.IsNull() && !Params().ContractInTx()) - tweakedPubKey.AddTweakToPubKey((unsigned char*)contract.begin()); - CKeyID keyID=tweakedPubKey.GetID(); - if(!Params().ContractInTx() && !Consensus::CheckValidTweakedAddress(keyID, pubKey)) +bool CRegisterAddressScript::Append(const pubKeyPair& p){ + if(_whitelistType != RA_PUBLICKEY && _whitelistType != RA_ONBOARDING) return false; - - std::vector vKeyIDNew = ToByteVector(keyID); - _payload.insert(_payload.end(), - vKeyIDNew.begin(), - vKeyIDNew.end()); - std::vector vPubKeyNew = ToByteVector(pubKey); - _payload.insert(_payload.end(), - vPubKeyNew.begin(), - vPubKeyNew.end()); + if(!Params().ContractInTx()){ + if(!Consensus::CheckValidTweakedAddress(p.first, p.second)) + return false; + + CBitcoinAddress addr(p.first); + + if(!addr.IsValid()) + return false; + + Append(AddrType::DERIVED); + + if(!boost::apply_visitor(CByteVecVisitor(this, false), p.first)) + return false; + + Append(p.second); + } else { + Append(p.first); + } + return true; } -bool CRegisterAddressScript::Append(const std::vector& keys){ - if(whitelistType != RA_PUBLICKEY && whitelistType != RA_ONBOARDING) +bool CRegisterAddressScript::Append(const CTxDestination& dest){ + if(_whitelistType != RA_PUBLICKEY && _whitelistType != RA_ONBOARDING) return false; - for(CPubKey pubKey : keys){ - if (!Append(pubKey)) + CTxDestination d = dest; + + if (!Params().ContractInTx()){ + uint256 contract = chainActive.Tip() ? chainActive.Tip()->hashContract : GetContractHash(); + if (!contract.IsNull()) return false; } + + CBitcoinAddress addr(dest); + + if(!addr.IsValid()) + return false; + + if(!boost::apply_visitor(CByteVecVisitor(this), dest)) + return false; + return true; } + bool CRegisterAddressScript::Append(const uint8_t nMultisig, const CTxDestination keyID, const std::vector& keys){ - if(whitelistType != RA_MULTISIG && whitelistType != RA_ONBOARDING) + if(_whitelistType != RA_MULTISIG && _whitelistType != RA_ONBOARDING) return false; - if (!Params().ContractInTx() && !(Consensus::CheckValidTweakedAddress(keyID, keys, nMultisig))) + if (!Params().ContractInTx()){ + + if(!(Consensus::CheckValidTweakedAddress(keyID, keys, nMultisig))) + return false; + + unsigned int nAppend=0; + if(Append(AddrType::MULTI)) ++nAppend; + AppendChar((unsigned char)nMultisig); + ++nAppend; + AppendChar((unsigned char)keys.size()); + ++nAppend; + + if(!boost::apply_visitor(CByteVecVisitor(this, false), keyID)) { + PopBack(nAppend); + return false; + } + + for(unsigned int i = 0; i < keys.size(); ++i){ + Append(keys[i]); + } + } else { + Append(keyID); + } + return true; +} + +bool CRegisterAddressScript::Append(const OnboardMultisig& data){ + if(_whitelistType != RA_MULTISIG && _whitelistType != RA_ONBOARDING) return false; - - _payload.insert(_payload.end(), - (unsigned char)nMultisig); - _payload.insert(_payload.end(), - (unsigned char)keys.size()); + return Append(data.nMultisig, data.scriptID, data.pubKeys); +} - CScriptID scriptID = boost::get(keyID); +void CRegisterAddressScript::Append(const std::vector& v){ _payload.insert(_payload.end(), - scriptID.begin(), - scriptID.end()); + v.begin(), + v.end()); +} + +bool CRegisterAddressScript::Append(const std::vector& v){ + if(_whitelistType != RA_PUBLICKEY && _whitelistType != RA_ONBOARDING) + return false; - for(unsigned int i = 0; i < keys.size(); ++i){ - _payload.insert(_payload.end(), - keys[i].begin(), - keys[i].end()); + for (auto p : v){ + if(!Append(p)) + return false; } return true; } -bool CRegisterAddressScript::Append(const std::vector& _data){ - if(whitelistType != RA_MULTISIG && whitelistType != RA_ONBOARDING) +bool CRegisterAddressScript::Append(const std::vector& v){ + if(_whitelistType != RA_PUBLICKEY && _whitelistType != RA_ONBOARDING) return false; - for(OnboardMultisig _multi : _data){ - if (!Append(_multi.nMultisig, _multi.scriptID, _multi.pubKeys)) + for (auto p : v){ + if(!Append(p)) return false; } return true; } +bool CRegisterAddressScript::Append(const std::vector& v){ + if(_whitelistType != RA_PUBLICKEY && _whitelistType != RA_ONBOARDING) + return false; + + for (auto p : v){ + if(!Append(p)) + return false; + } + return true; +} diff --git a/src/script/registeraddressscript.h b/src/script/registeraddressscript.h index 0936365703..5efc28a56b 100644 --- a/src/script/registeraddressscript.h +++ b/src/script/registeraddressscript.h @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -// An ecrypted "register address" transaction script. +// A "register address" transaction script. #pragma once #include "script.h" @@ -10,6 +10,8 @@ #include "validation.h" using ucvec=std::vector; +using pubKeyPair=std::pair; +using AddrType = CWhiteList::AddrType; struct OnboardMultisig { uint8_t nMultisig; @@ -33,22 +35,62 @@ class CRegisterAddressScript { //Encrypt the payload using the public, private key and build the script. virtual bool Finalize(CScript& script, const CPubKey& ePubKey, const CKey& ePrivKey); virtual bool FinalizeUnencrypted(CScript& script); - bool Append(const CPubKey& key); - bool Append(const std::vector& keys); - bool Append(const uint8_t nMultisig, const CTxDestination keyID, const std::vector& keys); - bool Append(const std::vector& _data); + bool BuildScript(CScript& script, const ucvec& sendData); + bool Append(const AddrType& addr); + bool Append(const pubKeyPair& keyPair); + bool Append(const CTxDestination& dest); + bool Append(const OnboardMultisig& data); + bool Append(const std::vector& v); + bool Append(const std::vector& v); + bool Append(const std::vector& v); + + bool Append(const uint8_t nMultisig, const CTxDestination keyID, + const std::vector& keys); + void Append(const std::vector& v); + + bool ScriptVersion(unsigned int nVersion){ + if (nVersion >1) return false; + _nScriptVersion = nVersion; + return true; + } + + unsigned int ScriptVersion() const{ + return _nScriptVersion; + } + + + void AppendChar(const unsigned char& c){ + _payload.push_back(c); + } + std::size_t getPayloadSize() { return _payload.size(); } virtual void clear(){_payload.clear(); _encrypted.clear(); ((CScript*)this)->clear();} //Make this a "deregister" transaction (remove address from whitelist). - void SetDeregister(bool bDereg){ + virtual void SetDeregister(bool bDereg){ bDereg ? _opcode = OP_DEREGISTERADDRESS: _opcode = OP_REGISTERADDRESS; } + void PopBack(){ + _payload.pop_back(); + } + + void PopBack(unsigned int n){ + for(unsigned int i=0; i& keys); + + unsigned int _nScriptVersion = 1; + }; \ No newline at end of file diff --git a/src/script/script.cpp b/src/script/script.cpp index 964f207a38..763641b241 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -283,6 +283,47 @@ bool CScript::IsWitnessProgram(int& version, std::vector& program return false; } +bool CScript::IsRegisteraddress() const { + int version; + std::vector data; + bool whitelist; + return IsRegisteraddress(version, data, whitelist); +} + +bool CScript::IsRegisteraddress(int &version, std::vector& data, bool& whitelist) const { + if(!size()>0) return false; + + CScript::const_iterator pc = begin(); + + if(*pc == OP_REGISTERADDRESS){ + whitelist = true; + } else if (*pc == OP_DEREGISTERADDRESS){ + whitelist = false; + } else { + return false; + } + + if(!IsPushOnly(++pc)) + return false; + + opcodetype opcode; + if(!GetOp(pc, opcode, data)) + return false; + + if (opcode <= OP_PUSHDATA4){ + version = 0; + return true; + } else if (opcode == OP_1) { + version = 1; + if(!GetOp(pc, opcode, data)) + return false; + return (opcode <= OP_PUSHDATA4); + } else { + return false; + } +} + + bool CScript::IsPushOnly(const_iterator pc) const { while (pc < end()) @@ -300,6 +341,7 @@ bool CScript::IsPushOnly(const_iterator pc) const return true; } + bool CScript::IsPushOnly() const { return this->IsPushOnly(begin()); diff --git a/src/script/script.h b/src/script/script.h index e5a3540840..5d310b4de8 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -664,10 +664,8 @@ class CScript : public CScriptBase return (size() > 0 && (*begin() == OP_RETURN || *begin() == OP_REGISTERADDRESS || *begin() == OP_DEREGISTERADDRESS)) || (size() > MAX_SCRIPT_SIZE) || (size() == 0); } - bool IsRegisteraddress() const - { - return (size() > 0 && (*begin() == OP_REGISTERADDRESS || *begin() == OP_DEREGISTERADDRESS)); - } + bool IsRegisteraddress() const; + bool IsRegisteraddress(int& version, std::vector& data, bool& whitelist) const; void clear() { diff --git a/src/script/sign.cpp b/src/script/sign.cpp index f0d347e70e..52b3cab64a 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -83,9 +83,13 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_NONSTANDARD: case TX_NULL_DATA: return false; - case TX_REGISTERADDRESS: + case TX_REGISTERADDRESS_V0: return false; - case TX_DEREGISTERADDRESS: + case TX_DEREGISTERADDRESS_V0: + return false; + case TX_REGISTERADDRESS_V1: + return false; + case TX_DEREGISTERADDRESS_V1: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index d54e8a7741..2345bc6d16 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -36,8 +36,10 @@ const char* GetTxnOutputType(txnouttype t) case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; case TX_NULL_DATA: return "nulldata"; - case TX_REGISTERADDRESS: return "registeraddress"; - case TX_DEREGISTERADDRESS: return "deregisteraddress"; + case TX_REGISTERADDRESS_V0: return "registeraddress_v0"; + case TX_DEREGISTERADDRESS_V0: return "deregisteraddress_v0"; + case TX_REGISTERADDRESS_V1: return "registeraddress_v1"; + case TX_DEREGISTERADDRESS_V1: return "deregisteraddress_v1"; case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; case TX_TRUE: return "true"; @@ -157,6 +159,33 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector radata; + int raversion; + bool rawhitelist; + if(scriptPubKey.IsRegisteraddress(raversion, radata, rawhitelist)){ + if(raversion == 0){ + if(rawhitelist){ + typeRet = TX_REGISTERADDRESS_V0; + } else { + typeRet = TX_DEREGISTERADDRESS_V0; + } + } else if(raversion == 1){ + if(rawhitelist){ + typeRet = TX_REGISTERADDRESS_V1; + } else { + typeRet = TX_DEREGISTERADDRESS_V1; + } + } + vSolutionsRet.push_back(radata); + return true; + } + + if (scriptPubKey == CScript() << OP_TRUE) { + typeRet = TX_TRUE; + return true; + } + // Provably prunable, data-carrying output // // So long as script passes the IsUnspendable() test and all but the first @@ -167,28 +196,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector= 1 && scriptPubKey[0] == OP_REGISTERADDRESS){ - if(scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)){ - typeRet = TX_REGISTERADDRESS; - return true; - } - } - - if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_DEREGISTERADDRESS){ - if(scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)){ - typeRet = TX_DEREGISTERADDRESS; - return true; - } - } - - if (scriptPubKey == CScript() << OP_TRUE) { - typeRet = TX_TRUE; - return true; - } - std::vector data; if (MatchPayToPubkey(scriptPubKey, data)) { typeRet = TX_PUBKEY; @@ -264,8 +271,9 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto vector vSolutions; if (!Solver(scriptPubKey, typeRet, vSolutions)) return false; - if (typeRet == TX_NULL_DATA || typeRet == TX_FEE || typeRet == TX_REGISTERADDRESS - || typeRet == TX_DEREGISTERADDRESS){ + if (typeRet == TX_NULL_DATA || typeRet == TX_FEE || typeRet == TX_REGISTERADDRESS_V0 + || typeRet == TX_DEREGISTERADDRESS_V0 || typeRet == TX_REGISTERADDRESS_V1 + || typeRet == TX_DEREGISTERADDRESS_V1){ // This is data, not addresses return false; } diff --git a/src/script/standard.h b/src/script/standard.h index d2d7d5e092..eceb6af1b6 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -56,8 +56,10 @@ enum txnouttype TX_SCRIPTHASH, TX_MULTISIG, TX_NULL_DATA, - TX_REGISTERADDRESS, - TX_DEREGISTERADDRESS, + TX_REGISTERADDRESS_V0, + TX_DEREGISTERADDRESS_V0, + TX_REGISTERADDRESS_V1, + TX_DEREGISTERADDRESS_V1, TX_WITNESS_V0_SCRIPTHASH, TX_WITNESS_V0_KEYHASH, TX_TRUE, diff --git a/src/validation.cpp b/src/validation.cpp index 2c98fefa00..f01e4203e2 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1829,24 +1829,31 @@ int GetSpendHeight(const CCoinsViewCache& inputs) } namespace Consensus { -bool CheckValidTweakedAddress(const CTxDestination keyID, const CPubKey& pubKey){ + +bool CheckValidTweakedAddress(const pubKeyPair& p){ + pubKeyPair p_copy = p; + return CheckValidTweakedAddress(p_copy.first, p_copy.second); +} + +bool CheckValidTweakedAddress(const CTxDestination& keyID, const CPubKey& pubKey){ uint256 contract = chainActive.Tip() ? chainActive.Tip()->hashContract : GetContractHash(); - CPubKey tmpPubKey = pubKey; + CTxDestination dest = keyID; + CPubKey tmpPubKey = pubKey; - if (!contract.IsNull()) - tmpPubKey.AddTweakToPubKey((unsigned char*)contract.begin()); + if (!contract.IsNull()) + tmpPubKey.AddTweakToPubKey((unsigned char*)contract.begin()); - if (tmpPubKey.GetID() != boost::get(keyID)) - return false; + CBitcoinAddress addr(dest); -return true; + CBitcoinAddress addrTweaked(tmpPubKey.GetID()); + + return addr.IsValid() && addr == addrTweaked; } //Used for multisig P2SH checking that has been created with tweaked addresses -bool CheckValidTweakedAddress(const CTxDestination keyID, const std::vector& pubKeys, const uint8_t nMultisig){ +bool CheckValidTweakedAddress(const CTxDestination& keyID, const std::vector& pubKeys, const uint8_t& nMultisig){ - CTxDestination destCopy = keyID; std::vector tweakedPubKeys = pubKeys; uint256 contract = chainActive.Tip() ? chainActive.Tip()->hashContract : GetContractHash(); @@ -1858,18 +1865,30 @@ bool CheckValidTweakedAddress(const CTxDestination keyID, const std::vector(multiKeyId) == boost::get(destCopy))) - return false; + CTxDestination dest = keyID; + CBitcoinAddress addressFromDest(dest); - return true; + return addressFromDest.IsScript() && addressFromDest == addressFromPubkeys; + +// if(!CBitcoinAddress(multiKeyId).IsScript() || +// !CBitcoinAddress(destCopy).IsScript()) +// return false; + +// if (!(boost::get(multiKeyId) == boost::get(destCopy))) +// return false; + + + + +// return true; } bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, std::set >& setPeginsSpent, std::vector *pvChecks, const bool cacheStore, bool fScriptChecks) diff --git a/src/validation.h b/src/validation.h index 5df9418cd0..bf15e77b67 100644 --- a/src/validation.h +++ b/src/validation.h @@ -458,18 +458,25 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true); namespace Consensus { + using pubKeyPair = std::pair; /** * Check wether the bitcoin address is derived from the public key via key tweaking * using the hash contract of the chain active tip. */ -bool CheckValidTweakedAddress(const CTxDestination keyID, const CPubKey& pubKey); +bool CheckValidTweakedAddress(const pubKeyPair& p); + +/** +* Check wether the bitcoin address is derived from the public key via key tweaking +* using the hash contract of the chain active tip. +*/ +bool CheckValidTweakedAddress(const CTxDestination& keyID, const CPubKey& pubKey); /** * Check whether the script address is derived from the public keys via key tweaking * using the hash contract of the chain active tip. */ -bool CheckValidTweakedAddress(const CTxDestination keyID, const std::vector& pubKeys, const uint8_t nMultisig); +bool CheckValidTweakedAddress(const CTxDestination& keyID, const std::vector& pubKeys, const uint8_t& nMultisig); /** * Check whether all inputs of this transaction are valid (no double spends and amounts) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 84ddbfb938..4626e66d43 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -690,8 +690,8 @@ UniValue createkycfile(const JSONRPCRequest& request) "2. \"pubkeylist\" (array, required) A json array of json objects\n" " [\n" " {\n" - " \"address\":\"string\", (string, required) The tweaked bitcoin address\n" - " \"pubkey\":\"string\", (string, required) The untweaked pubkey that the address was generated with\n" + " \"address\":\"string\", (string, required) The base58check address (tweaked for sidechains using the option contractintx=0). Can be P2SH or P2SH-P2WSH if contractintx=1, otherwise P2PKH only\n" + " \"pubkey\":\"string\", (string, required) The untweaked pubkey that the address was generated with. Not required for sidechains using the option contractintx=1.\n" " } \n" " ,...\n" " ]\n" @@ -748,7 +748,7 @@ UniValue createkycfile(const JSONRPCRequest& request) UniValue multisigList = request.params[2].get_array(); // produce output - ssFile << strprintf("# Created KYC file made by Bitcoin %s\n", CLIENT_BUILD); + ssFile << strprintf("# Created KYC file made by Ocean %s\n", CLIENT_BUILD); ssFile << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); ssFile << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); ssFile << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); @@ -772,34 +772,51 @@ UniValue createkycfile(const JSONRPCRequest& request) } } + bool fContractInTx = Params().ContractInTx(); + CPubKey* firstPubKey = nullptr; // add the tweaked bitcoin address and untweaked pubkey hex to a stringstream for(unsigned int i = 0; i < pubKeyList.size(); ++i) { UniValue pubkeyObj = pubKeyList[i]; - if (!pubkeyObj.exists("address") || !pubkeyObj.exists("pubkey")) - continue; + if (!pubkeyObj.exists("address")) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Address missing in pubkeylist"); std::string addrStr = pubkeyObj["address"].getValStr(); - std::string pubkeyStr = pubkeyObj["pubkey"].getValStr(); CBitcoinAddress addrNew; if(!addrNew.SetString(addrStr)) - continue; + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid address in pubkeylist: ") + addrStr); - std::vector pubKeyData(ParseHex(pubkeyStr.c_str())); - CPubKey pubKey = CPubKey(pubKeyData.begin(), pubKeyData.end()); - if(!pubKey.IsFullyValid()) - continue; + std::string pubkeyStr = ""; - if(!firstPubKey){ - firstPubKey = new CPubKey(pubKey); + if (!pubkeyObj.exists("pubkey")){ + if(!fContractInTx){ + throw JSONRPCError(RPC_INVALID_PARAMETER, "Pubkey missing in pubkeylist"); + } + } else { + pubkeyStr = pubkeyObj["pubkey"].getValStr(); } - ss << strprintf("%s %s\n", - addrStr, - HexStr(pubKey.begin(), pubKey.end())); + + if (pubkeyStr.size() > 0){ + std::vector pubKeyData(ParseHex(pubkeyStr.c_str())); + CPubKey pubKey = CPubKey(pubKeyData.begin(), pubKeyData.end()); + + if(!pubKey.IsFullyValid()) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid pubkey in pubkeylist: ") + pubkeyStr); + + if(!firstPubKey){ + firstPubKey = new CPubKey(pubKey); + } + + ss << strprintf("%s %s\n", + addrStr, + HexStr(pubKey.begin(), pubKey.end())); + } else { + ss << strprintf("%s\n",addrStr); + } } uint256 contract = chainActive.Tip() ? chainActive.Tip()->hashContract : GetContractHash(); @@ -808,38 +825,37 @@ UniValue createkycfile(const JSONRPCRequest& request) for(unsigned int i = 0; i < multisigList.size(); ++i) { UniValue multiObj = multisigList[i]; - if (!multiObj.exists("nmultisig") || !multiObj.exists("pubkeys")) - continue; + if (!multiObj.exists("nmultisig")) + throw JSONRPCError(RPC_INVALID_PARAMETER, "nmultisig missing in multisiglist"); + if(!multiObj.exists("pubkeys")) + throw JSONRPCError(RPC_INVALID_PARAMETER, "pubkeys missing in multisiglist"); + UniValue const &nMultiObj = find_value(multiObj, "nmultisig"); if (!nMultiObj.isNum()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "nmultisig must be an integer"); unsigned nMultisig = nMultiObj.get_int(); + std::stringstream ss2(""); + ss2 << MAX_P2SH_SIGOPS; if (nMultisig > MAX_P2SH_SIGOPS || nMultisig == 0) - continue; + throw JSONRPCError(RPC_INVALID_PARAMETER, "nmultisig must be an integer between 1 and " + ss2.str()); UniValue pubkeyArr = multiObj["pubkeys"].get_array(); - bool shouldContinue = false; std::vector pubKeyVec; for (unsigned int j = 0; j < pubkeyArr.size(); ++j){ std::string parseStr = pubkeyArr[j].get_str(); std::vector pubKeyData(ParseHex(parseStr.c_str())); CPubKey pubKey = CPubKey(pubKeyData.begin(), pubKeyData.end()); - if(!pubKey.IsFullyValid()){ - shouldContinue = true; - break; - } + if(!pubKey.IsFullyValid()) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid pubkey in multisiglist: ") + parseStr); pubKeyVec.push_back(pubKey); if(!firstPubKey){ firstPubKey = new CPubKey(pubKey); } } - if(shouldContinue) - continue; - std::vector tweakedPubKeys = pubKeyVec; if (Params().EmbedContract() &! contract.IsNull() && !Params().ContractInTx()){ @@ -856,7 +872,7 @@ UniValue createkycfile(const JSONRPCRequest& request) CTxDestination multiKeyId; multiKeyId = address.Get(); if (multiKeyId.which() == ((CTxDestination)CNoDestination()).which()) - continue; + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid address from pubkeys in multisiglist")); ss << strprintf("%d %s", nMultisig, @@ -912,8 +928,8 @@ UniValue createkycfile(const JSONRPCRequest& request) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open key dump file"); file << ssFile.str(); - file.close(); + file.close(); } else { result.push_back(Pair("kycfile", ssFile.str())); } @@ -1093,7 +1109,7 @@ UniValue validatekycfile(const JSONRPCRequest& request) - std::vector addressKeyIds = file.getAddressKeyIds(); + std::vector addressKeyIds = file.getAddresses(); UniValue addresses(UniValue::VARR); for(auto k: addressKeyIds){ CBitcoinAddress addr=CBitcoinAddress(k); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 6622b0e242..fe094d08ad 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -806,7 +806,7 @@ UniValue onboarduser(const JSONRPCRequest& request){ if (request.fHelp || request.params.size() != 1) throw runtime_error( "onboarduser \"filename\" \n" - "Read in derived keys and tweaked addresses from kyc file (see dumpkycfile) into the address whitelist. Assign a KYC public key to the user if using encrypted whitelist.\n" + "Read in derived keys and tweaked addresses from kyc file (see dumpkycfile, createkycfile) into the address whitelist. Assign a KYC public key to the user if using encrypted whitelist.\n" "\nArguments:\n" "1. \"filename\" (string, required) The kyc file name\n" @@ -1095,7 +1095,7 @@ static void SendAddNextToWhitelistTx(const CAsset& feeAsset, if (pwalletMain->GetKey(keyid, key)) { // verify exists //keysToReg.push_back(key.GetPubKey()); if(keysToReg.insert(keyid).second){ - if(!raScript->Append(key.GetPubKey())) + if(!raScript->Append(std::pair(keyid,key.GetPubKey()))) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Failed to append address to registeraddress script"); if(keysToReg.size() == nToRegister) break;