aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ecwrapper.cpp30
-rw-r--r--src/init.cpp27
-rw-r--r--src/main.cpp18
-rw-r--r--src/qt/bitcoinamountfield.cpp39
-rw-r--r--src/qt/forms/signverifymessagedialog.ui4
-rw-r--r--src/qt/paymentserver.cpp15
-rw-r--r--src/qt/paymentserver.h3
-rw-r--r--src/qt/test/paymentrequestdata.h76
-rw-r--r--src/qt/test/paymentservertests.cpp56
9 files changed, 219 insertions, 49 deletions
diff --git a/src/ecwrapper.cpp b/src/ecwrapper.cpp
index c29390eb98..5e3aec25ba 100644
--- a/src/ecwrapper.cpp
+++ b/src/ecwrapper.cpp
@@ -117,10 +117,34 @@ bool CECKey::SetPubKey(const unsigned char* pubkey, size_t size) {
}
bool CECKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
- // -1 = error, 0 = bad sig, 1 = good
- if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
+ if (vchSig.empty())
return false;
- return true;
+
+ // New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
+ unsigned char *norm_der = NULL;
+ ECDSA_SIG *norm_sig = ECDSA_SIG_new();
+ const unsigned char* sigptr = &vchSig[0];
+ assert(norm_sig);
+ if (d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()) == NULL)
+ {
+ /* As of OpenSSL 1.0.0p d2i_ECDSA_SIG frees and nulls the pointer on
+ * error. But OpenSSL's own use of this function redundantly frees the
+ * result. As ECDSA_SIG_free(NULL) is a no-op, and in the absence of a
+ * clear contract for the function behaving the same way is more
+ * conservative.
+ */
+ ECDSA_SIG_free(norm_sig);
+ return false;
+ }
+ int derlen = i2d_ECDSA_SIG(norm_sig, &norm_der);
+ ECDSA_SIG_free(norm_sig);
+ if (derlen <= 0)
+ return false;
+
+ // -1 = error, 0 = bad sig, 1 = good
+ bool ret = ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), norm_der, derlen, pkey) == 1;
+ OPENSSL_free(norm_der);
+ return ret;
}
bool CECKey::Recover(const uint256 &hash, const unsigned char *p64, int rec)
diff --git a/src/init.cpp b/src/init.cpp
index 03d67f0aa3..d6f1e1cb9f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -111,7 +111,28 @@ bool ShutdownRequested()
return fRequestShutdown;
}
+class CCoinsViewErrorCatcher : public CCoinsViewBacked
+{
+public:
+ CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
+ bool GetCoins(const uint256 &txid, CCoins &coins) const {
+ try {
+ return CCoinsViewBacked::GetCoins(txid, coins);
+ } catch(const std::runtime_error& e) {
+ uiInterface.ThreadSafeMessageBox(_("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR);
+ LogPrintf("Error reading from database: %s\n", e.what());
+ // Starting the shutdown sequence and returning false to the caller would be
+ // interpreted as 'entry not found' (as opposed to unable to read data), and
+ // could lead to invalid interpration. Just exit immediately, as we can't
+ // continue anyway, and all writes should be atomic.
+ abort();
+ }
+ }
+ // Writes do not need similar protection, as failure to write is handled by the caller.
+};
+
static CCoinsViewDB *pcoinsdbview = NULL;
+static CCoinsViewErrorCatcher *pcoinscatcher = NULL;
void Shutdown()
{
@@ -154,6 +175,8 @@ void Shutdown()
}
delete pcoinsTip;
pcoinsTip = NULL;
+ delete pcoinscatcher;
+ pcoinscatcher = NULL;
delete pcoinsdbview;
pcoinsdbview = NULL;
delete pblocktree;
@@ -990,11 +1013,13 @@ bool AppInit2(boost::thread_group& threadGroup)
UnloadBlockIndex();
delete pcoinsTip;
delete pcoinsdbview;
+ delete pcoinscatcher;
delete pblocktree;
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
- pcoinsTip = new CCoinsViewCache(pcoinsdbview);
+ pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
+ pcoinsTip = new CCoinsViewCache(pcoinscatcher);
if (fReindex)
pblocktree->WriteReindexing(true);
diff --git a/src/main.cpp b/src/main.cpp
index 2e9d954458..7cc69c318b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -139,9 +139,14 @@ namespace {
uint256 hash;
CBlockIndex *pindex; //! Optional.
int64_t nTime; //! Time of "getdata" request in microseconds.
+ int nValidatedQueuedBefore; //! Number of blocks queued with validated headers (globally) at the time this one is requested.
+ bool fValidatedHeaders; //! Whether this block has validated headers at the time of request.
};
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
+ /** Number of blocks in flight with validated headers. */
+ int nQueuedValidatedHeaders = 0;
+
/** Number of preferable block download peers. */
int nPreferredDownload = 0;
@@ -323,6 +328,7 @@ void MarkBlockAsReceived(const uint256& hash) {
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
+ nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders;
state->vBlocksInFlight.erase(itInFlight->second.second);
state->nBlocksInFlight--;
state->nStallingSince = 0;
@@ -338,7 +344,8 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex
// Make sure it's not listed somewhere already.
MarkBlockAsReceived(hash);
- QueuedBlock newentry = {hash, pindex, GetTimeMicros()};
+ QueuedBlock newentry = {hash, pindex, GetTimeMicros(), nQueuedValidatedHeaders, pindex != NULL};
+ nQueuedValidatedHeaders += newentry.fValidatedHeaders;
list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry);
state->nBlocksInFlight++;
mapBlocksInFlight[hash] = std::make_pair(nodeid, it);
@@ -4527,6 +4534,15 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id);
pto->fDisconnect = true;
}
+ // In case there is a block that has been in flight from this peer for (1 + 0.5 * N) times the block interval
+ // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to
+ // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link
+ // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes
+ // to unreasonably increase our timeout.
+ if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (2 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) {
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id);
+ pto->fDisconnect = true;
+ }
//
// Message: getdata (blocks)
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index fdb9bcd6d6..4c00aca985 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -20,6 +20,7 @@
class AmountSpinBox: public QAbstractSpinBox
{
Q_OBJECT
+
public:
explicit AmountSpinBox(QWidget *parent):
QAbstractSpinBox(parent),
@@ -72,23 +73,6 @@ public:
setValue(val);
}
- StepEnabled stepEnabled() const
- {
- StepEnabled rv = 0;
- if(text().isEmpty()) // Allow step-up with empty field
- return StepUpEnabled;
- bool valid = false;
- CAmount val = value(&valid);
- if(valid)
- {
- if(val > 0)
- rv |= StepDownEnabled;
- if(val < BitcoinUnits::maxMoney())
- rv |= StepUpEnabled;
- }
- return rv;
- }
-
void setDisplayUnit(int unit)
{
bool valid = false;
@@ -139,6 +123,7 @@ public:
}
return cachedMinimumSizeHint;
}
+
private:
int currentUnit;
CAmount singleStep;
@@ -179,6 +164,26 @@ protected:
return QAbstractSpinBox::event(event);
}
+ StepEnabled stepEnabled() const
+ {
+ if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
+ return StepNone;
+ if (text().isEmpty()) // Allow step-up with empty field
+ return StepUpEnabled;
+
+ StepEnabled rv = 0;
+ bool valid = false;
+ CAmount val = value(&valid);
+ if(valid)
+ {
+ if(val > 0)
+ rv |= StepDownEnabled;
+ if(val < BitcoinUnits::maxMoney())
+ rv |= StepUpEnabled;
+ }
+ return rv;
+ }
+
signals:
void valueChanged();
};
diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui
index 40b2da3228..92f6430c51 100644
--- a/src/qt/forms/signverifymessagedialog.ui
+++ b/src/qt/forms/signverifymessagedialog.ui
@@ -30,7 +30,7 @@
<item>
<widget class="QLabel" name="infoLabel_SM">
<property name="text">
- <string>You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</string>
+ <string>You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
@@ -237,7 +237,7 @@
<item>
<widget class="QLabel" name="infoLabel_VM">
<property name="text">
- <string>Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack.</string>
+ <string>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 66331ee4b4..8703966606 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -521,8 +521,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins
const payments::PaymentDetails& details = request.getDetails();
// Payment request network matches client network?
- if (details.network() != Params().NetworkIDString())
- {
+ if (!verifyNetwork(request.getDetails())) {
emit message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."),
CClientUIInterface::MSG_ERROR);
@@ -745,3 +744,15 @@ void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
// currently we don't futher process or store the paymentACK message
emit message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL);
}
+
+bool PaymentServer::verifyNetwork(const payments::PaymentDetails& requestDetails)
+{
+ bool fVerified = requestDetails.network() == Params().NetworkIDString();
+ if (!fVerified) {
+ qWarning() << QString("PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
+ .arg(__func__)
+ .arg(QString::fromStdString(requestDetails.network()))
+ .arg(QString::fromStdString(Params().NetworkIDString()));
+ }
+ return fVerified;
+}
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 2fc24395f6..9330e9a89c 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -91,6 +91,9 @@ public:
// This is now public, because we use it in paymentservertests.cpp
static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request);
+ // Verify that the payment request network matches the client network
+ static bool verifyNetwork(const payments::PaymentDetails& requestDetails);
+
signals:
// Fired when a valid payment request is received
void receivedPaymentRequest(SendCoinsRecipient);
diff --git a/src/qt/test/paymentrequestdata.h b/src/qt/test/paymentrequestdata.h
index 37544cdebb..67c9a41e61 100644
--- a/src/qt/test/paymentrequestdata.h
+++ b/src/qt/test/paymentrequestdata.h
@@ -6,16 +6,16 @@
// Data for paymentservertests.cpp
//
-// Base64/DER-encoded fake certificate authority certificate.
+// Base64/DER-encoded fake certificate authority certificates.
// Convert pem to base64/der with:
-// cat file.pem | openssl x509 -inform PEM -outform DER | openssl enc -base64
-//
+// openssl x509 -in cert.pem -inform PEM -outform DER | openssl enc -base64
+
// Serial Number: 10302349811211485352 (0x8ef94c91b112c0a8)
// Issuer: CN=PaymentRequest Test CA
// Subject: CN=PaymentRequest Test CA
// Not Valid After : Dec 8 16:37:24 2022 GMT
//
-const char* caCert_BASE64 =
+const char* caCert1_BASE64 =
"\
MIIB0DCCATmgAwIBAgIJAI75TJGxEsCoMA0GCSqGSIb3DQEBCwUAMCExHzAdBgNV\
BAMTFlBheW1lbnRSZXF1ZXN0IFRlc3QgQ0EwHhcNMTIxMjEwMTYzNzI0WhcNMjIx\
@@ -29,11 +29,36 @@ RtQcf0AJ9olzUMY4syehxbzUJP6aeXhZEYiMvdvcv9D55clq6+WLLlNT3jBgAaVn\
p3waRjPD4bUX3nv+ojz5s4puw7Qq5QUZlhGsMzPvwDGCmZkL\
";
+// Serial Number: f0:da:97:e4:38:d7:64:16
+// Issuer: CN=PaymentRequest Test CA
+// Subject: CN=PaymentRequest Test CA
+// Not Valid After : Jan 8 18:21:06 2025 GMT
+//
+const char* caCert2_BASE64 =
+"\
+MIIC1TCCAb2gAwIBAgIJAPDal+Q412QWMA0GCSqGSIb3DQEBCwUAMCExHzAdBgNV\
+BAMMFlBheW1lbnRSZXF1ZXN0IFRlc3QgQ0EwHhcNMTUwMTExMTgyMTA2WhcNMjUw\
+MTA4MTgyMTA2WjAhMR8wHQYDVQQDDBZQYXltZW50UmVxdWVzdCBUZXN0IENBMIIB\
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1S9wVLfTplJuT/1OaaBgl/Mb\
+I392v8S9kHbzYz7B4OTMslaO7piz0v3SO3TKMh0dswjiRdHrIgpO7XdIUQiU/ugg\
+xDw0kuNehfz1ycaGedlFFtFHTNXqLyIUF3dlwHhQwaomM6RXoJmxLny5BhYHEcmk\
+yWwr3Cdjd9gAZpblugVJB9C1e40uyL8ao4PHdLzOqO27iSe6riP8SwwisJZEbMaz\
+AZpgNEEMbIXPJEFvm5HTRXSMtQCOTSZYMFF0M2yrtmlECnz7hWP19b9bcoDzZQB4\
+ylIsFG/7q2jV7MC/e2STZv+niJiHL08RUdoFpAgzaxMgqj63C7B55HgNDNHJYQID\
+AQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBGejPxLxj9\
++crv6gUeEBMZPiUx7pUgcI22Wm5yymP96B4fwI3Y0DBehq20d76vbWGPN17Z6pH3\
+ge7PVY1SYqXtS6hXTo4olCm/BZADli+2Bs2xCiaa+Ltve4ufVej+bKJXN/YnrhvO\
+Kq+klQkuuHywU+GJV/NQeBqToIrSOBgi477NgLFCCCmmx2QWsxHoCFGfuRCBVseT\
+z2k/tMuALCDXGeZBRPTsGHu1y4cj84swAeoDK5QSQcI+Ub7GKc+zkoj02sdDLiMo\
+3wokYPcIy47oclhmb4xubHc+y7nF610yZBoC/zgbhbawnZ65hDDWkdQ/SVAnWZD7\
+9PFfmNnYPTQH\
+";
+
//
// This payment request validates directly against the
-// above certificate authority.
+// caCert1 certificate authority.
//
-const char* paymentrequest1_BASE64 =
+const char* paymentrequest1_cert1_BASE64 =
"\
Egt4NTA5K3NoYTI1NhrxAwruAzCCAeowggFToAMCAQICAQEwDQYJKoZIhvcNAQEL\
BQAwITEfMB0GA1UEAxMWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xMjEyMTAx\
@@ -55,7 +80,7 @@ SiWVbw0tX/68iSQEGGfh9n6ee/8Myb3ICdw=\
//
// Signed, but expired, merchant cert in the request
//
-const char* paymentrequest2_BASE64 =
+const char* paymentrequest2_cert1_BASE64 =
"\
Egt4NTA5K3NoYTI1NhrsAwrpAzCCAeUwggFOoAMCAQICAQMwDQYJKoZIhvcNAQEL\
BQAwITEfMB0GA1UEAxMWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xMzAyMjMy\
@@ -75,9 +100,9 @@ tejrSPOBNSJ3Mi/q5u2Yl4gJZY2b\
";
//
-// 10-long chain, all intermediates valid
+// 10-long certificate chain, all intermediates valid
//
-const char* paymentrequest3_BASE64 =
+const char* paymentrequest3_cert1_BASE64 =
"\
Egt4NTA5K3NoYTI1Nhq8JAr/AzCCAfswggFkoAMCAQICAQEwDQYJKoZIhvcNAQEL\
BQAwPzEUMBIGA1UEAwwLdGVzdGNhOC5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVx\
@@ -184,9 +209,9 @@ chhR/aHOuEMTxmc12K4rNlgYtHCsxLP9zd+6u0cva3TucZ6EzS8PKEib/+r12/52\
";
//
-// Long chain, with an invalid (expired) cert in the middle
+// Long certificate chain, with an expired certificate in the middle
//
-const char* paymentrequest4_BASE64 =
+const char* paymentrequest4_cert1_BASE64 =
"\
Egt4NTA5K3NoYTI1NhqeJAr/AzCCAfswggFkoAMCAQICAQEwDQYJKoZIhvcNAQEL\
BQAwPzEUMBIGA1UEAwwLdGVzdGNhOC5vcmcxJzAlBgNVBAoMHlBheW1lbnQgUmVx\
@@ -291,7 +316,10 @@ HXQjsfdR58qZQS9CS5DAtRUf0R8+43/wijO/hb49VNaNXmY+/cPHMkahP2aV3tZi\
FAyZblLik9A7ZvF+UsjeFQiHB5wzWQvbqk5wQ4yabHIXoYv/E0q+eQ==\
";
-const char* paymentrequest5_BASE64 =
+//
+// Validly signed, but by a CA not in our root CA list
+//
+const char* paymentrequest5_cert1_BASE64 =
"\
Egt4NTA5K3NoYTI1NhrxAwruAzCCAeowggFToAMCAQICAQEwDQYJKoZIhvcNAQEL\
BQAwITEfMB0GA1UEAxMWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xMzA0MTkx\
@@ -309,3 +337,27 @@ ssymvca1S/1KeM3n8Ydi2fi1JUzAAr59xPvNJRUeqCLP9upHn5z7br3P12Oz9A20\
5/4wL4ClPRPVnOHgij0bEg+y0tGESqmF1rfOfXDszlo2U92wCxS07kq79YAZJ1Zo\
XYh860/Q4wvc7lfiTe+dXBzPKAKhMy91yETY\
";
+
+//
+// Contains a testnet paytoaddress, so payment request network doesn't match client network
+//
+const char* paymentrequest1_cert2_BASE64 =
+"\
+Egt4NTA5K3NoYTI1NhrQBArNBDCCAkkwggExoAMCAQICAQEwDQYJKoZIhvcNAQEL\
+BQAwITEfMB0GA1UEAwwWUGF5bWVudFJlcXVlc3QgVGVzdCBDQTAeFw0xNTAxMTEx\
+ODIxMDhaFw0yNTAxMDgxODIxMDhaMCExHzAdBgNVBAMMFlBheW1lbnRSZXF1ZXN0\
+IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMsZqzkzeBGo+i2N\
+mUak3Ciodr1V7S062VOy7N0OQYNDQHYkgDFAUET7cEb5VJaHPv5m3ppTBpU9xBcf\
+wbHHUt4VjA+mhRmYrl1khjvZM+X8kEqvWn20BtcM9R6r0yIYec8UERDDHBleL/P8\
+RkxEnVLjYTV9zigCXfMsgYb3EQShAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ\
+KoZIhvcNAQELBQADggEBABUJpl3QCqsoDSxAsQdV6zKT4VGV76AzoGj7etQsQY+r\
++S26VfWh/fMobEzuxFChr0USgLJ6FoK78hAtoZvt1lrye9yqFv/ig3WLWsJKWHHb\
+3RT6oR03CIwZXFSUasi08QDVLxafwsU5OMcPLucF3a1lRL1ccYrNgVCCx1+X7Bos\
+tIgDGRQQ4AyoHTcfVd2hEGeUv7k14mOxFsAp6851yosHq9Q2kwmdH+rHEJbjof87\
+yyKLagc4owyXBZYkQmkeHWCNqnuRmO5vUsfVb0UUrkD64o7Th/NjwooA7SCiUXl6\
+dfygT1b7ggpx7GC+sP2DsIM47IAZ55drjqX5u2f+Ba0iPQoEdGVzdBIhCIDWwowE\
+Ehl2qRQErGqUUwSsaMpDvWIaGnJGNQqi8oisGNeMy6UFKgxKdXN0IFRlc3Rpbmcq\
+gAFwThsozZxkZxzCn4R8WxNiLFV6m0ye9fEtSbolfaW+EjBMpO03lr/dwNnrclhg\
+ew+A05xfZztrAt16XKEY7qKJ/eY2nLd0fVAIu/nIt+7/VYVXT83zLrWc150aRS7W\
+AdJbL3JOJLs6Eyp5zrPbfI8faRttFAdONKDrJgIpuW1E3g==\
+";
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index 70254cd75d..e6cdb58ef5 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -65,38 +65,44 @@ void PaymentServerTests::paymentServerTests()
OptionsModel optionsModel;
PaymentServer* server = new PaymentServer(NULL, false);
X509_STORE* caStore = X509_STORE_new();
- X509_STORE_add_cert(caStore, parse_b64der_cert(caCert_BASE64));
+ X509_STORE_add_cert(caStore, parse_b64der_cert(caCert1_BASE64));
PaymentServer::LoadRootCAs(caStore);
server->setOptionsModel(&optionsModel);
server->uiReady();
- // Now feed PaymentRequests to server, and observe signals it produces:
- std::vector<unsigned char> data = DecodeBase64(paymentrequest1_BASE64);
- SendCoinsRecipient r = handleRequest(server, data);
+ std::vector<unsigned char> data;
+ SendCoinsRecipient r;
QString merchant;
+
+ // Now feed PaymentRequests to server, and observe signals it produces
+
+ // This payment request validates directly against the
+ // caCert1 certificate authority:
+ data = DecodeBase64(paymentrequest1_cert1_BASE64);
+ r = handleRequest(server, data);
r.paymentRequest.getMerchant(caStore, merchant);
QCOMPARE(merchant, QString("testmerchant.org"));
- // Version of the above, with an expired certificate:
- data = DecodeBase64(paymentrequest2_BASE64);
+ // Signed, but expired, merchant cert in the request:
+ data = DecodeBase64(paymentrequest2_cert1_BASE64);
r = handleRequest(server, data);
r.paymentRequest.getMerchant(caStore, merchant);
QCOMPARE(merchant, QString(""));
- // Long certificate chain:
- data = DecodeBase64(paymentrequest3_BASE64);
+ // 10-long certificate chain, all intermediates valid:
+ data = DecodeBase64(paymentrequest3_cert1_BASE64);
r = handleRequest(server, data);
r.paymentRequest.getMerchant(caStore, merchant);
QCOMPARE(merchant, QString("testmerchant8.org"));
// Long certificate chain, with an expired certificate in the middle:
- data = DecodeBase64(paymentrequest4_BASE64);
+ data = DecodeBase64(paymentrequest4_cert1_BASE64);
r = handleRequest(server, data);
r.paymentRequest.getMerchant(caStore, merchant);
QCOMPARE(merchant, QString(""));
// Validly signed, but by a CA not in our root CA list:
- data = DecodeBase64(paymentrequest5_BASE64);
+ data = DecodeBase64(paymentrequest5_cert1_BASE64);
r = handleRequest(server, data);
r.paymentRequest.getMerchant(caStore, merchant);
QCOMPARE(merchant, QString(""));
@@ -104,11 +110,39 @@ void PaymentServerTests::paymentServerTests()
// Try again with no root CA's, verifiedMerchant should be empty:
caStore = X509_STORE_new();
PaymentServer::LoadRootCAs(caStore);
- data = DecodeBase64(paymentrequest1_BASE64);
+ data = DecodeBase64(paymentrequest1_cert1_BASE64);
r = handleRequest(server, data);
r.paymentRequest.getMerchant(caStore, merchant);
QCOMPARE(merchant, QString(""));
+ // Load second root certificate
+ caStore = X509_STORE_new();
+ X509_STORE_add_cert(caStore, parse_b64der_cert(caCert2_BASE64));
+ PaymentServer::LoadRootCAs(caStore);
+
+ QByteArray byteArray;
+
+ // For the tests below we just need the payment request data from
+ // paymentrequestdata.h parsed + stored in r.paymentRequest.
+ //
+ // These tests require us to bypass the following normal client execution flow
+ // shown below to be able to explicitly just trigger a certain condition!
+ //
+ // handleRequest()
+ // -> PaymentServer::eventFilter()
+ // -> PaymentServer::handleURIOrFile()
+ // -> PaymentServer::readPaymentRequestFromFile()
+ // -> PaymentServer::processPaymentRequest()
+
+ // Contains a testnet paytoaddress, so payment request network doesn't match client network:
+ data = DecodeBase64(paymentrequest1_cert2_BASE64);
+ byteArray = QByteArray((const char*)&data[0], data.size());
+ r.paymentRequest.parse(byteArray);
+ // Ensure the request is initialized, because network "main" is default, even for
+ // uninizialized payment requests and that will fail our test here.
+ QVERIFY(r.paymentRequest.IsInitialized());
+ QCOMPARE(PaymentServer::verifyNetwork(r.paymentRequest.getDetails()), false);
+
// Just get some random data big enough to trigger BIP70 DoS protection
unsigned char randData[BIP70_MAX_PAYMENTREQUEST_SIZE + 1];
GetRandBytes(randData, sizeof(randData));