aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.qt.include6
-rw-r--r--src/Makefile.test.include3
-rw-r--r--src/addrman.h2
-rw-r--r--src/blockencodings.cpp2
-rw-r--r--src/chainparams.cpp28
-rw-r--r--src/chainparams.h5
-rw-r--r--src/consensus/params.h8
-rw-r--r--src/dbwrapper.h12
-rw-r--r--src/httpserver.cpp71
-rw-r--r--src/httpserver.h10
-rw-r--r--src/indirectmap.h4
-rw-r--r--src/init.cpp115
-rw-r--r--src/key.h31
-rw-r--r--src/limitedmap.h7
-rw-r--r--src/main.cpp156
-rw-r--r--src/main.h21
-rw-r--r--src/miner.cpp2
-rw-r--r--src/net.cpp100
-rw-r--r--src/net.h7
-rw-r--r--src/netaddress.cpp716
-rw-r--r--src/netaddress.h171
-rw-r--r--src/netbase.cpp782
-rw-r--r--src/netbase.h167
-rw-r--r--src/policy/policy.cpp19
-rw-r--r--src/policy/policy.h12
-rw-r--r--src/primitives/transaction.cpp2
-rw-r--r--src/primitives/transaction.h8
-rw-r--r--src/protocol.h2
-rw-r--r--src/pubkey.h11
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/bitcoin.qrc2
-rw-r--r--src/qt/bitcoingui.cpp43
-rw-r--r--src/qt/bitcoingui.h9
-rw-r--r--src/qt/coincontroldialog.cpp90
-rw-r--r--src/qt/coincontroldialog.h8
-rw-r--r--src/qt/forms/coincontroldialog.ui61
-rw-r--r--src/qt/forms/sendcoinsdialog.ui39
-rw-r--r--src/qt/intro.cpp10
-rw-r--r--src/qt/intro.h5
-rw-r--r--src/qt/optionsdialog.cpp3
-rw-r--r--src/qt/optionsmodel.cpp36
-rw-r--r--src/qt/optionsmodel.h4
-rw-r--r--src/qt/res/icons/hd_disabled.pngbin0 -> 4328 bytes
-rw-r--r--src/qt/res/icons/hd_enabled.pngbin0 -> 1889 bytes
-rw-r--r--src/qt/res/src/hd_disabled.svg26
-rw-r--r--src/qt/res/src/hd_enabled.svg13
-rw-r--r--src/qt/rpcconsole.cpp9
-rw-r--r--src/qt/sendcoinsdialog.cpp9
-rw-r--r--src/qt/sendcoinsdialog.h1
-rw-r--r--src/qt/walletmodel.cpp5
-rw-r--r--src/qt/walletmodel.h2
-rw-r--r--src/qt/walletview.cpp6
-rw-r--r--src/qt/walletview.h2
-rw-r--r--src/rpc/blockchain.cpp36
-rw-r--r--src/rpc/mining.cpp24
-rw-r--r--src/rpc/net.cpp11
-rw-r--r--src/rpc/server.cpp9
-rw-r--r--src/script/bitcoinconsensus.h1
-rw-r--r--src/secp256k1/.gitignore19
-rw-r--r--src/secp256k1/.travis.yml19
-rw-r--r--src/secp256k1/Makefile.am72
-rw-r--r--src/secp256k1/README.md2
-rw-r--r--src/secp256k1/build-aux/m4/ax_jni_include_dir.m4140
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m44
-rw-r--r--src/secp256k1/configure.ac146
-rw-r--r--src/secp256k1/libsecp256k1.pc.in2
-rw-r--r--src/secp256k1/sage/group_prover.sage322
-rw-r--r--src/secp256k1/sage/secp256k1.sage306
-rw-r--r--src/secp256k1/sage/weierstrass_prover.sage264
-rw-r--r--src/secp256k1/src/asm/field_10x26_arm.s919
-rw-r--r--src/secp256k1/src/bench_ecdh.c3
-rw-r--r--src/secp256k1/src/bench_internal.c34
-rw-r--r--src/secp256k1/src/bench_verify.c44
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h67
-rw-r--r--src/secp256k1/src/ecmult_impl.h2
-rw-r--r--src/secp256k1/src/field.h8
-rw-r--r--src/secp256k1/src/field_10x26_impl.h12
-rw-r--r--src/secp256k1/src/field_5x52_impl.h1
-rw-r--r--src/secp256k1/src/field_5x52_int128_impl.h4
-rw-r--r--src/secp256k1/src/field_impl.h36
-rw-r--r--src/secp256k1/src/group.h5
-rw-r--r--src/secp256k1/src/group_impl.h36
-rw-r--r--src/secp256k1/src/hash.h2
-rw-r--r--src/secp256k1/src/hash_impl.h10
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java472
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java247
-rw-r--r--src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java45
-rw-r--r--src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java51
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c412
-rw-r--r--src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h112
-rw-r--r--src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c15
-rw-r--r--src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h22
-rw-r--r--src/secp256k1/src/modules/ecdh/Makefile.am.include2
-rw-r--r--src/secp256k1/src/modules/recovery/Makefile.am.include2
-rw-r--r--src/secp256k1/src/modules/schnorr/Makefile.am.include2
-rw-r--r--src/secp256k1/src/num.h6
-rw-r--r--src/secp256k1/src/num_gmp_impl.h26
-rw-r--r--src/secp256k1/src/scalar_impl.h2
-rw-r--r--src/secp256k1/src/secp256k1.c15
-rw-r--r--src/secp256k1/src/tests.c190
-rw-r--r--src/serialize.h4
-rw-r--r--src/test/README.md21
-rw-r--r--src/test/addrman_tests.cpp151
-rw-r--r--src/test/base58_tests.cpp3
-rwxr-xr-xsrc/test/bitcoin-util-test.py2
-rw-r--r--src/test/buildenv.py.in2
-rw-r--r--src/test/hash_tests.cpp4
-rw-r--r--src/test/miner_tests.cpp4
-rw-r--r--src/test/net_tests.cpp46
-rw-r--r--src/test/netbase_tests.cpp281
-rw-r--r--src/test/pmt_tests.cpp1
-rw-r--r--src/test/rpc_tests.cpp12
-rw-r--r--src/test/test_bitcoin.cpp5
-rw-r--r--src/test/util_tests.cpp9
-rw-r--r--src/timedata.cpp2
-rw-r--r--src/tinyformat.h700
-rw-r--r--src/torcontrol.cpp7
-rw-r--r--src/txmempool.cpp11
-rw-r--r--src/util.h28
-rw-r--r--src/validationinterface.cpp4
-rw-r--r--src/validationinterface.h6
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/rpcdump.cpp32
-rw-r--r--src/wallet/rpcwallet.cpp25
-rw-r--r--src/wallet/test/accounting_tests.cpp6
-rw-r--r--src/wallet/test/rpc_wallet_tests.cpp229
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp4
-rw-r--r--src/wallet/wallet.cpp389
-rw-r--r--src/wallet/wallet.h36
-rw-r--r--src/wallet/walletdb.cpp16
-rw-r--r--src/zmq/zmqnotificationinterface.cpp2
-rw-r--r--src/zmq/zmqnotificationinterface.h2
133 files changed, 6448 insertions, 2647 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e3eaacdb4c..03fac5bf97 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -105,6 +105,7 @@ BITCOIN_CORE_H = \
merkleblock.h \
miner.h \
net.h \
+ netaddress.h \
netbase.h \
noui.h \
policy/fees.h \
@@ -289,6 +290,7 @@ libbitcoin_common_a_SOURCES = \
core_write.cpp \
key.cpp \
keystore.cpp \
+ netaddress.cpp \
netbase.cpp \
protocol.cpp \
scheduler.cpp \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 7730aba375..8947aeaca0 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -257,6 +257,8 @@ RES_ICONS = \
qt/res/icons/filesave.png \
qt/res/icons/fontbigger.png \
qt/res/icons/fontsmaller.png \
+ qt/res/icons/hd_disabled.png \
+ qt/res/icons/hd_enabled.png \
qt/res/icons/history.png \
qt/res/icons/info.png \
qt/res/icons/key.png \
@@ -271,14 +273,14 @@ RES_ICONS = \
qt/res/icons/synced.png \
qt/res/icons/transaction0.png \
qt/res/icons/transaction2.png \
+ qt/res/icons/transaction_abandoned.png \
qt/res/icons/transaction_conflicted.png \
qt/res/icons/tx_inout.png \
qt/res/icons/tx_input.png \
qt/res/icons/tx_output.png \
qt/res/icons/tx_mined.png \
qt/res/icons/warning.png \
- qt/res/icons/verify.png \
- qt/res/icons/transaction_abandoned.png
+ qt/res/icons/verify.png
BITCOIN_QT_CPP = \
qt/bantablemodel.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 27e7694748..0748d1a39d 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -97,8 +97,7 @@ BITCOIN_TESTS += \
wallet/test/wallet_test_fixture.h \
wallet/test/accounting_tests.cpp \
wallet/test/wallet_tests.cpp \
- wallet/test/crypto_tests.cpp \
- wallet/test/rpc_wallet_tests.cpp
+ wallet/test/crypto_tests.cpp
endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
diff --git a/src/addrman.h b/src/addrman.h
index 1caf540758..9bab39049d 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_ADDRMAN_H
#define BITCOIN_ADDRMAN_H
-#include "netbase.h"
+#include "netaddress.h"
#include "protocol.h"
#include "random.h"
#include "sync.h"
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 5c4c3bd274..df237f8f26 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -75,7 +75,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
}
prefilled_count = cmpctblock.prefilledtxn.size();
- // Calculate map of txids -> positions and check mempool to see what we have (or dont)
+ // Calculate map of txids -> positions and check mempool to see what we have (or don't)
// Because well-formed cmpctblock messages will have a (relatively) uniform distribution
// of short IDs, any highly-uneven distribution of elements can be safely treated as a
// READ_STATUS_FAILED.
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 86bef1e105..ea6e3aada2 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -71,11 +71,10 @@ public:
CMainParams() {
strNetworkID = "main";
consensus.nSubsidyHalvingInterval = 210000;
- consensus.nMajorityEnforceBlockUpgrade = 750;
- consensus.nMajorityRejectBlockOutdated = 950;
- consensus.nMajorityWindow = 1000;
consensus.BIP34Height = 227931;
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
+ consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
+ consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -167,11 +166,10 @@ public:
CTestNetParams() {
strNetworkID = "test";
consensus.nSubsidyHalvingInterval = 210000;
- consensus.nMajorityEnforceBlockUpgrade = 51;
- consensus.nMajorityRejectBlockOutdated = 75;
- consensus.nMajorityWindow = 100;
consensus.BIP34Height = 21111;
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
+ consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
+ consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -247,11 +245,10 @@ public:
CRegTestParams() {
strNetworkID = "regtest";
consensus.nSubsidyHalvingInterval = 150;
- consensus.nMajorityEnforceBlockUpgrade = 750;
- consensus.nMajorityRejectBlockOutdated = 950;
- consensus.nMajorityWindow = 1000;
- consensus.BIP34Height = -1; // BIP34 has not necessarily activated on regtest
+ consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests)
consensus.BIP34Hash = uint256();
+ consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests)
+ consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in rpc activation tests)
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -303,6 +300,12 @@ public:
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >();
}
+
+ void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+ {
+ consensus.vDeployments[d].nStartTime = nStartTime;
+ consensus.vDeployments[d].nTimeout = nTimeout;
+ }
};
static CRegTestParams regTestParams;
@@ -330,4 +333,9 @@ void SelectParams(const std::string& network)
SelectBaseParams(network);
pCurrentParams = &Params(network);
}
+
+void UpdateRegtestBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+{
+ regTestParams.UpdateBIP9Parameters(d, nStartTime, nTimeout);
+}
diff --git a/src/chainparams.h b/src/chainparams.h
index 638893e9ad..0c3820b7c6 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -112,4 +112,9 @@ CChainParams& Params(const std::string& chain);
*/
void SelectParams(const std::string& chain);
+/**
+ * Allows modifying the BIP9 regtest parameters.
+ */
+void UpdateRegtestBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
+
#endif // BITCOIN_CHAINPARAMS_H
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 822ec87d69..5b2f49184f 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -39,13 +39,13 @@ struct BIP9Deployment {
struct Params {
uint256 hashGenesisBlock;
int nSubsidyHalvingInterval;
- /** Used to check majorities for block version upgrade */
- int nMajorityEnforceBlockUpgrade;
- int nMajorityRejectBlockOutdated;
- int nMajorityWindow;
/** Block height and hash at which BIP34 becomes active */
int BIP34Height;
uint256 BIP34Hash;
+ /** Block height at which BIP65 becomes active */
+ int BIP65Height;
+ /** Block height at which BIP66 becomes active */
+ int BIP66Height;
/**
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargetting period,
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index a0779d3ab9..47bdb31b5b 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -52,9 +52,9 @@ private:
public:
/**
- * @param[in] parent CDBWrapper that this batch is to be submitted to
+ * @param[in] _parent CDBWrapper that this batch is to be submitted to
*/
- CDBBatch(const CDBWrapper &parent) : parent(parent) { };
+ CDBBatch(const CDBWrapper &_parent) : parent(_parent) { };
template <typename K, typename V>
void Write(const K& key, const V& value)
@@ -94,11 +94,11 @@ private:
public:
/**
- * @param[in] parent Parent CDBWrapper instance.
- * @param[in] piterIn The original leveldb iterator.
+ * @param[in] _parent Parent CDBWrapper instance.
+ * @param[in] _piter The original leveldb iterator.
*/
- CDBIterator(const CDBWrapper &parent, leveldb::Iterator *piterIn) :
- parent(parent), piter(piterIn) { };
+ CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
+ parent(_parent), piter(_piter) { };
~CDBIterator();
bool Valid();
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 812940eaf9..f921305fcc 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
+#include <future>
#include <event2/event.h>
#include <event2/http.h>
@@ -34,9 +35,6 @@
#endif
#endif
-#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
-#include <boost/foreach.hpp>
-
/** Maximum size of http request (request line + headers) */
static const size_t MAX_HEADERS_SIZE = 8192;
@@ -68,8 +66,8 @@ class WorkQueue
{
private:
/** Mutex protects entire object */
- CWaitableCriticalSection cs;
- CConditionVariable cond;
+ std::mutex cs;
+ std::condition_variable cond;
std::deque<std::unique_ptr<WorkItem>> queue;
bool running;
size_t maxDepth;
@@ -82,12 +80,12 @@ private:
WorkQueue &wq;
ThreadCounter(WorkQueue &w): wq(w)
{
- boost::lock_guard<boost::mutex> lock(wq.cs);
+ std::lock_guard<std::mutex> lock(wq.cs);
wq.numThreads += 1;
}
~ThreadCounter()
{
- boost::lock_guard<boost::mutex> lock(wq.cs);
+ std::lock_guard<std::mutex> lock(wq.cs);
wq.numThreads -= 1;
wq.cond.notify_all();
}
@@ -108,7 +106,7 @@ public:
/** Enqueue a work item */
bool Enqueue(WorkItem* item)
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
if (queue.size() >= maxDepth) {
return false;
}
@@ -123,7 +121,7 @@ public:
while (running) {
std::unique_ptr<WorkItem> i;
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
while (running && queue.empty())
cond.wait(lock);
if (!running)
@@ -137,14 +135,14 @@ public:
/** Interrupt and exit loops */
void Interrupt()
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
running = false;
cond.notify_all();
}
/** Wait for worker threads to exit */
void WaitExit()
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
while (numThreads > 0)
cond.wait(lock);
}
@@ -152,7 +150,7 @@ public:
/** Return current depth of queue */
size_t Depth()
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
return queue.size();
}
};
@@ -189,7 +187,7 @@ static bool ClientAllowed(const CNetAddr& netaddr)
{
if (!netaddr.IsValid())
return false;
- BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets)
+ for(const CSubNet& subnet : rpc_allow_subnets)
if (subnet.Match(netaddr))
return true;
return false;
@@ -199,12 +197,17 @@ static bool ClientAllowed(const CNetAddr& netaddr)
static bool InitHTTPAllowList()
{
rpc_allow_subnets.clear();
- rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
- rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
+ CNetAddr localv4;
+ CNetAddr localv6;
+ LookupHost("127.0.0.1", localv4, false);
+ LookupHost("::1", localv6, false);
+ rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet
+ rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost
if (mapMultiArgs.count("-rpcallowip")) {
const std::vector<std::string>& vAllow = mapMultiArgs["-rpcallowip"];
- BOOST_FOREACH (std::string strAllow, vAllow) {
- CSubNet subnet(strAllow);
+ for (std::string strAllow : vAllow) {
+ CSubNet subnet;
+ LookupSubNet(strAllow.c_str(), subnet);
if (!subnet.IsValid()) {
uiInterface.ThreadSafeMessageBox(
strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
@@ -215,7 +218,7 @@ static bool InitHTTPAllowList()
}
}
std::string strAllowed;
- BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets)
+ for (const CSubNet& subnet : rpc_allow_subnets)
strAllowed += subnet.ToString() + " ";
LogPrint("http", "Allowing HTTP connections from: %s\n", strAllowed);
return true;
@@ -302,13 +305,14 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
}
/** Event dispatcher thread */
-static void ThreadHTTP(struct event_base* base, struct evhttp* http)
+static bool ThreadHTTP(struct event_base* base, struct evhttp* http)
{
RenameThread("bitcoin-http");
LogPrint("http", "Entering http event loop\n");
event_base_dispatch(base);
// Event loop will be interrupted by InterruptHTTPServer()
LogPrint("http", "Exited http event loop\n");
+ return event_base_got_break(base) == 0;
}
/** Bind HTTP server to specified addresses */
@@ -437,17 +441,22 @@ bool InitHTTPServer()
return true;
}
-boost::thread threadHTTP;
+std::thread threadHTTP;
+std::future<bool> threadResult;
bool StartHTTPServer()
{
LogPrint("http", "Starting HTTP server\n");
int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
- threadHTTP = boost::thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP));
+ std::packaged_task<bool(event_base*, evhttp*)> task(ThreadHTTP);
+ threadResult = task.get_future();
+ threadHTTP = std::thread(std::move(task), eventBase, eventHTTP);
- for (int i = 0; i < rpcThreads; i++)
- boost::thread(boost::bind(&HTTPWorkQueueRun, workQueue));
+ for (int i = 0; i < rpcThreads; i++) {
+ std::thread rpc_worker(HTTPWorkQueueRun, workQueue);
+ rpc_worker.detach();
+ }
return true;
}
@@ -456,7 +465,7 @@ void InterruptHTTPServer()
LogPrint("http", "Interrupting HTTP server\n");
if (eventHTTP) {
// Unlisten sockets
- BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) {
+ for (evhttp_bound_socket *socket : boundSockets) {
evhttp_del_accept_socket(eventHTTP, socket);
}
// Reject requests on current connections
@@ -482,15 +491,11 @@ void StopHTTPServer()
// master that appears to be solved, so in the future that solution
// could be used again (if desirable).
// (see discussion in https://github.com/bitcoin/bitcoin/pull/6990)
-#if BOOST_VERSION >= 105000
- if (!threadHTTP.try_join_for(boost::chrono::milliseconds(2000))) {
-#else
- if (!threadHTTP.timed_join(boost::posix_time::milliseconds(2000))) {
-#endif
+ if (threadResult.valid() && threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) {
LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n");
event_base_loopbreak(eventBase);
- threadHTTP.join();
}
+ threadHTTP.join();
}
if (eventHTTP) {
evhttp_free(eventHTTP);
@@ -517,7 +522,7 @@ static void httpevent_callback_fn(evutil_socket_t, short, void* data)
delete self;
}
-HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler):
+HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void(void)>& handler):
deleteWhenTriggered(deleteWhenTriggered), handler(handler)
{
ev = event_new(base, -1, 0, httpevent_callback_fn, this);
@@ -599,7 +604,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent* ev = new HTTPEvent(eventBase, true,
- boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL));
+ std::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL));
ev->trigger(0);
replySent = true;
req = 0; // transferred back to main thread
@@ -614,7 +619,7 @@ CService HTTPRequest::GetPeer()
const char* address = "";
uint16_t port = 0;
evhttp_connection_get_peer(con, (char**)&address, &port);
- peer = CService(address, port);
+ peer = LookupNumeric(address, port);
}
return peer;
}
diff --git a/src/httpserver.h b/src/httpserver.h
index 20a119cc5c..49d67f4b88 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -7,9 +7,7 @@
#include <string>
#include <stdint.h>
-#include <boost/thread.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/function.hpp>
+#include <functional>
static const int DEFAULT_HTTP_THREADS=4;
static const int DEFAULT_HTTP_WORKQUEUE=16;
@@ -35,7 +33,7 @@ void InterruptHTTPServer();
void StopHTTPServer();
/** Handler for requests to a certain HTTP path */
-typedef boost::function<void(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
+typedef std::function<bool(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
/** Register handler for prefix.
* If multiple handlers match a prefix, the first-registered one will
* be invoked.
@@ -132,7 +130,7 @@ public:
* deleteWhenTriggered deletes this event object after the event is triggered (and the handler called)
* handler is the handler to call when the event is triggered.
*/
- HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler);
+ HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void(void)>& handler);
~HTTPEvent();
/** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after
@@ -141,7 +139,7 @@ public:
void trigger(struct timeval* tv);
bool deleteWhenTriggered;
- boost::function<void(void)> handler;
+ std::function<void(void)> handler;
private:
struct event* ev;
};
diff --git a/src/indirectmap.h b/src/indirectmap.h
index 28e1e8dedd..76da4a6bd5 100644
--- a/src/indirectmap.h
+++ b/src/indirectmap.h
@@ -1,3 +1,7 @@
+// Copyright (c) 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.
+
#ifndef BITCOIN_INDIRECTMAP_H
#define BITCOIN_INDIRECTMAP_H
diff --git a/src/init.cpp b/src/init.cpp
index 312dfe1699..13c68e512a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -21,6 +21,7 @@
#include "key.h"
#include "main.h"
#include "miner.h"
+#include "netbase.h"
#include "net.h"
#include "policy/policy.h"
#include "rpc/server.h"
@@ -410,6 +411,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
+ strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)");
}
string debugCategories = "addrman, alert, bench, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT)
@@ -446,7 +448,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("Node relay options:"));
if (showDebug)
strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard()));
- strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Minimum bytes per sigop in transactions we relay and mine (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
+ strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER));
strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY));
strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT));
@@ -481,7 +483,7 @@ std::string LicenseInfo()
{
const std::string URL_SOURCE_CODE = "<https://github.com/bitcoin/bitcoin>";
const std::string URL_WEBSITE = "<https://bitcoincore.org>";
- // todo: remove urls from translations on next change
+
return CopyrightHolders(strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) + " ") + "\n" +
"\n" +
strprintf(_("Please contribute if you find %s useful. "
@@ -493,9 +495,9 @@ std::string LicenseInfo()
"\n" +
"\n" +
_("This is experimental software.") + "\n" +
- _("Distributed under the MIT software license, see the accompanying file COPYING or <http://www.opensource.org/licenses/mit-license.php>.") + "\n" +
+ strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s"), "COPYING", "<https://opensource.org/licenses/MIT>") + "\n" +
"\n" +
- _("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.") +
+ strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard."), "<https://www.openssl.org>") +
"\n";
}
@@ -510,6 +512,21 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex
boost::thread t(runCommand, strCmd); // thread runs free
}
+static bool fHaveGenesis = false;
+static boost::mutex cs_GenesisWait;
+static CConditionVariable condvar_GenesisWait;
+
+static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
+{
+ if (pBlockIndex != NULL) {
+ {
+ boost::unique_lock<boost::mutex> lock_GenesisWait(cs_GenesisWait);
+ fHaveGenesis = true;
+ }
+ condvar_GenesisWait.notify_all();
+ }
+}
+
struct CImportingNow
{
CImportingNow() {
@@ -975,6 +992,41 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
}
+ if (!mapMultiArgs["-bip9params"].empty()) {
+ // Allow overriding BIP9 parameters for testing
+ if (!Params().MineBlocksOnDemand()) {
+ return InitError("BIP9 parameters may only be overridden on regtest.");
+ }
+ const vector<string>& deployments = mapMultiArgs["-bip9params"];
+ for (auto i : deployments) {
+ std::vector<std::string> vDeploymentParams;
+ boost::split(vDeploymentParams, i, boost::is_any_of(":"));
+ if (vDeploymentParams.size() != 3) {
+ return InitError("BIP9 parameters malformed, expecting deployment:start:end");
+ }
+ int64_t nStartTime, nTimeout;
+ if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
+ return InitError(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
+ }
+ if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
+ return InitError(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
+ }
+ bool found = false;
+ for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j)
+ {
+ if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) {
+ UpdateRegtestBIP9Parameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
+ found = true;
+ LogPrintf("Setting BIP9 activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
+ break;
+ }
+ }
+ if (!found) {
+ return InitError(strprintf("Invalid deployment (%s)", vDeploymentParams[0]));
+ }
+ }
+ }
+
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
// Initialize elliptic curve code
@@ -1015,7 +1067,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Using data directory %s\n", strDataDir);
LogPrintf("Using config file %s\n", GetConfigFile().string());
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
- std::ostringstream strErrors;
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
if (nScriptCheckThreads) {
@@ -1083,7 +1134,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (mapArgs.count("-whitelist")) {
BOOST_FOREACH(const std::string& net, mapMultiArgs["-whitelist"]) {
- CSubNet subnet(net);
+ CSubNet subnet;
+ LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid())
return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
CNode::AddWhitelistedRange(subnet);
@@ -1096,7 +1148,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
std::string proxyArg = GetArg("-proxy", "");
SetLimited(NET_TOR);
if (proxyArg != "" && proxyArg != "0") {
- proxyType addrProxy = proxyType(CService(proxyArg, 9050), proxyRandomize);
+ CService resolved(LookupNumeric(proxyArg.c_str(), 9050));
+ proxyType addrProxy = proxyType(resolved, proxyRandomize);
if (!addrProxy.IsValid())
return InitError(strprintf(_("Invalid -proxy address: '%s'"), proxyArg));
@@ -1115,7 +1168,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (onionArg == "0") { // Handle -noonion/-onion=0
SetLimited(NET_TOR); // set onions as unreachable
} else {
- proxyType addrOnion = proxyType(CService(onionArg, 9050), proxyRandomize);
+ CService resolved(LookupNumeric(onionArg.c_str(), 9050));
+ proxyType addrOnion = proxyType(resolved, proxyRandomize);
if (!addrOnion.IsValid())
return InitError(strprintf(_("Invalid -onion address: '%s'"), onionArg));
SetProxy(NET_TOR, addrOnion);
@@ -1216,7 +1270,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// cache size calculations
int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
- nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache
+ nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
int64_t nBlockTreeDBCache = nTotalCache / 8;
nBlockTreeDBCache = std::min(nBlockTreeDBCache, (GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20);
nTotalCache -= nBlockTreeDBCache;
@@ -1286,7 +1340,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
break;
}
- if (!fReindex) {
+ if (!fReindex && chainActive.Tip() != NULL) {
uiInterface.InitMessage(_("Rewinding blocks..."));
if (!RewindBlockIndex(chainparams)) {
strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
@@ -1403,6 +1457,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 10: import blocks
+ if (!CheckDiskSpace())
+ return false;
+
+ // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
+ // No locking, as this happens before any background thread is started.
+ if (chainActive.Tip() == NULL) {
+ uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait);
+ } else {
+ fHaveGenesis = true;
+ }
+
if (mapArgs.count("-blocknotify"))
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
@@ -1412,38 +1477,23 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
BOOST_FOREACH(const std::string& strFile, mapMultiArgs["-loadblock"])
vImportFiles.push_back(strFile);
}
+
threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles));
// Wait for genesis block to be processed
- bool fHaveGenesis = false;
- while (!fHaveGenesis && !fRequestShutdown) {
- {
- LOCK(cs_main);
- fHaveGenesis = (chainActive.Tip() != NULL);
- }
-
- if (!fHaveGenesis) {
- MilliSleep(10);
+ {
+ boost::unique_lock<boost::mutex> lock(cs_GenesisWait);
+ while (!fHaveGenesis) {
+ condvar_GenesisWait.wait(lock);
}
+ uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
}
// ********************************************************* Step 11: start node
- if (!CheckDiskSpace())
- return false;
-
- if (!strErrors.str().empty())
- return InitError(strErrors.str());
-
//// debug print
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
LogPrintf("nBestHeight = %d\n", chainActive.Height());
-#ifdef ENABLE_WALLET
- LogPrintf("setKeyPool.size() = %u\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0);
- LogPrintf("mapWallet.size() = %u\n", pwalletMain ? pwalletMain->mapWallet.size() : 0);
- LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
-#endif
-
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler);
@@ -1456,9 +1506,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifdef ENABLE_WALLET
if (pwalletMain) {
- // Add wallet transactions that aren't already in a block to mapTransactions
- pwalletMain->ReacceptWalletTransactions();
-
// Run a thread to flush wallet periodically
threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile)));
}
diff --git a/src/key.h b/src/key.h
index b4f48d59f5..b589710bad 100644
--- a/src/key.h
+++ b/src/key.h
@@ -15,7 +15,7 @@
#include <vector>
-/**
+/**
* secp256k1:
* const unsigned int PRIVATE_KEY_SIZE = 279;
* const unsigned int PUBLIC_KEY_SIZE = 65;
@@ -45,6 +45,8 @@ private:
//! The actual byte data
unsigned char vch[32];
+ static_assert(sizeof(vch) == 32, "vch must be 32 bytes in length to not break serialization");
+
//! Check whether the 32-byte array pointed to be vch is valid keydata.
bool static Check(const unsigned char* vch);
@@ -70,20 +72,19 @@ public:
friend bool operator==(const CKey& a, const CKey& b)
{
- return a.fCompressed == b.fCompressed && a.size() == b.size() &&
- memcmp(&a.vch[0], &b.vch[0], a.size()) == 0;
+ return a.fCompressed == b.fCompressed &&
+ a.size() == b.size() &&
+ memcmp(&a.vch[0], &b.vch[0], a.size()) == 0;
}
//! Initialize using begin and end iterators to byte data.
template <typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn)
{
- if (pend - pbegin != 32) {
+ if (pend - pbegin != sizeof(vch)) {
fValid = false;
- return;
- }
- if (Check(&pbegin[0])) {
- memcpy(vch, (unsigned char*)&pbegin[0], 32);
+ } else if (Check(&pbegin[0])) {
+ memcpy(vch, (unsigned char*)&pbegin[0], sizeof(vch));
fValid = true;
fCompressed = fCompressedIn;
} else {
@@ -92,7 +93,7 @@ public:
}
//! Simple read-only vector-like interface.
- unsigned int size() const { return (fValid ? 32 : 0); }
+ unsigned int size() const { return (fValid ? sizeof(vch) : 0); }
const unsigned char* begin() const { return vch; }
const unsigned char* end() const { return vch + size(); }
@@ -110,7 +111,7 @@ public:
/**
* Convert the private key to a CPrivKey (serialized OpenSSL private key data).
- * This is expensive.
+ * This is expensive.
*/
CPrivKey GetPrivKey() const;
@@ -146,9 +147,6 @@ public:
//! Load private key and check that public key matches.
bool Load(CPrivKey& privkey, CPubKey& vchPubKey, bool fSkipCheck);
-
- //! Check whether an element of a signature (r or s) is valid.
- static bool CheckSignatureElement(const unsigned char* vch, int len, bool half);
};
struct CExtKey {
@@ -160,8 +158,11 @@ struct CExtKey {
friend bool operator==(const CExtKey& a, const CExtKey& b)
{
- return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
- a.chaincode == b.chaincode && a.key == b.key;
+ return a.nDepth == b.nDepth &&
+ memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
+ a.nChild == b.nChild &&
+ a.chaincode == b.chaincode &&
+ a.key == b.key;
}
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
diff --git a/src/limitedmap.h b/src/limitedmap.h
index 4d9bb4fa21..7841d7f4a4 100644
--- a/src/limitedmap.h
+++ b/src/limitedmap.h
@@ -66,8 +66,11 @@ public:
}
void update(const_iterator itIn, const mapped_type& v)
{
- // TODO: When we switch to C++11, use map.erase(itIn, itIn) to get the non-const iterator.
- iterator itTarget = map.find(itIn->first);
+ // Using map::erase() with empty range instead of map::find() to get a non-const iterator,
+ // since it is a constant time operation in C++11. For more details, see
+ // https://stackoverflow.com/questions/765148/how-to-remove-constness-of-const-iterator
+ iterator itTarget = map.erase(itIn, itIn);
+
if (itTarget == map.end())
return;
std::pair<rmap_iterator, rmap_iterator> itPair = rmap.equal_range(itTarget->second);
diff --git a/src/main.cpp b/src/main.cpp
index 0e7d172fbb..9a064b8e40 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -74,7 +74,6 @@ bool fHavePruned = false;
bool fPruneMode = false;
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
bool fRequireStandard = true;
-unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
size_t nCoinCacheUsage = 5000 * 300;
@@ -107,11 +106,6 @@ map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);
map<COutPoint, set<map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main);
void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/**
- * Returns true if there are nRequired or more blocks of minVersion or above
- * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards.
- */
-static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams);
static void CheckBlockIndex(const Consensus::Params& consensusParams);
/** Constant stuff for coinbase transactions we create: */
@@ -788,7 +782,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
return true;
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ for (const auto& txin : tx.vin) {
if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL))
return false;
}
@@ -1002,11 +996,11 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
unsigned int GetLegacySigOpCount(const CTransaction& tx)
{
unsigned int nSigOps = 0;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for (const auto& txin : tx.vin)
{
nSigOps += txin.scriptSig.GetSigOpCount(false);
}
- BOOST_FOREACH(const CTxOut& txout, tx.vout)
+ for (const auto& txout : tx.vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
}
@@ -1064,7 +1058,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
// Check for negative or overflow output values
CAmount nValueOut = 0;
- BOOST_FOREACH(const CTxOut& txout, tx.vout)
+ for (const auto& txout : tx.vout)
{
if (txout.nValue < 0)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
@@ -1077,7 +1071,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
// Check for duplicate inputs
set<COutPoint> vInOutPoints;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for (const auto& txin : tx.vin)
{
if (vInOutPoints.count(txin.prevout))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
@@ -1091,7 +1085,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
}
else
{
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for (const auto& txin : tx.vin)
if (txin.prevout.IsNull())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
}
@@ -1144,13 +1138,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
// Reject transactions with witness before segregated witness activates (override with -prematurewitness)
- if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus())) {
+ bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus());
+ if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !witnessEnabled) {
return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
}
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
- if (fRequireStandard && !IsStandardTx(tx, reason))
+ if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
// Only accept nLockTime-using transactions that can be mined in the next
@@ -1179,7 +1174,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// nSequence >= maxint-1 on all inputs.
//
// maxint-1 is picked to still allow use of nLockTime by
- // non-replacable transactions. All inputs rather than just one
+ // non-replaceable transactions. All inputs rather than just one
// is for the sake of multi-party protocols, where we don't
// want a single party to be able to disable replacement.
//
@@ -1296,7 +1291,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
// merely non-standard transaction.
- if ((nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) || (nBytesPerSigOp && nSigOpsCost > nSize * WITNESS_SCALE_FACTOR / nBytesPerSigOp))
+ if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
strprintf("%d", nSigOpsCost));
@@ -1546,7 +1541,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
}
- SyncWithWallets(tx, NULL, NULL);
+ SyncWithWallets(tx, NULL);
return true;
}
@@ -1993,7 +1988,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// is safe because block merkle hashes are still computed and checked,
// and any change will be caught at the next checkpoint. Of course, if
// the checkpoint is for a chain that's invalid due to false scriptSigs
- // this optimisation would allow an invalid chain to be accepted.
+ // this optimization would allow an invalid chain to be accepted.
if (fScriptChecks) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
@@ -2372,15 +2367,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
- // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks,
- // when 75% of the network has upgraded:
- if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) {
+ // Start enforcing the DERSIG (BIP66) rule
+ if (pindex->nHeight >= chainparams.GetConsensus().BIP66Height) {
flags |= SCRIPT_VERIFY_DERSIG;
}
- // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4
- // blocks, when 75% of the network has upgraded:
- if (block.nVersion >= 4 && IsSuperMajority(4, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) {
+ // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule
+ if (pindex->nHeight >= chainparams.GetConsensus().BIP65Height) {
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
}
@@ -2777,7 +2770,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
- SyncWithWallets(tx, pindexDelete->pprev, NULL);
+ SyncWithWallets(tx, pindexDelete->pprev);
}
return true;
}
@@ -2792,7 +2785,7 @@ static int64_t nTimePostConnect = 0;
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
-bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock)
+bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::list<CTransaction> &txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int> > &txChanged)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
@@ -2828,20 +2821,13 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
return false;
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
- // Remove conflicting transactions from the mempool.
- list<CTransaction> txConflicted;
+ // Remove conflicting transactions from the mempool.;
mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload());
// Update chainActive & related variables.
UpdateTip(pindexNew, chainparams);
- // Tell wallet about transactions that went from mempool
- // to conflicted:
- BOOST_FOREACH(const CTransaction &tx, txConflicted) {
- SyncWithWallets(tx, pindexNew, NULL);
- }
- // ... and about transactions that got confirmed:
- BOOST_FOREACH(const CTransaction &tx, pblock->vtx) {
- SyncWithWallets(tx, pindexNew, pblock);
- }
+
+ for(unsigned int i=0; i < pblock->vtx.size(); i++)
+ txChanged.push_back(std::make_tuple(pblock->vtx[i], pindexNew, i));
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
@@ -2923,7 +2909,7 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/
-static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound)
+static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::list<CTransaction>& txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int> >& txChanged)
{
AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -2956,7 +2942,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// Connect new blocks.
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
- if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) {
+ if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, txConflicted, txChanged)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
if (!state.CorruptionPossible())
@@ -3031,6 +3017,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
break;
const CBlockIndex *pindexFork;
+ std::list<CTransaction> txConflicted;
+ std::vector<std::tuple<CTransaction,CBlockIndex*,int> > txChanged;
bool fInitialDownload;
int nNewHeight;
{
@@ -3045,7 +3033,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
return true;
bool fInvalidFound = false;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound))
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound, txConflicted, txChanged))
return false;
if (fInvalidFound) {
@@ -3060,6 +3048,17 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
// Notifications/callbacks that can run without cs_main
+
+ // throw all transactions though the signal-interface
+ // while _not_ holding the cs_main lock
+ BOOST_FOREACH(const CTransaction &tx, txConflicted)
+ {
+ SyncWithWallets(tx, pindexNewTip);
+ }
+ // ... and about transactions that got confirmed:
+ for(unsigned int i = 0; i < txChanged.size(); i++)
+ SyncWithWallets(std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i]));
+
// Always notify the UI if a new block tip was connected
if (pindexFork != pindexNewTip) {
uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
@@ -3404,13 +3403,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
// Check transactions
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ for (const auto& tx : block.vtx)
if (!CheckTransaction(tx, state))
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage()));
unsigned int nSigOps = 0;
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ for (const auto& tx : block.vtx)
{
nSigOps += GetLegacySigOpCount(tx);
}
@@ -3502,8 +3501,9 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev, int64_t nAdjustedTime)
+bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
+ const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
@@ -3517,18 +3517,19 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
- for (int32_t version = 2; version < 5; ++version) // check for version 2, 3 and 4 upgrades
- if (block.nVersion < version && IsSuperMajority(version, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams))
- return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", version - 1),
- strprintf("rejected nVersion=0x%08x block", version - 1));
+ // check for version 2, 3 and 4 upgrades
+ if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||
+ (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
+ (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
+ return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion),
+ strprintf("rejected nVersion=0x%08x block", block.nVersion));
return true;
}
-bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev)
+bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
- const Consensus::Params& consensusParams = Params().GetConsensus();
// Start enforcing BIP113 (Median Time Past) using versionbits logic.
int nLockTimeFlags = 0;
@@ -3541,15 +3542,14 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
: block.GetBlockTime();
// Check that all transactions are finalized
- BOOST_FOREACH(const CTransaction& tx, block.vtx) {
+ for (const auto& tx : block.vtx) {
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) {
return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
}
}
- // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
- // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
- if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))
+ // Enforce rule that the coinbase starts with serialized block height
+ if (nHeight >= consensusParams.BIP34Height)
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
@@ -3567,7 +3567,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are
// multiple, the last one is used.
bool fHaveWitness = false;
- if (IsWitnessEnabled(pindexPrev, consensusParams)) {
+ if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != -1) {
bool malleated = false;
@@ -3576,11 +3576,11 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// already does not permit it, it is impossible to trigger in the
// witness tree.
if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) {
- return state.DoS(100, error("%s : invalid witness nonce size", __func__), REJECT_INVALID, "bad-witness-nonce-size", true);
+ return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__));
}
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) {
- return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true);
+ return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
}
fHaveWitness = true;
}
@@ -3590,7 +3590,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
if (!fHaveWitness) {
for (size_t i = 0; i < block.vtx.size(); i++) {
if (!block.vtx[i].wit.IsNull()) {
- return state.DoS(100, error("%s : unexpected witness data found", __func__), REJECT_INVALID, "unexpected-witness", true);
+ return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
}
}
}
@@ -3602,7 +3602,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// the block hash, so we couldn't mark the block as permanently
// failed).
if (GetBlockWeight(block) > MAX_BLOCK_WEIGHT) {
- return state.DoS(100, error("ContextualCheckBlock(): weight limit failed"), REJECT_INVALID, "bad-blk-weight");
+ return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
}
return true;
@@ -3689,7 +3689,8 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
}
if (fNewBlock) *fNewBlock = true;
- if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) || !ContextualCheckBlock(block, state, pindex->pprev)) {
+ if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime()) ||
+ !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
@@ -3722,19 +3723,6 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
return true;
}
-static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams)
-{
- unsigned int nFound = 0;
- for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++)
- {
- if (pstart->nVersion >= minVersion)
- ++nFound;
- pstart = pstart->pprev;
- }
- return (nFound >= nRequired);
-}
-
-
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp)
{
{
@@ -3780,7 +3768,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state));
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
- if (!ContextualCheckBlock(block, state, pindexPrev))
+ if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
return false;
@@ -3954,7 +3942,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash)
// Create new
CBlockIndex* pindexNew = new CBlockIndex();
if (!pindexNew)
- throw runtime_error("LoadBlockIndex(): new CBlockIndex failed");
+ throw runtime_error(std::string(__func__) + ": new CBlockIndex failed");
mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
@@ -4100,7 +4088,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
int nGoodTransactions = 0;
CValidationState state;
int reportDone = 0;
- LogPrintf("[0%]...");
+ LogPrintf("[0%%]...");
for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
{
boost::this_thread::interruption_point();
@@ -4331,8 +4319,6 @@ bool InitBlockIndex(const CChainParams& chainparams)
CBlockIndex *pindex = AddToBlockIndex(block);
if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("LoadBlockIndex(): genesis block not accepted");
- if (!ActivateBestChain(state, chainparams, &block))
- return error("LoadBlockIndex(): genesis block cannot be activated");
// Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
} catch (const std::runtime_error& e) {
@@ -4817,7 +4803,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
{
// If a peer is asking for old blocks, we're almost guaranteed
// they wont have a useful mempool to match against a compact block,
- // and we dont feel like constructing the object for them, so
+ // and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block.
if (mi->second->nHeight >= chainActive.Height() - 10) {
CBlockHeaderAndShortTxIDs cmpctblock(block);
@@ -4919,6 +4905,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (strCommand == NetMsgType::VERSION)
{
+ // Feeler connections exist only to verify if address is online.
+ if (pfrom->fFeeler) {
+ assert(pfrom->fInbound == false);
+ pfrom->fDisconnect = true;
+ }
+
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
@@ -5345,7 +5337,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
- Misbehaving(pfrom->GetId(), 100);
LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
return true;
}
@@ -5629,8 +5620,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash());
pfrom->PushMessage(NetMsgType::GETDATA, vInv);
- return true;
}
+ return true;
}
// If we're not close to tip yet, give up and let parallel block fetch work its magic
@@ -6148,6 +6139,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
+ else if (strCommand == NetMsgType::NOTFOUND) {
+ // We do not care about the NOTFOUND message, but logging an Unknown Command
+ // message would be undesirable as we transmit it ourselves.
+ }
+
else {
// Ignore unknown commands for extensibility
LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
diff --git a/src/main.h b/src/main.h
index 27121890f6..e9106fccf7 100644
--- a/src/main.h
+++ b/src/main.h
@@ -124,7 +124,6 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
/** Default for -permitbaremultisig */
static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
-static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
@@ -165,7 +164,6 @@ extern int nScriptCheckThreads;
extern bool fTxIndex;
extern bool fIsBareMultisigStd;
extern bool fRequireStandard;
-extern unsigned int nBytesPerSigOp;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern size_t nCoinCacheUsage;
@@ -216,7 +214,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals);
* block is made active. Note that it does not, however, guarantee that the
* specific block passed to it has been checked for validity!
*
- * @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation.
+ * @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganization; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation.
* @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid.
* @param[in] pblock The block we want to process.
* @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers.
@@ -354,9 +352,22 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
+/** Transaction validation functions */
+
/** Context-independent validity checks */
bool CheckTransaction(const CTransaction& tx, CValidationState& state);
+namespace Consensus {
+
+/**
+ * Check whether all inputs of this transaction are valid (no double spends and amounts)
+ * This does not modify the UTXO set. This does not check scripts and sigs.
+ * Preconditions: tx.IsCoinBase() is false.
+ */
+bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight);
+
+} // namespace Consensus
+
/**
* Check if transaction is final and can be included in a block with the
* specified height and time. Consensus critical.
@@ -447,8 +458,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock(). */
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex* pindexPrev, int64_t nAdjustedTime);
-bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev);
+bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime);
+bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
diff --git a/src/miner.cpp b/src/miner.cpp
index 25a5becf93..9575858840 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -187,7 +187,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0;
- pblocktemplate->vTxSigOpsCost[0] = GetLegacySigOpCount(pblock->vtx[0]);
+ pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(pblock->vtx[0]);
CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
diff --git a/src/net.cpp b/src/net.cpp
index 4cbc43e4d8..33dc10cbcb 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -17,6 +17,7 @@
#include "crypto/sha256.h"
#include "hash.h"
#include "primitives/transaction.h"
+#include "netbase.h"
#include "scheduler.h"
#include "ui_interface.h"
#include "utilstrencodings.h"
@@ -42,6 +43,9 @@
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
#define DUMP_ADDRESSES_INTERVAL 900
+// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
+#define FEELER_SLEEP_WINDOW 1
+
#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
@@ -60,6 +64,7 @@
namespace {
const int MAX_OUTBOUND_CONNECTIONS = 8;
+ const int MAX_FEELER_CONNECTIONS = 1;
struct ListenSocket {
SOCKET socket;
@@ -175,7 +180,7 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
// one by discovery.
CAddress GetLocalAddress(const CNetAddr *paddrPeer)
{
- CAddress ret(CService("0.0.0.0",GetListenPort()), NODE_NONE);
+ CAddress ret(CService(CNetAddr(),GetListenPort()), NODE_NONE);
CService addr;
if (GetLocal(addr, paddrPeer))
{
@@ -494,7 +499,7 @@ void CNode::PushVersion()
int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0);
int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
- CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0), addr.nServices));
+ CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
CAddress addrMe = GetLocalAddress(&addr);
GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
if (fLogIPs)
@@ -982,11 +987,11 @@ static bool AttemptToEvictConnection() {
uint64_t naMostConnections;
unsigned int nMostConnections = 0;
int64_t nMostConnectionsTime = 0;
- std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapAddrCounts;
+ std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes;
BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) {
- mapAddrCounts[node.nKeyedNetGroup].push_back(node);
- int64_t grouptime = mapAddrCounts[node.nKeyedNetGroup][0].nTimeConnected;
- size_t groupsize = mapAddrCounts[node.nKeyedNetGroup].size();
+ mapNetGroupNodes[node.nKeyedNetGroup].push_back(node);
+ int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected;
+ size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size();
if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) {
nMostConnections = groupsize;
@@ -996,7 +1001,7 @@ static bool AttemptToEvictConnection() {
}
// Reduce to the network group with the most connections
- vEvictionCandidates = std::move(mapAddrCounts[naMostConnections]);
+ vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]);
// Disconnect from the network group with the most connections
NodeId evicted = vEvictionCandidates.front().id;
@@ -1016,7 +1021,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
- int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS;
+ int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS);
+ assert(nMaxInbound > 0);
if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@@ -1396,8 +1402,11 @@ void ThreadMapPort()
{
if(externalIPAddress[0])
{
- LogPrintf("UPnP: ExternalIPAddress = %s\n", externalIPAddress);
- AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP);
+ CNetAddr resolved;
+ if(LookupHost(externalIPAddress, resolved, false)) {
+ LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str());
+ AddLocal(resolved, LOCAL_UPNP);
+ }
}
else
LogPrintf("UPnP: GetExternalIPAddress failed.\n");
@@ -1609,6 +1618,9 @@ void ThreadOpenConnections()
// Initiate network connections
int64_t nStart = GetTime();
+
+ // Minimum time before next feeler connection (in microseconds).
+ int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
while (true)
{
ProcessOneShot();
@@ -1623,7 +1635,9 @@ void ThreadOpenConnections()
static bool done = false;
if (!done) {
LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n");
- addrman.Add(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1"));
+ CNetAddr local;
+ LookupHost("127.0.0.1", local, false);
+ addrman.Add(convertSeed6(Params().FixedSeeds()), local);
done = true;
}
}
@@ -1646,13 +1660,36 @@ void ThreadOpenConnections()
}
}
}
+ assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS));
- int64_t nANow = GetAdjustedTime();
+ // Feeler Connections
+ //
+ // Design goals:
+ // * Increase the number of connectable addresses in the tried table.
+ //
+ // Method:
+ // * Choose a random address from new and attempt to connect to it if we can connect
+ // successfully it is added to tried.
+ // * Start attempting feeler connections only after node finishes making outbound
+ // connections.
+ // * Only make a feeler connection once every few minutes.
+ //
+ bool fFeeler = false;
+ if (nOutbound >= MAX_OUTBOUND_CONNECTIONS) {
+ int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
+ if (nTime > nNextFeeler) {
+ nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
+ fFeeler = true;
+ } else {
+ continue;
+ }
+ }
+ int64_t nANow = GetAdjustedTime();
int nTries = 0;
while (true)
{
- CAddrInfo addr = addrman.Select();
+ CAddrInfo addr = addrman.Select(fFeeler);
// if we selected an invalid address, restart
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
@@ -1676,7 +1713,7 @@ void ThreadOpenConnections()
if (nANow - addr.nLastTry < 600 && nTries < 30)
continue;
- // only consider nodes missing relevant services after 40 failed attemps
+ // only consider nodes missing relevant services after 40 failed attempts
if ((addr.nServices & nRelevantServices) != nRelevantServices && nTries < 40)
continue;
@@ -1688,8 +1725,17 @@ void ThreadOpenConnections()
break;
}
- if (addrConnect.IsValid())
- OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant);
+ if (addrConnect.IsValid()) {
+
+ if (fFeeler) {
+ // Add small amount of random noise before connection to avoid synchronization.
+ int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);
+ MilliSleep(randsleep);
+ LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString());
+ }
+
+ OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler);
+ }
}
}
@@ -1722,7 +1768,7 @@ std::vector<AddedNodeInfo> GetAddedNodeInfo()
}
BOOST_FOREACH(const std::string& strAddNode, lAddresses) {
- CService service(strAddNode, Params().GetDefaultPort());
+ CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort()));
if (service.IsValid()) {
// strAddNode is an IP:port
auto it = mapConnected.find(service);
@@ -1760,7 +1806,7 @@ void ThreadOpenAddedConnections()
CSemaphoreGrant grant(*semOutbound);
// If strAddedNode is an IP/port, decode it immediately, so
// OpenNetworkConnection can detect existing connections to that IP/port.
- CService service(info.strAddedNode, Params().GetDefaultPort());
+ CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort()));
OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false);
MilliSleep(500);
}
@@ -1771,7 +1817,7 @@ void ThreadOpenAddedConnections()
}
// if successful, this moves the passed grant to the constructed node
-bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot)
+bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
{
//
// Initiate outbound network connection
@@ -1795,6 +1841,8 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem
pnode->fNetworkNode = true;
if (fOneShot)
pnode->fOneShot = true;
+ if (fFeeler)
+ pnode->fFeeler = true;
return true;
}
@@ -2050,16 +2098,21 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
DumpBanlist();
}
+ uiInterface.InitMessage(_("Starting network threads..."));
+
fAddressesInitialized = true;
if (semOutbound == NULL) {
// initialize semaphore
- int nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections);
+ int nMaxOutbound = std::min((MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS), nMaxConnections);
semOutbound = new CSemaphore(nMaxOutbound);
}
- if (pnodeLocalHost == NULL)
- pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));
+ if (pnodeLocalHost == NULL) {
+ CNetAddr local;
+ LookupHost("127.0.0.1", local, false);
+ pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService(local, 0), nLocalServices));
+ }
Discover(threadGroup);
@@ -2096,7 +2149,7 @@ bool StopNode()
LogPrintf("StopNode()\n");
MapPort(false);
if (semOutbound)
- for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
+ for (int i=0; i<(MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++)
semOutbound->post();
if (fAddressesInitialized)
@@ -2437,6 +2490,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
fWhitelisted = false;
fOneShot = false;
fClient = false; // set by version message
+ fFeeler = false;
fInbound = fInboundIn;
fNetworkNode = false;
fSuccessfullyConnected = false;
diff --git a/src/net.h b/src/net.h
index 41315fc9b9..1ba2d47da7 100644
--- a/src/net.h
+++ b/src/net.h
@@ -10,7 +10,7 @@
#include "bloom.h"
#include "compat.h"
#include "limitedmap.h"
-#include "netbase.h"
+#include "netaddress.h"
#include "protocol.h"
#include "random.h"
#include "streams.h"
@@ -41,6 +41,8 @@ namespace boost {
static const int PING_INTERVAL = 2 * 60;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
static const int TIMEOUT_INTERVAL = 20 * 60;
+/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/
+static const int FEELER_INTERVAL = 120;
/** The maximum number of entries in an 'inv' protocol message */
static const unsigned int MAX_INV_SZ = 50000;
/** The maximum number of new addresses to accumulate before announcing. */
@@ -89,7 +91,7 @@ CNode* FindNode(const CSubNet& subNet);
CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& ip);
CNode* FindNode(const NodeId id); //TODO: Remove this
-bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
+bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
void MapPort(bool fUseUPnP);
unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
@@ -350,6 +352,7 @@ public:
// the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer;
bool fWhitelisted; // This peer can bypass DoS banning.
+ bool fFeeler; // If true this node is being used as a short lived feeler.
bool fOneShot;
bool fClient;
bool fInbound;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
new file mode 100644
index 0000000000..7000ce3f0a
--- /dev/null
+++ b/src/netaddress.cpp
@@ -0,0 +1,716 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifdef HAVE_CONFIG_H
+#include "config/bitcoin-config.h"
+#endif
+
+#include "netaddress.h"
+#include "hash.h"
+#include "utilstrencodings.h"
+#include "tinyformat.h"
+
+static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
+static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
+
+void CNetAddr::Init()
+{
+ memset(ip, 0, sizeof(ip));
+ scopeId = 0;
+}
+
+void CNetAddr::SetIP(const CNetAddr& ipIn)
+{
+ memcpy(ip, ipIn.ip, sizeof(ip));
+}
+
+void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
+{
+ switch(network)
+ {
+ case NET_IPV4:
+ memcpy(ip, pchIPv4, 12);
+ memcpy(ip+12, ip_in, 4);
+ break;
+ case NET_IPV6:
+ memcpy(ip, ip_in, 16);
+ break;
+ default:
+ assert(!"invalid network");
+ }
+}
+
+bool CNetAddr::SetSpecial(const std::string &strName)
+{
+ if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
+ std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
+ if (vchAddr.size() != 16-sizeof(pchOnionCat))
+ return false;
+ memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
+ for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
+ ip[i + sizeof(pchOnionCat)] = vchAddr[i];
+ return true;
+ }
+ return false;
+}
+
+CNetAddr::CNetAddr()
+{
+ Init();
+}
+
+CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
+{
+ SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr);
+}
+
+CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr, const uint32_t scope)
+{
+ SetRaw(NET_IPV6, (const uint8_t*)&ipv6Addr);
+ scopeId = scope;
+}
+
+unsigned int CNetAddr::GetByte(int n) const
+{
+ return ip[15-n];
+}
+
+bool CNetAddr::IsIPv4() const
+{
+ return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
+}
+
+bool CNetAddr::IsIPv6() const
+{
+ return (!IsIPv4() && !IsTor());
+}
+
+bool CNetAddr::IsRFC1918() const
+{
+ return IsIPv4() && (
+ GetByte(3) == 10 ||
+ (GetByte(3) == 192 && GetByte(2) == 168) ||
+ (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31)));
+}
+
+bool CNetAddr::IsRFC2544() const
+{
+ return IsIPv4() && GetByte(3) == 198 && (GetByte(2) == 18 || GetByte(2) == 19);
+}
+
+bool CNetAddr::IsRFC3927() const
+{
+ return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254);
+}
+
+bool CNetAddr::IsRFC6598() const
+{
+ return IsIPv4() && GetByte(3) == 100 && GetByte(2) >= 64 && GetByte(2) <= 127;
+}
+
+bool CNetAddr::IsRFC5737() const
+{
+ return IsIPv4() && ((GetByte(3) == 192 && GetByte(2) == 0 && GetByte(1) == 2) ||
+ (GetByte(3) == 198 && GetByte(2) == 51 && GetByte(1) == 100) ||
+ (GetByte(3) == 203 && GetByte(2) == 0 && GetByte(1) == 113));
+}
+
+bool CNetAddr::IsRFC3849() const
+{
+ return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8;
+}
+
+bool CNetAddr::IsRFC3964() const
+{
+ return (GetByte(15) == 0x20 && GetByte(14) == 0x02);
+}
+
+bool CNetAddr::IsRFC6052() const
+{
+ static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
+ return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0);
+}
+
+bool CNetAddr::IsRFC4380() const
+{
+ return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0);
+}
+
+bool CNetAddr::IsRFC4862() const
+{
+ static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
+ return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0);
+}
+
+bool CNetAddr::IsRFC4193() const
+{
+ return ((GetByte(15) & 0xFE) == 0xFC);
+}
+
+bool CNetAddr::IsRFC6145() const
+{
+ static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
+ return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0);
+}
+
+bool CNetAddr::IsRFC4843() const
+{
+ return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
+}
+
+bool CNetAddr::IsTor() const
+{
+ return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
+}
+
+bool CNetAddr::IsLocal() const
+{
+ // IPv4 loopback
+ if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0))
+ return true;
+
+ // IPv6 loopback (::1/128)
+ static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
+ if (memcmp(ip, pchLocal, 16) == 0)
+ return true;
+
+ return false;
+}
+
+bool CNetAddr::IsMulticast() const
+{
+ return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0)
+ || (GetByte(15) == 0xFF);
+}
+
+bool CNetAddr::IsValid() const
+{
+ // Cleanup 3-byte shifted addresses caused by garbage in size field
+ // of addr messages from versions before 0.2.9 checksum.
+ // Two consecutive addr messages look like this:
+ // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
+ // so if the first length field is garbled, it reads the second batch
+ // of addr misaligned by 3 bytes.
+ if (memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
+ return false;
+
+ // unspecified IPv6 address (::/128)
+ unsigned char ipNone[16] = {};
+ if (memcmp(ip, ipNone, 16) == 0)
+ return false;
+
+ // documentation IPv6 address
+ if (IsRFC3849())
+ return false;
+
+ if (IsIPv4())
+ {
+ // INADDR_NONE
+ uint32_t ipNone = INADDR_NONE;
+ if (memcmp(ip+12, &ipNone, 4) == 0)
+ return false;
+
+ // 0
+ ipNone = 0;
+ if (memcmp(ip+12, &ipNone, 4) == 0)
+ return false;
+ }
+
+ return true;
+}
+
+bool CNetAddr::IsRoutable() const
+{
+ return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal());
+}
+
+enum Network CNetAddr::GetNetwork() const
+{
+ if (!IsRoutable())
+ return NET_UNROUTABLE;
+
+ if (IsIPv4())
+ return NET_IPV4;
+
+ if (IsTor())
+ return NET_TOR;
+
+ return NET_IPV6;
+}
+
+std::string CNetAddr::ToStringIP() const
+{
+ if (IsTor())
+ return EncodeBase32(&ip[6], 10) + ".onion";
+ CService serv(*this, 0);
+ struct sockaddr_storage sockaddr;
+ socklen_t socklen = sizeof(sockaddr);
+ if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) {
+ char name[1025] = "";
+ if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST))
+ return std::string(name);
+ }
+ if (IsIPv4())
+ return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));
+ else
+ return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
+ GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12),
+ GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8),
+ GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4),
+ GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0));
+}
+
+std::string CNetAddr::ToString() const
+{
+ return ToStringIP();
+}
+
+bool operator==(const CNetAddr& a, const CNetAddr& b)
+{
+ return (memcmp(a.ip, b.ip, 16) == 0);
+}
+
+bool operator!=(const CNetAddr& a, const CNetAddr& b)
+{
+ return (memcmp(a.ip, b.ip, 16) != 0);
+}
+
+bool operator<(const CNetAddr& a, const CNetAddr& b)
+{
+ return (memcmp(a.ip, b.ip, 16) < 0);
+}
+
+bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
+{
+ if (!IsIPv4())
+ return false;
+ memcpy(pipv4Addr, ip+12, 4);
+ return true;
+}
+
+bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
+{
+ memcpy(pipv6Addr, ip, 16);
+ return true;
+}
+
+// get canonical identifier of an address' group
+// no two connections will be attempted to addresses with the same group
+std::vector<unsigned char> CNetAddr::GetGroup() const
+{
+ std::vector<unsigned char> vchRet;
+ int nClass = NET_IPV6;
+ int nStartByte = 0;
+ int nBits = 16;
+
+ // all local addresses belong to the same group
+ if (IsLocal())
+ {
+ nClass = 255;
+ nBits = 0;
+ }
+
+ // all unroutable addresses belong to the same group
+ if (!IsRoutable())
+ {
+ nClass = NET_UNROUTABLE;
+ nBits = 0;
+ }
+ // for IPv4 addresses, '1' + the 16 higher-order bits of the IP
+ // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix
+ else if (IsIPv4() || IsRFC6145() || IsRFC6052())
+ {
+ nClass = NET_IPV4;
+ nStartByte = 12;
+ }
+ // for 6to4 tunnelled addresses, use the encapsulated IPv4 address
+ else if (IsRFC3964())
+ {
+ nClass = NET_IPV4;
+ nStartByte = 2;
+ }
+ // for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address
+ else if (IsRFC4380())
+ {
+ vchRet.push_back(NET_IPV4);
+ vchRet.push_back(GetByte(3) ^ 0xFF);
+ vchRet.push_back(GetByte(2) ^ 0xFF);
+ return vchRet;
+ }
+ else if (IsTor())
+ {
+ nClass = NET_TOR;
+ nStartByte = 6;
+ nBits = 4;
+ }
+ // for he.net, use /36 groups
+ else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
+ nBits = 36;
+ // for the rest of the IPv6 network, use /32 groups
+ else
+ nBits = 32;
+
+ vchRet.push_back(nClass);
+ while (nBits >= 8)
+ {
+ vchRet.push_back(GetByte(15 - nStartByte));
+ nStartByte++;
+ nBits -= 8;
+ }
+ if (nBits > 0)
+ vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1));
+
+ return vchRet;
+}
+
+uint64_t CNetAddr::GetHash() const
+{
+ uint256 hash = Hash(&ip[0], &ip[16]);
+ uint64_t nRet;
+ memcpy(&nRet, &hash, sizeof(nRet));
+ return nRet;
+}
+
+// private extensions to enum Network, only returned by GetExtNetwork,
+// and only used in GetReachabilityFrom
+static const int NET_UNKNOWN = NET_MAX + 0;
+static const int NET_TEREDO = NET_MAX + 1;
+int static GetExtNetwork(const CNetAddr *addr)
+{
+ if (addr == NULL)
+ return NET_UNKNOWN;
+ if (addr->IsRFC4380())
+ return NET_TEREDO;
+ return addr->GetNetwork();
+}
+
+/** Calculates a metric for how reachable (*this) is from a given partner */
+int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
+{
+ enum Reachability {
+ REACH_UNREACHABLE,
+ REACH_DEFAULT,
+ REACH_TEREDO,
+ REACH_IPV6_WEAK,
+ REACH_IPV4,
+ REACH_IPV6_STRONG,
+ REACH_PRIVATE
+ };
+
+ if (!IsRoutable())
+ return REACH_UNREACHABLE;
+
+ int ourNet = GetExtNetwork(this);
+ int theirNet = GetExtNetwork(paddrPartner);
+ bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145();
+
+ switch(theirNet) {
+ case NET_IPV4:
+ switch(ourNet) {
+ default: return REACH_DEFAULT;
+ case NET_IPV4: return REACH_IPV4;
+ }
+ case NET_IPV6:
+ switch(ourNet) {
+ default: return REACH_DEFAULT;
+ case NET_TEREDO: return REACH_TEREDO;
+ case NET_IPV4: return REACH_IPV4;
+ case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled
+ }
+ case NET_TOR:
+ switch(ourNet) {
+ default: return REACH_DEFAULT;
+ case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well
+ case NET_TOR: return REACH_PRIVATE;
+ }
+ case NET_TEREDO:
+ switch(ourNet) {
+ default: return REACH_DEFAULT;
+ case NET_TEREDO: return REACH_TEREDO;
+ case NET_IPV6: return REACH_IPV6_WEAK;
+ case NET_IPV4: return REACH_IPV4;
+ }
+ case NET_UNKNOWN:
+ case NET_UNROUTABLE:
+ default:
+ switch(ourNet) {
+ default: return REACH_DEFAULT;
+ case NET_TEREDO: return REACH_TEREDO;
+ case NET_IPV6: return REACH_IPV6_WEAK;
+ case NET_IPV4: return REACH_IPV4;
+ case NET_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address
+ }
+ }
+}
+
+void CService::Init()
+{
+ port = 0;
+}
+
+CService::CService()
+{
+ Init();
+}
+
+CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn)
+{
+}
+
+CService::CService(const struct in_addr& ipv4Addr, unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn)
+{
+}
+
+CService::CService(const struct in6_addr& ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn)
+{
+}
+
+CService::CService(const struct sockaddr_in& addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port))
+{
+ assert(addr.sin_family == AF_INET);
+}
+
+CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr, addr.sin6_scope_id), port(ntohs(addr.sin6_port))
+{
+ assert(addr.sin6_family == AF_INET6);
+}
+
+bool CService::SetSockAddr(const struct sockaddr *paddr)
+{
+ switch (paddr->sa_family) {
+ case AF_INET:
+ *this = CService(*(const struct sockaddr_in*)paddr);
+ return true;
+ case AF_INET6:
+ *this = CService(*(const struct sockaddr_in6*)paddr);
+ return true;
+ default:
+ return false;
+ }
+}
+
+unsigned short CService::GetPort() const
+{
+ return port;
+}
+
+bool operator==(const CService& a, const CService& b)
+{
+ return (CNetAddr)a == (CNetAddr)b && a.port == b.port;
+}
+
+bool operator!=(const CService& a, const CService& b)
+{
+ return (CNetAddr)a != (CNetAddr)b || a.port != b.port;
+}
+
+bool operator<(const CService& a, const CService& b)
+{
+ return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port);
+}
+
+bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
+{
+ if (IsIPv4()) {
+ if (*addrlen < (socklen_t)sizeof(struct sockaddr_in))
+ return false;
+ *addrlen = sizeof(struct sockaddr_in);
+ struct sockaddr_in *paddrin = (struct sockaddr_in*)paddr;
+ memset(paddrin, 0, *addrlen);
+ if (!GetInAddr(&paddrin->sin_addr))
+ return false;
+ paddrin->sin_family = AF_INET;
+ paddrin->sin_port = htons(port);
+ return true;
+ }
+ if (IsIPv6()) {
+ if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6))
+ return false;
+ *addrlen = sizeof(struct sockaddr_in6);
+ struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6*)paddr;
+ memset(paddrin6, 0, *addrlen);
+ if (!GetIn6Addr(&paddrin6->sin6_addr))
+ return false;
+ paddrin6->sin6_scope_id = scopeId;
+ paddrin6->sin6_family = AF_INET6;
+ paddrin6->sin6_port = htons(port);
+ return true;
+ }
+ return false;
+}
+
+std::vector<unsigned char> CService::GetKey() const
+{
+ std::vector<unsigned char> vKey;
+ vKey.resize(18);
+ memcpy(&vKey[0], ip, 16);
+ vKey[16] = port / 0x100;
+ vKey[17] = port & 0x0FF;
+ return vKey;
+}
+
+std::string CService::ToStringPort() const
+{
+ return strprintf("%u", port);
+}
+
+std::string CService::ToStringIPPort() const
+{
+ if (IsIPv4() || IsTor()) {
+ return ToStringIP() + ":" + ToStringPort();
+ } else {
+ return "[" + ToStringIP() + "]:" + ToStringPort();
+ }
+}
+
+std::string CService::ToString() const
+{
+ return ToStringIPPort();
+}
+
+void CService::SetPort(unsigned short portIn)
+{
+ port = portIn;
+}
+
+CSubNet::CSubNet():
+ valid(false)
+{
+ memset(netmask, 0, sizeof(netmask));
+}
+
+CSubNet::CSubNet(const CNetAddr &addr, int32_t mask)
+{
+ valid = true;
+ network = addr;
+ // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
+ memset(netmask, 255, sizeof(netmask));
+
+ // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
+ const int astartofs = network.IsIPv4() ? 12 : 0;
+
+ int32_t n = mask;
+ if(n >= 0 && n <= (128 - astartofs*8)) // Only valid if in range of bits of address
+ {
+ n += astartofs*8;
+ // Clear bits [n..127]
+ for (; n < 128; ++n)
+ netmask[n>>3] &= ~(1<<(7-(n&7)));
+ } else
+ valid = false;
+
+ // Normalize network according to netmask
+ for(int x=0; x<16; ++x)
+ network.ip[x] &= netmask[x];
+}
+
+CSubNet::CSubNet(const CNetAddr &addr, const CNetAddr &mask)
+{
+ valid = true;
+ network = addr;
+ // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
+ memset(netmask, 255, sizeof(netmask));
+
+ // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
+ const int astartofs = network.IsIPv4() ? 12 : 0;
+
+ for(int x=astartofs; x<16; ++x)
+ netmask[x] = mask.ip[x];
+
+ // Normalize network according to netmask
+ for(int x=0; x<16; ++x)
+ network.ip[x] &= netmask[x];
+}
+
+CSubNet::CSubNet(const CNetAddr &addr):
+ valid(addr.IsValid())
+{
+ memset(netmask, 255, sizeof(netmask));
+ network = addr;
+}
+
+bool CSubNet::Match(const CNetAddr &addr) const
+{
+ if (!valid || !addr.IsValid())
+ return false;
+ for(int x=0; x<16; ++x)
+ if ((addr.ip[x] & netmask[x]) != network.ip[x])
+ return false;
+ return true;
+}
+
+static inline int NetmaskBits(uint8_t x)
+{
+ switch(x) {
+ case 0x00: return 0; break;
+ case 0x80: return 1; break;
+ case 0xc0: return 2; break;
+ case 0xe0: return 3; break;
+ case 0xf0: return 4; break;
+ case 0xf8: return 5; break;
+ case 0xfc: return 6; break;
+ case 0xfe: return 7; break;
+ case 0xff: return 8; break;
+ default: return -1; break;
+ }
+}
+
+std::string CSubNet::ToString() const
+{
+ /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */
+ int cidr = 0;
+ bool valid_cidr = true;
+ int n = network.IsIPv4() ? 12 : 0;
+ for (; n < 16 && netmask[n] == 0xff; ++n)
+ cidr += 8;
+ if (n < 16) {
+ int bits = NetmaskBits(netmask[n]);
+ if (bits < 0)
+ valid_cidr = false;
+ else
+ cidr += bits;
+ ++n;
+ }
+ for (; n < 16 && valid_cidr; ++n)
+ if (netmask[n] != 0x00)
+ valid_cidr = false;
+
+ /* Format output */
+ std::string strNetmask;
+ if (valid_cidr) {
+ strNetmask = strprintf("%u", cidr);
+ } else {
+ if (network.IsIPv4())
+ strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
+ else
+ strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
+ netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
+ netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
+ netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
+ netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
+ }
+
+ return network.ToString() + "/" + strNetmask;
+}
+
+bool CSubNet::IsValid() const
+{
+ return valid;
+}
+
+bool operator==(const CSubNet& a, const CSubNet& b)
+{
+ return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16);
+}
+
+bool operator!=(const CSubNet& a, const CSubNet& b)
+{
+ return !(a==b);
+}
+
+bool operator<(const CSubNet& a, const CSubNet& b)
+{
+ return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
+}
diff --git a/src/netaddress.h b/src/netaddress.h
new file mode 100644
index 0000000000..9330fe3328
--- /dev/null
+++ b/src/netaddress.h
@@ -0,0 +1,171 @@
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_NETADDRESS_H
+#define BITCOIN_NETADDRESS_H
+
+#if defined(HAVE_CONFIG_H)
+#include "config/bitcoin-config.h"
+#endif
+
+#include "compat.h"
+#include "serialize.h"
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+enum Network
+{
+ NET_UNROUTABLE = 0,
+ NET_IPV4,
+ NET_IPV6,
+ NET_TOR,
+
+ NET_MAX,
+};
+
+/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */
+class CNetAddr
+{
+ protected:
+ unsigned char ip[16]; // in network byte order
+ uint32_t scopeId; // for scoped/link-local ipv6 addresses
+
+ public:
+ CNetAddr();
+ CNetAddr(const struct in_addr& ipv4Addr);
+ void Init();
+ void SetIP(const CNetAddr& ip);
+
+ /**
+ * Set raw IPv4 or IPv6 address (in network byte order)
+ * @note Only NET_IPV4 and NET_IPV6 are allowed for network.
+ */
+ void SetRaw(Network network, const uint8_t *data);
+
+ bool SetSpecial(const std::string &strName); // for Tor addresses
+ bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
+ bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
+ bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
+ bool IsRFC2544() const; // IPv4 inter-network communcations (192.18.0.0/15)
+ bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10)
+ bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
+ bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
+ bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
+ bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
+ bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
+ bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
+ bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28)
+ bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
+ bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
+ bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
+ bool IsTor() const;
+ bool IsLocal() const;
+ bool IsRoutable() const;
+ bool IsValid() const;
+ bool IsMulticast() const;
+ enum Network GetNetwork() const;
+ std::string ToString() const;
+ std::string ToStringIP() const;
+ unsigned int GetByte(int n) const;
+ uint64_t GetHash() const;
+ bool GetInAddr(struct in_addr* pipv4Addr) const;
+ std::vector<unsigned char> GetGroup() const;
+ int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const;
+
+ CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
+ bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
+
+ friend bool operator==(const CNetAddr& a, const CNetAddr& b);
+ friend bool operator!=(const CNetAddr& a, const CNetAddr& b);
+ friend bool operator<(const CNetAddr& a, const CNetAddr& b);
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ READWRITE(FLATDATA(ip));
+ }
+
+ friend class CSubNet;
+};
+
+class CSubNet
+{
+ protected:
+ /// Network (base) address
+ CNetAddr network;
+ /// Netmask, in network byte order
+ uint8_t netmask[16];
+ /// Is this value valid? (only used to signal parse errors)
+ bool valid;
+
+ public:
+ CSubNet();
+ CSubNet(const CNetAddr &addr, int32_t mask);
+ CSubNet(const CNetAddr &addr, const CNetAddr &mask);
+
+ //constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
+ explicit CSubNet(const CNetAddr &addr);
+
+ bool Match(const CNetAddr &addr) const;
+
+ std::string ToString() const;
+ bool IsValid() const;
+
+ friend bool operator==(const CSubNet& a, const CSubNet& b);
+ friend bool operator!=(const CSubNet& a, const CSubNet& b);
+ friend bool operator<(const CSubNet& a, const CSubNet& b);
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ READWRITE(network);
+ READWRITE(FLATDATA(netmask));
+ READWRITE(FLATDATA(valid));
+ }
+};
+
+/** A combination of a network address (CNetAddr) and a (TCP) port */
+class CService : public CNetAddr
+{
+ protected:
+ unsigned short port; // host order
+
+ public:
+ CService();
+ CService(const CNetAddr& ip, unsigned short port);
+ CService(const struct in_addr& ipv4Addr, unsigned short port);
+ CService(const struct sockaddr_in& addr);
+ void Init();
+ void SetPort(unsigned short portIn);
+ unsigned short GetPort() const;
+ bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
+ bool SetSockAddr(const struct sockaddr* paddr);
+ friend bool operator==(const CService& a, const CService& b);
+ friend bool operator!=(const CService& a, const CService& b);
+ friend bool operator<(const CService& a, const CService& b);
+ std::vector<unsigned char> GetKey() const;
+ std::string ToString() const;
+ std::string ToStringPort() const;
+ std::string ToStringIPPort() const;
+
+ CService(const struct in6_addr& ipv6Addr, unsigned short port);
+ CService(const struct sockaddr_in6& addr);
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ READWRITE(FLATDATA(ip));
+ unsigned short portN = htons(port);
+ READWRITE(FLATDATA(portN));
+ if (ser_action.ForRead())
+ port = ntohs(portN);
+ }
+};
+
+#endif // BITCOIN_NETADDRESS_H
diff --git a/src/netbase.cpp b/src/netbase.cpp
index e2a516986c..4f243ec6f5 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -42,8 +42,6 @@ static CCriticalSection cs_proxyInfos;
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
-static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
-
// Need ample time for negotiation for very slow proxies such as Tor (milliseconds)
static const int SOCKS5_RECV_TIMEOUT = 20 * 1000;
@@ -195,6 +193,16 @@ bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nM
return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup);
}
+bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup)
+{
+ std::vector<CNetAddr> vIP;
+ LookupHost(pszName, vIP, 1, fAllowLookup);
+ if(vIP.empty())
+ return false;
+ addr = vIP.front();
+ return true;
+}
+
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions)
{
if (pszName[0] == 0)
@@ -223,9 +231,14 @@ bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLoo
return true;
}
-bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
+CService LookupNumeric(const char *pszName, int portDefault)
{
- return Lookup(pszName, addr, portDefault, false);
+ CService addr;
+ // "1.2:345" will fail to resolve the ip, but will still set the port.
+ // If the ip fails to resolve, re-init the result.
+ if(!Lookup(pszName, addr, portDefault, false))
+ addr = CService();
+ return addr;
}
struct timeval MillisToTimeval(int64_t nTimeout)
@@ -629,777 +642,48 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest
}
}
- addr = CService("0.0.0.0:0");
+ addr = CService();
if (!HaveNameProxy())
return false;
return ConnectThroughProxy(nameProxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed);
}
-void CNetAddr::Init()
-{
- memset(ip, 0, sizeof(ip));
- scopeId = 0;
-}
-
-void CNetAddr::SetIP(const CNetAddr& ipIn)
-{
- memcpy(ip, ipIn.ip, sizeof(ip));
-}
-
-void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
-{
- switch(network)
- {
- case NET_IPV4:
- memcpy(ip, pchIPv4, 12);
- memcpy(ip+12, ip_in, 4);
- break;
- case NET_IPV6:
- memcpy(ip, ip_in, 16);
- break;
- default:
- assert(!"invalid network");
- }
-}
-
-static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
-
-bool CNetAddr::SetSpecial(const std::string &strName)
-{
- if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
- std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
- if (vchAddr.size() != 16-sizeof(pchOnionCat))
- return false;
- memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
- for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
- ip[i + sizeof(pchOnionCat)] = vchAddr[i];
- return true;
- }
- return false;
-}
-
-CNetAddr::CNetAddr()
-{
- Init();
-}
-
-CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
-{
- SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr);
-}
-
-CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr, const uint32_t scope)
-{
- SetRaw(NET_IPV6, (const uint8_t*)&ipv6Addr);
- scopeId = scope;
-}
-
-CNetAddr::CNetAddr(const char *pszIp)
-{
- Init();
- std::vector<CNetAddr> vIP;
- if (LookupHost(pszIp, vIP, 1, false))
- *this = vIP[0];
-}
-
-CNetAddr::CNetAddr(const std::string &strIp)
-{
- Init();
- std::vector<CNetAddr> vIP;
- if (LookupHost(strIp.c_str(), vIP, 1, false))
- *this = vIP[0];
-}
-
-unsigned int CNetAddr::GetByte(int n) const
-{
- return ip[15-n];
-}
-
-bool CNetAddr::IsIPv4() const
-{
- return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
-}
-
-bool CNetAddr::IsIPv6() const
-{
- return (!IsIPv4() && !IsTor());
-}
-
-bool CNetAddr::IsRFC1918() const
-{
- return IsIPv4() && (
- GetByte(3) == 10 ||
- (GetByte(3) == 192 && GetByte(2) == 168) ||
- (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31)));
-}
-
-bool CNetAddr::IsRFC2544() const
-{
- return IsIPv4() && GetByte(3) == 198 && (GetByte(2) == 18 || GetByte(2) == 19);
-}
-
-bool CNetAddr::IsRFC3927() const
-{
- return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254);
-}
-
-bool CNetAddr::IsRFC6598() const
-{
- return IsIPv4() && GetByte(3) == 100 && GetByte(2) >= 64 && GetByte(2) <= 127;
-}
-
-bool CNetAddr::IsRFC5737() const
-{
- return IsIPv4() && ((GetByte(3) == 192 && GetByte(2) == 0 && GetByte(1) == 2) ||
- (GetByte(3) == 198 && GetByte(2) == 51 && GetByte(1) == 100) ||
- (GetByte(3) == 203 && GetByte(2) == 0 && GetByte(1) == 113));
-}
-
-bool CNetAddr::IsRFC3849() const
-{
- return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && GetByte(12) == 0xB8;
-}
-
-bool CNetAddr::IsRFC3964() const
-{
- return (GetByte(15) == 0x20 && GetByte(14) == 0x02);
-}
-
-bool CNetAddr::IsRFC6052() const
-{
- static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
- return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0);
-}
-
-bool CNetAddr::IsRFC4380() const
-{
- return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && GetByte(12) == 0);
-}
-
-bool CNetAddr::IsRFC4862() const
-{
- static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
- return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0);
-}
-
-bool CNetAddr::IsRFC4193() const
-{
- return ((GetByte(15) & 0xFE) == 0xFC);
-}
-
-bool CNetAddr::IsRFC6145() const
-{
- static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
- return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0);
-}
-
-bool CNetAddr::IsRFC4843() const
-{
- return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
-}
-
-bool CNetAddr::IsTor() const
-{
- return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
-}
-
-bool CNetAddr::IsLocal() const
-{
- // IPv4 loopback
- if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0))
- return true;
-
- // IPv6 loopback (::1/128)
- static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
- if (memcmp(ip, pchLocal, 16) == 0)
- return true;
-
- return false;
-}
-
-bool CNetAddr::IsMulticast() const
-{
- return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0)
- || (GetByte(15) == 0xFF);
-}
-
-bool CNetAddr::IsValid() const
-{
- // Cleanup 3-byte shifted addresses caused by garbage in size field
- // of addr messages from versions before 0.2.9 checksum.
- // Two consecutive addr messages look like this:
- // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
- // so if the first length field is garbled, it reads the second batch
- // of addr misaligned by 3 bytes.
- if (memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
- return false;
-
- // unspecified IPv6 address (::/128)
- unsigned char ipNone[16] = {};
- if (memcmp(ip, ipNone, 16) == 0)
- return false;
-
- // documentation IPv6 address
- if (IsRFC3849())
- return false;
-
- if (IsIPv4())
- {
- // INADDR_NONE
- uint32_t ipNone = INADDR_NONE;
- if (memcmp(ip+12, &ipNone, 4) == 0)
- return false;
-
- // 0
- ipNone = 0;
- if (memcmp(ip+12, &ipNone, 4) == 0)
- return false;
- }
-
- return true;
-}
-
-bool CNetAddr::IsRoutable() const
-{
- return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal());
-}
-
-enum Network CNetAddr::GetNetwork() const
-{
- if (!IsRoutable())
- return NET_UNROUTABLE;
-
- if (IsIPv4())
- return NET_IPV4;
-
- if (IsTor())
- return NET_TOR;
-
- return NET_IPV6;
-}
-
-std::string CNetAddr::ToStringIP() const
-{
- if (IsTor())
- return EncodeBase32(&ip[6], 10) + ".onion";
- CService serv(*this, 0);
- struct sockaddr_storage sockaddr;
- socklen_t socklen = sizeof(sockaddr);
- if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) {
- char name[1025] = "";
- if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST))
- return std::string(name);
- }
- if (IsIPv4())
- return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));
- else
- return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12),
- GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8),
- GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4),
- GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0));
-}
-
-std::string CNetAddr::ToString() const
-{
- return ToStringIP();
-}
-
-bool operator==(const CNetAddr& a, const CNetAddr& b)
-{
- return (memcmp(a.ip, b.ip, 16) == 0);
-}
-
-bool operator!=(const CNetAddr& a, const CNetAddr& b)
-{
- return (memcmp(a.ip, b.ip, 16) != 0);
-}
-
-bool operator<(const CNetAddr& a, const CNetAddr& b)
-{
- return (memcmp(a.ip, b.ip, 16) < 0);
-}
-
-bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
-{
- if (!IsIPv4())
- return false;
- memcpy(pipv4Addr, ip+12, 4);
- return true;
-}
-
-bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
-{
- memcpy(pipv6Addr, ip, 16);
- return true;
-}
-
-// get canonical identifier of an address' group
-// no two connections will be attempted to addresses with the same group
-std::vector<unsigned char> CNetAddr::GetGroup() const
-{
- std::vector<unsigned char> vchRet;
- int nClass = NET_IPV6;
- int nStartByte = 0;
- int nBits = 16;
-
- // all local addresses belong to the same group
- if (IsLocal())
- {
- nClass = 255;
- nBits = 0;
- }
-
- // all unroutable addresses belong to the same group
- if (!IsRoutable())
- {
- nClass = NET_UNROUTABLE;
- nBits = 0;
- }
- // for IPv4 addresses, '1' + the 16 higher-order bits of the IP
- // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix
- else if (IsIPv4() || IsRFC6145() || IsRFC6052())
- {
- nClass = NET_IPV4;
- nStartByte = 12;
- }
- // for 6to4 tunnelled addresses, use the encapsulated IPv4 address
- else if (IsRFC3964())
- {
- nClass = NET_IPV4;
- nStartByte = 2;
- }
- // for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address
- else if (IsRFC4380())
- {
- vchRet.push_back(NET_IPV4);
- vchRet.push_back(GetByte(3) ^ 0xFF);
- vchRet.push_back(GetByte(2) ^ 0xFF);
- return vchRet;
- }
- else if (IsTor())
- {
- nClass = NET_TOR;
- nStartByte = 6;
- nBits = 4;
- }
- // for he.net, use /36 groups
- else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
- nBits = 36;
- // for the rest of the IPv6 network, use /32 groups
- else
- nBits = 32;
-
- vchRet.push_back(nClass);
- while (nBits >= 8)
- {
- vchRet.push_back(GetByte(15 - nStartByte));
- nStartByte++;
- nBits -= 8;
- }
- if (nBits > 0)
- vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1));
-
- return vchRet;
-}
-
-uint64_t CNetAddr::GetHash() const
-{
- uint256 hash = Hash(&ip[0], &ip[16]);
- uint64_t nRet;
- memcpy(&nRet, &hash, sizeof(nRet));
- return nRet;
-}
-
-// private extensions to enum Network, only returned by GetExtNetwork,
-// and only used in GetReachabilityFrom
-static const int NET_UNKNOWN = NET_MAX + 0;
-static const int NET_TEREDO = NET_MAX + 1;
-int static GetExtNetwork(const CNetAddr *addr)
-{
- if (addr == NULL)
- return NET_UNKNOWN;
- if (addr->IsRFC4380())
- return NET_TEREDO;
- return addr->GetNetwork();
-}
-
-/** Calculates a metric for how reachable (*this) is from a given partner */
-int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
-{
- enum Reachability {
- REACH_UNREACHABLE,
- REACH_DEFAULT,
- REACH_TEREDO,
- REACH_IPV6_WEAK,
- REACH_IPV4,
- REACH_IPV6_STRONG,
- REACH_PRIVATE
- };
-
- if (!IsRoutable())
- return REACH_UNREACHABLE;
-
- int ourNet = GetExtNetwork(this);
- int theirNet = GetExtNetwork(paddrPartner);
- bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145();
-
- switch(theirNet) {
- case NET_IPV4:
- switch(ourNet) {
- default: return REACH_DEFAULT;
- case NET_IPV4: return REACH_IPV4;
- }
- case NET_IPV6:
- switch(ourNet) {
- default: return REACH_DEFAULT;
- case NET_TEREDO: return REACH_TEREDO;
- case NET_IPV4: return REACH_IPV4;
- case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled
- }
- case NET_TOR:
- switch(ourNet) {
- default: return REACH_DEFAULT;
- case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well
- case NET_TOR: return REACH_PRIVATE;
- }
- case NET_TEREDO:
- switch(ourNet) {
- default: return REACH_DEFAULT;
- case NET_TEREDO: return REACH_TEREDO;
- case NET_IPV6: return REACH_IPV6_WEAK;
- case NET_IPV4: return REACH_IPV4;
- }
- case NET_UNKNOWN:
- case NET_UNROUTABLE:
- default:
- switch(ourNet) {
- default: return REACH_DEFAULT;
- case NET_TEREDO: return REACH_TEREDO;
- case NET_IPV6: return REACH_IPV6_WEAK;
- case NET_IPV4: return REACH_IPV4;
- case NET_TOR: return REACH_PRIVATE; // either from Tor, or don't care about our address
- }
- }
-}
-
-void CService::Init()
-{
- port = 0;
-}
-
-CService::CService()
-{
- Init();
-}
-
-CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn)
-{
-}
-
-CService::CService(const struct in_addr& ipv4Addr, unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn)
-{
-}
-
-CService::CService(const struct in6_addr& ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn)
-{
-}
-
-CService::CService(const struct sockaddr_in& addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port))
-{
- assert(addr.sin_family == AF_INET);
-}
-
-CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr, addr.sin6_scope_id), port(ntohs(addr.sin6_port))
-{
- assert(addr.sin6_family == AF_INET6);
-}
-
-bool CService::SetSockAddr(const struct sockaddr *paddr)
-{
- switch (paddr->sa_family) {
- case AF_INET:
- *this = CService(*(const struct sockaddr_in*)paddr);
- return true;
- case AF_INET6:
- *this = CService(*(const struct sockaddr_in6*)paddr);
- return true;
- default:
- return false;
- }
-}
-
-CService::CService(const char *pszIpPort)
-{
- Init();
- CService ip;
- if (Lookup(pszIpPort, ip, 0, false))
- *this = ip;
-}
-
-CService::CService(const char *pszIpPort, int portDefault)
-{
- Init();
- CService ip;
- if (Lookup(pszIpPort, ip, portDefault, false))
- *this = ip;
-}
-
-CService::CService(const std::string &strIpPort)
-{
- Init();
- CService ip;
- if (Lookup(strIpPort.c_str(), ip, 0, false))
- *this = ip;
-}
-
-CService::CService(const std::string &strIpPort, int portDefault)
-{
- Init();
- CService ip;
- if (Lookup(strIpPort.c_str(), ip, portDefault, false))
- *this = ip;
-}
-
-unsigned short CService::GetPort() const
-{
- return port;
-}
-
-bool operator==(const CService& a, const CService& b)
-{
- return (CNetAddr)a == (CNetAddr)b && a.port == b.port;
-}
-
-bool operator!=(const CService& a, const CService& b)
-{
- return (CNetAddr)a != (CNetAddr)b || a.port != b.port;
-}
-
-bool operator<(const CService& a, const CService& b)
-{
- return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port);
-}
-
-bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
-{
- if (IsIPv4()) {
- if (*addrlen < (socklen_t)sizeof(struct sockaddr_in))
- return false;
- *addrlen = sizeof(struct sockaddr_in);
- struct sockaddr_in *paddrin = (struct sockaddr_in*)paddr;
- memset(paddrin, 0, *addrlen);
- if (!GetInAddr(&paddrin->sin_addr))
- return false;
- paddrin->sin_family = AF_INET;
- paddrin->sin_port = htons(port);
- return true;
- }
- if (IsIPv6()) {
- if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6))
- return false;
- *addrlen = sizeof(struct sockaddr_in6);
- struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6*)paddr;
- memset(paddrin6, 0, *addrlen);
- if (!GetIn6Addr(&paddrin6->sin6_addr))
- return false;
- paddrin6->sin6_scope_id = scopeId;
- paddrin6->sin6_family = AF_INET6;
- paddrin6->sin6_port = htons(port);
- return true;
- }
- return false;
-}
-
-std::vector<unsigned char> CService::GetKey() const
-{
- std::vector<unsigned char> vKey;
- vKey.resize(18);
- memcpy(&vKey[0], ip, 16);
- vKey[16] = port / 0x100;
- vKey[17] = port & 0x0FF;
- return vKey;
-}
-
-std::string CService::ToStringPort() const
-{
- return strprintf("%u", port);
-}
-
-std::string CService::ToStringIPPort() const
-{
- if (IsIPv4() || IsTor()) {
- return ToStringIP() + ":" + ToStringPort();
- } else {
- return "[" + ToStringIP() + "]:" + ToStringPort();
- }
-}
-
-std::string CService::ToString() const
-{
- return ToStringIPPort();
-}
-
-void CService::SetPort(unsigned short portIn)
-{
- port = portIn;
-}
-
-CSubNet::CSubNet():
- valid(false)
-{
- memset(netmask, 0, sizeof(netmask));
-}
-
-CSubNet::CSubNet(const std::string &strSubnet)
+bool LookupSubNet(const char* pszName, CSubNet& ret)
{
+ std::string strSubnet(pszName);
size_t slash = strSubnet.find_last_of('/');
std::vector<CNetAddr> vIP;
- valid = true;
- // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
- memset(netmask, 255, sizeof(netmask));
-
std::string strAddress = strSubnet.substr(0, slash);
if (LookupHost(strAddress.c_str(), vIP, 1, false))
{
- network = vIP[0];
+ CNetAddr network = vIP[0];
if (slash != strSubnet.npos)
{
std::string strNetmask = strSubnet.substr(slash + 1);
int32_t n;
// IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
- const int astartofs = network.IsIPv4() ? 12 : 0;
- if (ParseInt32(strNetmask, &n)) // If valid number, assume /24 symtex
- {
- if(n >= 0 && n <= (128 - astartofs*8)) // Only valid if in range of bits of address
- {
- n += astartofs*8;
- // Clear bits [n..127]
- for (; n < 128; ++n)
- netmask[n>>3] &= ~(1<<(7-(n&7)));
- }
- else
- {
- valid = false;
- }
+ if (ParseInt32(strNetmask, &n)) { // If valid number, assume /24 syntax
+ ret = CSubNet(network, n);
+ return ret.IsValid();
}
else // If not a valid number, try full netmask syntax
{
- if (LookupHost(strNetmask.c_str(), vIP, 1, false)) // Never allow lookup for netmask
- {
- // Copy only the *last* four bytes in case of IPv4, the rest of the mask should stay 1's as
- // we don't want pchIPv4 to be part of the mask.
- for(int x=astartofs; x<16; ++x)
- netmask[x] = vIP[0].ip[x];
- }
- else
- {
- valid = false;
+ // Never allow lookup for netmask
+ if (LookupHost(strNetmask.c_str(), vIP, 1, false)) {
+ ret = CSubNet(network, vIP[0]);
+ return ret.IsValid();
}
}
}
- }
- else
- {
- valid = false;
- }
-
- // Normalize network according to netmask
- for(int x=0; x<16; ++x)
- network.ip[x] &= netmask[x];
-}
-
-CSubNet::CSubNet(const CNetAddr &addr):
- valid(addr.IsValid())
-{
- memset(netmask, 255, sizeof(netmask));
- network = addr;
-}
-
-bool CSubNet::Match(const CNetAddr &addr) const
-{
- if (!valid || !addr.IsValid())
- return false;
- for(int x=0; x<16; ++x)
- if ((addr.ip[x] & netmask[x]) != network.ip[x])
- return false;
- return true;
-}
-
-static inline int NetmaskBits(uint8_t x)
-{
- switch(x) {
- case 0x00: return 0; break;
- case 0x80: return 1; break;
- case 0xc0: return 2; break;
- case 0xe0: return 3; break;
- case 0xf0: return 4; break;
- case 0xf8: return 5; break;
- case 0xfc: return 6; break;
- case 0xfe: return 7; break;
- case 0xff: return 8; break;
- default: return -1; break;
- }
-}
-
-std::string CSubNet::ToString() const
-{
- /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */
- int cidr = 0;
- bool valid_cidr = true;
- int n = network.IsIPv4() ? 12 : 0;
- for (; n < 16 && netmask[n] == 0xff; ++n)
- cidr += 8;
- if (n < 16) {
- int bits = NetmaskBits(netmask[n]);
- if (bits < 0)
- valid_cidr = false;
else
- cidr += bits;
- ++n;
- }
- for (; n < 16 && valid_cidr; ++n)
- if (netmask[n] != 0x00)
- valid_cidr = false;
-
- /* Format output */
- std::string strNetmask;
- if (valid_cidr) {
- strNetmask = strprintf("%u", cidr);
- } else {
- if (network.IsIPv4())
- strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
- else
- strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
- netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
- netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
- netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
+ {
+ ret = CSubNet(network);
+ return ret.IsValid();
+ }
}
-
- return network.ToString() + "/" + strNetmask;
-}
-
-bool CSubNet::IsValid() const
-{
- return valid;
-}
-
-bool operator==(const CSubNet& a, const CSubNet& b)
-{
- return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16);
-}
-
-bool operator!=(const CSubNet& a, const CSubNet& b)
-{
- return !(a==b);
-}
-
-bool operator<(const CSubNet& a, const CSubNet& b)
-{
- return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
+ return false;
}
#ifdef WIN32
diff --git a/src/netbase.h b/src/netbase.h
index 65187a17cf..bb12019a82 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -10,6 +10,7 @@
#endif
#include "compat.h"
+#include "netaddress.h"
#include "serialize.h"
#include <stdint.h>
@@ -24,168 +25,6 @@ static const int DEFAULT_CONNECT_TIMEOUT = 5000;
//! -dns default
static const int DEFAULT_NAME_LOOKUP = true;
-#ifdef WIN32
-// In MSVC, this is defined as a macro, undefine it to prevent a compile and link error
-#undef SetPort
-#endif
-
-enum Network
-{
- NET_UNROUTABLE = 0,
- NET_IPV4,
- NET_IPV6,
- NET_TOR,
-
- NET_MAX,
-};
-
-/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */
-class CNetAddr
-{
- protected:
- unsigned char ip[16]; // in network byte order
- uint32_t scopeId; // for scoped/link-local ipv6 addresses
-
- public:
- CNetAddr();
- CNetAddr(const struct in_addr& ipv4Addr);
- explicit CNetAddr(const char *pszIp);
- explicit CNetAddr(const std::string &strIp);
- void Init();
- void SetIP(const CNetAddr& ip);
-
- /**
- * Set raw IPv4 or IPv6 address (in network byte order)
- * @note Only NET_IPV4 and NET_IPV6 are allowed for network.
- */
- void SetRaw(Network network, const uint8_t *data);
-
- bool SetSpecial(const std::string &strName); // for Tor addresses
- bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
- bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
- bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
- bool IsRFC2544() const; // IPv4 inter-network communcations (192.18.0.0/15)
- bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10)
- bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
- bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
- bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
- bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
- bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
- bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
- bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28)
- bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
- bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
- bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
- bool IsTor() const;
- bool IsLocal() const;
- bool IsRoutable() const;
- bool IsValid() const;
- bool IsMulticast() const;
- enum Network GetNetwork() const;
- std::string ToString() const;
- std::string ToStringIP() const;
- unsigned int GetByte(int n) const;
- uint64_t GetHash() const;
- bool GetInAddr(struct in_addr* pipv4Addr) const;
- std::vector<unsigned char> GetGroup() const;
- int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const;
-
- CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
- bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
-
- friend bool operator==(const CNetAddr& a, const CNetAddr& b);
- friend bool operator!=(const CNetAddr& a, const CNetAddr& b);
- friend bool operator<(const CNetAddr& a, const CNetAddr& b);
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- READWRITE(FLATDATA(ip));
- }
-
- friend class CSubNet;
-};
-
-class CSubNet
-{
- protected:
- /// Network (base) address
- CNetAddr network;
- /// Netmask, in network byte order
- uint8_t netmask[16];
- /// Is this value valid? (only used to signal parse errors)
- bool valid;
-
- public:
- CSubNet();
- explicit CSubNet(const std::string &strSubnet);
-
- //constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
- explicit CSubNet(const CNetAddr &addr);
-
- bool Match(const CNetAddr &addr) const;
-
- std::string ToString() const;
- bool IsValid() const;
-
- friend bool operator==(const CSubNet& a, const CSubNet& b);
- friend bool operator!=(const CSubNet& a, const CSubNet& b);
- friend bool operator<(const CSubNet& a, const CSubNet& b);
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- READWRITE(network);
- READWRITE(FLATDATA(netmask));
- READWRITE(FLATDATA(valid));
- }
-};
-
-/** A combination of a network address (CNetAddr) and a (TCP) port */
-class CService : public CNetAddr
-{
- protected:
- unsigned short port; // host order
-
- public:
- CService();
- CService(const CNetAddr& ip, unsigned short port);
- CService(const struct in_addr& ipv4Addr, unsigned short port);
- CService(const struct sockaddr_in& addr);
- explicit CService(const char *pszIpPort, int portDefault);
- explicit CService(const char *pszIpPort);
- explicit CService(const std::string& strIpPort, int portDefault);
- explicit CService(const std::string& strIpPort);
- void Init();
- void SetPort(unsigned short portIn);
- unsigned short GetPort() const;
- bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
- bool SetSockAddr(const struct sockaddr* paddr);
- friend bool operator==(const CService& a, const CService& b);
- friend bool operator!=(const CService& a, const CService& b);
- friend bool operator<(const CService& a, const CService& b);
- std::vector<unsigned char> GetKey() const;
- std::string ToString() const;
- std::string ToStringPort() const;
- std::string ToStringIPPort() const;
-
- CService(const struct in6_addr& ipv6Addr, unsigned short port);
- CService(const struct sockaddr_in6& addr);
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- READWRITE(FLATDATA(ip));
- unsigned short portN = htons(port);
- READWRITE(FLATDATA(portN));
- if (ser_action.ForRead())
- port = ntohs(portN);
- }
-};
-
class proxyType
{
public:
@@ -207,9 +46,11 @@ bool IsProxy(const CNetAddr &addr);
bool SetNameProxy(const proxyType &addrProxy);
bool HaveNameProxy();
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);
-bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0);
+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 = 0);
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = 0);
/** Return readable error string for a network error code */
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 8617db00c8..48080abc77 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -31,7 +31,7 @@
* DUP CHECKSIG DROP ... repeated 100 times... OP_1
*/
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
+bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled)
{
std::vector<std::vector<unsigned char> > vSolutions;
if (!Solver(scriptPubKey, whichType, vSolutions))
@@ -50,10 +50,13 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
(!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes))
return false;
+ else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
+ return false;
+
return whichType != TX_NONSTANDARD;
}
-bool IsStandardTx(const CTransaction& tx, std::string& reason)
+bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled)
{
if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) {
reason = "version";
@@ -92,7 +95,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
unsigned int nDataOut = 0;
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
- if (!::IsStandard(txout.scriptPubKey, whichType)) {
+ if (!::IsStandard(txout.scriptPubKey, whichType, witnessEnabled)) {
reason = "scriptpubkey";
return false;
}
@@ -151,12 +154,14 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true;
}
-int64_t GetVirtualTransactionSize(int64_t nWeight)
+unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
+
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
{
- return (nWeight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
+ return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}
-int64_t GetVirtualTransactionSize(const CTransaction& tx)
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost)
{
- return GetVirtualTransactionSize(GetTransactionWeight(tx));
+ return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost);
}
diff --git a/src/policy/policy.h b/src/policy/policy.h
index f5f8652fb5..6bf5ca0ee5 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -28,6 +28,8 @@ static const unsigned int MAX_P2SH_SIGOPS = 15;
static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5;
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
+/** Default for -bytespersigop */
+static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
@@ -53,12 +55,12 @@ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
LOCKTIME_MEDIAN_TIME_PAST;
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
+bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled = false);
/**
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
-bool IsStandardTx(const CTransaction& tx, std::string& reason);
+bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled = false);
/**
* Check for standard transaction types
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
@@ -66,8 +68,10 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason);
*/
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
+extern unsigned int nBytesPerSigOp;
+
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
-int64_t GetVirtualTransactionSize(int64_t nWeight);
-int64_t GetVirtualTransactionSize(const CTransaction& tx);
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
#endif // BITCOIN_POLICY_POLICY_H
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 8d63805643..2fdc59ea07 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -100,7 +100,7 @@ CAmount CTransaction::GetValueOut() const
{
nValueOut += it->nValue;
if (!MoneyRange(it->nValue) || !MoneyRange(nValueOut))
- throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
}
return nValueOut;
}
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index d37890667f..5689d15bf7 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -290,6 +290,8 @@ struct CMutableTransaction;
*/
template<typename Stream, typename Operation, typename TxType>
inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) {
+ const bool fAllowWitness = !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS);
+
READWRITE(*const_cast<int32_t*>(&tx.nVersion));
unsigned char flags = 0;
if (ser_action.ForRead()) {
@@ -298,7 +300,7 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
const_cast<CTxWitness*>(&tx.wit)->SetNull();
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- if (tx.vin.size() == 0 && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) {
+ if (tx.vin.size() == 0 && fAllowWitness) {
/* We read a dummy or an empty vin. */
READWRITE(flags);
if (flags != 0) {
@@ -309,7 +311,7 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
/* We read a non-empty vin. Assume a normal vout follows. */
READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
}
- if ((flags & 1) && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) {
+ if ((flags & 1) && fAllowWitness) {
/* The witness flag is present, and we support witnesses. */
flags ^= 1;
const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
@@ -322,7 +324,7 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
} else {
// Consistency check
assert(tx.wit.vtxinwit.size() <= tx.vin.size());
- if (!(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) {
+ if (fAllowWitness) {
/* Check whether witnesses need to be serialized. */
if (!tx.wit.IsNull()) {
flags |= 1;
diff --git a/src/protocol.h b/src/protocol.h
index 15f27e2d2f..015215b2a6 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -10,7 +10,7 @@
#ifndef BITCOIN_PROTOCOL_H
#define BITCOIN_PROTOCOL_H
-#include "netbase.h"
+#include "netaddress.h"
#include "serialize.h"
#include "uint256.h"
#include "version.h"
diff --git a/src/pubkey.h b/src/pubkey.h
index db5444ea9d..aebfdbc826 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -13,7 +13,7 @@
#include <stdexcept>
#include <vector>
-/**
+/**
* secp256k1:
* const unsigned int PRIVATE_KEY_SIZE = 279;
* const unsigned int PUBLIC_KEY_SIZE = 65;
@@ -156,7 +156,7 @@ public:
/*
* Check syntactic correctness.
- *
+ *
* Note that this is consensus critical as CheckSig() calls it!
*/
bool IsValid() const
@@ -203,8 +203,11 @@ struct CExtPubKey {
friend bool operator==(const CExtPubKey &a, const CExtPubKey &b)
{
- return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
- a.chaincode == b.chaincode && a.pubkey == b.pubkey;
+ return a.nDepth == b.nDepth &&
+ memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
+ a.nChild == b.nChild &&
+ a.chaincode == b.chaincode &&
+ a.pubkey == b.pubkey;
}
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 64b5c83d72..430e6dd0e8 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -578,7 +578,8 @@ int main(int argc, char *argv[])
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
- Intro::pickDataDirectory();
+ if (!Intro::pickDataDirectory())
+ return 0;
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index 24b0bae3ec..ca5b1fa673 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -50,6 +50,8 @@
<file alias="fontsmaller">res/icons/fontsmaller.png</file>
<file alias="prompticon">res/icons/chevron.png</file>
<file alias="transaction_abandoned">res/icons/transaction_abandoned.png</file>
+ <file alias="hd_enabled">res/icons/hd_enabled.png</file>
+ <file alias="hd_disabled">res/icons/hd_disabled.png</file>
</qresource>
<qresource prefix="/movies">
<file alias="spinner-000">res/movies/spinner-000.png</file>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 9042e3b56a..272df3fdae 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -80,7 +80,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
clientModel(0),
walletFrame(0),
unitDisplayControl(0),
- labelEncryptionIcon(0),
+ labelWalletEncryptionIcon(0),
+ labelWalletHDStatusIcon(0),
labelConnectionsIcon(0),
labelBlocksIcon(0),
progressBarLabel(0),
@@ -194,7 +195,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
frameBlocksLayout->setContentsMargins(3,0,3,0);
frameBlocksLayout->setSpacing(3);
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
- labelEncryptionIcon = new QLabel();
+ labelWalletEncryptionIcon = new QLabel();
+ labelWalletHDStatusIcon = new QLabel();
labelConnectionsIcon = new QLabel();
labelBlocksIcon = new QLabel();
if(enableWallet)
@@ -202,7 +204,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(unitDisplayControl);
frameBlocksLayout->addStretch();
- frameBlocksLayout->addWidget(labelEncryptionIcon);
+ frameBlocksLayout->addWidget(labelWalletEncryptionIcon);
+ frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
}
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelConnectionsIcon);
@@ -902,17 +905,22 @@ void BitcoinGUI::closeEvent(QCloseEvent *event)
#ifndef Q_OS_MAC // Ignored on Mac
if(clientModel && clientModel->getOptionsModel())
{
- if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
- !clientModel->getOptionsModel()->getMinimizeOnClose())
+ if(!clientModel->getOptionsModel()->getMinimizeOnClose())
{
// close rpcConsole in case it was open to make some space for the shutdown window
rpcConsole->close();
QApplication::quit();
}
+ else
+ {
+ QMainWindow::showMinimized();
+ event->ignore();
+ }
}
-#endif
+#else
QMainWindow::closeEvent(event);
+#endif
}
void BitcoinGUI::showEvent(QShowEvent *event)
@@ -983,28 +991,37 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
return false;
}
+void BitcoinGUI::setHDStatus(int hdEnabled)
+{
+ labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletHDStatusIcon->setToolTip(hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
+
+ // eventually disable the QLabel to set its opacity to 50%
+ labelWalletHDStatusIcon->setEnabled(hdEnabled);
+}
+
void BitcoinGUI::setEncryptionStatus(int status)
{
switch(status)
{
case WalletModel::Unencrypted:
- labelEncryptionIcon->hide();
+ labelWalletEncryptionIcon->hide();
encryptWalletAction->setChecked(false);
changePassphraseAction->setEnabled(false);
encryptWalletAction->setEnabled(true);
break;
case WalletModel::Unlocked:
- labelEncryptionIcon->show();
- labelEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
- labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
+ labelWalletEncryptionIcon->show();
+ labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
break;
case WalletModel::Locked:
- labelEncryptionIcon->show();
- labelEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
- labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
+ labelWalletEncryptionIcon->show();
+ labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 12e7702ed8..41770929b4 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -82,7 +82,8 @@ private:
WalletFrame *walletFrame;
UnitDisplayStatusBarControl *unitDisplayControl;
- QLabel *labelEncryptionIcon;
+ QLabel *labelWalletEncryptionIcon;
+ QLabel *labelWalletHDStatusIcon;
QLabel *labelConnectionsIcon;
QLabel *labelBlocksIcon;
QLabel *progressBarLabel;
@@ -169,6 +170,12 @@ public Q_SLOTS:
*/
void setEncryptionStatus(int status);
+ /** Set the hd-enabled status as shown in the UI.
+ @param[in] status current hd enabled status
+ @see WalletModel::EncryptionStatus
+ */
+ void setHDStatus(int hdEnabled);
+
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
/** Show incoming transaction notification for new transactions. */
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 837f8ba6c1..f53242100c 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -76,7 +76,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget
QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
- QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
@@ -85,7 +84,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget
connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee()));
connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee()));
connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes()));
- connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority()));
connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput()));
connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange()));
@@ -94,7 +92,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
- ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
@@ -124,16 +121,14 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget
ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString());
ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
- ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
- ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170);
- ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290);
- ui->treeWidget->setColumnWidth(COLUMN_DATE, 110);
- ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100);
- ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100);
+ ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110);
+ ui->treeWidget->setColumnWidth(COLUMN_LABEL, 190);
+ ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 320);
+ ui->treeWidget->setColumnWidth(COLUMN_DATE, 130);
+ ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transaction hash in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but don't show it
- ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64 in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_DATE_INT64, true); // store date int64 in this column, but don't show it
// default view is sorted by amount desc
@@ -325,12 +320,6 @@ void CoinControlDialog::clipboardBytes()
GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
}
-// copy label "Priority" to clipboard
-void CoinControlDialog::clipboardPriority()
-{
- GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
-}
-
// copy label "Dust" to clipboard
void CoinControlDialog::clipboardLowOutput()
{
@@ -419,25 +408,6 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
#endif
}
-// return human readable label for priority number
-QString CoinControlDialog::getPriorityLabel(double dPriority, double mempoolEstimatePriority)
-{
- double dPriorityMedium = mempoolEstimatePriority;
-
- if (dPriorityMedium <= 0)
- dPriorityMedium = AllowFreeThreshold(); // not enough data, back to hard-coded
-
- if (dPriority / 1000000 > dPriorityMedium) return tr("highest");
- else if (dPriority / 100000 > dPriorityMedium) return tr("higher");
- else if (dPriority / 10000 > dPriorityMedium) return tr("high");
- else if (dPriority / 1000 > dPriorityMedium) return tr("medium-high");
- else if (dPriority > dPriorityMedium) return tr("medium");
- else if (dPriority * 10 > dPriorityMedium) return tr("low-medium");
- else if (dPriority * 100 > dPriorityMedium) return tr("low");
- else if (dPriority * 1000 > dPriorityMedium) return tr("lower");
- else return tr("lowest");
-}
-
// shows count of locked unspent outputs
void CoinControlDialog::updateLabelLocked()
{
@@ -473,7 +443,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
}
}
- QString sPriorityLabel = tr("none");
CAmount nAmount = 0;
CAmount nPayFee = 0;
CAmount nAfterFee = 0;
@@ -551,11 +520,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes += nQuantity; // account for the witness byte that holds the number of stack items for each input.
}
- // Priority
- double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);
- dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
- sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority);
-
// in the subtract fee from amount case, we can tell if zero change already and subtract the bytes, so that fee calculation afterwards is accurate
if (CoinControlDialog::fSubtractFeeFromAmount)
if (nAmount - nPayAmount == 0)
@@ -568,6 +532,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// Allow free? (require at least hard-coded threshold and default to that if no estimate)
+ double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);
+ dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold());
fAllowFree = (dPriority >= dPriorityNeeded);
@@ -617,7 +583,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
- QLabel *l6 = dialog->findChild<QLabel *>("labelCoinControlPriority");
QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
@@ -633,7 +598,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee
l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee
l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes
- l6->setText(sPriorityLabel); // Priority
l7->setText(fDust ? tr("yes") : tr("no")); // Dust
l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
if (nPayFee > 0 && (coinControl->nMinimumTotalFee < nPayFee))
@@ -644,21 +608,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
l8->setText(ASYMP_UTF8 + l8->text());
}
- // turn labels "red"
- l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000
- l6->setStyleSheet((dPriority > 0 && !fAllowFree) ? "color:red;" : ""); // Priority < "medium"
- l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes"
+ // turn label red when dust
+ l7->setStyleSheet((fDust) ? "color:red;" : "");
// tool tips
- QString toolTip1 = tr("This label turns red if the transaction size is greater than 1000 bytes.") + "<br /><br />";
- toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, CWallet::GetRequiredFee(1000))) + "<br /><br />";
- toolTip1 += tr("Can vary +/- 1 byte per input.");
-
- QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "<br /><br />";
- toolTip2 += tr("This label turns red if the priority is smaller than \"medium\".") + "<br /><br />";
- toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, CWallet::GetRequiredFee(1000)));
-
- QString toolTip3 = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold.");
+ 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
double dFeeVary;
@@ -671,14 +625,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
l3->setToolTip(toolTip4);
l4->setToolTip(toolTip4);
- l5->setToolTip(toolTip1);
- l6->setToolTip(toolTip2);
- l7->setToolTip(toolTip3);
+ l7->setToolTip(toolTipDust);
l8->setToolTip(toolTip4);
dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip());
dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip());
dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip());
- dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip());
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
@@ -702,7 +653,6 @@ void CoinControlDialog::updateView()
QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
- double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);
std::map<QString, std::vector<COutput> > mapCoins;
model->listCoins(mapCoins);
@@ -731,11 +681,8 @@ void CoinControlDialog::updateView()
}
CAmount nSum = 0;
- double dPrioritySum = 0;
int nChildren = 0;
- int nInputSum = 0;
BOOST_FOREACH(const COutput& out, coins.second) {
- int nInputSize = 0;
nSum += out.tx->vout[out.i].nValue;
nChildren++;
@@ -755,11 +702,6 @@ void CoinControlDialog::updateView()
// if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress)))
itemOutput->setText(COLUMN_ADDRESS, sAddress);
-
- CPubKey pubkey;
- CKeyID *keyid = boost::get<CKeyID>(&outputAddress);
- if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed())
- nInputSize = 29; // 29 = 180 - 151 (public key is 180 bytes, priority free area is 151 bytes)
}
// label
@@ -788,13 +730,6 @@ void CoinControlDialog::updateView()
// confirmations
itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
- // priority
- double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
- itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority));
- itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
- dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
- nInputSum += nInputSize;
-
// transaction hash
uint256 txhash = out.tx->GetHash();
itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex()));
@@ -819,12 +754,9 @@ void CoinControlDialog::updateView()
// amount
if (treeMode)
{
- dPrioritySum = dPrioritySum / (nInputSum + 78);
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
- itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum, mempoolEstimatePriority));
- itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " "));
}
}
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 1a467eb2ff..7d73421e3a 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -40,7 +40,6 @@ public:
// static because also called from sendcoinsdialog
static void updateLabels(WalletModel*, QDialog*);
- static QString getPriorityLabel(double dPriority, double mempoolEstimatePriority);
static QList<CAmount> payAmounts;
static CCoinControl *coinControl;
@@ -72,11 +71,9 @@ private:
COLUMN_ADDRESS,
COLUMN_DATE,
COLUMN_CONFIRMATIONS,
- COLUMN_PRIORITY,
COLUMN_TXHASH,
COLUMN_VOUT_INDEX,
COLUMN_AMOUNT_INT64,
- COLUMN_PRIORITY_INT64,
COLUMN_DATE_INT64
};
@@ -87,8 +84,6 @@ private:
{
if (column == COLUMN_AMOUNT_INT64)
return COLUMN_AMOUNT;
- else if (column == COLUMN_PRIORITY_INT64)
- return COLUMN_PRIORITY;
else if (column == COLUMN_DATE_INT64)
return COLUMN_DATE;
}
@@ -96,8 +91,6 @@ private:
{
if (column == COLUMN_AMOUNT)
return COLUMN_AMOUNT_INT64;
- else if (column == COLUMN_PRIORITY)
- return COLUMN_PRIORITY_INT64;
else if (column == COLUMN_DATE)
return COLUMN_DATE_INT64;
}
@@ -118,7 +111,6 @@ private Q_SLOTS:
void clipboardFee();
void clipboardAfterFee();
void clipboardBytes();
- void clipboardPriority();
void clipboardLowOutput();
void clipboardChange();
void radioTreeMode(bool);
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index c1fef6b9b1..1ea00eb5c3 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -140,7 +140,10 @@
</widget>
</item>
<item row="1" column="0">
- <widget class="QLabel" name="labelCoinControlPriorityText">
+ <widget class="QLabel" name="labelCoinControlLowOutputText">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="font">
<font>
<weight>75</weight>
@@ -148,12 +151,15 @@
</font>
</property>
<property name="text">
- <string>Priority:</string>
+ <string>Dust:</string>
</property>
</widget>
</item>
<item row="1" column="1">
- <widget class="QLabel" name="labelCoinControlPriority">
+ <widget class="QLabel" name="labelCoinControlLowOutput">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -161,7 +167,7 @@
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
- <string notr="true">medium</string>
+ <string notr="true">no</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
@@ -213,41 +219,6 @@
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="labelCoinControlLowOutputText">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Dust:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="labelCoinControlLowOutput">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="contextMenuPolicy">
- <enum>Qt::ActionsContextMenu</enum>
- </property>
- <property name="text">
- <string notr="true">no</string>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
</layout>
</item>
<item>
@@ -431,7 +402,7 @@
<bool>false</bool>
</property>
<property name="columnCount">
- <number>12</number>
+ <number>10</number>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
@@ -474,16 +445,6 @@
</column>
<column>
<property name="text">
- <string>Priority</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string/>
- </property>
- </column>
- <column>
- <property name="text">
<string/>
</property>
</column>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 12d6a62c08..06e09074d1 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -332,7 +332,7 @@
</widget>
</item>
<item row="1" column="0">
- <widget class="QLabel" name="labelCoinControlPriorityText">
+ <widget class="QLabel" name="labelCoinControlLowOutputText">
<property name="font">
<font>
<weight>75</weight>
@@ -340,12 +340,12 @@
</font>
</property>
<property name="text">
- <string>Priority:</string>
+ <string>Dust:</string>
</property>
</widget>
</item>
<item row="1" column="1">
- <widget class="QLabel" name="labelCoinControlPriority">
+ <widget class="QLabel" name="labelCoinControlLowOutput">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -353,7 +353,7 @@
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
- <string notr="true">medium</string>
+ <string notr="true">no</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
@@ -411,36 +411,7 @@
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="labelCoinControlLowOutputText">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Dust:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="labelCoinControlLowOutput">
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="contextMenuPolicy">
- <enum>Qt::ActionsContextMenu</enum>
- </property>
- <property name="text">
- <string notr="true">no</string>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- </layout>
+ </layout>
</item>
<item>
<layout class="QFormLayout" name="formLayoutCoinControl4">
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 6d6af54290..1a241ae0f0 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -165,20 +165,20 @@ QString Intro::getDefaultDataDirectory()
return GUIUtil::boostPathToQString(GetDefaultDataDir());
}
-void Intro::pickDataDirectory()
+bool Intro::pickDataDirectory()
{
namespace fs = boost::filesystem;
QSettings settings;
/* If data directory provided on command line, no need to look at settings
or show a picking dialog */
if(!GetArg("-datadir", "").empty())
- return;
+ return true;
/* 1) Default data directory for operating system */
QString dataDir = getDefaultDataDirectory();
/* 2) Allow QSettings to override default dir */
dataDir = settings.value("strDataDir", dataDir).toString();
- if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR))
+ if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || GetBoolArg("-resetguisettings", false))
{
/* If current default data directory does not exist, let the user choose one */
Intro intro;
@@ -190,7 +190,7 @@ void Intro::pickDataDirectory()
if(!intro.exec())
{
/* Cancel clicked */
- exit(0);
+ return false;
}
dataDir = intro.getDataDirectory();
try {
@@ -204,6 +204,7 @@ void Intro::pickDataDirectory()
}
settings.setValue("strDataDir", dataDir);
+ settings.setValue("fReset", false);
}
/* Only override -datadir if different from the default, to make it possible to
* override -datadir in the bitcoin.conf file in the default data directory
@@ -211,6 +212,7 @@ void Intro::pickDataDirectory()
*/
if(dataDir != getDefaultDataDirectory())
SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
+ return true;
}
void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 9e2e96dc9e..ee768a7ad8 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -35,10 +35,13 @@ public:
/**
* Determine data directory. Let the user choose if the current one doesn't exist.
*
+ * @returns true if a data directory was selected, false if the user cancelled the selection
+ * dialog.
+ *
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static void pickDataDirectory();
+ static bool pickDataDirectory();
/**
* Determine default data directory for operating system.
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index f2db398899..f73bb87064 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -327,7 +327,8 @@ QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) cons
{
Q_UNUSED(pos);
// Validate the proxy
- proxyType addrProxy = proxyType(CService(input.toStdString(), 9050), true);
+ CService serv(LookupNumeric(input.toStdString().c_str(), 9050));
+ proxyType addrProxy = proxyType(serv, true);
if (addrProxy.IsValid())
return QValidator::Acceptable;
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index cc2cbc0e66..f82e153b67 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -15,7 +15,9 @@
#include "init.h"
#include "main.h" // For DEFAULT_SCRIPTCHECK_THREADS
#include "net.h"
+#include "netbase.h"
#include "txdb.h" // for -dbcache defaults
+#include "intro.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
@@ -43,6 +45,8 @@ void OptionsModel::Init(bool resetSettings)
if (resetSettings)
Reset();
+ checkAndMigrate();
+
QSettings settings;
// Ensure restart flag is unset on client startup
@@ -96,6 +100,9 @@ void OptionsModel::Init(bool resetSettings)
if (!SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
addOverriddenOption("-par");
+ if (!settings.contains("strDataDir"))
+ settings.setValue("strDataDir", Intro::getDefaultDataDirectory());
+
// Wallet
#ifdef ENABLE_WALLET
if (!settings.contains("bSpendZeroConfChange"))
@@ -148,9 +155,19 @@ void OptionsModel::Reset()
{
QSettings settings;
+ // Save the strDataDir setting
+ QString dataDir = Intro::getDefaultDataDirectory();
+ dataDir = settings.value("strDataDir", dataDir).toString();
+
// Remove all entries from our QSettings object
settings.clear();
+ // Set strDataDir
+ settings.setValue("strDataDir", dataDir);
+
+ // Set that this was reset
+ settings.setValue("fReset", true);
+
// default setting for OptionsModel::StartAtStartup - disabled
if (GUIUtil::GetStartOnSystemStartup())
GUIUtil::SetStartOnSystemStartup(false);
@@ -429,3 +446,22 @@ bool OptionsModel::isRestartRequired()
QSettings settings;
return settings.value("fRestartRequired", false).toBool();
}
+
+void OptionsModel::checkAndMigrate()
+{
+ // Migration of default values
+ // Check if the QSettings container was already loaded with this client version
+ QSettings settings;
+ static const char strSettingsVersionKey[] = "nSettingsVersion";
+ int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0;
+ if (settingsVersion < CLIENT_VERSION)
+ {
+ // -dbcache was bumped from 100 to 300 in 0.13
+ // see https://github.com/bitcoin/bitcoin/pull/8273
+ // force people to upgrade to the new value if they are using 100MB
+ if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100)
+ settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
+
+ settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
+ }
+} \ No newline at end of file
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 3b491ceac2..b23b5f2607 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -84,9 +84,11 @@ private:
/* settings that were overriden by command-line */
QString strOverriddenByCommandLine;
- /// Add option to list of GUI options overridden through command line/config file
+ // Add option to list of GUI options overridden through command line/config file
void addOverriddenOption(const std::string &option);
+ // Check settings version and upgrade default values if required
+ void checkAndMigrate();
Q_SIGNALS:
void displayUnitChanged(int unit);
void coinControlFeaturesChanged(bool);
diff --git a/src/qt/res/icons/hd_disabled.png b/src/qt/res/icons/hd_disabled.png
new file mode 100644
index 0000000000..687b6d2e38
--- /dev/null
+++ b/src/qt/res/icons/hd_disabled.png
Binary files differ
diff --git a/src/qt/res/icons/hd_enabled.png b/src/qt/res/icons/hd_enabled.png
new file mode 100644
index 0000000000..568dde1cd1
--- /dev/null
+++ b/src/qt/res/icons/hd_enabled.png
Binary files differ
diff --git a/src/qt/res/src/hd_disabled.svg b/src/qt/res/src/hd_disabled.svg
new file mode 100644
index 0000000000..035f4431c7
--- /dev/null
+++ b/src/qt/res/src/hd_disabled.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 595.3 841.9" enable-background="new 0 0 595.3 841.9" xml:space="preserve">
+<g>
+ <path d="M81.3,336.5v66.8h70.4v-66.8H190v174h-38.3v-75.1H81.3v75.1H43v-174H81.3z"/>
+ <path d="M298.7,336.5c11.2,0,21.6,1.8,31.3,5.4c9.7,3.6,18,8.9,25.1,16.1c7.1,7.2,12.6,16.1,16.6,26.8c4,10.7,6,23.3,6,37.8
+ c0,12.7-1.6,24.4-4.9,35.1c-3.3,10.7-8.2,20-14.7,27.8c-6.6,7.8-14.8,13.9-24.6,18.4c-9.8,4.5-21.4,6.7-34.7,6.7h-75.1v-174H298.7z
+ M296,478.3c5.5,0,10.9-0.9,16.1-2.7c5.2-1.8,9.8-4.8,13.9-8.9c4.1-4.1,7.3-9.5,9.7-16.2c2.4-6.7,3.7-14.8,3.7-24.4
+ c0-8.8-0.9-16.7-2.6-23.8s-4.5-13.1-8.4-18.2c-3.9-5-9.1-8.9-15.5-11.6c-6.4-2.7-14.3-4-23.8-4h-27.3v109.7H296z"/>
+</g>
+<g>
+ <g>
+ <line x1="32" y1="555.9" x2="358" y2="293.9"/>
+ </g>
+ <g>
+ <path fill="#FFFFFF" d="M32,580.9c-7.3,0-14.6-3.2-19.5-9.3c-8.6-10.8-6.9-26.5,3.8-35.1l326-262c10.8-8.6,26.5-6.9,35.1,3.8
+ c8.6,10.8,6.9,26.5-3.8,35.1l-326,262C43,579.1,37.5,580.9,32,580.9z"/>
+ </g>
+ <g>
+ <path d="M32,573.9c-5.3,0-10.5-2.3-14-6.7c-6.2-7.7-5-19.1,2.8-25.3l326-262c7.8-6.2,19.1-5,25.3,2.8c6.2,7.7,5,19.1-2.8,25.3
+ l-326,262C40,572.6,36,573.9,32,573.9z"/>
+ </g>
+</g>
+</svg>
diff --git a/src/qt/res/src/hd_enabled.svg b/src/qt/res/src/hd_enabled.svg
new file mode 100644
index 0000000000..cbaa16f8f0
--- /dev/null
+++ b/src/qt/res/src/hd_enabled.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 595.3 841.9" enable-background="new 0 0 595.3 841.9" xml:space="preserve">
+<g>
+ <path d="M81.3,336.5v66.8h70.4v-66.8H190v174h-38.3v-75.1H81.3v75.1H43v-174H81.3z"/>
+ <path d="M298.7,336.5c11.2,0,21.6,1.8,31.3,5.4c9.7,3.6,18,8.9,25.1,16.1c7.1,7.2,12.6,16.1,16.6,26.8c4,10.7,6,23.3,6,37.8
+ c0,12.7-1.6,24.4-4.9,35.1c-3.3,10.7-8.2,20-14.7,27.8c-6.6,7.8-14.8,13.9-24.6,18.4c-9.8,4.5-21.4,6.7-34.7,6.7h-75.1v-174H298.7z
+ M296,478.3c5.5,0,10.9-0.9,16.1-2.7c5.2-1.8,9.8-4.8,13.9-8.9c4.1-4.1,7.3-9.5,9.7-16.2c2.4-6.7,3.7-14.8,3.7-24.4
+ c0-8.8-0.9-16.7-2.6-23.8s-4.5-13.1-8.4-18.2c-3.9-5-9.1-8.9-15.5-11.6c-6.4-2.7-14.3-4-23.8-4h-27.3v109.7H296z"/>
+</g>
+</svg>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 650ff8b00d..bcaa9164c9 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -16,6 +16,7 @@
#include "bantablemodel.h"
#include "chainparams.h"
+#include "netbase.h"
#include "rpc/server.h"
#include "rpc/client.h"
#include "util.h"
@@ -898,7 +899,10 @@ void RPCConsole::banSelectedNode(int bantime)
int port = 0;
SplitHostPort(nStr, port, addr);
- CNode::Ban(CNetAddr(addr), BanReasonManuallyAdded, bantime);
+ CNetAddr resolved;
+ if(!LookupHost(addr.c_str(), resolved, false))
+ return;
+ CNode::Ban(resolved, BanReasonManuallyAdded, bantime);
clearSelectedNode();
clientModel->getBanTableModel()->refresh();
@@ -912,8 +916,9 @@ void RPCConsole::unbanSelectedNode()
// Get currently selected ban address
QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address);
- CSubNet possibleSubnet(strNode.toStdString());
+ CSubNet possibleSubnet;
+ LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
if (possibleSubnet.IsValid())
{
CNode::Unban(possibleSubnet);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 6d50be56ec..3e96bb18c3 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -69,7 +69,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa
QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
- QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
@@ -77,7 +76,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa
connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
- connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority()));
connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
@@ -85,7 +83,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
- ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
@@ -681,12 +678,6 @@ void SendCoinsDialog::coinControlClipboardBytes()
GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
}
-// Coin Control: copy label "Priority" to clipboard
-void SendCoinsDialog::coinControlClipboardPriority()
-{
- GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
-}
-
// Coin Control: copy label "Dust" to clipboard
void SendCoinsDialog::coinControlClipboardLowOutput()
{
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index be4f2ee44b..83dac0bd11 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -88,7 +88,6 @@ private Q_SLOTS:
void coinControlClipboardFee();
void coinControlClipboardAfterFee();
void coinControlClipboardBytes();
- void coinControlClipboardPriority();
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
void setMinimumFee();
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 3867310cd6..ae7efc7a0d 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -683,3 +683,8 @@ bool WalletModel::abandonTransaction(uint256 hash) const
LOCK2(cs_main, wallet->cs_wallet);
return wallet->AbandonTransaction(hash);
}
+
+bool WalletModel::hdEnabled() const
+{
+ return wallet->IsHDEnabled();
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index e5470bf618..a15ecf899b 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -203,6 +203,8 @@ public:
bool transactionCanBeAbandoned(uint256 hash) const;
bool abandonTransaction(uint256 hash) const;
+ bool hdEnabled() const;
+
private:
CWallet *wallet;
bool fHaveWatchOnly;
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 6ce98ef160..495ebfd834 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -98,6 +98,9 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui)
// Pass through transaction notifications
connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString)));
+
+ // Connect HD enabled state signal
+ connect(this, SIGNAL(hdEnabledStatusChanged(int)), gui, SLOT(setHDStatus(int)));
}
}
@@ -130,6 +133,9 @@ void WalletView::setWalletModel(WalletModel *walletModel)
connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int)));
updateEncryptionStatus();
+ // update HD status
+ Q_EMIT hdEnabledStatusChanged(walletModel->hdEnabled());
+
// Balloon pop-up for new transaction
connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(processNewTransaction(QModelIndex,int,int)));
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index dbb289f425..2045605954 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -117,6 +117,8 @@ Q_SIGNALS:
void message(const QString &title, const QString &message, unsigned int style);
/** Encryption status of wallet changed */
void encryptionStatusChanged(int status);
+ /** HD-Enabled status of wallet changed (only possible during startup) */
+ void hdEnabledStatusChanged(int hdEnabled);
/** Notify that a new transaction appeared */
void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label);
};
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 9dc896b7af..e3c32d905a 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -817,22 +817,23 @@ UniValue verifychain(const UniValue& params, bool fHelp)
}
/** Implementation of IsSuperMajority with better feedback */
-static UniValue SoftForkMajorityDesc(int minVersion, CBlockIndex* pindex, int nRequired, const Consensus::Params& consensusParams)
+static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
- int nFound = 0;
- CBlockIndex* pstart = pindex;
- for (int i = 0; i < consensusParams.nMajorityWindow && pstart != NULL; i++)
+ UniValue rv(UniValue::VOBJ);
+ bool activated = false;
+ switch(version)
{
- if (pstart->nVersion >= minVersion)
- ++nFound;
- pstart = pstart->pprev;
+ case 2:
+ activated = pindex->nHeight >= consensusParams.BIP34Height;
+ break;
+ case 3:
+ activated = pindex->nHeight >= consensusParams.BIP66Height;
+ break;
+ case 4:
+ activated = pindex->nHeight >= consensusParams.BIP65Height;
+ break;
}
-
- UniValue rv(UniValue::VOBJ);
- rv.push_back(Pair("status", nFound >= nRequired));
- rv.push_back(Pair("found", nFound));
- rv.push_back(Pair("required", nRequired));
- rv.push_back(Pair("window", consensusParams.nMajorityWindow));
+ rv.push_back(Pair("status", activated));
return rv;
}
@@ -841,8 +842,7 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex*
UniValue rv(UniValue::VOBJ);
rv.push_back(Pair("id", name));
rv.push_back(Pair("version", version));
- rv.push_back(Pair("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)));
- rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams)));
+ rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams)));
return rv;
}
@@ -897,13 +897,9 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" {\n"
" \"id\": \"xxxx\", (string) name of softfork\n"
" \"version\": xx, (numeric) block version\n"
- " \"enforce\": { (object) progress toward enforcing the softfork rules for new-version blocks\n"
+ " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
" \"status\": xx, (boolean) true if threshold reached\n"
- " \"found\": xx, (numeric) number of blocks with the new version found\n"
- " \"required\": xx, (numeric) number of blocks required to trigger\n"
- " \"window\": xx, (numeric) maximum size of examined window of recent blocks\n"
" },\n"
- " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n"
" }, ...\n"
" ],\n"
" \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 92ca4bab6b..a26340f3e4 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -227,10 +227,11 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
" \"currentblockweight\": nnn, (numeric) The last block weight\n"
" \"currentblocktx\": nnn, (numeric) The last block transaction\n"
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
- " \"errors\": \"...\" (string) Current errors\n"
+ " \"errors\": \"...\" (string) Current errors\n"
+ " \"networkhashps\": nnn, (numeric) The network hashes per second\n"
" \"pooledtx\": n (numeric) The size of the mem pool\n"
" \"testnet\": true|false (boolean) If using testnet or not\n"
- " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
+ " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getmininginfo", "")
@@ -546,6 +547,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
UpdateTime(pblock, consensusParams, pindexPrev);
pblock->nNonce = 0;
+ // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
+ const bool fPreSegWit = (THRESHOLD_ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache));
+
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
UniValue transactions(UniValue::VARR);
@@ -574,7 +578,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
int index_in_template = i - 1;
entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
- entry.push_back(Pair("sigops", pblocktemplate->vTxSigOpsCost[index_in_template]));
+ int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
+ if (fPreSegWit) {
+ assert(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
+ nTxSigOps /= WITNESS_SCALE_FACTOR;
+ }
+ entry.push_back(Pair("sigops", nTxSigOps));
entry.push_back(Pair("weight", GetTransactionWeight(tx)));
transactions.push_back(entry);
@@ -642,7 +651,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
if (nMaxVersionPreVB >= 2) {
// If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here
- // Because BIP 34 changed how the generation transaction is serialised, we can only use version/force back to v2 blocks
+ // Because BIP 34 changed how the generation transaction is serialized, we can only use version/force back to v2 blocks
// This is safe to do [otherwise-]unconditionally only because we are throwing an exception above if a non-force deployment gets activated
// Note that this can probably also be removed entirely after the first BIP9 non-force deployment (ie, probably segwit) gets activated
aMutable.push_back("version/force");
@@ -657,7 +666,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
result.push_back(Pair("mutable", aMutable));
result.push_back(Pair("noncerange", "00000000ffffffff"));
- result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS_COST));
+ int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST;
+ if (fPreSegWit) {
+ assert(nSigOpLimit % WITNESS_SCALE_FACTOR == 0);
+ nSigOpLimit /= WITNESS_SCALE_FACTOR;
+ }
+ result.push_back(Pair("sigoplimit", nSigOpLimit));
result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SERIALIZED_SIZE));
result.push_back(Pair("weightlimit", (int64_t)MAX_BLOCK_WEIGHT));
result.push_back(Pair("curtime", pblock->GetBlockTime()));
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index b85c7b2e1a..4ce122648b 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -484,7 +484,7 @@ UniValue setban(const UniValue& params, bool fHelp)
"\nExamples:\n"
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
- + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400")
+ + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
);
CSubNet subNet;
@@ -494,10 +494,13 @@ UniValue setban(const UniValue& params, bool fHelp)
if (params[0].get_str().find("/") != string::npos)
isSubnet = true;
- if (!isSubnet)
- netAddr = CNetAddr(params[0].get_str());
+ if (!isSubnet) {
+ CNetAddr resolved;
+ LookupHost(params[0].get_str().c_str(), resolved, false);
+ netAddr = resolved;
+ }
else
- subNet = CSubNet(params[0].get_str());
+ LookupSubNet(params[0].get_str().c_str(), subNet);
if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet");
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 23149baa6d..5fb97f7496 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -25,6 +25,8 @@
#include <boost/thread.hpp>
#include <boost/algorithm/string/case_conv.hpp> // for to_upper()
+#include <memory> // for unique_ptr
+
using namespace RPCServer;
using namespace std;
@@ -34,9 +36,8 @@ static std::string rpcWarmupStatus("RPC server started");
static CCriticalSection cs_rpcWarmup;
/* Timer-creating functions */
static RPCTimerInterface* timerInterface = NULL;
-/* Map of name to timer.
- * @note Can be changed to std::unique_ptr when C++11 */
-static std::map<std::string, boost::shared_ptr<RPCTimerBase> > deadlineTimers;
+/* Map of name to timer. */
+static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
static struct CRPCSignals
{
@@ -490,7 +491,7 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6
throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
deadlineTimers.erase(name);
LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
- deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000))));
+ deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
}
CRPCTable tableRPC;
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
index 6f868d0d6d..f73a8e30bc 100644
--- a/src/script/bitcoinconsensus.h
+++ b/src/script/bitcoinconsensus.h
@@ -51,6 +51,7 @@ enum
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65)
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141)
};
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index e0b7b7a48a..efb277d347 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -25,17 +25,24 @@ config.status
libtool
.deps/
.dirstamp
-build-aux/
*.lo
*.o
*~
src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
-m4/libtool.m4
-m4/ltoptions.m4
-m4/ltsugar.m4
-m4/ltversion.m4
-m4/lt~obsolete.m4
+build-aux/config.guess
+build-aux/config.sub
+build-aux/depcomp
+build-aux/install-sh
+build-aux/ltmain.sh
+build-aux/m4/libtool.m4
+build-aux/m4/lt~obsolete.m4
+build-aux/m4/ltoptions.m4
+build-aux/m4/ltsugar.m4
+build-aux/m4/ltversion.m4
+build-aux/missing
+build-aux/compile
+build-aux/test-driver
src/stamp-h1
libsecp256k1.pc
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index 4e1e73c39f..2c5c63adad 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -6,26 +6,31 @@ addons:
compiler:
- clang
- gcc
+cache:
+ directories:
+ - src/java/guava/
env:
global:
- - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no
+ - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no EXPERIMENTAL=no
+ - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
matrix:
- SCALAR=32bit RECOVERY=yes
- - SCALAR=32bit FIELD=32bit ECDH=yes
+ - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
- SCALAR=64bit
- FIELD=64bit RECOVERY=yes
- FIELD=64bit ENDOMORPHISM=yes
- - FIELD=64bit ENDOMORPHISM=yes ECDH=yes
+ - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- FIELD=64bit ASM=x86_64
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- - FIELD=32bit SCHNORR=yes
+ - FIELD=32bit SCHNORR=yes EXPERIMENTAL=yes
- FIELD=32bit ENDOMORPHISM=yes
- BIGNUM=no
- - BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes
+ - BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- EXTRAFLAGS=CFLAGS=-O0
+ - BUILD=check-java ECDH=yes SCHNORR=yes EXPERIMENTAL=yes
matrix:
fast_finish: true
include:
@@ -55,9 +60,11 @@ matrix:
packages:
- gcc-multilib
- libgmp-dev:i386
+before_install: mkdir -p `dirname $GUAVA_JAR`
+install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi
before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- - ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
+ - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index 7772a4e9d2..3d130bdcbd 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -1,6 +1,12 @@
ACLOCAL_AMFLAGS = -I build-aux/m4
lib_LTLIBRARIES = libsecp256k1.la
+if USE_JNI
+JNI_LIB = libsecp256k1_jni.la
+noinst_LTLIBRARIES = $(JNI_LIB)
+else
+JNI_LIB =
+endif
include_HEADERS = include/secp256k1.h
noinst_HEADERS =
noinst_HEADERS += src/scalar.h
@@ -32,6 +38,7 @@ noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
noinst_HEADERS += src/field_5x52_asm_impl.h
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
+noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
@@ -45,35 +52,80 @@ noinst_HEADERS += contrib/lax_der_parsing.c
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
noinst_HEADERS += contrib/lax_der_privatekey_parsing.c
+if USE_EXTERNAL_ASM
+COMMON_LIB = libsecp256k1_common.la
+noinst_LTLIBRARIES = $(COMMON_LIB)
+else
+COMMON_LIB =
+endif
+
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsecp256k1.pc
+if USE_EXTERNAL_ASM
+if USE_ASM_ARM
+libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s
+endif
+endif
+
libsecp256k1_la_SOURCES = src/secp256k1.c
-libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
-libsecp256k1_la_LIBADD = $(SECP_LIBS)
+libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
+libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB)
+libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c
+libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
noinst_PROGRAMS =
if USE_BENCHMARK
noinst_PROGRAMS += bench_verify bench_sign bench_internal
bench_verify_SOURCES = src/bench_verify.c
-bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
+bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_sign_SOURCES = src/bench_sign.c
-bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS)
+bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
-bench_internal_LDADD = $(SECP_LIBS)
-bench_internal_CPPFLAGS = $(SECP_INCLUDES)
+bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
+bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
endif
if USE_TESTS
noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c
-tests_CPPFLAGS = -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
-tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS)
+tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
+tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
tests_LDFLAGS = -static
TESTS = tests
endif
+JAVAROOT=src/java
+JAVAORG=org/bitcoin
+JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
+CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
+JAVA_FILES= \
+ $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
+ $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
+ $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
+ $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java
+
+if USE_JNI
+
+$(JAVA_GUAVA):
+ @echo Guava is missing. Fetch it via: \
+ wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@)
+ @false
+
+.stamp-java: $(JAVA_FILES)
+ @echo Compiling $^
+ $(AM_V_at)$(CLASSPATH_ENV) javac $^
+ @touch $@
+
+if USE_TESTS
+
+check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
+ $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test
+
+endif
+endif
+
if USE_ECMULT_STATIC_PRECOMPUTATION
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
@@ -93,10 +145,10 @@ $(bench_internal_OBJECTS): src/ecmult_static_context.h
src/ecmult_static_context.h: $(gen_context_BIN)
./$(gen_context_BIN)
-CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
+CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
endif
-EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
+EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)
if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index 6095db4220..8cd344ea81 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -1,7 +1,7 @@
libsecp256k1
============
-[![Build Status](https://travis-ci.org/bitcoin/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin/secp256k1)
+[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1)
Optimized C library for EC operations on curve secp256k1.
diff --git a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
new file mode 100644
index 0000000000..1fc3627614
--- /dev/null
+++ b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
@@ -0,0 +1,140 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_JNI_INCLUDE_DIR
+#
+# DESCRIPTION
+#
+# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
+# programs using the JNI interface.
+#
+# JNI include directories are usually in the Java distribution. This is
+# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
+# that order. When this macro completes, a list of directories is left in
+# the variable JNI_INCLUDE_DIRS.
+#
+# Example usage follows:
+#
+# AX_JNI_INCLUDE_DIR
+#
+# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
+# do
+# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
+# done
+#
+# If you want to force a specific compiler:
+#
+# - at the configure.in level, set JAVAC=yourcompiler before calling
+# AX_JNI_INCLUDE_DIR
+#
+# - at the configure level, setenv JAVAC
+#
+# Note: This macro can work with the autoconf M4 macros for Java programs.
+# This particular macro is not part of the original set of macros.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Don Anderson <dda@sleepycat.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 10
+
+AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
+AC_DEFUN([AX_JNI_INCLUDE_DIR],[
+
+JNI_INCLUDE_DIRS=""
+
+if test "x$JAVA_HOME" != x; then
+ _JTOPDIR="$JAVA_HOME"
+else
+ if test "x$JAVAC" = x; then
+ JAVAC=javac
+ fi
+ AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
+ if test "x$_ACJNI_JAVAC" = xno; then
+ AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
+ fi
+ _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
+ _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
+fi
+
+case "$host_os" in
+ darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+ _JINC="$_JTOPDIR/Headers";;
+ *) _JINC="$_JTOPDIR/include";;
+esac
+_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
+_AS_ECHO_LOG([_JINC=$_JINC])
+
+# On Mac OS X 10.6.4, jni.h is a symlink:
+# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
+# -> ../../CurrentJDK/Headers/jni.h.
+
+AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
+[
+if test -f "$_JINC/jni.h"; then
+ ac_cv_jni_header_path="$_JINC"
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
+else
+ _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
+ if test -f "$_JTOPDIR/include/jni.h"; then
+ ac_cv_jni_header_path="$_JTOPDIR/include"
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
+ else
+ ac_cv_jni_header_path=none
+ fi
+fi
+])
+
+
+
+# get the likely subdirectories for system specific java includes
+case "$host_os" in
+bsdi*) _JNI_INC_SUBDIRS="bsdos";;
+darwin*) _JNI_INC_SUBDIRS="darwin";;
+freebsd*) _JNI_INC_SUBDIRS="freebsd";;
+linux*) _JNI_INC_SUBDIRS="linux genunix";;
+osf*) _JNI_INC_SUBDIRS="alpha";;
+solaris*) _JNI_INC_SUBDIRS="solaris";;
+mingw*) _JNI_INC_SUBDIRS="win32";;
+cygwin*) _JNI_INC_SUBDIRS="win32";;
+*) _JNI_INC_SUBDIRS="genunix";;
+esac
+
+if test "x$ac_cv_jni_header_path" != "xnone"; then
+ # add any subdirectories that are present
+ for JINCSUBDIR in $_JNI_INC_SUBDIRS
+ do
+ if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
+ JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
+ fi
+ done
+fi
+])
+
+# _ACJNI_FOLLOW_SYMLINKS <path>
+# Follows symbolic links on <path>,
+# finally setting variable _ACJNI_FOLLOWED
+# ----------------------------------------
+AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
+# find the include directory relative to the javac executable
+_cur="$1"
+while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
+ AC_MSG_CHECKING([symlink for $_cur])
+ _slink=`ls -ld "$_cur" | sed 's/.* -> //'`
+ case "$_slink" in
+ /*) _cur="$_slink";;
+ # 'X' avoids triggering unwanted echo options.
+ *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
+ esac
+ AC_MSG_RESULT([$_cur])
+done
+_ACJNI_FOLLOWED="$_cur"
+])# _ACJNI
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index d41bbb6487..b25d8adb92 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -3,13 +3,13 @@ AC_DEFUN([SECP_INT128_CHECK],[
has_int128=$ac_cv_type___int128
])
-dnl
+dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
AC_MSG_CHECKING(for x86_64 assembly availability)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <stdint.h>]],[[
uint64_t a = 11, tmp;
- __asm__ __volatile__("movq $0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
+ __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
]])],[has_64bit_asm=yes],[has_64bit_asm=no])
AC_MSG_RESULT([$has_64bit_asm])
])
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 786d8dcfb9..0743c36690 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -29,6 +29,7 @@ AC_PROG_CC_C89
if test x"$ac_cv_prog_cc_c89" = x"no"; then
AC_MSG_ERROR([c89 compiler support required])
fi
+AM_PROG_AS
case $host_os in
*darwin*)
@@ -93,23 +94,33 @@ AC_ARG_ENABLE(tests,
[use_tests=$enableval],
[use_tests=yes])
+AC_ARG_ENABLE(openssl_tests,
+ AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]),
+ [enable_openssl_tests=$enableval],
+ [enable_openssl_tests=auto])
+
+AC_ARG_ENABLE(experimental,
+ AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]),
+ [use_experimental=$enableval],
+ [use_experimental=no])
+
AC_ARG_ENABLE(endomorphism,
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
[use_endomorphism=$enableval],
[use_endomorphism=no])
-
+
AC_ARG_ENABLE(ecmult_static_precomputation,
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]),
[use_ecmult_static_precomputation=$enableval],
- [use_ecmult_static_precomputation=yes])
+ [use_ecmult_static_precomputation=auto])
AC_ARG_ENABLE(module_ecdh,
- AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (default is no)]),
+ AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]),
[enable_module_ecdh=$enableval],
[enable_module_ecdh=no])
AC_ARG_ENABLE(module_schnorr,
- AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]),
+ AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (experimental)]),
[enable_module_schnorr=$enableval],
[enable_module_schnorr=no])
@@ -118,6 +129,11 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=$enableval],
[enable_module_recovery=no])
+AC_ARG_ENABLE(jni,
+ AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
+ [use_jni=$enableval],
+ [use_jni=auto])
+
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
@@ -127,8 +143,8 @@ AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto])
-AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|no|auto]
-[Specify assembly optimizations to use. Default is auto])],[req_asm=$withval], [req_asm=auto])
+AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
+[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto])
AC_CHECK_TYPES([__int128])
@@ -138,6 +154,34 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
[ AC_MSG_RESULT([no])
])
+if test x"$use_ecmult_static_precomputation" != x"no"; then
+ save_cross_compiling=$cross_compiling
+ cross_compiling=no
+ TEMP_CC="$CC"
+ CC="$CC_FOR_BUILD"
+ AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([], [return 0])],
+ [working_native_cc=yes],
+ [working_native_cc=no],[dnl])
+ CC="$TEMP_CC"
+ cross_compiling=$save_cross_compiling
+
+ if test x"$working_native_cc" = x"no"; then
+ set_precomp=no
+ if test x"$use_ecmult_static_precomputation" = x"yes"; then
+ AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ else
+ AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
+ fi
+ else
+ AC_MSG_RESULT([ok])
+ set_precomp=yes
+ fi
+else
+ set_precomp=no
+fi
+
if test x"$req_asm" = x"auto"; then
SECP_64BIT_ASM_CHECK
if test x"$has_64bit_asm" = x"yes"; then
@@ -155,6 +199,8 @@ else
AC_MSG_ERROR([x86_64 assembly optimization requested but not available])
fi
;;
+ arm)
+ ;;
no)
;;
*)
@@ -247,10 +293,15 @@ else
fi
# select assembly optimization
+use_external_asm=no
+
case $set_asm in
x86_64)
AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations])
;;
+arm)
+ use_external_asm=yes
+ ;;
no)
;;
*)
@@ -305,16 +356,51 @@ esac
if test x"$use_tests" = x"yes"; then
SECP_OPENSSL_CHECK
if test x"$has_openssl_ec" = x"yes"; then
- AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
- SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
- SECP_TEST_LIBS="$CRYPTO_LIBS"
-
- case $host in
- *mingw*)
- SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
- ;;
- esac
+ if test x"$enable_openssl_tests" != x"no"; then
+ AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
+ SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
+ SECP_TEST_LIBS="$CRYPTO_LIBS"
+
+ case $host in
+ *mingw*)
+ SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
+ ;;
+ esac
+ fi
+ else
+ if test x"$enable_openssl_tests" = x"yes"; then
+ AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available])
+ fi
+ fi
+else
+ if test x"$enable_openssl_tests" = x"yes"; then
+ AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled])
+ fi
+fi
+if test x"$use_jni" != x"no"; then
+ AX_JNI_INCLUDE_DIR
+ have_jni_dependencies=yes
+ if test x"$enable_module_schnorr" = x"no"; then
+ have_jni_dependencies=no
+ fi
+ if test x"$enable_module_ecdh" = x"no"; then
+ have_jni_dependencies=no
+ fi
+ if test "x$JNI_INCLUDE_DIRS" = "x"; then
+ have_jni_dependencies=no
+ fi
+ if test "x$have_jni_dependencies" = "xno"; then
+ if test x"$use_jni" = x"yes"; then
+ AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and Schnorr and try again.])
+ fi
+ AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
+ use_jni=no
+ else
+ use_jni=yes
+ for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
+ JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR"
+ done
fi
fi
@@ -345,18 +431,43 @@ fi
AC_C_BIGENDIAN()
+if test x"$use_external_asm" = x"yes"; then
+ AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
+fi
+
+AC_MSG_NOTICE([Using static precomputation: $set_precomp])
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
AC_MSG_NOTICE([Using field implementation: $set_field])
AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
-
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
+AC_MSG_NOTICE([Using jni: $use_jni])
+
+if test x"$enable_experimental" = x"yes"; then
+ AC_MSG_NOTICE([******])
+ AC_MSG_NOTICE([WARNING: experimental build])
+ AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
+ AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
+ AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
+ AC_MSG_NOTICE([******])
+else
+ if test x"$enable_module_schnorr" = x"yes"; then
+ AC_MSG_ERROR([Schnorr signature module is experimental. Use --enable-experimental to allow.])
+ fi
+ if test x"$enable_module_ecdh" = x"yes"; then
+ AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
+ fi
+ if test x"$set_asm" = x"arm"; then
+ AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
+ fi
+fi
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
+AC_SUBST(JNI_INCLUDES)
AC_SUBST(SECP_INCLUDES)
AC_SUBST(SECP_LIBS)
AC_SUBST(SECP_TEST_LIBS)
@@ -367,6 +478,9 @@ AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_pr
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
+AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
+AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
+AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
dnl make sure nothing new is exported so that we don't break the cache
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
diff --git a/src/secp256k1/libsecp256k1.pc.in b/src/secp256k1/libsecp256k1.pc.in
index 1c72dd0003..a0d006f113 100644
--- a/src/secp256k1/libsecp256k1.pc.in
+++ b/src/secp256k1/libsecp256k1.pc.in
@@ -5,7 +5,7 @@ includedir=@includedir@
Name: libsecp256k1
Description: Optimized C library for EC operations on curve secp256k1
-URL: https://github.com/bitcoin/secp256k1
+URL: https://github.com/bitcoin-core/secp256k1
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
Libs.private: @SECP_LIBS@
diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage
new file mode 100644
index 0000000000..ab580c5b23
--- /dev/null
+++ b/src/secp256k1/sage/group_prover.sage
@@ -0,0 +1,322 @@
+# This code supports verifying group implementations which have branches
+# or conditional statements (like cmovs), by allowing each execution path
+# to independently set assumptions on input or intermediary variables.
+#
+# The general approach is:
+# * A constraint is a tuple of two sets of of symbolic expressions:
+# the first of which are required to evaluate to zero, the second of which
+# are required to evaluate to nonzero.
+# - A constraint is said to be conflicting if any of its nonzero expressions
+# is in the ideal with basis the zero expressions (in other words: when the
+# zero expressions imply that one of the nonzero expressions are zero).
+# * There is a list of laws that describe the intended behaviour, including
+# laws for addition and doubling. Each law is called with the symbolic point
+# coordinates as arguments, and returns:
+# - A constraint describing the assumptions under which it is applicable,
+# called "assumeLaw"
+# - A constraint describing the requirements of the law, called "require"
+# * Implementations are transliterated into functions that operate as well on
+# algebraic input points, and are called once per combination of branches
+# exectured. Each execution returns:
+# - A constraint describing the assumptions this implementation requires
+# (such as Z1=1), called "assumeFormula"
+# - A constraint describing the assumptions this specific branch requires,
+# but which is by construction guaranteed to cover the entire space by
+# merging the results from all branches, called "assumeBranch"
+# - The result of the computation
+# * All combinations of laws with implementation branches are tried, and:
+# - If the combination of assumeLaw, assumeFormula, and assumeBranch results
+# in a conflict, it means this law does not apply to this branch, and it is
+# skipped.
+# - For others, we try to prove the require constraints hold, assuming the
+# information in assumeLaw + assumeFormula + assumeBranch, and if this does
+# not succeed, we fail.
+# + To prove an expression is zero, we check whether it belongs to the
+# ideal with the assumed zero expressions as basis. This test is exact.
+# + To prove an expression is nonzero, we check whether each of its
+# factors is contained in the set of nonzero assumptions' factors.
+# This test is not exact, so various combinations of original and
+# reduced expressions' factors are tried.
+# - If we succeed, we print out the assumptions from assumeFormula that
+# weren't implied by assumeLaw already. Those from assumeBranch are skipped,
+# as we assume that all constraints in it are complementary with each other.
+#
+# Based on the sage verification scripts used in the Explicit-Formulas Database
+# by Tanja Lange and others, see http://hyperelliptic.org/EFD
+
+class fastfrac:
+ """Fractions over rings."""
+
+ def __init__(self,R,top,bot=1):
+ """Construct a fractional, given a ring, a numerator, and denominator."""
+ self.R = R
+ if parent(top) == ZZ or parent(top) == R:
+ self.top = R(top)
+ self.bot = R(bot)
+ elif top.__class__ == fastfrac:
+ self.top = top.top
+ self.bot = top.bot * bot
+ else:
+ self.top = R(numerator(top))
+ self.bot = R(denominator(top)) * bot
+
+ def iszero(self,I):
+ """Return whether this fraction is zero given an ideal."""
+ return self.top in I and self.bot not in I
+
+ def reduce(self,assumeZero):
+ zero = self.R.ideal(map(numerator, assumeZero))
+ return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))
+
+ def __add__(self,other):
+ """Add two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top + self.bot * other,self.bot)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot)
+ return NotImplemented
+
+ def __sub__(self,other):
+ """Subtract two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top - self.bot * other,self.bot)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot)
+ return NotImplemented
+
+ def __neg__(self):
+ """Return the negation of a fraction."""
+ return fastfrac(self.R,-self.top,self.bot)
+
+ def __mul__(self,other):
+ """Multiply two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top * other,self.bot)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.top,self.bot * other.bot)
+ return NotImplemented
+
+ def __rmul__(self,other):
+ """Multiply something else with a fraction."""
+ return self.__mul__(other)
+
+ def __div__(self,other):
+ """Divide two fractions."""
+ if parent(other) == ZZ:
+ return fastfrac(self.R,self.top,self.bot * other)
+ if other.__class__ == fastfrac:
+ return fastfrac(self.R,self.top * other.bot,self.bot * other.top)
+ return NotImplemented
+
+ def __pow__(self,other):
+ """Compute a power of a fraction."""
+ if parent(other) == ZZ:
+ if other < 0:
+ # Negative powers require flipping top and bottom
+ return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other))
+ else:
+ return fastfrac(self.R,self.top ^ other,self.bot ^ other)
+ return NotImplemented
+
+ def __str__(self):
+ return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))"
+ def __repr__(self):
+ return "%s" % self
+
+ def numerator(self):
+ return self.top
+
+class constraints:
+ """A set of constraints, consisting of zero and nonzero expressions.
+
+ Constraints can either be used to express knowledge or a requirement.
+
+ Both the fields zero and nonzero are maps from expressions to description
+ strings. The expressions that are the keys in zero are required to be zero,
+ and the expressions that are the keys in nonzero are required to be nonzero.
+
+ Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in
+ nonzero could be multiplied into a single key. This is often much less
+ efficient to work with though, so we keep them separate inside the
+ constraints. This allows higher-level code to do fast checks on the individual
+ nonzero elements, or combine them if needed for stronger checks.
+
+ We can't multiply the different zero elements, as it would suffice for one of
+ the factors to be zero, instead of all of them. Instead, the zero elements are
+ typically combined into an ideal first.
+ """
+
+ def __init__(self, **kwargs):
+ if 'zero' in kwargs:
+ self.zero = dict(kwargs['zero'])
+ else:
+ self.zero = dict()
+ if 'nonzero' in kwargs:
+ self.nonzero = dict(kwargs['nonzero'])
+ else:
+ self.nonzero = dict()
+
+ def negate(self):
+ return constraints(zero=self.nonzero, nonzero=self.zero)
+
+ def __add__(self, other):
+ zero = self.zero.copy()
+ zero.update(other.zero)
+ nonzero = self.nonzero.copy()
+ nonzero.update(other.nonzero)
+ return constraints(zero=zero, nonzero=nonzero)
+
+ def __str__(self):
+ return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero)
+
+ def __repr__(self):
+ return "%s" % self
+
+
+def conflicts(R, con):
+ """Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
+ zero = R.ideal(map(numerator, con.zero))
+ if 1 in zero:
+ return True
+ # First a cheap check whether any of the individual nonzero terms conflict on
+ # their own.
+ for nonzero in con.nonzero:
+ if nonzero.iszero(zero):
+ return True
+ # It can be the case that entries in the nonzero set do not individually
+ # conflict with the zero set, but their combination does. For example, knowing
+ # that either x or y is zero is equivalent to having x*y in the zero set.
+ # Having x or y individually in the nonzero set is not a conflict, but both
+ # simultaneously is, so that is the right thing to check for.
+ if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero):
+ return True
+ return False
+
+
+def get_nonzero_set(R, assume):
+ """Calculate a simple set of nonzero expressions"""
+ zero = R.ideal(map(numerator, assume.zero))
+ nonzero = set()
+ for nz in map(numerator, assume.nonzero):
+ for (f,n) in nz.factor():
+ nonzero.add(f)
+ rnz = zero.reduce(nz)
+ for (f,n) in rnz.factor():
+ nonzero.add(f)
+ return nonzero
+
+
+def prove_nonzero(R, exprs, assume):
+ """Check whether an expression is provably nonzero, given assumptions"""
+ zero = R.ideal(map(numerator, assume.zero))
+ nonzero = get_nonzero_set(R, assume)
+ expl = set()
+ ok = True
+ for expr in exprs:
+ if numerator(expr) in zero:
+ return (False, [exprs[expr]])
+ allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
+ for (f, n) in allexprs.factor():
+ if f not in nonzero:
+ ok = False
+ if ok:
+ return (True, None)
+ ok = True
+ for (f, n) in zero.reduce(numerator(allexprs)).factor():
+ if f not in nonzero:
+ ok = False
+ if ok:
+ return (True, None)
+ ok = True
+ for expr in exprs:
+ for (f,n) in numerator(expr).factor():
+ if f not in nonzero:
+ ok = False
+ if ok:
+ return (True, None)
+ ok = True
+ for expr in exprs:
+ for (f,n) in zero.reduce(numerator(expr)).factor():
+ if f not in nonzero:
+ expl.add(exprs[expr])
+ if expl:
+ return (False, list(expl))
+ else:
+ return (True, None)
+
+
+def prove_zero(R, exprs, assume):
+ """Check whether all of the passed expressions are provably zero, given assumptions"""
+ r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
+ if not r:
+ return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
+ zero = R.ideal(map(numerator, assume.zero))
+ nonzero = prod(x for x in assume.nonzero)
+ expl = []
+ for expr in exprs:
+ if not expr.iszero(zero):
+ expl.append(exprs[expr])
+ if not expl:
+ return (True, None)
+ return (False, expl)
+
+
+def describe_extra(R, assume, assumeExtra):
+ """Describe what assumptions are added, given existing assumptions"""
+ zerox = assume.zero.copy()
+ zerox.update(assumeExtra.zero)
+ zero = R.ideal(map(numerator, assume.zero))
+ zeroextra = R.ideal(map(numerator, zerox))
+ nonzero = get_nonzero_set(R, assume)
+ ret = set()
+ # Iterate over the extra zero expressions
+ for base in assumeExtra.zero:
+ if base not in zero:
+ add = []
+ for (f, n) in numerator(base).factor():
+ if f not in nonzero:
+ add += ["%s" % f]
+ if add:
+ ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
+ # Iterate over the extra nonzero expressions
+ for nz in assumeExtra.nonzero:
+ nzr = zeroextra.reduce(numerator(nz))
+ if nzr not in zeroextra:
+ for (f,n) in nzr.factor():
+ if zeroextra.reduce(f) not in nonzero:
+ ret.add("%s != 0" % zeroextra.reduce(f))
+ return ", ".join(x for x in ret)
+
+
+def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require):
+ """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions"""
+ assume = assumeLaw + assumeAssert + assumeBranch
+
+ if conflicts(R, assume):
+ # This formula does not apply
+ return None
+
+ describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)
+
+ ok, msg = prove_zero(R, require.zero, assume)
+ if not ok:
+ return "FAIL, %s fails (assuming %s)" % (str(msg), describe)
+
+ res, expl = prove_nonzero(R, require.nonzero, assume)
+ if not res:
+ return "FAIL, %s fails (assuming %s)" % (str(expl), describe)
+
+ if describe != "":
+ return "OK (assuming %s)" % describe
+ else:
+ return "OK"
+
+
+def concrete_verify(c):
+ for k in c.zero:
+ if k != 0:
+ return (False, c.zero[k])
+ for k in c.nonzero:
+ if k == 0:
+ return (False, c.nonzero[k])
+ return (True, None)
diff --git a/src/secp256k1/sage/secp256k1.sage b/src/secp256k1/sage/secp256k1.sage
new file mode 100644
index 0000000000..a97e732f7f
--- /dev/null
+++ b/src/secp256k1/sage/secp256k1.sage
@@ -0,0 +1,306 @@
+# Test libsecp256k1' group operation implementations using prover.sage
+
+import sys
+
+load("group_prover.sage")
+load("weierstrass_prover.sage")
+
+def formula_secp256k1_gej_double_var(a):
+ """libsecp256k1's secp256k1_gej_double_var, used by various addition functions"""
+ rz = a.Z * a.Y
+ rz = rz * 2
+ t1 = a.X^2
+ t1 = t1 * 3
+ t2 = t1^2
+ t3 = a.Y^2
+ t3 = t3 * 2
+ t4 = t3^2
+ t4 = t4 * 2
+ t3 = t3 * a.X
+ rx = t3
+ rx = rx * 4
+ rx = -rx
+ rx = rx + t2
+ t2 = -t2
+ t3 = t3 * 6
+ t3 = t3 + t2
+ ry = t1 * t3
+ t2 = -t4
+ ry = ry + t2
+ return jacobianpoint(rx, ry, rz)
+
+def formula_secp256k1_gej_add_var(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_var"""
+ if branch == 0:
+ return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
+ if branch == 1:
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
+ z22 = b.Z^2
+ z12 = a.Z^2
+ u1 = a.X * z22
+ u2 = b.X * z12
+ s1 = a.Y * z22
+ s1 = s1 * b.Z
+ s2 = b.Y * z12
+ s2 = s2 * a.Z
+ h = -u1
+ h = h + u2
+ i = -s1
+ i = i + s2
+ if branch == 2:
+ r = formula_secp256k1_gej_double_var(a)
+ return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r)
+ if branch == 3:
+ return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity())
+ i2 = i^2
+ h2 = h^2
+ h3 = h2 * h
+ h = h * b.Z
+ rz = a.Z * h
+ t = u1 * h2
+ rx = t
+ rx = rx * 2
+ rx = rx + h3
+ rx = -rx
+ rx = rx + i2
+ ry = -rx
+ ry = ry + t
+ ry = ry * i
+ h3 = h3 * s1
+ h3 = -h3
+ ry = ry + h3
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_ge_var(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1"""
+ if branch == 0:
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
+ if branch == 1:
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
+ z12 = a.Z^2
+ u1 = a.X
+ u2 = b.X * z12
+ s1 = a.Y
+ s2 = b.Y * z12
+ s2 = s2 * a.Z
+ h = -u1
+ h = h + u2
+ i = -s1
+ i = i + s2
+ if (branch == 2):
+ r = formula_secp256k1_gej_double_var(a)
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
+ if (branch == 3):
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
+ i2 = i^2
+ h2 = h^2
+ h3 = h * h2
+ rz = a.Z * h
+ t = u1 * h2
+ rx = t
+ rx = rx * 2
+ rx = rx + h3
+ rx = -rx
+ rx = rx + i2
+ ry = -rx
+ ry = ry + t
+ ry = ry * i
+ h3 = h3 * s1
+ h3 = -h3
+ ry = ry + h3
+ return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_zinv_var(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_zinv_var"""
+ bzinv = b.Z^(-1)
+ if branch == 0:
+ return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a)
+ if branch == 1:
+ bzinv2 = bzinv^2
+ bzinv3 = bzinv2 * bzinv
+ rx = b.X * bzinv2
+ ry = b.Y * bzinv3
+ rz = 1
+ return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz))
+ azz = a.Z * bzinv
+ z12 = azz^2
+ u1 = a.X
+ u2 = b.X * z12
+ s1 = a.Y
+ s2 = b.Y * z12
+ s2 = s2 * azz
+ h = -u1
+ h = h + u2
+ i = -s1
+ i = i + s2
+ if branch == 2:
+ r = formula_secp256k1_gej_double_var(a)
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
+ if branch == 3:
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
+ i2 = i^2
+ h2 = h^2
+ h3 = h * h2
+ rz = a.Z
+ rz = rz * h
+ t = u1 * h2
+ rx = t
+ rx = rx * 2
+ rx = rx + h3
+ rx = -rx
+ rx = rx + i2
+ ry = -rx
+ ry = ry + t
+ ry = ry * i
+ h3 = h3 * s1
+ h3 = -h3
+ ry = ry + h3
+ return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_ge(branch, a, b):
+ """libsecp256k1's secp256k1_gej_add_ge"""
+ zeroes = {}
+ nonzeroes = {}
+ a_infinity = False
+ if (branch & 4) != 0:
+ nonzeroes.update({a.Infinity : 'a_infinite'})
+ a_infinity = True
+ else:
+ zeroes.update({a.Infinity : 'a_finite'})
+ zz = a.Z^2
+ u1 = a.X
+ u2 = b.X * zz
+ s1 = a.Y
+ s2 = b.Y * zz
+ s2 = s2 * a.Z
+ t = u1
+ t = t + u2
+ m = s1
+ m = m + s2
+ rr = t^2
+ m_alt = -u2
+ tt = u1 * m_alt
+ rr = rr + tt
+ degenerate = (branch & 3) == 3
+ if (branch & 1) != 0:
+ zeroes.update({m : 'm_zero'})
+ else:
+ nonzeroes.update({m : 'm_nonzero'})
+ if (branch & 2) != 0:
+ zeroes.update({rr : 'rr_zero'})
+ else:
+ nonzeroes.update({rr : 'rr_nonzero'})
+ rr_alt = s1
+ rr_alt = rr_alt * 2
+ m_alt = m_alt + u1
+ if not degenerate:
+ rr_alt = rr
+ m_alt = m
+ n = m_alt^2
+ q = n * t
+ n = n^2
+ if degenerate:
+ n = m
+ t = rr_alt^2
+ rz = a.Z * m_alt
+ infinity = False
+ if (branch & 8) != 0:
+ if not a_infinity:
+ infinity = True
+ zeroes.update({rz : 'r.z=0'})
+ else:
+ nonzeroes.update({rz : 'r.z!=0'})
+ rz = rz * 2
+ q = -q
+ t = t + q
+ rx = t
+ t = t * 2
+ t = t + q
+ t = t * rr_alt
+ t = t + n
+ ry = -t
+ rx = rx * 4
+ ry = ry * 4
+ if a_infinity:
+ rx = b.X
+ ry = b.Y
+ rz = 1
+ if infinity:
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity())
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz))
+
+def formula_secp256k1_gej_add_ge_old(branch, a, b):
+ """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx"""
+ a_infinity = (branch & 1) != 0
+ zero = {}
+ nonzero = {}
+ if a_infinity:
+ nonzero.update({a.Infinity : 'a_infinite'})
+ else:
+ zero.update({a.Infinity : 'a_finite'})
+ zz = a.Z^2
+ u1 = a.X
+ u2 = b.X * zz
+ s1 = a.Y
+ s2 = b.Y * zz
+ s2 = s2 * a.Z
+ z = a.Z
+ t = u1
+ t = t + u2
+ m = s1
+ m = m + s2
+ n = m^2
+ q = n * t
+ n = n^2
+ rr = t^2
+ t = u1 * u2
+ t = -t
+ rr = rr + t
+ t = rr^2
+ rz = m * z
+ infinity = False
+ if (branch & 2) != 0:
+ if not a_infinity:
+ infinity = True
+ else:
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity())
+ zero.update({rz : 'r.z=0'})
+ else:
+ nonzero.update({rz : 'r.z!=0'})
+ rz = rz * (0 if a_infinity else 2)
+ rx = t
+ q = -q
+ rx = rx + q
+ q = q * 3
+ t = t * 2
+ t = t + q
+ t = t * rr
+ t = t + n
+ ry = -t
+ rx = rx * (0 if a_infinity else 4)
+ ry = ry * (0 if a_infinity else 4)
+ t = b.X
+ t = t * (1 if a_infinity else 0)
+ rx = rx + t
+ t = b.Y
+ t = t * (1 if a_infinity else 0)
+ ry = ry + t
+ t = (1 if a_infinity else 0)
+ rz = rz + t
+ if infinity:
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity())
+ return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz))
+
+if __name__ == "__main__":
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
+ check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)
+
+ if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive":
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
+ check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)
diff --git a/src/secp256k1/sage/weierstrass_prover.sage b/src/secp256k1/sage/weierstrass_prover.sage
new file mode 100644
index 0000000000..03ef2ec901
--- /dev/null
+++ b/src/secp256k1/sage/weierstrass_prover.sage
@@ -0,0 +1,264 @@
+# Prover implementation for Weierstrass curves of the form
+# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws
+# operating on affine and Jacobian coordinates, including the point at infinity
+# represented by a 4th variable in coordinates.
+
+load("group_prover.sage")
+
+
+class affinepoint:
+ def __init__(self, x, y, infinity=0):
+ self.x = x
+ self.y = y
+ self.infinity = infinity
+ def __str__(self):
+ return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity)
+
+
+class jacobianpoint:
+ def __init__(self, x, y, z, infinity=0):
+ self.X = x
+ self.Y = y
+ self.Z = z
+ self.Infinity = infinity
+ def __str__(self):
+ return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity)
+
+
+def point_at_infinity():
+ return jacobianpoint(1, 1, 1, 1)
+
+
+def negate(p):
+ if p.__class__ == affinepoint:
+ return affinepoint(p.x, -p.y)
+ if p.__class__ == jacobianpoint:
+ return jacobianpoint(p.X, -p.Y, p.Z)
+ assert(False)
+
+
+def on_weierstrass_curve(A, B, p):
+ """Return a set of zero-expressions for an affine point to be on the curve"""
+ return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'})
+
+
+def tangential_to_weierstrass_curve(A, B, p12, p3):
+ """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)"""
+ return constraints(zero={
+ (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve'
+ })
+
+
+def colinear(p1, p2, p3):
+ """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear"""
+ return constraints(zero={
+ (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1',
+ (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2',
+ (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3'
+ })
+
+
+def good_affine_point(p):
+ return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'})
+
+
+def good_jacobian_point(p):
+ return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'})
+
+
+def good_point(p):
+ return constraints(nonzero={p.Z^6 : 'nonzero_X'})
+
+
+def finite(p, *affine_fns):
+ con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'})
+ if p.Z != 0:
+ return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con)
+ else:
+ return con
+
+def infinite(p):
+ return constraints(nonzero={p.Infinity : 'infinite_point'})
+
+
+def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC):
+ """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions"""
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ on_weierstrass_curve(A, B, pb) +
+ finite(pA) +
+ finite(pB) +
+ constraints(nonzero={pa.x - pb.x : 'different_x'}))
+ require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
+ colinear(pa, pb, negate(pc))))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC):
+ """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions"""
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ on_weierstrass_curve(A, B, pb) +
+ finite(pA) +
+ finite(pB) +
+ constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'}))
+ require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
+ tangential_to_weierstrass_curve(A, B, pa, negate(pc))))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ on_weierstrass_curve(A, B, pb) +
+ finite(pA) +
+ finite(pB) +
+ constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'}))
+ require = infinite(pC)
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pb) +
+ infinite(pA) +
+ finite(pB))
+ require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'}))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ on_weierstrass_curve(A, B, pa) +
+ infinite(pB) +
+ finite(pA))
+ require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'}))
+ return (assumeLaw, require)
+
+
+def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC):
+ assumeLaw = (good_affine_point(pa) +
+ good_affine_point(pb) +
+ good_jacobian_point(pA) +
+ good_jacobian_point(pB) +
+ infinite(pA) +
+ infinite(pB))
+ require = infinite(pC)
+ return (assumeLaw, require)
+
+
+laws_jacobian_weierstrass = {
+ 'add': law_jacobian_weierstrass_add,
+ 'double': law_jacobian_weierstrass_double,
+ 'add_opposite': law_jacobian_weierstrass_add_opposites,
+ 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a,
+ 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b,
+ 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab
+}
+
+
+def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
+ """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
+ F = Integers(p)
+ print "Formula %s on Z%i:" % (name, p)
+ points = []
+ for x in xrange(0, p):
+ for y in xrange(0, p):
+ point = affinepoint(F(x), F(y))
+ r, e = concrete_verify(on_weierstrass_curve(A, B, point))
+ if r:
+ points.append(point)
+
+ for za in xrange(1, p):
+ for zb in xrange(1, p):
+ for pa in points:
+ for pb in points:
+ for ia in xrange(2):
+ for ib in xrange(2):
+ pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
+ pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
+ for branch in xrange(0, branches):
+ assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
+ pC.X = F(pC.X)
+ pC.Y = F(pC.Y)
+ pC.Z = F(pC.Z)
+ pC.Infinity = F(pC.Infinity)
+ r, e = concrete_verify(assumeAssert + assumeBranch)
+ if r:
+ match = False
+ for key in laws_jacobian_weierstrass:
+ assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC)
+ r, e = concrete_verify(assumeLaw)
+ if r:
+ if match:
+ print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)
+ else:
+ match = True
+ r, e = concrete_verify(require)
+ if not r:
+ print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)
+ print
+
+
+def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
+ assumeLaw, require = f(A, B, pa, pb, pA, pB, pC)
+ return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require)
+
+def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
+ """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically"""
+ R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
+ lift = lambda x: fastfrac(R,x)
+ ax = lift(ax)
+ ay = lift(ay)
+ Az = lift(Az)
+ bx = lift(bx)
+ by = lift(by)
+ Bz = lift(Bz)
+ Ai = lift(Ai)
+ Bi = lift(Bi)
+
+ pa = affinepoint(ax, ay, Ai)
+ pb = affinepoint(bx, by, Bi)
+ pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai)
+ pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi)
+
+ res = {}
+
+ for key in laws_jacobian_weierstrass:
+ res[key] = []
+
+ print ("Formula " + name + ":")
+ count = 0
+ for branch in xrange(branches):
+ assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
+ pC.X = lift(pC.X)
+ pC.Y = lift(pC.Y)
+ pC.Z = lift(pC.Z)
+ pC.Infinity = lift(pC.Infinity)
+
+ for key in laws_jacobian_weierstrass:
+ res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
+
+ for key in res:
+ print " %s:" % key
+ val = res[key]
+ for x in val:
+ if x[0] is not None:
+ print " branch %i: %s" % (x[1], x[0])
+
+ print
diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s
new file mode 100644
index 0000000000..5df561f2fc
--- /dev/null
+++ b/src/secp256k1/src/asm/field_10x26_arm.s
@@ -0,0 +1,919 @@
+@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm:
+/**********************************************************************
+ * Copyright (c) 2014 Wladimir J. van der Laan *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+/*
+ARM implementation of field_10x26 inner loops.
+
+Note:
+
+- To avoid unnecessary loads and make use of available registers, two
+ 'passes' have every time been interleaved, with the odd passes accumulating c' and d'
+ which will be added to c and d respectively in the the even passes
+
+*/
+
+ .syntax unified
+ .arch armv7-a
+ @ eabi attributes - see readelf -A
+ .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes
+ .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no
+ .eabi_attribute 10, 0 @ Tag_FP_arch = none
+ .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
+ .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
+ .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed
+ .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
+ .text
+
+ @ Field constants
+ .set field_R0, 0x3d10
+ .set field_R1, 0x400
+ .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff
+
+ .align 2
+ .global secp256k1_fe_mul_inner
+ .type secp256k1_fe_mul_inner, %function
+ @ Arguments:
+ @ r0 r Restrict: can overlap with a, not with b
+ @ r1 a
+ @ r2 b
+ @ Stack (total 4+10*4 = 44)
+ @ sp + #0 saved 'r' pointer
+ @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
+secp256k1_fe_mul_inner:
+ stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
+ sub sp, sp, #48 @ frame=44 + alignment
+ str r0, [sp, #0] @ save result address, we need it only at the end
+
+ /******************************************
+ * Main computation code.
+ ******************************************
+
+ Allocation:
+ r0,r14,r7,r8 scratch
+ r1 a (pointer)
+ r2 b (pointer)
+ r3:r4 c
+ r5:r6 d
+ r11:r12 c'
+ r9:r10 d'
+
+ Note: do not write to r[] here, it may overlap with a[]
+ */
+
+ /* A - interleaved with B */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #9*4] @ b[9]
+ ldr r0, [r1, #1*4] @ a[1]
+ umull r5, r6, r7, r8 @ d = a[0] * b[9]
+ ldr r14, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r8 @ d' = a[1] * b[9]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r5, r6, r0, r14 @ d += a[1] * b[8]
+ ldr r8, [r2, #7*4] @ b[7]
+ umlal r9, r10, r7, r14 @ d' += a[2] * b[8]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r5, r6, r7, r8 @ d += a[2] * b[7]
+ ldr r14, [r2, #6*4] @ b[6]
+ umlal r9, r10, r0, r8 @ d' += a[3] * b[7]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r5, r6, r0, r14 @ d += a[3] * b[6]
+ ldr r8, [r2, #5*4] @ b[5]
+ umlal r9, r10, r7, r14 @ d' += a[4] * b[6]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r5, r6, r7, r8 @ d += a[4] * b[5]
+ ldr r14, [r2, #4*4] @ b[4]
+ umlal r9, r10, r0, r8 @ d' += a[5] * b[5]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r5, r6, r0, r14 @ d += a[5] * b[4]
+ ldr r8, [r2, #3*4] @ b[3]
+ umlal r9, r10, r7, r14 @ d' += a[6] * b[4]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r5, r6, r7, r8 @ d += a[6] * b[3]
+ ldr r14, [r2, #2*4] @ b[2]
+ umlal r9, r10, r0, r8 @ d' += a[7] * b[3]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r14 @ d += a[7] * b[2]
+ ldr r8, [r2, #1*4] @ b[1]
+ umlal r9, r10, r7, r14 @ d' += a[8] * b[2]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r8 @ d += a[8] * b[1]
+ ldr r14, [r2, #0*4] @ b[0]
+ umlal r9, r10, r0, r8 @ d' += a[9] * b[1]
+ ldr r7, [r1, #0*4] @ a[0]
+ umlal r5, r6, r0, r14 @ d += a[9] * b[0]
+ @ r7,r14 used in B
+
+ bic r0, r5, field_not_M @ t9 = d & M
+ str r0, [sp, #4 + 4*9]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ /* B */
+ umull r3, r4, r7, r14 @ c = a[0] * b[0]
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u0 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u0 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t0 = c & M
+ str r14, [sp, #4 + 0*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u0 * R1
+ umlal r3, r4, r0, r14
+
+ /* C - interleaved with D */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #2*4] @ b[2]
+ ldr r14, [r2, #1*4] @ b[1]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[2]
+ ldr r0, [r1, #1*4] @ a[1]
+ umlal r3, r4, r7, r14 @ c += a[0] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r11, r12, r0, r14 @ c' += a[1] * b[1]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r8 @ c += a[1] * b[0]
+ ldr r14, [r2, #9*4] @ b[9]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[0]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r5, r6, r7, r14 @ d += a[2] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r14 @ d' = a[3] * b[9]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r5, r6, r0, r8 @ d += a[3] * b[8]
+ ldr r14, [r2, #7*4] @ b[7]
+ umlal r9, r10, r7, r8 @ d' += a[4] * b[8]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r5, r6, r7, r14 @ d += a[4] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r9, r10, r0, r14 @ d' += a[5] * b[7]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r5, r6, r0, r8 @ d += a[5] * b[6]
+ ldr r14, [r2, #5*4] @ b[5]
+ umlal r9, r10, r7, r8 @ d' += a[6] * b[6]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r5, r6, r7, r14 @ d += a[6] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r9, r10, r0, r14 @ d' += a[7] * b[5]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r8 @ d += a[7] * b[4]
+ ldr r14, [r2, #3*4] @ b[3]
+ umlal r9, r10, r7, r8 @ d' += a[8] * b[4]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r14 @ d += a[8] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r9, r10, r0, r14 @ d' += a[9] * b[3]
+ umlal r5, r6, r0, r8 @ d += a[9] * b[2]
+
+ bic r0, r5, field_not_M @ u1 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u1 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t1 = c & M
+ str r14, [sp, #4 + 1*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u1 * R1
+ umlal r3, r4, r0, r14
+
+ /* D */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u2 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u2 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t2 = c & M
+ str r14, [sp, #4 + 2*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u2 * R1
+ umlal r3, r4, r0, r14
+
+ /* E - interleaved with F */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #4*4] @ b[4]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[4]
+ ldr r8, [r2, #3*4] @ b[3]
+ umlal r3, r4, r7, r8 @ c += a[0] * b[3]
+ ldr r7, [r1, #1*4] @ a[1]
+ umlal r11, r12, r7, r8 @ c' += a[1] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r3, r4, r7, r8 @ c += a[1] * b[2]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[2]
+ ldr r8, [r2, #1*4] @ b[1]
+ umlal r3, r4, r7, r8 @ c += a[2] * b[1]
+ ldr r7, [r1, #3*4] @ a[3]
+ umlal r11, r12, r7, r8 @ c' += a[3] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r3, r4, r7, r8 @ c += a[3] * b[0]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r11, r12, r7, r8 @ c' += a[4] * b[0]
+ ldr r8, [r2, #9*4] @ b[9]
+ umlal r5, r6, r7, r8 @ d += a[4] * b[9]
+ ldr r7, [r1, #5*4] @ a[5]
+ umull r9, r10, r7, r8 @ d' = a[5] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umlal r5, r6, r7, r8 @ d += a[5] * b[8]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r9, r10, r7, r8 @ d' += a[6] * b[8]
+ ldr r8, [r2, #7*4] @ b[7]
+ umlal r5, r6, r7, r8 @ d += a[6] * b[7]
+ ldr r7, [r1, #7*4] @ a[7]
+ umlal r9, r10, r7, r8 @ d' += a[7] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r5, r6, r7, r8 @ d += a[7] * b[6]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r9, r10, r7, r8 @ d' += a[8] * b[6]
+ ldr r8, [r2, #5*4] @ b[5]
+ umlal r5, r6, r7, r8 @ d += a[8] * b[5]
+ ldr r7, [r1, #9*4] @ a[9]
+ umlal r9, r10, r7, r8 @ d' += a[9] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r5, r6, r7, r8 @ d += a[9] * b[4]
+
+ bic r0, r5, field_not_M @ u3 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u3 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t3 = c & M
+ str r14, [sp, #4 + 3*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u3 * R1
+ umlal r3, r4, r0, r14
+
+ /* F */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u4 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u4 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t4 = c & M
+ str r14, [sp, #4 + 4*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u4 * R1
+ umlal r3, r4, r0, r14
+
+ /* G - interleaved with H */
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r8, [r2, #6*4] @ b[6]
+ ldr r14, [r2, #5*4] @ b[5]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[6]
+ ldr r0, [r1, #1*4] @ a[1]
+ umlal r3, r4, r7, r14 @ c += a[0] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r11, r12, r0, r14 @ c' += a[1] * b[5]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r8 @ c += a[1] * b[4]
+ ldr r14, [r2, #3*4] @ b[3]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[4]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r3, r4, r7, r14 @ c += a[2] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r11, r12, r0, r14 @ c' += a[3] * b[3]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r3, r4, r0, r8 @ c += a[3] * b[2]
+ ldr r14, [r2, #1*4] @ b[1]
+ umlal r11, r12, r7, r8 @ c' += a[4] * b[2]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r3, r4, r7, r14 @ c += a[4] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r11, r12, r0, r14 @ c' += a[5] * b[1]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r3, r4, r0, r8 @ c += a[5] * b[0]
+ ldr r14, [r2, #9*4] @ b[9]
+ umlal r11, r12, r7, r8 @ c' += a[6] * b[0]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r5, r6, r7, r14 @ d += a[6] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r14 @ d' = a[7] * b[9]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r8 @ d += a[7] * b[8]
+ ldr r14, [r2, #7*4] @ b[7]
+ umlal r9, r10, r7, r8 @ d' += a[8] * b[8]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r14 @ d += a[8] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r9, r10, r0, r14 @ d' += a[9] * b[7]
+ umlal r5, r6, r0, r8 @ d += a[9] * b[6]
+
+ bic r0, r5, field_not_M @ u5 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u5 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t5 = c & M
+ str r14, [sp, #4 + 5*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u5 * R1
+ umlal r3, r4, r0, r14
+
+ /* H */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u6 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u6 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t6 = c & M
+ str r14, [sp, #4 + 6*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u6 * R1
+ umlal r3, r4, r0, r14
+
+ /* I - interleaved with J */
+ ldr r8, [r2, #8*4] @ b[8]
+ ldr r7, [r1, #0*4] @ a[0]
+ ldr r14, [r2, #7*4] @ b[7]
+ umull r11, r12, r7, r8 @ c' = a[0] * b[8]
+ ldr r0, [r1, #1*4] @ a[1]
+ umlal r3, r4, r7, r14 @ c += a[0] * b[7]
+ ldr r8, [r2, #6*4] @ b[6]
+ umlal r11, r12, r0, r14 @ c' += a[1] * b[7]
+ ldr r7, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r8 @ c += a[1] * b[6]
+ ldr r14, [r2, #5*4] @ b[5]
+ umlal r11, r12, r7, r8 @ c' += a[2] * b[6]
+ ldr r0, [r1, #3*4] @ a[3]
+ umlal r3, r4, r7, r14 @ c += a[2] * b[5]
+ ldr r8, [r2, #4*4] @ b[4]
+ umlal r11, r12, r0, r14 @ c' += a[3] * b[5]
+ ldr r7, [r1, #4*4] @ a[4]
+ umlal r3, r4, r0, r8 @ c += a[3] * b[4]
+ ldr r14, [r2, #3*4] @ b[3]
+ umlal r11, r12, r7, r8 @ c' += a[4] * b[4]
+ ldr r0, [r1, #5*4] @ a[5]
+ umlal r3, r4, r7, r14 @ c += a[4] * b[3]
+ ldr r8, [r2, #2*4] @ b[2]
+ umlal r11, r12, r0, r14 @ c' += a[5] * b[3]
+ ldr r7, [r1, #6*4] @ a[6]
+ umlal r3, r4, r0, r8 @ c += a[5] * b[2]
+ ldr r14, [r2, #1*4] @ b[1]
+ umlal r11, r12, r7, r8 @ c' += a[6] * b[2]
+ ldr r0, [r1, #7*4] @ a[7]
+ umlal r3, r4, r7, r14 @ c += a[6] * b[1]
+ ldr r8, [r2, #0*4] @ b[0]
+ umlal r11, r12, r0, r14 @ c' += a[7] * b[1]
+ ldr r7, [r1, #8*4] @ a[8]
+ umlal r3, r4, r0, r8 @ c += a[7] * b[0]
+ ldr r14, [r2, #9*4] @ b[9]
+ umlal r11, r12, r7, r8 @ c' += a[8] * b[0]
+ ldr r0, [r1, #9*4] @ a[9]
+ umlal r5, r6, r7, r14 @ d += a[8] * b[9]
+ ldr r8, [r2, #8*4] @ b[8]
+ umull r9, r10, r0, r14 @ d' = a[9] * b[9]
+ umlal r5, r6, r0, r8 @ d += a[9] * b[8]
+
+ bic r0, r5, field_not_M @ u7 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u7 * R0
+ umlal r3, r4, r0, r14
+
+ bic r14, r3, field_not_M @ t7 = c & M
+ str r14, [sp, #4 + 7*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u7 * R1
+ umlal r3, r4, r0, r14
+
+ /* J */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u8 = d & M
+ str r0, [sp, #4 + 8*4]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u8 * R0
+ umlal r3, r4, r0, r14
+
+ /******************************************
+ * compute and write back result
+ ******************************************
+ Allocation:
+ r0 r
+ r3:r4 c
+ r5:r6 d
+ r7 t0
+ r8 t1
+ r9 t2
+ r11 u8
+ r12 t9
+ r1,r2,r10,r14 scratch
+
+ Note: do not read from a[] after here, it may overlap with r[]
+ */
+ ldr r0, [sp, #0]
+ add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
+ ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
+ add r1, r0, #3*4
+ stmia r1, {r2,r7,r8,r9,r10}
+
+ bic r2, r3, field_not_M @ r[8] = c & M
+ str r2, [r0, #8*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u8 * R1
+ umlal r3, r4, r11, r14
+ movw r14, field_R0 @ c += d * R0
+ umlal r3, r4, r5, r14
+ adds r3, r3, r12 @ c += t9
+ adc r4, r4, #0
+
+ add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
+ ldmia r1, {r7,r8,r9}
+
+ ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
+ str r2, [r0, #9*4]
+ mov r3, r3, lsr #22 @ c >>= 22
+ orr r3, r3, r4, asl #10
+ mov r4, r4, lsr #22
+ movw r14, field_R1 << 4 @ c += d * (R1 << 4)
+ umlal r3, r4, r5, r14
+
+ movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
+ umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
+ adds r5, r5, r7 @ d.lo += t0
+ mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
+ adc r6, r6, 0 @ d.hi += carry
+
+ bic r2, r5, field_not_M @ r[0] = d & M
+ str r2, [r0, #0*4]
+
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
+ umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
+ adds r5, r5, r8 @ d.lo += t1
+ adc r6, r6, #0 @ d.hi += carry
+ adds r5, r5, r1 @ d.lo += tmp.lo
+ mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
+ adc r6, r6, r2 @ d.hi += carry + tmp.hi
+
+ bic r2, r5, field_not_M @ r[1] = d & M
+ str r2, [r0, #1*4]
+ mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
+ orr r5, r5, r6, asl #6
+
+ add r5, r5, r9 @ d += t2
+ str r5, [r0, #2*4] @ r[2] = d
+
+ add sp, sp, #48
+ ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
+ .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner
+
+ .align 2
+ .global secp256k1_fe_sqr_inner
+ .type secp256k1_fe_sqr_inner, %function
+ @ Arguments:
+ @ r0 r Can overlap with a
+ @ r1 a
+ @ Stack (total 4+10*4 = 44)
+ @ sp + #0 saved 'r' pointer
+ @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
+secp256k1_fe_sqr_inner:
+ stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
+ sub sp, sp, #48 @ frame=44 + alignment
+ str r0, [sp, #0] @ save result address, we need it only at the end
+ /******************************************
+ * Main computation code.
+ ******************************************
+
+ Allocation:
+ r0,r14,r2,r7,r8 scratch
+ r1 a (pointer)
+ r3:r4 c
+ r5:r6 d
+ r11:r12 c'
+ r9:r10 d'
+
+ Note: do not write to r[] here, it may overlap with a[]
+ */
+ /* A interleaved with B */
+ ldr r0, [r1, #1*4] @ a[1]*2
+ ldr r7, [r1, #0*4] @ a[0]
+ mov r0, r0, asl #1
+ ldr r14, [r1, #9*4] @ a[9]
+ umull r3, r4, r7, r7 @ c = a[0] * a[0]
+ ldr r8, [r1, #8*4] @ a[8]
+ mov r7, r7, asl #1
+ umull r5, r6, r7, r14 @ d = a[0]*2 * a[9]
+ ldr r7, [r1, #2*4] @ a[2]*2
+ umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9]
+ ldr r14, [r1, #7*4] @ a[7]
+ umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8]
+ mov r7, r7, asl #1
+ ldr r0, [r1, #3*4] @ a[3]*2
+ umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8]
+ ldr r8, [r1, #6*4] @ a[6]
+ umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7]
+ mov r0, r0, asl #1
+ ldr r7, [r1, #4*4] @ a[4]*2
+ umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7]
+ ldr r14, [r1, #5*4] @ a[5]
+ mov r7, r7, asl #1
+ umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6]
+ umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6]
+ umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5]
+ umlal r9, r10, r14, r14 @ d' += a[5] * a[5]
+
+ bic r0, r5, field_not_M @ t9 = d & M
+ str r0, [sp, #4 + 9*4]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ /* B */
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u0 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u0 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t0 = c & M
+ str r14, [sp, #4 + 0*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u0 * R1
+ umlal r3, r4, r0, r14
+
+ /* C interleaved with D */
+ ldr r0, [r1, #0*4] @ a[0]*2
+ ldr r14, [r1, #1*4] @ a[1]
+ mov r0, r0, asl #1
+ ldr r8, [r1, #2*4] @ a[2]
+ umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1]
+ mov r7, r8, asl #1 @ a[2]*2
+ umull r11, r12, r14, r14 @ c' = a[1] * a[1]
+ ldr r14, [r1, #9*4] @ a[9]
+ umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2]
+ ldr r0, [r1, #3*4] @ a[3]*2
+ ldr r8, [r1, #8*4] @ a[8]
+ umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9]
+ mov r0, r0, asl #1
+ ldr r7, [r1, #4*4] @ a[4]*2
+ umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9]
+ ldr r14, [r1, #7*4] @ a[7]
+ umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8]
+ mov r7, r7, asl #1
+ ldr r0, [r1, #5*4] @ a[5]*2
+ umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8]
+ ldr r8, [r1, #6*4] @ a[6]
+ mov r0, r0, asl #1
+ umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7]
+ umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7]
+ umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6]
+ umlal r9, r10, r8, r8 @ d' += a[6] * a[6]
+
+ bic r0, r5, field_not_M @ u1 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u1 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t1 = c & M
+ str r14, [sp, #4 + 1*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u1 * R1
+ umlal r3, r4, r0, r14
+
+ /* D */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u2 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u2 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t2 = c & M
+ str r14, [sp, #4 + 2*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u2 * R1
+ umlal r3, r4, r0, r14
+
+ /* E interleaved with F */
+ ldr r7, [r1, #0*4] @ a[0]*2
+ ldr r0, [r1, #1*4] @ a[1]*2
+ ldr r14, [r1, #2*4] @ a[2]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #3*4] @ a[3]
+ ldr r2, [r1, #4*4]
+ umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3]
+ mov r0, r0, asl #1
+ umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4]
+ mov r2, r2, asl #1 @ a[4]*2
+ umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3]
+ ldr r8, [r1, #9*4] @ a[9]
+ umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2]
+ ldr r0, [r1, #5*4] @ a[5]*2
+ umlal r11, r12, r14, r14 @ c' += a[2] * a[2]
+ ldr r14, [r1, #8*4] @ a[8]
+ mov r0, r0, asl #1
+ umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9]
+ ldr r7, [r1, #6*4] @ a[6]*2
+ umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #7*4] @ a[7]
+ umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8]
+ umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8]
+ umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7]
+ umlal r9, r10, r8, r8 @ d' += a[7] * a[7]
+
+ bic r0, r5, field_not_M @ u3 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u3 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t3 = c & M
+ str r14, [sp, #4 + 3*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u3 * R1
+ umlal r3, r4, r0, r14
+
+ /* F */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u4 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u4 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t4 = c & M
+ str r14, [sp, #4 + 4*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u4 * R1
+ umlal r3, r4, r0, r14
+
+ /* G interleaved with H */
+ ldr r7, [r1, #0*4] @ a[0]*2
+ ldr r0, [r1, #1*4] @ a[1]*2
+ mov r7, r7, asl #1
+ ldr r8, [r1, #5*4] @ a[5]
+ ldr r2, [r1, #6*4] @ a[6]
+ umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5]
+ ldr r14, [r1, #4*4] @ a[4]
+ mov r0, r0, asl #1
+ umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6]
+ ldr r7, [r1, #2*4] @ a[2]*2
+ umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #3*4] @ a[3]
+ umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4]
+ mov r0, r2, asl #1 @ a[6]*2
+ umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4]
+ ldr r14, [r1, #9*4] @ a[9]
+ umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3]
+ ldr r7, [r1, #7*4] @ a[7]*2
+ umlal r11, r12, r8, r8 @ c' += a[3] * a[3]
+ mov r7, r7, asl #1
+ ldr r8, [r1, #8*4] @ a[8]
+ umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9]
+ umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9]
+ umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8]
+ umlal r9, r10, r8, r8 @ d' += a[8] * a[8]
+
+ bic r0, r5, field_not_M @ u5 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u5 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t5 = c & M
+ str r14, [sp, #4 + 5*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u5 * R1
+ umlal r3, r4, r0, r14
+
+ /* H */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ adds r5, r5, r9 @ d += d'
+ adc r6, r6, r10
+
+ bic r0, r5, field_not_M @ u6 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u6 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t6 = c & M
+ str r14, [sp, #4 + 6*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u6 * R1
+ umlal r3, r4, r0, r14
+
+ /* I interleaved with J */
+ ldr r7, [r1, #0*4] @ a[0]*2
+ ldr r0, [r1, #1*4] @ a[1]*2
+ mov r7, r7, asl #1
+ ldr r8, [r1, #7*4] @ a[7]
+ ldr r2, [r1, #8*4] @ a[8]
+ umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7]
+ ldr r14, [r1, #6*4] @ a[6]
+ mov r0, r0, asl #1
+ umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8]
+ ldr r7, [r1, #2*4] @ a[2]*2
+ umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7]
+ ldr r8, [r1, #5*4] @ a[5]
+ umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6]
+ ldr r0, [r1, #3*4] @ a[3]*2
+ mov r7, r7, asl #1
+ umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6]
+ ldr r14, [r1, #4*4] @ a[4]
+ mov r0, r0, asl #1
+ umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5]
+ mov r2, r2, asl #1 @ a[8]*2
+ umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5]
+ umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4]
+ umlal r11, r12, r14, r14 @ c' += a[4] * a[4]
+ ldr r8, [r1, #9*4] @ a[9]
+ umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9]
+ @ r8 will be used in J
+
+ bic r0, r5, field_not_M @ u7 = d & M
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u7 * R0
+ umlal r3, r4, r0, r14
+ bic r14, r3, field_not_M @ t7 = c & M
+ str r14, [sp, #4 + 7*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u7 * R1
+ umlal r3, r4, r0, r14
+
+ /* J */
+ adds r3, r3, r11 @ c += c'
+ adc r4, r4, r12
+ umlal r5, r6, r8, r8 @ d += a[9] * a[9]
+
+ bic r0, r5, field_not_M @ u8 = d & M
+ str r0, [sp, #4 + 8*4]
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+ movw r14, field_R0 @ c += u8 * R0
+ umlal r3, r4, r0, r14
+
+ /******************************************
+ * compute and write back result
+ ******************************************
+ Allocation:
+ r0 r
+ r3:r4 c
+ r5:r6 d
+ r7 t0
+ r8 t1
+ r9 t2
+ r11 u8
+ r12 t9
+ r1,r2,r10,r14 scratch
+
+ Note: do not read from a[] after here, it may overlap with r[]
+ */
+ ldr r0, [sp, #0]
+ add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
+ ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
+ add r1, r0, #3*4
+ stmia r1, {r2,r7,r8,r9,r10}
+
+ bic r2, r3, field_not_M @ r[8] = c & M
+ str r2, [r0, #8*4]
+ mov r3, r3, lsr #26 @ c >>= 26
+ orr r3, r3, r4, asl #6
+ mov r4, r4, lsr #26
+ mov r14, field_R1 @ c += u8 * R1
+ umlal r3, r4, r11, r14
+ movw r14, field_R0 @ c += d * R0
+ umlal r3, r4, r5, r14
+ adds r3, r3, r12 @ c += t9
+ adc r4, r4, #0
+
+ add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
+ ldmia r1, {r7,r8,r9}
+
+ ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
+ str r2, [r0, #9*4]
+ mov r3, r3, lsr #22 @ c >>= 22
+ orr r3, r3, r4, asl #10
+ mov r4, r4, lsr #22
+ movw r14, field_R1 << 4 @ c += d * (R1 << 4)
+ umlal r3, r4, r5, r14
+
+ movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
+ umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
+ adds r5, r5, r7 @ d.lo += t0
+ mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
+ adc r6, r6, 0 @ d.hi += carry
+
+ bic r2, r5, field_not_M @ r[0] = d & M
+ str r2, [r0, #0*4]
+
+ mov r5, r5, lsr #26 @ d >>= 26
+ orr r5, r5, r6, asl #6
+ mov r6, r6, lsr #26
+
+ movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
+ umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
+ adds r5, r5, r8 @ d.lo += t1
+ adc r6, r6, #0 @ d.hi += carry
+ adds r5, r5, r1 @ d.lo += tmp.lo
+ mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
+ adc r6, r6, r2 @ d.hi += carry + tmp.hi
+
+ bic r2, r5, field_not_M @ r[1] = d & M
+ str r2, [r0, #1*4]
+ mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
+ orr r5, r5, r6, asl #6
+
+ add r5, r5, r9 @ d += t2
+ str r5, [r0, #2*4] @ r[2] = d
+
+ add sp, sp, #48
+ ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
+ .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner
+
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index 5a7c6376e0..cde5e2dbb4 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -28,7 +28,8 @@ static void bench_ecdh_setup(void* arg) {
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
};
- data->ctx = secp256k1_context_create(0);
+ /* create a context with no capabilities */
+ data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
for (i = 0; i < 32; i++) {
data->scalar[i] = i + 1;
}
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 7809f5f8cf..0809f77bda 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -181,12 +181,12 @@ void bench_field_inverse_var(void* arg) {
}
}
-void bench_field_sqrt_var(void* arg) {
+void bench_field_sqrt(void* arg) {
int i;
bench_inv_t *data = (bench_inv_t*)arg;
for (i = 0; i < 20000; i++) {
- secp256k1_fe_sqrt_var(&data->fe_x, &data->fe_x);
+ secp256k1_fe_sqrt(&data->fe_x, &data->fe_x);
secp256k1_fe_add(&data->fe_x, &data->fe_y);
}
}
@@ -227,6 +227,15 @@ void bench_group_add_affine_var(void* arg) {
}
}
+void bench_group_jacobi_var(void* arg) {
+ int i;
+ bench_inv_t *data = (bench_inv_t*)arg;
+
+ for (i = 0; i < 20000; i++) {
+ secp256k1_gej_has_quad_y_var(&data->gej_x);
+ }
+}
+
void bench_ecmult_wnaf(void* arg) {
int i;
bench_inv_t *data = (bench_inv_t*)arg;
@@ -299,6 +308,21 @@ void bench_context_sign(void* arg) {
}
}
+#ifndef USE_NUM_NONE
+void bench_num_jacobi(void* arg) {
+ int i;
+ bench_inv_t *data = (bench_inv_t*)arg;
+ secp256k1_num nx, norder;
+
+ secp256k1_scalar_get_num(&nx, &data->scalar_x);
+ secp256k1_scalar_order_get_num(&norder);
+ secp256k1_scalar_get_num(&norder, &data->scalar_y);
+
+ for (i = 0; i < 200000; i++) {
+ secp256k1_num_jacobi(&nx, &norder);
+ }
+}
+#endif
int have_flag(int argc, char** argv, char *flag) {
char** argm = argv + argc;
@@ -333,12 +357,13 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000);
- if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt_var", bench_field_sqrt_var, bench_setup, NULL, &data, 10, 20000);
+ if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000);
@@ -350,5 +375,8 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20);
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200);
+#ifndef USE_NUM_NONE
+ if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000);
+#endif
return 0;
}
diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c
index 5718320cda..418defa0aa 100644
--- a/src/secp256k1/src/bench_verify.c
+++ b/src/secp256k1/src/bench_verify.c
@@ -11,6 +11,12 @@
#include "util.h"
#include "bench.h"
+#ifdef ENABLE_OPENSSL_TESTS
+#include <openssl/bn.h>
+#include <openssl/ecdsa.h>
+#include <openssl/obj_mac.h>
+#endif
+
typedef struct {
secp256k1_context *ctx;
unsigned char msg[32];
@@ -19,6 +25,9 @@ typedef struct {
size_t siglen;
unsigned char pubkey[33];
size_t pubkeylen;
+#ifdef ENABLE_OPENSSL_TESTS
+ EC_GROUP* ec_group;
+#endif
} benchmark_verify_t;
static void benchmark_verify(void* arg) {
@@ -40,6 +49,36 @@ static void benchmark_verify(void* arg) {
}
}
+#ifdef ENABLE_OPENSSL_TESTS
+static void benchmark_verify_openssl(void* arg) {
+ int i;
+ benchmark_verify_t* data = (benchmark_verify_t*)arg;
+
+ for (i = 0; i < 20000; i++) {
+ data->sig[data->siglen - 1] ^= (i & 0xFF);
+ data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
+ data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
+ {
+ EC_KEY *pkey = EC_KEY_new();
+ const unsigned char *pubkey = &data->pubkey[0];
+ int result;
+
+ CHECK(pkey != NULL);
+ result = EC_KEY_set_group(pkey, data->ec_group);
+ CHECK(result);
+ result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL;
+ CHECK(result);
+ result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0);
+ CHECK(result);
+ EC_KEY_free(pkey);
+ }
+ data->sig[data->siglen - 1] ^= (i & 0xFF);
+ data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
+ data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
+ }
+}
+#endif
+
int main(void) {
int i;
secp256k1_pubkey pubkey;
@@ -62,6 +101,11 @@ int main(void) {
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000);
+#ifdef ENABLE_OPENSSL_TESTS
+ data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
+ run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000);
+ EC_GROUP_free(data.ec_group);
+#endif
secp256k1_context_destroy(data.ctx);
return 0;
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 90ac94770e..7a6a25318c 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -58,22 +58,24 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
int global_sign;
int skew = 0;
int word = 0;
+
/* 1 2 3 */
int u_last;
int u;
-#ifdef USE_ENDOMORPHISM
int flip;
int bit;
secp256k1_scalar neg_s;
int not_neg_one;
- /* If we are using the endomorphism, we cannot handle even numbers by negating
- * them, since we are working with 128-bit numbers whose negations would be 256
- * bits, eliminating the performance advantage. Instead we use a technique from
+ /* Note that we cannot handle even numbers by negating them to be odd, as is
+ * done in other implementations, since if our scalars were specified to have
+ * width < 256 for performance reasons, their negations would have width 256
+ * and we'd lose any performance benefit. Instead, we use a technique from
* Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even)
- * or 2 (for odd) to the number we are encoding, then compensating after the
- * multiplication. */
- /* Negative 128-bit numbers will be negated, since otherwise they are 256-bit */
+ * or 2 (for odd) to the number we are encoding, returning a skew value indicating
+ * this, and having the caller compensate after doing the multiplication. */
+
+ /* Negative numbers will be negated to keep their bit representation below the maximum width */
flip = secp256k1_scalar_is_high(&s);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
bit = flip ^ (s.d[0] & 1);
@@ -89,11 +91,6 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
global_sign = secp256k1_scalar_cond_negate(&s, flip);
global_sign *= not_neg_one * 2 - 1;
skew = 1 << bit;
-#else
- /* Otherwise, we just negate to force oddness */
- int is_even = secp256k1_scalar_is_even(&s);
- global_sign = secp256k1_scalar_cond_negate(&s, is_even);
-#endif
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
@@ -127,15 +124,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_ge tmpa;
secp256k1_fe Z;
+ int skew_1;
+ int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
#ifdef USE_ENDOMORPHISM
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
- int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
- int skew_1;
int skew_lam;
secp256k1_scalar q_1, q_lam;
-#else
- int wnaf[1 + WNAF_SIZE(WINDOW_A - 1)];
#endif
int i;
@@ -145,18 +140,10 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
#ifdef USE_ENDOMORPHISM
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
- /* no need for zero correction when using endomorphism since even
- * numbers have one added to them anyway */
skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1);
skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1);
#else
- int is_zero = secp256k1_scalar_is_zero(scalar);
- /* the wNAF ladder cannot handle zero, so bump this to one .. we will
- * correct the result after the fact */
- sc.d[0] += is_zero;
- VERIFY_CHECK(!secp256k1_scalar_is_zero(&sc));
-
- secp256k1_wnaf_const(wnaf, sc, WINDOW_A - 1);
+ skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1);
#endif
/* Calculate odd multiples of a.
@@ -179,21 +166,15 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
/* first loop iteration (separated out so we can directly set r, rather
* than having it start at infinity, get doubled several times, then have
* its new value added to it) */
-#ifdef USE_ENDOMORPHISM
i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
secp256k1_gej_set_ge(r, &tmpa);
-
+#ifdef USE_ENDOMORPHISM
i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
secp256k1_gej_add_ge(r, r, &tmpa);
-#else
- i = wnaf[WNAF_SIZE(WINDOW_A - 1)];
- VERIFY_CHECK(i != 0);
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
- secp256k1_gej_set_ge(r, &tmpa);
#endif
/* remaining loop iterations */
for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) {
@@ -202,59 +183,57 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
for (j = 0; j < WINDOW_A - 1; ++j) {
secp256k1_gej_double_nonzero(r, r, NULL);
}
-#ifdef USE_ENDOMORPHISM
+
n = wnaf_1[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
-
+#ifdef USE_ENDOMORPHISM
n = wnaf_lam[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
-#else
- n = wnaf[i];
- VERIFY_CHECK(n != 0);
- ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
- secp256k1_gej_add_ge(r, r, &tmpa);
#endif
}
secp256k1_fe_mul(&r->z, &r->z, &Z);
-#ifdef USE_ENDOMORPHISM
{
/* Correct for wNAF skew */
secp256k1_ge correction = *a;
secp256k1_ge_storage correction_1_stor;
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_storage correction_lam_stor;
+#endif
secp256k1_ge_storage a2_stor;
secp256k1_gej tmpj;
secp256k1_gej_set_ge(&tmpj, &correction);
secp256k1_gej_double_var(&tmpj, &tmpj, NULL);
secp256k1_ge_set_gej(&correction, &tmpj);
secp256k1_ge_to_storage(&correction_1_stor, a);
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_to_storage(&correction_lam_stor, a);
+#endif
secp256k1_ge_to_storage(&a2_stor, &correction);
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
+#endif
/* Apply the correction */
secp256k1_ge_from_storage(&correction, &correction_1_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
+#ifdef USE_ENDOMORPHISM
secp256k1_ge_from_storage(&correction, &correction_lam_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_ge_mul_lambda(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
- }
-#else
- /* correct for zero */
- r->infinity |= is_zero;
#endif
+ }
}
#endif
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index e6e5f47188..81ae08e100 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -11,6 +11,8 @@
#include "scalar.h"
#include "ecmult.h"
+#include <string.h>
+
/* optimal for 128-bit and 256-bit exponents. */
#define WINDOW_A 5
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index 2d52af5e36..c5ba074244 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -57,6 +57,9 @@ static int secp256k1_fe_is_zero(const secp256k1_fe *a);
static int secp256k1_fe_is_odd(const secp256k1_fe *a);
/** Compare two field elements. Requires magnitude-1 inputs. */
+static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b);
+
+/** Same as secp256k1_fe_equal, but may be variable time. */
static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b);
/** Compare two field elements. Requires both inputs to be normalized */
@@ -92,7 +95,10 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a);
* The input's magnitude can be at most 8. The output magnitude is 1 (but not
* guaranteed to be normalized). The result in r will always be a square
* itself. */
-static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a);
+static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a);
+
+/** Checks whether a field element is a quadratic residue. */
+static int secp256k1_fe_is_quad_var(const secp256k1_fe *a);
/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be
* at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 212cc5396a..7b8c079608 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -7,8 +7,6 @@
#ifndef _SECP256K1_FIELD_REPR_IMPL_H_
#define _SECP256K1_FIELD_REPR_IMPL_H_
-#include <stdio.h>
-#include <string.h>
#include "util.h"
#include "num.h"
#include "field.h"
@@ -429,6 +427,14 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_f
#endif
}
+#if defined(USE_EXTERNAL_ASM)
+
+/* External assembler implementation */
+void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b);
+void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a);
+
+#else
+
#ifdef VERIFY
#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0)
#else
@@ -1037,7 +1043,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t
VERIFY_BITS(r[2], 27);
/* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
}
-
+#endif
static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) {
#ifdef VERIFY
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index b31e24ab81..7a99eb21ec 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -11,7 +11,6 @@
#include "libsecp256k1-config.h"
#endif
-#include <string.h>
#include "util.h"
#include "num.h"
#include "field.h"
diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h
index 9280bb5ea2..0bf22bdd3e 100644
--- a/src/secp256k1/src/field_5x52_int128_impl.h
+++ b/src/secp256k1/src/field_5x52_int128_impl.h
@@ -137,7 +137,7 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t
VERIFY_BITS(r[2], 52);
VERIFY_BITS(c, 63);
/* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
- c += d * R + t3;;
+ c += d * R + t3;
VERIFY_BITS(c, 100);
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[3] = c & M; c >>= 52;
@@ -259,7 +259,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t
VERIFY_BITS(c, 63);
/* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
- c += d * R + t3;;
+ c += d * R + t3;
VERIFY_BITS(c, 100);
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
r[3] = c & M; c >>= 52;
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 77f4aae2f9..52cd902eb3 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -21,6 +21,13 @@
#error "Please select field implementation"
#endif
+SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
+ secp256k1_fe na;
+ secp256k1_fe_negate(&na, a, 1);
+ secp256k1_fe_add(&na, b);
+ return secp256k1_fe_normalizes_to_zero(&na);
+}
+
SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) {
secp256k1_fe na;
secp256k1_fe_negate(&na, a, 1);
@@ -28,7 +35,7 @@ SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const
return secp256k1_fe_normalizes_to_zero_var(&na);
}
-static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) {
+static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
/** Given that p is congruent to 3 mod 4, we can compute the square root of
* a mod p as the (p+1)/4'th power of a.
*
@@ -123,7 +130,7 @@ static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) {
/* Check that a square root was actually calculated */
secp256k1_fe_sqr(&t1, r);
- return secp256k1_fe_equal_var(&t1, a);
+ return secp256k1_fe_equal(&t1, a);
}
static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) {
@@ -280,4 +287,29 @@ static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k
r[0] = u;
}
+static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
+#ifndef USE_NUM_NONE
+ unsigned char b[32];
+ secp256k1_num n;
+ secp256k1_num m;
+ /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
+ static const unsigned char prime[32] = {
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
+ };
+
+ secp256k1_fe c = *a;
+ secp256k1_fe_normalize_var(&c);
+ secp256k1_fe_get_b32(b, &c);
+ secp256k1_num_set_bin(&n, b, 32);
+ secp256k1_num_set_bin(&m, prime, 32);
+ return secp256k1_num_jacobi(&n, &m) >= 0;
+#else
+ secp256k1_fe r;
+ return secp256k1_fe_sqrt(&r, a);
+#endif
+}
+
#endif
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index ebfe1ca70c..d515716744 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -47,7 +47,7 @@ static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const se
* and a Y coordinate that is a quadratic residue modulo p. The return value
* is true iff a coordinate with the given X coordinate exists.
*/
-static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x);
+static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x);
/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness
* for Y. Return value indicates whether the result is valid. */
@@ -94,6 +94,9 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a);
/** Check whether a group element is the point at infinity. */
static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
+/** Check whether a group element's y coordinate is a quadratic residue. */
+static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
+
/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0).
* a may not be zero. Constant time. */
static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 42e2f6e6eb..3e9c4c410d 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -7,8 +7,6 @@
#ifndef _SECP256K1_GROUP_IMPL_H_
#define _SECP256K1_GROUP_IMPL_H_
-#include <string.h>
-
#include "num.h"
#include "field.h"
#include "group.h"
@@ -165,7 +163,7 @@ static void secp256k1_ge_clear(secp256k1_ge *r) {
secp256k1_fe_clear(&r->y);
}
-static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x) {
+static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
secp256k1_fe x2, x3, c;
r->x = *x;
secp256k1_fe_sqr(&x2, x);
@@ -173,11 +171,11 @@ static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x) {
r->infinity = 0;
secp256k1_fe_set_int(&c, 7);
secp256k1_fe_add(&c, &x3);
- return secp256k1_fe_sqrt_var(&r->y, &c);
+ return secp256k1_fe_sqrt(&r->y, &c);
}
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
- if (!secp256k1_ge_set_xquad_var(r, x)) {
+ if (!secp256k1_ge_set_xquad(r, x)) {
return 0;
}
secp256k1_fe_normalize_var(&r->y);
@@ -251,11 +249,23 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
}
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
- /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate */
+ /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
+ *
+ * Note that there is an implementation described at
+ * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+ * which trades a multiply for a square, but in practice this is actually slower,
+ * mainly because it requires more normalizations.
+ */
secp256k1_fe t1,t2,t3,t4;
/** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
* Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
+ *
+ * Having said this, if this function receives a point on a sextic twist, e.g. by
+ * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
+ * since -6 does have a cube root mod p. For this point, this function will not set
+ * the infinity flag even though the point doubles to infinity, and the result
+ * point will be gibberish (z = 0 but infinity = 0).
*/
r->infinity = a->infinity;
if (r->infinity) {
@@ -623,4 +633,18 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
}
#endif
+static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
+ secp256k1_fe yz;
+
+ if (a->infinity) {
+ return 0;
+ }
+
+ /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as
+ * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z
+ is */
+ secp256k1_fe_mul(&yz, &a->y, &a->z);
+ return secp256k1_fe_is_quad_var(&yz);
+}
+
#endif
diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h
index 0ff01e63fa..fca98cab9f 100644
--- a/src/secp256k1/src/hash.h
+++ b/src/secp256k1/src/hash.h
@@ -11,7 +11,7 @@
#include <stdint.h>
typedef struct {
- uint32_t s[32];
+ uint32_t s[8];
uint32_t buf[16]; /* In big endian */
size_t bytes;
} secp256k1_sha256_t;
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index ae55df6d8a..b47e65f830 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -269,15 +269,13 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256
rng->retry = 0;
}
-
+#undef BE32
#undef Round
-#undef sigma0
#undef sigma1
-#undef Sigma0
+#undef sigma0
#undef Sigma1
-#undef Ch
+#undef Sigma0
#undef Maj
-#undef ReadBE32
-#undef WriteBE32
+#undef Ch
#endif
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
index 90a498eaa2..be67048fbe 100644
--- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
+++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java
@@ -1,60 +1,478 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Copyright 2014-2016 the libsecp256k1 contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package org.bitcoin;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.math.BigInteger;
import com.google.common.base.Preconditions;
-
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import static org.bitcoin.NativeSecp256k1Util.*;
/**
- * This class holds native methods to handle ECDSA verification.
- * You can find an example library that can be used for this at
- * https://github.com/sipa/secp256k1
+ * <p>This class holds native methods to handle ECDSA verification.</p>
+ *
+ * <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
+ *
+ * <p>To build secp256k1 for use with bitcoinj, run
+ * `./configure --enable-jni --enable-experimental --enable-module-schnorr --enable-module-ecdh`
+ * and `make` then copy `.libs/libsecp256k1.so` to your system library path
+ * or point the JVM to the folder containing it with -Djava.library.path
+ * </p>
*/
public class NativeSecp256k1 {
- public static final boolean enabled;
- static {
- boolean isEnabled = true;
- try {
- System.loadLibrary("javasecp256k1");
- } catch (UnsatisfiedLinkError e) {
- isEnabled = false;
- }
- enabled = isEnabled;
- }
-
+
+ private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ private static final Lock r = rwl.readLock();
+ private static final Lock w = rwl.writeLock();
private static ThreadLocal<ByteBuffer> nativeECDSABuffer = new ThreadLocal<ByteBuffer>();
/**
* Verifies the given secp256k1 signature in native code.
* Calling when enabled == false is undefined (probably library not loaded)
- *
+ *
* @param data The data which was signed, must be exactly 32 bytes
* @param signature The signature
* @param pub The public key which did the signing
*/
- public static boolean verify(byte[] data, byte[] signature, byte[] pub) {
+ public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{
Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520);
ByteBuffer byteBuff = nativeECDSABuffer.get();
- if (byteBuff == null) {
- byteBuff = ByteBuffer.allocateDirect(32 + 8 + 520 + 520);
+ if (byteBuff == null || byteBuff.capacity() < 520) {
+ byteBuff = ByteBuffer.allocateDirect(520);
byteBuff.order(ByteOrder.nativeOrder());
nativeECDSABuffer.set(byteBuff);
}
byteBuff.rewind();
byteBuff.put(data);
- byteBuff.putInt(signature.length);
- byteBuff.putInt(pub.length);
byteBuff.put(signature);
byteBuff.put(pub);
- return secp256k1_ecdsa_verify(byteBuff) == 1;
+
+ byte[][] retByteArray;
+
+ r.lock();
+ try {
+ return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1;
+ } finally {
+ r.unlock();
+ }
+ }
+
+ /**
+ * libsecp256k1 Create an ECDSA signature.
+ *
+ * @param data Message hash, 32 bytes
+ * @param key Secret key, 32 bytes
+ *
+ * Return values
+ * @param sig byte array of signature
+ */
+ public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{
+ Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < 32 + 32) {
+ byteBuff = ByteBuffer.allocateDirect(32 + 32);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(data);
+ byteBuff.put(sec);
+
+ byte[][] retByteArray;
+
+ r.lock();
+ try {
+ retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] sigArr = retByteArray[0];
+ int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(sigArr.length, sigLen, "Got bad signature length.");
+
+ return retVal == 0 ? new byte[0] : sigArr;
+ }
+
+ /**
+ * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid
+ *
+ * @param seckey ECDSA Secret key, 32 bytes
+ */
+ public static boolean secKeyVerify(byte[] seckey) {
+ Preconditions.checkArgument(seckey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < seckey.length) {
+ byteBuff = ByteBuffer.allocateDirect(seckey.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seckey);
+
+ r.lock();
+ try {
+ return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1;
+ } finally {
+ r.unlock();
+ }
+ }
+
+
+ /**
+ * libsecp256k1 Compute Pubkey - computes public key from secret key
+ *
+ * @param seckey ECDSA Secret key, 32 bytes
+ *
+ * Return values
+ * @param pubkey ECDSA Public key, 33 or 65 bytes
+ */
+ //TODO add a 'compressed' arg
+ public static byte[] computePubkey(byte[] seckey) throws AssertFailException{
+ Preconditions.checkArgument(seckey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < seckey.length) {
+ byteBuff = ByteBuffer.allocateDirect(seckey.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seckey);
+
+ byte[][] retByteArray;
+
+ r.lock();
+ try {
+ retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] pubArr = retByteArray[0];
+ int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
+
+ return retVal == 0 ? new byte[0]: pubArr;
+ }
+
+ /**
+ * libsecp256k1 Cleanup - This destroys the secp256k1 context object
+ * This should be called at the end of the program for proper cleanup of the context.
+ */
+ public static synchronized void cleanup() {
+ w.lock();
+ try {
+ secp256k1_destroy_context(Secp256k1Context.getContext());
+ } finally {
+ w.unlock();
+ }
+ }
+
+ public static long cloneContext() {
+ r.lock();
+ try {
+ return secp256k1_ctx_clone(Secp256k1Context.getContext());
+ } finally { r.unlock(); }
+ }
+
+ /**
+ * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param seckey 32-byte seckey
+ */
+ public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(privkey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(privkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] privArr = retByteArray[0];
+
+ int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(privArr.length, privLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return privArr;
}
/**
- * @param byteBuff signature format is byte[32] data,
- * native-endian int signatureLength, native-endian int pubkeyLength,
- * byte[signatureLength] signature, byte[pubkeyLength] pub
- * @returns 1 for valid signature, anything else for invalid
+ * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param seckey 32-byte seckey
*/
- private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff);
+ public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(privkey.length == 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(privkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] privArr = retByteArray[0];
+
+ int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(privArr.length, privLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return privArr;
+ }
+
+ /**
+ * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param pubkey 32-byte seckey
+ */
+ public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(pubkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length);
+ } finally {
+ r.unlock();
+ }
+
+ byte[] pubArr = retByteArray[0];
+
+ int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return pubArr;
+ }
+
+ /**
+ * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it
+ *
+ * @param tweak some bytes to tweak with
+ * @param pubkey 32-byte seckey
+ */
+ public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{
+ Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
+ byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(pubkey);
+ byteBuff.put(tweak);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length);
+ } finally {
+ r.unlock();
+ }
+
+ byte[] pubArr = retByteArray[0];
+
+ int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
+ int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
+
+ assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
+
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return pubArr;
+ }
+
+ /**
+ * libsecp256k1 create ECDH secret - constant time ECDH calculation
+ *
+ * @param seckey byte array of secret key used in exponentiaion
+ * @param pubkey byte array of public key used in exponentiaion
+ */
+ public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{
+ Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) {
+ byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seckey);
+ byteBuff.put(pubkey);
+
+ byte[][] retByteArray;
+ r.lock();
+ try {
+ retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length);
+ } finally {
+ r.unlock();
+ }
+
+ byte[] resArr = retByteArray[0];
+ int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
+
+ assertEquals(resArr.length, 32, "Got bad result length.");
+ assertEquals(retVal, 1, "Failed return value check.");
+
+ return resArr;
+ }
+
+ /**
+ * libsecp256k1 randomize - updates the context randomization
+ *
+ * @param seed 32-byte random seed
+ */
+ public static synchronized boolean randomize(byte[] seed) throws AssertFailException{
+ Preconditions.checkArgument(seed.length == 32 || seed == null);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null || byteBuff.capacity() < seed.length) {
+ byteBuff = ByteBuffer.allocateDirect(seed.length);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(seed);
+
+ w.lock();
+ try {
+ return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1;
+ } finally {
+ w.unlock();
+ }
+ }
+
+ public static byte[] schnorrSign(byte[] data, byte[] sec) throws AssertFailException {
+ Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
+
+ ByteBuffer byteBuff = nativeECDSABuffer.get();
+ if (byteBuff == null) {
+ byteBuff = ByteBuffer.allocateDirect(32 + 32);
+ byteBuff.order(ByteOrder.nativeOrder());
+ nativeECDSABuffer.set(byteBuff);
+ }
+ byteBuff.rewind();
+ byteBuff.put(data);
+ byteBuff.put(sec);
+
+ byte[][] retByteArray;
+
+ r.lock();
+ try {
+ retByteArray = secp256k1_schnorr_sign(byteBuff, Secp256k1Context.getContext());
+ } finally {
+ r.unlock();
+ }
+
+ byte[] sigArr = retByteArray[0];
+ int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
+
+ assertEquals(sigArr.length, 64, "Got bad signature length.");
+
+ return retVal == 0 ? new byte[0] : sigArr;
+ }
+
+ private static native long secp256k1_ctx_clone(long context);
+
+ private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen);
+
+ private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen);
+
+ private static native void secp256k1_destroy_context(long context);
+
+ private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen);
+
+ private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context);
+
+ private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
+
+ private static native byte[][] secp256k1_schnorr_sign(ByteBuffer byteBuff, long context);
+
+ private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
+
}
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
new file mode 100644
index 0000000000..f18ce95810
--- /dev/null
+++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java
@@ -0,0 +1,247 @@
+package org.bitcoin;
+
+import com.google.common.io.BaseEncoding;
+import java.util.Arrays;
+import java.math.BigInteger;
+import javax.xml.bind.DatatypeConverter;
+import static org.bitcoin.NativeSecp256k1Util.*;
+
+/**
+ * This class holds test cases defined for testing this library.
+ */
+public class NativeSecp256k1Test {
+
+ //TODO improve comments/add more tests
+ /**
+ * This tests verify() for a valid signature
+ */
+ public static void testVerifyPos() throws AssertFailException{
+ boolean result = false;
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
+ byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+
+ result = NativeSecp256k1.verify( data, sig, pub);
+ assertEquals( result, true , "testVerifyPos");
+ }
+
+ /**
+ * This tests verify() for a non-valid signature
+ */
+ public static void testVerifyNeg() throws AssertFailException{
+ boolean result = false;
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing"
+ byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+
+ result = NativeSecp256k1.verify( data, sig, pub);
+ //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
+ assertEquals( result, false , "testVerifyNeg");
+ }
+
+ /**
+ * This tests secret key verify() for a valid secretkey
+ */
+ public static void testSecKeyVerifyPos() throws AssertFailException{
+ boolean result = false;
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+
+ result = NativeSecp256k1.secKeyVerify( sec );
+ //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
+ assertEquals( result, true , "testSecKeyVerifyPos");
+ }
+
+ /**
+ * This tests secret key verify() for a invalid secretkey
+ */
+ public static void testSecKeyVerifyNeg() throws AssertFailException{
+ boolean result = false;
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
+
+ result = NativeSecp256k1.secKeyVerify( sec );
+ //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
+ assertEquals( result, false , "testSecKeyVerifyNeg");
+ }
+
+ /**
+ * This tests public key create() for a valid secretkey
+ */
+ public static void testPubKeyCreatePos() throws AssertFailException{
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.computePubkey( sec);
+ String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos");
+ }
+
+ /**
+ * This tests public key create() for a invalid secretkey
+ */
+ public static void testPubKeyCreateNeg() throws AssertFailException{
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.computePubkey( sec);
+ String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( pubkeyString, "" , "testPubKeyCreateNeg");
+ }
+
+ /**
+ * This tests sign() for a valid secretkey
+ */
+ public static void testSignPos() throws AssertFailException{
+
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.sign(data, sec);
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
+ }
+
+ /**
+ * This tests sign() for a invalid secretkey
+ */
+ public static void testSignNeg() throws AssertFailException{
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.sign(data, sec);
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString, "" , "testSignNeg");
+ }
+
+ /**
+ * This tests private key tweak-add
+ */
+ public static void testPrivKeyTweakAdd_1() throws AssertFailException {
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1");
+ }
+
+ /**
+ * This tests private key tweak-mul
+ */
+ public static void testPrivKeyTweakMul_1() throws AssertFailException {
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1");
+ }
+
+ /**
+ * This tests private key tweak-add uncompressed
+ */
+ public static void testPrivKeyTweakAdd_2() throws AssertFailException {
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2");
+ }
+
+ /**
+ * This tests private key tweak-mul uncompressed
+ */
+ public static void testPrivKeyTweakMul_2() throws AssertFailException {
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+ byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
+
+ byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data );
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2");
+ }
+
+ /**
+ * This tests seed randomization
+ */
+ public static void testRandomize() throws AssertFailException {
+ byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random"
+ boolean result = NativeSecp256k1.randomize(seed);
+ assertEquals( result, true, "testRandomize");
+ }
+
+ /**
+ * This tests signSchnorr() for a valid secretkey
+ */
+ public static void testSchnorrSign() throws AssertFailException{
+
+ byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.schnorrSign(data, sec);
+ String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( sigString, "C5E929AA058B982048760422D3B563749B7D0E50C5EBD8CD2FFC23214BD6A2F1B072C13880997EBA847CF20F2F90FCE07C1CA33A890A4127095A351127F8D95F" , "testSchnorrSign");
+ }
+
+ /**
+ * This tests signSchnorr() for a valid secretkey
+ */
+ public static void testCreateECDHSecret() throws AssertFailException{
+
+ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
+ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
+
+ byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub);
+ String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
+ assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret");
+ }
+
+ public static void main(String[] args) throws AssertFailException{
+
+
+ System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n");
+
+ assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" );
+
+ //Test verify() success/fail
+ testVerifyPos();
+ testVerifyNeg();
+
+ //Test secKeyVerify() success/fail
+ testSecKeyVerifyPos();
+ testSecKeyVerifyNeg();
+
+ //Test computePubkey() success/fail
+ testPubKeyCreatePos();
+ testPubKeyCreateNeg();
+
+ //Test sign() success/fail
+ testSignPos();
+ testSignNeg();
+
+ //Test Schnorr (partial support) //TODO
+ testSchnorrSign();
+ //testSchnorrVerify
+ //testSchnorrRecovery
+
+ //Test privKeyTweakAdd() 1
+ testPrivKeyTweakAdd_1();
+
+ //Test privKeyTweakMul() 2
+ testPrivKeyTweakMul_1();
+
+ //Test privKeyTweakAdd() 3
+ testPrivKeyTweakAdd_2();
+
+ //Test privKeyTweakMul() 4
+ testPrivKeyTweakMul_2();
+
+ //Test randomize()
+ testRandomize();
+
+ //Test ECDH
+ testCreateECDHSecret();
+
+ NativeSecp256k1.cleanup();
+
+ System.out.println(" All tests passed." );
+
+ }
+}
diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
new file mode 100644
index 0000000000..04732ba044
--- /dev/null
+++ b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014-2016 the libsecp256k1 contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bitcoin;
+
+public class NativeSecp256k1Util{
+
+ public static void assertEquals( int val, int val2, String message ) throws AssertFailException{
+ if( val != val2 )
+ throw new AssertFailException("FAIL: " + message);
+ }
+
+ public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{
+ if( val != val2 )
+ throw new AssertFailException("FAIL: " + message);
+ else
+ System.out.println("PASS: " + message);
+ }
+
+ public static void assertEquals( String val, String val2, String message ) throws AssertFailException{
+ if( !val.equals(val2) )
+ throw new AssertFailException("FAIL: " + message);
+ else
+ System.out.println("PASS: " + message);
+ }
+
+ public static class AssertFailException extends Exception {
+ public AssertFailException(String message) {
+ super( message );
+ }
+ }
+}
diff --git a/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java b/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java
new file mode 100644
index 0000000000..216c986a8b
--- /dev/null
+++ b/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014-2016 the libsecp256k1 contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.bitcoin;
+
+/**
+ * This class holds the context reference used in native methods
+ * to handle ECDSA operations.
+ */
+public class Secp256k1Context {
+ private static final boolean enabled; //true if the library is loaded
+ private static final long context; //ref to pointer to context obj
+
+ static { //static initializer
+ boolean isEnabled = true;
+ long contextRef = -1;
+ try {
+ System.loadLibrary("secp256k1");
+ contextRef = secp256k1_init_context();
+ } catch (UnsatisfiedLinkError e) {
+ System.out.println("UnsatisfiedLinkError: " + e.toString());
+ isEnabled = false;
+ }
+ enabled = isEnabled;
+ context = contextRef;
+ }
+
+ public static boolean isEnabled() {
+ return enabled;
+ }
+
+ public static long getContext() {
+ if(!enabled) return -1; //sanity check
+ return context;
+ }
+
+ private static native long secp256k1_init_context();
+}
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
index bb4cd70728..dba9524dd4 100644
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
+++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c
@@ -1,23 +1,411 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
#include "org_bitcoin_NativeSecp256k1.h"
#include "include/secp256k1.h"
+#include "include/secp256k1_ecdh.h"
+#include "include/secp256k1_recovery.h"
+#include "include/secp256k1_schnorr.h"
-JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
- (JNIEnv* env, jclass classObject, jobject byteBufferObject)
+
+SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
+ (JNIEnv* env, jclass classObject, jlong ctx_l)
+{
+ const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx);
+
+ (void)classObject;(void)env;
+
+ return ctx_clone_l;
+
+}
+
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+
+ (void)classObject;
+
+ return secp256k1_context_randomize(ctx, seed);
+
+}
+
+SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
+ (JNIEnv* env, jclass classObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ secp256k1_context_destroy(ctx);
+
+ (void)classObject;(void)env;
+}
+
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+
+ unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* sigdata = { (unsigned char*) (data + 32) };
+ const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) };
+
+ secp256k1_ecdsa_signature sig;
+ secp256k1_pubkey pubkey;
+
+ int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen);
+
+ if( ret ) {
+ ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
+
+ if( ret ) {
+ ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey);
+ }
+ }
+
+ (void)classObject;
+
+ return ret;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ unsigned char* secKey = (unsigned char*) (data + 32);
+
+ jobjectArray retArray;
+ jbyteArray sigArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ secp256k1_ecdsa_signature sig[72];
+
+ int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL );
+
+ unsigned char outputSer[72];
+ size_t outputLen = 72;
+
+ if( ret ) {
+ int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ sigArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+
+ (void)classObject;
+
+ return secp256k1_ec_seckey_verify(ctx, secKey);
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+
+ secp256k1_pubkey pubkey;
+
+ jobjectArray retArray;
+ jbyteArray pubkeyArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey);
+
+ unsigned char outputSer[65];
+ size_t outputLen = 65;
+
+ if( ret ) {
+ int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ pubkeyArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (privkey + 32);
+
+ jobjectArray retArray;
+ jbyteArray privArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ int privkeylen = 32;
+
+ int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak);
+
+ intsarray[0] = privkeylen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ privArray = (*env)->NewByteArray(env, privkeylen);
+ (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
+ (*env)->SetObjectArrayElement(env, retArray, 0, privArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (privkey + 32);
+
+ jobjectArray retArray;
+ jbyteArray privArray, intsByteArray;
+ unsigned char intsarray[2];
+
+ int privkeylen = 32;
+
+ int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak);
+
+ intsarray[0] = privkeylen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ privArray = (*env)->NewByteArray(env, privkeylen);
+ (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
+ (*env)->SetObjectArrayElement(env, retArray, 0, privArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/
+ unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (pkey + publen);
+
+ jobjectArray retArray;
+ jbyteArray pubArray, intsByteArray;
+ unsigned char intsarray[2];
+ unsigned char outputSer[65];
+ size_t outputLen = 65;
+
+ secp256k1_pubkey pubkey;
+ int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
+
+ if( ret ) {
+ ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak);
+ }
+
+ if( ret ) {
+ int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ pubArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
{
- unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
- int sigLen = *((int*)(data + 32));
- int pubLen = *((int*)(data + 32 + 4));
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* tweak = (unsigned char*) (pkey + publen);
+
+ jobjectArray retArray;
+ jbyteArray pubArray, intsByteArray;
+ unsigned char intsarray[2];
+ unsigned char outputSer[65];
+ size_t outputLen = 65;
+
+ secp256k1_pubkey pubkey;
+ int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
+
+ if ( ret ) {
+ ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak);
+ }
+
+ if( ret ) {
+ int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
+ }
+
+ intsarray[0] = outputLen;
+ intsarray[1] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ pubArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
+ (*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
- return secp256k1_ecdsa_verify(data, 32, data+32+8, sigLen, data+32+8+sigLen, pubLen);
+ intsByteArray = (*env)->NewByteArray(env, 2);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
}
-static void __javasecp256k1_attach(void) __attribute__((constructor));
-static void __javasecp256k1_detach(void) __attribute__((destructor));
+SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine
+ (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys)
+{
+ (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys;
-static void __javasecp256k1_attach(void) {
- secp256k1_start(SECP256K1_START_VERIFY);
+ return 0;
}
-static void __javasecp256k1_detach(void) {
- secp256k1_stop();
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ unsigned char* secKey = (unsigned char*) (data + 32);
+
+ jobjectArray retArray;
+ jbyteArray sigArray, intsByteArray;
+ unsigned char intsarray[1];
+ unsigned char sig[64];
+
+ int ret = secp256k1_schnorr_sign(ctx, sig, data, secKey, NULL, NULL);
+
+ intsarray[0] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ sigArray = (*env)->NewByteArray(env, 64);
+ (*env)->SetByteArrayRegion(env, sigArray, 0, 64, (jbyte*)sig);
+ (*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 1);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
+}
+
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
+{
+ secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
+ const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject);
+ const unsigned char* pubdata = (const unsigned char*) (secdata + 32);
+
+ jobjectArray retArray;
+ jbyteArray outArray, intsByteArray;
+ unsigned char intsarray[1];
+ secp256k1_pubkey pubkey;
+ unsigned char nonce_res[32];
+ size_t outputLen = 32;
+
+ int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
+
+ if (ret) {
+ ret = secp256k1_ecdh(
+ ctx,
+ nonce_res,
+ &pubkey,
+ secdata
+ );
+ }
+
+ intsarray[0] = ret;
+
+ retArray = (*env)->NewObjectArray(env, 2,
+ (*env)->FindClass(env, "[B"),
+ (*env)->NewByteArray(env, 1));
+
+ outArray = (*env)->NewByteArray(env, outputLen);
+ (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res);
+ (*env)->SetObjectArrayElement(env, retArray, 0, outArray);
+
+ intsByteArray = (*env)->NewByteArray(env, 1);
+ (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
+ (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
+
+ (void)classObject;
+
+ return retArray;
}
diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
index d7fb004fa8..4125a1f523 100644
--- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
+++ b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h
@@ -1,5 +1,6 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
+#include "include/secp256k1.h"
/* Header for class org_bitcoin_NativeSecp256k1 */
#ifndef _Included_org_bitcoin_NativeSecp256k1
@@ -9,11 +10,116 @@ extern "C" {
#endif
/*
* Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ctx_clone
+ * Signature: (J)J
+ */
+SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_context_randomize
+ * Signature: (Ljava/nio/ByteBuffer;J)I
+ */
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_privkey_tweak_add
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_privkey_tweak_mul
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_pubkey_tweak_add
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
+ (JNIEnv *, jclass, jobject, jlong, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_pubkey_tweak_mul
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
+ (JNIEnv *, jclass, jobject, jlong, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_destroy_context
+ * Signature: (J)V
+ */
+SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdsa_verify
- * Signature: (Ljava/nio/ByteBuffer;)I
+ * Signature: (Ljava/nio/ByteBuffer;JII)I
+ */
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
+ (JNIEnv *, jclass, jobject, jlong, jint, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ecdsa_sign
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ec_seckey_verify
+ * Signature: (Ljava/nio/ByteBuffer;J)I
+ */
+SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ec_pubkey_create
+ * Signature: (Ljava/nio/ByteBuffer;J)[[B
*/
-JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
- (JNIEnv *, jclass, jobject);
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
+ (JNIEnv *, jclass, jobject, jlong);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ec_pubkey_parse
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
+ (JNIEnv *, jclass, jobject, jlong, jint);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_schnorr_sign
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l);
+
+/*
+ * Class: org_bitcoin_NativeSecp256k1
+ * Method: secp256k1_ecdh
+ * Signature: (Ljava/nio/ByteBuffer;JI)[[B
+ */
+SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
+ (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen);
+
#ifdef __cplusplus
}
diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c
new file mode 100644
index 0000000000..a52939e7e7
--- /dev/null
+++ b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include "org_bitcoin_Secp256k1Context.h"
+#include "include/secp256k1.h"
+
+SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
+ (JNIEnv* env, jclass classObject)
+{
+ secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+
+ (void)classObject;(void)env;
+
+ return (uintptr_t)ctx;
+}
+
diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h
new file mode 100644
index 0000000000..0d2bc84b7f
--- /dev/null
+++ b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h
@@ -0,0 +1,22 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+#include "include/secp256k1.h"
+/* Header for class org_bitcoin_Secp256k1Context */
+
+#ifndef _Included_org_bitcoin_Secp256k1Context
+#define _Included_org_bitcoin_Secp256k1Context
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_bitcoin_Secp256k1Context
+ * Method: secp256k1_init_context
+ * Signature: ()J
+ */
+SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
+ (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/secp256k1/src/modules/ecdh/Makefile.am.include b/src/secp256k1/src/modules/ecdh/Makefile.am.include
index 670b9c1152..e3088b4697 100644
--- a/src/secp256k1/src/modules/ecdh/Makefile.am.include
+++ b/src/secp256k1/src/modules/ecdh/Makefile.am.include
@@ -4,5 +4,5 @@ noinst_HEADERS += src/modules/ecdh/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_ecdh
bench_ecdh_SOURCES = src/bench_ecdh.c
-bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS)
+bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif
diff --git a/src/secp256k1/src/modules/recovery/Makefile.am.include b/src/secp256k1/src/modules/recovery/Makefile.am.include
index 5de3ea33ea..bf23c26e71 100644
--- a/src/secp256k1/src/modules/recovery/Makefile.am.include
+++ b/src/secp256k1/src/modules/recovery/Makefile.am.include
@@ -4,5 +4,5 @@ noinst_HEADERS += src/modules/recovery/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_recover
bench_recover_SOURCES = src/bench_recover.c
-bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS)
+bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif
diff --git a/src/secp256k1/src/modules/schnorr/Makefile.am.include b/src/secp256k1/src/modules/schnorr/Makefile.am.include
index b3bfa7d5cc..f1af8e8325 100644
--- a/src/secp256k1/src/modules/schnorr/Makefile.am.include
+++ b/src/secp256k1/src/modules/schnorr/Makefile.am.include
@@ -6,5 +6,5 @@ noinst_HEADERS += src/modules/schnorr/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_schnorr_verify
bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
-bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
+bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif
diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h
index ebfa71eb44..7bb9c5be8c 100644
--- a/src/secp256k1/src/num.h
+++ b/src/secp256k1/src/num.h
@@ -32,6 +32,9 @@ static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsi
/** Compute a modular inverse. The input must be less than the modulus. */
static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m);
+/** Compute the jacobi symbol (a|b). b must be positive and odd. */
+static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b);
+
/** Compare the absolute value of two numbers. */
static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b);
@@ -57,6 +60,9 @@ static void secp256k1_num_shift(secp256k1_num *r, int bits);
/** Check whether a number is zero. */
static int secp256k1_num_is_zero(const secp256k1_num *a);
+/** Check whether a number is one. */
+static int secp256k1_num_is_one(const secp256k1_num *a);
+
/** Check whether a number is strictly negative. */
static int secp256k1_num_is_neg(const secp256k1_num *a);
diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h
index 7b6a89719a..3a46495eea 100644
--- a/src/secp256k1/src/num_gmp_impl.h
+++ b/src/secp256k1/src/num_gmp_impl.h
@@ -144,6 +144,32 @@ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a,
memset(v, 0, sizeof(v));
}
+static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) {
+ int ret;
+ mpz_t ga, gb;
+ secp256k1_num_sanity(a);
+ secp256k1_num_sanity(b);
+ VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1));
+
+ mpz_inits(ga, gb, NULL);
+
+ mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data);
+ mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data);
+ if (a->neg) {
+ mpz_neg(ga, ga);
+ }
+
+ ret = mpz_jacobi(ga, gb);
+
+ mpz_clears(ga, gb, NULL);
+
+ return ret;
+}
+
+static int secp256k1_num_is_one(const secp256k1_num *a) {
+ return (a->limbs == 1 && a->data[0] == 1);
+}
+
static int secp256k1_num_is_zero(const secp256k1_num *a) {
return (a->limbs == 1 && a->data[0] == 0);
}
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index 88ea97de86..c5baf4df41 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -7,8 +7,6 @@
#ifndef _SECP256K1_SCALAR_IMPL_H_
#define _SECP256K1_SCALAR_IMPL_H_
-#include <string.h>
-
#include "group.h"
#include "scalar.h"
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index 62d192baeb..7973d60c36 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -4,8 +4,6 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
-#define SECP256K1_BUILD (1)
-
#include "include/secp256k1.h"
#include "util.h"
@@ -152,7 +150,6 @@ static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) {
int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) {
secp256k1_ge Q;
- (void)ctx;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
memset(pubkey, 0, sizeof(*pubkey));
@@ -170,7 +167,6 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o
size_t len;
int ret = 0;
- (void)ctx;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(outputlen != NULL);
ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65));
@@ -216,7 +212,7 @@ static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const
int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
secp256k1_scalar r, s;
- (void)ctx;
+ VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(input != NULL);
@@ -234,7 +230,7 @@ int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp25
int ret = 1;
int overflow = 0;
- (void)ctx;
+ VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(input64 != NULL);
@@ -253,7 +249,7 @@ int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp25
int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) {
secp256k1_scalar r, s;
- (void)ctx;
+ VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(outputlen != NULL);
ARG_CHECK(sig != NULL);
@@ -265,7 +261,7 @@ int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsign
int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) {
secp256k1_scalar r, s;
- (void)ctx;
+ VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output64 != NULL);
ARG_CHECK(sig != NULL);
@@ -398,7 +394,6 @@ int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char
int overflow;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- (void)ctx;
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
ret = !overflow && !secp256k1_scalar_is_zero(&sec);
@@ -437,7 +432,6 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
ARG_CHECK(tweak != NULL);
- (void)ctx;
secp256k1_scalar_set_b32(&term, tweak, &overflow);
secp256k1_scalar_set_b32(&sec, seckey, NULL);
@@ -485,7 +479,6 @@ int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
ARG_CHECK(tweak != NULL);
- (void)ctx;
secp256k1_scalar_set_b32(&factor, tweak, &overflow);
secp256k1_scalar_set_b32(&sec, seckey, NULL);
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index 687a5f2fdd..b32cb90813 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -473,6 +473,8 @@ void test_num_negate(void) {
}
void test_num_add_sub(void) {
+ int i;
+ secp256k1_scalar s;
secp256k1_num n1;
secp256k1_num n2;
secp256k1_num n1p2, n2p1, n1m2, n2m1;
@@ -498,6 +500,110 @@ void test_num_add_sub(void) {
CHECK(!secp256k1_num_eq(&n2p1, &n1));
secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */
CHECK(secp256k1_num_eq(&n2p1, &n1));
+
+ /* check is_one */
+ secp256k1_scalar_set_int(&s, 1);
+ secp256k1_scalar_get_num(&n1, &s);
+ CHECK(secp256k1_num_is_one(&n1));
+ /* check that 2^n + 1 is never 1 */
+ secp256k1_scalar_get_num(&n2, &s);
+ for (i = 0; i < 250; ++i) {
+ secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */
+ secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */
+ CHECK(!secp256k1_num_is_one(&n1p2));
+ }
+}
+
+void test_num_mod(void) {
+ int i;
+ secp256k1_scalar s;
+ secp256k1_num order, n;
+
+ /* check that 0 mod anything is 0 */
+ random_scalar_order_test(&s);
+ secp256k1_scalar_get_num(&order, &s);
+ secp256k1_scalar_set_int(&s, 0);
+ secp256k1_scalar_get_num(&n, &s);
+ secp256k1_num_mod(&n, &order);
+ CHECK(secp256k1_num_is_zero(&n));
+
+ /* check that anything mod 1 is 0 */
+ secp256k1_scalar_set_int(&s, 1);
+ secp256k1_scalar_get_num(&order, &s);
+ secp256k1_scalar_get_num(&n, &s);
+ secp256k1_num_mod(&n, &order);
+ CHECK(secp256k1_num_is_zero(&n));
+
+ /* check that increasing the number past 2^256 does not break this */
+ random_scalar_order_test(&s);
+ secp256k1_scalar_get_num(&n, &s);
+ /* multiply by 2^8, which'll test this case with high probability */
+ for (i = 0; i < 8; ++i) {
+ secp256k1_num_add(&n, &n, &n);
+ }
+ secp256k1_num_mod(&n, &order);
+ CHECK(secp256k1_num_is_zero(&n));
+}
+
+void test_num_jacobi(void) {
+ secp256k1_scalar sqr;
+ secp256k1_scalar small;
+ secp256k1_scalar five; /* five is not a quadratic residue */
+ secp256k1_num order, n;
+ int i;
+ /* squares mod 5 are 1, 4 */
+ const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 };
+
+ /* check some small values with 5 as the order */
+ secp256k1_scalar_set_int(&five, 5);
+ secp256k1_scalar_get_num(&order, &five);
+ for (i = 0; i < 10; ++i) {
+ secp256k1_scalar_set_int(&small, i);
+ secp256k1_scalar_get_num(&n, &small);
+ CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]);
+ }
+
+ /** test large values with 5 as group order */
+ secp256k1_scalar_get_num(&order, &five);
+ /* we first need a scalar which is not a multiple of 5 */
+ do {
+ secp256k1_num fiven;
+ random_scalar_order_test(&sqr);
+ secp256k1_scalar_get_num(&fiven, &five);
+ secp256k1_scalar_get_num(&n, &sqr);
+ secp256k1_num_mod(&n, &fiven);
+ } while (secp256k1_num_is_zero(&n));
+ /* next force it to be a residue. 2 is a nonresidue mod 5 so we can
+ * just multiply by two, i.e. add the number to itself */
+ if (secp256k1_num_jacobi(&n, &order) == -1) {
+ secp256k1_num_add(&n, &n, &n);
+ }
+
+ /* test residue */
+ CHECK(secp256k1_num_jacobi(&n, &order) == 1);
+ /* test nonresidue */
+ secp256k1_num_add(&n, &n, &n);
+ CHECK(secp256k1_num_jacobi(&n, &order) == -1);
+
+ /** test with secp group order as order */
+ secp256k1_scalar_order_get_num(&order);
+ random_scalar_order_test(&sqr);
+ secp256k1_scalar_sqr(&sqr, &sqr);
+ /* test residue */
+ secp256k1_scalar_get_num(&n, &sqr);
+ CHECK(secp256k1_num_jacobi(&n, &order) == 1);
+ /* test nonresidue */
+ secp256k1_scalar_mul(&sqr, &sqr, &five);
+ secp256k1_scalar_get_num(&n, &sqr);
+ CHECK(secp256k1_num_jacobi(&n, &order) == -1);
+ /* test multiple of the order*/
+ CHECK(secp256k1_num_jacobi(&order, &order) == 0);
+
+ /* check one less than the order */
+ secp256k1_scalar_set_int(&small, 1);
+ secp256k1_scalar_get_num(&n, &small);
+ secp256k1_num_sub(&n, &order, &n);
+ CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */
}
void run_num_smalltests(void) {
@@ -505,6 +611,8 @@ void run_num_smalltests(void) {
for (i = 0; i < 100*count; i++) {
test_num_negate();
test_num_add_sub();
+ test_num_mod();
+ test_num_jacobi();
}
}
#endif
@@ -689,6 +797,10 @@ void scalar_test(void) {
secp256k1_scalar_inverse(&inv, &inv);
/* Inverting one must result in one. */
CHECK(secp256k1_scalar_is_one(&inv));
+#ifndef USE_NUM_NONE
+ secp256k1_scalar_get_num(&invnum, &inv);
+ CHECK(secp256k1_num_is_one(&invnum));
+#endif
}
}
@@ -855,7 +967,7 @@ void run_scalar_tests(void) {
secp256k1_scalar zzv;
#endif
int overflow;
- unsigned char chal[32][2][32] = {
+ unsigned char chal[33][2][32] = {
{{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff,
@@ -1111,9 +1223,17 @@ void run_scalar_tests(void) {
{0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00,
0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}}
+ 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}},
+ {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb,
+ 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03},
+ {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb,
+ 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}}
};
- unsigned char res[32][2][32] = {
+ unsigned char res[33][2][32] = {
{{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9,
0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1,
0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6,
@@ -1369,10 +1489,18 @@ void run_scalar_tests(void) {
{0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2,
0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38,
0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34,
- 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}}
+ 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}},
+ {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34,
+ 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13,
+ 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46,
+ 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5},
+ {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34,
+ 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13,
+ 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46,
+ 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}}
};
secp256k1_scalar_set_int(&one, 1);
- for (i = 0; i < 32; i++) {
+ for (i = 0; i < 33; i++) {
secp256k1_scalar_set_b32(&x, chal[i][0], &overflow);
CHECK(!overflow);
secp256k1_scalar_set_b32(&y, chal[i][1], &overflow);
@@ -1446,7 +1574,7 @@ void random_fe_non_zero(secp256k1_fe *nz) {
void random_fe_non_square(secp256k1_fe *ns) {
secp256k1_fe r;
random_fe_non_zero(ns);
- if (secp256k1_fe_sqrt_var(&r, ns)) {
+ if (secp256k1_fe_sqrt(&r, ns)) {
secp256k1_fe_negate(ns, ns, 1);
}
}
@@ -1641,7 +1769,7 @@ void run_sqr(void) {
void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) {
secp256k1_fe r1, r2;
- int v = secp256k1_fe_sqrt_var(&r1, a);
+ int v = secp256k1_fe_sqrt(&r1, a);
CHECK((v == 0) == (k == NULL));
if (k != NULL) {
@@ -1951,8 +2079,8 @@ void test_add_neg_y_diff_x(void) {
* of the sum to be wrong (since infinity has no xy coordinates).
* HOWEVER, if the x-coordinates are different, infinity is the
* wrong answer, and such degeneracies are exposed. This is the
- * root of https://github.com/bitcoin/secp256k1/issues/257 which
- * this test is a regression test for.
+ * root of https://github.com/bitcoin-core/secp256k1/issues/257
+ * which this test is a regression test for.
*
* These points were generated in sage as
* # secp256k1 params
@@ -2051,15 +2179,16 @@ void run_ec_combine(void) {
void test_group_decompress(const secp256k1_fe* x) {
/* The input itself, normalized. */
secp256k1_fe fex = *x;
- secp256k1_fe tmp;
+ secp256k1_fe fez;
/* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */
secp256k1_ge ge_quad, ge_even, ge_odd;
+ secp256k1_gej gej_quad;
/* Return values of the above calls. */
int res_quad, res_even, res_odd;
secp256k1_fe_normalize_var(&fex);
- res_quad = secp256k1_ge_set_xquad_var(&ge_quad, &fex);
+ res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex);
res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0);
res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1);
@@ -2085,13 +2214,29 @@ void test_group_decompress(const secp256k1_fe* x) {
CHECK(secp256k1_fe_equal_var(&ge_odd.x, x));
/* Check that the Y coordinate result in ge_quad is a square. */
- CHECK(secp256k1_fe_sqrt_var(&tmp, &ge_quad.y));
- secp256k1_fe_sqr(&tmp, &tmp);
- CHECK(secp256k1_fe_equal_var(&tmp, &ge_quad.y));
+ CHECK(secp256k1_fe_is_quad_var(&ge_quad.y));
/* Check odd/even Y in ge_odd, ge_even. */
CHECK(secp256k1_fe_is_odd(&ge_odd.y));
CHECK(!secp256k1_fe_is_odd(&ge_even.y));
+
+ /* Check secp256k1_gej_has_quad_y_var. */
+ secp256k1_gej_set_ge(&gej_quad, &ge_quad);
+ CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
+ do {
+ random_fe_test(&fez);
+ } while (secp256k1_fe_is_zero(&fez));
+ secp256k1_gej_rescale(&gej_quad, &fez);
+ CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
+ secp256k1_gej_neg(&gej_quad, &gej_quad);
+ CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
+ do {
+ random_fe_test(&fez);
+ } while (secp256k1_fe_is_zero(&fez));
+ secp256k1_gej_rescale(&gej_quad, &fez);
+ CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
+ secp256k1_gej_neg(&gej_quad, &gej_quad);
+ CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
}
}
@@ -2383,9 +2528,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar x, shift;
int wnaf[256] = {0};
int i;
-#ifdef USE_ENDOMORPHISM
int skew;
-#endif
secp256k1_scalar num = *number;
secp256k1_scalar_set_int(&x, 0);
@@ -2395,10 +2538,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
- skew = secp256k1_wnaf_const(wnaf, num, w);
-#else
- secp256k1_wnaf_const(wnaf, num, w);
#endif
+ skew = secp256k1_wnaf_const(wnaf, num, w);
for (i = WNAF_SIZE(w); i >= 0; --i) {
secp256k1_scalar t;
@@ -2417,10 +2558,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
}
secp256k1_scalar_add(&x, &x, &t);
}
-#ifdef USE_ENDOMORPHISM
- /* Skew num because when encoding 128-bit numbers as odd we use an offset */
+ /* Skew num because when encoding numbers as odd we use an offset */
secp256k1_scalar_cadd_bit(&num, skew == 2, 1);
-#endif
CHECK(secp256k1_scalar_eq(&x, &num));
}
@@ -3484,12 +3623,14 @@ void run_ecdsa_end_to_end(void) {
int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) {
static const unsigned char zeroes[32] = {0};
+#ifdef ENABLE_OPENSSL_TESTS
static const unsigned char max_scalar[32] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40
};
+#endif
int ret = 0;
@@ -3607,13 +3748,13 @@ static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) {
static void damage_array(unsigned char *sig, size_t *len) {
int pos;
int action = secp256k1_rand_bits(3);
- if (action < 1) {
+ if (action < 1 && *len > 3) {
/* Delete a byte. */
pos = secp256k1_rand_int(*len);
memmove(sig + pos, sig + pos + 1, *len - pos - 1);
(*len)--;
return;
- } else if (action < 2) {
+ } else if (action < 2 && *len < 2048) {
/* Insert a byte. */
pos = secp256k1_rand_int(1 + *len);
memmove(sig + pos + 1, sig + pos, *len - pos);
@@ -3785,6 +3926,7 @@ void run_ecdsa_der_parse(void) {
int certainly_der = 0;
int certainly_not_der = 0;
random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der);
+ CHECK(buflen <= 2048);
for (j = 0; j < 16; j++) {
int ret = 0;
if (j > 0) {
diff --git a/src/serialize.h b/src/serialize.h
index 378ed39074..04ab9aa2e7 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -322,8 +322,8 @@ uint64_t ReadCompactSize(Stream& is)
* 0: [0x00] 256: [0x81 0x00]
* 1: [0x01] 16383: [0xFE 0x7F]
* 127: [0x7F] 16384: [0xFF 0x00]
- * 128: [0x80 0x00] 16511: [0x80 0xFF 0x7F]
- * 255: [0x80 0x7F] 65535: [0x82 0xFD 0x7F]
+ * 128: [0x80 0x00] 16511: [0xFF 0x7F]
+ * 255: [0x80 0x7F] 65535: [0x82 0xFE 0x7F]
* 2^32: [0x8E 0xFE 0xFE 0xFF 0x00]
*/
diff --git a/src/test/README.md b/src/test/README.md
index b2d6be14f1..61462642bf 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -5,18 +5,15 @@ sense to simply use this framework rather than require developers to
configure some other framework (we want as few impediments to creating
unit tests as possible).
-The build system is setup to compile an executable called "test_bitcoin"
+The build system is setup to compile an executable called `test_bitcoin`
that runs all of the unit tests. The main source file is called
-test_bitcoin.cpp, which simply includes other files that contain the
-actual unit tests (outside of a couple required preprocessor
-directives). The pattern is to create one test file for each class or
-source file for which you want to create unit tests. The file naming
-convention is "<source_filename>_tests.cpp" and such files should wrap
-their tests in a test suite called "<source_filename>_tests". For an
-examples of this pattern, examine uint160_tests.cpp and
-uint256_tests.cpp.
-
-Add the source files to /src/Makefile.test.include to add them to the build.
+test_bitcoin.cpp. To add a new unit test file to our test suite you need
+to add the file to `src/Makefile.test.include`. The pattern is to create
+one test file for each class or source file for which you want to create
+unit tests. The file naming convention is `<source_filename>_tests.cpp`
+and such files should wrap their tests in a test suite
+called `<source_filename>_tests`. For an example of this pattern,
+examine `uint256_tests.cpp`.
For further reading, I found the following website to be helpful in
explaining how the boost unit test framework works:
@@ -31,5 +28,5 @@ example, to run just the getarg_tests verbosely:
test_bitcoin --run_test=getarg_tests/doubledash
-Run test_bitcoin --help for the full list.
+Run `test_bitcoin --help` for the full list.
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index b6cec24b57..5f150e4812 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -7,6 +7,7 @@
#include <boost/test/unit_test.hpp>
#include "hash.h"
+#include "netbase.h"
#include "random.h"
using namespace std;
@@ -50,6 +51,30 @@ public:
}
};
+static CNetAddr ResolveIP(const char* ip)
+{
+ CNetAddr addr;
+ BOOST_CHECK_MESSAGE(LookupHost(ip, addr, false), strprintf("failed to resolve: %s", ip));
+ return addr;
+}
+
+static CNetAddr ResolveIP(std::string ip)
+{
+ return ResolveIP(ip.c_str());
+}
+
+static CService ResolveService(const char* ip, int port = 0)
+{
+ CService serv;
+ BOOST_CHECK_MESSAGE(Lookup(ip, serv, port, false), strprintf("failed to resolve: %s:%i", ip, port));
+ return serv;
+}
+
+static CService ResolveService(std::string ip, int port = 0)
+{
+ return ResolveService(ip.c_str(), port);
+}
+
BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(addrman_simple)
@@ -59,7 +84,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- CNetAddr source = CNetAddr("252.2.2.2");
+ CNetAddr source = ResolveIP("252.2.2.2");
// Test 1: Does Addrman respond correctly when empty.
BOOST_CHECK(addrman.size() == 0);
@@ -67,7 +92,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_CHECK(addr_null.ToString() == "[::]:0");
// Test 2: Does Addrman::Add work as expected.
- CService addr1 = CService("250.1.1.1", 8333);
+ CService addr1 = ResolveService("250.1.1.1", 8333);
addrman.Add(CAddress(addr1, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 1);
CAddrInfo addr_ret1 = addrman.Select();
@@ -75,14 +100,14 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
// Test 3: Does IP address deduplication work correctly.
// Expected dup IP should not be added.
- CService addr1_dup = CService("250.1.1.1", 8333);
+ CService addr1_dup = ResolveService("250.1.1.1", 8333);
addrman.Add(CAddress(addr1_dup, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 1);
// Test 5: New table has one addr and we add a diff addr we should
// have two addrs.
- CService addr2 = CService("250.1.1.2", 8333);
+ CService addr2 = ResolveService("250.1.1.2", 8333);
addrman.Add(CAddress(addr2, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 2);
@@ -100,16 +125,16 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- CNetAddr source = CNetAddr("252.2.2.2");
+ CNetAddr source = ResolveIP("252.2.2.2");
BOOST_CHECK(addrman.size() == 0);
// Test 7; Addr with same IP but diff port does not replace existing addr.
- CService addr1 = CService("250.1.1.1", 8333);
+ CService addr1 = ResolveService("250.1.1.1", 8333);
addrman.Add(CAddress(addr1, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 1);
- CService addr1_port = CService("250.1.1.1", 8334);
+ CService addr1_port = ResolveService("250.1.1.1", 8334);
addrman.Add(CAddress(addr1_port, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 1);
CAddrInfo addr_ret2 = addrman.Select();
@@ -132,10 +157,10 @@ BOOST_AUTO_TEST_CASE(addrman_select)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- CNetAddr source = CNetAddr("252.2.2.2");
+ CNetAddr source = ResolveIP("252.2.2.2");
// Test 9: Select from new with 1 addr in new.
- CService addr1 = CService("250.1.1.1", 8333);
+ CService addr1 = ResolveService("250.1.1.1", 8333);
addrman.Add(CAddress(addr1, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 1);
@@ -156,24 +181,24 @@ BOOST_AUTO_TEST_CASE(addrman_select)
// Add three addresses to new table.
- CService addr2 = CService("250.3.1.1", 8333);
- CService addr3 = CService("250.3.2.2", 9999);
- CService addr4 = CService("250.3.3.3", 9999);
+ CService addr2 = ResolveService("250.3.1.1", 8333);
+ CService addr3 = ResolveService("250.3.2.2", 9999);
+ CService addr4 = ResolveService("250.3.3.3", 9999);
- addrman.Add(CAddress(addr2, NODE_NONE), CService("250.3.1.1", 8333));
- addrman.Add(CAddress(addr3, NODE_NONE), CService("250.3.1.1", 8333));
- addrman.Add(CAddress(addr4, NODE_NONE), CService("250.4.1.1", 8333));
+ addrman.Add(CAddress(addr2, NODE_NONE), ResolveService("250.3.1.1", 8333));
+ addrman.Add(CAddress(addr3, NODE_NONE), ResolveService("250.3.1.1", 8333));
+ addrman.Add(CAddress(addr4, NODE_NONE), ResolveService("250.4.1.1", 8333));
// Add three addresses to tried table.
- CService addr5 = CService("250.4.4.4", 8333);
- CService addr6 = CService("250.4.5.5", 7777);
- CService addr7 = CService("250.4.6.6", 8333);
+ CService addr5 = ResolveService("250.4.4.4", 8333);
+ CService addr6 = ResolveService("250.4.5.5", 7777);
+ CService addr7 = ResolveService("250.4.6.6", 8333);
- addrman.Add(CAddress(addr5, NODE_NONE), CService("250.3.1.1", 8333));
+ addrman.Add(CAddress(addr5, NODE_NONE), ResolveService("250.3.1.1", 8333));
addrman.Good(CAddress(addr5, NODE_NONE));
- addrman.Add(CAddress(addr6, NODE_NONE), CService("250.3.1.1", 8333));
+ addrman.Add(CAddress(addr6, NODE_NONE), ResolveService("250.3.1.1", 8333));
addrman.Good(CAddress(addr6, NODE_NONE));
- addrman.Add(CAddress(addr7, NODE_NONE), CService("250.1.1.3", 8333));
+ addrman.Add(CAddress(addr7, NODE_NONE), ResolveService("250.1.1.3", 8333));
addrman.Good(CAddress(addr7, NODE_NONE));
// Test 11: 6 addrs + 1 addr from last test = 7.
@@ -193,12 +218,12 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- CNetAddr source = CNetAddr("252.2.2.2");
+ CNetAddr source = ResolveIP("252.2.2.2");
BOOST_CHECK(addrman.size() == 0);
for (unsigned int i = 1; i < 18; i++) {
- CService addr = CService("250.1.1." + boost::to_string(i));
+ CService addr = ResolveService("250.1.1." + boost::to_string(i));
addrman.Add(CAddress(addr, NODE_NONE), source);
//Test 13: No collision in new table yet.
@@ -206,11 +231,11 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
}
//Test 14: new table collision!
- CService addr1 = CService("250.1.1.18");
+ CService addr1 = ResolveService("250.1.1.18");
addrman.Add(CAddress(addr1, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 17);
- CService addr2 = CService("250.1.1.19");
+ CService addr2 = ResolveService("250.1.1.19");
addrman.Add(CAddress(addr2, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 18);
}
@@ -222,12 +247,12 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- CNetAddr source = CNetAddr("252.2.2.2");
+ CNetAddr source = ResolveIP("252.2.2.2");
BOOST_CHECK(addrman.size() == 0);
for (unsigned int i = 1; i < 80; i++) {
- CService addr = CService("250.1.1." + boost::to_string(i));
+ CService addr = ResolveService("250.1.1." + boost::to_string(i));
addrman.Add(CAddress(addr, NODE_NONE), source);
addrman.Good(CAddress(addr, NODE_NONE));
@@ -237,11 +262,11 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
}
//Test 16: tried table collision!
- CService addr1 = CService("250.1.1.80");
+ CService addr1 = ResolveService("250.1.1.80");
addrman.Add(CAddress(addr1, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 79);
- CService addr2 = CService("250.1.1.81");
+ CService addr2 = ResolveService("250.1.1.81");
addrman.Add(CAddress(addr2, NODE_NONE), source);
BOOST_CHECK(addrman.size() == 80);
}
@@ -255,12 +280,12 @@ BOOST_AUTO_TEST_CASE(addrman_find)
BOOST_CHECK(addrman.size() == 0);
- CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE);
- CAddress addr2 = CAddress(CService("250.1.2.1", 9999), NODE_NONE);
- CAddress addr3 = CAddress(CService("251.255.2.1", 8333), NODE_NONE);
+ CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
+ CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
+ CAddress addr3 = CAddress(ResolveService("251.255.2.1", 8333), NODE_NONE);
- CNetAddr source1 = CNetAddr("250.1.2.1");
- CNetAddr source2 = CNetAddr("250.1.2.2");
+ CNetAddr source1 = ResolveIP("250.1.2.1");
+ CNetAddr source2 = ResolveIP("250.1.2.2");
addrman.Add(addr1, source1);
addrman.Add(addr2, source2);
@@ -294,8 +319,8 @@ BOOST_AUTO_TEST_CASE(addrman_create)
BOOST_CHECK(addrman.size() == 0);
- CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE);
- CNetAddr source1 = CNetAddr("250.1.2.1");
+ CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
+ CNetAddr source1 = ResolveIP("250.1.2.1");
int nId;
CAddrInfo* pinfo = addrman.Create(addr1, source1, &nId);
@@ -317,8 +342,8 @@ BOOST_AUTO_TEST_CASE(addrman_delete)
BOOST_CHECK(addrman.size() == 0);
- CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE);
- CNetAddr source1 = CNetAddr("250.1.2.1");
+ CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
+ CNetAddr source1 = ResolveIP("250.1.2.1");
int nId;
addrman.Create(addr1, source1, &nId);
@@ -344,18 +369,18 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
vector<CAddress> vAddr1 = addrman.GetAddr();
BOOST_CHECK(vAddr1.size() == 0);
- CAddress addr1 = CAddress(CService("250.250.2.1", 8333), NODE_NONE);
+ CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false
- CAddress addr2 = CAddress(CService("250.251.2.2", 9999), NODE_NONE);
+ CAddress addr2 = CAddress(ResolveService("250.251.2.2", 9999), NODE_NONE);
addr2.nTime = GetAdjustedTime();
- CAddress addr3 = CAddress(CService("251.252.2.3", 8333), NODE_NONE);
+ CAddress addr3 = CAddress(ResolveService("251.252.2.3", 8333), NODE_NONE);
addr3.nTime = GetAdjustedTime();
- CAddress addr4 = CAddress(CService("252.253.3.4", 8333), NODE_NONE);
+ CAddress addr4 = CAddress(ResolveService("252.253.3.4", 8333), NODE_NONE);
addr4.nTime = GetAdjustedTime();
- CAddress addr5 = CAddress(CService("252.254.4.5", 8333), NODE_NONE);
+ CAddress addr5 = CAddress(ResolveService("252.254.4.5", 8333), NODE_NONE);
addr5.nTime = GetAdjustedTime();
- CNetAddr source1 = CNetAddr("250.1.2.1");
- CNetAddr source2 = CNetAddr("250.2.3.3");
+ CNetAddr source1 = ResolveIP("250.1.2.1");
+ CNetAddr source2 = ResolveIP("250.2.3.3");
// Test 23: Ensure GetAddr works with new addresses.
addrman.Add(addr1, source1);
@@ -378,11 +403,11 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
int octet2 = (i / 256) % 256;
int octet3 = (i / (256 * 2)) % 256;
string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + "." + boost::to_string(octet3) + ".23";
- CAddress addr = CAddress(CService(strAddr), NODE_NONE);
+ CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE);
// Ensure that for all addrs in addrman, isTerrible == false.
addr.nTime = GetAdjustedTime();
- addrman.Add(addr, CNetAddr(strAddr));
+ addrman.Add(addr, ResolveIP(strAddr));
if (i % 8 == 0)
addrman.Good(addr);
}
@@ -403,10 +428,10 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- CAddress addr1 = CAddress(CService("250.1.1.1", 8333), NODE_NONE);
- CAddress addr2 = CAddress(CService("250.1.1.1", 9999), NODE_NONE);
+ CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
+ CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
- CNetAddr source1 = CNetAddr("250.1.1.1");
+ CNetAddr source1 = ResolveIP("250.1.1.1");
CAddrInfo info1 = CAddrInfo(addr1, source1);
@@ -431,8 +456,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
set<int> buckets;
for (int i = 0; i < 255; i++) {
CAddrInfo infoi = CAddrInfo(
- CAddress(CService("250.1.1." + boost::to_string(i)), NODE_NONE),
- CNetAddr("250.1.1." + boost::to_string(i)));
+ CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE),
+ ResolveIP("250.1.1." + boost::to_string(i)));
int bucket = infoi.GetTriedBucket(nKey1);
buckets.insert(bucket);
}
@@ -443,8 +468,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
buckets.clear();
for (int j = 0; j < 255; j++) {
CAddrInfo infoj = CAddrInfo(
- CAddress(CService("250." + boost::to_string(j) + ".1.1"), NODE_NONE),
- CNetAddr("250." + boost::to_string(j) + ".1.1"));
+ CAddress(ResolveService("250." + boost::to_string(j) + ".1.1"), NODE_NONE),
+ ResolveIP("250." + boost::to_string(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1);
buckets.insert(bucket);
}
@@ -460,10 +485,10 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
// Set addrman addr placement to be deterministic.
addrman.MakeDeterministic();
- CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE);
- CAddress addr2 = CAddress(CService("250.1.2.1", 9999), NODE_NONE);
+ CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
+ CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
- CNetAddr source1 = CNetAddr("250.1.2.1");
+ CNetAddr source1 = ResolveIP("250.1.2.1");
CAddrInfo info1 = CAddrInfo(addr1, source1);
@@ -484,8 +509,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
set<int> buckets;
for (int i = 0; i < 255; i++) {
CAddrInfo infoi = CAddrInfo(
- CAddress(CService("250.1.1." + boost::to_string(i)), NODE_NONE),
- CNetAddr("250.1.1." + boost::to_string(i)));
+ CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE),
+ ResolveIP("250.1.1." + boost::to_string(i)));
int bucket = infoi.GetNewBucket(nKey1);
buckets.insert(bucket);
}
@@ -496,9 +521,9 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
buckets.clear();
for (int j = 0; j < 4 * 255; j++) {
CAddrInfo infoj = CAddrInfo(CAddress(
- CService(
+ ResolveService(
boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE),
- CNetAddr("251.4.1.1"));
+ ResolveIP("251.4.1.1"));
int bucket = infoj.GetNewBucket(nKey1);
buckets.insert(bucket);
}
@@ -509,8 +534,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
buckets.clear();
for (int p = 0; p < 255; p++) {
CAddrInfo infoj = CAddrInfo(
- CAddress(CService("250.1.1.1"), NODE_NONE),
- CNetAddr("250." + boost::to_string(p) + ".1.1"));
+ CAddress(ResolveService("250.1.1.1"), NODE_NONE),
+ ResolveIP("250." + boost::to_string(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1);
buckets.insert(bucket);
}
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index 01eb2aee9e..ac3ab4c83f 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -121,7 +121,6 @@ public:
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
{
UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
- std::vector<unsigned char> result;
CBitcoinSecret secret;
CBitcoinAddress addr;
SelectParams(CBaseChainParams::MAIN);
@@ -179,7 +178,6 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
{
UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
- std::vector<unsigned char> result;
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
@@ -247,7 +245,6 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
{
UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases
- std::vector<unsigned char> result;
CBitcoinSecret secret;
CBitcoinAddress addr;
diff --git a/src/test/bitcoin-util-test.py b/src/test/bitcoin-util-test.py
index 95dd3e81b4..882b5c67b8 100755
--- a/src/test/bitcoin-util-test.py
+++ b/src/test/bitcoin-util-test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
# Copyright 2014 BitPay, Inc.
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/buildenv.py.in b/src/test/buildenv.py.in
index 1618bdeb76..153f34a3db 100644
--- a/src/test/buildenv.py.in
+++ b/src/test/buildenv.py.in
@@ -1,2 +1,2 @@
-#!/usr/bin/python
+#!/usr/bin/env python
exeext="@EXEEXT@"
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 82d61209b5..fa9624f13d 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -122,6 +122,10 @@ BOOST_AUTO_TEST_CASE(siphash)
hasher3.Write(uint64_t(x)|(uint64_t(x+1)<<8)|(uint64_t(x+2)<<16)|(uint64_t(x+3)<<24)|
(uint64_t(x+4)<<32)|(uint64_t(x+5)<<40)|(uint64_t(x+6)<<48)|(uint64_t(x+7)<<56));
}
+
+ CHashWriter ss(SER_DISK, CLIENT_VERSION);
+ ss << CTransaction();
+ BOOST_CHECK_EQUAL(SipHashUint256(1, 2, ss.GetHash()), 0x79751e980c2a0a35ULL);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index fd581db52e..15fceb963a 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -181,9 +181,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
- // Disable size accounting (CPFP does not support it)
- mapArgs["-blockmaxsize"] = strprintf("%u", MAX_BLOCK_SERIALIZED_SIZE);
-
+ // Note that by default, these tests run with size accounting enabled.
const CChainParams& chainparams = Params(CBaseChainParams::MAIN);
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
CBlockTemplate *pblocktemplate;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index d005d6a163..267d1b55e1 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -9,6 +9,7 @@
#include "serialize.h"
#include "streams.h"
#include "net.h"
+#include "netbase.h"
#include "chainparams.h"
using namespace std;
@@ -51,8 +52,12 @@ public:
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
s << nUBuckets;
- CAddress addr = CAddress(CService("252.1.1.1", 7777), NODE_NONE);
- CAddrInfo info = CAddrInfo(addr, CNetAddr("252.2.2.2"));
+ CService serv;
+ Lookup("252.1.1.1", serv, 7777, false);
+ CAddress addr = CAddress(serv, NODE_NONE);
+ CNetAddr resolved;
+ LookupHost("252.2.2.2", resolved, false);
+ CAddrInfo info = CAddrInfo(addr, resolved);
s << info;
}
};
@@ -74,14 +79,17 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
CAddrManUncorrupted addrmanUncorrupted;
addrmanUncorrupted.MakeDeterministic();
- CService addr1 = CService("250.7.1.1", 8333);
- CService addr2 = CService("250.7.2.2", 9999);
- CService addr3 = CService("250.7.3.3", 9999);
+ CService addr1, addr2, addr3;
+ Lookup("250.7.1.1", addr1, 8333, false);
+ Lookup("250.7.2.2", addr2, 9999, false);
+ Lookup("250.7.3.3", addr3, 9999, false);
// Add three addresses to new table.
- addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), CService("252.5.1.1", 8333));
- addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), CService("252.5.1.1", 8333));
- addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), CService("252.5.1.1", 8333));
+ CService source;
+ Lookup("252.5.1.1", source, 8333, false);
+ addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source);
+ addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source);
+ addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source);
// Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
@@ -142,4 +150,26 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
BOOST_CHECK(addrman2.size() == 0);
}
+BOOST_AUTO_TEST_CASE(cnode_simple_test)
+{
+ SOCKET hSocket = INVALID_SOCKET;
+
+ in_addr ipv4Addr;
+ ipv4Addr.s_addr = 0xa0b0c001;
+
+ CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
+ std::string pszDest = "";
+ bool fInboundIn = false;
+
+ // Test that fFeeler is false by default.
+ CNode* pnode1 = new CNode(hSocket, addr, pszDest, fInboundIn);
+ BOOST_CHECK(pnode1->fInbound == false);
+ BOOST_CHECK(pnode1->fFeeler == false);
+
+ fInboundIn = true;
+ CNode* pnode2 = new CNode(hSocket, addr, pszDest, fInboundIn);
+ BOOST_CHECK(pnode2->fInbound == true);
+ BOOST_CHECK(pnode2->fFeeler == false);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 4168f75e9a..18ad5dc90b 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -14,37 +14,54 @@ using namespace std;
BOOST_FIXTURE_TEST_SUITE(netbase_tests, BasicTestingSetup)
+static CNetAddr ResolveIP(const char* ip)
+{
+ CNetAddr addr;
+ LookupHost(ip, addr, false);
+ return addr;
+}
+
+static CSubNet ResolveSubNet(const char* subnet)
+{
+ CSubNet ret;
+ LookupSubNet(subnet, ret);
+ return ret;
+}
+
BOOST_AUTO_TEST_CASE(netbase_networks)
{
- BOOST_CHECK(CNetAddr("127.0.0.1").GetNetwork() == NET_UNROUTABLE);
- BOOST_CHECK(CNetAddr("::1").GetNetwork() == NET_UNROUTABLE);
- BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork() == NET_IPV4);
- BOOST_CHECK(CNetAddr("2001::8888").GetNetwork() == NET_IPV6);
- BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
+ BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE);
+ BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE);
+ BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4);
+ BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6);
+ BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
+
}
BOOST_AUTO_TEST_CASE(netbase_properties)
{
- BOOST_CHECK(CNetAddr("127.0.0.1").IsIPv4());
- BOOST_CHECK(CNetAddr("::FFFF:192.168.1.1").IsIPv4());
- BOOST_CHECK(CNetAddr("::1").IsIPv6());
- BOOST_CHECK(CNetAddr("10.0.0.1").IsRFC1918());
- BOOST_CHECK(CNetAddr("192.168.1.1").IsRFC1918());
- BOOST_CHECK(CNetAddr("172.31.255.255").IsRFC1918());
- BOOST_CHECK(CNetAddr("2001:0DB8::").IsRFC3849());
- BOOST_CHECK(CNetAddr("169.254.1.1").IsRFC3927());
- BOOST_CHECK(CNetAddr("2002::1").IsRFC3964());
- BOOST_CHECK(CNetAddr("FC00::").IsRFC4193());
- BOOST_CHECK(CNetAddr("2001::2").IsRFC4380());
- BOOST_CHECK(CNetAddr("2001:10::").IsRFC4843());
- BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
- BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052());
- BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
- BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal());
- BOOST_CHECK(CNetAddr("::1").IsLocal());
- BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
- BOOST_CHECK(CNetAddr("2001::1").IsRoutable());
- BOOST_CHECK(CNetAddr("127.0.0.1").IsValid());
+
+ BOOST_CHECK(ResolveIP("127.0.0.1").IsIPv4());
+ BOOST_CHECK(ResolveIP("::FFFF:192.168.1.1").IsIPv4());
+ BOOST_CHECK(ResolveIP("::1").IsIPv6());
+ BOOST_CHECK(ResolveIP("10.0.0.1").IsRFC1918());
+ BOOST_CHECK(ResolveIP("192.168.1.1").IsRFC1918());
+ BOOST_CHECK(ResolveIP("172.31.255.255").IsRFC1918());
+ BOOST_CHECK(ResolveIP("2001:0DB8::").IsRFC3849());
+ BOOST_CHECK(ResolveIP("169.254.1.1").IsRFC3927());
+ BOOST_CHECK(ResolveIP("2002::1").IsRFC3964());
+ BOOST_CHECK(ResolveIP("FC00::").IsRFC4193());
+ BOOST_CHECK(ResolveIP("2001::2").IsRFC4380());
+ BOOST_CHECK(ResolveIP("2001:10::").IsRFC4843());
+ BOOST_CHECK(ResolveIP("FE80::").IsRFC4862());
+ BOOST_CHECK(ResolveIP("64:FF9B::").IsRFC6052());
+ BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
+ BOOST_CHECK(ResolveIP("127.0.0.1").IsLocal());
+ BOOST_CHECK(ResolveIP("::1").IsLocal());
+ BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable());
+ BOOST_CHECK(ResolveIP("2001::1").IsRoutable());
+ BOOST_CHECK(ResolveIP("127.0.0.1").IsValid());
+
}
bool static TestSplitHost(string test, string host, int port)
@@ -76,9 +93,7 @@ BOOST_AUTO_TEST_CASE(netbase_splithost)
bool static TestParse(string src, string canon)
{
- CService addr;
- if (!LookupNumeric(src.c_str(), addr, 65535))
- return canon == "";
+ CService addr(LookupNumeric(src.c_str(), 65535));
return canon == addr.ToString();
}
@@ -90,165 +105,185 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
BOOST_CHECK(TestParse("::", "[::]:65535"));
BOOST_CHECK(TestParse("[::]:8333", "[::]:8333"));
BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535"));
- BOOST_CHECK(TestParse(":::", ""));
+ BOOST_CHECK(TestParse(":::", "[::]:0"));
}
BOOST_AUTO_TEST_CASE(onioncat_test)
{
+
// values from https://web.archive.org/web/20121122003543/http://www.cypherpunk.at/onioncat/wiki/OnionCat
- CNetAddr addr1("5wyqrzbvrdsumnok.onion");
- CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca");
+ CNetAddr addr1(ResolveIP("5wyqrzbvrdsumnok.onion"));
+ CNetAddr addr2(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca"));
BOOST_CHECK(addr1 == addr2);
BOOST_CHECK(addr1.IsTor());
BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
BOOST_CHECK(addr1.IsRoutable());
+
}
BOOST_AUTO_TEST_CASE(subnet_test)
{
- BOOST_CHECK(CSubNet("1.2.3.0/24") == CSubNet("1.2.3.0/255.255.255.0"));
- BOOST_CHECK(CSubNet("1.2.3.0/24") != CSubNet("1.2.4.0/255.255.255.0"));
- BOOST_CHECK(CSubNet("1.2.3.0/24").Match(CNetAddr("1.2.3.4")));
- BOOST_CHECK(!CSubNet("1.2.2.0/24").Match(CNetAddr("1.2.3.4")));
- BOOST_CHECK(CSubNet("1.2.3.4").Match(CNetAddr("1.2.3.4")));
- BOOST_CHECK(CSubNet("1.2.3.4/32").Match(CNetAddr("1.2.3.4")));
- BOOST_CHECK(!CSubNet("1.2.3.4").Match(CNetAddr("5.6.7.8")));
- BOOST_CHECK(!CSubNet("1.2.3.4/32").Match(CNetAddr("5.6.7.8")));
- BOOST_CHECK(CSubNet("::ffff:127.0.0.1").Match(CNetAddr("127.0.0.1")));
- BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8").Match(CNetAddr("1:2:3:4:5:6:7:8")));
- BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8").Match(CNetAddr("1:2:3:4:5:6:7:9")));
- BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:0/112").Match(CNetAddr("1:2:3:4:5:6:7:1234")));
- BOOST_CHECK(CSubNet("192.168.0.1/24").Match(CNetAddr("192.168.0.2")));
- BOOST_CHECK(CSubNet("192.168.0.20/29").Match(CNetAddr("192.168.0.18")));
- BOOST_CHECK(CSubNet("1.2.2.1/24").Match(CNetAddr("1.2.2.4")));
- BOOST_CHECK(CSubNet("1.2.2.110/31").Match(CNetAddr("1.2.2.111")));
- BOOST_CHECK(CSubNet("1.2.2.20/26").Match(CNetAddr("1.2.2.63")));
+
+ BOOST_CHECK(ResolveSubNet("1.2.3.0/24") == ResolveSubNet("1.2.3.0/255.255.255.0"));
+ BOOST_CHECK(ResolveSubNet("1.2.3.0/24") != ResolveSubNet("1.2.4.0/255.255.255.0"));
+ BOOST_CHECK(ResolveSubNet("1.2.3.0/24").Match(ResolveIP("1.2.3.4")));
+ BOOST_CHECK(!ResolveSubNet("1.2.2.0/24").Match(ResolveIP("1.2.3.4")));
+ BOOST_CHECK(ResolveSubNet("1.2.3.4").Match(ResolveIP("1.2.3.4")));
+ BOOST_CHECK(ResolveSubNet("1.2.3.4/32").Match(ResolveIP("1.2.3.4")));
+ BOOST_CHECK(!ResolveSubNet("1.2.3.4").Match(ResolveIP("5.6.7.8")));
+ BOOST_CHECK(!ResolveSubNet("1.2.3.4/32").Match(ResolveIP("5.6.7.8")));
+ BOOST_CHECK(ResolveSubNet("::ffff:127.0.0.1").Match(ResolveIP("127.0.0.1")));
+ BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8").Match(ResolveIP("1:2:3:4:5:6:7:8")));
+ BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8").Match(ResolveIP("1:2:3:4:5:6:7:9")));
+ BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:0/112").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
+ BOOST_CHECK(ResolveSubNet("192.168.0.1/24").Match(ResolveIP("192.168.0.2")));
+ BOOST_CHECK(ResolveSubNet("192.168.0.20/29").Match(ResolveIP("192.168.0.18")));
+ BOOST_CHECK(ResolveSubNet("1.2.2.1/24").Match(ResolveIP("1.2.2.4")));
+ BOOST_CHECK(ResolveSubNet("1.2.2.110/31").Match(ResolveIP("1.2.2.111")));
+ BOOST_CHECK(ResolveSubNet("1.2.2.20/26").Match(ResolveIP("1.2.2.63")));
// All-Matching IPv6 Matches arbitrary IPv4 and IPv6
- BOOST_CHECK(CSubNet("::/0").Match(CNetAddr("1:2:3:4:5:6:7:1234")));
- BOOST_CHECK(CSubNet("::/0").Match(CNetAddr("1.2.3.4")));
+ BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
+ BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
// All-Matching IPv4 does not Match IPv6
- BOOST_CHECK(!CSubNet("0.0.0.0/0").Match(CNetAddr("1:2:3:4:5:6:7:1234")));
+ BOOST_CHECK(!ResolveSubNet("0.0.0.0/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
// Invalid subnets Match nothing (not even invalid addresses)
- BOOST_CHECK(!CSubNet().Match(CNetAddr("1.2.3.4")));
- BOOST_CHECK(!CSubNet("").Match(CNetAddr("4.5.6.7")));
- BOOST_CHECK(!CSubNet("bloop").Match(CNetAddr("0.0.0.0")));
- BOOST_CHECK(!CSubNet("bloop").Match(CNetAddr("hab")));
+ BOOST_CHECK(!CSubNet().Match(ResolveIP("1.2.3.4")));
+ BOOST_CHECK(!ResolveSubNet("").Match(ResolveIP("4.5.6.7")));
+ BOOST_CHECK(!ResolveSubNet("bloop").Match(ResolveIP("0.0.0.0")));
+ BOOST_CHECK(!ResolveSubNet("bloop").Match(ResolveIP("hab")));
// Check valid/invalid
- BOOST_CHECK(CSubNet("1.2.3.0/0").IsValid());
- BOOST_CHECK(!CSubNet("1.2.3.0/-1").IsValid());
- BOOST_CHECK(CSubNet("1.2.3.0/32").IsValid());
- BOOST_CHECK(!CSubNet("1.2.3.0/33").IsValid());
- BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/0").IsValid());
- BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/33").IsValid());
- BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8/-1").IsValid());
- BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/128").IsValid());
- BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8/129").IsValid());
- BOOST_CHECK(!CSubNet("fuzzy").IsValid());
+ BOOST_CHECK(ResolveSubNet("1.2.3.0/0").IsValid());
+ BOOST_CHECK(!ResolveSubNet("1.2.3.0/-1").IsValid());
+ BOOST_CHECK(ResolveSubNet("1.2.3.0/32").IsValid());
+ BOOST_CHECK(!ResolveSubNet("1.2.3.0/33").IsValid());
+ BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/0").IsValid());
+ BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/33").IsValid());
+ BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8/-1").IsValid());
+ BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/128").IsValid());
+ BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8/129").IsValid());
+ BOOST_CHECK(!ResolveSubNet("fuzzy").IsValid());
//CNetAddr constructor test
- BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).IsValid());
- BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.1")));
- BOOST_CHECK(!CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.2")));
- BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).ToString() == "127.0.0.1/32");
+ BOOST_CHECK(CSubNet(ResolveIP("127.0.0.1")).IsValid());
+ BOOST_CHECK(CSubNet(ResolveIP("127.0.0.1")).Match(ResolveIP("127.0.0.1")));
+ BOOST_CHECK(!CSubNet(ResolveIP("127.0.0.1")).Match(ResolveIP("127.0.0.2")));
+ BOOST_CHECK(CSubNet(ResolveIP("127.0.0.1")).ToString() == "127.0.0.1/32");
- BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).IsValid());
- BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:8")));
- BOOST_CHECK(!CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:9")));
- BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128");
+ CSubNet subnet = CSubNet(ResolveIP("1.2.3.4"), 32);
+ BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
+ subnet = CSubNet(ResolveIP("1.2.3.4"), 8);
+ BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8");
+ subnet = CSubNet(ResolveIP("1.2.3.4"), 0);
+ BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0");
- CSubNet subnet = CSubNet("1.2.3.4/255.255.255.255");
+ subnet = CSubNet(ResolveIP("1.2.3.4"), ResolveIP("255.255.255.255"));
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
- subnet = CSubNet("1.2.3.4/255.255.255.254");
+ subnet = CSubNet(ResolveIP("1.2.3.4"), ResolveIP("255.0.0.0"));
+ BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8");
+ subnet = CSubNet(ResolveIP("1.2.3.4"), ResolveIP("0.0.0.0"));
+ BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0");
+
+ BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).IsValid());
+ BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:8")));
+ BOOST_CHECK(!CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:9")));
+ BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128");
+
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.255");
+ BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.254");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/31");
- subnet = CSubNet("1.2.3.4/255.255.255.252");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.252");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/30");
- subnet = CSubNet("1.2.3.4/255.255.255.248");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.248");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/29");
- subnet = CSubNet("1.2.3.4/255.255.255.240");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.240");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/28");
- subnet = CSubNet("1.2.3.4/255.255.255.224");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.224");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/27");
- subnet = CSubNet("1.2.3.4/255.255.255.192");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.192");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/26");
- subnet = CSubNet("1.2.3.4/255.255.255.128");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.128");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/25");
- subnet = CSubNet("1.2.3.4/255.255.255.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.255.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/24");
- subnet = CSubNet("1.2.3.4/255.255.254.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.254.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.2.0/23");
- subnet = CSubNet("1.2.3.4/255.255.252.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.252.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/22");
- subnet = CSubNet("1.2.3.4/255.255.248.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.248.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/21");
- subnet = CSubNet("1.2.3.4/255.255.240.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.240.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/20");
- subnet = CSubNet("1.2.3.4/255.255.224.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.224.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/19");
- subnet = CSubNet("1.2.3.4/255.255.192.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.192.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/18");
- subnet = CSubNet("1.2.3.4/255.255.128.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.128.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/17");
- subnet = CSubNet("1.2.3.4/255.255.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/16");
- subnet = CSubNet("1.2.3.4/255.254.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.254.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/15");
- subnet = CSubNet("1.2.3.4/255.252.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.252.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/14");
- subnet = CSubNet("1.2.3.4/255.248.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.248.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/13");
- subnet = CSubNet("1.2.3.4/255.240.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.240.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/12");
- subnet = CSubNet("1.2.3.4/255.224.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.224.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/11");
- subnet = CSubNet("1.2.3.4/255.192.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.192.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/10");
- subnet = CSubNet("1.2.3.4/255.128.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.128.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/9");
- subnet = CSubNet("1.2.3.4/255.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/255.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8");
- subnet = CSubNet("1.2.3.4/254.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/254.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/7");
- subnet = CSubNet("1.2.3.4/252.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/252.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/6");
- subnet = CSubNet("1.2.3.4/248.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/248.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/5");
- subnet = CSubNet("1.2.3.4/240.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/240.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/4");
- subnet = CSubNet("1.2.3.4/224.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/224.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/3");
- subnet = CSubNet("1.2.3.4/192.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/192.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/2");
- subnet = CSubNet("1.2.3.4/128.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/128.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/1");
- subnet = CSubNet("1.2.3.4/0.0.0.0");
+ subnet = ResolveSubNet("1.2.3.4/0.0.0.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0");
- subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ subnet = ResolveSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/128");
- subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:0000:0000:0000:0000:0000:0000:0000");
+ subnet = ResolveSubNet("1:2:3:4:5:6:7:8/ffff:0000:0000:0000:0000:0000:0000:0000");
BOOST_CHECK_EQUAL(subnet.ToString(), "1::/16");
- subnet = CSubNet("1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000");
+ subnet = ResolveSubNet("1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000");
BOOST_CHECK_EQUAL(subnet.ToString(), "::/0");
- subnet = CSubNet("1.2.3.4/255.255.232.0");
+ subnet = ResolveSubNet("1.2.3.4/255.255.232.0");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/255.255.232.0");
- subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
+ subnet = ResolveSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
+
}
BOOST_AUTO_TEST_CASE(netbase_getgroup)
{
- BOOST_CHECK(CNetAddr("127.0.0.1").GetGroup() == boost::assign::list_of(0)); // Local -> !Routable()
- BOOST_CHECK(CNetAddr("257.0.0.1").GetGroup() == boost::assign::list_of(0)); // !Valid -> !Routable()
- BOOST_CHECK(CNetAddr("10.0.0.1").GetGroup() == boost::assign::list_of(0)); // RFC1918 -> !Routable()
- BOOST_CHECK(CNetAddr("169.254.1.1").GetGroup() == boost::assign::list_of(0)); // RFC3927 -> !Routable()
- BOOST_CHECK(CNetAddr("1.2.3.4").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // IPv4
- BOOST_CHECK(CNetAddr("::FFFF:0:102:304").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC6145
- BOOST_CHECK(CNetAddr("64:FF9B::102:304").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC6052
- BOOST_CHECK(CNetAddr("2002:102:304:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC3964
- BOOST_CHECK(CNetAddr("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC4380
- BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == boost::assign::list_of((unsigned char)NET_TOR)(239)); // Tor
- BOOST_CHECK(CNetAddr("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(4)(112)(175)); //he.net
- BOOST_CHECK(CNetAddr("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(32)(1)); //IPv6
+
+ BOOST_CHECK(ResolveIP("127.0.0.1").GetGroup() == boost::assign::list_of(0)); // Local -> !Routable()
+ BOOST_CHECK(ResolveIP("257.0.0.1").GetGroup() == boost::assign::list_of(0)); // !Valid -> !Routable()
+ BOOST_CHECK(ResolveIP("10.0.0.1").GetGroup() == boost::assign::list_of(0)); // RFC1918 -> !Routable()
+ BOOST_CHECK(ResolveIP("169.254.1.1").GetGroup() == boost::assign::list_of(0)); // RFC3927 -> !Routable()
+ BOOST_CHECK(ResolveIP("1.2.3.4").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // IPv4
+ BOOST_CHECK(ResolveIP("::FFFF:0:102:304").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC6145
+ BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC6052
+ BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC3964
+ BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV4)(1)(2)); // RFC4380
+ BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == boost::assign::list_of((unsigned char)NET_TOR)(239)); // Tor
+ BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(4)(112)(175)); //he.net
+ BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == boost::assign::list_of((unsigned char)NET_IPV6)(32)(1)(32)(1)); //IPv6
+
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index 74ffe0cc74..e9c8691745 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -122,7 +122,6 @@ BOOST_AUTO_TEST_CASE(pmt_malleability)
std::vector<bool> vMatch = boost::assign::list_of(false)(false)(false)(false)(false)(false)(false)(false)(false)(true)(true)(false);
CPartialMerkleTree tree(vTxid, vMatch);
- std::vector<uint256> vTxid2;
std::vector<unsigned int> vIndex;
BOOST_CHECK(tree.ExtractMatches(vTxid, vIndex).IsNull());
}
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index bbda6a48f4..a15915aad2 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -18,18 +18,6 @@
using namespace std;
-UniValue
-createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL)
-{
- UniValue result(UniValue::VARR);
- result.push_back(nRequired);
- UniValue addresses(UniValue::VARR);
- if (address1) addresses.push_back(address1);
- if (address2) addresses.push_back(address2);
- result.push_back(addresses);
- return result;
-}
-
UniValue CallRPC(string args)
{
vector<string> vArgs;
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 856f9b8423..056f2982cf 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -60,6 +60,11 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
InitBlockIndex(chainparams);
+ {
+ CValidationState state;
+ bool ok = ActivateBestChain(state, chainparams);
+ BOOST_CHECK(ok);
+ }
nScriptCheckThreads = 3;
for (int i=0; i < nScriptCheckThreads-1; i++)
threadGroup.create_thread(&ThreadScriptCheck);
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index e467a4171d..efd4498747 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -243,11 +243,7 @@ BOOST_AUTO_TEST_CASE(util_IsHex)
BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
{
- int i;
- int count=0;
-
seed_insecure_rand(true);
-
for (int mod=2;mod<11;mod++)
{
int mask = 1;
@@ -256,10 +252,9 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
//mask is 2^ceil(log2(mod))-1
while(mask<mod-1)mask=(mask<<1)+1;
- count = 0;
+ int count = 0;
//How often does it get a zero from the uniform range [0,mod)?
- for (i=0;i<10000;i++)
- {
+ for (int i = 0; i < 10000; i++) {
uint32_t rval;
do{
rval=insecure_rand()&mask;
diff --git a/src/timedata.cpp b/src/timedata.cpp
index b6bcf86fbf..25fc494121 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -8,7 +8,7 @@
#include "timedata.h"
-#include "netbase.h"
+#include "netaddress.h"
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
diff --git a/src/tinyformat.h b/src/tinyformat.h
index c6ec0419b3..17f0360c42 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -67,7 +67,9 @@
// weekday, month, day, hour, min);
// std::cout << date;
//
-// These are the three primary interface functions.
+// These are the three primary interface functions. There is also a
+// convenience function printfln() which appends a newline to the usual result
+// of printf() for super simple logging.
//
//
// User defined format functions
@@ -86,6 +88,18 @@
// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an
// example, see the implementation of printf() at the end of the source file.
//
+// Sometimes it's useful to be able to pass a list of format arguments through
+// to a non-template function. The FormatList class is provided as a way to do
+// this by storing the argument list in a type-opaque way. Continuing the
+// example from above, we construct a FormatList using makeFormatList():
+//
+// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min);
+//
+// The format list can now be passed into any non-template function and used
+// via a call to the vformat() function:
+//
+// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
+//
//
// Additional API information
// --------------------------
@@ -118,6 +132,7 @@ namespace tfm = tinyformat;
//------------------------------------------------------------------------------
// Implementation details.
+#include <algorithm>
#include <cassert>
#include <iostream>
#include <sstream>
@@ -133,20 +148,20 @@ namespace tfm = tinyformat;
# endif
#endif
-#ifdef __GNUC__
-# define TINYFORMAT_NOINLINE __attribute__((noinline))
-#elif defined(_MSC_VER)
-# define TINYFORMAT_NOINLINE __declspec(noinline)
-#else
-# define TINYFORMAT_NOINLINE
-#endif
-
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
// std::showpos is broken on old libstdc++ as provided with OSX. See
// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
#endif
+#ifdef __APPLE__
+// Workaround OSX linker warning: xcode uses different default symbol
+// visibilities for static libs vs executables (see issue #25)
+# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
+#else
+# define TINYFORMAT_HIDDEN
+#endif
+
namespace tinyformat {
//------------------------------------------------------------------------------
@@ -247,6 +262,29 @@ struct convertToInt<T,true>
static int invoke(const T& value) { return static_cast<int>(value); }
};
+// Format at most ntrunc characters to the given stream.
+template<typename T>
+inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
+{
+ std::ostringstream tmp;
+ tmp << value;
+ std::string result = tmp.str();
+ out.write(result.c_str(), (std::min)(ntrunc, static_cast<int>(result.size())));
+}
+#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \
+inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
+{ \
+ std::streamsize len = 0; \
+ while(len < ntrunc && value[len] != 0) \
+ ++len; \
+ out.write(value, len); \
+}
+// Overload for const char* and char*. Could overload for signed & unsigned
+// char too, but these are technically unneeded for printf compatibility.
+TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
+TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
+#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR
+
} // namespace detail
@@ -255,18 +293,20 @@ struct convertToInt<T,true>
// desired.
-// Format a value into a stream. Called from format() for all types by default.
-//
-// Users may override this for their own types. When this function is called,
-// the stream flags will have been modified according to the format string.
-// The format specification is provided in the range [fmtBegin, fmtEnd).
-//
-// By default, formatValue() uses the usual stream insertion operator
-// operator<< to format the type T, with special cases for the %c and %p
-// conversions.
+/// Format a value into a stream, delegating to operator<< by default.
+///
+/// Users may override this for their own types. When this function is called,
+/// the stream flags will have been modified according to the format string.
+/// The format specification is provided in the range [fmtBegin, fmtEnd). For
+/// truncating conversions, ntrunc is set to the desired maximum number of
+/// characters, for example "%.7s" calls formatValue with ntrunc = 7.
+///
+/// By default, formatValue() uses the usual stream insertion operator
+/// operator<< to format the type T, with special cases for the %c and %p
+/// conversions.
template<typename T>
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
- const char* fmtEnd, const T& value)
+ const char* fmtEnd, int ntrunc, const T& value)
{
#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS
// Since we don't support printing of wchar_t using "%ls", make it fail at
@@ -288,6 +328,12 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
#endif
+ else if(ntrunc >= 0)
+ {
+ // Take care not to overread C strings in truncating conversions like
+ // "%.4s" where at most 4 characters may be read.
+ detail::formatTruncated(out, value, ntrunc);
+ }
else
out << value;
}
@@ -296,7 +342,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
// Overloaded version for char types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
- const char* fmtEnd, charType value) \
+ const char* fmtEnd, int /**/, charType value) \
{ \
switch(*(fmtEnd-1)) \
{ \
@@ -435,225 +481,91 @@ cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' +
namespace detail {
-// Class holding current position in format string and an output stream into
-// which arguments are formatted.
-class FormatIterator
+// Type-opaque holder for an argument to format(), with associated actions on
+// the type held as explicit function pointers. This allows FormatArg's for
+// each argument to be allocated as a homogenous array inside FormatList
+// whereas a naive implementation based on inheritance does not.
+class FormatArg
{
public:
- // Flags for features not representable with standard stream state
- enum ExtraFormatFlags
- {
- Flag_None = 0,
- Flag_TruncateToPrecision = 1<<0, // truncate length to stream precision()
- Flag_SpacePadPositive = 1<<1, // pad positive values with spaces
- Flag_VariableWidth = 1<<2, // variable field width in arg list
- Flag_VariablePrecision = 1<<3 // variable field precision in arg list
- };
-
- // out is the output stream, fmt is the full format string
- FormatIterator(std::ostream& out, const char* fmt)
- : m_out(out),
- m_fmt(fmt),
- m_extraFlags(Flag_None),
- m_wantWidth(false),
- m_wantPrecision(false),
- m_variableWidth(0),
- m_variablePrecision(0),
- m_origWidth(out.width()),
- m_origPrecision(out.precision()),
- m_origFlags(out.flags()),
- m_origFill(out.fill())
+ FormatArg() {}
+
+ template<typename T>
+ FormatArg(const T& value)
+ : m_value(static_cast<const void*>(&value)),
+ m_formatImpl(&formatImpl<T>),
+ m_toIntImpl(&toIntImpl<T>)
{ }
- // Print remaining part of format string.
- void finish()
+ void format(std::ostream& out, const char* fmtBegin,
+ const char* fmtEnd, int ntrunc) const
{
- // It would be nice if we could do this from the destructor, but we
- // can't if TINFORMAT_ERROR is used to throw an exception!
- m_fmt = printFormatStringLiteral(m_out, m_fmt);
- if(*m_fmt != '\0')
- TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
+ m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
}
- ~FormatIterator()
+ int toInt() const
{
- // Restore stream state
- m_out.width(m_origWidth);
- m_out.precision(m_origPrecision);
- m_out.flags(m_origFlags);
- m_out.fill(m_origFill);
+ return m_toIntImpl(m_value);
}
- template<typename T>
- void accept(const T& value);
-
private:
- // Parse and return an integer from the string c, as atoi()
- // On return, c is set to one past the end of the integer.
- static int parseIntAndAdvance(const char*& c)
+ template<typename T>
+ TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin,
+ const char* fmtEnd, int ntrunc, const void* value)
{
- int i = 0;
- for(;*c >= '0' && *c <= '9'; ++c)
- i = 10*i + (*c - '0');
- return i;
+ formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T*>(value));
}
- // Format at most truncLen characters of a C string to the given
- // stream. Return true if formatting proceeded (generic version always
- // returns false)
template<typename T>
- static bool formatCStringTruncate(std::ostream& /*out*/, const T& /*value*/,
- std::streamsize /*truncLen*/)
+ TINYFORMAT_HIDDEN static int toIntImpl(const void* value)
{
- return false;
- }
-# define TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(type) \
- static bool formatCStringTruncate(std::ostream& out, type* value, \
- std::streamsize truncLen) \
- { \
- std::streamsize len = 0; \
- while(len < truncLen && value[len] != 0) \
- ++len; \
- out.write(value, len); \
- return true; \
- }
- // Overload for const char* and char*. Could overload for signed &
- // unsigned char too, but these are technically unneeded for printf
- // compatibility.
- TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(const char)
- TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(char)
-# undef TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE
-
- // Print literal part of format string and return next format spec
- // position.
- //
- // Skips over any occurrences of '%%', printing a literal '%' to the
- // output. The position of the first % character of the next
- // nontrivial format spec is returned, or the end of string.
- static const char* printFormatStringLiteral(std::ostream& out,
- const char* fmt)
- {
- const char* c = fmt;
- for(; true; ++c)
- {
- switch(*c)
- {
- case '\0':
- out.write(fmt, static_cast<std::streamsize>(c - fmt));
- return c;
- case '%':
- out.write(fmt, static_cast<std::streamsize>(c - fmt));
- if(*(c+1) != '%')
- return c;
- // for "%%", tack trailing % onto next literal section.
- fmt = ++c;
- break;
- }
- }
+ return convertToInt<T>::invoke(*static_cast<const T*>(value));
}
- static const char* streamStateFromFormat(std::ostream& out,
- unsigned int& extraFlags,
- const char* fmtStart,
- int variableWidth,
- int variablePrecision);
-
- // Private copy & assign: Kill gcc warnings with -Weffc++
- FormatIterator(const FormatIterator&);
- FormatIterator& operator=(const FormatIterator&);
-
- // Stream, current format string & state
- std::ostream& m_out;
- const char* m_fmt;
- unsigned int m_extraFlags;
- // State machine info for handling of variable width & precision
- bool m_wantWidth;
- bool m_wantPrecision;
- int m_variableWidth;
- int m_variablePrecision;
- // Saved stream state
- std::streamsize m_origWidth;
- std::streamsize m_origPrecision;
- std::ios::fmtflags m_origFlags;
- char m_origFill;
+ const void* m_value;
+ void (*m_formatImpl)(std::ostream& out, const char* fmtBegin,
+ const char* fmtEnd, int ntrunc, const void* value);
+ int (*m_toIntImpl)(const void* value);
};
-// Accept a value for formatting into the internal stream.
-template<typename T>
-TINYFORMAT_NOINLINE // < greatly reduces bloat in optimized builds
-void FormatIterator::accept(const T& value)
+// Parse and return an integer from the string c, as atoi()
+// On return, c is set to one past the end of the integer.
+inline int parseIntAndAdvance(const char*& c)
{
- // Parse the format string
- const char* fmtEnd = 0;
- if(m_extraFlags == Flag_None && !m_wantWidth && !m_wantPrecision)
- {
- m_fmt = printFormatStringLiteral(m_out, m_fmt);
- fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, 0, 0);
- m_wantWidth = (m_extraFlags & Flag_VariableWidth) != 0;
- m_wantPrecision = (m_extraFlags & Flag_VariablePrecision) != 0;
- }
- // Consume value as variable width and precision specifier if necessary
- if(m_extraFlags & (Flag_VariableWidth | Flag_VariablePrecision))
- {
- if(m_wantWidth || m_wantPrecision)
- {
- int v = convertToInt<T>::invoke(value);
- if(m_wantWidth)
- {
- m_variableWidth = v;
- m_wantWidth = false;
- }
- else if(m_wantPrecision)
- {
- m_variablePrecision = v;
- m_wantPrecision = false;
- }
- return;
- }
- // If we get here, we've set both the variable precision and width as
- // required and we need to rerun the stream state setup to insert these.
- fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt,
- m_variableWidth, m_variablePrecision);
- }
+ int i = 0;
+ for(;*c >= '0' && *c <= '9'; ++c)
+ i = 10*i + (*c - '0');
+ return i;
+}
- // Format the value into the stream.
- if(!(m_extraFlags & (Flag_SpacePadPositive | Flag_TruncateToPrecision)))
- formatValue(m_out, m_fmt, fmtEnd, value);
- else
+// Print literal part of format string and return next format spec
+// position.
+//
+// Skips over any occurrences of '%%', printing a literal '%' to the
+// output. The position of the first % character of the next
+// nontrivial format spec is returned, or the end of string.
+inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
+{
+ const char* c = fmt;
+ for(;; ++c)
{
- // The following are special cases where there's no direct
- // correspondence between stream formatting and the printf() behaviour.
- // Instead, we simulate the behaviour crudely by formatting into a
- // temporary string stream and munging the resulting string.
- std::ostringstream tmpStream;
- tmpStream.copyfmt(m_out);
- if(m_extraFlags & Flag_SpacePadPositive)
- tmpStream.setf(std::ios::showpos);
- // formatCStringTruncate is required for truncating conversions like
- // "%.4s" where at most 4 characters of the c-string should be read.
- // If we didn't include this special case, we might read off the end.
- if(!( (m_extraFlags & Flag_TruncateToPrecision) &&
- formatCStringTruncate(tmpStream, value, m_out.precision()) ))
- {
- // Not a truncated c-string; just format normally.
- formatValue(tmpStream, m_fmt, fmtEnd, value);
- }
- std::string result = tmpStream.str(); // allocates... yuck.
- if(m_extraFlags & Flag_SpacePadPositive)
+ switch(*c)
{
- for(size_t i = 0, iend = result.size(); i < iend; ++i)
- if(result[i] == '+')
- result[i] = ' ';
+ case '\0':
+ out.write(fmt, c - fmt);
+ return c;
+ case '%':
+ out.write(fmt, c - fmt);
+ if(*(c+1) != '%')
+ return c;
+ // for "%%", tack trailing % onto next literal section.
+ fmt = ++c;
+ break;
+ default:
+ break;
}
- if((m_extraFlags & Flag_TruncateToPrecision) &&
- (int)result.size() > (int)m_out.precision())
- m_out.write(result.c_str(), m_out.precision());
- else
- m_out << result;
}
- m_extraFlags = Flag_None;
- m_fmt = fmtEnd;
}
@@ -663,13 +575,14 @@ void FormatIterator::accept(const T& value)
// with the form "%[flags][width][.precision][length]type".
//
// Formatting options which can't be natively represented using the ostream
-// state are returned in the extraFlags parameter which is a bitwise
-// combination of values from the ExtraFormatFlags enum.
-inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
- unsigned int& extraFlags,
- const char* fmtStart,
- int variableWidth,
- int variablePrecision)
+// state are returned in spacePadPositive (for space padded positive numbers)
+// and ntrunc (for truncating conversions). argIndex is incremented if
+// necessary to pull out variable width and precision . The function returns a
+// pointer to the character after the end of the current format spec.
+inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive,
+ int& ntrunc, const char* fmtStart,
+ const detail::FormatArg* formatters,
+ int& argIndex, int numFormatters)
{
if(*fmtStart != '%')
{
@@ -684,9 +597,9 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
out.unsetf(std::ios::adjustfield | std::ios::basefield |
std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
- extraFlags = Flag_None;
bool precisionSet = false;
bool widthSet = false;
+ int widthExtra = 0;
const char* c = fmtStart + 1;
// 1) Parse flags
for(;; ++c)
@@ -713,12 +626,15 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
case ' ':
// overridden by show positive sign, '+' flag.
if(!(out.flags() & std::ios::showpos))
- extraFlags |= Flag_SpacePadPositive;
+ spacePadPositive = true;
continue;
case '+':
out.setf(std::ios::showpos);
- extraFlags &= ~Flag_SpacePadPositive;
+ spacePadPositive = false;
+ widthExtra = 1;
continue;
+ default:
+ break;
}
break;
}
@@ -731,15 +647,19 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
if(*c == '*')
{
widthSet = true;
- if(variableWidth < 0)
+ int width = 0;
+ if(argIndex < numFormatters)
+ width = formatters[argIndex++].toInt();
+ else
+ TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
+ if(width < 0)
{
// negative widths correspond to '-' flag set
out.fill(' ');
out.setf(std::ios::left, std::ios::adjustfield);
- variableWidth = -variableWidth;
+ width = -width;
}
- out.width(variableWidth);
- extraFlags |= Flag_VariableWidth;
+ out.width(width);
++c;
}
// 3) Parse precision
@@ -750,8 +670,10 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
if(*c == '*')
{
++c;
- extraFlags |= Flag_VariablePrecision;
- precision = variablePrecision;
+ if(argIndex < numFormatters)
+ precision = formatters[argIndex++].toInt();
+ else
+ TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
}
else
{
@@ -814,7 +736,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
break;
case 's':
if(precisionSet)
- extraFlags |= Flag_TruncateToPrecision;
+ ntrunc = static_cast<int>(out.precision());
// Make %s print booleans as "true" and "false"
out.setf(std::ios::boolalpha);
break;
@@ -826,6 +748,8 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly "
"terminated by end of string");
return c;
+ default:
+ break;
}
if(intConversion && precisionSet && !widthSet)
{
@@ -833,7 +757,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
// padded with zeros on the left). This isn't really supported by the
// iostreams, but we can approximately simulate it with the width if
// the width isn't otherwise used.
- out.width(out.precision());
+ out.width(out.precision() + widthExtra);
out.setf(std::ios::internal, std::ios::adjustfield);
out.fill('0');
}
@@ -841,170 +765,282 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
}
-
//------------------------------------------------------------------------------
-// Private format function on top of which the public interface is implemented.
-// We enforce a mimimum of one value to be formatted to prevent bugs looking like
-//
-// const char* myStr = "100% broken";
-// printf(myStr); // Parses % as a format specifier
-#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
-
-template<typename T1>
-void format(FormatIterator& fmtIter, const T1& value1)
+inline void formatImpl(std::ostream& out, const char* fmt,
+ const detail::FormatArg* formatters,
+ int numFormatters)
{
- fmtIter.accept(value1);
- fmtIter.finish();
+ // Saved stream state
+ std::streamsize origWidth = out.width();
+ std::streamsize origPrecision = out.precision();
+ std::ios::fmtflags origFlags = out.flags();
+ char origFill = out.fill();
+
+ for (int argIndex = 0; argIndex < numFormatters; ++argIndex)
+ {
+ // Parse the format string
+ fmt = printFormatStringLiteral(out, fmt);
+ bool spacePadPositive = false;
+ int ntrunc = -1;
+ const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt,
+ formatters, argIndex, numFormatters);
+ if (argIndex >= numFormatters)
+ {
+ // Check args remain after reading any variable width/precision
+ TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
+ return;
+ }
+ const FormatArg& arg = formatters[argIndex];
+ // Format the arg into the stream.
+ if(!spacePadPositive)
+ arg.format(out, fmt, fmtEnd, ntrunc);
+ else
+ {
+ // The following is a special case with no direct correspondence
+ // between stream formatting and the printf() behaviour. Simulate
+ // it crudely by formatting into a temporary string stream and
+ // munging the resulting string.
+ std::ostringstream tmpStream;
+ tmpStream.copyfmt(out);
+ tmpStream.setf(std::ios::showpos);
+ arg.format(tmpStream, fmt, fmtEnd, ntrunc);
+ std::string result = tmpStream.str(); // allocates... yuck.
+ for(size_t i = 0, iend = result.size(); i < iend; ++i)
+ if(result[i] == '+') result[i] = ' ';
+ out << result;
+ }
+ fmt = fmtEnd;
+ }
+
+ // Print remaining part of format string.
+ fmt = printFormatStringLiteral(out, fmt);
+ if(*fmt != '\0')
+ TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
+
+ // Restore stream state
+ out.width(origWidth);
+ out.precision(origPrecision);
+ out.flags(origFlags);
+ out.fill(origFill);
}
-// General version for C++11
-template<typename T1, typename... Args>
-void format(FormatIterator& fmtIter, const T1& value1, const Args&... args)
+} // namespace detail
+
+
+/// List of template arguments format(), held in a type-opaque way.
+///
+/// A const reference to FormatList (typedef'd as FormatListRef) may be
+/// conveniently used to pass arguments to non-template functions: All type
+/// information has been stripped from the arguments, leaving just enough of a
+/// common interface to perform formatting as required.
+class FormatList
{
- fmtIter.accept(value1);
- format(fmtIter, args...);
-}
+ public:
+ FormatList(detail::FormatArg* formatters, int N)
+ : m_formatters(formatters), m_N(N) { }
-#else
+ friend void vformat(std::ostream& out, const char* fmt,
+ const FormatList& list);
-inline void format(FormatIterator& fmtIter)
+ private:
+ const detail::FormatArg* m_formatters;
+ int m_N;
+};
+
+/// Reference to type-opaque format list for passing to vformat()
+typedef const FormatList& FormatListRef;
+
+
+namespace detail {
+
+// Format list subclass with fixed storage to avoid dynamic allocation
+template<int N>
+class FormatListN : public FormatList
{
- fmtIter.finish();
-}
+ public:
+#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
+ template<typename... Args>
+ FormatListN(const Args&... args)
+ : FormatList(&m_formatterStore[0], N),
+ m_formatterStore { FormatArg(args)... }
+ { static_assert(sizeof...(args) == N, "Number of args must be N"); }
+#else // C++98 version
+ void init(int) {}
+# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \
+ \
+ template<TINYFORMAT_ARGTYPES(n)> \
+ FormatListN(TINYFORMAT_VARARGS(n)) \
+ : FormatList(&m_formatterStore[0], n) \
+ { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
+ \
+ template<TINYFORMAT_ARGTYPES(n)> \
+ void init(int i, TINYFORMAT_VARARGS(n)) \
+ { \
+ m_formatterStore[i] = FormatArg(v1); \
+ init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \
+ }
-// General version for C++98
-#define TINYFORMAT_MAKE_FORMAT_DETAIL(n) \
-template<TINYFORMAT_ARGTYPES(n)> \
-void format(detail::FormatIterator& fmtIter, TINYFORMAT_VARARGS(n)) \
-{ \
- fmtIter.accept(v1); \
- format(fmtIter TINYFORMAT_PASSARGS_TAIL(n)); \
-}
+ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
+# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
+#endif
-TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_DETAIL)
-#undef TINYFORMAT_MAKE_FORMAT_DETAIL
+ private:
+ FormatArg m_formatterStore[N];
+};
-#endif // End C++98 variadic template emulation for format()
+// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
+template<> class FormatListN<0> : public FormatList
+{
+ public: FormatListN() : FormatList(0, 0) {}
+};
} // namespace detail
//------------------------------------------------------------------------------
-// Implement all the main interface functions here in terms of detail::format()
+// Primary API functions
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
-// C++11 - the simple case
-template<typename T1, typename... Args>
-void format(std::ostream& out, const char* fmt, const T1& v1, const Args&... args)
+/// Make type-agnostic format list from list of template arguments.
+///
+/// The exact return type of this function is an implementation detail and
+/// shouldn't be relied upon. Instead it should be stored as a FormatListRef:
+///
+/// FormatListRef formatList = makeFormatList( /*...*/ );
+template<typename... Args>
+detail::FormatListN<sizeof...(Args)> makeFormatList(const Args&... args)
{
- detail::FormatIterator fmtIter(out, fmt);
- format(fmtIter, v1, args...);
+ return detail::FormatListN<sizeof...(args)>(args...);
}
-template<typename T1, typename... Args>
-std::string format(const char* fmt, const T1& v1, const Args&... args)
+#else // C++98 version
+
+inline detail::FormatListN<0> makeFormatList()
+{
+ return detail::FormatListN<0>();
+}
+#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \
+template<TINYFORMAT_ARGTYPES(n)> \
+detail::FormatListN<n> makeFormatList(TINYFORMAT_VARARGS(n)) \
+{ \
+ return detail::FormatListN<n>(TINYFORMAT_PASSARGS(n)); \
+}
+TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
+#undef TINYFORMAT_MAKE_MAKEFORMATLIST
+
+#endif
+
+/// Format list of arguments to the stream according to the given format string.
+///
+/// The name vformat() is chosen for the semantic similarity to vprintf(): the
+/// list of format arguments is held in a single function argument.
+inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
+{
+ detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
+}
+
+
+#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
+
+/// Format list of arguments to the stream according to given format string.
+template<typename... Args>
+void format(std::ostream& out, const char* fmt, const Args&... args)
+{
+ vformat(out, fmt, makeFormatList(args...));
+}
+
+/// Format list of arguments according to the given format string and return
+/// the result as a string.
+template<typename... Args>
+std::string format(const char* fmt, const Args&... args)
{
std::ostringstream oss;
- format(oss, fmt, v1, args...);
+ format(oss, fmt, args...);
return oss.str();
}
-template<typename T1, typename... Args>
-std::string format(const std::string &fmt, const T1& v1, const Args&... args)
+/// Format list of arguments to std::cout, according to the given format string
+template<typename... Args>
+void printf(const char* fmt, const Args&... args)
+{
+ format(std::cout, fmt, args...);
+}
+
+template<typename... Args>
+void printfln(const char* fmt, const Args&... args)
+{
+ format(std::cout, fmt, args...);
+ std::cout << '\n';
+}
+
+#else // C++98 version
+
+inline void format(std::ostream& out, const char* fmt)
+{
+ vformat(out, fmt, makeFormatList());
+}
+
+inline std::string format(const char* fmt)
{
std::ostringstream oss;
- format(oss, fmt.c_str(), v1, args...);
+ format(oss, fmt);
return oss.str();
}
-template<typename T1, typename... Args>
-void printf(const char* fmt, const T1& v1, const Args&... args)
+inline void printf(const char* fmt)
{
- format(std::cout, fmt, v1, args...);
+ format(std::cout, fmt);
}
-#else
+inline void printfln(const char* fmt)
+{
+ format(std::cout, fmt);
+ std::cout << '\n';
+}
-// C++98 - define the interface functions using the wrapping macros
#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \
\
template<TINYFORMAT_ARGTYPES(n)> \
void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
- tinyformat::detail::FormatIterator fmtIter(out, fmt); \
- tinyformat::detail::format(fmtIter, TINYFORMAT_PASSARGS(n)); \
+ vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
std::ostringstream oss; \
- tinyformat::format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
+ format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
return oss.str(); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
-std::string format(const std::string &fmt, TINYFORMAT_VARARGS(n)) \
+void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
- std::ostringstream oss; \
- tinyformat::format(oss, fmt.c_str(), TINYFORMAT_PASSARGS(n)); \
- return oss.str(); \
+ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
-void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \
+void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
- tinyformat::format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
+ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
+ std::cout << '\n'; \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)
#undef TINYFORMAT_MAKE_FORMAT_FUNCS
-#endif
-
-//------------------------------------------------------------------------------
-// Define deprecated wrapping macro for backward compatibility in tinyformat
-// 1.x. Will be removed in version 2!
-#define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS
-#define TINYFORMAT_WRAP_FORMAT_N(n, returnType, funcName, funcDeclSuffix, \
- bodyPrefix, streamName, bodySuffix) \
-template<TINYFORMAT_ARGTYPES(n)> \
-returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt, \
- TINYFORMAT_VARARGS(n)) funcDeclSuffix \
-{ \
- bodyPrefix \
- tinyformat::format(streamName, fmt, TINYFORMAT_PASSARGS(n)); \
- bodySuffix \
-} \
-
-#define TINYFORMAT_WRAP_FORMAT(returnType, funcName, funcDeclSuffix, \
- bodyPrefix, streamName, bodySuffix) \
-inline \
-returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt \
- ) funcDeclSuffix \
-{ \
- bodyPrefix \
- tinyformat::detail::FormatIterator(streamName, fmt).finish(); \
- bodySuffix \
-} \
-TINYFORMAT_WRAP_FORMAT_N(1 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(2 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(3 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(4 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(5 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(6 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(7 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(8 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(9 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(10, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(11, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(12, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(13, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(14, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(15, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(16, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
+#endif
+// Added for Bitcoin Core
+template<typename... Args>
+std::string format(const std::string &fmt, const Args&... args)
+{
+ std::ostringstream oss;
+ format(oss, fmt.c_str(), args...);
+ return oss.str();
+}
} // namespace tinyformat
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 0d6b655675..99c45d489c 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -4,6 +4,7 @@
#include "torcontrol.h"
#include "utilstrencodings.h"
+#include "netbase.h"
#include "net.h"
#include "util.h"
#include "crypto/hmac_sha256.h"
@@ -437,8 +438,7 @@ void TorController::add_onion_cb(TorControlConnection& conn, const TorControlRep
if ((i = m.find("PrivateKey")) != m.end())
private_key = i->second;
}
-
- service = CService(service_id+".onion", GetListenPort());
+ service = LookupNumeric(std::string(service_id+".onion").c_str(), GetListenPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile());
@@ -462,7 +462,8 @@ void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& r
// Now that we know Tor is running setup the proxy for onion addresses
// if -onion isn't set to something else.
if (GetArg("-onion", "") == "") {
- proxyType addrOnion = proxyType(CService("127.0.0.1", 9050), true);
+ CService resolved(LookupNumeric("127.0.0.1", 9050));
+ proxyType addrOnion = proxyType(resolved, true);
SetProxy(NET_TOR, addrOnion);
SetLimited(NET_TOR, false);
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 691baa6744..b631c48484 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -75,7 +75,7 @@ void CTxMemPoolEntry::UpdateLockPoints(const LockPoints& lp)
size_t CTxMemPoolEntry::GetTxSize() const
{
- return GetVirtualTransactionSize(nTxWeight);
+ return GetVirtualTransactionSize(nTxWeight, sigOpCost);
}
// Update the given tx for any in-mempool descendants.
@@ -657,6 +657,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
uint64_t innerUsage = 0;
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
+ const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate);
LOCK(cs);
list<const CTxMemPoolEntry*> waitingOnDependants;
@@ -737,7 +738,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
waitingOnDependants.push_back(&(*it));
else {
CValidationState state;
- assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, NULL));
+ bool fCheckResult = tx.IsCoinBase() ||
+ Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight);
+ assert(fCheckResult);
UpdateCoins(tx, mempoolDuplicate, 1000000);
}
}
@@ -751,7 +754,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
stepsSinceLastRemove++;
assert(stepsSinceLastRemove < waitingOnDependants.size());
} else {
- assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, NULL));
+ bool fCheckResult = entry->GetTx().IsCoinBase() ||
+ Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight);
+ assert(fCheckResult);
UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000);
stepsSinceLastRemove = 0;
}
diff --git a/src/util.h b/src/util.h
index ac4b947785..39328b51ef 100644
--- a/src/util.h
+++ b/src/util.h
@@ -77,33 +77,17 @@ int LogPrintStr(const std::string &str);
#define LogPrintf(...) LogPrint(NULL, __VA_ARGS__)
-template<typename T1, typename... Args>
-static inline int LogPrint(const char* category, const char* fmt, const T1& v1, const Args&... args)
+template<typename... Args>
+static inline int LogPrint(const char* category, const char* fmt, const Args&... args)
{
if(!LogAcceptCategory(category)) return 0; \
- return LogPrintStr(tfm::format(fmt, v1, args...));
+ return LogPrintStr(tfm::format(fmt, args...));
}
-template<typename T1, typename... Args>
-bool error(const char* fmt, const T1& v1, const Args&... args)
+template<typename... Args>
+bool error(const char* fmt, const Args&... args)
{
- LogPrintStr("ERROR: " + tfm::format(fmt, v1, args...) + "\n");
- return false;
-}
-
-/**
- * Zero-arg versions of logging and error, these are not covered by
- * the variadic templates above (and don't take format arguments but
- * bare strings).
- */
-static inline int LogPrint(const char* category, const char* s)
-{
- if(!LogAcceptCategory(category)) return 0;
- return LogPrintStr(s);
-}
-static inline bool error(const char* s)
-{
- LogPrintStr(std::string("ERROR: ") + s + "\n");
+ LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n");
return false;
}
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 8da0c72858..cf1d6ca086 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -48,6 +48,6 @@ void UnregisterAllValidationInterfaces() {
g_signals.UpdatedBlockTip.disconnect_all_slots();
}
-void SyncWithWallets(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock) {
- g_signals.SyncTransaction(tx, pindex, pblock);
+void SyncWithWallets(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {
+ g_signals.SyncTransaction(tx, pindex, posInBlock);
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 01b8e47650..094b1cfe26 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -28,12 +28,12 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */
void UnregisterAllValidationInterfaces();
/** Push an updated transaction to all registered wallets */
-void SyncWithWallets(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock = NULL);
+void SyncWithWallets(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock = -1);
class CValidationInterface {
protected:
virtual void UpdatedBlockTip(const CBlockIndex *pindex) {}
- virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock) {}
+ virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {}
virtual void SetBestChain(const CBlockLocator &locator) {}
virtual void UpdatedTransaction(const uint256 &hash) {}
virtual void Inventory(const uint256 &hash) {}
@@ -50,7 +50,7 @@ struct CMainSignals {
/** Notifies listeners of updated block chain tip */
boost::signals2::signal<void (const CBlockIndex *)> UpdatedBlockTip;
/** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */
- boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, const CBlock *)> SyncTransaction;
+ boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction;
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
/** Notifies listeners of a new active block chain. */
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index c906785e9e..cfd007ca1c 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -284,7 +284,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
pdb = NULL;
--bitdb.mapFileUseCount[strFile];
strFile = "";
- throw runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFile));
+ throw runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
}
if (fCreate && !Exists(string("version"))) {
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index d55cc68dc0..fe8b53ceb0 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -309,8 +309,7 @@ UniValue importprunedfunds(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
if (pwalletMain->IsMine(tx)) {
- CWalletDB walletdb(pwalletMain->strWalletFile, "r+", false);
- pwalletMain->AddToWallet(wtx, false, &walletdb);
+ pwalletMain->AddToWallet(wtx, false);
return NullUniValue;
}
@@ -602,19 +601,42 @@ UniValue dumpwallet(const UniValue& params, bool fHelp)
file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
file << "\n";
+
+ // add the base58check encoded extended master if the wallet uses HD
+ CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID;
+ if (!masterKeyID.IsNull())
+ {
+ CKey key;
+ if (pwalletMain->GetKey(masterKeyID, key))
+ {
+ CExtKey masterKey;
+ masterKey.SetMaster(key.begin(), key.size());
+
+ CBitcoinExtKey b58extkey;
+ b58extkey.SetKey(masterKey);
+
+ file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n";
+ }
+ }
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
std::string strTime = EncodeDumpTime(it->first);
std::string strAddr = CBitcoinAddress(keyid).ToString();
CKey key;
if (pwalletMain->GetKey(keyid, key)) {
+ file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime);
if (pwalletMain->mapAddressBook.count(keyid)) {
- file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr);
+ file << strprintf("label=%s", EncodeDumpString(pwalletMain->mapAddressBook[keyid].name));
+ } else if (keyid == masterKeyID) {
+ file << "hdmaster=1";
} else if (setKeyPool.count(keyid)) {
- file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
+ file << "reserve=1";
+ } else if (pwalletMain->mapKeyMetadata[keyid].hdKeypath == "m") {
+ file << "inactivehdmaster=1";
} else {
- file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
+ file << "change=1";
}
+ file << strprintf(" # addr=%s%s\n", strAddr, (pwalletMain->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath="+pwalletMain->mapKeyMetadata[keyid].hdKeypath : ""));
}
}
file << "\n";
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index b4831ad795..aa0a9374c1 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -10,7 +10,6 @@
#include "init.h"
#include "main.h"
#include "net.h"
-#include "netbase.h"
#include "policy/rbf.h"
#include "rpc/server.h"
#include "timedata.h"
@@ -2081,7 +2080,7 @@ UniValue encryptwallet(const UniValue& params, bool fHelp)
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
StartShutdown();
- return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
+ return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
}
UniValue lockunspent(const UniValue& params, bool fHelp)
@@ -2260,16 +2259,16 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
"Returns an object containing various wallet state info.\n"
"\nResult:\n"
"{\n"
- " \"walletversion\": xxxxx, (numeric) the wallet version\n"
- " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
- " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
- " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
- " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
- " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
- " \"masterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n"
+ " \"walletversion\": xxxxx, (numeric) the wallet version\n"
+ " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
+ " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
+ " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
+ " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
+ " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
+ " \"hdmasterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
@@ -2291,7 +2290,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID;
if (!masterKeyID.IsNull())
- obj.push_back(Pair("masterkeyid", masterKeyID.GetHex()));
+ obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex()));
return obj;
}
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index d075b2b641..a6cada46a2 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
pwalletMain->AddAccountingEntry(ae, walletdb);
wtx.mapValue["comment"] = "z";
- pwalletMain->AddToWallet(wtx, false, &walletdb);
+ pwalletMain->AddToWallet(wtx);
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
vpwtx[0]->nTimeReceived = (unsigned int)1333333335;
vpwtx[0]->nOrderPos = -1;
@@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
--tx.nLockTime; // Just to change the hash :)
*static_cast<CTransaction*>(&wtx) = CTransaction(tx);
}
- pwalletMain->AddToWallet(wtx, false, &walletdb);
+ pwalletMain->AddToWallet(wtx);
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
vpwtx[1]->nTimeReceived = (unsigned int)1333333336;
@@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
--tx.nLockTime; // Just to change the hash :)
*static_cast<CTransaction*>(&wtx) = CTransaction(tx);
}
- pwalletMain->AddToWallet(wtx, false, &walletdb);
+ pwalletMain->AddToWallet(wtx);
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
vpwtx[2]->nOrderPos = -1;
diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp
deleted file mode 100644
index 4e7d177f51..0000000000
--- a/src/wallet/test/rpc_wallet_tests.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright (c) 2013-2015 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 "rpc/server.h"
-#include "rpc/client.h"
-
-#include "base58.h"
-#include "main.h"
-#include "wallet/wallet.h"
-
-#include "wallet/test/wallet_test_fixture.h"
-
-#include <boost/algorithm/string.hpp>
-#include <boost/test/unit_test.hpp>
-
-#include <univalue.h>
-
-using namespace std;
-
-extern UniValue createArgs(int nRequired, const char* address1 = NULL, const char* address2 = NULL);
-extern UniValue CallRPC(string args);
-
-extern CWallet* pwalletMain;
-
-BOOST_FIXTURE_TEST_SUITE(rpc_wallet_tests, WalletTestingSetup)
-
-BOOST_AUTO_TEST_CASE(rpc_addmultisig)
-{
- rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor;
-
- // old, 65-byte-long:
- const char address1Hex[] = "0434e3e09f49ea168c5bbf53f877ff4206923858aab7c7e1df25bc263978107c95e35065a27ef6f1b27222db0ec97e0e895eaca603d3ee0d4c060ce3d8a00286c8";
- // new, compressed:
- const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4";
-
- UniValue v;
- CBitcoinAddress address;
- BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false));
- address.SetString(v.get_str());
- BOOST_CHECK(address.IsValid() && address.IsScript());
-
- BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false));
- address.SetString(v.get_str());
- BOOST_CHECK(address.IsValid() && address.IsScript());
-
- BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false));
- address.SetString(v.get_str());
- BOOST_CHECK(address.IsValid() && address.IsScript());
-
- BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error);
- BOOST_CHECK_THROW(addmultisig(createArgs(1), false), runtime_error);
- BOOST_CHECK_THROW(addmultisig(createArgs(2, address1Hex), false), runtime_error);
-
- BOOST_CHECK_THROW(addmultisig(createArgs(1, ""), false), runtime_error);
- BOOST_CHECK_THROW(addmultisig(createArgs(1, "NotAValidPubkey"), false), runtime_error);
-
- string short1(address1Hex, address1Hex + sizeof(address1Hex) - 2); // last byte missing
- BOOST_CHECK_THROW(addmultisig(createArgs(2, short1.c_str()), false), runtime_error);
-
- string short2(address1Hex + 1, address1Hex + sizeof(address1Hex)); // first byte missing
- BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error);
-}
-
-BOOST_AUTO_TEST_CASE(rpc_wallet)
-{
- // Test RPC calls for various wallet statistics
- UniValue r;
- CPubKey demoPubkey;
- CBitcoinAddress demoAddress;
- UniValue retValue;
- string strAccount = "walletDemoAccount";
- CBitcoinAddress setaccountDemoAddress;
- {
- LOCK(pwalletMain->cs_wallet);
-
- demoPubkey = pwalletMain->GenerateNewKey();
- demoAddress = CBitcoinAddress(CTxDestination(demoPubkey.GetID()));
- string strPurpose = "receive";
- BOOST_CHECK_NO_THROW({ /*Initialize Wallet with an account */
- CWalletDB walletdb(pwalletMain->strWalletFile);
- CAccount account;
- account.vchPubKey = demoPubkey;
- pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, strPurpose);
- walletdb.WriteAccount(strAccount, account);
- });
-
- CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey();
- setaccountDemoAddress = CBitcoinAddress(CTxDestination(setaccountDemoPubkey.GetID()));
- }
- /*********************************
- * setaccount
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("setaccount " + setaccountDemoAddress.ToString() + " nullaccount"));
- /* 1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ is not owned by the test wallet. */
- BOOST_CHECK_THROW(CallRPC("setaccount 1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ nullaccount"), runtime_error);
- BOOST_CHECK_THROW(CallRPC("setaccount"), runtime_error);
- /* 1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4X (33 chars) is an illegal address (should be 34 chars) */
- BOOST_CHECK_THROW(CallRPC("setaccount 1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4X nullaccount"), runtime_error);
-
-
- /*********************************
- * getbalance
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("getbalance"));
- BOOST_CHECK_NO_THROW(CallRPC("getbalance " + demoAddress.ToString()));
-
- /*********************************
- * listunspent
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listunspent"));
- BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error);
- BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error);
- BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error);
- BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error);
- BOOST_CHECK_NO_THROW(r = CallRPC("listunspent 0 1 []"));
- BOOST_CHECK(r.get_array().empty());
-
- /*********************************
- * listreceivedbyaddress
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress"));
- BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0"));
- BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error);
- BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error);
- BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true"));
- BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error);
-
- /*********************************
- * listreceivedbyaccount
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount"));
- BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0"));
- BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error);
- BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error);
- BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true"));
- BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error);
-
- /*********************************
- * listsinceblock
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listsinceblock"));
-
- /*********************************
- * listtransactions
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listtransactions"));
- BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + demoAddress.ToString()));
- BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + demoAddress.ToString() + " 20"));
- BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + demoAddress.ToString() + " 20 0"));
- BOOST_CHECK_THROW(CallRPC("listtransactions " + demoAddress.ToString() + " not_int"), runtime_error);
-
- /*********************************
- * listlockunspent
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listlockunspent"));
-
- /*********************************
- * listaccounts
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listaccounts"));
-
- /*********************************
- * listaddressgroupings
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("listaddressgroupings"));
-
- /*********************************
- * getrawchangeaddress
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("getrawchangeaddress"));
-
- /*********************************
- * getnewaddress
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("getnewaddress"));
- BOOST_CHECK_NO_THROW(CallRPC("getnewaddress getnewaddress_demoaccount"));
-
- /*********************************
- * getaccountaddress
- *********************************/
- BOOST_CHECK_NO_THROW(CallRPC("getaccountaddress \"\""));
- BOOST_CHECK_NO_THROW(CallRPC("getaccountaddress accountThatDoesntExists")); // Should generate a new account
- BOOST_CHECK_NO_THROW(retValue = CallRPC("getaccountaddress " + strAccount));
- BOOST_CHECK(CBitcoinAddress(retValue.get_str()).Get() == demoAddress.Get());
-
- /*********************************
- * getaccount
- *********************************/
- BOOST_CHECK_THROW(CallRPC("getaccount"), runtime_error);
- BOOST_CHECK_NO_THROW(CallRPC("getaccount " + demoAddress.ToString()));
-
- /*********************************
- * signmessage + verifymessage
- *********************************/
- BOOST_CHECK_NO_THROW(retValue = CallRPC("signmessage " + demoAddress.ToString() + " mymessage"));
- BOOST_CHECK_THROW(CallRPC("signmessage"), runtime_error);
- /* Should throw error because this address is not loaded in the wallet */
- BOOST_CHECK_THROW(CallRPC("signmessage 1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ mymessage"), runtime_error);
-
- /* missing arguments */
- BOOST_CHECK_THROW(CallRPC("verifymessage " + demoAddress.ToString()), runtime_error);
- BOOST_CHECK_THROW(CallRPC("verifymessage " + demoAddress.ToString() + " " + retValue.get_str()), runtime_error);
- /* Illegal address */
- BOOST_CHECK_THROW(CallRPC("verifymessage 1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4X " + retValue.get_str() + " mymessage"), runtime_error);
- /* wrong address */
- BOOST_CHECK(CallRPC("verifymessage 1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ " + retValue.get_str() + " mymessage").get_bool() == false);
- /* Correct address and signature but wrong message */
- BOOST_CHECK(CallRPC("verifymessage " + demoAddress.ToString() + " " + retValue.get_str() + " wrongmessage").get_bool() == false);
- /* Correct address, message and signature*/
- BOOST_CHECK(CallRPC("verifymessage " + demoAddress.ToString() + " " + retValue.get_str() + " mymessage").get_bool() == true);
-
- /*********************************
- * getaddressesbyaccount
- *********************************/
- BOOST_CHECK_THROW(CallRPC("getaddressesbyaccount"), runtime_error);
- BOOST_CHECK_NO_THROW(retValue = CallRPC("getaddressesbyaccount " + strAccount));
- UniValue arr = retValue.get_array();
- BOOST_CHECK(arr.size() > 0);
- BOOST_CHECK(CBitcoinAddress(arr[0].get_str()).Get() == demoAddress.Get());
-
- /*********************************
- * fundrawtransaction
- *********************************/
- BOOST_CHECK_THROW(CallRPC("fundrawtransaction 28z"), runtime_error);
- BOOST_CHECK_THROW(CallRPC("fundrawtransaction 01000000000180969800000000001976a91450ce0a4b0ee0ddeb633da85199728b940ac3fe9488ac00000000"), runtime_error);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 9036ee26d8..a76db37617 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -1,3 +1,7 @@
+// Copyright (c) 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 "wallet/test/wallet_test_fixture.h"
#include "rpc/server.h"
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 5908dfeace..769322969d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -98,7 +98,7 @@ CPubKey CWallet::GenerateNewKey()
CKeyMetadata metadata(nCreationTime);
// use HD key derivation if HD was enabled during wallet creation
- if (!hdChain.masterKeyID.IsNull()) {
+ if (IsHDEnabled()) {
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey key; //master key seed (256bit)
CExtKey masterKey; //hd master key
@@ -108,7 +108,7 @@ CPubKey CWallet::GenerateNewKey()
// try to get the master key
if (!GetKey(hdChain.masterKeyID, key))
- throw std::runtime_error("CWallet::GenerateNewKey(): Master key not found");
+ throw std::runtime_error(std::string(__func__) + ": Master key not found");
masterKey.SetMaster(key.begin(), key.size());
@@ -135,7 +135,7 @@ CPubKey CWallet::GenerateNewKey()
// update the chain model in the database
if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
- throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
+ throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
} else {
secret.MakeNewKey(fCompressed);
}
@@ -152,7 +152,7 @@ CPubKey CWallet::GenerateNewKey()
nTimeFirstKey = nCreationTime;
if (!AddKeyPubKey(secret, pubkey))
- throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
+ throw std::runtime_error(std::string(__func__) + ": AddKey failed");
return pubkey;
}
@@ -626,6 +626,15 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
Lock();
Unlock(strWalletPassphrase);
+
+ // if we are using HD, replace the HD master key (seed) with a new one
+ if (IsHDEnabled()) {
+ CKey key;
+ CPubKey masterPubKey = GenerateNewHDMasterKey();
+ if (!SetHDMasterKey(masterPubKey))
+ return false;
+ }
+
NewKeyPool();
Lock();
@@ -732,138 +741,143 @@ void CWallet::MarkDirty()
}
}
-bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb)
+bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
+ LOCK(cs_wallet);
+
+ CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose);
+
uint256 hash = wtxIn.GetHash();
- if (fFromLoadWallet)
+ // Inserts only if not already there, returns tx inserted or tx found
+ pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn));
+ CWalletTx& wtx = (*ret.first).second;
+ wtx.BindWallet(this);
+ bool fInsertedNew = ret.second;
+ if (fInsertedNew)
{
- mapWallet[hash] = wtxIn;
- CWalletTx& wtx = mapWallet[hash];
- wtx.BindWallet(this);
+ wtx.nTimeReceived = GetAdjustedTime();
+ wtx.nOrderPos = IncOrderPosNext(&walletdb);
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
- AddToSpends(hash);
- BOOST_FOREACH(const CTxIn& txin, wtx.vin) {
- if (mapWallet.count(txin.prevout.hash)) {
- CWalletTx& prevtx = mapWallet[txin.prevout.hash];
- if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
- MarkConflicted(prevtx.hashBlock, wtx.GetHash());
- }
- }
- }
- }
- else
- {
- LOCK(cs_wallet);
- // Inserts only if not already there, returns tx inserted or tx found
- pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn));
- CWalletTx& wtx = (*ret.first).second;
- wtx.BindWallet(this);
- bool fInsertedNew = ret.second;
- if (fInsertedNew)
- {
- wtx.nTimeReceived = GetAdjustedTime();
- wtx.nOrderPos = IncOrderPosNext(pwalletdb);
- wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
-
- wtx.nTimeSmart = wtx.nTimeReceived;
- if (!wtxIn.hashUnset())
+
+ wtx.nTimeSmart = wtx.nTimeReceived;
+ if (!wtxIn.hashUnset())
+ {
+ if (mapBlockIndex.count(wtxIn.hashBlock))
{
- if (mapBlockIndex.count(wtxIn.hashBlock))
+ int64_t latestNow = wtx.nTimeReceived;
+ int64_t latestEntry = 0;
{
- int64_t latestNow = wtx.nTimeReceived;
- int64_t latestEntry = 0;
+ // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
+ int64_t latestTolerated = latestNow + 300;
+ const TxItems & txOrdered = wtxOrdered;
+ for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
- // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
- int64_t latestTolerated = latestNow + 300;
- const TxItems & txOrdered = wtxOrdered;
- for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
+ CWalletTx *const pwtx = (*it).second.first;
+ if (pwtx == &wtx)
+ continue;
+ CAccountingEntry *const pacentry = (*it).second.second;
+ int64_t nSmartTime;
+ if (pwtx)
{
- CWalletTx *const pwtx = (*it).second.first;
- if (pwtx == &wtx)
- continue;
- CAccountingEntry *const pacentry = (*it).second.second;
- int64_t nSmartTime;
- if (pwtx)
- {
- nSmartTime = pwtx->nTimeSmart;
- if (!nSmartTime)
- nSmartTime = pwtx->nTimeReceived;
- }
- else
- nSmartTime = pacentry->nTime;
- if (nSmartTime <= latestTolerated)
- {
- latestEntry = nSmartTime;
- if (nSmartTime > latestNow)
- latestNow = nSmartTime;
- break;
- }
+ nSmartTime = pwtx->nTimeSmart;
+ if (!nSmartTime)
+ nSmartTime = pwtx->nTimeReceived;
+ }
+ else
+ nSmartTime = pacentry->nTime;
+ if (nSmartTime <= latestTolerated)
+ {
+ latestEntry = nSmartTime;
+ if (nSmartTime > latestNow)
+ latestNow = nSmartTime;
+ break;
}
}
-
- int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime();
- wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
}
- else
- LogPrintf("AddToWallet(): found %s in block %s not in index\n",
- wtxIn.GetHash().ToString(),
- wtxIn.hashBlock.ToString());
+
+ int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime();
+ wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
}
- AddToSpends(hash);
+ else
+ LogPrintf("AddToWallet(): found %s in block %s not in index\n",
+ wtxIn.GetHash().ToString(),
+ wtxIn.hashBlock.ToString());
}
+ AddToSpends(hash);
+ }
- bool fUpdated = false;
- if (!fInsertedNew)
+ bool fUpdated = false;
+ if (!fInsertedNew)
+ {
+ // Merge
+ if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock)
{
- // Merge
- if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock)
- {
- wtx.hashBlock = wtxIn.hashBlock;
- fUpdated = true;
- }
- // If no longer abandoned, update
- if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned())
- {
- wtx.hashBlock = wtxIn.hashBlock;
- fUpdated = true;
- }
- if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex))
- {
- wtx.nIndex = wtxIn.nIndex;
- fUpdated = true;
- }
- if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
- {
- wtx.fFromMe = wtxIn.fFromMe;
- fUpdated = true;
- }
+ wtx.hashBlock = wtxIn.hashBlock;
+ fUpdated = true;
+ }
+ // If no longer abandoned, update
+ if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned())
+ {
+ wtx.hashBlock = wtxIn.hashBlock;
+ fUpdated = true;
+ }
+ if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex))
+ {
+ wtx.nIndex = wtxIn.nIndex;
+ fUpdated = true;
+ }
+ if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
+ {
+ wtx.fFromMe = wtxIn.fFromMe;
+ fUpdated = true;
}
+ }
- //// debug print
- LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
+ //// debug print
+ LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
- // Write to disk
- if (fInsertedNew || fUpdated)
- if (!pwalletdb->WriteTx(wtx))
- return false;
+ // Write to disk
+ if (fInsertedNew || fUpdated)
+ if (!walletdb.WriteTx(wtx))
+ return false;
- // Break debit/credit balance caches:
- wtx.MarkDirty();
+ // Break debit/credit balance caches:
+ wtx.MarkDirty();
- // Notify UI of new or updated transaction
- NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
+ // Notify UI of new or updated transaction
+ NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
- // notify an external script when a wallet transaction comes in or is updated
- std::string strCmd = GetArg("-walletnotify", "");
+ // notify an external script when a wallet transaction comes in or is updated
+ std::string strCmd = GetArg("-walletnotify", "");
- if ( !strCmd.empty())
- {
- boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
- boost::thread t(runCommand, strCmd); // thread runs free
- }
+ if ( !strCmd.empty())
+ {
+ boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
+ boost::thread t(runCommand, strCmd); // thread runs free
+ }
+
+ return true;
+}
+bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
+{
+ uint256 hash = wtxIn.GetHash();
+
+ mapWallet[hash] = wtxIn;
+ CWalletTx& wtx = mapWallet[hash];
+ wtx.BindWallet(this);
+ wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
+ AddToSpends(hash);
+ BOOST_FOREACH(const CTxIn& txin, wtx.vin) {
+ if (mapWallet.count(txin.prevout.hash)) {
+ CWalletTx& prevtx = mapWallet[txin.prevout.hash];
+ if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
+ MarkConflicted(prevtx.hashBlock, wtx.GetHash());
+ }
+ }
}
+
return true;
}
@@ -872,18 +886,18 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
* pblock is optional, but should be provided if the transaction is known to be in a block.
* If fUpdate is true, existing transactions will be updated.
*/
-bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
{
AssertLockHeld(cs_wallet);
- if (pblock) {
+ if (posInBlock != -1) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
if (range.first->second != tx.GetHash()) {
- LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pblock->GetHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);
- MarkConflicted(pblock->GetHash(), range.first->second);
+ LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);
+ MarkConflicted(pIndex->GetBlockHash(), range.first->second);
}
range.first++;
}
@@ -897,14 +911,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
CWalletTx wtx(this,tx);
// Get merkle branch if transaction was found in a block
- if (pblock)
- wtx.SetMerkleBranch(*pblock);
+ if (posInBlock != -1)
+ wtx.SetMerkleBranch(pIndex, posInBlock);
- // Do not flush the wallet here for performance reasons
- // this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism
- CWalletDB walletdb(strWalletFile, "r+", false);
-
- return AddToWallet(wtx, false, &walletdb);
+ return AddToWallet(wtx, false);
}
}
return false;
@@ -1027,11 +1037,11 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
}
-void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock)
+void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock)
{
LOCK2(cs_main, cs_wallet);
- if (!AddToWalletIfInvolvingMe(tx, pblock, true))
+ if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1084,7 +1094,7 @@ isminetype CWallet::IsMine(const CTxOut& txout) const
CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const
{
if (!MoneyRange(txout.nValue))
- throw std::runtime_error("CWallet::GetCredit(): value out of range");
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
return ((IsMine(txout) & filter) ? txout.nValue : 0);
}
@@ -1113,7 +1123,7 @@ bool CWallet::IsChange(const CTxOut& txout) const
CAmount CWallet::GetChange(const CTxOut& txout) const
{
if (!MoneyRange(txout.nValue))
- throw std::runtime_error("CWallet::GetChange(): value out of range");
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0);
}
@@ -1137,7 +1147,7 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co
{
nDebit += GetDebit(txin, filter);
if (!MoneyRange(nDebit))
- throw std::runtime_error("CWallet::GetDebit(): value out of range");
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
}
return nDebit;
}
@@ -1149,7 +1159,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c
{
nCredit += GetCredit(txout, filter);
if (!MoneyRange(nCredit))
- throw std::runtime_error("CWallet::GetCredit(): value out of range");
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
}
return nCredit;
}
@@ -1161,25 +1171,48 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
{
nChange += GetChange(txout);
if (!MoneyRange(nChange))
- throw std::runtime_error("CWallet::GetChange(): value out of range");
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
}
return nChange;
}
-bool CWallet::SetHDMasterKey(const CKey& key)
+CPubKey CWallet::GenerateNewHDMasterKey()
+{
+ CKey key;
+ key.MakeNewKey(true);
+
+ int64_t nCreationTime = GetTime();
+ CKeyMetadata metadata(nCreationTime);
+
+ // calculate the pubkey
+ CPubKey pubkey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubkey));
+
+ // set the hd keypath to "m" -> Master, refers the masterkeyid to itself
+ metadata.hdKeypath = "m";
+ metadata.hdMasterKeyID = pubkey.GetID();
+
+ {
+ LOCK(cs_wallet);
+
+ // mem store the metadata
+ mapKeyMetadata[pubkey.GetID()] = metadata;
+
+ // write the key&metadata to the database
+ if (!AddKeyPubKey(key, pubkey))
+ throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
+ }
+
+ return pubkey;
+}
+
+bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
{
LOCK(cs_wallet);
// ensure this wallet.dat can only be opened by clients supporting HD
SetMinVersion(FEATURE_HD);
- // store the key as normal "key"/"ckey" object
- // in the database
- // key metadata is not required
- CPubKey pubkey = key.GetPubKey();
- if (!AddKeyPubKey(key, pubkey))
- throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
-
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
@@ -1194,12 +1227,17 @@ bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
{
LOCK(cs_wallet);
if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain))
- throw runtime_error("AddHDChain(): writing chain failed");
+ throw runtime_error(std::string(__func__) + ": writing chain failed");
hdChain = chain;
return true;
}
+bool CWallet::IsHDEnabled()
+{
+ return !hdChain.masterKeyID.IsNull();
+}
+
int64_t CWalletTx::GetTxTime() const
{
int64_t n = nTimeSmart;
@@ -1366,9 +1404,10 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
CBlock block;
ReadBlockFromDisk(block, pindex, Params().GetConsensus());
- BOOST_FOREACH(CTransaction& tx, block.vtx)
+ int posInBlock;
+ for (posInBlock = 0; posInBlock < (int)block.vtx.size(); posInBlock++)
{
- if (AddToWalletIfInvolvingMe(tx, &block, fUpdate))
+ if (AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate))
ret++;
}
pindex = chainActive.Next(pindex);
@@ -2414,17 +2453,12 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
LOCK2(cs_main, cs_wallet);
LogPrintf("CommitTransaction:\n%s", wtxNew.ToString());
{
- // This is only to keep the database open to defeat the auto-flush for the
- // duration of this scope. This is the only place where this optimization
- // maybe makes sense; please don't do it anywhere else.
- CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r+") : NULL;
-
// Take key pair from key pool so it won't be used again
reservekey.KeepKey();
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
- AddToWallet(wtxNew, false, pwalletdb);
+ AddToWallet(wtxNew);
// Notify that old coins are spent
set<CWalletTx*> setCoins;
@@ -2434,9 +2468,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
coin.BindWallet(this);
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
}
-
- if (fFileBacked)
- delete pwalletdb;
}
// Track how many getdata requests our transaction gets
@@ -2681,7 +2712,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
if (!setKeyPool.empty())
nEnd = *(--setKeyPool.end()) + 1;
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey())))
- throw runtime_error("TopUpKeyPool(): writing generated key failed");
+ throw runtime_error(std::string(__func__) + ": writing generated key failed");
setKeyPool.insert(nEnd);
LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size());
}
@@ -2708,9 +2739,9 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
nIndex = *(setKeyPool.begin());
setKeyPool.erase(setKeyPool.begin());
if (!walletdb.ReadPool(nIndex, keypool))
- throw runtime_error("ReserveKeyFromKeyPool(): read failed");
+ throw runtime_error(std::string(__func__) + ": read failed");
if (!HaveKey(keypool.vchPubKey.GetID()))
- throw runtime_error("ReserveKeyFromKeyPool(): unknown key in key pool");
+ throw runtime_error(std::string(__func__) + ": unknown key in key pool");
assert(keypool.vchPubKey.IsValid());
LogPrintf("keypool reserve %d\n", nIndex);
}
@@ -2769,7 +2800,7 @@ int64_t CWallet::GetOldestKeyPoolTime()
CWalletDB walletdb(strWalletFile);
int64_t nIndex = *(setKeyPool.begin());
if (!walletdb.ReadPool(nIndex, keypool))
- throw runtime_error("GetOldestKeyPoolTime(): read oldest key in keypool failed");
+ throw runtime_error(std::string(__func__) + ": read oldest key in keypool failed");
assert(keypool.vchPubKey.IsValid());
return keypool.nTime;
}
@@ -2996,11 +3027,11 @@ void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress) const
{
CKeyPool keypool;
if (!walletdb.ReadPool(id, keypool))
- throw runtime_error("GetAllReserveKeyHashes(): read failed");
+ throw runtime_error(std::string(__func__) + ": read failed");
assert(keypool.vchPubKey.IsValid());
CKeyID keyID = keypool.vchPubKey.GetID();
if (!HaveKey(keyID))
- throw runtime_error("GetAllReserveKeyHashes(): unknown key in key pool");
+ throw runtime_error(std::string(__func__) + ": unknown key in key pool");
setAddress.insert(keyID);
}
}
@@ -3107,7 +3138,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
mapKeyBirth[it->first] = it->second.nCreateTime;
// map in which we'll infer heights of other keys
- CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin
+ CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin
std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock;
std::set<CKeyID> setKeys;
GetKeys(setKeys);
@@ -3296,12 +3327,11 @@ bool CWallet::InitLoadWallet()
if (fFirstRun)
{
// Create new keyUser and set as default key
- if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && walletInstance->hdChain.masterKeyID.IsNull()) {
+ if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
// generate a new master key
- CKey key;
- key.MakeNewKey(true);
- if (!walletInstance->SetHDMasterKey(key))
- throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed");
+ CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
+ if (!walletInstance->SetHDMasterKey(masterPubKey))
+ throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
}
CPubKey newDefaultKey;
if (walletInstance->GetKeyFromPool(newDefaultKey)) {
@@ -3314,9 +3344,9 @@ bool CWallet::InitLoadWallet()
}
else if (mapArgs.count("-usehd")) {
bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
- if (!walletInstance->hdChain.masterKeyID.IsNull() && !useHD)
+ if (walletInstance->IsHDEnabled() && !useHD)
return InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile));
- if (walletInstance->hdChain.masterKeyID.IsNull() && useHD)
+ if (!walletInstance->IsHDEnabled() && useHD)
return InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile));
}
@@ -3386,7 +3416,17 @@ bool CWallet::InitLoadWallet()
}
walletInstance->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
+ {
+ LOCK(walletInstance->cs_wallet);
+ LogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
+ LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
+ LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
+ }
+ // Add wallet transactions that aren't already in a block to mapTransactions
+ walletInstance->ReacceptWalletTransactions();
+
pwalletMain = walletInstance;
+
return true;
}
@@ -3501,31 +3541,18 @@ CWalletKey::CWalletKey(int64_t nExpires)
nTimeExpires = nExpires;
}
-int CMerkleTx::SetMerkleBranch(const CBlock& block)
+int CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
{
AssertLockHeld(cs_main);
- CBlock blockTmp;
// Update the tx's hashBlock
- hashBlock = block.GetHash();
+ hashBlock = pindex->GetBlockHash();
- // Locate the transaction
- for (nIndex = 0; nIndex < (int)block.vtx.size(); nIndex++)
- if (block.vtx[nIndex] == *(CTransaction*)this)
- break;
- if (nIndex == (int)block.vtx.size())
- {
- nIndex = -1;
- LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n");
- return 0;
- }
+ // set the position of the transaction in the block
+ nIndex = posInBlock;
// Is the tx in a block that's in the main chain
- BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
- if (mi == mapBlockIndex.end())
- return 0;
- const CBlockIndex* pindex = (*mi).second;
- if (!pindex || !chainActive.Contains(pindex))
+ if (!chainActive.Contains(pindex))
return 0;
return chainActive.Height() - pindex->nHeight + 1;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 3a3cb6d851..c06513650c 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -200,7 +200,7 @@ public:
READWRITE(nIndex);
}
- int SetMerkleBranch(const CBlock& block);
+ int SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock);
/**
* Return depth of transaction in blockchain:
@@ -581,6 +581,9 @@ private:
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
+ bool fFileBacked;
+
+ std::set<int64_t> setKeyPool;
public:
/*
* Main wallet lock.
@@ -591,10 +594,20 @@ public:
*/
mutable CCriticalSection cs_wallet;
- bool fFileBacked;
std::string strWalletFile;
- std::set<int64_t> setKeyPool;
+ void LoadKeyPool(int nIndex, const CKeyPool &keypool)
+ {
+ setKeyPool.insert(nIndex);
+
+ // If no metadata exists yet, create a default with the pool key's
+ // creation time. Note that this may be overwritten by actually
+ // stored metadata for that key later, which is fine.
+ CKeyID keyid = keypool.vchPubKey.GetID();
+ if (mapKeyMetadata.count(keyid) == 0)
+ mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
+ }
+
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
@@ -729,9 +742,10 @@ public:
bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew = false);
void MarkDirty();
- bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock);
- bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate);
+ bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
+ bool LoadToWallet(const CWalletTx& wtxIn);
+ void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock);
+ bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime);
@@ -899,10 +913,16 @@ public:
/* Set the HD chain model (chain child index counters) */
bool SetHDChain(const CHDChain& chain, bool memonly);
+ const CHDChain& GetHDChain() { return hdChain; }
+ /* Returns true if HD is enabled */
+ bool IsHDEnabled();
+
+ /* Generates a new HD master key (will not be activated) */
+ CPubKey GenerateNewHDMasterKey();
+
/* Set the current HD master key (will reset the chain child index counters) */
- bool SetHDMasterKey(const CKey& key);
- const CHDChain& GetHDChain() { return hdChain; }
+ bool SetHDMasterKey(const CPubKey& key);
};
/** A key allocated from the key pool. */
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 72af8ab7b2..e6e9fc484b 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -215,7 +215,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin
Dbc* pcursor = GetCursor();
if (!pcursor)
- throw runtime_error("CWalletDB::ListAccountCreditDebit(): cannot create DB cursor");
+ throw runtime_error(std::string(__func__) + ": cannot create DB cursor");
unsigned int fFlags = DB_SET_RANGE;
while (true)
{
@@ -231,7 +231,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin
else if (ret != 0)
{
pcursor->close();
- throw runtime_error("CWalletDB::ListAccountCreditDebit(): error scanning DB");
+ throw runtime_error(std::string(__func__) + ": error scanning DB");
}
// Unserialize
@@ -400,7 +400,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
if (wtx.nOrderPos == -1)
wss.fAnyUnordered = true;
- pwallet->AddToWallet(wtx, true, NULL);
+ pwallet->LoadToWallet(wtx);
}
else if (strType == "acentry")
{
@@ -556,14 +556,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> nIndex;
CKeyPool keypool;
ssValue >> keypool;
- pwallet->setKeyPool.insert(nIndex);
-
- // If no metadata exists yet, create a default with the pool key's
- // creation time. Note that this may be overwritten by actually
- // stored metadata for that key later, which is fine.
- CKeyID keyid = keypool.vchPubKey.GetID();
- if (pwallet->mapKeyMetadata.count(keyid) == 0)
- pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
+
+ pwallet->LoadKeyPool(nIndex, keypool);
}
else if (strType == "version")
{
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 8705532429..376e7dec59 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -141,7 +141,7 @@ void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex)
}
}
-void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, const CBlock* pblock)
+void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock)
{
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
{
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index 7b52e7775b..a853447267 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -24,7 +24,7 @@ protected:
void Shutdown();
// CValidationInterface
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock);
+ void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock);
void UpdatedBlockTip(const CBlockIndex *pindex);
private: