Merge pull request #3284 from PastaPastaPasta/backports-0.16-pr3

Backports 0.16 pr3
This commit is contained in:
UdjinM6 2020-01-16 14:24:11 +03:00 committed by GitHub
commit 7e85f09007
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1286 additions and 661 deletions

View File

@ -1,37 +0,0 @@
Bag Attributes
friendlyName: Developer ID Application: BITCOIN FOUNDATION, INC., THE
localKeyID: 6B 9C 6C A8 A5 73 70 70 E2 57 A3 49 D8 62 FB 97 C7 A5 5D 5E
subject=/UID=PBV4GLS9J4/CN=Developer ID Application: BITCOIN FOUNDATION, INC., THE/OU=PBV4GLS9J4/O=BITCOIN FOUNDATION, INC., THE/C=US
issuer=/CN=Developer ID Certification Authority/OU=Apple Certification Authority/O=Apple Inc./C=US
-----BEGIN CERTIFICATE-----
MIIFhzCCBG+gAwIBAgIIJ0r1rumyfZAwDQYJKoZIhvcNAQELBQAweTEtMCsGA1UE
AwwkRGV2ZWxvcGVyIElEIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQL
DB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUg
SW5jLjELMAkGA1UEBhMCVVMwHhcNMTMwMTEwMjIzOTAxWhcNMTgwMTExMjIzOTAx
WjCBqDEaMBgGCgmSJomT8ixkAQEMClBCVjRHTFM5SjQxQDA+BgNVBAMMN0RldmVs
b3BlciBJRCBBcHBsaWNhdGlvbjogQklUQ09JTiBGT1VOREFUSU9OLCBJTkMuLCBU
SEUxEzARBgNVBAsMClBCVjRHTFM5SjQxJjAkBgNVBAoMHUJJVENPSU4gRk9VTkRB
VElPTiwgSU5DLiwgVEhFMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALTd5zURuZVoJviusr119aktXksenb9IN9vq6kBbq38vxEk7
9wkKMES2XfBRh0HxcEizGzhMNy5OCXuTLMaNMihYdfwYSoBoR2foEU+6kjPUnyJ4
dQBFLJZJr5/QeQmALmYHEgZ6lwXFD2lU8t92340zeJ4y5LZw5pcEHtH9IummYDut
OGCkCGXDcjL+5nHhNScJiXHhswM+62o6XXsQiP6EWbM1CsgrGTNLtaa0U/UvVDwE
79YKklSC5Bog2LD0jBcTuveI66mFzqu++L9X9u+ZArtebwCl7BPNQ+uboYy5uV2d
zf8lpNNZLfXCFjoLe9bLICKfZ7ub9V5aC8+GhckCAwEAAaOCAeEwggHdMD4GCCsG
AQUFBwEBBDIwMDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuYXBwbGUuY29tL29j
c3AtZGV2aWQwMTAdBgNVHQ4EFgQUa5xsqKVzcHDiV6NJ2GL7l8elXV4wDAYDVR0T
AQH/BAIwADAfBgNVHSMEGDAWgBRXF+2iz9x8mKEQ4Py+hy0s8uMXVDCCAQ4GA1Ud
IASCAQUwggEBMIH+BgkqhkiG92NkBQEwgfAwKAYIKwYBBQUHAgEWHGh0dHA6Ly93
d3cuYXBwbGUuY29tL2FwcGxlY2EwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ug
b24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRh
bmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNv
bmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmlj
YXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud
JQEB/wQMMAoGCCsGAQUFBwMDMBMGCiqGSIb3Y2QGAQ0BAf8EAgUAMA0GCSqGSIb3
DQEBCwUAA4IBAQAfJ0BjID/1dS2aEeVyhAzPzCBjG8vm0gDf+/qfwRn3+yWeL9vS
nMdbilwM48IyQWTagjGGcojbsAd/vE4N7NhQyHInoCllNoeor1I5xx+blTaGRBK+
dDhJbbdlGCjsLnH/BczGZi5fyEJds9lUIrp1hJidRcUKO76qb/9gc6qNZpl1vH5k
lDUuJYt7YhAs+L6rTXDyqcK9maeQr0gaOPsRRAQLLwiQCorPeMTUNsbVMdMwZYJs
R+PxiAnk+nyi7rfiFvPoASAYUuI6OzYL/Fa6QU4/gYyPgic944QYVkaQBnc0vEP1
nXq6LGKwgVGcqJnkr/E2kui5gJoV5C3qll3e
-----END CERTIFICATE-----

View File

@ -1,37 +0,0 @@
Bag Attributes
friendlyName: The Bitcoin Foundation, Inc.'s COMODO CA Limited ID
localKeyID: 8C 94 64 E3 B5 B0 41 89 5B 89 B0 57 CC 74 B9 44 E5 B2 92 66
subject=/C=US/postalCode=98104-1444/ST=WA/L=Seattle/street=Suite 300/street=71 Columbia St/O=The Bitcoin Foundation, Inc./CN=The Bitcoin Foundation, Inc.
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2
-----BEGIN CERTIFICATE-----
MIIFeDCCBGCgAwIBAgIRAJVYMd+waOER7lUqtiz3M2IwDQYJKoZIhvcNAQEFBQAw
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxITAfBgNV
BAMTGENPTU9ETyBDb2RlIFNpZ25pbmcgQ0EgMjAeFw0xMzAxMTYwMDAwMDBaFw0x
NDAxMTYyMzU5NTlaMIG8MQswCQYDVQQGEwJVUzETMBEGA1UEEQwKOTgxMDQtMTQ0
NDELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAkMCVN1aXRl
IDMwMDEXMBUGA1UECQwONzEgQ29sdW1iaWEgU3QxJTAjBgNVBAoMHFRoZSBCaXRj
b2luIEZvdW5kYXRpb24sIEluYy4xJTAjBgNVBAMMHFRoZSBCaXRjb2luIEZvdW5k
YXRpb24sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChUwLD
u/hu5aFZ/n11B27awONaaDrmHm0pamiWHb01yL4JmTBtaLCrSftF8RhCscQ8jpI0
UG1Cchmay0e3zH5o5XRs0H9C3x+SM5ozms0TWDmAYiB8aQEghsGovDk0D2nyTQeK
Q0xqyCh0m8ZPOnMnYrakHEmF6WvhLdJvI6Od4KIwbKxgN17cPFIfLVsZ7GrzmmbU
Gdi4wSQCHy5rxzvBxho8Qq/SfBl93uOMUrqOHjOUAPhNuTJG3t/MdhU8Zp24s29M
abHtYkT9W86hMjIiI8RTAR+WHKVglx9SB0cjDabXN8SZ3gME0+H++LyzlySHT8sI
ykepojZ7UBRgp9w3AgMBAAGjggGzMIIBrzAfBgNVHSMEGDAWgBQexbEsfYfaAmh8
JbwMB4Q/ts/e8TAdBgNVHQ4EFgQUfPf+ZyDWl/4LH0Y5BuJTelkRd/EwDgYDVR0P
AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ
YIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQMCMCswKQYI
KwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEEGA1UdHwQ6
MDgwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWdu
aW5nQ0EyLmNybDByBggrBgEFBQcBAQRmMGQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9j
cnQuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWduaW5nQ0EyLmNydDAkBggrBgEF
BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCgGA1UdEQQhMB+BHWxpbmRz
YXlAYml0Y29pbmZvdW5kYXRpb24ub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAqibjo
D4HG5XSIIMCmYE5RgQBSEAJfI+EZERk1G9F83ZUWr0yNRZCw4O+RaM7xQhvJhEoD
G2kpk/q2bNOc71/VyZ6SrE1JRVUON41/Flhz4M6cP0BclTicXvh+efVwqZhIz+ws
UxF2hvC/1Xx6rqI7NYAlOYXk2MSUq3HREo+gWUPKM8em4MZZV/7XCH4QbsfxOl1J
xS6EOQmV8hfUN4KRXI5WfGUmedBxq7dM0RSJOSQl8fq2f+JjRLfjQwQucy7LDY+y
pRTsL2TdQV/DuDuI3s0NHRGznQNddoX5jqpXhSQFAAdgrhN1gGkWaaTPzr9IF2TG
qgr6PEp9tIYC+MbM
-----END CERTIFICATE-----

View File

@ -1,46 +0,0 @@
Code-signing private key notes
==
The private keys for these certificates were generated on Gavin's main work machine,
following the certificate authority's recommendations for generating certificate
signing requests.
For OSX, the private key was generated by Keychain.app on Gavin's main work machine.
The key and certificate is in a separate, passphrase-protected keychain file that is
unlocked to sign the Bitcoin-Qt.app bundle.
For Windows, the private key was generated by Firefox running on Gavin's main work machine.
The key and certificate were exported into a separate, passphrase-protected PKCS#12 file, and
then deleted from Firefox's keystore. The exported file is used to sign the Windows setup.exe.
Threat analysis
--
Gavin is a single point of failure. He could be coerced to divulge the secret signing keys,
allowing somebody to distribute a Bitcoin-Qt.app or bitcoin-qt-setup.exe with a valid
signature but containing a malicious binary.
Or the machine Gavin uses to sign the binaries could be compromised, either remotely or
by breaking in to his office, allowing the attacker to get the private key files and then
install a keylogger to get the passphrase that protects them.
Threat Mitigation
--
"Air gapping" the machine used to do the signing will not work, because the signing
process needs to access a timestamp server over the network. And it would not
prevent the "rubber hose cryptography" threat (coercing Gavin to sign a bad binary
or divulge the private keys).
Windows binaries are reproducibly 'gitian-built', and the setup.exe file created
by the NSIS installer system is a 7zip archive, so you could check to make sure
that the bitcoin-qt.exe file inside the installer had not been tampered with.
However, an attacker could modify the installer's code, so when the setup.exe
was run it compromised users' systems. A volunteer to write an auditing tool
that checks the setup.exe for tampering, and checks the files in it against
the list of Gitian signatures, is needed.
The long-term solution is something like the 'gitian downloader' system, which
uses signatures from multiple developers to determine whether or not a binary
should be trusted. However, that just pushes the problem to "how will
non-technical users securely get the Gitian downloader code to start?"

View File

