Skip to content

Commit

Permalink
Merge pull request #147 from commerceblock/debug-multisig
Browse files Browse the repository at this point in the history
Debug multisig and whitelist synchronisation
  • Loading branch information
lawlawlaw authored Jun 21, 2019
2 parents 2d8909c + 634e825 commit d7a3829
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 102 deletions.
115 changes: 107 additions & 8 deletions qa/rpc-tests/onboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,79 @@ def __init__(self):
self.extra_args[1].append("-regtest=0")
self.extra_args[1].append("-pkhwhitelist-scan=1")
self.extra_args[1].append("-keypool=100")
self.extra_args[1].append("-freezelist=1")
self.extra_args[1].append("-burnlistist=1")
self.extra_args[1].append("-initialfreecoins=2100000000000000")
self.extra_args[1].append("-policycoins=50000000000000")
self.extra_args[1].append("-initialfreecoinsdestination=76a914b87ed64e2613422571747f5d968fff29a466e24e88ac")
self.extra_args[1].append("-issuancecoinsdestination=76a914df4439eb1a54b3a91d71979a0bb5b3f5971ff44c88ac")
self.extra_args[1].append("-freezelistcoinsdestination=76a91474168445da07d331faabd943422653dbe19321cd88ac")
self.extra_args[1].append("-burnlistcoinsdestination=76a9142166a4cd304b86db7dfbbc7309131fb0c4b645cd88ac")
self.extra_args[1].append("-whitelistcoinsdestination=76a914427bf8530a3962ed77fd3c07d17fd466cb31c2fd88ac")
self.extra_args[2].append("-rescan=1")
self.extra_args[2].append("-regtest=0")
self.extra_args[2].append("-pkhwhitelist-scan=1")
self.extra_args[2].append("-keypool=100")
self.extra_args[2].append("-freezelist=1")
self.extra_args[2].append("-burnlist=1")
self.extra_args[2].append("-pkhwhitelist=1")
self.extra_args[2].append("-rescan=1")
self.extra_args[2].append("-initialfreecoins=2100000000000000")
self.extra_args[2].append("-policycoins=50000000000000")
self.extra_args[2].append("-regtest=0")
self.extra_args[2].append("-initialfreecoinsdestination=76a914b87ed64e2613422571747f5d968fff29a466e24e88ac")
self.extra_args[2].append("-issuancecoinsdestination=76a914df4439eb1a54b3a91d71979a0bb5b3f5971ff44c88ac")
self.extra_args[2].append("-freezelistcoinsdestination=76a91474168445da07d331faabd943422653dbe19321cd88ac")
self.extra_args[2].append("-burnlistcoinsdestination=76a9142166a4cd304b86db7dfbbc7309131fb0c4b645cd88ac")
self.extra_args[2].append("-whitelistcoinsdestination=76a914427bf8530a3962ed77fd3c07d17fd466cb31c2fd88ac")
self.files=[]
self.nodes=[]

def setup_network(self, split=False):
#Start a node, get the wallet file, stop the node and use the wallet file as the whitelisting wallet
#Start nodes
self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args[:3])
#Set up wallet path and dump the wallet
wlwalletname="wlwallet.dat"
wlwalletpath=os.path.join(self.options.tmpdir,wlwalletname)
print(self.options.tmpdir)
time.sleep(5)
self.nodes[0].backupwallet(wlwalletpath)

#Stop the nodes
stop_nodes(self.nodes)
time.sleep(5)

#Copy the wallet file to the node 0 and 2 data dirs
#Give nodes 0 and 2 the same wallet (whitelist wallet)
node0path=os.path.join(self.options.tmpdir, "node"+str(0))
node1path=os.path.join(self.options.tmpdir, "node"+str(1))
node2path=os.path.join(self.options.tmpdir, "node"+str(2))

dest0=os.path.join(node0path, "ocean_test")
dest0=os.path.join(dest0, wlwalletname)
dest2=os.path.join(node2path, "ocean_test")
dest2=os.path.join(dest2, wlwalletname)

