Browse Source

Merge #11117: Prepare for non-Base58 addresses

864cd2787 Move CBitcoinAddress to base58.cpp (Pieter Wuille)
5c8ff0d44 Introduce wrappers around CBitcoinAddress (Pieter Wuille)

Pull request description:

  This patch removes the need for the intermediary Base58 type `CBitcoinAddress`, by providing {`Encode`,`Decode`,`IsValid`}`Destination` functions that directly operate on the conversion between `std::string`s and `CTxDestination`.

  As a side, it also fixes a number of indentation issues, and removes probably several unnecessary implicit `CTxDestination`<->`CBitcoinAddress` conversions.

  This change is far from complete. In follow-ups I'd like to:
  * Split off the specific address and key encoding logic from base58.h, and move it to a address.h or so.
  * Replace `CTxDestination` with a non-`boost::variant` version (which can be more efficient as `boost::variant` allocates everything on the heap, and remove the need for `boost::get<...>` and `IsValidDestination` calls everywhere).
  * Do the same for `CBitcoinSecret`, `CBitcoinExtKey`, and `CBitcoinExtPubKey`.

  However, I've tried to keep this patch to be minimally invasive, but still enough to support non-Base58 addresses. Perhaps a smaller patch is possible to hack Bech32 support into `CBitcoinAddress`, but I would consider that a move in the wrong direction.

Tree-SHA512: c2c77ffb57caeadf2429b1c2562ce60e8c7be8aa9f8e51b591f354b6b441162625b2efe14c023a1ae485cf2ed417263afa35c892891dfaa7844e7fbabccab85e
0.16
Wladimir J. van der Laan 7 years ago
parent
commit
961901f77e
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
  1. 46
      src/base58.cpp
  2. 29
      src/base58.h
  3. 23
      src/bitcoin-tx.cpp
  4. 5
      src/core_write.cpp
  5. 22
      src/qt/addresstablemodel.cpp
  6. 4
      src/qt/bitcoinaddressvalidator.cpp
  7. 2
      src/qt/coincontroldialog.cpp
  8. 5
      src/qt/guiutil.cpp
  9. 15
      src/qt/paymentserver.cpp
  10. 5
      src/qt/sendcoinsdialog.cpp
  11. 24
      src/qt/signverifymessagedialog.cpp
  12. 8
      src/qt/test/wallettests.cpp
  13. 11
      src/qt/transactiondesc.cpp
  14. 4
      src/qt/transactionrecord.cpp
  15. 13
      src/qt/walletmodel.cpp
  16. 7
      src/rpc/mining.cpp
  17. 48
      src/rpc/misc.cpp
  18. 19
      src/rpc/rawtransaction.cpp
  19. 4
      src/script/standard.cpp
  20. 5
      src/script/standard.h
  21. 32
      src/test/base58_tests.cpp
  22. 27
      src/test/key_tests.cpp
  23. 83
      src/wallet/rpcdump.cpp
  24. 159
      src/wallet/rpcwallet.cpp
  25. 16
      src/wallet/wallet.cpp
  26. 6
      src/wallet/walletdb.cpp

46
src/base58.cpp

@ -212,6 +212,30 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const
namespace 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 &params) 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<bool> class CBitcoinAddressVisitor : public boost::static_visitor<bool>
{ {
private: private:
@ -318,3 +342,25 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
{ {
return SetString(strSecret.c_str()); 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();
}

29
src/base58.h

@ -95,30 +95,6 @@ public:
bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } 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 &params) 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 * A base58-encoded secret key
*/ */
@ -167,4 +143,9 @@ public:
typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey; typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey;
typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey; typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE, 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& params);
#endif // BITCOIN_BASE58_H #endif // BITCOIN_BASE58_H

23
src/bitcoin-tx.cpp

@ -271,11 +271,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn
// extract and validate ADDRESS // extract and validate ADDRESS
std::string strAddr = vStrInputParts[1]; std::string strAddr = vStrInputParts[1];
CBitcoinAddress addr(strAddr); CTxDestination destination = DecodeDestination(strAddr);
if (!addr.IsValid()) if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address"); 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 // construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey); CTxOut txout(value, scriptPubKey);
@ -314,10 +314,8 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str
scriptPubKey = GetScriptForWitness(scriptPubKey); scriptPubKey = GetScriptForWitness(scriptPubKey);
} }
if (bScriptHash) { if (bScriptHash) {
// Get the address for the redeem script, then call // Get the ID for the script, and then construct a P2SH destination for it.
// GetScriptForDestination() to construct a P2SH scriptPubKey. scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
CBitcoinAddress redeemScriptAddr(scriptPubKey);
scriptPubKey = GetScriptForDestination(redeemScriptAddr.Get());
} }
// construct TxOut, append to transaction output list // construct TxOut, append to transaction output list
@ -381,10 +379,8 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
scriptPubKey = GetScriptForWitness(scriptPubKey); scriptPubKey = GetScriptForWitness(scriptPubKey);
} }
if (bScriptHash) { if (bScriptHash) {
// Get the address for the redeem script, then call // Get the ID for the script, and then construct a P2SH destination for it.
// GetScriptForDestination() to construct a P2SH scriptPubKey. scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
CBitcoinAddress addr(scriptPubKey);
scriptPubKey = GetScriptForDestination(addr.Get());
} }
// construct TxOut, append to transaction output list // construct TxOut, append to transaction output list
@ -447,8 +443,7 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
scriptPubKey = GetScriptForWitness(scriptPubKey); scriptPubKey = GetScriptForWitness(scriptPubKey);
} }
if (bScriptHash) { if (bScriptHash) {
CBitcoinAddress addr(scriptPubKey); scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
scriptPubKey = GetScriptForDestination(addr.Get());
} }
// construct TxOut, append to transaction output list // construct TxOut, append to transaction output list

5
src/core_write.cpp

@ -148,8 +148,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
out.pushKV("type", GetTxnOutputType(type)); out.pushKV("type", GetTxnOutputType(type));
UniValue a(UniValue::VARR); UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses) for (const CTxDestination& addr : addresses) {
a.push_back(CBitcoinAddress(addr).ToString()); a.push_back(EncodeDestination(addr));
}
out.pushKV("addresses", a); out.pushKV("addresses", a);
} }

22
src/qt/addresstablemodel.cpp

@ -82,14 +82,14 @@ public:
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook) for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook)
{ {
const CBitcoinAddress& address = item.first; const CTxDestination& address = item.first;
bool fMine = IsMine(*wallet, address.Get()); bool fMine = IsMine(*wallet, address);
AddressTableEntry::Type addressType = translateTransactionType( AddressTableEntry::Type addressType = translateTransactionType(
QString::fromStdString(item.second.purpose), fMine); QString::fromStdString(item.second.purpose), fMine);
const std::string& strName = item.second.name; const std::string& strName = item.second.name;
cachedAddressTable.append(AddressTableEntry(addressType, cachedAddressTable.append(AddressTableEntry(addressType,
QString::fromStdString(strName), QString::fromStdString(strName),
QString::fromStdString(address.ToString()))); QString::fromStdString(EncodeDestination(address))));
} }
} }
// qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
@ -246,7 +246,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
if(role == Qt::EditRole) if(role == Qt::EditRole)
{ {
LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */ LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get(); CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
if(index.column() == Label) if(index.column() == Label)
{ {
// Do nothing, if old label == new label // Do nothing, if old label == new label
@ -257,7 +257,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
} }
wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose); wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
} else if(index.column() == Address) { } else if(index.column() == Address) {
CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get(); CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
// Refuse to set invalid address, set error status and return false // Refuse to set invalid address, set error status and return false
if(boost::get<CNoDestination>(&newAddress)) if(boost::get<CNoDestination>(&newAddress))
{ {
@ -358,7 +358,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// Check for duplicate addresses // Check for duplicate addresses
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get())) if(wallet->mapAddressBook.count(DecodeDestination(strAddress)))
{ {
editStatus = DUPLICATE_ADDRESS; editStatus = DUPLICATE_ADDRESS;
return QString(); return QString();
@ -384,7 +384,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString(); return QString();
} }
} }
strAddress = CBitcoinAddress(newKey.GetID()).ToString(); strAddress = EncodeDestination(newKey.GetID());
} }
else else
{ {
@ -394,7 +394,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// Add entry // Add entry
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel, wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
(type == Send ? "send" : "receive")); (type == Send ? "send" : "receive"));
} }
return QString::fromStdString(strAddress); return QString::fromStdString(strAddress);
@ -412,7 +412,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
} }
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get()); wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
} }
return true; return true;
} }
@ -423,8 +423,8 @@ QString AddressTableModel::labelForAddress(const QString &address) const
{ {
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
CBitcoinAddress address_parsed(address.toStdString()); CTxDestination destination = DecodeDestination(address.toStdString());
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get()); std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(destination);
if (mi != wallet->mapAddressBook.end()) if (mi != wallet->mapAddressBook.end())
{ {
return QString::fromStdString(mi->second.name); return QString::fromStdString(mi->second.name);

4
src/qt/bitcoinaddressvalidator.cpp

@ -89,9 +89,9 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po
{ {
Q_UNUSED(pos); Q_UNUSED(pos);
// Validate the passed Bitcoin address // Validate the passed Bitcoin address
CBitcoinAddress addr(input.toStdString()); if (IsValidDestinationString(input.toStdString())) {
if (addr.IsValid())
return QValidator::Acceptable; return QValidator::Acceptable;
}
return QValidator::Invalid; return QValidator::Invalid;
} }

2
src/qt/coincontroldialog.cpp

@ -660,7 +660,7 @@ void CoinControlDialog::updateView()
QString sAddress = ""; QString sAddress = "";
if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) 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 // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress))) if (!treeMode || (!(sAddress == sWalletAddress)))

5
src/qt/guiutil.cpp

@ -112,8 +112,9 @@ static std::string DummyAddress(const CChainParams &params)
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata)); sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
for(int i=0; i<256; ++i) { // Try every trailing byte for(int i=0; i<256; ++i) { // Try every trailing byte
std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()); std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size());
if (!CBitcoinAddress(s).IsValid()) if (!IsValidDestinationString(s)) {
return s; return s;
}
sourcedata[sourcedata.size()-1] += 1; sourcedata[sourcedata.size()-1] += 1;
} }
return ""; return "";
@ -248,7 +249,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
bool isDust(const QString& address, const CAmount& amount) bool isDust(const QString& address, const CAmount& amount)
{ {
CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); CTxDestination dest = DecodeDestination(address.toStdString());
CScript script = GetScriptForDestination(dest); CScript script = GetScriptForDestination(dest);
CTxOut txOut(amount, script); CTxOut txOut(amount, script);
return IsDust(txOut, ::dustRelayFee); return IsDust(txOut, ::dustRelayFee);

15
src/qt/paymentserver.cpp

@ -218,20 +218,18 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
SendCoinsRecipient r; SendCoinsRecipient r;
if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
{ {
CBitcoinAddress address(r.address.toStdString());
auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN); auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
if (address.IsValid(*tempChainParams)) if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
{
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
} } else {
else {
tempChainParams = CreateChainParams(CBaseChainParams::TESTNET); tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
if (address.IsValid(*tempChainParams)) if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
SelectParams(CBaseChainParams::TESTNET); SelectParams(CBaseChainParams::TESTNET);
} }
} }
} }
}
else if (QFile::exists(arg)) // Filename else if (QFile::exists(arg)) // Filename
{ {
savedPaymentRequests.append(arg); savedPaymentRequests.append(arg);
@ -441,8 +439,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
SendCoinsRecipient recipient; SendCoinsRecipient recipient;
if (GUIUtil::parseBitcoinURI(s, &recipient)) if (GUIUtil::parseBitcoinURI(s, &recipient))
{ {
CBitcoinAddress address(recipient.address.toStdString()); if (!IsValidDestinationString(recipient.address.toStdString())) {
if (!address.IsValid()) {
Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
CClientUIInterface::MSG_ERROR); CClientUIInterface::MSG_ERROR);
} }
@ -560,7 +557,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
CTxDestination dest; CTxDestination dest;
if (ExtractDestination(sendingTo.first, dest)) { if (ExtractDestination(sendingTo.first, dest)) {
// Append destination address // Append destination address
addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); addresses.append(QString::fromStdString(EncodeDestination(dest)));
} }
else if (!recipient.authenticatedMerchant.isEmpty()) { else if (!recipient.authenticatedMerchant.isEmpty()) {
// Unauthenticated payment requests to custom bitcoin addresses are not supported // Unauthenticated payment requests to custom bitcoin addresses are not supported

5
src/qt/sendcoinsdialog.cpp

@ -777,19 +777,18 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
CoinControlDialog::coinControl->destChange = CNoDestination(); CoinControlDialog::coinControl->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
CBitcoinAddress addr = CBitcoinAddress(text.toStdString()); const CTxDestination dest = DecodeDestination(text.toStdString());
if (text.isEmpty()) // Nothing entered if (text.isEmpty()) // Nothing entered
{ {
ui->labelCoinControlChangeLabel->setText(""); ui->labelCoinControlChangeLabel->setText("");
} }
else if (!addr.IsValid()) // Invalid address else if (!IsValidDestination(dest)) // Invalid address
{ {
ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address")); ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
} }
else // Valid address else // Valid address
{ {
const CTxDestination dest = addr.Get();
if (!model->IsSpendable(dest)) { if (!model->IsSpendable(dest)) {
ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));

24
src/qt/signverifymessagedialog.cpp

@ -117,16 +117,14 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
/* Clear old signature to ensure users don't get confused on error with an old signature displayed */ /* Clear old signature to ensure users don't get confused on error with an old signature displayed */
ui->signatureOut_SM->clear(); ui->signatureOut_SM->clear();
CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); CTxDestination destination = DecodeDestination(ui->addressIn_SM->text().toStdString());
if (!addr.IsValid()) if (!IsValidDestination(destination)) {
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); 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.")); ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return; return;
} }
CKeyID keyID; const CKeyID* keyID = boost::get<CKeyID>(&destination);
if (!addr.GetKeyID(keyID)) if (!keyID) {
{
ui->addressIn_SM->setValid(false); ui->addressIn_SM->setValid(false);
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
@ -142,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
} }
CKey key; CKey key;
if (!model->getPrivKey(keyID, key)) if (!model->getPrivKey(*keyID, key))
{ {
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
@ -197,16 +195,13 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked()
void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
{ {
CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); CTxDestination destination = DecodeDestination(ui->addressIn_VM->text().toStdString());
if (!addr.IsValid()) if (!IsValidDestination(destination)) {
{
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); 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.")); ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return; return;
} }
CKeyID keyID; if (!boost::get<CKeyID>(&destination)) {
if (!addr.GetKeyID(keyID))
{
ui->addressIn_VM->setValid(false); ui->addressIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
@ -237,8 +232,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
return; return;
} }
if (!(CBitcoinAddress(pubkey.GetID()) == addr)) if (!(CTxDestination(pubkey.GetID()) == destination)) {
{
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")); ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>"));
return; return;

8
src/qt/test/wallettests.cpp

@ -57,11 +57,11 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false)
} }
//! Send coins to address and return txid. //! Send coins to address and return txid.
uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount, bool rbf) uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf)
{ {
QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries"); QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget()); SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString())); entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address)));
entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount); entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);
sendCoinsDialog.findChild<QFrame*>("frameFee") sendCoinsDialog.findChild<QFrame*>("frameFee")
->findChild<QFrame*>("frameFeeSelection") ->findChild<QFrame*>("frameFeeSelection")
@ -172,8 +172,8 @@ void TestSendCoins()
// Send two transactions, and verify they are added to transaction list. // Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105); QCOMPARE(transactionTableModel->rowCount({}), 105);
uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */); uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */);
uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */); uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */);
QCOMPARE(transactionTableModel->rowCount({}), 107); QCOMPARE(transactionTableModel->rowCount({}), 107);
QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid());