@ -214,6 +214,11 @@ protected:
public:
CCoinsViewCache(CCoinsView *baseIn);
/**
* By deleting the copy constructor, we prevent accidentally using it when one intends to create a cache on top of a base cache.
*/
CCoinsViewCache(const CCoinsViewCache &) = delete;
// Standard CCoinsView methods
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
@ -290,11 +295,6 @@ public:
private:
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
/**
* By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache.
*/
CCoinsViewCache(const CCoinsViewCache &);
};
//! Utility function to add all of a transaction's outputs to a cache.

View File

@ -540,6 +540,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED));
strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE));
strUsage += HelpMessageOpt("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used");
strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE));
strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages");
strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages");
@ -2210,6 +2211,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
connOptions.m_msgproc = peerLogic.get();
connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = gArgs.GetArgs("-addnode");
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
@ -2240,9 +2242,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
connOptions.vWhitelistedRange.push_back(subnet);
}
if (gArgs.IsArgSet("-seednode")) {
connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
}
// Initiate outbound connections unless connect=0
connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect");
if (!connOptions.m_use_addrman_outgoing) {

View File

@ -111,7 +111,7 @@ void CActiveMasternodeManager::Init()
// Check socket connectivity
LogPrintf("CActiveDeterministicMasternodeManager::Init -- Checking inbound connection to '%s'\n", activeMasternodeInfo.service.ToString());
SOCKET hSocket;
bool fConnected = ConnectSocket(activeMasternodeInfo.service, hSocket, nConnectTimeout) && IsSelectableSocket(hSocket);
bool fConnected = ConnectSocketDirectly(activeMasternodeInfo.service, hSocket, nConnectTimeout) && IsSelectableSocket(hSocket);
CloseSocket(hSocket);
if (!fConnected) {

View File

@ -145,11 +145,10 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
const int64_t nOneWeek = 7*24*60*60;
std::vector<CAddress> vSeedsOut;
vSeedsOut.reserve(vSeedsIn.size());
for (std::vector<SeedSpec6>::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i)
{
for (const auto& seed_in : vSeedsIn) {
struct in6_addr ip;
memcpy(&ip, i->addr, sizeof(ip));
CAddress addr(CService(ip, i->port), NODE_NETWORK);
memcpy(&ip, seed_in.addr, sizeof(ip));
CAddress addr(CService(ip, seed_in.port), NODE_NETWORK);
addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
vSeedsOut.push_back(addr);
}
@ -309,18 +308,22 @@ bool IsReachable(const CNetAddr& addr)
CNode* CConnman::FindNode(const CNetAddr& ip)
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
if ((CNetAddr)pnode->addr == ip)
return (pnode);
for (CNode* pnode : vNodes) {
if ((CNetAddr)pnode->addr == ip) {
return pnode;
}
}
return nullptr;
}
CNode* CConnman::FindNode(const CSubNet& subNet)
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
if (subNet.Match((CNetAddr)pnode->addr))
return (pnode);
for (CNode* pnode : vNodes) {
if (subNet.Match((CNetAddr)pnode->addr)) {
return pnode;
}
}
return nullptr;
}
@ -329,7 +332,7 @@ CNode* CConnman::FindNode(const std::string& addrName)
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
if (pnode->GetAddrName() == addrName) {
return (pnode);
return pnode;
}
}
return nullptr;
@ -338,9 +341,11 @@ CNode* CConnman::FindNode(const std::string& addrName)
CNode* CConnman::FindNode(const CService& addr)
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
if((CService)pnode->addr == addr)
return (pnode);
for (CNode* pnode : vNodes) {
if ((CService)pnode->addr == addr) {
return pnode;
}
}
return nullptr;
}
@ -397,19 +402,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
}
// Connect
SOCKET hSocket;
bool proxyConnectionFailed = false;
if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) :
ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed))
{
if (!IsSelectableSocket(hSocket)) {
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
CloseSocket(hSocket);
// Resolve
const int default_port = Params().GetDefaultPort();
if (pszDest) {
std::vector<CService> resolved;
if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE);
if (!addrConnect.IsValid()) {
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s", addrConnect.ToString(), pszDest);
return nullptr;
}
if (pszDest && addrConnect.IsValid()) {
// It is possible that we already have a connection to the IP/port pszDest resolved to.
// In that case, drop the connection that was just created, and return the existing CNode instead.
// Also store the name we used to connect in that CNode, so that future FindNode() calls to that
@ -419,13 +421,40 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (pnode)
{
pnode->MaybeSetAddrName(std::string(pszDest));
CloseSocket(hSocket);
LogPrintf("Failed to open new connection, already connected\n");
return nullptr;
}
}
}
// Connect
bool connected = false;
SOCKET hSocket;
proxyType proxy;
if (addrConnect.IsValid()) {
bool proxyConnectionFailed = false;
if (GetProxy(addrConnect.GetNetwork(), proxy))
connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed);
else // no proxy needed (none set for target network)
connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout);
if (!proxyConnectionFailed) {
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
// the proxy, mark this as an attempt.
addrman.Attempt(addrConnect, fCountFailure);
}
} else if (pszDest && GetNameProxy(proxy)) {
std::string host;
int port = default_port;
SplitHostPort(std::string(pszDest), port, host);
connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr);
}
if (connected) {
if (!IsSelectableSocket(hSocket)) {
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
CloseSocket(hSocket);
return nullptr;
}
// Add node
NodeId id = GetNewNodeId();
@ -436,10 +465,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
return pnode;
} else if (!proxyConnectionFailed) {
// If connecting to the node failed, and failure is not caused by a problem connecting to
// the proxy, mark this as an attempt.
addrman.Attempt(addrConnect, fCountFailure);
}
return nullptr;
@ -491,10 +516,9 @@ void CConnman::ClearBanned()
bool CConnman::IsBanned(CNetAddr ip)
{
LOCK(cs_setBanned);
for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++)
{
CSubNet subNet = (*it).first;
CBanEntry banEntry = (*it).second;
for (const auto& it : setBanned) {
CSubNet subNet = it.first;
CBanEntry banEntry = it.second;
if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) {
return true;
@ -997,7 +1021,7 @@ bool CConnman::AttemptToEvictConnection()
{
LOCK(cs_vNodes);
for (CNode *node : vNodes) {
for (const CNode* node : vNodes) {
if (node->fWhitelisted)
continue;
if (!node->fInbound)
@ -1095,9 +1119,9 @@ bool CConnman::AttemptToEvictConnection()
// Disconnect from the network group with the most connections
NodeId evicted = vEvictionCandidates.front().id;
LOCK(cs_vNodes);
for(std::vector<CNode*>::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) {
if ((*it)->GetId() == evicted) {
(*it)->fDisconnect = true;
for (CNode* pnode : vNodes) {
if (pnode->GetId() == evicted) {
pnode->fDisconnect = true;
return true;
}
}
@ -1122,7 +1146,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes) {
for (const CNode* pnode : vNodes) {
if (pnode->fInbound) {
nInbound++;
if (!pnode->verifiedProRegTxHash.IsNull()) {
@ -2021,8 +2045,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
{
LOCK(cs_vAddedNodes);
ret.reserve(vAddedNodes.size());
for (const std::string& strAddNode : vAddedNodes)
lAddresses.push_back(strAddNode);
std::copy(vAddedNodes.cbegin(), vAddedNodes.cend(), std::back_inserter(lAddresses));
}
@ -2068,11 +2091,6 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
void CConnman::ThreadOpenAddedConnections()
{
{
LOCK(cs_vAddedNodes);
vAddedNodes = gArgs.GetArgs("-addnode");
}
while (true)
{
CSemaphoreGrant grant(*semAddnode);
@ -2085,11 +2103,9 @@ void CConnman::ThreadOpenAddedConnections()
// the addednodeinfo state might change.
break;
}
// If strAddedNode is an IP/port, decode it immediately, so
// OpenNetworkConnection can detect existing connections to that IP/port.
tried = true;
CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort()));
OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true);
CAddress addr(CService(), NODE_NONE);
OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), false, false, true);
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
return;
}
@ -2796,9 +2812,8 @@ std::vector<CAddress> CConnman::GetAddresses()
bool CConnman::AddNode(const std::string& strNode)
{
LOCK(cs_vAddedNodes);
for(std::vector<std::string>::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) {
if (strNode == *it)
return false;
for (const std::string& it : vAddedNodes) {
if (strNode == it) return false;
}
vAddedNodes.push_back(strNode);
@ -2820,8 +2835,8 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
bool CConnman::AddPendingMasternode(const CService& service)
{
LOCK(cs_vPendingMasternodes);
for(std::vector<CService>::const_iterator it = vPendingMasternodes.begin(); it != vPendingMasternodes.end(); ++it) {
if (service == *it)
for(const auto& s : vPendingMasternodes) {
if (service == s)
return false;
}
@ -2924,9 +2939,11 @@ size_t CConnman::GetNodeCount(NumConnections flags)
return vNodes.size();
int nNum = 0;
for(std::vector<CNode*>::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it)
if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
for (const auto& pnode : vNodes) {
if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) {
nNum++;
}
}
return nNum;
}
@ -2941,8 +2958,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)
vstats.clear();
LOCK(cs_vNodes);
vstats.reserve(vNodes.size());
for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) {
CNode* pnode = *it;
for (CNode* pnode : vNodes) {
vstats.emplace_back();
pnode->copyStats(vstats.back());
}

View File

@ -171,6 +171,7 @@ public:
std::vector<CService> vBinds, vWhiteBinds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
};
void Init(const Options& connOptions) {
@ -187,6 +188,7 @@ public:
nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
vWhitelistedRange = connOptions.vWhitelistedRange;
vAddedNodes = connOptions.m_added_nodes;
}
CConnman(uint64_t seed0, uint64_t seed1);
@ -895,13 +897,11 @@ public:
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false);
~CNode();
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
private:
CNode(const CNode&);
void operator=(const CNode&);
const NodeId id;
const uint64_t nLocalHostNonce;
// Services offered to this peer
const ServiceFlags nLocalServices;

View File

@ -452,7 +452,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
return true;
}
bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout)
bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout)
{
hSocketRet = INVALID_SOCKET;
@ -587,7 +587,7 @@ bool IsProxy(const CNetAddr &addr) {
return false;
}
static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
{
SOCKET hSocket = INVALID_SOCKET;
// first connect to proxy server
@ -611,47 +611,6 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe
hSocketRet = hSocket;
return true;
}
bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
{
proxyType proxy;
if (outProxyConnectionFailed)
*outProxyConnectionFailed = false;
if (GetProxy(addrDest.GetNetwork(), proxy))
return ConnectThroughProxy(proxy, addrDest.ToStringIP(), addrDest.GetPort(), hSocketRet, nTimeout, outProxyConnectionFailed);
else // no proxy needed (none set for target network)
return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout);
}
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed)
{
std::string strDest;
int port = portDefault;
if (outProxyConnectionFailed)
*outProxyConnectionFailed = false;
SplitHostPort(std::string(pszDest), port, strDest);
proxyType proxy;
GetNameProxy(proxy);
std::vector<CService> addrResolved;
if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy(), 256)) {
if (addrResolved.size() > 0) {
addr = addrResolved[GetRand(addrResolved.size())];
return ConnectSocket(addr, hSocketRet, nTimeout);
}
}
addr = CService();
if (!HaveNameProxy())
return false;
return ConnectThroughProxy(proxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed);
}
bool LookupSubNet(const char* pszName, CSubNet& ret)
{
std::string strSubnet(pszName);

View File

@ -45,14 +45,15 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
bool SetNameProxy(const proxyType &addrProxy);
bool HaveNameProxy();
bool GetNameProxy(proxyType &nameProxyOut);
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup);
bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup);
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
CService LookupNumeric(const char *pszName, int portDefault = 0);
bool LookupSubNet(const char *pszName, CSubNet& subnet);
bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = nullptr);
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = nullptr);
bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout);
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed);
/** Return readable error string for a network error code */
std::string NetworkErrorString(int err);
/** Close socket and set hSocket to INVALID_SOCKET */

