mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
Merge pull request #3284 from PastaPastaPasta/backports-0.16-pr3
Backports 0.16 pr3
This commit is contained in:
commit
7e85f09007
@ -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-----
|
@ -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-----
|
@ -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?"
|
10
src/coins.h
10
src/coins.h
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
134
src/net.cpp
134
src/net.cpp
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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]++;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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", "")
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -168,6 +168,8 @@ public:
|
||||
bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
|
||||
};
|
||||
|
||||
bool IsDeprecatedRPCEnabled(const std::string& method);
|
||||
|
||||
extern CRPCTable tableRPC;
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
32
src/univalue/README.md
Normal 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).
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
147
src/univalue/lib/univalue_get.cpp
Normal file
147
src/univalue/lib/univalue_get.cpp
Normal 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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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";
|
||||
|
4
src/univalue/test/.gitignore
vendored
4
src/univalue/test/.gitignore
vendored
@ -1,4 +1,8 @@
|
||||
|
||||
object
|
||||
unitester
|
||||
test_json
|
||||
no_nul
|
||||
|
||||
*.trs
|
||||
*.log
|
||||
|
@ -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.
|
||||
|
BIN
src/univalue/test/fail42.json
Normal file
BIN
src/univalue/test/fail42.json
Normal file
Binary file not shown.
1
src/univalue/test/fail44.json
Normal file
1
src/univalue/test/fail44.json
Normal file
@ -0,0 +1 @@
|
||||
"This file ends without a newline or close-quote.
|
8
src/univalue/test/no_nul.cpp
Normal file
8
src/univalue/test/no_nul.cpp
Normal 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;
|
||||
}
|
395
src/univalue/test/object.cpp
Normal file
395
src/univalue/test/object.cpp
Normal 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;
|
||||
}
|
||||
|
1
src/univalue/test/round3.json
Normal file
1
src/univalue/test/round3.json
Normal file
@ -0,0 +1 @@
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
1
src/univalue/test/round4.json
Normal file
1
src/univalue/test/round4.json
Normal file
@ -0,0 +1 @@
|
||||
7
|
1
src/univalue/test/round5.json
Normal file
1
src/univalue/test/round5.json
Normal file
@ -0,0 +1 @@
|
||||
true
|
1
src/univalue/test/round6.json
Normal file
1
src/univalue/test/round6.json
Normal file
@ -0,0 +1 @@
|
||||
false
|
1
src/univalue/test/round7.json
Normal file
1
src/univalue/test/round7.json
Normal file
@ -0,0 +1 @@
|
||||
null
|
24
src/univalue/test/test_json.cpp
Normal file
24
src/univalue/test/test_json.cpp
Normal 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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
23
test/functional/deprecated_rpc.py
Executable file
23
test/functional/deprecated_rpc.py
Executable 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()
|
@ -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()
|
86
test/functional/notifications.py
Executable file
86
test/functional/notifications.py
Executable 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()
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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',
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user