11
src/qt/transactiondesc.cpp

@ -91,9 +91,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (nNet > 0) if (nNet > 0)
{ {
// Credit // Credit
if (CBitcoinAddress(rec->address).IsValid()) if (IsValidDestinationString(rec->address)) {
{ CTxDestination address = DecodeDestination(rec->address);
CTxDestination address = CBitcoinAddress(rec->address).Get();
if (wallet->mapAddressBook.count(address)) if (wallet->mapAddressBook.count(address))
{ {
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
@ -118,7 +117,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Online transaction // Online transaction
std::string strAddress = wtx.mapValue["to"]; std::string strAddress = wtx.mapValue["to"];
strHTML += "<b>" + tr("To") + ":</b> "; strHTML += "<b>" + tr("To") + ":</b> ";
CTxDestination dest = CBitcoinAddress(strAddress).Get(); CTxDestination dest = DecodeDestination(strAddress);
if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " ";
strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
@ -189,7 +188,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("To") + ":</b> "; strHTML += "<b>" + tr("To") + ":</b> ";
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
if(toSelf == ISMINE_SPENDABLE) if(toSelf == ISMINE_SPENDABLE)
strHTML += " (own address)"; strHTML += " (own address)";
else if(toSelf & ISMINE_WATCH_ONLY) else if(toSelf & ISMINE_WATCH_ONLY)
@ -304,7 +303,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
{ {
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); strHTML += QString::fromStdString(EncodeDestination(address));
} }
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>"; strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";

4
src/qt/transactionrecord.cpp

@ -55,7 +55,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{ {
// Received by Bitcoin Address // Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress; sub.type = TransactionRecord::RecvWithAddress;
sub.address = CBitcoinAddress(address).ToString(); sub.address = EncodeDestination(address);
} }
else else
{ {
@ -127,7 +127,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{ {
// Sent to Bitcoin Address // Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress; sub.type = TransactionRecord::SendToAddress;
sub.address = CBitcoinAddress(address).ToString(); sub.address = EncodeDestination(address);
} }
else else
{ {

13
src/qt/walletmodel.cpp

@ -188,8 +188,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly)
bool WalletModel::validateAddress(const QString &address) bool WalletModel::validateAddress(const QString &address)
{ {
CBitcoinAddress addressParsed(address.toStdString()); return IsValidDestinationString(address.toStdString());
return addressParsed.IsValid();
} }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl) WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
@ -247,7 +246,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
setAddress.insert(rcp.address); setAddress.insert(rcp.address);
++nAddresses; ++nAddresses;
CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString()));
CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
vecSend.push_back(recipient); vecSend.push_back(recipient);
@ -348,7 +347,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
if (!rcp.paymentRequest.IsInitialized()) if (!rcp.paymentRequest.IsInitialized())
{ {
std::string strAddress = rcp.address.toStdString(); std::string strAddress = rcp.address.toStdString();
CTxDestination dest = CBitcoinAddress(strAddress).Get(); CTxDestination dest = DecodeDestination(strAddress);
std::string strLabel = rcp.label.toStdString(); std::string strLabel = rcp.label.toStdString();
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
@ -464,7 +463,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
const CTxDestination &address, const std::string &label, bool isMine, const CTxDestination &address, const std::string &label, bool isMine,
const std::string &purpose, ChangeType status) 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 strLabel = QString::fromStdString(label);
QString strPurpose = QString::fromStdString(purpose); QString strPurpose = QString::fromStdString(purpose);
@ -596,7 +595,7 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const
void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
{ {
for (auto& group : wallet->ListCoins()) { for (auto& group : wallet->ListCoins()) {
auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())]; auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))];
for (auto& coin : group.second) { for (auto& coin : group.second) {
resultGroup.emplace_back(std::move(coin)); resultGroup.emplace_back(std::move(coin));
} }
@ -634,7 +633,7 @@ void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests
bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) 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; std::stringstream ss;
ss << nId; ss << nId;

7
src/rpc/mining.cpp

@ -176,12 +176,13 @@ UniValue generatetoaddress(const JSONRPCRequest& request)
nMaxTries = request.params[2].get_int(); nMaxTries = request.params[2].get_int();
} }
CBitcoinAddress address(request.params[1].get_str()); CTxDestination destination = DecodeDestination(request.params[1].get_str());
if (!address.IsValid()) if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
}
std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>(); std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
coinbaseScript->reserveScript = GetScriptForDestination(address.Get()); coinbaseScript->reserveScript = GetScriptForDestination(destination);
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false); return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
} }