View File

@ -179,6 +179,7 @@ TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
: buckets(defaultBuckets), bucketMap(defaultBucketMap)
{
decay = _decay;
assert(_scale != 0 && "_scale must be non-zero");
scale = _scale;
confAvg.resize(maxPeriods);
for (unsigned int i = 0; i < maxPeriods; i++) {
@ -417,6 +418,9 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
}
filein >> scale;
if (scale == 0) {
throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
}
}
filein >> avg;
@ -502,6 +506,7 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
}
}
if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period
assert(scale != 0);
unsigned int periodsAgo = blocksAgo / scale;
for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) {
failAvg[i][bucketindex]++;

View File

@ -126,7 +126,6 @@ static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1
return 0;
}
spos = pos;
pos += slen;
/* Ignore leading zeroes in R */
while (rlen > 0 && input[rpos] == 0) {

View File

@ -628,6 +628,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold.");
// how many satoshis the estimated fee can vary per byte we guess wrong
assert(nBytes != 0);
double dFeeVary = (double)nPayFee / nBytes;
QString toolTip4 = tr("Can vary +/- %1 duff(s) per input.").arg(dFeeVary);

View File

@ -27,6 +27,7 @@
#include "util.h"
#include "utilstrencodings.h"
#include "hash.h"
#include "warnings.h"
#include "evo/specialtx.h"
#include "evo/cbtx.h"
@ -1401,8 +1402,11 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
" \"mediantime\": xxxxxx, (numeric) median time for the current best block\n"
" \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n"
" \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
" \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n"
" \"pruned\": xx, (boolean) if the blocks are subject to pruning\n"
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored\n"
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
" \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
" \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
" \"softforks\": [ (array) status of softforks in progress\n"
" {\n"
" \"id\": \"xxxx\", (string) name of softfork\n"
@ -1428,6 +1432,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
" }\n"
" }\n"
" }\n"
" \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getblockchaininfo", "")
@ -1445,7 +1450,24 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast()));
obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip())));
obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
obj.push_back(Pair("size_on_disk", CalculateCurrentUsage()));
obj.push_back(Pair("pruned", fPruneMode));
if (fPruneMode) {
CBlockIndex* block = chainActive.Tip();
assert(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
}
obj.push_back(Pair("pruneheight", block->nHeight));
// if 0, execution bypasses the whole if block.
bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1);
obj.push_back(Pair("automatic_pruning", automatic_pruning));
if (automatic_pruning) {
obj.push_back(Pair("prune_target_size", nPruneTarget));
}
}
const Consensus::Params& consensusParams = Params().GetConsensus();
CBlockIndex* tip = chainActive.Tip();
@ -1464,14 +1486,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.push_back(Pair("softforks", softforks));
obj.push_back(Pair("bip9_softforks", bip9_softforks));
if (fPruneMode)
{
CBlockIndex *block = chainActive.Tip();
while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA))
block = block->pprev;
obj.push_back(Pair("pruneheight", block->nHeight));
}
obj.push_back(Pair("warnings", GetWarnings("statusbar")));
return obj;
}
@ -1779,9 +1794,12 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
"2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n"
"\nResult:\n"
"{\n"
" \"time\": xxxxx, (numeric) The timestamp for the statistics in UNIX format.\n"
" \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n"
" \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n"
" \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window.\n"
" \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n"
" \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n"
" \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n"
" \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getchaintxstats", "")
@ -1791,10 +1809,6 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
if (!request.params[0].isNull()) {
blockcount = request.params[0].get_int();
}
bool havehash = !request.params[1].isNull();
uint256 hash;
if (havehash) {
@ -1819,8 +1833,14 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
assert(pindex != nullptr);
if (blockcount < 1 || blockcount >= pindex->nHeight) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 1 and the block's height");
if (request.params[0].isNull()) {
blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
} else {
blockcount = request.params[0].get_int();
if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1");
}
}
const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount);
@ -1830,7 +1850,14 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("time", (int64_t)pindex->nTime));
ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx));
ret.push_back(Pair("window_block_count", blockcount));
if (blockcount > 0) {
ret.push_back(Pair("window_tx_count", nTxDiff));
ret.push_back(Pair("window_interval", nTimeDiff));
if (nTimeDiff > 0) {
ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff));
}
}
return ret;
}

View File

@ -210,10 +210,11 @@ UniValue getmininginfo(const JSONRPCRequest& request)
" \"currentblocksize\": nnn, (numeric) The last block size\n"
" \"currentblocktx\": nnn, (numeric) The last block transaction\n"
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
" \"errors\": \"...\" (string) Current errors\n"
" \"networkhashps\": nnn, (numeric) The network hashes per second\n"
" \"pooledtx\": n (numeric) The size of the mempool\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
" \"warnings\": \"...\" (string) any network and blockchain warnings\n"
" \"errors\": \"...\" (string) DEPRECATED. Same as warnings. Only shown when dashd is started with -deprecatedrpc=getmininginfo\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getmininginfo", "")
@ -228,10 +229,14 @@ UniValue getmininginfo(const JSONRPCRequest& request)
obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
obj.push_back(Pair("networkhashps", getnetworkhashps(request)));
obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
obj.push_back(Pair("chain", Params().NetworkIDString()));
if (IsDeprecatedRPCEnabled("getmininginfo")) {
obj.push_back(Pair("errors", GetWarnings("statusbar")));
} else {
obj.push_back(Pair("warnings", GetWarnings("statusbar")));
}
return obj;
}
@ -816,6 +821,12 @@ UniValue estimatefee(const JSONRPCRequest& request)
+ HelpExampleCli("estimatefee", "6")
);
if (!IsDeprecatedRPCEnabled("estimatefee")) {
throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee is deprecated and will be fully removed in v0.17. "
"To use estimatefee in v0.16, restart dashd with -deprecatedrpc=estimatefee.\n"
"Projects should transition to using estimatesmartfee before upgrading to v0.17");
}
RPCTypeCheck(request.params, {UniValue::VNUM});
int nBlocks = request.params[0].get_int();

View File

@ -457,7 +457,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
" }\n"
" ,...\n"
" ]\n"
" \"warnings\": \"...\" (string) any network warnings\n"
" \"warnings\": \"...\" (string) any network and blockchain warnings\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getnetworkinfo", "")

View File

@ -57,6 +57,7 @@ enum RPCErrorCode
RPC_VERIFY_REJECTED = -26, //!< Transaction or block was rejected by network rules
RPC_VERIFY_ALREADY_IN_CHAIN = -27, //!< Transaction already in chain
RPC_IN_WARMUP = -28, //!< Client still warming up
RPC_METHOD_DEPRECATED = -32, //!< RPC method is deprecated
//! Aliases for backward compatibility
RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR,

View File

@ -906,9 +906,14 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
} else {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
}
}
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);

View File

@ -446,6 +446,13 @@ void JSONRPCRequest::parse(const UniValue& valRequest)
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
}
bool IsDeprecatedRPCEnabled(const std::string& method)
{
const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
}
static UniValue JSONRPCExecOne(JSONRPCRequest jreq, const UniValue& req)
{
UniValue rpc_result(UniValue::VOBJ);

View File

@ -168,6 +168,8 @@ public:
bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
};
bool IsDeprecatedRPCEnabled(const std::string& method);
extern CRPCTable tableRPC;
/**

View File

@ -1156,11 +1156,9 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
assert(nIn < txTo.vin.size());
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
if (nIn >= txTo.vin.size()) {
// nIn out of range
return one;
}
// Check for invalid use of SIGHASH_SINGLE
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {

View File

@ -69,6 +69,8 @@ const char* ScriptErrorString(const ScriptError serror)
return "NOPx reserved for soft-fork upgrades";
case SCRIPT_ERR_PUBKEYTYPE:
return "Public key is neither compressed or uncompressed";
case SCRIPT_ERR_CLEANSTACK:
return "Extra items left on stack after execution";
case SCRIPT_ERR_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;

View File

@ -412,10 +412,6 @@ public:
class CAutoFile
{
private:
// Disallow copies
CAutoFile(const CAutoFile&);
CAutoFile& operator=(const CAutoFile&);
const int nType;
const int nVersion;
@ -432,6 +428,10 @@ public:
fclose();
}
// Disallow copies
CAutoFile(const CAutoFile&) = delete;
CAutoFile& operator=(const CAutoFile&) = delete;
void fclose()
{
if (file) {
@ -521,10 +521,6 @@ public:
class CBufferedFile
{
private:
// Disallow copies
CBufferedFile(const CBufferedFile&);
CBufferedFile& operator=(const CBufferedFile&);
const int nType;
const int nVersion;
@ -566,6 +562,10 @@ public:
fclose();
}
// Disallow copies
CBufferedFile(const CBufferedFile&) = delete;
CBufferedFile& operator=(const CBufferedFile&) = delete;
int GetVersion() const { return nVersion; }
int GetType() const { return nType; }

View File

@ -50,6 +50,9 @@ public:
Arena(void *base, size_t size, size_t alignment);
virtual ~Arena();
Arena(const Arena& other) = delete; // non construction-copyable
Arena& operator=(const Arena&) = delete; // non copyable
/** Memory statistics. */
struct Stats
{
@ -85,9 +88,6 @@ public:
*/
bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; }
private:
Arena(const Arena& other) = delete; // non construction-copyable
Arena& operator=(const Arena&) = delete; // non copyable
/** Map of chunk address to chunk information. This class makes use of the
* sorted order to merge previous and next chunks during deallocation.
*/
@ -153,6 +153,9 @@ public:
explicit LockedPool(std::unique_ptr<LockedPageAllocator> allocator, LockingFailed_Callback lf_cb_in = nullptr);
~LockedPool();
LockedPool(const LockedPool& other) = delete; // non construction-copyable
LockedPool& operator=(const LockedPool&) = delete; // non copyable
/** Allocate size bytes from this arena.
* Returns pointer on success, or 0 if memory is full or
* the application tried to allocate 0 bytes.
@ -168,9 +171,6 @@ public:
/** Get pool usage statistics */
Stats stats() const;
private:
LockedPool(const LockedPool& other) = delete; // non construction-copyable
LockedPool& operator=(const LockedPool&) = delete; // non copyable
std::unique_ptr<LockedPageAllocator> allocator;
/** Create an arena from locked pages */

