Skip to content

Commit

Permalink
Introduce wrappers around CBitcoinAddress
Browse files Browse the repository at this point in the history
This patch removes the need for the intermediary Base58 type
CBitcoinAddress, by providing {Encode,Decode,IsValid}Destination
function that directly operate on the conversion between strings
and CTxDestination.
  • Loading branch information
sipa committed Sep 6, 2017
1 parent 6866b49 commit 5c8ff0d
Show file tree
Hide file tree
Showing 26 changed files with 299 additions and 277 deletions.
22 changes: 22 additions & 0 deletions src/base58.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,25 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
{
return SetString(strSecret.c_str());
}

std::string EncodeDestination(const CTxDestination& dest)
{
CBitcoinAddress addr(dest);
if (!addr.IsValid()) return "";
return addr.ToString();
}

CTxDestination DecodeDestination(const std::string& str)
{
return CBitcoinAddress(str).Get();
}

bool IsValidDestinationString(const std::string& str, const CChainParams& params)
{
return CBitcoinAddress(str).IsValid(params);
}

bool IsValidDestinationString(const std::string& str)
{
return CBitcoinAddress(str).IsValid();
}
5 changes: 5 additions & 0 deletions src/base58.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,9 @@ template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtK
typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey;
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
25 changes: 10 additions & 15 deletions src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn

// 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);
Expand Down Expand Up @@ -314,10 +314,8 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str
scriptPubKey = GetScriptForWitness(scriptPubKey);
}
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
Expand Down Expand Up @@ -381,10 +379,8 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
scriptPubKey = GetScriptForWitness(scriptPubKey);
}
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
Expand Down Expand Up @@ -444,11 +440,10 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
}

if (bSegWit) {
scriptPubKey = GetScriptForWitness(scriptPubKey);
scriptPubKey = GetScriptForWitness(scriptPubKey);
}
if (bScriptHash) {
CBitcoinAddress addr(scriptPubKey);
scriptPubKey = GetScriptForDestination(addr.Get());
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}

// construct TxOut, append to transaction output list
Expand Down
5 changes: 3 additions & 2 deletions src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
out.pushKV("type", GetTxnOutputType(type));

UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses)
a.push_back(CBitcoinAddress(addr).ToString());
for (const CTxDestination& addr : addresses) {
a.push_back(EncodeDestination(addr));
}
out.pushKV("addresses", a);
}

Expand Down
22 changes: 11 additions & 11 deletions src/qt/addresstablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ class AddressTablePriv
LOCK(wallet->cs_wallet);
for (const std::pair<CTxDestination, CAddressBookData>& 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 to be sorted in asc order
Expand Down Expand Up @@ -246,7 +246,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
if(role == Qt::EditRole)
{
LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get();
CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
if(index.column() == Label)
{
// Do nothing, if old label == new label
Expand All @@ -257,7 +257,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
}
wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
} 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
if(boost::get<CNoDestination>(&newAddress))
{
Expand Down Expand Up @@ -358,7 +358,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// 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();
Expand All @@ -384,7 +384,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
strAddress = CBitcoinAddress(newKey.GetID()).ToString();
strAddress = EncodeDestination(newKey.GetID());
}
else
{
Expand All @@ -394,7 +394,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// 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);
Expand All @@ -412,7 +412,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
}
{
LOCK(wallet->cs_wallet);
wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
}
return true;
}
Expand All @@ -423,8 +423,8 @@ QString AddressTableModel::labelForAddress(const QString &address) const
{
{
LOCK(wallet->cs_wallet);
CBitcoinAddress address_parsed(address.toStdString());
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
CTxDestination destination = DecodeDestination(address.toStdString());
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(destination);
if (mi != wallet->mapAddressBook.end())
{
return QString::fromStdString(mi->second.name);
Expand Down
4 changes: 2 additions & 2 deletions src/qt/bitcoinaddressvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po
{
Q_UNUSED(pos);
// Validate the passed Bitcoin address
CBitcoinAddress addr(input.toStdString());
if (addr.IsValid())
if (IsValidDestinationString(input.toStdString())) {
return QValidator::Acceptable;
}

return QValidator::Invalid;
}
2 changes: 1 addition & 1 deletion src/qt/coincontroldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ void CoinControlDialog::updateView()
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
if (!treeMode || (!(sAddress == sWalletAddress)))
Expand Down
5 changes: 3 additions & 2 deletions src/qt/guiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ static std::string DummyAddress(const CChainParams &params)
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
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())
if (!IsValidDestinationString(s)) {
return s;
}
sourcedata[sourcedata.size()-1] += 1;
}
return "";
Expand Down Expand Up @@ -248,7 +249,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)

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 IsDust(txOut, ::dustRelayFee);
Expand Down
15 changes: 6 additions & 9 deletions src/qt/paymentserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,17 +218,15 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
SendCoinsRecipient r;
if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
{
CBitcoinAddress address(r.address.toStdString());
auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);

if (address.IsValid(*tempChainParams))
{
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
SelectParams(CBaseChainParams::MAIN);
}
else {
} else {
tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
if (address.IsValid(*tempChainParams))
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
SelectParams(CBaseChainParams::TESTNET);
}
}
}
}
Expand Down Expand Up @@ -441,8 +439,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
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),
CClientUIInterface::MSG_ERROR);
}
Expand Down Expand Up @@ -560,7 +557,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
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
Expand Down
5 changes: 2 additions & 3 deletions src/qt/sendcoinsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,19 +777,18 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
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()) // Invalid address
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"));

Expand Down
24 changes: 9 additions & 15 deletions src/qt/signverifymessagedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
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<CKeyID>(&destination);
if (!keyID) {
ui->addressIn_SM->setValid(false);
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."));
Expand All @@ -142,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}

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."));
Expand Down Expand Up @@ -197,16 +195,13 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked()

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<CKeyID>(&destination)) {
ui->addressIn_VM->setValid(false);
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."));
Expand Down Expand Up @@ -237,8 +232,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
return;
}

if (!(CBitcoinAddress(pubkey.GetID()) == addr))
{
if (!(CTxDestination(pubkey.GetID()) == destination)) {
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>"));
return;
Expand Down
8 changes: 4 additions & 4 deletions src/qt/test/wallettests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false)
}

//! 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");
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);
sendCoinsDialog.findChild<QFrame*>("frameFee")
->findChild<QFrame*>("frameFeeSelection")
Expand Down Expand Up @@ -172,8 +172,8 @@ void TestSendCoins()
// Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105);
uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */);
uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */);
uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */);
uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */);
QCOMPARE(transactionTableModel->rowCount({}), 107);
QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
Expand Down
Loading

0 comments on commit 5c8ff0d

Please sign in to comment.