From 07444da1dbd1dc1817ce614fd1c1d54929cc124c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 22 Aug 2017 18:02:33 -0700 Subject: [PATCH] Introduce wrappers around CBitcoinAddress This patch removes the need for the intermediary Base58 type CBitcoinAddress, by providing {Encode,Decode,IsValid}Destination function that directly operate on the conversion between strings and CTxDestination. --- src/base58.cpp | 22 +++++ src/base58.h | 5 ++ src/bitcoin-tx.cpp | 9 +- src/core_write.cpp | 5 +- src/rpcmisc.cpp | 46 +++++----- src/rpcrawtransaction.cpp | 26 +++--- src/script/standard.cpp | 4 + src/script/standard.h | 5 +- src/test/base58_tests.cpp | 32 +++---- src/test/key_tests.cpp | 27 +++--- src/wallet/rpcdump.cpp | 31 ++++--- src/wallet/rpcwallet.cpp | 177 ++++++++++++++++++++------------------ src/wallet/wallet.cpp | 16 ++-- src/wallet/walletdb.cpp | 6 +- 14 files changed, 225 insertions(+), 186 deletions(-) diff --git a/src/base58.cpp b/src/base58.cpp index f59eb6ba2..df5db8091 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -323,6 +323,28 @@ bool CBitcoinSecret::SetString(const std::string& strSecret) return SetString(strSecret.c_str()); } +std::string EncodeDestination(const CTxDestination& dest) +{ + CBitcoinAddress addr(dest); + if (!addr.IsValid()) return ""; + return addr.ToString(); +} + +CTxDestination DecodeDestination(const std::string& str) +{ + return CBitcoinAddress(str).Get(); +} + +bool IsValidDestinationString(const std::string& str, const CChainParams& params) +{ + return CBitcoinAddress(str).IsValid(params); +} + +bool IsValidDestinationString(const std::string& str) +{ + return CBitcoinAddress(str).IsValid(); +} + template bool CZCEncoding::Set(const DATA_TYPE& addr) { diff --git a/src/base58.h b/src/base58.h index 38262fb95..22c816d90 100644 --- a/src/base58.h +++ b/src/base58.h @@ -214,4 +214,9 @@ public: typedef CBitcoinExtKeyBase CBitcoinExtKey; typedef CBitcoinExtKeyBase CBitcoinExtPubKey; +std::string EncodeDestination(const CTxDestination& dest); +CTxDestination DecodeDestination(const std::string& str); +bool IsValidDestinationString(const std::string& str); +bool IsValidDestinationString(const std::string& str, const CChainParams& params); + #endif // BITCOIN_BASE58_H diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 82e72d0bd..1158563f2 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -240,12 +240,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn // extract and validate ADDRESS std::string strAddr = strInput.substr(pos + 1, std::string::npos); - CBitcoinAddress addr(strAddr); - if (!addr.IsValid()) + CTxDestination destination = DecodeDestination(strAddr); + if (!IsValidDestination(destination)) { throw std::runtime_error("invalid TX output address"); - - // build standard output script via GetScriptForDestination() - CScript scriptPubKey = GetScriptForDestination(addr.Get()); + } + CScript scriptPubKey = GetScriptForDestination(destination); // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); diff --git a/src/core_write.cpp b/src/core_write.cpp index 533fedfe7..df1f83456 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -143,8 +143,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, out.pushKV("type", GetTxnOutputType(type)); UniValue a(UniValue::VARR); - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); + for (const CTxDestination& addr : addresses) { + a.push_back(EncodeDestination(addr)); + } out.pushKV("addresses", a); } diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 96dbb756a..1f863ae70 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -137,8 +137,9 @@ public: obj.push_back(Pair("script", GetTxnOutputType(whichType))); obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); UniValue a(UniValue::VARR); - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); + for (const CTxDestination& addr : addresses) { + a.push_back(EncodeDestination(addr)); + } obj.push_back(Pair("addresses", a)); if (whichType == TX_MULTISIG) obj.push_back(Pair("sigsrequired", nRequired)); @@ -178,15 +179,14 @@ UniValue validateaddress(const UniValue& params, bool fHelp) LOCK(cs_main); #endif - CBitcoinAddress address(params[0].get_str()); - bool isValid = address.IsValid(); + CTxDestination dest = DecodeDestination(params[0].get_str()); + bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); ret.push_back(Pair("isvalid", isValid)); if (isValid) { - CTxDestination dest = address.Get(); - string currentAddress = address.ToString(); + std::string currentAddress = EncodeDestination(dest); ret.push_back(Pair("address", currentAddress)); CScript scriptPubKey = GetScriptForDestination(dest); @@ -293,17 +293,16 @@ CScript _createmultisig_redeemScript(const UniValue& params) const std::string& ks = keys[i].get_str(); #ifdef ENABLE_WALLET // Case 1: Bitcoin address and we have full public key: - CBitcoinAddress address(ks); - if (pwalletMain && address.IsValid()) - { - CKeyID keyID; - if (!address.GetKeyID(keyID)) - throw runtime_error( - strprintf("%s does not refer to a key",ks)); + CTxDestination dest = DecodeDestination(ks); + if (pwalletMain && IsValidDestination(dest)) { + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { + throw std::runtime_error(strprintf("%s does not refer to a key", ks)); + } CPubKey vchPubKey; - if (!pwalletMain->GetPubKey(keyID, vchPubKey)) - throw runtime_error( - strprintf("no full public key for address %s",ks)); + if (!pwalletMain->GetPubKey(*keyID, vchPubKey)) { + throw std::runtime_error(strprintf("no full public key for address %s", ks)); + } if (!vchPubKey.IsFullyValid()) throw runtime_error(" Invalid public key: "+ks); pubkeys[i] = vchPubKey; @@ -367,10 +366,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp) // Construct using pay-to-script-hash: CScript inner = _createmultisig_redeemScript(params); CScriptID innerID(inner); - CBitcoinAddress address(innerID); UniValue result(UniValue::VOBJ); - result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("address", EncodeDestination(innerID))); result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); return result; @@ -405,13 +403,15 @@ UniValue verifymessage(const UniValue& params, bool fHelp) string strSign = params[1].get_str(); string strMessage = params[2].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) + CTxDestination destination = DecodeDestination(strAddress); + if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) + const CKeyID *keyID = boost::get(&destination); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + } bool fInvalid = false; vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); @@ -427,7 +427,7 @@ UniValue verifymessage(const UniValue& params, bool fHelp) if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) return false; - return (pubkey.GetID() == keyID); + return (pubkey.GetID() == *keyID); } UniValue setmocktime(const UniValue& params, bool fHelp) diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 7e03df07a..3f6981619 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -50,8 +50,9 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud out.push_back(Pair("type", GetTxnOutputType(type))); UniValue a(UniValue::VARR); - BOOST_FOREACH(const CTxDestination& addr, addresses) - a.push_back(CBitcoinAddress(addr).ToString()); + for (const CTxDestination& addr : addresses) { + a.push_back(EncodeDestination(addr)); + } out.push_back(Pair("addresses", a)); } @@ -492,18 +493,19 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) rawTx.vin.push_back(in); } - set setAddress; + std::set destinations; vector addrList = sendTo.getKeys(); - BOOST_FOREACH(const string& name_, addrList) { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+name_); + for (const std::string& name_ : addrList) { + CTxDestination destination = DecodeDestination(name_); + if (!IsValidDestination(destination)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_); + } - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (!destinations.insert(destination).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); + } - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(destination); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); @@ -646,7 +648,7 @@ UniValue decodescript(const UniValue& params, bool fHelp) } ScriptPubKeyToJSON(script, r, false); - r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script)))); return r; } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index ce50e3aad..88cde3698 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -316,3 +316,7 @@ CScript GetScriptForMultisig(int nRequired, const std::vector& keys) script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; return script; } + +bool IsValidDestination(const CTxDestination& dest) { + return dest.which() != 0; +} diff --git a/src/script/standard.h b/src/script/standard.h index da778878b..fdb02f7c7 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -78,10 +78,13 @@ public: * * CNoDestination: no destination set * * CKeyID: TX_PUBKEYHASH destination * * CScriptID: TX_SCRIPTHASH destination - * A CTxDestination is the internal data type encoded in a CBitcoinAddress + * A CTxDestination is the internal data type encoded in a bitcoin address */ typedef boost::variant CTxDestination; +/** Check whether a CTxDestination is a CNoDestination. */ +bool IsValidDestination(const CTxDestination& dest); + const char* GetTxnOutputType(txnouttype t); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 361d8b8ea..0531aaadf 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); std::vector result; CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; SelectParams(CBaseChainParams::MAIN); for (size_t idx = 0; idx < tests.size(); idx++) { @@ -147,7 +147,6 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key - // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not! BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); CKey privkey = secret.GetKey(); @@ -155,18 +154,17 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); // Private key must be invalid public key - addr.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); } else { std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" // Must be valid public key - BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest); - BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest); - BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest); - CTxDestination dest = addr.Get(); - BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); + BOOST_CHECK_MESSAGE((boost::get(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest); + BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest); // Public key must be invalid private key secret.SetString(exp_base58string); @@ -229,17 +227,11 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) BOOST_ERROR("Bad addrtype: " << strTest); continue; } - CBitcoinAddress addrOut; - BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest); - BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); + std::string address = EncodeDestination(dest); + BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest); } } - // Visiting a CNoDestination must fail - CBitcoinAddress dummyAddr; - CTxDestination nodest = CNoDestination(); - BOOST_CHECK(!dummyAddr.Set(nodest)); - SelectParams(CBaseChainParams::MAIN); } @@ -249,7 +241,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases std::vector result; CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; for (size_t idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; @@ -262,8 +254,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - addr.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest); secret.SetString(exp_base58string); BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 37e1eb244..fa01d8383 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -21,17 +21,16 @@ using namespace std; using namespace libzcash; -static const string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"); -static const string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"); -static const string strSecret1C ("Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"); -static const string strSecret2C ("L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"); -static const CBitcoinAddress addr1 ("t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe"); -static const CBitcoinAddress addr2 ("t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst"); -static const CBitcoinAddress addr1C("t1ffus9J1vhxvFqLoExGBRPjE7BcJxiSCTC"); -static const CBitcoinAddress addr2C("t1VJL2dPUyXK7avDRGqhqQA5bw2eEMdhyg6"); +static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; +static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; +static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; +static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; +static const std::string addr1 = "t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe"; +static const std::string addr2 = "t1Xxa5ZVPKvs9bGMn7aWTiHjyHvR31XkUst"; +static const std::string addr1C = "t1ffus9J1vhxvFqLoExGBRPjE7BcJxiSCTC"; +static const std::string addr2C = "t1VJL2dPUyXK7avDRGqhqQA5bw2eEMdhyg6"; - -static const string strAddressBad("t1aMkLwU1LcMZYN7TgXUJAwzA1r44dbLkSp"); +static const std::string strAddressBad = "t1aMkLwU1LcMZYN7TgXUJAwzA1r44dbLkSp"; #ifdef KEY_TESTS_DUMPINFO @@ -107,10 +106,10 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); - BOOST_CHECK(addr1.Get() == CTxDestination(pubkey1.GetID())); - BOOST_CHECK(addr2.Get() == CTxDestination(pubkey2.GetID())); - BOOST_CHECK(addr1C.Get() == CTxDestination(pubkey1C.GetID())); - BOOST_CHECK(addr2C.Get() == CTxDestination(pubkey2C.GetID())); + BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID())); + BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID())); + BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID())); + BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID())); for (int n=0; n<16; n++) { diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 0e232373d..b064ed1a9 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -175,9 +175,9 @@ UniValue importaddress(const UniValue& params, bool fHelp) CScript script; - CBitcoinAddress address(params[0].get_str()); - if (address.IsValid()) { - script = GetScriptForDestination(address.Get()); + CTxDestination dest = DecodeDestination(params[0].get_str()); + if (IsValidDestination(dest)) { + script = GetScriptForDestination(dest); } else if (IsHex(params[0].get_str())) { std::vector data(ParseHex(params[0].get_str())); script = CScript(data.begin(), data.end()); @@ -199,8 +199,8 @@ UniValue importaddress(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); // add to address book or update label - if (address.IsValid()) - pwalletMain->SetAddressBook(address.Get(), strLabel, "receive"); + if (IsValidDestination(dest)) + pwalletMain->SetAddressBook(dest, strLabel, "receive"); // Don't throw error in case an address is already there if (pwalletMain->HaveWatchOnly(script)) @@ -333,7 +333,7 @@ UniValue importwallet_impl(const UniValue& params, bool fHelp, bool fImportZKeys assert(key.VerifyPubKey(pubkey)); CKeyID keyid = pubkey.GetID(); if (pwalletMain->HaveKey(keyid)) { - LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString()); + LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); continue; } int64_t nTime = DecodeDumpTime(vstr[1]); @@ -351,7 +351,7 @@ UniValue importwallet_impl(const UniValue& params, bool fHelp, bool fImportZKeys fLabel = true; } } - LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); + LogPrintf("Importing %s...\n", EncodeDestination(keyid)); if (!pwalletMain->AddKeyPubKey(key, pubkey)) { fGood = false; continue; @@ -405,16 +405,19 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - string strAddress = params[0].get_str(); - CBitcoinAddress address; - if (!address.SetString(strAddress)) + std::string strAddress = params[0].get_str(); + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); - CKeyID keyID; - if (!address.GetKeyID(keyID)) + } + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + } CKey vchSecret; - if (!pwalletMain->GetKey(keyID, vchSecret)) + if (!pwalletMain->GetKey(*keyID, vchSecret)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + } return CBitcoinSecret(vchSecret).ToString(); } @@ -515,7 +518,7 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys) for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; std::string strTime = EncodeDumpTime(it->first); - std::string strAddr = CBitcoinAddress(keyid).ToString(); + std::string strAddr = EncodeDestination(keyid); CKey key; if (pwalletMain->GetKey(keyid, key)) { if (pwalletMain->mapAddressBook.count(keyid)) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ead413296..364f0ecdd 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -146,11 +146,11 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) pwalletMain->SetAddressBook(keyID, strAccount, "receive"); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } -CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) +CTxDestination GetAccountAddress(std::string strAccount, bool bForceNew=false) { CWalletDB walletdb(pwalletMain->strWalletFile); @@ -184,7 +184,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) walletdb.WriteAccount(strAccount, account); } - return CBitcoinAddress(account.vchPubKey.GetID()); + return account.vchPubKey.GetID(); } UniValue getaccountaddress(const UniValue& params, bool fHelp) @@ -214,7 +214,7 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp) UniValue ret(UniValue::VSTR); - ret = GetAccountAddress(strAccount).ToString(); + ret = EncodeDestination(GetAccountAddress(strAccount)); return ret; } @@ -250,7 +250,7 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp) CKeyID keyID = vchPubKey.GetID(); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } @@ -273,25 +273,25 @@ UniValue setaccount(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + } string strAccount; if (params.size() > 1) strAccount = AccountFromValue(params[1]); // Only add the account if the address is yours. - if (IsMine(*pwalletMain, address.Get())) - { + if (IsMine(*pwalletMain, dest)) { // Detect when changing the account of an address that is the 'unused current key' of another account: - if (pwalletMain->mapAddressBook.count(address.Get())) - { - string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name; - if (address == GetAccountAddress(strOldAccount)) + if (pwalletMain->mapAddressBook.count(dest)) { + std::string strOldAccount = pwalletMain->mapAddressBook[dest].name; + if (dest == GetAccountAddress(strOldAccount)) { GetAccountAddress(strOldAccount, true); + } } - pwalletMain->SetAddressBook(address.Get(), strAccount, "receive"); + pwalletMain->SetAddressBook(dest, strAccount, "receive"); } else throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); @@ -320,14 +320,16 @@ UniValue getaccount(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + } - string strAccount; - map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) + std::string strAccount; + std::map::iterator mi = pwalletMain->mapAddressBook.find(dest); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; + } return strAccount; } @@ -359,12 +361,12 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) // Find all addresses that have the given account UniValue ret(UniValue::VARR); - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) - { - const CBitcoinAddress& address = item.first; - const string& strName = item.second.name; - if (strName == strAccount) - ret.push_back(address.ToString()); + for (const std::pair& item : pwalletMain->mapAddressBook) { + const CTxDestination& dest = item.first; + const std::string& strName = item.second.name; + if (strName == strAccount) { + ret.push_back(EncodeDestination(dest)); + } } return ret; } @@ -431,9 +433,10 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + } // Amount CAmount nAmount = AmountFromValue(params[1]); @@ -453,7 +456,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + SendMoney(dest, nAmount, fSubtractFeeFromAmount, wtx); return wtx.GetHash().GetHex(); } @@ -489,18 +492,18 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); UniValue jsonGroupings(UniValue::VARR); - map balances = pwalletMain->GetAddressBalances(); - BOOST_FOREACH(set grouping, pwalletMain->GetAddressGroupings()) - { + std::map balances = pwalletMain->GetAddressBalances(); + for (const std::set& grouping : pwalletMain->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); - BOOST_FOREACH(CTxDestination address, grouping) + for (const CTxDestination& address : grouping) { UniValue addressInfo(UniValue::VARR); - addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(ValueFromAmount(balances[address])); { - if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) - addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); + if (pwalletMain->mapAddressBook.find(address) != pwalletMain->mapAddressBook.end()) { + addressInfo.push_back(pwalletMain->mapAddressBook.find(address)->second.name); + } } jsonGrouping.push_back(addressInfo); } @@ -542,17 +545,20 @@ UniValue signmessage(const UniValue& params, bool fHelp) string strAddress = params[0].get_str(); string strMessage = params[1].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + } CKey key; - if (!pwalletMain->GetKey(keyID, key)) + if (!pwalletMain->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; @@ -593,12 +599,14 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); - CScript scriptPubKey = GetScriptForDestination(address.Get()); - if (!IsMine(*pwalletMain, scriptPubKey)) + } + CScript scriptPubKey = GetScriptForDestination(dest); + if (!IsMine(*pwalletMain, scriptPubKey)) { return ValueFromAmount(0); + } // Minimum confirmations int nMinDepth = 1; @@ -907,10 +915,11 @@ UniValue sendfrom(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - string strAccount = AccountFromValue(params[0]); - CBitcoinAddress address(params[1].get_str()); - if (!address.IsValid()) + std::string strAccount = AccountFromValue(params[0]); + CTxDestination dest = DecodeDestination(params[1].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); + } CAmount nAmount = AmountFromValue(params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -932,7 +941,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) if (nAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - SendMoney(address.Get(), nAmount, false, wtx); + SendMoney(dest, nAmount, false, wtx); return wtx.GetHash().GetHex(); } @@ -996,22 +1005,23 @@ UniValue sendmany(const UniValue& params, bool fHelp) if (params.size() > 4) subtractFeeFromAmount = params[4].get_array(); - set setAddress; - vector vecSend; + std::set destinations; + std::vector vecSend; CAmount totalAmount = 0; - vector keys = sendTo.getKeys(); - BOOST_FOREACH(const string& name_, keys) - { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+name_); + std::vector keys = sendTo.getKeys(); + for (const std::string& name_ : keys) { + CTxDestination dest = DecodeDestination(name_); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_); + } - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (destinations.count(dest)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); + } + destinations.insert(dest); - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(dest); CAmount nAmount = AmountFromValue(sendTo[name_]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -1097,7 +1107,7 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) pwalletMain->AddCScript(inner); pwalletMain->SetAddressBook(innerID, strAccount, "send"); - return CBitcoinAddress(innerID).ToString(); + return EncodeDestination(innerID); } @@ -1133,10 +1143,9 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) filter = filter | ISMINE_WATCH_ONLY; // Tally - map mapTally; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; + std::map mapTally; + for (const std::pair& pairWtx : pwalletMain->mapWallet) { + const CWalletTx& wtx = pairWtx.second; if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; @@ -1166,12 +1175,11 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) // Reply UniValue ret(UniValue::VARR); - map mapAccountTally; - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) - { - const CBitcoinAddress& address = item.first; - const string& strAccount = item.second.name; - map::iterator it = mapTally.find(address); + std::map mapAccountTally; + for (const std::pair& item : pwalletMain->mapAddressBook) { + const CTxDestination& dest = item.first; + const std::string& strAccount = item.second.name; + std::map::iterator it = mapTally.find(dest); if (it == mapTally.end() && !fIncludeEmpty) continue; @@ -1197,7 +1205,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) UniValue obj(UniValue::VOBJ); if(fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("address", EncodeDestination(dest))); obj.push_back(Pair("account", strAccount)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); @@ -1308,9 +1316,9 @@ UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { - CBitcoinAddress addr; - if (addr.Set(dest)) - entry.push_back(Pair("address", addr.ToString())); + if (IsValidDestination(dest)) { + entry.push_back(Pair("address", EncodeDestination(dest))); + } } void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) @@ -2367,17 +2375,18 @@ UniValue listunspent(const UniValue& params, bool fHelp) if (params.size() > 1) nMaxDepth = params[1].get_int(); - set setAddress; + std::set destinations; if (params.size() > 2) { UniValue inputs = params[2].get_array(); for (size_t idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; - CBitcoinAddress address(input.get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+input.get_str()); - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); - setAddress.insert(address); + CTxDestination dest = DecodeDestination(input.get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + input.get_str()); + } + if (!destinations.insert(dest).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str()); + } } } @@ -2394,7 +2403,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey; bool fValidAddress = ExtractDestination(scriptPubKey, address); - if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + if (destinations.size() && (!fValidAddress || !destinations.count(address))) continue; UniValue entry(UniValue::VOBJ); @@ -2403,7 +2412,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) entry.push_back(Pair("generated", out.tx->IsCoinBase())); if (fValidAddress) { - entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + entry.push_back(Pair("address", EncodeDestination(address))); if (pwalletMain->mapAddressBook.count(address)) entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d6d0a274a..78968f69e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -300,7 +300,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript) * these. Do not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { - std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString(); + std::string strAddr = EncodeDestination(CScriptID(redeemScript)); LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); return true; @@ -3018,9 +3018,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); if (!fFileBacked) return false; - if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) + if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(EncodeDestination(address), strPurpose)) return false; - return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); + return CWalletDB(strWalletFile).WriteName(EncodeDestination(address), strName); } bool CWallet::DelAddressBook(const CTxDestination& address) @@ -3031,7 +3031,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address) if(fFileBacked) { // Delete destdata tuples associated with address - std::string strAddress = CBitcoinAddress(address).ToString(); + std::string strAddress = EncodeDestination(address); BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) { CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); @@ -3044,8 +3044,8 @@ bool CWallet::DelAddressBook(const CTxDestination& address) if (!fFileBacked) return false; - CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString()); - return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); + CWalletDB(strWalletFile).ErasePurpose(EncodeDestination(address)); + return CWalletDB(strWalletFile).EraseName(EncodeDestination(address)); } bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) @@ -3573,7 +3573,7 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); if (!fFileBacked) return true; - return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value); + return CWalletDB(strWalletFile).WriteDestData(EncodeDestination(dest), key, value); } bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) @@ -3582,7 +3582,7 @@ bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) return false; if (!fFileBacked) return true; - return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key); + return CWalletDB(strWalletFile).EraseDestData(EncodeDestination(dest), key); } bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index dc1b40d95..8b4a7be2c 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -409,13 +409,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { string strAddress; ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; + ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name; } else if (strType == "purpose") { string strAddress; ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; + ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose; } else if (strType == "tx") { @@ -698,7 +698,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> strAddress; ssKey >> strKey; ssValue >> strValue; - if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) + if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue)) { strErr = "Error reading wallet database: LoadDestData failed"; return false;