48
src/rpc/misc.cpp

@ -152,8 +152,9 @@ public:
obj.push_back(Pair("script", GetTxnOutputType(whichType))); obj.push_back(Pair("script", GetTxnOutputType(whichType)));
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
UniValue a(UniValue::VARR); UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses) for (const CTxDestination& addr : addresses) {
a.push_back(CBitcoinAddress(addr).ToString()); a.push_back(EncodeDestination(addr));
}
obj.push_back(Pair("addresses", a)); obj.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG) if (whichType == TX_MULTISIG)
obj.push_back(Pair("sigsrequired", nRequired)); obj.push_back(Pair("sigsrequired", nRequired));
@ -207,15 +208,14 @@ UniValue validateaddress(const JSONRPCRequest& request)
LOCK(cs_main); LOCK(cs_main);
#endif #endif
CBitcoinAddress address(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
bool isValid = address.IsValid(); bool isValid = IsValidDestination(dest);
UniValue ret(UniValue::VOBJ); UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("isvalid", isValid)); ret.push_back(Pair("isvalid", isValid));
if (isValid) if (isValid)
{ {
CTxDestination dest = address.Get(); std::string currentAddress = EncodeDestination(dest);
std::string currentAddress = address.ToString();
ret.push_back(Pair("address", currentAddress)); ret.push_back(Pair("address", currentAddress));
CScript scriptPubKey = GetScriptForDestination(dest); CScript scriptPubKey = GetScriptForDestination(dest);
@ -230,10 +230,10 @@ UniValue validateaddress(const JSONRPCRequest& request)
if (pwallet && pwallet->mapAddressBook.count(dest)) { if (pwallet && pwallet->mapAddressBook.count(dest)) {
ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name)); ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name));
} }
CKeyID keyID;
if (pwallet) { if (pwallet) {
const auto& meta = pwallet->mapKeyMetadata; const auto& meta = pwallet->mapKeyMetadata;
auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end(); const CKeyID *keyID = boost::get<CKeyID>(&dest);
auto it = keyID ? meta.find(*keyID) : meta.end();
if (it == meta.end()) { if (it == meta.end()) {
it = meta.find(CScriptID(scriptPubKey)); it = meta.find(CScriptID(scriptPubKey));
} }
@ -277,16 +277,15 @@ CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& pa
const std::string& ks = keys[i].get_str(); const std::string& ks = keys[i].get_str();
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
// Case 1: Bitcoin address and we have full public key: // Case 1: Bitcoin address and we have full public key:
CBitcoinAddress address(ks); CTxDestination dest = DecodeDestination(ks);
if (pwallet && address.IsValid()) { if (pwallet && IsValidDestination(dest)) {
CKeyID keyID; const CKeyID *keyID = boost::get<CKeyID>(&dest);
if (!address.GetKeyID(keyID)) if (!keyID) {
throw std::runtime_error( throw std::runtime_error(strprintf("%s does not refer to a key", ks));
strprintf("%s does not refer to a key",ks)); }
CPubKey vchPubKey; CPubKey vchPubKey;
if (!pwallet->GetPubKey(keyID, vchPubKey)) { if (!pwallet->GetPubKey(*keyID, vchPubKey)) {
throw std::runtime_error( throw std::runtime_error(strprintf("no full public key for address %s", ks));
strprintf("no full public key for address %s",ks));
} }
if (!vchPubKey.IsFullyValid()) if (!vchPubKey.IsFullyValid())
throw std::runtime_error(" Invalid public key: "+ks); throw std::runtime_error(" Invalid public key: "+ks);
@ -357,10 +356,9 @@ UniValue createmultisig(const JSONRPCRequest& request)
// Construct using pay-to-script-hash: // Construct using pay-to-script-hash:
CScript inner = _createmultisig_redeemScript(pwallet, request.params); CScript inner = _createmultisig_redeemScript(pwallet, request.params);
CScriptID innerID(inner); CScriptID innerID(inner);
CBitcoinAddress address(innerID);
UniValue result(UniValue::VOBJ); 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()))); result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
return result; return result;
@ -395,13 +393,15 @@ UniValue verifymessage(const JSONRPCRequest& request)
std::string strSign = request.params[1].get_str(); std::string strSign = request.params[1].get_str();
std::string strMessage = request.params[2].get_str(); std::string strMessage = request.params[2].get_str();
CBitcoinAddress addr(strAddress); CTxDestination destination = DecodeDestination(strAddress);
if (!addr.IsValid()) if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
CKeyID keyID; const CKeyID *keyID = boost::get<CKeyID>(&destination);
if (!addr.GetKeyID(keyID)) if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
bool fInvalid = false; bool fInvalid = false;
std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid); std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
@ -417,7 +417,7 @@ UniValue verifymessage(const JSONRPCRequest& request)
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
return false; return false;
return (pubkey.GetID() == keyID); return (pubkey.GetID() == *keyID);
} }
UniValue signmessagewithprivkey(const JSONRPCRequest& request) UniValue signmessagewithprivkey(const JSONRPCRequest& request)

19
src/rpc/rawtransaction.cpp

@ -384,7 +384,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
rawTx.vin.push_back(in); rawTx.vin.push_back(in);
} }
std::set<CBitcoinAddress> setAddress; std::set<CTxDestination> destinations;
std::vector<std::string> addrList = sendTo.getKeys(); std::vector<std::string> addrList = sendTo.getKeys();
for (const std::string& name_ : addrList) { for (const std::string& name_ : addrList) {
@ -394,15 +394,16 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
CTxOut out(0, CScript() << OP_RETURN << data); CTxOut out(0, CScript() << OP_RETURN << data);
rawTx.vout.push_back(out); rawTx.vout.push_back(out);
} else { } else {
CBitcoinAddress address(name_); CTxDestination destination = DecodeDestination(name_);
if (!address.IsValid()) if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_); 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_); 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_]); CAmount nAmount = AmountFromValue(sendTo[name_]);
CTxOut out(nAmount, scriptPubKey); CTxOut out(nAmount, scriptPubKey);
@ -529,7 +530,7 @@ UniValue decodescript(const JSONRPCRequest& request)
if (type.isStr() && type.get_str() != "scripthash") { if (type.isStr() && type.get_str() != "scripthash") {
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // 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. // 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; return r;

4
src/script/standard.cpp

@ -317,3 +317,7 @@ CScript GetScriptForWitness(const CScript& redeemscript)
ret << OP_0 << ToByteVector(hash); ret << OP_0 << ToByteVector(hash);
return ret; return ret;
} }
bool IsValidDestination(const CTxDestination& dest) {
return dest.which() != 0;
}

5
src/script/standard.h

@ -77,10 +77,13 @@ public:
* * CNoDestination: no destination set * * CNoDestination: no destination set
* * CKeyID: TX_PUBKEYHASH destination * * CKeyID: TX_PUBKEYHASH destination
* * CScriptID: TX_SCRIPTHASH 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<CNoDestination, CKeyID, CScriptID> CTxDestination; typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
/** Get the name of a txnouttype as a C string, or nullptr if unknown. */ /** Get the name of a txnouttype as a C string, or nullptr if unknown. */
const char* GetTxnOutputType(txnouttype t); const char* GetTxnOutputType(txnouttype t);

32
src/test/base58_tests.cpp

@ -121,7 +121,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))); UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
CBitcoinSecret secret; CBitcoinSecret secret;
CBitcoinAddress addr; CTxDestination destination;
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
for (unsigned int idx = 0; idx < tests.size(); idx++) { for (unsigned int idx = 0; idx < tests.size(); idx++) {
@ -145,7 +145,6 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
{ {
bool isCompressed = find_value(metadata, "isCompressed").get_bool(); bool isCompressed = find_value(metadata, "isCompressed").get_bool();
// Must be valid private key // 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.SetString(exp_base58string), "!SetString:"+ strTest);
BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
CKey privkey = secret.GetKey(); CKey privkey = secret.GetKey();
@ -153,18 +152,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); 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 // Private key must be invalid public key
addr.SetString(exp_base58string); destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
} }
else else
{ {
std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
// Must be valid public key // Must be valid public key
BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest); destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest); BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest);
CTxDestination dest = addr.Get(); BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest);
BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest);
// Public key must be invalid private key // Public key must be invalid private key
secret.SetString(exp_base58string); secret.SetString(exp_base58string);
@ -226,17 +224,11 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
BOOST_ERROR("Bad addrtype: " << strTest); BOOST_ERROR("Bad addrtype: " << strTest);
continue; continue;
} }
CBitcoinAddress addrOut; std::string address = EncodeDestination(dest);
BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest); BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest);
BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest);
} }
} }
// Visiting a CNoDestination must fail
CBitcoinAddress dummyAddr;
CTxDestination nodest = CNoDestination();
BOOST_CHECK(!dummyAddr.Set(nodest));
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
} }
@ -245,7 +237,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 UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases
CBitcoinSecret secret; CBitcoinSecret secret;
CBitcoinAddress addr; CTxDestination destination;
for (unsigned int idx = 0; idx < tests.size(); idx++) { for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx]; UniValue test = tests[idx];
@ -258,8 +250,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
std::string exp_base58string = test[0].get_str(); std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key // must be invalid as public and as private key
addr.SetString(exp_base58string); destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest);
secret.SetString(exp_base58string); secret.SetString(exp_base58string);
BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
} }

27
src/test/key_tests.cpp