shutil.copyfile(wlwalletpath,dest0)
shutil.copyfile(wlwalletpath,dest2)

print(node2path)
print(wlwalletpath)
print(dest0)
time.sleep(5)

#Start the nodes again with a different wallet path argument
self.extra_args[0].append("-wallet="+wlwalletname)
self.extra_args[2].append("-wallet="+wlwalletname)
self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args[:3])

time.sleep(5)

#Node0 and node2 wallets should be the same
addr0=self.nodes[0].getnewaddress()
addr2=self.nodes[2].getnewaddress()
print(addr0)
print(addr2)
assert(addr0 == addr2)

connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
Expand Down Expand Up @@ -130,11 +181,6 @@ def run_test (self):
wltxid = txid
wlvalue = rawtx["vout"][0]["value"]

#Whitelist node 0 addresses
keys_main=self.initfile("keys.main")
self.nodes[0].dumpderivedkeys(keys_main)
self.nodes[0].readwhitelist(keys_main)

#No kycpubkeys available
kycfile="kycfile.dat"
try:
Expand All @@ -148,6 +194,14 @@ def run_test (self):
self.sync_all()
time.sleep(5)


#Onboard node0
kycfile0=self.initfile("kycfile0.dat")
userOnboardPubKey=self.nodes[0].dumpkycfile(kycfile0)
self.nodes[0].onboarduser(kycfile0)
self.nodes[0].generate(101)
self.sync_all()

#Onboard node1
kycfile=self.initfile("kycfile.dat")
userOnboardPubKey=self.nodes[1].dumpkycfile(kycfile)
Expand Down Expand Up @@ -182,6 +236,8 @@ def run_test (self):

bal1=self.nodes[1].getwalletinfo()["balance"]["CBT"]

print(bal1)
print(ntosend)
assert_equal(float(bal1),float(ntosend))

#Restart the nodes. The whitelist will be restored. TODO
Expand Down Expand Up @@ -379,6 +435,49 @@ def run_test (self):
except JSONRPCException as e:
assert("too many keys in input array" in e.error['message'])


#assert whitelist file are the same for the two nodes
wl0file=self.initfile(self.options.tmpdir+"wl0.dat")
self.nodes[0].dumpwhitelist(wl0file)

wl2file=self.initfile(self.options.tmpdir+"wl2.dat")
self.nodes[2].dumpwhitelist(wl2file)

assert(filecmp.cmp(wl0file, wl2file))

with open(wl0file, 'r') as fin0, open(wl2file, 'r') as fin2:
lines0=fin0.readlines()
lines2=fin2.readlines()

set0=set(lines0)
set2=set(lines2)

len0=len(set0)
len2=len(set2)

if len0 != len2:
print("len0: " + str(len0))
print("len2: " + str(len2))

assert(len0 == len2)

diff0=set0.difference(set2)
diff2=set2.difference(set0)

lendiff0 = len(diff0)
lendiff2 = len(diff2)


if lendiff0 > 0:
print(diff0)

if lendiff2 > 0:
print(diff2)

assert(lendiff0 == 0)
assert(lendiff2 == 0)


self.cleanup_files()
return

Expand Down
4 changes: 2 additions & 2 deletions qa/rpc-tests/test_framework/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,15 +367,15 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=

return proxy

def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, firstnode=0):
"""
Start multiple bitcoinds, return RPC connections to them
"""
if extra_args is None: extra_args = [ None for _ in range(num_nodes) ]
if binary is None: binary = [ None for _ in range(num_nodes) ]
rpcs = []
try:
for i in range(num_nodes):
for i in range(firstnode, num_nodes):
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i]))
except: # If one node failed to start, stop the others
stop_nodes(rpcs)
Expand Down
82 changes: 63 additions & 19 deletions src/policy/whitelist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chain.h"
#include "whitelist.h"
#include "validation.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
#include "ecies_hex.h"
#include "policy/policy.h"
#include "rpc/server.h"

