Payment Protocol: X509-validated payment requests

Add support for a Payment Protocol to Bitcoin-Qt.

Payment messages are protocol-buffer encoded and communicated over
http(s), so this adds a dependency on the Google protocol buffer
library, and requires Qt with OpenSSL support.
This commit is contained in:
Gavin Andresen
2013-07-22 16:50:39 +10:00
parent 47d0534368
commit a41d5fe019
48 changed files with 2467 additions and 342 deletions

View File

@@ -127,31 +127,60 @@ bool WalletModel::validateAddress(const QString &address)
WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients)
{
qint64 total = 0;
QSet<QString> setAddress;
QString hex;
std::vector<std::pair<CScript, int64> > vecSend;
QByteArray transaction;
if(recipients.empty())
{
return OK;
}
QSet<QString> setAddress; // Used to detect duplicates
int nAddresses = 0;
// Pre-check input data for validity
foreach(const SendCoinsRecipient &rcp, recipients)
{
if(!validateAddress(rcp.address))
{
return InvalidAddress;
if (rcp.paymentRequest.IsInitialized())
{ // PaymentRequest...
int64 subtotal = 0;
const payments::PaymentDetails& details = rcp.paymentRequest.getDetails();
for (int i = 0; i < details.outputs_size(); i++)
{
const payments::Output& out = details.outputs(i);
if (out.amount() <= 0) continue;
subtotal += out.amount();
const unsigned char* scriptStr = (const unsigned char*)out.script().data();
CScript scriptPubKey(scriptStr, scriptStr+out.script().size());
vecSend.push_back(std::pair<CScript, int64>(scriptPubKey, out.amount()));
}
if (subtotal <= 0)
{
return InvalidAmount;
}
total += subtotal;
}
setAddress.insert(rcp.address);
else
{ // User-entered bitcoin address / amount:
if(!validateAddress(rcp.address))
{
return InvalidAddress;
}
if(rcp.amount <= 0)
{
return InvalidAmount;
}
setAddress.insert(rcp.address);
++nAddresses;
if(rcp.amount <= 0)
{
return InvalidAmount;
CScript scriptPubKey;
scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
vecSend.push_back(std::pair<CScript, int64>(scriptPubKey, rcp.amount));
total += rcp.amount;
}
total += rcp.amount;
}
if(recipients.size() > setAddress.size())
if(setAddress.size() != nAddresses)
{
return DuplicateAddress;
}
@@ -169,19 +198,10 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
{
LOCK2(cs_main, wallet->cs_wallet);
// Sendmany
std::vector<std::pair<CScript, int64> > vecSend;
foreach(const SendCoinsRecipient &rcp, recipients)
{
CScript scriptPubKey;
scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
vecSend.push_back(make_pair(scriptPubKey, rcp.amount));
}
CWalletTx wtx;
CReserveKey keyChange(wallet);
int64 nFeeRequired = 0;
std::string strFailReason;
CWalletTx wtx;
bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason);
if(!fCreated)
@@ -194,6 +214,18 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
CClientUIInterface::MSG_ERROR);
return TransactionCreationFailed;
}
// Store PaymentRequests in wtx.vOrderForm in wallet.
foreach(const SendCoinsRecipient &rcp, recipients)
{
if (rcp.paymentRequest.IsInitialized())
{
std::string key("PaymentRequest");
std::string value;
rcp.paymentRequest.SerializeToString(&value);
wtx.vOrderForm.push_back(make_pair(key, value));
}
}
if(!uiInterface.ThreadSafeAskFee(nFeeRequired))
{
return Aborted;
@@ -202,10 +234,15 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
{
return TransactionCommitFailed;
}
hex = QString::fromStdString(wtx.GetHash().GetHex());
CTransaction* t = (CTransaction*)&wtx;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << *t;
transaction.append(&(ssTx[0]), ssTx.size());
}
// Add addresses / update labels that we've sent to to the address book
// Add addresses / update labels that we've sent to to the address book,
// and emit coinsSent signal
foreach(const SendCoinsRecipient &rcp, recipients)
{
std::string strAddress = rcp.address.toStdString();
@@ -217,14 +254,19 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
// Check if we have a new address or an updated label
if (mi == wallet->mapAddressBook.end() || mi->second.name != strLabel)
if (mi == wallet->mapAddressBook.end())
{
wallet->SetAddressBookName(dest, strLabel);
wallet->SetAddressBook(dest, strLabel, "send");
}
else if (mi->second.name != strLabel)
{
wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
}
}
emit coinsSent(wallet, rcp, transaction);
}
return SendCoinsReturn(OK, 0, hex);
return SendCoinsReturn(OK, 0);
}
OptionsModel *WalletModel::getOptionsModel()