@ -16,17 +16,16 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
static const std::string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"); static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
static const std::string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"); static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3";
static const std::string strSecret1C ("Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"); static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw";
static const std::string strSecret2C ("L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"); static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g";
static const CBitcoinAddress addr1 ("1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"); static const std::string addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ";
static const CBitcoinAddress addr2 ("1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"); static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ";
static const CBitcoinAddress addr1C("1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"); static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs";
static const CBitcoinAddress addr2C("1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"); static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs";
static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF";
static const std::string strAddressBad("1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF");
BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup) BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup)
@ -74,10 +73,10 @@ BOOST_AUTO_TEST_CASE(key_test1)
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
BOOST_CHECK(addr1.Get() == CTxDestination(pubkey1.GetID())); BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID()));
BOOST_CHECK(addr2.Get() == CTxDestination(pubkey2.GetID())); BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID()));
BOOST_CHECK(addr1C.Get() == CTxDestination(pubkey1C.GetID())); BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID()));
BOOST_CHECK(addr2C.Get() == CTxDestination(pubkey2C.GetID())); BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID()));
for (int n=0; n<16; n++) for (int n=0; n<16; n++)
{ {

83
src/wallet/rpcdump.cpp

@ -181,7 +181,7 @@ UniValue abortrescan(const JSONRPCRequest& request)
return true; return true;
} }
void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel); void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel);
void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
{ {
if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
@ -198,7 +198,7 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) { if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
} }
ImportAddress(pwallet, CBitcoinAddress(CScriptID(script)), strLabel); ImportAddress(pwallet, CScriptID(script), strLabel);
} else { } else {
CTxDestination destination; CTxDestination destination;
if (ExtractDestination(script, destination)) { if (ExtractDestination(script, destination)) {
@ -207,13 +207,13 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
} }
} }
void ImportAddress(CWallet* const pwallet, const CBitcoinAddress& address, const std::string& strLabel) void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel)
{ {
CScript script = GetScriptForDestination(address.Get()); CScript script = GetScriptForDestination(dest);
ImportScript(pwallet, script, strLabel, false); ImportScript(pwallet, script, strLabel, false);
// add to address book or update label // add to address book or update label
if (address.IsValid()) if (IsValidDestination(dest))
pwallet->SetAddressBook(address.Get(), strLabel, "receive"); pwallet->SetAddressBook(dest, strLabel, "receive");
} }
UniValue importaddress(const JSONRPCRequest& request) UniValue importaddress(const JSONRPCRequest& request)
@ -265,11 +265,12 @@ UniValue importaddress(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet); LOCK2(cs_main, pwallet->cs_wallet);
CBitcoinAddress address(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (address.IsValid()) { if (IsValidDestination(dest)) {
if (fP2SH) if (fP2SH) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
ImportAddress(pwallet, address, strLabel); }
ImportAddress(pwallet, dest, strLabel);
} else if (IsHex(request.params[0].get_str())) { } else if (IsHex(request.params[0].get_str())) {
std::vector<unsigned char> data(ParseHex(request.params[0].get_str())); std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH); ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH);
@ -432,7 +433,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet); LOCK2(cs_main, pwallet->cs_wallet);
ImportAddress(pwallet, CBitcoinAddress(pubKey.GetID()), strLabel); ImportAddress(pwallet, pubKey.GetID(), strLabel);
ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false); ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
if (fRescan) if (fRescan)
@ -506,7 +507,7 @@ UniValue importwallet(const JSONRPCRequest& request)
assert(key.VerifyPubKey(pubkey)); assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID(); CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) { if (pwallet->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; continue;
} }
int64_t nTime = DecodeDumpTime(vstr[1]); int64_t nTime = DecodeDumpTime(vstr[1]);
@ -524,7 +525,7 @@ UniValue importwallet(const JSONRPCRequest& request)
fLabel = true; fLabel = true;
} }
} }
LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); LogPrintf("Importing %s...\n", EncodeDestination(keyid));
if (!pwallet->AddKeyPubKey(key, pubkey)) { if (!pwallet->AddKeyPubKey(key, pubkey)) {
fGood = false; fGood = false;
continue; continue;
@ -573,14 +574,16 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
std::string strAddress = request.params[0].get_str(); std::string strAddress = request.params[0].get_str();
CBitcoinAddress address; CTxDestination dest = DecodeDestination(strAddress);
if (!address.SetString(strAddress)) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
CKeyID keyID; }
if (!address.GetKeyID(keyID)) const CKeyID *keyID = boost::get<CKeyID>(&dest);
if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
}
CKey vchSecret; CKey vchSecret;
if (!pwallet->GetKey(keyID, vchSecret)) { if (!pwallet->GetKey(*keyID, vchSecret)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
} }
return CBitcoinSecret(vchSecret).ToString(); return CBitcoinSecret(vchSecret).ToString();
@ -659,7 +662,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second; const CKeyID &keyid = it->second;
std::string strTime = EncodeDumpTime(it->first); std::string strTime = EncodeDumpTime(it->first);
std::string strAddr = CBitcoinAddress(keyid).ToString(); std::string strAddr = EncodeDestination(keyid);
CKey key; CKey key;
if (pwallet->GetKey(keyid, key)) { if (pwallet->GetKey(keyid, key)) {
file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime);
@ -715,14 +718,14 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
// Parse the output. // Parse the output.
CScript script; CScript script;
CBitcoinAddress address; CTxDestination dest;
if (!isScript) { if (!isScript) {
address = CBitcoinAddress(output); dest = DecodeDestination(output);
if (!address.IsValid()) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
} }
script = GetScriptForDestination(address.Get()); script = GetScriptForDestination(dest);
} else { } else {
if (!IsHex(output)) { if (!IsHex(output)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
@ -780,8 +783,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
} }
CBitcoinAddress redeemAddress = CBitcoinAddress(CScriptID(redeemScript)); CTxDestination redeem_dest = CScriptID(redeemScript);
CScript redeemDestination = GetScriptForDestination(redeemAddress.Get()); CScript redeemDestination = GetScriptForDestination(redeem_dest);
if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@ -794,8 +797,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
} }
// add to address book or update label // add to address book or update label
if (address.IsValid()) { if (IsValidDestination(dest)) {
pwallet->SetAddressBook(address.Get(), label, "receive"); pwallet->SetAddressBook(dest, label, "receive");
} }
// Import private keys. // Import private keys.
@ -854,27 +857,25 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
} }
CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); CTxDestination pubkey_dest = pubKey.GetID();
// Consistency check. // Consistency check.
if (!isScript && !(pubKeyAddress.Get() == address.Get())) { if (!isScript && !(pubkey_dest == dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
} }
// Consistency check. // Consistency check.
if (isScript) { if (isScript) {
CBitcoinAddress scriptAddress;
CTxDestination destination; CTxDestination destination;
if (ExtractDestination(script, destination)) { if (ExtractDestination(script, destination)) {
scriptAddress = CBitcoinAddress(destination); if (!(destination == pubkey_dest)) {
if (!(scriptAddress.Get() == pubKeyAddress.Get())) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
} }
} }
} }
CScript pubKeyScript = GetScriptForDestination(pubKeyAddress.Get()); CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) { if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@ -887,8 +888,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
} }
// add to address book or update label // add to address book or update label
if (pubKeyAddress.IsValid()) { if (IsValidDestination(pubkey_dest)) {
pwallet->SetAddressBook(pubKeyAddress.Get(), label, "receive"); pwallet->SetAddressBook(pubkey_dest, label, "receive");
} }
// TODO Is this necessary? // TODO Is this necessary?
@ -927,21 +928,19 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
CPubKey pubKey = key.GetPubKey(); CPubKey pubKey = key.GetPubKey();
assert(key.VerifyPubKey(pubKey)); assert(key.VerifyPubKey(pubKey));
CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); CTxDestination pubkey_dest = pubKey.GetID();
// Consistency check. // Consistency check.
if (!isScript && !(pubKeyAddress.Get() == address.Get())) { if (!isScript && !(pubkey_dest == dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
} }
// Consistency check. // Consistency check.
if (isScript) { if (isScript) {
CBitcoinAddress scriptAddress;
CTxDestination destination; CTxDestination destination;
if (ExtractDestination(script, destination)) { if (ExtractDestination(script, destination)) {
scriptAddress = CBitcoinAddress(destination); if (!(destination == pubkey_dest)) {
if (!(scriptAddress.Get() == pubKeyAddress.Get())) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
} }
} }
@ -980,8 +979,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6
if (scriptPubKey.getType() == UniValue::VOBJ) { if (scriptPubKey.getType() == UniValue::VOBJ) {
// add to address book or update label // add to address book or update label
if (address.IsValid()) { if (IsValidDestination(dest)) {
pwallet->SetAddressBook(address.Get(), label, "receive"); pwallet->SetAddressBook(dest, label, "receive");
} }
} }

159
src/wallet/rpcwallet.cpp

@ -170,18 +170,18 @@ UniValue getnewaddress(const JSONRPCRequest& request)
pwallet->SetAddressBook(keyID, strAccount, "receive"); pwallet->SetAddressBook(keyID, strAccount, "receive");
return CBitcoinAddress(keyID).ToString(); return EncodeDestination(keyID);
} }
CBitcoinAddress GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false) CTxDestination GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false)
{ {
CPubKey pubKey; CPubKey pubKey;
if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) { if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
} }
return CBitcoinAddress(pubKey.GetID()); return pubKey.GetID();
} }
UniValue getaccountaddress(const JSONRPCRequest& request) UniValue getaccountaddress(const JSONRPCRequest& request)
@ -213,7 +213,7 @@ UniValue getaccountaddress(const JSONRPCRequest& request)
UniValue ret(UniValue::VSTR); UniValue ret(UniValue::VSTR);
ret = GetAccountAddress(pwallet, strAccount).ToString(); ret = EncodeDestination(GetAccountAddress(pwallet, strAccount));
return ret; return ret;
} }
@ -252,7 +252,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
CKeyID keyID = vchPubKey.GetID(); CKeyID keyID = vchPubKey.GetID();
return CBitcoinAddress(keyID).ToString(); return EncodeDestination(keyID);
} }
@ -277,24 +277,25 @@ UniValue setaccount(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet); LOCK2(cs_main, pwallet->cs_wallet);
CBitcoinAddress address(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!address.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
std::string strAccount; std::string strAccount;
if (!request.params[1].isNull()) if (!request.params[1].isNull())
strAccount = AccountFromValue(request.params[1]); strAccount = AccountFromValue(request.params[1]);
// Only add the account if the address is yours. // Only add the account if the address is yours.
if (IsMine(*pwallet, address.Get())) { if (IsMine(*pwallet, dest)) {
// Detect when changing the account of an address that is the 'unused current key' of another account: // Detect when changing the account of an address that is the 'unused current key' of another account:
if (pwallet->mapAddressBook.count(address.Get())) { if (pwallet->mapAddressBook.count(dest)) {
std::string strOldAccount = pwallet->mapAddressBook[address.Get()].name; std::string strOldAccount = pwallet->mapAddressBook[dest].name;
if (address == GetAccountAddress(pwallet, strOldAccount)) { if (dest == GetAccountAddress(pwallet, strOldAccount)) {
GetAccountAddress(pwallet, strOldAccount, true); GetAccountAddress(pwallet, strOldAccount, true);
} }
} }
pwallet->SetAddressBook(address.Get(), strAccount, "receive"); pwallet->SetAddressBook(dest, strAccount, "receive");
} }
else else
throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address");
@ -325,12 +326,13 @@ UniValue getaccount(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet); LOCK2(cs_main, pwallet->cs_wallet);
CBitcoinAddress address(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!address.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
std::string strAccount; std::string strAccount;
std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(address.Get()); std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) {
strAccount = (*mi).second.name; strAccount = (*mi).second.name;
} }
@ -367,11 +369,12 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
// Find all addresses that have the given account // Find all addresses that have the given account
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) { for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
const CBitcoinAddress& address = item.first; const CTxDestination& dest = item.first;
const std::string& strName = item.second.name; const std::string& strName = item.second.name;
if (strName == strAccount) if (strName == strAccount) {
ret.push_back(address.ToString()); ret.push_back(EncodeDestination(dest));
}
} }
return ret; return ret;
} }
@ -454,9 +457,10 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
ObserveSafeMode(); ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet); LOCK2(cs_main, pwallet->cs_wallet);
CBitcoinAddress address(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!address.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
// Amount // Amount
CAmount nAmount = AmountFromValue(request.params[1]); CAmount nAmount = AmountFromValue(request.params[1]);
@ -493,7 +497,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(pwallet);
SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, coin_control); SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx, coin_control);
return wtx.GetHash().GetHex(); return wtx.GetHash().GetHex();
} }
@ -533,16 +537,16 @@ UniValue listaddressgroupings(const JSONRPCRequest& request)
UniValue jsonGroupings(UniValue::VARR); UniValue jsonGroupings(UniValue::VARR);
std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(); std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
for (std::set<CTxDestination> grouping : pwallet->GetAddressGroupings()) { for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR); UniValue jsonGrouping(UniValue::VARR);
for (CTxDestination address : grouping) for (const CTxDestination& address : grouping)
{ {
UniValue addressInfo(UniValue::VARR); UniValue addressInfo(UniValue::VARR);
addressInfo.push_back(CBitcoinAddress(address).ToString()); addressInfo.push_back(EncodeDestination(address));
addressInfo.push_back(ValueFromAmount(balances[address])); addressInfo.push_back(ValueFromAmount(balances[address]));
{ {
if (pwallet->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwallet->mapAddressBook.end()) { if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) {
addressInfo.push_back(pwallet->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); addressInfo.push_back(pwallet->mapAddressBook.find(address)->second.name);
} }
} }
jsonGrouping.push_back(addressInfo); jsonGrouping.push_back(addressInfo);
@ -587,16 +591,18 @@ UniValue signmessage(const JSONRPCRequest& request)
std::string strAddress = request.params[0].get_str(); std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str(); std::string strMessage = request.params[1].get_str();
CBitcoinAddress addr(strAddress); CTxDestination dest = DecodeDestination(strAddress);
if (!addr.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
CKeyID keyID; const CKeyID *keyID = boost::get<CKeyID>(&dest);
if (!addr.GetKeyID(keyID)) if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
CKey key; CKey key;
if (!pwallet->GetKey(keyID, key)) { if (!pwallet->GetKey(*keyID, key)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
} }
@ -642,10 +648,11 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet); LOCK2(cs_main, pwallet->cs_wallet);
// Bitcoin address // Bitcoin address
CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!address.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
CScript scriptPubKey = GetScriptForDestination(address.Get()); }
CScript scriptPubKey = GetScriptForDestination(dest);
if (!IsMine(*pwallet, scriptPubKey)) { if (!IsMine(*pwallet, scriptPubKey)) {
return ValueFromAmount(0); return ValueFromAmount(0);
} }
@ -915,9 +922,10 @@ UniValue sendfrom(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet); LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = AccountFromValue(request.params[0]); std::string strAccount = AccountFromValue(request.params[0]);
CBitcoinAddress address(request.params[1].get_str()); CTxDestination dest = DecodeDestination(request.params[1].get_str());
if (!address.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
CAmount nAmount = AmountFromValue(request.params[2]); CAmount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= 0) if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
@ -940,7 +948,7 @@ UniValue sendfrom(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
CCoinControl no_coin_control; // This is a deprecated API CCoinControl no_coin_control; // This is a deprecated API
SendMoney(pwallet, address.Get(), nAmount, false, wtx, no_coin_control); SendMoney(pwallet, dest, nAmount, false, wtx, no_coin_control);
return wtx.GetHash().GetHex(); return wtx.GetHash().GetHex();
} }
@ -1032,22 +1040,23 @@ UniValue sendmany(const JSONRPCRequest& request)
} }
} }
std::set<CBitcoinAddress> setAddress; std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend; std::vector<CRecipient> vecSend;
CAmount totalAmount = 0; CAmount totalAmount = 0;
std::vector<std::string> keys = sendTo.getKeys(); std::vector<std::string> keys = sendTo.getKeys();
for (const std::string& name_ : keys) for (const std::string& name_ : keys) {
{ CTxDestination dest = DecodeDestination(name_);
CBitcoinAddress address(name_); if (!IsValidDestination(dest)) {
if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
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_); 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_]); CAmount nAmount = AmountFromValue(sendTo[name_]);
if (nAmount <= 0) if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
@ -1138,7 +1147,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
pwallet->AddCScript(inner); pwallet->AddCScript(inner);
pwallet->SetAddressBook(innerID, strAccount, "send"); pwallet->SetAddressBook(innerID, strAccount, "send");
return CBitcoinAddress(innerID).ToString(); return EncodeDestination(innerID);
} }
class Witnessifier : public boost::static_visitor<bool> class Witnessifier : public boost::static_visitor<bool>
@ -1226,12 +1235,12 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
} }
} }
CBitcoinAddress address(request.params[0].get_str()); CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!address.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
Witnessifier w(pwallet); Witnessifier w(pwallet);
CTxDestination dest = address.Get();
bool ret = boost::apply_visitor(w, dest); bool ret = boost::apply_visitor(w, dest);
if (!ret) { if (!ret) {
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
@ -1239,7 +1248,7 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
pwallet->SetAddressBook(w.result, "", "receive"); pwallet->SetAddressBook(w.result, "", "receive");
return CBitcoinAddress(w.result).ToString(); return EncodeDestination(w.result);
} }
struct tallyitem struct tallyitem
@ -1274,7 +1283,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
filter = filter | ISMINE_WATCH_ONLY; filter = filter | ISMINE_WATCH_ONLY;
// Tally // Tally
std::map<CBitcoinAddress, tallyitem> mapTally; std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second; const CWalletTx& wtx = pairWtx.second;
@ -1307,10 +1316,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
// Reply // Reply
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> mapAccountTally; std::map<std::string, tallyitem> mapAccountTally;
for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) { for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
const CBitcoinAddress& address = item.first; const CTxDestination& dest = item.first;
const std::string& strAccount = item.second.name; const std::string& strAccount = item.second.name;
std::map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address); std::map<CTxDestination, tallyitem>::iterator it = mapTally.find(dest);
if (it == mapTally.end() && !fIncludeEmpty) if (it == mapTally.end() && !fIncludeEmpty)
continue; continue;
@ -1336,7 +1345,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
if(fIsWatchonly) if(fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true)); 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("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
@ -1461,9 +1470,9 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request)
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
{ {
CBitcoinAddress addr; if (IsValidDestination(dest)) {
if (addr.Set(dest)) entry.push_back(Pair("address", EncodeDestination(dest)));
entry.push_back(Pair("address", addr.ToString())); }
} }
/** /**
@ -2717,18 +2726,19 @@ UniValue listunspent(const JSONRPCRequest& request)
nMaxDepth = request.params[1].get_int(); nMaxDepth = request.params[1].get_int();
} }
std::set<CBitcoinAddress> setAddress; std::set<CTxDestination> destinations;
if (!request.params[2].isNull()) { if (!request.params[2].isNull()) {
RPCTypeCheckArgument(request.params[2], UniValue::VARR); RPCTypeCheckArgument(request.params[2], UniValue::VARR);
UniValue inputs = request.params[2].get_array(); UniValue inputs = request.params[2].get_array();
for (unsigned int idx = 0; idx < inputs.size(); idx++) { for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx]; const UniValue& input = inputs[idx];
CBitcoinAddress address(input.get_str()); CTxDestination dest = DecodeDestination(input.get_str());
if (!address.IsValid()) if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+input.get_str()); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str());
if (setAddress.count(address)) }
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+input.get_str()); if (!destinations.insert(dest).second) {
setAddress.insert(address); throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
}
} }
} }
@ -2770,7 +2780,7 @@ UniValue listunspent(const JSONRPCRequest& request)
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address); bool fValidAddress = ExtractDestination(scriptPubKey, address);
if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) if (destinations.size() && (!fValidAddress || !destinations.count(address)))
continue; continue;
UniValue entry(UniValue::VOBJ); UniValue entry(UniValue::VOBJ);
@ -2778,7 +2788,7 @@ UniValue listunspent(const JSONRPCRequest& request)
entry.push_back(Pair("vout", out.i)); entry.push_back(Pair("vout", out.i));
if (fValidAddress) { if (fValidAddress) {
entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); entry.push_back(Pair("address", EncodeDestination(address)));
if (pwallet->mapAddressBook.count(address)) { if (pwallet->mapAddressBook.count(address)) {
entry.push_back(Pair("account", pwallet->mapAddressBook[address].name)); entry.push_back(Pair("account", pwallet->mapAddressBook[address].name));
@ -2901,12 +2911,13 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
true, true); true, true);
if (options.exists("changeAddress")) { 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"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
}
coinControl.destChange = address.Get(); coinControl.destChange = dest;
} }
if (options.exists("changePosition")) if (options.exists("changePosition"))

16
src/wallet/wallet.cpp

@ -307,7 +307,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
* these. Do not add them to the wallet and warn. */ * these. Do not add them to the wallet and warn. */
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) 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", 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); __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
return true; return true;
@ -3072,9 +3072,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
} }
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(EncodeDestination(address), strPurpose))
return false; return false;
return CWalletDB(*dbw).WriteName(CBitcoinAddress(address).ToString(), strName); return CWalletDB(*dbw).WriteName(EncodeDestination(address), strName);
} }
bool CWallet::DelAddressBook(const CTxDestination& address) bool CWallet::DelAddressBook(const CTxDestination& address)
@ -3083,7 +3083,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
LOCK(cs_wallet); // mapAddressBook LOCK(cs_wallet); // mapAddressBook
// Delete destdata tuples associated with address // Delete destdata tuples associated with address
std::string strAddress = CBitcoinAddress(address).ToString(); std::string strAddress = EncodeDestination(address);
for (const std::pair<std::string, std::string> &item : mapAddressBook[address].destdata) for (const std::pair<std::string, std::string> &item : mapAddressBook[address].destdata)
{ {
CWalletDB(*dbw).EraseDestData(strAddress, item.first); CWalletDB(*dbw).EraseDestData(strAddress, item.first);
@ -3093,8 +3093,8 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED);
CWalletDB(*dbw).ErasePurpose(CBitcoinAddress(address).ToString()); CWalletDB(*dbw).ErasePurpose(EncodeDestination(address));
return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString()); return CWalletDB(*dbw).EraseName(EncodeDestination(address));
} }
const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
@ -3711,14 +3711,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co
return false; return false;
mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
return CWalletDB(*dbw).WriteDestData(CBitcoinAddress(dest).ToString(), key, value); return CWalletDB(*dbw).WriteDestData(EncodeDestination(dest), key, value);
} }
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
{ {
if (!mapAddressBook[dest].destdata.erase(key)) if (!mapAddressBook[dest].destdata.erase(key))
return false; return false;
return CWalletDB(*dbw).EraseDestData(CBitcoinAddress(dest).ToString(), key); return CWalletDB(*dbw).EraseDestData(EncodeDestination(dest), key);
} }
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)

6
src/wallet/walletdb.cpp

@ -253,13 +253,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
{ {
std::string strAddress; std::string strAddress;
ssKey >> strAddress; ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name;
} }
else if (strType == "purpose") else if (strType == "purpose")
{ {
std::string strAddress; std::string strAddress;
ssKey >> strAddress; ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose;
} }
else if (strType == "tx") else if (strType == "tx")
{ {
@ -493,7 +493,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> strAddress; ssKey >> strAddress;
ssKey >> strKey; ssKey >> strKey;
ssValue >> strValue; ssValue >> strValue;
if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue))
{ {
strErr = "Error reading wallet database: LoadDestData failed"; strErr = "Error reading wallet database: LoadDestData failed";
return false; return false;

Loading…
Cancel
Save