View File

@ -102,7 +102,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
assert(false);
}
static void push_lock(void* c, const CLockLocation& locklocation, bool fTry)
static void push_lock(void* c, const CLockLocation& locklocation)
{
if (lockstack.get() == nullptr)
lockstack.reset(new LockStack);
@ -134,7 +134,7 @@ static void pop_lock()
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
{
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry), fTry);
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry));
}
void LeaveCritical()

View File

@ -112,10 +112,10 @@ class CBlockTreeDB : public CDBWrapper
{
public:
explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
private:
CBlockTreeDB(const CBlockTreeDB&);
void operator=(const CBlockTreeDB&);
public:
CBlockTreeDB(const CBlockTreeDB&) = delete;
CBlockTreeDB& operator=(const CBlockTreeDB&) = delete;
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
bool ReadLastBlockFile(int &nFile);

View File

@ -12,6 +12,7 @@ pkgconfig_DATA = pc/libunivalue.pc
libunivalue_la_SOURCES = \
lib/univalue.cpp \
lib/univalue_get.cpp \
lib/univalue_read.cpp \
lib/univalue_write.cpp
@ -20,7 +21,7 @@ libunivalue_la_LDFLAGS = \
-no-undefined
libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include
TESTS = test/unitester
TESTS = test/object test/unitester test/no_nul
GENBIN = gen/gen$(BUILD_EXEEXT)
GEN_SRCS = gen/gen.cpp
@ -33,7 +34,7 @@ gen: lib/univalue_escapes.h $(GENBIN)
@echo Updating $<
$(AM_V_at)$(GENBIN) > lib/univalue_escapes.h
noinst_PROGRAMS = $(TESTS)
noinst_PROGRAMS = $(TESTS) test/test_json
TEST_DATA_DIR=test
@ -42,6 +43,21 @@ test_unitester_LDADD = libunivalue.la
test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\"
test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
test_test_json_SOURCES = test/test_json.cpp
test_test_json_LDADD = libunivalue.la
test_test_json_CXXFLAGS = -I$(top_srcdir)/include
test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
test_no_nul_SOURCES = test/no_nul.cpp
test_no_nul_LDADD = libunivalue.la
test_no_nul_CXXFLAGS = -I$(top_srcdir)/include
test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
test_object_SOURCES = test/object.cpp
test_object_LDADD = libunivalue.la
test_object_CXXFLAGS = -I$(top_srcdir)/include
test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
TEST_FILES = \
$(TEST_DATA_DIR)/fail10.json \
$(TEST_DATA_DIR)/fail11.json \
@ -77,6 +93,8 @@ TEST_FILES = \
$(TEST_DATA_DIR)/fail39.json \
$(TEST_DATA_DIR)/fail40.json \
$(TEST_DATA_DIR)/fail41.json \
$(TEST_DATA_DIR)/fail42.json \
$(TEST_DATA_DIR)/fail44.json \
$(TEST_DATA_DIR)/fail3.json \
$(TEST_DATA_DIR)/fail4.json \
$(TEST_DATA_DIR)/fail5.json \
@ -88,6 +106,11 @@ TEST_FILES = \
$(TEST_DATA_DIR)/pass2.json \
$(TEST_DATA_DIR)/pass3.json \
$(TEST_DATA_DIR)/round1.json \
$(TEST_DATA_DIR)/round2.json
$(TEST_DATA_DIR)/round2.json \
$(TEST_DATA_DIR)/round3.json \
$(TEST_DATA_DIR)/round4.json \
$(TEST_DATA_DIR)/round5.json \
$(TEST_DATA_DIR)/round6.json \
$(TEST_DATA_DIR)/round7.json
EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS)

View File

@ -1,7 +0,0 @@
UniValue
A universal value object, with JSON encoding (output) and decoding (input).
Built as a single dynamic RAII C++ object class, and no templates.

32
src/univalue/README.md Normal file
View File

