diff --git a/src/base58.h b/src/base58.h --- a/src/base58.h +++ b/src/base58.h @@ -106,32 +106,6 @@ bool operator>(const CBase58Data &b58) const { return CompareTo(b58) > 0; } }; -/** base58-encoded Bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the - * serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the - * serialized redemption script. - */ -class CBitcoinAddress : public CBase58Data { -public: - bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); - bool IsValid() const; - bool IsValid(const CChainParams ¶ms) const; - - CBitcoinAddress() {} - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } - CBitcoinAddress(const std::string &strAddress) { SetString(strAddress); } - CBitcoinAddress(const char *pszAddress) { SetString(pszAddress); } - - CTxDestination Get() const; - bool GetKeyID(CKeyID &keyID) const; - bool IsScript() const; -}; - /** * A base58-encoded secret key */ @@ -182,4 +156,10 @@ CChainParams::EXT_PUBLIC_KEY> 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 ¶ms); + #endif // BITCOIN_BASE58_H diff --git a/src/base58.cpp b/src/base58.cpp --- a/src/base58.cpp +++ b/src/base58.cpp @@ -208,6 +208,33 @@ } namespace { +/** + * base58-encoded Bitcoin addresses. + * Public-key-hash-addresses have version 0 (or 111 testnet). + * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the + * serialized public key. + * Script-hash-addresses have version 5 (or 196 testnet). + * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the + * serialized redemption script. + */ +class CBitcoinAddress : public CBase58Data { +public: + bool Set(const CKeyID &id); + bool Set(const CScriptID &id); + bool Set(const CTxDestination &dest); + bool IsValid() const; + bool IsValid(const CChainParams ¶ms) const; + + CBitcoinAddress() {} + CBitcoinAddress(const CTxDestination &dest) { Set(dest); } + CBitcoinAddress(const std::string &strAddress) { SetString(strAddress); } + CBitcoinAddress(const char *pszAddress) { SetString(pszAddress); } + + CTxDestination Get() const; + bool GetKeyID(CKeyID &keyID) const; + bool IsScript() const; +}; + class CBitcoinAddressVisitor : public boost::static_visitor { private: CBitcoinAddress *addr; @@ -308,3 +335,21 @@ bool CBitcoinSecret::SetString(const std::string &strSecret) { return SetString(strSecret.c_str()); } + +std::string EncodeDestination(const CTxDestination &dest) { + CBitcoinAddress addr(dest); + return addr.IsValid() ? addr.ToString() : ""; +} + +CTxDestination DecodeDestination(const std::string &str) { + return CBitcoinAddress(str).Get(); +} + +bool IsValidDestinationString(const std::string &str, + const CChainParams ¶ms) { + return CBitcoinAddress(str).IsValid(params); +} + +bool IsValidDestinationString(const std::string &str) { + return CBitcoinAddress(str).IsValid(); +} diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -277,13 +277,11 @@ // extract and validate ADDRESS std::string strAddr = vStrInputParts[1]; - 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); @@ -310,7 +308,6 @@ } CScript scriptPubKey = GetScriptForRawPubKey(pubkey); - CBitcoinAddress addr(scriptPubKey); // Extract and validate FLAGS bool bScriptHash = false; @@ -320,10 +317,9 @@ } if (bScriptHash) { - // Get the address for the redeem script, then call - // GetScriptForDestination() to construct a P2SH scriptPubKey. - CBitcoinAddress redeemScriptAddr(scriptPubKey); - scriptPubKey = GetScriptForDestination(redeemScriptAddr.Get()); + // Get the ID for the script, and then construct a P2SH destination for + // it. + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -387,10 +383,9 @@ CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bScriptHash) { - // Get the address for the redeem script, then call - // GetScriptForDestination() to construct a P2SH scriptPubKey. - CBitcoinAddress addr(scriptPubKey); - scriptPubKey = GetScriptForDestination(addr.Get()); + // Get the ID for the script, and then construct a P2SH destination for + // it. + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -450,8 +445,7 @@ } if (bScriptHash) { - CBitcoinAddress addr(scriptPubKey); - scriptPubKey = GetScriptForDestination(addr.Get()); + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list diff --git a/src/core_write.cpp b/src/core_write.cpp --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -176,9 +176,8 @@ UniValue a(UniValue::VARR); for (const CTxDestination &addr : addresses) { - a.push_back(CBitcoinAddress(addr).ToString()); + a.push_back(EncodeDestination(addr)); } - out.pushKV("addresses", a); } diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -81,14 +81,14 @@ LOCK(wallet->cs_wallet); for (const std::pair &item : wallet->mapAddressBook) { - const CBitcoinAddress &address = item.first; - bool fMine = IsMine(*wallet, address.Get()); + const CTxDestination &address = item.first; + bool fMine = IsMine(*wallet, address); AddressTableEntry::Type addressType = translateTransactionType( QString::fromStdString(item.second.purpose), fMine); const std::string &strName = item.second.name; cachedAddressTable.append(AddressTableEntry( addressType, QString::fromStdString(strName), - QString::fromStdString(address.ToString()))); + QString::fromStdString(EncodeDestination(address)))); } } // qLowerBound() and qUpperBound() require our cachedAddressTable list @@ -233,7 +233,7 @@ /* For SetAddressBook / DelAddressBook */ LOCK(wallet->cs_wallet); CTxDestination curAddress = - CBitcoinAddress(rec->address.toStdString()).Get(); + DecodeDestination(rec->address.toStdString()); if (index.column() == Label) { // Do nothing, if old label == new label if (rec->label == value.toString()) { @@ -244,7 +244,7 @@ strPurpose); } else if (index.column() == Address) { CTxDestination newAddress = - CBitcoinAddress(value.toString().toStdString()).Get(); + DecodeDestination(value.toString().toStdString()); // Refuse to set invalid address, set error status and return false if (boost::get(&newAddress)) { editStatus = INVALID_ADDRESS; @@ -335,8 +335,7 @@ // Check for duplicate addresses { LOCK(wallet->cs_wallet); - if (wallet->mapAddressBook.count( - CBitcoinAddress(strAddress).Get())) { + if (wallet->mapAddressBook.count(DecodeDestination(strAddress))) { editStatus = DUPLICATE_ADDRESS; return QString(); } @@ -356,7 +355,7 @@ return QString(); } } - strAddress = CBitcoinAddress(newKey.GetID()).ToString(); + strAddress = EncodeDestination(newKey.GetID()); } else { return QString(); } @@ -364,7 +363,7 @@ // Add entry { LOCK(wallet->cs_wallet); - wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel, + wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, (type == Send ? "send" : "receive")); } return QString::fromStdString(strAddress); @@ -382,8 +381,7 @@ } { LOCK(wallet->cs_wallet); - wallet->DelAddressBook( - CBitcoinAddress(rec->address.toStdString()).Get()); + wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); } return true; } @@ -393,9 +391,9 @@ QString AddressTableModel::labelForAddress(const QString &address) const { { LOCK(wallet->cs_wallet); - CBitcoinAddress address_parsed(address.toStdString()); + CTxDestination destination = DecodeDestination(address.toStdString()); std::map::iterator mi = - wallet->mapAddressBook.find(address_parsed.Get()); + wallet->mapAddressBook.find(destination); if (mi != wallet->mapAddressBook.end()) { return QString::fromStdString(mi->second.name); } diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -76,8 +76,9 @@ int &pos) const { Q_UNUSED(pos); // Validate the passed Bitcoin address - CBitcoinAddress addr(input.toStdString()); - if (addr.IsValid()) return QValidator::Acceptable; + if (IsValidDestinationString(input.toStdString())) { + return QValidator::Acceptable; + } return QValidator::Invalid; } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -741,8 +741,8 @@ QString sAddress = ""; if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) { - sAddress = QString::fromStdString( - CBitcoinAddress(outputAddress).ToString()); + sAddress = + QString::fromStdString(EncodeDestination(outputAddress)); // if listMode or change => show bitcoin address. In tree mode, // address is not shown again for direct wallet address outputs diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -122,7 +122,9 @@ for (int i = 0; i < 256; ++i) { // Try every trailing byte std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()); - if (!CBitcoinAddress(s).IsValid()) return s; + if (!IsValidDestinationString(s)) { + return s; + } sourcedata[sourcedata.size() - 1] += 1; } return ""; @@ -246,7 +248,7 @@ } bool isDust(const QString &address, const CAmount &amount) { - CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); + CTxDestination dest = DecodeDestination(address.toStdString()); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); return txOut.IsDust(dustRelayFee); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -223,11 +223,12 @@ SendCoinsRecipient r; if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) { - CBitcoinAddress address(r.address.toStdString()); - - if (address.IsValid(Params(CBaseChainParams::MAIN))) { + if (IsValidDestinationString(r.address.toStdString(), + Params(CBaseChainParams::MAIN))) { SelectParams(CBaseChainParams::MAIN); - } else if (address.IsValid(Params(CBaseChainParams::TESTNET))) { + } else if (IsValidDestinationString( + r.address.toStdString(), + Params(CBaseChainParams::TESTNET))) { SelectParams(CBaseChainParams::TESTNET); } } @@ -420,8 +421,8 @@ // normal URI SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) { - CBitcoinAddress address(recipient.address.toStdString()); - if (!address.IsValid()) { + if (!IsValidDestinationString( + recipient.address.toStdString())) { Q_EMIT message( tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), @@ -545,8 +546,7 @@ CTxDestination dest; if (ExtractDestination(sendingTo.first, dest)) { // Append destination address - addresses.append( - QString::fromStdString(CBitcoinAddress(dest).ToString())); + addresses.append(QString::fromStdString(EncodeDestination(dest))); } else if (!recipient.authenticatedMerchant.isEmpty()) { // Unauthenticated payment requests to custom bitcoin addresses are // not supported (there is no good way to tell the user where they diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -828,18 +828,17 @@ CoinControlDialog::coinControl->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); - CBitcoinAddress addr = CBitcoinAddress(text.toStdString()); + const CTxDestination dest = DecodeDestination(text.toStdString()); if (text.isEmpty()) { // Nothing entered ui->labelCoinControlChangeLabel->setText(""); - } else if (!addr.IsValid()) { + } else if (!IsValidDestination(dest)) { // Invalid address ui->labelCoinControlChangeLabel->setText( tr("Warning: Invalid Bitcoin address")); } else { // Valid address - const CTxDestination dest = addr.Get(); if (!model->IsSpendable(dest)) { ui->labelCoinControlChangeLabel->setText( tr("Warning: Unknown change address")); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -112,16 +112,17 @@ * old signature displayed */ ui->signatureOut_SM->clear(); - CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); - if (!addr.IsValid()) { + CTxDestination destination = + DecodeDestination(ui->addressIn_SM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText( tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) { + const CKeyID *keyID = boost::get(&destination); + if (!keyID) { ui->addressIn_SM->setValid(false); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText( @@ -138,7 +139,7 @@ } CKey key; - if (!model->getPrivKey(keyID, key)) { + if (!model->getPrivKey(*keyID, key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText( tr("Private key for the entered address is not available.")); @@ -191,16 +192,16 @@ } void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() { - CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); - if (!addr.IsValid()) { + CTxDestination destination = + DecodeDestination(ui->addressIn_VM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText( tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) { + if (!boost::get(&destination)) { ui->addressIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText( @@ -236,7 +237,7 @@ return; } - if (!(CBitcoinAddress(pubkey.GetID()) == addr)) { + if (!(CTxDestination(pubkey.GetID()) == destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(QString("") + tr("Message verification failed.") + diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -90,8 +90,8 @@ // Offline transaction if (nNet > 0) { // Credit - if (CBitcoinAddress(rec->address).IsValid()) { - CTxDestination address = CBitcoinAddress(rec->address).Get(); + if (IsValidDestinationString(rec->address)) { + CTxDestination address = DecodeDestination(rec->address); if (wallet->mapAddressBook.count(address)) { strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; @@ -122,7 +122,7 @@ // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "" + tr("To") + ": "; - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) strHTML += @@ -195,8 +195,8 @@ GUIUtil::HtmlEscape( wallet->mapAddressBook[address].name) + " "; - strHTML += GUIUtil::HtmlEscape( - CBitcoinAddress(address).ToString()); + strHTML += + GUIUtil::HtmlEscape(EncodeDestination(address)); if (toSelf == ISMINE_SPENDABLE) strHTML += " (own address)"; else if (toSelf & ISMINE_WATCH_ONLY) @@ -351,8 +351,8 @@ wallet->mapAddressBook[address].name) + " "; } - strHTML += QString::fromStdString( - CBitcoinAddress(address).ToString()); + strHTML += + QString::fromStdString(EncodeDestination(address)); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -57,7 +57,7 @@ IsMine(*wallet, address)) { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { // Received by IP connection (deprecated features), or a // multisignature or other non-simple transaction @@ -119,7 +119,7 @@ if (ExtractDestination(txout.scriptPubKey, address)) { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { // Sent to IP, or other non-address transaction like OP_EVAL sub.type = TransactionRecord::SendToOther; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -170,8 +170,7 @@ } bool WalletModel::validateAddress(const QString &address) { - CBitcoinAddress addressParsed(address.toStdString()); - return addressParsed.IsValid(); + return IsValidDestinationString(address.toStdString()); } WalletModel::SendCoinsReturn @@ -226,7 +225,7 @@ ++nAddresses; CScript scriptPubKey = GetScriptForDestination( - CBitcoinAddress(rcp.address.toStdString()).Get()); + DecodeDestination(rcp.address.toStdString())); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); @@ -330,7 +329,7 @@ // Don't touch the address book when we have a payment request if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); @@ -433,8 +432,7 @@ const std::string &label, bool isMine, const std::string &purpose, ChangeType status) { - QString strAddress = - QString::fromStdString(CBitcoinAddress(address).ToString()); + QString strAddress = QString::fromStdString(EncodeDestination(address)); QString strLabel = QString::fromStdString(label); QString strPurpose = QString::fromStdString(purpose); @@ -600,8 +598,8 @@ !ExtractDestination(cout.tx->tx->vout[cout.i].scriptPubKey, address)) continue; - mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())] - .push_back(out); + mapCoins[QString::fromStdString(EncodeDestination(address))].push_back( + out); } } @@ -641,7 +639,7 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) { - CTxDestination dest = CBitcoinAddress(sAddress).Get(); + CTxDestination dest = DecodeDestination(sAddress); std::stringstream ss; ss << nId; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -243,14 +243,14 @@ nMaxTries = request.params[2].get_int(); } - CBitcoinAddress address(request.params[1].get_str()); - if (!address.IsValid()) { + CTxDestination destination = DecodeDestination(request.params[1].get_str()); + if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); } std::shared_ptr coinbaseScript(new CReserveScript()); - coinbaseScript->reserveScript = GetScriptForDestination(address.Get()); + coinbaseScript->reserveScript = GetScriptForDestination(destination); return generateBlocks(config, coinbaseScript, nGenerate, nMaxTries, false); } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -157,7 +157,7 @@ Pair("hex", HexStr(subscript.begin(), subscript.end()))); UniValue a(UniValue::VARR); for (const CTxDestination &addr : addresses) { - a.push_back(CBitcoinAddress(addr).ToString()); + a.push_back(EncodeDestination(addr)); } obj.push_back(Pair("addresses", a)); if (whichType == TX_MULTISIG) @@ -217,14 +217,13 @@ LOCK(cs_main); #endif - CBitcoinAddress address(request.params[0].get_str()); - bool isValid = address.IsValid(); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); ret.push_back(Pair("isvalid", isValid)); if (isValid) { - CTxDestination dest = address.Get(); - std::string currentAddress = address.ToString(); + std::string currentAddress = EncodeDestination(dest); ret.push_back(Pair("address", currentAddress)); CScript scriptPubKey = GetScriptForDestination(dest); @@ -238,13 +237,14 @@ Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true : false)); UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest); ret.pushKVs(detail); - if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) + if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) { ret.push_back( Pair("account", pwalletMain->mapAddressBook[dest].name)); - CKeyID keyID; + } if (pwalletMain) { const auto &meta = pwalletMain->mapKeyMetadata; - auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end(); + const CKeyID *keyID = boost::get(&dest); + auto it = keyID ? meta.find(*keyID) : meta.end(); if (it == meta.end()) { it = meta.find(CScriptID(scriptPubKey)); } @@ -289,16 +289,18 @@ 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)) + 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)) + if (!pwalletMain->GetPubKey(*keyID, vchPubKey)) { throw std::runtime_error( strprintf("no full public key for address %s", ks)); + } if (!vchPubKey.IsFullyValid()) throw std::runtime_error(" Invalid public key: " + ks); pubkeys[i] = vchPubKey; @@ -372,10 +374,9 @@ // Construct using pay-to-script-hash: CScript inner = createmultisig_redeemScript(request.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; @@ -418,12 +419,15 @@ std::string strSign = request.params[1].get_str(); std::string strMessage = request.params[2].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + 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; std::vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); @@ -439,7 +443,7 @@ CPubKey pubkey; if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) return false; - return (pubkey.GetID() == keyID); + return (pubkey.GetID() == *keyID); } static UniValue signmessagewithprivkey(const Config &config, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -54,7 +54,7 @@ UniValue a(UniValue::VARR); for (const CTxDestination &addr : addresses) { - a.push_back(CBitcoinAddress(addr).ToString()); + a.push_back(EncodeDestination(addr)); } out.push_back(Pair("addresses", a)); @@ -531,7 +531,7 @@ rawTx.vin.push_back(in); } - std::set setAddress; + std::set destinations; std::vector addrList = sendTo.getKeys(); for (const std::string &name_ : addrList) { if (name_ == "data") { @@ -541,23 +541,21 @@ CTxOut out(0, CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { - CBitcoinAddress address(name_); - if (!address.IsValid()) { + CTxDestination destination = DecodeDestination(name_); + if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); } - if (setAddress.count(address)) { + if (!destinations.insert(destination).second) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); } - setAddress.insert(address); - - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(destination); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); @@ -690,8 +688,7 @@ if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. - 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.h b/src/script/standard.h --- a/src/script/standard.h +++ b/src/script/standard.h @@ -68,11 +68,12 @@ * * 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; const char *GetTxnOutputType(txnouttype t); +bool IsValidDestination(const CTxDestination &dest); bool Solver(const CScript &scriptPubKey, txnouttype &typeRet, std::vector> &vSolutionsRet); diff --git a/src/script/standard.cpp b/src/script/standard.cpp --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -255,3 +255,7 @@ script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; return script; } + +bool IsValidDestination(const CTxDestination &dest) { + return dest.which() != 0; +} diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -128,7 +128,7 @@ json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; SelectParams(CBaseChainParams::MAIN); for (unsigned int idx = 0; idx < tests.size(); idx++) { @@ -151,8 +151,6 @@ if (isPrivkey) { 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); @@ -165,21 +163,23 @@ "key mismatch:" + strTest); // Private key must be invalid public key - addr.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!addr.IsValid(), + 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" + // "script" or "pubkey" + std::string exp_addrType = + find_value(metadata, "addrType").get_str(); // 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"), + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination), + "!IsValid:" + strTest); + BOOST_CHECK_MESSAGE((boost::get(&destination) != + nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest); - CTxDestination dest = addr.Get(); BOOST_CHECK_MESSAGE( - boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), + boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), + destination), "addrType mismatch" + strTest); // Public key must be invalid private key @@ -236,18 +236,12 @@ BOOST_ERROR("Bad addrtype: " << strTest); continue; } - CBitcoinAddress addrOut; - BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest); - BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, + 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); } @@ -260,7 +254,7 @@ json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; for (unsigned int idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; @@ -273,8 +267,9 @@ 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 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -16,20 +16,20 @@ #include -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 CBitcoinAddress addr1("1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"); -static const CBitcoinAddress addr2("1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"); -static const CBitcoinAddress addr1C("1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"); -static const CBitcoinAddress addr2C("1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"); - -static const std::string strAddressBad("1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"); +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 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"; +static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"; +static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"; +static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"; + +static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"; #ifdef KEY_TESTS_DUMPINFO void dumpKeyInfo(uint256 privkey) { @@ -101,10 +101,10 @@ 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++) { std::string strMsg = strprintf("Very secret message %i: 11", n); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -154,7 +154,7 @@ return NullUniValue; } -void ImportAddress(const CBitcoinAddress &address, const std::string &strLabel); +void ImportAddress(const CTxDestination &dest, const std::string &strLabel); void ImportScript(const CScript &script, const std::string &strLabel, bool isRedeemScript) { if (!isRedeemScript && ::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) @@ -173,7 +173,7 @@ !pwalletMain->AddCScript(script)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); - ImportAddress(CBitcoinAddress(CScriptID(script)), strLabel); + ImportAddress(CScriptID(script), strLabel); } else { CTxDestination destination; if (ExtractDestination(script, destination)) { @@ -182,13 +182,12 @@ } } -void ImportAddress(const CBitcoinAddress &address, - const std::string &strLabel) { - CScript script = GetScriptForDestination(address.Get()); +void ImportAddress(const CTxDestination &dest, const std::string &strLabel) { + CScript script = GetScriptForDestination(dest); ImportScript(script, strLabel, false); // add to address book or update label - if (address.IsValid()) - pwalletMain->SetAddressBook(address.Get(), strLabel, "receive"); + if (IsValidDestination(dest)) + pwalletMain->SetAddressBook(dest, strLabel, "receive"); } UniValue importaddress(const Config &config, const JSONRPCRequest &request) { @@ -241,13 +240,14 @@ LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (address.IsValid()) { - if (fP2SH) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (IsValidDestination(dest)) { + if (fP2SH) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use " "a script instead"); - ImportAddress(address, strLabel); + } + ImportAddress(dest, strLabel); } else if (IsHex(request.params[0].get_str())) { std::vector data(ParseHex(request.params[0].get_str())); ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH); @@ -430,7 +430,7 @@ LOCK2(cs_main, pwalletMain->cs_wallet); - ImportAddress(CBitcoinAddress(pubKey.GetID()), strLabel); + ImportAddress(pubKey.GetID(), strLabel); ImportScript(GetScriptForRawPubKey(pubKey), strLabel, false); if (fRescan) { @@ -501,7 +501,7 @@ CKeyID keyid = pubkey.GetID(); if (pwalletMain->HaveKey(keyid)) { LogPrintf("Skipping import of %s (key already present)\n", - CBitcoinAddress(keyid).ToString()); + EncodeDestination(keyid)); continue; } int64_t nTime = DecodeDumpTime(vstr[1]); @@ -516,7 +516,7 @@ fLabel = true; } } - LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); + LogPrintf("Importing %s...\n", EncodeDestination(keyid)); if (!pwalletMain->AddKeyPubKey(key, pubkey)) { fGood = false; continue; @@ -570,17 +570,20 @@ EnsureWalletIsUnlocked(); std::string strAddress = request.params[0].get_str(); - CBitcoinAddress address; - if (!address.SetString(strAddress)) + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin 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(); } @@ -653,7 +656,7 @@ 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)) { file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), @@ -721,15 +724,15 @@ // Parse the output. CScript script; - CBitcoinAddress address; + CTxDestination dest; if (!isScript) { - address = CBitcoinAddress(output); - if (!address.IsValid()) { + dest = DecodeDestination(output); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } - script = GetScriptForDestination(address.Get()); + script = GetScriptForDestination(dest); } else { if (!IsHex(output)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, @@ -801,10 +804,8 @@ "Error adding p2sh redeemScript to wallet"); } - CBitcoinAddress redeemAddress = - CBitcoinAddress(CScriptID(redeemScript)); - CScript redeemDestination = - GetScriptForDestination(redeemAddress.Get()); + CTxDestination redeem_dest = CScriptID(redeemScript); + CScript redeemDestination = GetScriptForDestination(redeem_dest); if (::IsMine(*pwalletMain, redeemDestination) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, @@ -821,8 +822,8 @@ } // add to address book or update label - if (address.IsValid()) { - pwalletMain->SetAddressBook(address.Get(), label, "receive"); + if (IsValidDestination(dest)) { + pwalletMain->SetAddressBook(dest, label, "receive"); } // Import private keys. @@ -888,30 +889,27 @@ "Pubkey is not a valid public key"); } - CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); + CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubKeyAddress.Get() == address.Get())) { + if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { - CBitcoinAddress scriptAddress; CTxDestination destination; if (ExtractDestination(script, destination)) { - scriptAddress = CBitcoinAddress(destination); - if (!(scriptAddress.Get() == pubKeyAddress.Get())) { + if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } } } - CScript pubKeyScript = - GetScriptForDestination(pubKeyAddress.Get()); + CScript pubKeyScript = GetScriptForDestination(pubkey_dest); if (::IsMine(*pwalletMain, pubKeyScript) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " @@ -929,9 +927,8 @@ } // add to address book or update label - if (pubKeyAddress.IsValid()) { - pwalletMain->SetAddressBook(pubKeyAddress.Get(), label, - "receive"); + if (IsValidDestination(pubkey_dest)) { + pwalletMain->SetAddressBook(pubkey_dest, label, "receive"); } // TODO Is this necessary? @@ -978,22 +975,20 @@ CPubKey pubKey = key.GetPubKey(); assert(key.VerifyPubKey(pubKey)); - CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); + CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubKeyAddress.Get() == address.Get())) { + if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { - CBitcoinAddress scriptAddress; CTxDestination destination; if (ExtractDestination(script, destination)) { - scriptAddress = CBitcoinAddress(destination); - if (!(scriptAddress.Get() == pubKeyAddress.Get())) { + if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } @@ -1039,9 +1034,8 @@ if (scriptPubKey.getType() == UniValue::VOBJ) { // add to address book or update label - if (address.IsValid()) { - pwalletMain->SetAddressBook(address.Get(), label, - "receive"); + if (IsValidDestination(dest)) { + pwalletMain->SetAddressBook(dest, label, "receive"); } } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -144,11 +144,11 @@ pwalletMain->SetAddressBook(keyID, strAccount, "receive"); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } -CBitcoinAddress GetAccountAddress(std::string strAccount, - bool bForceNew = false) { +CTxDestination GetAccountAddress(std::string strAccount, + bool bForceNew = false) { CPubKey pubKey; if (!pwalletMain->GetAccountPubkey(pubKey, strAccount, bForceNew)) { throw JSONRPCError( @@ -156,7 +156,7 @@ "Error: Keypool ran out, please call keypoolrefill first"); } - return CBitcoinAddress(pubKey.GetID()); + return pubKey.GetID(); } static UniValue getaccountaddress(const Config &config, @@ -192,7 +192,7 @@ UniValue ret(UniValue::VSTR); - ret = GetAccountAddress(strAccount).ToString(); + ret = EncodeDestination(GetAccountAddress(strAccount)); return ret; } @@ -232,7 +232,7 @@ CKeyID keyID = vchPubKey.GetID(); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } static UniValue setaccount(const Config &config, @@ -262,8 +262,8 @@ LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) { + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } @@ -274,17 +274,16 @@ } // 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())) { - std::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"); @@ -318,15 +317,15 @@ LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) { + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string strAccount; std::map::iterator mi = - pwalletMain->mapAddressBook.find(address.Get()); + pwalletMain->mapAddressBook.find(dest); if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } @@ -364,11 +363,13 @@ // Find all addresses that have the given account UniValue ret(UniValue::VARR); - for (const std::pair &item : + for (const std::pair &item : pwalletMain->mapAddressBook) { - const CBitcoinAddress &address = item.first; + const CTxDestination &dest = item.first; const std::string &strName = item.second.name; - if (strName == strAccount) ret.push_back(address.ToString()); + if (strName == strAccount) { + ret.push_back(EncodeDestination(dest)); + } } return ret; @@ -476,10 +477,9 @@ LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, - "Invalid Bitcoin address"); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } // Amount @@ -506,7 +506,7 @@ EnsureWalletIsUnlocked(); - SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); + SendMoney(dest, nAmount, fSubtractFeeFromAmount, wtx); return wtx.GetId().GetHex(); } @@ -548,20 +548,18 @@ UniValue jsonGroupings(UniValue::VARR); std::map balances = pwalletMain->GetAddressBalances(); - for (std::set grouping : + for (const std::set &grouping : pwalletMain->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); - for (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()) != + if (pwalletMain->mapAddressBook.find(address) != pwalletMain->mapAddressBook.end()) { - addressInfo.push_back(pwalletMain->mapAddressBook - .find(CBitcoinAddress(address).Get()) - ->second.name); + addressInfo.push_back( + pwalletMain->mapAddressBook.find(address)->second.name); } jsonGrouping.push_back(addressInfo); } @@ -615,18 +613,18 @@ std::string strAddress = request.params[0].get_str(); std::string strMessage = request.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"); } @@ -682,13 +680,12 @@ LOCK2(cs_main, pwalletMain->cs_wallet); // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); - if (!address.IsValid()) { + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } - - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwalletMain, scriptPubKey)) { return ValueFromAmount(0); } @@ -1065,12 +1062,11 @@ LOCK2(cs_main, pwalletMain->cs_wallet); std::string strAccount = AccountFromValue(request.params[0]); - CBitcoinAddress address(request.params[1].get_str()); - if (!address.IsValid()) { + CTxDestination dest = DecodeDestination(request.params[1].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } - CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -1103,7 +1099,7 @@ "Account has insufficient funds"); } - SendMoney(address.Get(), nAmount, false, wtx); + SendMoney(dest, nAmount, false, wtx); return wtx.GetId().GetHex(); } @@ -1213,27 +1209,27 @@ subtractFeeFromAmount = request.params[4].get_array(); } - std::set setAddress; + std::set destinations; std::vector vecSend; CAmount totalAmount = 0; std::vector keys = sendTo.getKeys(); for (const std::string &name_ : keys) { - CBitcoinAddress address(name_); - if (!address.IsValid()) { + CTxDestination dest = DecodeDestination(name_); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); } - if (setAddress.count(address)) { + if (destinations.count(dest)) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); } - setAddress.insert(address); + 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"); @@ -1342,7 +1338,7 @@ pwalletMain->AddCScript(inner); pwalletMain->SetAddressBook(innerID, strAccount, "send"); - return CBitcoinAddress(innerID).ToString(); + return EncodeDestination(innerID); } struct tallyitem { @@ -1376,7 +1372,7 @@ filter = filter | ISMINE_WATCH_ONLY; // Tally - std::map mapTally; + std::map mapTally; for (std::map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { @@ -1419,12 +1415,11 @@ // Reply UniValue ret(UniValue::VARR); std::map mapAccountTally; - for (const std::pair &item : + for (const std::pair &item : pwalletMain->mapAddressBook) { - const CBitcoinAddress &address = item.first; + const CTxDestination &dest = item.first; const std::string &strAccount = item.second.name; - std::map::iterator it = - mapTally.find(address); + std::map::iterator it = mapTally.find(dest); if (it == mapTally.end() && !fIncludeEmpty) { continue; } @@ -1448,7 +1443,7 @@ 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( @@ -1592,9 +1587,8 @@ } 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))); } } @@ -3041,25 +3035,24 @@ nMaxDepth = request.params[1].get_int(); } - std::set setAddress; + std::set destinations; if (request.params.size() > 2 && !request.params[2].isNull()) { RPCTypeCheckArgument(request.params[2], UniValue::VARR); UniValue inputs = request.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()) { + CTxDestination dest = DecodeDestination(input.get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str()); } - if (setAddress.count(address)) { + if (!destinations.insert(dest).second) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str()); } - setAddress.insert(address); } } @@ -3083,20 +3076,16 @@ const CScript &scriptPubKey = out.tx->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); entry.push_back(Pair("txid", out.tx->GetId().GetHex())); entry.push_back(Pair("vout", out.i)); 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( @@ -3247,15 +3236,16 @@ true, true); if (options.exists("changeAddress")) { - CBitcoinAddress address(options["changeAddress"].get_str()); + CTxDestination dest = + DecodeDestination(options["changeAddress"].get_str()); - if (!address.IsValid()) { + if (!IsValidDestination(dest)) { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address"); } - changeAddress = address.Get(); + changeAddress = dest; } if (options.exists("changePosition")) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -278,8 +278,7 @@ * 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", @@ -3236,12 +3235,12 @@ if (!strPurpose.empty() && !CWalletDB(strWalletFile) - .WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) { + .WritePurpose(EncodeDestination(address), strPurpose)) { return false; } return CWalletDB(strWalletFile) - .WriteName(CBitcoinAddress(address).ToString(), strName); + .WriteName(EncodeDestination(address), strName); } bool CWallet::DelAddressBook(const CTxDestination &address) { @@ -3251,7 +3250,7 @@ if (fFileBacked) { // Delete destdata tuples associated with address. - std::string strAddress = CBitcoinAddress(address).ToString(); + std::string strAddress = EncodeDestination(address); for (const std::pair &item : mapAddressBook[address].destdata) { CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); @@ -3268,9 +3267,8 @@ 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) { @@ -3870,7 +3868,7 @@ } return CWalletDB(strWalletFile) - .WriteDestData(CBitcoinAddress(dest).ToString(), key, value); + .WriteDestData(EncodeDestination(dest), key, value); } bool CWallet::EraseDestData(const CTxDestination &dest, @@ -3883,8 +3881,7 @@ 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, diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -286,13 +286,12 @@ std::string strAddress; ssKey >> strAddress; ssValue >> - pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; + pwallet->mapAddressBook[DecodeDestination(strAddress)].name; } else if (strType == "purpose") { std::string strAddress; ssKey >> strAddress; ssValue >> - pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()] - .purpose; + pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose; } else if (strType == "tx") { uint256 hash; ssKey >> hash; @@ -479,8 +478,8 @@ 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; }