CWhiteList::CWhiteList(){
_asset=whitelistAsset;
Expand Down Expand Up @@ -237,24 +239,25 @@ void CWhiteList::add_multisig_whitelist(const std::string& sAddress, const UniVa
add_multisig_whitelist(address, pubKeyVec, kycPubKey, nMultisig);
}

#ifdef ENABLE_WALLET
bool CWhiteList::RegisterAddress(const CTransaction& tx, const CBlockIndex* pindex){
boost::recursive_mutex::scoped_lock scoped_lock(_mtx);
CCoinsViewCache mapInputs(pcoinsTip);
mapInputs.SetBestBlock(pindex->GetBlockHash());
return RegisterAddress(tx, mapInputs);
}
#endif //#ifdef ENABLE_WALLET

bool CWhiteList::RegisterAddress(const CTransaction& tx, const CCoinsViewCache& mapInputs){
boost::recursive_mutex::scoped_lock scoped_lock(_mtx);
#ifdef ENABLE_WALLET
boost::recursive_mutex::scoped_lock scoped_lock(_mtx);
if(!mapInputs.HaveInputs(tx))
return false; // No inputs for tx in cache

if (tx.IsCoinBase())
return false; // Coinbases don't use vin normally

LOCK2(cs_main, pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();

//Check if this is a TX_REGISTERADDRESS. If so, read the data into a byte vector.
opcodetype opcode;
std::vector<unsigned char> bytes;
Expand Down Expand Up @@ -316,6 +319,10 @@ bool CWhiteList::RegisterAddress(const CTransaction& tx, const CCoinsViewCache&
//Onboarding must be done using the whitelist asset
if(!IsWhitelistAssetOnly(tx)) return false;
// Check if reading from the client node

//The user onboard pubkey is one of the unassigned KYC pubkeys - so
//this will have been derived already in add_unassigned_kycpubkey,
//even if the node has been restarted
if(pwalletMain->GetKey(userOnboardPubKey.GetID(), userOnboardPrivKey)){
// kycPubKey assigned to me by the whitelisting node
pwalletMain->SetKYCPubKey(kycPubKey);
Expand All @@ -324,7 +331,7 @@ bool CWhiteList::RegisterAddress(const CTransaction& tx, const CCoinsViewCache&
inputPubKeys.insert(userOnboardPubKey);
inputPubKey = userOnboardPubKey;
decryptPubKey = inputPubKey;
} else {
} else {
it1=bytes.begin(); //Reset iterator
kycPubKey=pwalletMain->GetKYCPubKey(); //For the non-whitelisting nodes
kycKey=kycPubKey.GetID();
Expand Down Expand Up @@ -361,12 +368,20 @@ bool CWhiteList::RegisterAddress(const CTransaction& tx, const CCoinsViewCache&
std::unique_ptr<CBitcoinAddress> kycAddr(new CBitcoinAddress(kycKey));

// Get the KYC private key from the wallet.
// If not found, generate new keys up to nMaxGap.
// This will allow the nsigning nodes to generate the necessary keys
// nMaxUnassigned is the maximum number of unassigned keys
CKey decryptPrivKey;
if(!pwalletMain->GetKey(kycKey, decryptPrivKey)){
//Non-whitelisting node
if(!pwalletMain->GetKey(inputPubKey.GetID(), decryptPrivKey)) return false;
if(!pwalletMain->GetKey(inputPubKey.GetID(), decryptPrivKey)){
//Whitelisting node?
// For debugging - translate into bitcoin address
CBitcoinAddress addr(kycKey);
std::string sAddr = addr.ToString();
if(!pwalletMain->GetKey(kycKey, decryptPrivKey))
return false;
} else {
decryptPubKey=kycPubKey;
}
}

//Decrypt
CECIES_hex decryptor;
Expand All @@ -383,9 +398,11 @@ bool CWhiteList::RegisterAddress(const CTransaction& tx, const CCoinsViewCache&
LogPrintf("POLICY: wallet not enabled - unable to process registeraddress transaction.\n");
return false;
#endif //#ifdef ENABLE_WALLET
return true;
}



bool CWhiteList::RegisterDecryptedAddresses(const std::vector<unsigned char>& data, const std::unique_ptr<CPubKey>& kycPubKey){
//Interpret the data
//First 20 bytes: keyID
Expand Down Expand Up @@ -439,11 +456,11 @@ bool CWhiteList::RegisterDecryptedAddresses(const std::vector<unsigned char>& da
}
try{
add_derived(addrNew, pubKeyNew, kycPubKey);
++pairsAdded;
} catch (std::invalid_argument e){
LogPrintf(std::string(e.what()) + "\n");
return bSuccess;
}
++pairsAdded;
bSuccess = true;
}
}
Expand Down Expand Up @@ -607,7 +624,8 @@ bool CWhiteList::Update(const CTransaction& tx, const CCoinsViewCache& mapInputs
// The first dummy key in the multisig is the (scrambled) kyc public key.
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);

if(prev.nAsset.GetAsset() != whitelistAsset)
return false;
std::vector<std::vector<unsigned char> > vSolutions;
txnouttype whichType;

Expand Down Expand Up @@ -688,6 +706,7 @@ void CWhiteList::whitelist_kyc(const CKeyID& keyId, const COutPoint* outPoint){
set_kyc_status(keyId, CWhiteList::status::white);
if(outPoint)
_kycPubkeyOutPointMap[keyId]=*outPoint;
_kycUnassignedSet.erase(keyId);
}

bool CWhiteList::get_kycpubkey_outpoint(const CKeyID& keyId, COutPoint& outPoint){
Expand Down Expand Up @@ -738,22 +757,27 @@ bool CWhiteList::get_unassigned_kyc(CPubKey& pubKey){
if(peek_unassigned_kyc(pubKey)){
//Move from "unassigned" to whitelisted in this node.
//Other nodes will be notified via the user onboard transaction.
whitelist_kyc(pubKey.GetID());
CKeyID id = pubKey.GetID();
whitelist_kyc(id);
_kycUnassignedQueue.pop();
auto it = _kycUnassignedSet.find(pubKey.GetID());
if(it != _kycUnassignedSet.end()){
_kycUnassignedSet.erase(it);
}
_kycUnassignedSet.erase(id);
return true;
}
return false;
}

bool CWhiteList::peek_unassigned_kyc(CPubKey& pubKey){
boost::recursive_mutex::scoped_lock scoped_lock(_mtx);
if (_kycUnassignedQueue.empty()) return false;
pubKey = _kycUnassignedQueue.front();
return true;
while (!_kycUnassignedQueue.empty()){
pubKey = _kycUnassignedQueue.front();
auto it = _kycUnassignedSet.find(pubKey.GetID());
if(it == _kycUnassignedSet.end()){
_kycUnassignedQueue.pop();
} else {
return true;
}
}
return false;
}

bool CWhiteList::is_unassigned_kyc(const CKeyID& kycKeyID){
Expand All @@ -765,7 +789,27 @@ bool CWhiteList::is_unassigned_kyc(const CKeyID& kycKeyID){

void CWhiteList::add_unassigned_kyc(const CPubKey& id){
boost::recursive_mutex::scoped_lock scoped_lock(_mtx);
_kycUnassignedSet.insert(id.GetID());
//If this is the whitelisting node, the private key should be in the wallet.
//Generate new keys up to the limit until found.
//If not found, return and error.
CKeyID kycKey=id.GetID();
#ifdef ENABLE_WALLET
if(fRequireWhitelistCheck){
LOCK2(cs_main, pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
CKey privKey;
int nTries=0;
while(!pwalletMain->GetKey(kycKey, privKey)){
CPubKey newPubKey = pwalletMain->GenerateNewKey();
if(++nTries > MAX_UNASSIGNED_KYCPUBKEYS){
LogPrintf("ERROR: kyc privkey not in whitelisting node wallet"+HexStr(id.begin(), id.end())+"\n");
break;
}
}
}
#endif //ENABLE_WALLET

_kycUnassignedSet.insert(kycKey);
_kycUnassignedQueue.push(id);
}

Expand Down
Loading

0 comments on commit d7a3829

Please sign in to comment.