@ -0,0 +1,32 @@
# UniValue
## Summary
A universal value class, with JSON encoding and decoding.
UniValue is an abstract data type that may be a null, boolean, string,
number, array container, or a key/value dictionary container, nested to
an arbitrary depth.
This class is aligned with the JSON standard, [RFC
7159](https://tools.ietf.org/html/rfc7159.html).
## Installation
This project is a standard GNU
[autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html)
project. Build and install instructions are available in the `INSTALL`
file provided with GNU autotools.
```
$ ./autogen.sh
$ ./configure
$ make
```
## Design
UniValue provides a single dynamic RAII C++ object class,
and minimizes template use (contra json_spirit).

View File

@ -1,7 +1,7 @@
m4_define([libunivalue_major_version], [1])
m4_define([libunivalue_minor_version], [1])
m4_define([libunivalue_micro_version], [2])
m4_define([libunivalue_interface_age], [2])
m4_define([libunivalue_micro_version], [3])
m4_define([libunivalue_interface_age], [3])
# If you need a modifier for the version number.
# Normally empty, but can be used to make "fixup" releases.
m4_define([libunivalue_extraversion], [])
@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter
m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()])
AC_INIT([univalue], [1.0.2],
AC_INIT([univalue], [1.0.3],
[http://github.com/jgarzik/univalue/])
dnl make the compilation flags quiet unless V=1 is used

View File

@ -7,6 +7,7 @@
#define __UNIVALUE_H__
#include <stdint.h>
#include <string.h>
#include <string>
#include <vector>
@ -69,10 +70,11 @@ public:
size_t size() const { return values.size(); }
bool getBool() const { return isTrue(); }
bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes);
void getObjMap(std::map<std::string,UniValue>& kv) const;
bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes) const;
const UniValue& operator[](const std::string& key) const;
const UniValue& operator[](unsigned int index) const;
bool exists(const std::string& key) const { return (findKey(key) >= 0); }
const UniValue& operator[](size_t index) const;
bool exists(const std::string& key) const { size_t i; return findKey(key, i); }
bool isNull() const { return (typ == VNULL); }
bool isTrue() const { return (typ == VBOOL) && (val == "1"); }
@ -92,8 +94,25 @@ public:
std::string s(val_);
return push_back(s);
}
bool push_back(uint64_t val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_back(int64_t val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_back(int val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_back(double val_) {
UniValue tmpVal(val_);
return push_back(tmpVal);
}
bool push_backV(const std::vector<UniValue>& vec);
void __pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const std::string& val_) {
UniValue tmpVal(VSTR, val_);
@ -124,9 +143,10 @@ public:
std::string write(unsigned int prettyIndent = 0,
unsigned int indentLevel = 0) const;
bool read(const char *raw);
bool read(const char *raw, size_t len);
bool read(const char *raw) { return read(raw, strlen(raw)); }
bool read(const std::string& rawStr) {
return read(rawStr.c_str());
return read(rawStr.data(), rawStr.size());
}
private:
@ -135,7 +155,7 @@ private:
std::vector<std::string> keys;
std::vector<UniValue> values;
int findKey(const std::string& key) const;
bool findKey(const std::string& key, size_t& retIdx) const;
void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
@ -240,7 +260,7 @@ enum jtokentype {
};
extern enum jtokentype getJsonToken(std::string& tokenVal,
unsigned int& consumed, const char *raw);
unsigned int& consumed, const char *raw, const char *end);
extern const char *uvTypeName(UniValue::VType t);
static inline bool jsonTokenIsValue(enum jtokentype jtt)

View File

@ -4,75 +4,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <errno.h>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <stdlib.h>
#include <string.h>
#include "univalue.h"
namespace
{
static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed
return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false;
return true;
}
bool ParseInt32(const std::string& str, int32_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int32_t)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int32_t>::min() &&
n <= std::numeric_limits<int32_t>::max();
}
bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
// Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int64_t*.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int64_t>::min() &&
n <= std::numeric_limits<int64_t>::max();
}
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false;
std::istringstream text(str);
text.imbue(std::locale::classic());
double result;
text >> result;
if(out) *out = result;
return text.eof() && !text.fail();
}
}
using namespace std;
const UniValue NullUniValue;
@ -104,7 +41,7 @@ static bool validNumStr(const string& s)
{
string tokenVal;
unsigned int consumed;
enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str());
enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size());
return (tt == JTOK_NUMBER);
}
@ -189,13 +126,22 @@ bool UniValue::push_backV(const std::vector<UniValue>& vec)
return true;
}
void UniValue::__pushKV(const std::string& key, const UniValue& val_)
{
keys.push_back(key);
values.push_back(val_);
}
bool UniValue::pushKV(const std::string& key, const UniValue& val_)
{
if (typ != VOBJ)
return false;
keys.push_back(key);
values.push_back(val_);
size_t idx;
if (findKey(key, idx))
values[idx] = val_;
else
__pushKV(key, val_);
return true;
}
@ -204,30 +150,43 @@ bool UniValue::pushKVs(const UniValue& obj)
if (typ != VOBJ || obj.typ != VOBJ)
return false;
for (unsigned int i = 0; i < obj.keys.size(); i++) {
keys.push_back(obj.keys[i]);
values.push_back(obj.values.at(i));
}
for (size_t i = 0; i < obj.keys.size(); i++)
__pushKV(obj.keys[i], obj.values.at(i));
return true;
}
int UniValue::findKey(const std::string& key) const
void UniValue::getObjMap(std::map<std::string,UniValue>& kv) const
{
for (unsigned int i = 0; i < keys.size(); i++) {
if (keys[i] == key)
return (int) i;
}
if (typ != VOBJ)
return;
return -1;
kv.clear();
for (size_t i = 0; i < keys.size(); i++)
kv[keys[i]] = values[i];
}
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t)
bool UniValue::findKey(const std::string& key, size_t& retIdx) const
{
for (size_t i = 0; i < keys.size(); i++) {
if (keys[i] == key) {
retIdx = i;
return true;
}
}
return false;
}
bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) const
{
if (typ != VOBJ)
return false;
for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin();
it != t.end(); ++it) {
int idx = findKey(it->first);
if (idx < 0)
size_t idx = 0;
if (!findKey(it->first, idx))
return false;
if (values.at(idx).getType() != it->second)
@ -242,14 +201,14 @@ const UniValue& UniValue::operator[](const std::string& key) const
if (typ != VOBJ)
return NullUniValue;
int index = findKey(key);
if (index < 0)
size_t index = 0;
if (!findKey(key, index))
return NullUniValue;
return values.at(index);
}
const UniValue& UniValue::operator[](unsigned int index) const
const UniValue& UniValue::operator[](size_t index) const
{
if (typ != VOBJ && typ != VARR)
return NullUniValue;
@ -283,75 +242,3 @@ const UniValue& find_value(const UniValue& obj, const std::string& name)
return NullUniValue;
}
const std::vector<std::string>& UniValue::getKeys() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return keys;
}
const std::vector<UniValue>& UniValue::getValues() const
{
if (typ != VOBJ && typ != VARR)
throw std::runtime_error("JSON value is not an object or array as expected");
return values;
}
bool UniValue::get_bool() const
{
if (typ != VBOOL)
throw std::runtime_error("JSON value is not a boolean as expected");
return getBool();
}
const std::string& UniValue::get_str() const
{
if (typ != VSTR)
throw std::runtime_error("JSON value is not a string as expected");
return getValStr();
}
int UniValue::get_int() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int32_t retval;
if (!ParseInt32(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
int64_t UniValue::get_int64() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int64_t retval;
if (!ParseInt64(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
double UniValue::get_real() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not a number as expected");
double retval;
if (!ParseDouble(getValStr(), &retval))
throw std::runtime_error("JSON double out of range");
return retval;
}
const UniValue& UniValue::get_obj() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return *this;
}
const UniValue& UniValue::get_array() const
{
if (typ != VARR)
throw std::runtime_error("JSON value is not an array as expected");
return *this;
}

View File

@ -0,0 +1,147 @@
// Copyright 2014 BitPay Inc.
// Copyright 2015 Bitcoin Core Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdexcept>
#include <vector>
#include <limits>
#include <string>
#include "univalue.h"
namespace
{
static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed
return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false;
return true;
}
bool ParseInt32(const std::string& str, int32_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int32_t)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int32_t>::min() &&
n <= std::numeric_limits<int32_t>::max();
}
bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = NULL;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
// Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int64_t*.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int64_t>::min() &&
n <= std::numeric_limits<int64_t>::max();
}
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false;
std::istringstream text(str);
text.imbue(std::locale::classic());
double result;
text >> result;
if(out) *out = result;
return text.eof() && !text.fail();
}
}
const std::vector<std::string>& UniValue::getKeys() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return keys;
}
const std::vector<UniValue>& UniValue::getValues() const
{
if (typ != VOBJ && typ != VARR)
throw std::runtime_error("JSON value is not an object or array as expected");
return values;
}
bool UniValue::get_bool() const
{
if (typ != VBOOL)
throw std::runtime_error("JSON value is not a boolean as expected");
return getBool();
}
const std::string& UniValue::get_str() const
{
if (typ != VSTR)
throw std::runtime_error("JSON value is not a string as expected");
return getValStr();
}
int UniValue::get_int() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int32_t retval;
if (!ParseInt32(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
int64_t UniValue::get_int64() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not an integer as expected");
int64_t retval;
if (!ParseInt64(getValStr(), &retval))
throw std::runtime_error("JSON integer out of range");
return retval;
}
double UniValue::get_real() const
{
if (typ != VNUM)
throw std::runtime_error("JSON value is not a number as expected");
double retval;
if (!ParseDouble(getValStr(), &retval))
throw std::runtime_error("JSON double out of range");
return retval;
}
const UniValue& UniValue::get_obj() const
{
if (typ != VOBJ)
throw std::runtime_error("JSON value is not an object as expected");
return *this;
}
const UniValue& UniValue::get_array() const
{
if (typ != VARR)
throw std::runtime_error("JSON value is not an array as expected");
return *this;
}

View File

@ -43,21 +43,21 @@ static const char *hatoui(const char *first, const char *last,
}
enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
const char *raw)
const char *raw, const char *end)
{
tokenVal.clear();
consumed = 0;
const char *rawStart = raw;
while ((*raw) && (json_isspace(*raw))) // skip whitespace
while (raw < end && (json_isspace(*raw))) // skip whitespace
raw++;
switch (*raw) {
case 0:
if (raw >= end)
return JTOK_NONE;
switch (*raw) {
case '{':
raw++;
consumed = (raw - rawStart);
@ -127,40 +127,40 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
numStr += *raw; // copy first char
raw++;
if ((*first == '-') && (!json_isdigit(*raw)))
if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
return JTOK_ERR;
while ((*raw) && json_isdigit(*raw)) { // copy digits
while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
// part 2: frac
if (*raw == '.') {
if (raw < end && *raw == '.') {
numStr += *raw; // copy .
raw++;
if (!json_isdigit(*raw))
if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
while ((*raw) && json_isdigit(*raw)) { // copy digits
while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}
// part 3: exp
if (*raw == 'e' || *raw == 'E') {
if (raw < end && (*raw == 'e' || *raw == 'E')) {
numStr += *raw; // copy E
raw++;
if (*raw == '-' || *raw == '+') { // copy +/-
if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
numStr += *raw;
raw++;
}
if (!json_isdigit(*raw))
if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
while ((*raw) && json_isdigit(*raw)) { // copy digits
while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
@ -177,13 +177,16 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
string valStr;
JSONUTF8StringFilter writer(valStr);
while (*raw) {
if ((unsigned char)*raw < 0x20)
while (true) {
if (raw >= end || (unsigned char)*raw < 0x20)
return JTOK_ERR;
else if (*raw == '\\') {
raw++; // skip backslash
if (raw >= end)
return JTOK_ERR;
switch (*raw) {
case '"': writer.push_back('\"'); break;
case '\\': writer.push_back('\\'); break;
@ -196,7 +199,8 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
case 'u': {
unsigned int codepoint;
if (hatoui(raw + 1, raw + 1 + 4, codepoint) !=
if (raw + 1 + 4 >= end ||
hatoui(raw + 1, raw + 1 + 4, codepoint) !=
raw + 1 + 4)
return JTOK_ERR;
writer.push_back_u(codepoint);
@ -246,7 +250,7 @@ enum expect_bits {
#define setExpect(bit) (expectMask |= EXP_##bit)
#define clearExpect(bit) (expectMask &= ~EXP_##bit)
bool UniValue::read(const char *raw)
bool UniValue::read(const char *raw, size_t size)
{
clear();
@ -257,10 +261,11 @@ bool UniValue::read(const char *raw)
unsigned int consumed;
enum jtokentype tok = JTOK_NONE;
enum jtokentype last_tok = JTOK_NONE;
const char* end = raw + size;
do {
last_tok = tok;
tok = getJsonToken(tokenVal, consumed, raw);
tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok == JTOK_NONE || tok == JTOK_ERR)
return false;
raw += consumed;
@ -371,9 +376,6 @@ bool UniValue::read(const char *raw)
case JTOK_KW_NULL:
case JTOK_KW_TRUE:
case JTOK_KW_FALSE: {
if (!stack.size())
return false;
UniValue tmpVal;
switch (tok) {
case JTOK_KW_NULL:
@ -388,6 +390,11 @@ bool UniValue::read(const char *raw)
default: /* impossible */ break;
}
if (!stack.size()) {
*this = tmpVal;
break;
}
UniValue *top = stack.back();
top->values.push_back(tmpVal);
@ -396,10 +403,12 @@ bool UniValue::read(const char *raw)
}
case JTOK_NUMBER: {
if (!stack.size())
return false;
UniValue tmpVal(VNUM, tokenVal);
if (!stack.size()) {
*this = tmpVal;
break;
}
UniValue *top = stack.back();
top->values.push_back(tmpVal);
@ -408,17 +417,18 @@ bool UniValue::read(const char *raw)
}
case JTOK_STRING: {
if (!stack.size())
return false;
UniValue *top = stack.back();
if (expect(OBJ_NAME)) {
UniValue *top = stack.back();
top->keys.push_back(tokenVal);
clearExpect(OBJ_NAME);
setExpect(COLON);
} else {
UniValue tmpVal(VSTR, tokenVal);
if (!stack.size()) {
*this = tmpVal;
break;
}
UniValue *top = stack.back();
top->values.push_back(tmpVal);
}
@ -432,7 +442,7 @@ bool UniValue::read(const char *raw)
} while (!stack.empty ());
/* Check that nothing follows the initial construct (parsed above). */
tok = getJsonToken(tokenVal, consumed, raw);
tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok != JTOK_NONE)
return false;

View File

@ -42,20 +42,20 @@ public:
}
}
// Write codepoint directly, possibly collating surrogate pairs
void push_back_u(unsigned int codepoint)
void push_back_u(unsigned int codepoint_)
{
// Only accept full codepoints in open state
if (state)
is_valid = false;
if (codepoint >= 0xD800 && codepoint < 0xDC00) { // First half of surrogate pair
if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair
if (surpair) // Two subsequent surrogate pair openers - fail
is_valid = false;
else
surpair = codepoint;
} else if (codepoint >= 0xDC00 && codepoint < 0xE000) { // Second half of surrogate pair
surpair = codepoint_;
} else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair
if (surpair) { // Open surrogate pair, expect second half
// Compute code point from UTF-16 surrogate pair
append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint - 0xDC00));
append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00));
surpair = 0;
} else // First half of surrogate pair not followed by second
is_valid = false;
@ -63,7 +63,7 @@ public:
if (surpair) // First half of surrogate pair not followed by second
is_valid = false;
else
append_codepoint(codepoint);
append_codepoint(codepoint_);
}
}
// Check that we're in a state where the string can be ended
@ -91,22 +91,22 @@ private:
// Two subsequent \u.... may have to be replaced with one actual codepoint.
unsigned int surpair; // First of UTF-16 surrogate pair
void append_codepoint(unsigned int codepoint)
void append_codepoint(unsigned int codepoint_)
{
if (codepoint <= 0x7f)
str.push_back((char)codepoint);
else if (codepoint <= 0x7FF) {
str.push_back((char)(0xC0 | (codepoint >> 6)));
str.push_back((char)(0x80 | (codepoint & 0x3F)));
} else if (codepoint <= 0xFFFF) {
str.push_back((char)(0xE0 | (codepoint >> 12)));
str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint & 0x3F)));
} else if (codepoint <= 0x1FFFFF) {
str.push_back((char)(0xF0 | (codepoint >> 18)));
str.push_back((char)(0x80 | ((codepoint >> 12) & 0x3F)));
str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint & 0x3F)));
if (codepoint_ <= 0x7f)
str.push_back((char)codepoint_);
else if (codepoint_ <= 0x7FF) {
str.push_back((char)(0xC0 | (codepoint_ >> 6)));
str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
} else if (codepoint_ <= 0xFFFF) {
str.push_back((char)(0xE0 | (codepoint_ >> 12)));
str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
} else if (codepoint_ <= 0x1FFFFF) {
str.push_back((char)(0xF0 | (codepoint_ >> 18)));
str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F)));
str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
}
}
};

