diff --git a/qa/rpc-tests/onboard.py b/qa/rpc-tests/onboard.py index 3c0a0c7c0d..6083515305 100755 --- a/qa/rpc-tests/onboard.py +++ b/qa/rpc-tests/onboard.py @@ -32,6 +32,8 @@ 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") @@ -39,21 +41,70 @@ def __init__(self): 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) @@ -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: @@ -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) @@ -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 @@ -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 diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 937ba9b9da..3735e2532a 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -367,7 +367,7 @@ 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 """ @@ -375,7 +375,7 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None 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) diff --git a/src/policy/whitelist.cpp b/src/policy/whitelist.cpp index 8c0b73bd8e..b1c56e3ee7 100644 --- a/src/policy/whitelist.cpp +++ b/src/policy/whitelist.cpp @@ -2,6 +2,7 @@ // 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 @@ -9,6 +10,7 @@ #endif #include "ecies_hex.h" #include "policy/policy.h" +#include "rpc/server.h" CWhiteList::CWhiteList(){ _asset=whitelistAsset; @@ -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 bytes; @@ -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); @@ -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(); @@ -361,12 +368,20 @@ bool CWhiteList::RegisterAddress(const CTransaction& tx, const CCoinsViewCache& std::unique_ptr 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; @@ -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& data, const std::unique_ptr& kycPubKey){ //Interpret the data //First 20 bytes: keyID @@ -439,11 +456,11 @@ bool CWhiteList::RegisterDecryptedAddresses(const std::vector& da } try{ add_derived(addrNew, pubKeyNew, kycPubKey); + ++pairsAdded; } catch (std::invalid_argument e){ LogPrintf(std::string(e.what()) + "\n"); return bSuccess; } - ++pairsAdded; bSuccess = true; } } @@ -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 > vSolutions; txnouttype whichType; @@ -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){ @@ -738,12 +757,10 @@ 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; @@ -751,9 +768,16 @@ bool CWhiteList::get_unassigned_kyc(CPubKey& pubKey){ 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){ @@ -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); } diff --git a/src/policy/whitelist.h b/src/policy/whitelist.h index 371c813a84..f21ed26fce 100644 --- a/src/policy/whitelist.h +++ b/src/policy/whitelist.h @@ -17,6 +17,8 @@ class CWhiteList : public CPolicyList{ CWhiteList(); virtual ~CWhiteList(); + static const int64_t MAX_UNASSIGNED_KYCPUBKEYS=10000; + enum status { white, black @@ -35,7 +37,6 @@ class CWhiteList : public CPolicyList{ void add_derived(const std::string& sAddress, const std::string& sKey); //Multisig whitelisting below - void add_multisig_whitelist(const std::string& sAddress, const UniValue& sPubKeys, const std::string& sKYCAddress, const uint8_t nMultisig); @@ -48,15 +49,13 @@ class CWhiteList : public CPolicyList{ void add_multisig_whitelist(const CBitcoinAddress& address, const std::vector& pubKeys, const uint8_t nMultisig); - bool RegisterAddress(const CTransaction& tx, const CCoinsViewCache& mapInputs); - bool RegisterDecryptedAddresses(const std::vector& data, const std::unique_ptr& kycPubKey); bool IsRegisterAddressMulti(const std::vector::const_iterator start,const std::vector::const_iterator vend); -#ifdef ENABLE_WALLET bool RegisterAddress(const CTransaction& tx, const CBlockIndex* pindex); -#endif //#ifdef ENABLE_WALLET + + bool RegisterAddress(const CTransaction& tx, const CCoinsViewCache& mapInputs); //Update from transaction virtual bool Update(const CTransaction& tx, const CCoinsViewCache& mapInputs); @@ -72,10 +71,6 @@ class CWhiteList : public CPolicyList{ //Query the set of unassigned kyc pub keys for the presence of pubKey bool is_unassigned_kyc(const CKeyID& kycKeyID); - int64_t get_n_unassigned_kyc_pubkeys() const{ - return _kycUnassignedQueue.size(); - } - void add_unassigned_kyc(const CPubKey& pubKey); bool LookupKYCKey(const CTxDestination keyId, CKeyID& kycKeyIdFound); @@ -104,6 +99,14 @@ class CWhiteList : public CPolicyList{ unsigned int n_my_pending(); bool kycFromUserOnboard(const CPubKey& userOnboard, CPubKey& kyc); + + int64_t n_kyc_pubkeys() const{ + return _kycStatusMap.size(); + } + + int64_t n_unassigned_kyc_pubkeys() const{ + return _kycUnassignedSet.size(); + } private: //Make add_sorted private because we only want verified derived keys diff --git a/src/script/onboardingscript.cpp b/src/script/onboardingscript.cpp index c277b1966d..208c20d946 100644 --- a/src/script/onboardingscript.cpp +++ b/src/script/onboardingscript.cpp @@ -22,13 +22,6 @@ COnboardingScript::~COnboardingScript(){ } -//bool COnboardingScript::SetKeys(const CKey* privKey, const CPubKey* pubKey){ -// CRegisterAddressScript::SetKeys(privKey, pubKey); -// _kycPubKey=privKey->GetPubKey(); -// _userPubKey=*pubKey; -// return true; -//} - bool COnboardingScript::Finalize(CScript& script, const CPubKey& onboardPubKey, const CKey& kycPrivKey){ @@ -41,14 +34,7 @@ bool COnboardingScript::Finalize(CScript& script, //Onboarding keys ucvec vPubKeyKYC = ToByteVector(kycPrivKey.GetPubKey()); - _payload.insert(_payload.end(), - vPubKeyKYC.begin(), - vPubKeyKYC.end()); - ucvec vPubKeyUser = ToByteVector(onboardPubKey); - _payload.insert(_payload.end(), - vPubKeyUser.begin(), - vPubKeyUser.end()); //Append the keys ucvec sendData = vPubKeyKYC; @@ -68,24 +54,12 @@ bool COnboardingScript::FinalizeUnencrypted(CScript& script){ return false; //Onboarding keys ucvec vPubKeyKYC = ToByteVector(_kycPubKey); - _payload.insert(_payload.end(), - vPubKeyKYC.begin(), - vPubKeyKYC.end()); - ucvec vPubKeyUser = ToByteVector(_userPubKey); - _payload.insert(_payload.end(), - vPubKeyUser.begin(), - vPubKeyUser.end()); //Append the keys ucvec sendData = vPubKeyKYC; sendData.insert(sendData.end(), vPubKeyUser.begin(), vPubKeyUser.end()); - //Append dummy IV - ucvec dummy_iv; - dummy_iv.resize(AES_BLOCKSIZE); - sendData.insert(sendData.end(), dummy_iv.begin(), dummy_iv.end()); - //Append the addresses (unencrypted) sendData.insert(sendData.end(), _payload.begin(), _payload.end()); script.clear(); diff --git a/src/script/registeraddressscript.cpp b/src/script/registeraddressscript.cpp index 32ee8582d3..1d70718972 100644 --- a/src/script/registeraddressscript.cpp +++ b/src/script/registeraddressscript.cpp @@ -54,7 +54,8 @@ bool CRegisterAddressScript::Append(const CPubKey& pubKey){ if (!contract.IsNull()) tweakedPubKey.AddTweakToPubKey((unsigned char*)contract.begin()); CKeyID keyID=tweakedPubKey.GetID(); - assert(Consensus::CheckValidTweakedAddress(keyID, pubKey)); + if(!Consensus::CheckValidTweakedAddress(keyID, pubKey)) + return false; std::vector vKeyIDNew = ToByteVector(keyID); _payload.insert(_payload.end(), diff --git a/src/validation.cpp b/src/validation.cpp index 3a15040043..8ac8e35c93 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2754,12 +2754,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if(tx.vout[0].nAsset.GetAsset() == burnlistAsset) UpdateBurnList(tx,view); } if(fRequireWhitelistCheck || fScanWhitelist){ - if(!addressWhitelist.RegisterAddress(tx, view)){ - if(tx.vout[0].nAsset.GetAsset() == whitelistAsset) { + if(!addressWhitelist.RegisterAddress(tx, view)){ addressWhitelist.Update(tx,view); } } - } if(fRecordInflation) { UpdateAssetMap(tx); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a63ab14c1d..50090e7cd4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -618,7 +618,7 @@ UniValue onboarduser(const JSONRPCRequest& request){ return wtx.GetHash().GetHex(); } -static void FinalizeRegisterAddressTx(CRegisterAddressScript* raScript, const CAsset& feeAsset, const CPubKey& pubKey, CWalletTx& wtxNew) +static UniValue FinalizeRegisterAddressTx(CRegisterAddressScript* raScript, const CAsset& feeAsset, const CPubKey& pubKey, CWalletTx& wtxNew) { CScript dummyScript; raScript->FinalizeUnencrypted(dummyScript); @@ -723,6 +723,8 @@ static void FinalizeRegisterAddressTx(CRegisterAddressScript* raScript, const CA strError = strprintf("Error: The transaction was rejected! Reason given: %s %s", state.GetRejectReason(), state.GetDebugMessage()); throw JSONRPCError(RPC_WALLET_ERROR, strError); } + + return wtxNew.GetHash().GetHex(); } //Register an P2SH multi address to the @@ -766,14 +768,17 @@ static void SendAddNextMultiToWhitelistTx(const CAsset& feeAsset, const CPubKey& pubKeyVec.push_back(tpubKey); } - if(raScript->Append(nMultisig, keyid, pubKeyVec)){ - //if(keysToReg.size()>=nToRegister) break; - addressWhitelist.add_my_pending(keyid); - } - else + if(!raScript->Append(nMultisig, keyid, pubKeyVec)) throw JSONRPCError(RPC_INVALID_PARAMETER, "The process of p2sh whitelisting transaction serialization with present parameters has failed"); - FinalizeRegisterAddressTx(raScript, feeAsset, pubKey, wtxNew); + addressWhitelist.add_my_pending(keyid); + + try{ + FinalizeRegisterAddressTx(raScript, feeAsset, pubKey, wtxNew); + } catch(...){ + addressWhitelist.remove_my_pending(keyid); + throw std::current_exception(); + } } //Register an unwhitelisted address from the keypool to the @@ -804,35 +809,57 @@ static void SendAddNextToWhitelistTx(const CAsset& feeAsset, CRegisterAddressScript* raScript = new CRegisterAddressScript(RA_PUBLICKEY); - int nReg=0; + CBitcoinAddress addr; // get the next registered base58check encoded tweaked public key and add it to the whitelist std::set setKeyPool; //Get new addresses to register, topping up the key pool if necessary - while(nReg < nToRegister){ + + std::set keysToReg; + + nToRegister; + int nReg=0; + int nWl=0; + + while(keysToReg.size() < nToRegister){ + pwalletMain->TopUpKeyPool(setKeyPool.size()+nToRegister - nReg + nWl); pwalletMain->GetAllReserveKeys(setKeyPool); - for(std::set::const_iterator it = setKeyPool.begin(); it != setKeyPool.end(); ++it) { - const CKeyID &keyid = *it; - if (addressWhitelist.is_whitelisted(keyid) || addressWhitelist.is_my_pending(keyid)) - continue; - addr.Set(keyid); - CKey key; - if (pwalletMain->GetKey(keyid, key)) { // verify exists - //keysToReg.push_back(key.GetPubKey()); - if(raScript->Append(key.GetPubKey())){ - //if(keysToReg.size()>=nToRegister) break; - addressWhitelist.add_my_pending(keyid); - nReg++; - if(nReg>=nToRegister) break; + for(std::set::const_iterator it = setKeyPool.begin(); + it != setKeyPool.end(); ++it) { + const CKeyID &keyid = *it; + if (addressWhitelist.is_whitelisted(keyid) || addressWhitelist.is_my_pending(keyid)){ + nWl++; + continue; + } + addr.Set(keyid); + CKey key; + if (pwalletMain->GetKey(keyid, key)) { // verify exists + //keysToReg.push_back(key.GetPubKey()); + if(keysToReg.insert(keyid).second){ + if(!raScript->Append(key.GetPubKey())) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Failed to append address to registeraddress script"); + if(keysToReg.size() == nToRegister) + break; + } + } - } } - pwalletMain->TopUpKeyPool(setKeyPool.size()+nToRegister - nReg); } - FinalizeRegisterAddressTx(raScript, feeAsset, pubKey, wtxNew); + //Add to my pending here in case TX fails. + for(auto& key : keysToReg){ + addressWhitelist.add_my_pending(key); + } + try{ + FinalizeRegisterAddressTx(raScript, feeAsset, pubKey, wtxNew); + } catch(...){ + for(auto& key : keysToReg){ + addressWhitelist.remove_my_pending(key); + } + throw std::current_exception(); + } } @@ -1115,7 +1142,28 @@ UniValue whitelistkycpubkeys(const JSONRPCRequest& request){ varr = UniValue(UniValue::VARR); varr.push_back(result["hex"]); request3.params = varr; - return sendrawtransaction(request3); + + return (int)sendrawtransaction(request3).size(); +} + +UniValue getnunassignedkycpubkeys(const JSONRPCRequest& request){ + if (!EnsureWalletIsAvailable(request.fHelp)) + return NullUniValue; + + if (request.fHelp || request.params.size() != 0) { + throw runtime_error( + "getunassignedkycpubkey\n" + "Returns the number of unassigned KYC public keys; really only useful for debugging purposes\n" + + "\nExamples:\n" + + HelpExampleCli("getnunassignedkycpubkeys", "") + + HelpExampleRpc("getnunassignedkycpubkeys", "") + ); + } + if(!fScanWhitelist && !fRequireWhitelistCheck) + throw JSONRPCError(RPC_MISC_ERROR, "pkhwhitelist and pkhwhitelist-scan are nor enabled\n"); + + return addressWhitelist.n_unassigned_kyc_pubkeys(); } UniValue topupkycpubkeys(const JSONRPCRequest& request){ @@ -1138,21 +1186,25 @@ UniValue topupkycpubkeys(const JSONRPCRequest& request){ LOCK2(cs_main, pwalletMain->cs_wallet); + int64_t nKeysToAdd=request.params[0].get_int64()-addressWhitelist.n_unassigned_kyc_pubkeys(); - int64_t nKeysToAdd=request.params[0].get_int64()-addressWhitelist.get_n_unassigned_kyc_pubkeys(); - int nKeysToAddMax=1000; - if(nKeysToAdd>nKeysToAddMax) - nKeysToAdd = nKeysToAddMax; + int64_t unassignedDiff = CWhiteList::MAX_UNASSIGNED_KYCPUBKEYS-addressWhitelist.n_unassigned_kyc_pubkeys(); + if(nKeysToAdd > unassignedDiff){ + nKeysToAdd = unassignedDiff; + } UniValue kycpubkeys(UniValue::VARR); - + + EnsureWalletIsUnlocked(); UniValue ret(UniValue::VARR); UniValue varr(UniValue::VARR); - JSONRPCRequest request2; int iMax=nKeysToAdd-1; int nMaxPerTx=100; + int nAdded=0; + JSONRPCRequest request2; + for(int i=0; iGenerateNewKey(); std::vector datavec = ToByteVector(kycPubKey); @@ -1160,13 +1212,14 @@ UniValue topupkycpubkeys(const JSONRPCRequest& request){ if(kycpubkeys.size() == nMaxPerTx || (i==iMax && kycpubkeys.size()>0)){ varr.push_back(kycpubkeys); request2.params = varr; - ret.push_back(whitelistkycpubkeys(request2)); + whitelistkycpubkeys(request2).get_int(); + nAdded += kycpubkeys.size(); kycpubkeys=UniValue(UniValue::VARR); varr=UniValue(UniValue::VARR); } } - return ret; + return nAdded; } UniValue sendaddtowhitelisttx(const JSONRPCRequest& request){