View File

@ -79,8 +79,6 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s
s += values[i].write(prettyIndent, indentLevel + 1);
if (i != (values.size() - 1)) {
s += ",";
if (prettyIndent)
s += " ";
}
if (prettyIndent)
s += "\n";

View File

@ -1,4 +1,8 @@
object
unitester
test_json
no_nul
*.trs
*.log

View File

@ -1 +1 @@
"A JSON payload should be an object or array, not a string."
"This is a string that never ends, yes it goes on and on, my friends.

Binary file not shown.

View File

@ -0,0 +1 @@
"This file ends without a newline or close-quote.

View File

@ -0,0 +1,8 @@
#include "univalue.h"
int main (int argc, char *argv[])
{
char buf[] = "___[1,2,3]___";
UniValue val;
return val.read(buf + 3, 7) ? 0 : 1;
}

View File

@ -0,0 +1,395 @@
// Copyright (c) 2014 BitPay Inc.
// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <stdint.h>
#include <vector>
#include <string>
#include <map>
#include <cassert>
#include <stdexcept>
#include <univalue.h>
#define BOOST_FIXTURE_TEST_SUITE(a, b)
#define BOOST_AUTO_TEST_CASE(funcName) void funcName()
#define BOOST_AUTO_TEST_SUITE_END()
#define BOOST_CHECK(expr) assert(expr)
#define BOOST_CHECK_EQUAL(v1, v2) assert((v1) == (v2))
#define BOOST_CHECK_THROW(stmt, excMatch) { \
try { \
(stmt); \
} catch (excMatch & e) { \
} catch (...) { \
assert(0); \
} \
}
#define BOOST_CHECK_NO_THROW(stmt) { \
try { \
(stmt); \
} catch (...) { \
assert(0); \
} \
}
BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(univalue_constructor)
{
UniValue v1;
BOOST_CHECK(v1.isNull());
UniValue v2(UniValue::VSTR);
BOOST_CHECK(v2.isStr());
UniValue v3(UniValue::VSTR, "foo");
BOOST_CHECK(v3.isStr());
BOOST_CHECK_EQUAL(v3.getValStr(), "foo");
UniValue numTest;
BOOST_CHECK(numTest.setNumStr("82"));
BOOST_CHECK(numTest.isNum());
BOOST_CHECK_EQUAL(numTest.getValStr(), "82");
uint64_t vu64 = 82;
UniValue v4(vu64);
BOOST_CHECK(v4.isNum());
BOOST_CHECK_EQUAL(v4.getValStr(), "82");
int64_t vi64 = -82;
UniValue v5(vi64);
BOOST_CHECK(v5.isNum());
BOOST_CHECK_EQUAL(v5.getValStr(), "-82");
int vi = -688;
UniValue v6(vi);
BOOST_CHECK(v6.isNum());
BOOST_CHECK_EQUAL(v6.getValStr(), "-688");
double vd = -7.21;
UniValue v7(vd);
BOOST_CHECK(v7.isNum());
BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21");
std::string vs("yawn");
UniValue v8(vs);
BOOST_CHECK(v8.isStr());
BOOST_CHECK_EQUAL(v8.getValStr(), "yawn");
const char *vcs = "zappa";
UniValue v9(vcs);
BOOST_CHECK(v9.isStr());
BOOST_CHECK_EQUAL(v9.getValStr(), "zappa");
}
BOOST_AUTO_TEST_CASE(univalue_typecheck)
{
UniValue v1;
BOOST_CHECK(v1.setNumStr("1"));
BOOST_CHECK(v1.isNum());
BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error);
UniValue v2;
BOOST_CHECK(v2.setBool(true));
BOOST_CHECK_EQUAL(v2.get_bool(), true);
BOOST_CHECK_THROW(v2.get_int(), std::runtime_error);
UniValue v3;
BOOST_CHECK(v3.setNumStr("32482348723847471234"));
BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error);
BOOST_CHECK(v3.setNumStr("1000"));
BOOST_CHECK_EQUAL(v3.get_int64(), 1000);
UniValue v4;
BOOST_CHECK(v4.setNumStr("2147483648"));
BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648);
BOOST_CHECK_THROW(v4.get_int(), std::runtime_error);
BOOST_CHECK(v4.setNumStr("1000"));
BOOST_CHECK_EQUAL(v4.get_int(), 1000);
BOOST_CHECK_THROW(v4.get_str(), std::runtime_error);
BOOST_CHECK_EQUAL(v4.get_real(), 1000);
BOOST_CHECK_THROW(v4.get_array(), std::runtime_error);
BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error);
BOOST_CHECK_THROW(v4.getValues(), std::runtime_error);
BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error);
UniValue v5;
BOOST_CHECK(v5.read("[true, 10]"));
BOOST_CHECK_NO_THROW(v5.get_array());
std::vector<UniValue> vals = v5.getValues();
BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error);
BOOST_CHECK_EQUAL(vals[0].get_bool(), true);
BOOST_CHECK_EQUAL(vals[1].get_int(), 10);
BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error);
}
BOOST_AUTO_TEST_CASE(univalue_set)
{
UniValue v(UniValue::VSTR, "foo");
v.clear();
BOOST_CHECK(v.isNull());
BOOST_CHECK_EQUAL(v.getValStr(), "");
BOOST_CHECK(v.setObject());
BOOST_CHECK(v.isObject());
BOOST_CHECK_EQUAL(v.size(), 0);
BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ);
BOOST_CHECK(v.empty());
BOOST_CHECK(v.setArray());
BOOST_CHECK(v.isArray());
BOOST_CHECK_EQUAL(v.size(), 0);
BOOST_CHECK(v.setStr("zum"));
BOOST_CHECK(v.isStr());
BOOST_CHECK_EQUAL(v.getValStr(), "zum");
BOOST_CHECK(v.setFloat(-1.01));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "-1.01");
BOOST_CHECK(v.setInt((int)1023));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "1023");
BOOST_CHECK(v.setInt((int64_t)-1023LL));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "-1023");
BOOST_CHECK(v.setInt((uint64_t)1023ULL));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "1023");
BOOST_CHECK(v.setNumStr("-688"));
BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "-688");
BOOST_CHECK(v.setBool(false));
BOOST_CHECK_EQUAL(v.isBool(), true);
BOOST_CHECK_EQUAL(v.isTrue(), false);
BOOST_CHECK_EQUAL(v.isFalse(), true);
BOOST_CHECK_EQUAL(v.getBool(), false);
BOOST_CHECK(v.setBool(true));
BOOST_CHECK_EQUAL(v.isBool(), true);
BOOST_CHECK_EQUAL(v.isTrue(), true);
BOOST_CHECK_EQUAL(v.isFalse(), false);
BOOST_CHECK_EQUAL(v.getBool(), true);
BOOST_CHECK(!v.setNumStr("zombocom"));
BOOST_CHECK(v.setNull());
BOOST_CHECK(v.isNull());
}
BOOST_AUTO_TEST_CASE(univalue_array)
{
UniValue arr(UniValue::VARR);
UniValue v((int64_t)1023LL);
BOOST_CHECK(arr.push_back(v));
std::string vStr("zippy");
BOOST_CHECK(arr.push_back(vStr));
const char *s = "pippy";
BOOST_CHECK(arr.push_back(s));
std::vector<UniValue> vec;
v.setStr("boing");
vec.push_back(v);
v.setStr("going");
vec.push_back(v);
BOOST_CHECK(arr.push_backV(vec));
BOOST_CHECK(arr.push_back((uint64_t) 400ULL));
BOOST_CHECK(arr.push_back((int64_t) -400LL));
BOOST_CHECK(arr.push_back((int) -401));
BOOST_CHECK(arr.push_back(-40.1));
BOOST_CHECK_EQUAL(arr.empty(), false);
BOOST_CHECK_EQUAL(arr.size(), 9);
BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023");
BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy");
BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy");
BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing");
BOOST_CHECK_EQUAL(arr[4].getValStr(), "going");
BOOST_CHECK_EQUAL(arr[5].getValStr(), "400");
BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400");
BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401");
BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1");
BOOST_CHECK_EQUAL(arr[999].getValStr(), "");
arr.clear();
BOOST_CHECK(arr.empty());
BOOST_CHECK_EQUAL(arr.size(), 0);
}
BOOST_AUTO_TEST_CASE(univalue_object)
{
UniValue obj(UniValue::VOBJ);
std::string strKey, strVal;
UniValue v;
strKey = "age";
v.setInt(100);
BOOST_CHECK(obj.pushKV(strKey, v));
strKey = "first";
strVal = "John";
BOOST_CHECK(obj.pushKV(strKey, strVal));
strKey = "last";
const char *cVal = "Smith";
BOOST_CHECK(obj.pushKV(strKey, cVal));
strKey = "distance";
BOOST_CHECK(obj.pushKV(strKey, (int64_t) 25));
strKey = "time";
BOOST_CHECK(obj.pushKV(strKey, (uint64_t) 3600));
strKey = "calories";
BOOST_CHECK(obj.pushKV(strKey, (int) 12));
strKey = "temperature";
BOOST_CHECK(obj.pushKV(strKey, (double) 90.012));
UniValue obj2(UniValue::VOBJ);
BOOST_CHECK(obj2.pushKV("cat1", 9000));
BOOST_CHECK(obj2.pushKV("cat2", 12345));
BOOST_CHECK(obj.pushKVs(obj2));
BOOST_CHECK_EQUAL(obj.empty(), false);
BOOST_CHECK_EQUAL(obj.size(), 9);
BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100");
BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John");
BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith");
BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25");
BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600");
BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12");
BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012");
BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000");
BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345");
BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), "");
BOOST_CHECK(obj.exists("age"));
BOOST_CHECK(obj.exists("first"));
BOOST_CHECK(obj.exists("last"));
BOOST_CHECK(obj.exists("distance"));
BOOST_CHECK(obj.exists("time"));
BOOST_CHECK(obj.exists("calories"));
BOOST_CHECK(obj.exists("temperature"));
BOOST_CHECK(obj.exists("cat1"));
BOOST_CHECK(obj.exists("cat2"));
BOOST_CHECK(!obj.exists("nyuknyuknyuk"));
std::map<std::string, UniValue::VType> objTypes;
objTypes["age"] = UniValue::VNUM;
objTypes["first"] = UniValue::VSTR;
objTypes["last"] = UniValue::VSTR;
objTypes["distance"] = UniValue::VNUM;
objTypes["time"] = UniValue::VNUM;
objTypes["calories"] = UniValue::VNUM;
objTypes["temperature"] = UniValue::VNUM;
objTypes["cat1"] = UniValue::VNUM;
objTypes["cat2"] = UniValue::VNUM;
BOOST_CHECK(obj.checkObject(objTypes));
objTypes["cat2"] = UniValue::VSTR;
BOOST_CHECK(!obj.checkObject(objTypes));
obj.clear();
BOOST_CHECK(obj.empty());
BOOST_CHECK_EQUAL(obj.size(), 0);
BOOST_CHECK_EQUAL(obj.getType(), UniValue::VNULL);
BOOST_CHECK_EQUAL(obj.setObject(), true);
UniValue uv;
uv.setInt(42);
obj.__pushKV("age", uv);
BOOST_CHECK_EQUAL(obj.size(), 1);
BOOST_CHECK_EQUAL(obj["age"].getValStr(), "42");
uv.setInt(43);
obj.pushKV("age", uv);
BOOST_CHECK_EQUAL(obj.size(), 1);
BOOST_CHECK_EQUAL(obj["age"].getValStr(), "43");
obj.pushKV("name", "foo bar");
std::map<std::string,UniValue> kv;
obj.getObjMap(kv);
BOOST_CHECK_EQUAL(kv["age"].getValStr(), "43");
BOOST_CHECK_EQUAL(kv["name"].getValStr(), "foo bar");
}
static const char *json1 =
"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]";
BOOST_AUTO_TEST_CASE(univalue_readwrite)
{
UniValue v;
BOOST_CHECK(v.read(json1));
std::string strJson1(json1);
BOOST_CHECK(v.read(strJson1));
BOOST_CHECK(v.isArray());
BOOST_CHECK_EQUAL(v.size(), 2);
BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000");
UniValue obj = v[1];
BOOST_CHECK(obj.isObject());
BOOST_CHECK_EQUAL(obj.size(), 3);
BOOST_CHECK(obj["key1"].isStr());
std::string correctValue("str");
correctValue.push_back('\0');
BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue);
BOOST_CHECK(obj["key2"].isNum());
BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800");
BOOST_CHECK(obj["key3"].isObject());
BOOST_CHECK_EQUAL(strJson1, v.write());
/* Check for (correctly reporting) a parsing error if the initial
JSON construct is followed by more stuff. Note that whitespace
is, of course, exempt. */
BOOST_CHECK(v.read(" {}\n "));
BOOST_CHECK(v.isObject());
BOOST_CHECK(v.read(" []\n "));
BOOST_CHECK(v.isArray());
BOOST_CHECK(!v.read("@{}"));
BOOST_CHECK(!v.read("{} garbage"));
BOOST_CHECK(!v.read("[]{}"));
BOOST_CHECK(!v.read("{}[]"));
BOOST_CHECK(!v.read("{} 42"));
}
BOOST_AUTO_TEST_SUITE_END()
int main (int argc, char *argv[])
{
univalue_constructor();
univalue_typecheck();
univalue_set();
univalue_array();
univalue_object();
univalue_readwrite();
return 0;
}

View File

@ -0,0 +1 @@
"abcdefghijklmnopqrstuvwxyz"

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
false

View File

@ -0,0 +1 @@
null

View File

@ -0,0 +1,24 @@
// Test program that can be called by the JSON test suite at
// https://github.com/nst/JSONTestSuite.
//
// It reads JSON input from stdin and exits with code 0 if it can be parsed
// successfully. It also pretty prints the parsed JSON value to stdout.
#include <iostream>
#include <string>
#include "univalue.h"
using namespace std;
int main (int argc, char *argv[])
{
UniValue val;
if (val.read(string(istreambuf_iterator<char>(cin),
istreambuf_iterator<char>()))) {
cout << val.write(1 /* prettyIndent */, 4 /* indentLevel */) << endl;
return 0;
} else {
cerr << "JSON Parse Error." << endl;
return 1;
}
}

View File

@ -113,6 +113,8 @@ static const char *filenames[] = {
"fail39.json", // invalid unicode: only second half of surrogate pair
"fail40.json", // invalid unicode: broken UTF-8
"fail41.json", // invalid unicode: unfinished UTF-8
"fail42.json", // valid json with garbage following a nul byte
"fail44.json", // unterminated string
"fail3.json",
"fail4.json", // extra comma
"fail5.json",
@ -125,6 +127,11 @@ static const char *filenames[] = {
"pass3.json",
"round1.json", // round-trip test
"round2.json", // unicode
"round3.json", // bare string
"round4.json", // bare number
"round5.json", // bare true
"round6.json", // bare false
"round7.json", // bare null
};
// Test \u handling

View File

@ -3658,8 +3658,10 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
*/
/* Calculate the amount of disk space the block & undo files currently use */
static uint64_t CalculateCurrentUsage()
uint64_t CalculateCurrentUsage()
{
LOCK(cs_LastBlockFile);
uint64_t retval = 0;
for (const CBlockFileInfo &file : vinfoBlockFile) {
retval += file.nSize + file.nUndoSize;
@ -3670,6 +3672,8 @@ static uint64_t CalculateCurrentUsage()
/* Prune a block file (modify associated database entries)*/
void PruneOneBlockFile(const int fileNumber)
{
LOCK(cs_LastBlockFile);
for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) {
CBlockIndex* pindex = it->second;
if (pindex->nFile == fileNumber) {
@ -4634,6 +4638,8 @@ std::string CBlockFileInfo::ToString() const {
CBlockFileInfo* GetBlockFileInfo(size_t n)
{
LOCK(cs_LastBlockFile);
return &vinfoBlockFile.at(n);
}

View File

@ -296,6 +296,9 @@ CAmount GetMasternodePayment(int nHeight, CAmount blockValue);
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex);
/** Calculate the amount of disk space the block & undo files currently use */
uint64_t CalculateCurrentUsage();
/**
* Mark one block file as pruned.
*/

View File

@ -156,6 +156,9 @@ public:
explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~CDB() { Close(); }
CDB(const CDB&) = delete;
CDB& operator=(const CDB&) = delete;
void Flush();
void Close();
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
@ -168,10 +171,6 @@ public:
/* verifies the database file */
static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
private:
CDB(const CDB&);
void operator=(const CDB&);
public:
template <typename K, typename T>
bool Read(const K& key, T& value)

View File

@ -3699,6 +3699,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
if (recipient.fSubtractFeeFromAmount)
{
assert(nSubtractFeeFromAmount != 0);
txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
if (fFirst) // first receiver pays the remainder not divisible by output count

View File

@ -122,6 +122,8 @@ public:
m_dbw(dbw)
{
}
CWalletDB(const CWalletDB&) = delete;
CWalletDB& operator=(const CWalletDB&) = delete;
bool WriteName(const std::string& strAddress, const std::string& strName);
bool EraseName(const std::string& strAddress);
@ -203,9 +205,6 @@ public:
private:
CDB batch;
CWalletDBWrapper& m_dbw;
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);
};
//! Compacts BDB state so that wallet.dat is self-contained (if there are changes)

View File

@ -24,6 +24,8 @@ import subprocess
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_greater_than_or_equal,
assert_raises,
assert_raises_rpc_error,
assert_is_hex_string,
@ -33,9 +35,19 @@ from test_framework.util import (
class BlockchainTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [['-stopatheight=207']]
self.setup_clean_chain = True
self.extra_args = [['-stopatheight=207', '-prune=1', '-txindex=0']]
def run_test(self):
# Have to prepare the chain manually here.
# txindex=1 by default in Dash which is incompatible with pruning.
self.set_genesis_mocktime()
for i in range(200):
self.bump_mocktime(156)
self.nodes[0].setmocktime(self.mocktime)
self.nodes[0].generate(1)
# Actual tests
self._test_getblockchaininfo()
self._test_getchaintxstats()
self._test_gettxoutsetinfo()
self._test_getblockheader()
@ -44,6 +56,56 @@ class BlockchainTest(BitcoinTestFramework):
self._test_stopatheight()
assert self.nodes[0].verifychain(4, 0)
def _test_getblockchaininfo(self):
self.log.info("Test getblockchaininfo")
keys = [
'bestblockhash',
'bip9_softforks',
'blocks',
'chain',
'chainwork',
'difficulty',
'headers',
'mediantime',
'pruned',
'size_on_disk',
'softforks',
'verificationprogress',
'warnings',
]
res = self.nodes[0].getblockchaininfo()
# result should have these additional pruning keys if manual pruning is enabled
assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys))
# size_on_disk should be > 0
assert_greater_than(res['size_on_disk'], 0)
# pruneheight should be greater or equal to 0
assert_greater_than_or_equal(res['pruneheight'], 0)
# check other pruning fields given that prune=1
assert res['pruned']
assert not res['automatic_pruning']
self.restart_node(0, ['-stopatheight=207', '-txindex=0', '-mocktime=%d' % self.mocktime])
res = self.nodes[0].getblockchaininfo()
# should have exact keys
assert_equal(sorted(res.keys()), keys)
self.restart_node(0, ['-stopatheight=207', '-prune=550', '-txindex=0', '-mocktime=%d' % self.mocktime])
res = self.nodes[0].getblockchaininfo()
# result should have these additional pruning keys if prune=550
assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys))
# check related fields
assert res['pruned']
assert_equal(res['pruneheight'], 0)
assert res['automatic_pruning']
assert_equal(res['prune_target_size'], 576716800)
assert_greater_than(res['size_on_disk'], 0)
def _test_getchaintxstats(self):
chaintxstats = self.nodes[0].getchaintxstats(1)
# 200 txs plus genesis tx
@ -52,6 +114,28 @@ class BlockchainTest(BitcoinTestFramework):
# we have to round because of binary math
assert_equal(round(chaintxstats['txrate'] * 156, 10), Decimal(1))
b1 = self.nodes[0].getblock(self.nodes[0].getblockhash(1))
b200 = self.nodes[0].getblock(self.nodes[0].getblockhash(200))
time_diff = b200['mediantime'] - b1['mediantime']
chaintxstats = self.nodes[0].getchaintxstats()
assert_equal(chaintxstats['time'], b200['time'])
assert_equal(chaintxstats['txcount'], 201)
assert_equal(chaintxstats['window_block_count'], 199)
assert_equal(chaintxstats['window_tx_count'], 199)
assert_equal(chaintxstats['window_interval'], time_diff)
assert_equal(round(chaintxstats['txrate'] * time_diff, 10), Decimal(199))
chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1['hash'])
assert_equal(chaintxstats['time'], b1['time'])
assert_equal(chaintxstats['txcount'], 2)
assert_equal(chaintxstats['window_block_count'], 0)
assert('window_tx_count' not in chaintxstats)
assert('window_interval' not in chaintxstats)
assert('txrate' not in chaintxstats)
assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, 201)
def _test_gettxoutsetinfo(self):
node = self.nodes[0]
res = node.gettxoutsetinfo()
@ -141,7 +225,7 @@ class BlockchainTest(BitcoinTestFramework):
pass # The node already shut down before response
self.log.debug('Node should stop at this height...')
self.nodes[0].wait_until_stopped()
self.start_node(0)
self.start_node(0, ['-txindex=0', '-mocktime=%d' % self.mocktime])
assert_equal(self.nodes[0].getblockcount(), 207)

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python3
# Copyright (c) 2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test deprecation of RPC calls."""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_raises_rpc_error
class DeprecatedRpcTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
self.extra_args = [[], ["-deprecatedrpc=estimatefee"]]
def run_test(self):
self.log.info("estimatefee: Shows deprecated message")
assert_raises_rpc_error(-32, 'estimatefee is deprecated', self.nodes[0].estimatefee, 1)
self.log.info("Using -deprecatedrpc=estimatefee bypasses the error")
self.nodes[1].estimatefee(1)
if __name__ == '__main__':
DeprecatedRpcTest().main()

View File

@ -1,59 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2014-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the -alertnotify option."""
import os
import time
from test_framework.test_framework import BitcoinTestFramework
class ForkNotifyTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
def setup_network(self):
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
with open(self.alert_filename, 'w', encoding='utf8'):
pass # Just open then close to create zero-length file
self.extra_args = [["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""],
["-blockversion=211"]]
super().setup_network()
def run_test(self):
# Mine 51 up-version blocks
self.nodes[1].generate(51)
self.sync_all()
# -alertnotify should trigger on the 51'st,
# but mine and sync another to give
# -alertnotify time to write
self.nodes[1].generate(1)
self.sync_all()
# Give bitcoind 10 seconds to write the alert notification
timeout = 10.0
while timeout > 0:
if os.path.exists(self.alert_filename) and os.path.getsize(self.alert_filename):
break
time.sleep(0.1)
timeout -= 0.1
else:
assert False, "-alertnotify did not warn of up-version blocks"
with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text = f.read()
# Mine more up-version blocks, should not get more alerts:
self.nodes[1].generate(1)
self.sync_all()
self.nodes[1].generate(1)
self.sync_all()
with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text2 = f.read()
if alert_text != alert_text2:
raise AssertionError("-alertnotify excessive warning of up-version blocks")
if __name__ == '__main__':
ForkNotifyTest().main()

View File

@ -0,0 +1,86 @@
#!/usr/bin/env python3
# Copyright (c) 2014-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the -alertnotify, -blocknotify and -walletnotify options."""
import os
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, wait_until, connect_nodes_bi
class NotificationsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
def setup_network(self):
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
self.block_filename = os.path.join(self.options.tmpdir, "blocks.txt")
self.tx_filename = os.path.join(self.options.tmpdir, "transactions.txt")
# -alertnotify and -blocknotify on node0, walletnotify on node1
self.extra_args = [["-blockversion=2",
"-alertnotify=echo %%s >> %s" % self.alert_filename,
"-blocknotify=echo %%s >> %s" % self.block_filename],
["-blockversion=211",
"-rescan",
"-walletnotify=echo %%s >> %s" % self.tx_filename]]
super().setup_network()
def run_test(self):
self.log.info("test -blocknotify")
block_count = 10
blocks = self.nodes[1].generate(block_count)
# wait at most 10 seconds for expected file size before reading the content
wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10)
# file content should equal the generated blocks hashes
with open(self.block_filename, 'r') as f:
assert_equal(sorted(blocks), sorted(f.read().splitlines()))
self.log.info("test -walletnotify")
# wait at most 10 seconds for expected file size before reading the content
wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10)
# file content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
with open(self.tx_filename, 'r') as f:
assert_equal(sorted(txids_rpc), sorted(f.read().splitlines()))
os.remove(self.tx_filename)
self.log.info("test -walletnotify after rescan")
# restart node to rescan to force wallet notifications
self.restart_node(1)
connect_nodes_bi(self.nodes, 0, 1)
wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10)
# file content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
with open(self.tx_filename, 'r') as f:
assert_equal(sorted(txids_rpc), sorted(f.read().splitlines()))
# Mine another 41 up-version blocks. -alertnotify should trigger on the 51st.
self.log.info("test -alertnotify")
self.nodes[1].generate(41)
self.sync_all()
# Give dashd 10 seconds to write the alert notification
wait_until(lambda: os.path.isfile(self.alert_filename) and os.path.getsize(self.alert_filename), timeout=10)
with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text = f.read()
# Mine more up-version blocks, should not get more alerts:
self.nodes[1].generate(2)
self.sync_all()
with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text2 = f.read()
self.log.info("-alertnotify should not continue notifying for more unknown version blocks")
assert_equal(alert_text, alert_text2)
if __name__ == '__main__':
NotificationsTest().main()

View File

@ -84,7 +84,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Check that we're not getting any versionbit-related errors in
# get*info()
assert(not VB_PATTERN.match(self.nodes[0].getinfo()["errors"]))
assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["errors"]))
assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["warnings"]))
assert(not VB_PATTERN.match(self.nodes[0].getnetworkinfo()["warnings"]))
# 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling
@ -96,7 +96,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# being of unexpected version.
# Check that get*info() shows some kind of error.
assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getinfo()["errors"])
assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["errors"])
assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"])
# Mine a period worth of expected blocks so the generic block-version warning
@ -117,7 +117,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Connecting one block should be enough to generate an error.
self.nodes[0].generate(1)
assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getinfo()["errors"])
assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["errors"])
assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"])
self.stop_nodes()
self.test_versionbits_in_alert_file()

View File

@ -151,7 +151,7 @@ class EstimateFeeTest(BitcoinTestFramework):
which we will use to generate our transactions.
"""
self.add_nodes(3, extra_args=[["-maxorphantxsize=1000", "-whitelist=127.0.0.1"],
["-blockmaxsize=17000", "-maxorphantxsize=1000", "-whitelist=127.0.0.1"],
["-blockmaxsize=17000", "-maxorphantxsize=1000", "-whitelist=127.0.0.1", "-deprecatedrpc=estimatefee"],
["-blockmaxsize=8000", "-maxorphantxsize=1000", "-whitelist=127.0.0.1"]])
# Use node0 to mine blocks for input splitting
# Node1 mines small blocks but that are bigger than the expected transaction rate.

View File

@ -1715,6 +1715,7 @@ class NodeConn(asyncore.dispatcher):
self.dstaddr = dstaddr
self.dstport = dstport
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.sendbuf = b""
self.recvbuf = b""
self.ver_send = 209
@ -1855,6 +1856,13 @@ class NodeConn(asyncore.dispatcher):
tmsg += h[:4]
tmsg += data
with mininode_lock:
if (len(self.sendbuf) == 0 and not pushbuf):
try:
sent = self.send(tmsg)
self.sendbuf = tmsg[sent:]
except BlockingIOError:
self.sendbuf = tmsg
else:
self.sendbuf += tmsg
self.last_sent = time.time()

View File

@ -295,6 +295,11 @@ class BitcoinTestFramework(object):
# Wait for nodes to stop
node.wait_until_stopped()
def restart_node(self, i, extra_args=None):
"""Stop and start a test node"""
self.stop_node(i)
self.start_node(i, extra_args)
def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None):
with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
try:

View File

@ -109,6 +109,7 @@ BASE_SCRIPTS= [
'spentindex.py',
'decodescript.py',
'blockchain.py',
'deprecated_rpc.py',
'disablewallet.py',
'net.py',
'keypool.py',
@ -164,7 +165,7 @@ EXTENDED_SCRIPTS = [
'txn_doublespend.py',
'txn_clone.py --mineblock',
'txindex.py',
'forknotify.py',
'notifications.py',
'invalidateblock.py',
]