Merge remote-tracking branch 'upstream/master' into feature/3722-feature_flags

This commit is contained in:
Kris Nuttycombe 2021-04-02 09:02:16 -06:00
commit 041d363232
47 changed files with 2271 additions and 449 deletions

689
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -33,10 +33,18 @@ tracing = "0.1"
tracing-core = "0.1"
tracing-appender = "0.1"
zcash_history = "0.2"
zcash_primitives = "0.4"
zcash_proofs = "0.4"
zcash_primitives = "0.5"
zcash_proofs = "0.5"
ed25519-zebra = "2.0.0"
# Metrics
hyper = { version = "=0.14.2", default-features = false, features = ["server", "tcp", "http1"] }
ipnet = "2"
metrics = "0.14.2"
metrics-exporter-prometheus = "0.3"
thiserror = "1"
tokio = { version = "1.0", features = ["rt", "net", "time", "macros"] }
# Temporary workaround for https://github.com/myrrlyn/funty/issues/3
funty = "=1.1.0"

View File

@ -124,14 +124,24 @@ Files: src/crypto/ctaes/*
Copyright: Copyright (c) 2016 Pieter Wuille
License: Expat
Files: src/rust/include/rust/map.h
Copyright: Copyright (c) 2012 William Swanson
License: Expat-with-advertising-clause
Files: src/rust/include/rust/VA_OPT.hpp
Copyright: Copyright (c) 2019 Will Wray
License: Boost-Software-License-1.0
Files: src/rust/include/tracing.h
src/rust/src/tracing_ffi.rs
Copyright: Copyright (c) 2020 Jack Grigg
License: Expat
Files: src/rust/include/tracing/map.h
Copyright: Copyright (c) 2012 William Swanson
License: Expat-with-advertising-clause
Files: src/rust/src/metrics_ffi/prometheus.rs
Copyright:
2020-2021 The contributors to the metrics project
2021 Jack Grigg
License: Expat
Files: src/secp256k1/*
Copyright: Copyright (c) 2013 Pieter Wuille

View File

@ -0,0 +1,6 @@
scrape_configs:
- job_name: 'zcashd'
scrape_interval: 500ms
metrics_path: '/'
static_configs:
- targets: ['127.0.0.1:9969']

View File

@ -1,12 +1,12 @@
package=native_rust
$(package)_version=1.49.0
$(package)_version=1.51.0
$(package)_download_path=https://static.rust-lang.org/dist
$(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz
$(package)_sha256_hash_linux=8b14446df82f3707d69cf58fed92f18e0bff91621c62baf89288ef70e3e92981
$(package)_sha256_hash_linux=9e125977aa13f012a68fdc6663629c685745091ae244f0587dd55ea4e3a3e42f
$(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz
$(package)_sha256_hash_darwin=fe3e248bc4b0ee0a2595693687ad845c8a8bda824a56c9321520bcca02433716
$(package)_sha256_hash_darwin=765212098a415996b767d1e372ce266caf94027402b269fec33291fffc085ca4
$(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz
$(package)_sha256_hash_freebsd=dced98577e834f511cae8e58290539ad6b8dd40ae512e90d1371f650961bd930
$(package)_sha256_hash_freebsd=d764ab80889460caca86cda7b7ca2ced80544bb477634adc8cade0e27f4f663b
# Mapping from GCC canonical hosts to Rust targets
# If a mapping is not present, we assume they are identical, unless $host_os is
@ -15,9 +15,9 @@ $(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu
$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu
# Mapping from Rust targets to SHA-256 hashes
$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=c58bd4f0738ff662f70e35c19bfa6b8eb12ad54b0fbdce32ee3e50186c04a969
$(package)_rust_std_sha256_hash_x86_64-apple-darwin=c4389a8534b8da3ae3570646d68fea9a25268b17ed138867e31d4517312759af
$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=61275ed8bb8350e58e619a99104b8ba9a4bdd715b2ce03e20cb33f5b19e84a9c
$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=a6ed4abe59dfaf2119e2803f67fd8aef757a622ae3ac9a040946af2b02f4c269
$(package)_rust_std_sha256_hash_x86_64-apple-darwin=2856bc46d3624ae2658897c15388c0c353bea916963a2fc5991c23b920d5678c
$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=55f871bdaf361a26280ca5396297cc7c67237cd86d4ebfe3cbdf9fac14ce0327
define rust_target
$(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(if $(findstring freebsd,$(3)),x86_64-unknown-freebsd,$(2))))
@ -47,12 +47,12 @@ define $(package)_extract_cmds
endef
define $(package)_stage_cmds
bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig && \
bash ./install.sh --without=rust-docs --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig && \
../$(canonical_host)/install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig
endef
else
define $(package)_stage_cmds
bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig
bash ./install.sh --without=rust-docs --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig
endef
endif

View File

@ -1,6 +1,8 @@
# The zcashd Book
[zcashd](README.md)
- [User Documentation](user.md)
- [Metrics](user/metrics.md)
- [Design](design.md)
- [Chain state](design/chain-state.md)
- ["Coins" view](design/coins-view.md)

6
doc/book/src/user.md Normal file
View File

@ -0,0 +1,6 @@
# User Documentation
This section contains user documentation specific to `zcashd`.
See [here](https://zcash.readthedocs.io/) for more general Zcash documentation, as well as
installation instructions for `zcashd`.

View File

@ -0,0 +1,84 @@
# zcashd metrics
## Metrics UI
This is the user interface that `zcashd` displays by default when run. It
displays a small selection of interesting metrics, but is not intended for
programmatic consumption.
## RPC methods
`zcashd` provides the following JSON-RPC methods that expose node metrics:
- Chain:
- `getblockchaininfo`: Various state info regarding block chain processing.
- `gettxoutsetinfo`: Statistics about the unspent transparent transaction output set.
- `getmempoolinfo`: Details on the active state of the TX memory pool.
- P2P network:
- `getnetworkinfo`: Various state info regarding P2P networking.
- `getpeerinfo`: Data about each connected network node.
- `getdeprecationinfo`: The current node version and deprecation block height.
- Miscellaneous
- `getmemoryinfo`: Information about memory usage.
- `getmininginfo`: Mining-related information.
- `getinfo` (deprecated): A small subset of the above metrics.
You can see what each method provides with `zcash-cli help METHOD_NAME`.
## Prometheus support
`zcashd` can optionally expose an HTTP server that acts as a Prometheus scrape
endpoint. The server will respond to `GET` requests on any request path.
To enable the endpoint, add `-prometheusport=<port>` to your `zcashd`
configuration (either in `zcash.conf` or on the command line). After
restarting `zcashd` you can then test the endpoint by querying it:
```
$ curl http://127.0.0.1:<port>
# TYPE zcash_net_out_messages counter
zcash_net_out_messages 181
# TYPE zcash_net_in_bytes_total counter
zcash_net_in_bytes_total 3701998
# TYPE zcash_net_in_messages counter
zcash_net_in_messages 184
# TYPE zcashd_build_info counter
zcashd_build_info{version="v4.2.0"} 1
# TYPE zcash_chain_verified_block_total counter
zcash_chain_verified_block_total 162
...
```
By default, access is restricted to localhost. This can be expanded with
`-metricsallowip=<ip>`, which can specify IPs or subnets. Note that HTTPS is not
supported, and therefore connections to the endpoint are not encrypted or
authenticated. Access to the endpoint should be assumed to compromise the
privacy of node operations, by the provided metrics and/or by timing side
channels. Non-localhost access is **strongly discouraged** if the node has a
wallet holding live funds.
### Example metrics collection with Docker
The example instructions below were tested on Windows 10 using Docker Desktop
with the WSL 2 backend:
```
# Create a storage volume for Grafana (once)
docker volume create grafana-storage
# Create a storage volume for Prometheus (once)
docker volume create prometheus-storage
# Run Prometheus
# You will need to modify ~/contrib/metrics/prometheus.yaml to match the
# endpoint configured with -prometheusmetrics (and possibly also for your Docker
# network setup).
docker run --detach -p 9090:9090 --volume prometheus-storage:/prometheus --volume ~/contrib/metrics/prometheus.yaml:/etc/prometheus/prometheus.yml prom/prometheus
# Run Grafana
docker run --detach -p 3030:3030 --env GF_SERVER_HTTP_PORT=3030 --volume grafana-storage:/var/lib/grafana grafana/grafana
```

View File

@ -36,3 +36,20 @@ blocks and transactions to fewer nodes.
Reducing the maximum connected nodes to a minimum could be desirable if traffic
limits are tiny. Keep in mind that bitcoin's trustless model works best if you are
connected to a handful of nodes.
## 4. Turn off transaction relay (`-blocksonly`)
Forwarding transactions to peers increases the P2P traffic. To only sync blocks
with other peers, you can disable transaction relay.
Be reminded of the effects of this setting.
- Fee estimation will no longer work.
- It sets the flag "-walletbroadcast" to be "0", only if it is currently unset.
Doing so disables the automatic broadcasting of transactions from wallet. Not
relaying other's transactions could hurt your privacy if used while a wallet
is loaded or if you use the node to broadcast transactions.
- If a peer is whitelisted and "-whitelistforcerelay" is set to "1" (which will
also set "whitelistrelay" to "1"), we will still receive and relay their transactions.
- It makes block propagation slower because compact block relay can only be
used when transaction relay is enabled.

View File

@ -4,3 +4,25 @@ release-notes at release time)
Notable changes
===============
Prometheus metrics
------------------
`zcashd` can now be configured to optionally expose an HTTP server that acts as
a Prometheus scrape endpoint. The server will respond to `GET` requests on any
request path.
To enable the endpoint, add `-prometheusport=<port>` to your `zcashd`
configuration (either in `zcash.conf` or on the command line). After
restarting `zcashd` you can then test the endpoint by querying it with e.g.
`curl http://127.0.0.1:<port>`.
By default, access is restricted to localhost. This can be expanded with
`-metricsallowip=<ip>`, which can specify IPs or subnets. Note that HTTPS is not
supported, and therefore connections to the endpoint are not encrypted or
authenticated. Access to the endpoint should be assumed to compromise the
privacy of node operations, by the provided metrics and/or by timing side
channels. Non-localhost access is **strongly discouraged** if the node has a
wallet holding live funds.
The specific metrics names may change in subsequent releases, in particular to
improve interoperability with `zebrad`.

View File

@ -27,6 +27,3 @@ native_b2 1.75.0 2021-03-01
# Google Test 1.10.0 requires adding CMake to the depends system.
googletest 1.10.0 2021-03-01
# We will likely switch to the 1.51.0 release when it is out.
native_rust 1.50.0 2021-07-01

View File

@ -205,7 +205,7 @@ class GithubTagReleaseLister:
class BerkeleyDbReleaseLister:
def known_releases(self):
url = "https://www.oracle.com/technetwork/products/berkeleydb/downloads/index-082944.html"
url = "https://www.oracle.com/database/technologies/related/berkeleydb-downloads.html"
r = requests.get(url)
if r.status_code != 200:
raise RuntimeError("Request to Berkeley DB download directory failed.")

View File

@ -1 +1 @@
1.49.0
1.51.0

View File

@ -355,13 +355,6 @@ public:
"t3PSn5TbMMAEw7Eu36DYctFezRzpX1hzf3M", /* main-index: 45*/
"t3R3Y5vnBLrEn8L6wFjPjBLnxSUQsKnmFpv", /* main-index: 46*/
"t3Pcm737EsVkGTbhsu2NekKtJeG92mvYyoN", /* main-index: 47*/
// "t3PZ9PPcLzgL57XRSG5ND4WNBC9UTFb8DXv", /* main-index: 48*/
// "t3L1WgcyQ95vtpSgjHfgANHyVYvffJZ9iGb", /* main-index: 49*/
// "t3JtoXqsv3FuS7SznYCd5pZJGU9di15mdd7", /* main-index: 50*/
// "t3hLJHrHs3ytDgExxr1mD8DYSrk1TowGV25", /* main-index: 51*/
// "t3fmYHU2DnVaQgPhDs6TMFVmyC3qbWEWgXN", /* main-index: 52*/
// "t3T4WmAp6nrLkJ24iPpGeCe1fSWTPv47ASG", /* main-index: 53*/
// "t3fP6GrDM4QVwdjFhmCxGNbe7jXXXSDQ5dv", /* main-index: 54*/
};
assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight(0));

View File

@ -119,7 +119,7 @@ public:
void Run()
{
ThreadCounter count(*this);
while (running) {
while (true) {
std::unique_ptr<WorkItem> i;
{
std::unique_lock<std::mutex> lock(cs);

View File

@ -64,6 +64,8 @@
#include "zmq/zmqnotificationinterface.h"
#endif
#include <rust/metrics.h>
#include "librustzcash.h"
using namespace std;
@ -329,6 +331,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS));
strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"));
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"));
if (showDebug)
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless '-whitelistforcerelay' is '1', in which case whitelisted peers' transactions will be relayed. RPC transactions are not affected. (default: %u)"), DEFAULT_BLOCKSONLY));
strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS));
strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
@ -396,6 +400,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt("-whitelist=<netmask>", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") +
" " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway"));
strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted inbound peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY));
strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted inbound peers even they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY));
strUsage += HelpMessageOpt("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET));
#ifdef ENABLE_WALLET
@ -410,6 +416,15 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"));
#endif
strUsage += HelpMessageGroup(_("Monitoring options:"));
strUsage += HelpMessageOpt("-metricsallowip=<ip>", _("Allow metrics connections from specified source. "
"Valid for <ip> 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). "
"This option can be specified multiple times. (default: only localhost)"));
strUsage += HelpMessageOpt("-metricsbind=<addr>", _("Bind to given address to listen for metrics connections. (default: bind to all interfaces)"));
strUsage += HelpMessageOpt("-prometheusport=<port>", _("Expose node metrics in the Prometheus exposition format. "
"An HTTP listener will be started on <port>, which responds to GET requests on any request path. "
"Use -metricsallowip and -metricsbind to control access."));
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
if (showDebug)
{
@ -798,6 +813,22 @@ void InitParameterInteraction()
if (SoftSetBoolArg("-rescan", true))
LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
}
// disable walletbroadcast and whitelistrelay in blocksonly mode
if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (SoftSetBoolArg("-whitelistrelay", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__);
#ifdef ENABLE_WALLET
if (SoftSetBoolArg("-walletbroadcast", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
#endif
}
// Forcing relay from whitelisted hosts implies we will accept relays from them in the first place.
if (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
}
void InitLogging()
@ -911,6 +942,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 2: parameter interactions
const CChainParams& chainparams = Params();
// also see: InitParameterInteraction()
// Set this early so that experimental features are correctly enabled/disabled
auto err = InitExperimentalMode();
if (err) {
@ -927,7 +960,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
#endif
}
// Make sure enough file descriptors are available
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
@ -1221,6 +1254,34 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Count uptime
MarkStartTime();
int prometheusPort = GetArg("-prometheusport", -1);
if (prometheusPort > 0) {
const std::vector<std::string>& vAllow = mapMultiArgs["-metricsallowip"];
std::vector<const char*> vAllowCstr;
for (const std::string& strAllow : vAllow) {
vAllowCstr.push_back(strAllow.c_str());
}
std::string metricsBind = GetArg("-metricsbind", "");
const char* metricsBindCstr = nullptr;
if (!metricsBind.empty()) {
metricsBindCstr = metricsBind.c_str();
}
// Start up the metrics runtime. This spins off a Rust thread that runs
// the Prometheus exporter. We just let this thread die at process end.
LogPrintf("metrics thread start");
if (!metrics_run(metricsBindCstr, vAllowCstr.data(), vAllowCstr.size(), prometheusPort)) {
return InitError(strprintf(_("Failed to start Prometheus metrics exporter")));
}
}
// Expose binary metadata to metrics, using a single time series with value 1.
// https://www.robustperception.io/exposing-the-software-version-to-prometheus
MetricsIncrementCounter(
"zcashd.build.info",
"version", CLIENT_BUILD.c_str());
if ((chainparams.NetworkIDString() != "regtest") &&
GetBoolArg("-showmetrics", isatty(STDOUT_FILENO)) &&
!fPrintToConsole && !GetBoolArg("-daemon", false)) {

View File

@ -18,6 +18,7 @@ bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) con
{
CKey key;
if (!GetKey(address, key)) {
LOCK(cs_KeyStore);
WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
if (it != mapWatchKeys.end()) {
vchPubKeyOut = it->second;

View File

@ -46,6 +46,7 @@
#include <boost/thread.hpp>
#include <rust/ed25519.h>
#include <rust/metrics.h>
using namespace std;
@ -64,7 +65,7 @@ CCriticalSection cs_main;
BlockMap mapBlockIndex;
CChain chainActive;
CBlockIndex *pindexBestHeader = NULL;
static int64_t nTimeBestReceived = 0;
static std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
@ -361,7 +362,7 @@ int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consens
void InitializeNode(NodeId nodeid, const CNode *pnode) {
LOCK(cs_main);
CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second;
state.name = pnode->addrName;
state.name = pnode->GetAddrName();
state.address = pnode->addr;
}
@ -1737,6 +1738,10 @@ bool AcceptToMemoryPool(
}
pool.EnsureSizeLimit();
MetricsGauge("zcash.mempool.size.transactions", mempool.size());
MetricsGauge("zcash.mempool.size.bytes", mempool.GetTotalTxSize());
MetricsGauge("zcash.mempool.usage.bytes", mempool.DynamicMemoryUsage());
}
}
@ -3256,6 +3261,85 @@ void PruneAndFlush() {
FlushStateToDisk(Params(), state, FLUSH_STATE_NONE);
}
struct PoolMetrics {
std::optional<size_t> created;
std::optional<size_t> spent;
std::optional<size_t> unspent;
std::optional<CAmount> value;
static PoolMetrics Sprout(CBlockIndex *pindex, CCoinsViewCache *view) {
PoolMetrics stats;
stats.value = pindex->nChainSproutValue;
// RewindBlockIndex calls DisconnectTip in a way that can potentially cause a
// Sprout tree to not exist (the rewind_index RPC test reliably triggers this).
// We only need to access the tree during disconnection for metrics purposes, and
// we will never encounter this rewind situation on either mainnet or testnet, so
// if we can't access the Sprout tree we default to zero.
SproutMerkleTree sproutTree;
if (view->GetSproutAnchorAt(pindex->hashFinalSproutRoot, sproutTree)) {
stats.created = sproutTree.size();
} else {
stats.created = 0;
}
return stats;
}
static PoolMetrics Sapling(CBlockIndex *pindex, CCoinsViewCache *view) {
PoolMetrics stats;
stats.value = pindex->nChainSaplingValue;
// Before Sapling activation, the Sapling commitment set is empty.
SaplingMerkleTree saplingTree;
if (view->GetSaplingAnchorAt(pindex->hashFinalSaplingRoot, saplingTree)) {
stats.created = saplingTree.size();
} else {
stats.created = 0;
}
return stats;
}
static PoolMetrics Transparent(CBlockIndex *pindex, CCoinsViewCache *view) {
PoolMetrics stats;
// TODO: Collect transparent pool value.
// TODO: Figure out a way to efficiently collect UTXO set metrics
// (view->GetStats() is too slow to call during block verification).
return stats;
}
};
#define RenderPoolMetrics(poolName, poolMetrics) \
do { \
if (poolMetrics.created) { \
MetricsStaticGauge( \
"zcash.pool.notes.created", \
poolMetrics.created.value(), \
"name", poolName); \
} \
if (poolMetrics.spent) { \
MetricsStaticGauge( \
"zcash.pool.notes.spent", \
poolMetrics.spent.value(), \
"name", poolName); \
} \
if (poolMetrics.unspent) { \
MetricsStaticGauge( \
"zcash.pool.notes.unspent", \
poolMetrics.unspent.value(), \
"name", poolName); \
} \
if (poolMetrics.value) { \
MetricsStaticGauge( \
"zcash.pool.value.zatoshis", \
poolMetrics.value.value(), \
"name", poolName); \
} \
} while (0)
/** Update chainActive and related internal data structures. */
void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
chainActive.SetTip(pindexNew);
@ -3283,6 +3367,15 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
"progress", progress.c_str(),
"cache", cache.c_str());
auto sproutPool = PoolMetrics::Sprout(pindexNew, pcoinsTip);
auto saplingPool = PoolMetrics::Sapling(pindexNew, pcoinsTip);
auto transparentPool = PoolMetrics::Transparent(pindexNew, pcoinsTip);
MetricsGauge("zcash.chain.verified.block.height", pindexNew->nHeight);
RenderPoolMetrics("sprout", sproutPool);
RenderPoolMetrics("sapling", saplingPool);
RenderPoolMetrics("transparent", transparentPool);
cvBlockChange.notify_all();
}
@ -3414,6 +3507,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
MetricsIncrementCounter("zcash.chain.verified.block.total");
MetricsHistogram("zcash.chain.verified.block.seconds", (nTime6 - nTime1) * 0.000001);
return true;
}
@ -3633,12 +3728,13 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
*/
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock)
{
CBlockIndex *pindexNewTip = NULL;
CBlockIndex *pindexMostWork = NULL;
CBlockIndex *pindexNewTip = NULL;
do {
boost::this_thread::interruption_point();
bool fInitialDownload;
int nNewHeight;
{
LOCK(cs_main);
pindexMostWork = FindMostWorkChain();
@ -3652,6 +3748,7 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams,
pindexNewTip = chainActive.Tip();
fInitialDownload = IsInitialBlockDownload(chainparams.GetConsensus());
nNewHeight = chainActive.Height();
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
@ -3667,13 +3764,13 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams,
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
}
// Notify external listeners about the new tip.
GetMainSignals().UpdatedBlockTip(pindexNewTip);
}
} while(pindexMostWork != chainActive.Tip());
} while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus());
// Write changes periodically to disk, after relay.
@ -5542,10 +5639,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
pfrom->PushMessage("block", block);
else // MSG_FILTERED_BLOCK)
{
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
bool send = false;
CMerkleBlock merkleBlock;
{
CMerkleBlock merkleBlock(block, *pfrom->pfilter);
LOCK(pfrom->cs_filter);
if (pfrom->pfilter) {
send = true;
merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
}
}
if (send) {
pfrom->PushMessage("merkleblock", merkleBlock);
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
@ -5651,6 +5754,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
if (pfrom->nVersion != 0)
{
pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 1);
return false;
}
@ -5659,7 +5763,11 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
CAddress addrMe;
CAddress addrFrom;
uint64_t nNonce = 1;
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
std::string strSubVer;
std::string cleanSubVer;
uint64_t nServices;
vRecv >> pfrom->nVersion >> nServices >> nTime >> addrMe;
pfrom->nServices = nServices;
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
@ -5692,11 +5800,14 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
if (!vRecv.empty())
vRecv >> addrFrom >> nNonce;
if (!vRecv.empty()) {
vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH);
pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer, SAFE_CHARS_SUBVERSION);
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH);
cleanSubVer = SanitizeString(strSubVer, SAFE_CHARS_SUBVERSION);
}
if (!vRecv.empty()) {
int nStartingHeight;
vRecv >> nStartingHeight;
pfrom->nStartingHeight = nStartingHeight;
}
if (!vRecv.empty())
vRecv >> pfrom->nStartingHeight;
if (!vRecv.empty())
vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
else
@ -5710,7 +5821,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
return true;
}
pfrom->addrLocal = addrMe;
pfrom->SetAddrLocal(addrMe);
if (pfrom->fInbound && addrMe.IsRoutable())
{
SeenLocal(addrMe);
@ -5720,10 +5831,18 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
if (pfrom->fInbound)
pfrom->PushVersion();
{
LOCK(pfrom->cs_SubVer);
pfrom->strSubVer = strSubVer;
pfrom->cleanSubVer = cleanSubVer;
}
pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
// Potentially mark this peer as a preferred download peer.
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
{
LOCK(cs_main);
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
}
// Change version
pfrom->PushMessage("verack");
@ -5741,7 +5860,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr, insecure_rand);
} else if (IsPeerAddrLocalGood(pfrom)) {
addr.SetIP(pfrom->addrLocal);
addr.SetIP(addrMe);
LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr, insecure_rand);
}
@ -5776,7 +5895,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n",
pfrom->cleanSubVer, pfrom->nVersion,
cleanSubVer, pfrom->nVersion,
pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
remoteAddr);
@ -5787,6 +5906,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
else if (pfrom->nVersion == 0)
{
// Must have a version message before anything else
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 1);
return false;
}
@ -5834,6 +5954,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
return true;
if (vAddr.size() > 1000)
{
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 20);
return error("message addr size() = %u", vAddr.size());
}
@ -5893,10 +6014,17 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 20);
return error("message inv size() = %u", vInv.size());
}
bool fBlocksOnly = GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
fBlocksOnly = false;
LOCK(cs_main);
std::vector<CInv> vToFetch;
@ -5934,8 +6062,12 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
}
LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
}
} else {
if (!fAlreadyHave && !IsInitialBlockDownload(chainparams.GetConsensus()))
}
else
{
if (fBlocksOnly)
LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id);
else if (!fAlreadyHave && !IsInitialBlockDownload(chainparams.GetConsensus()))
pfrom->AskFor(inv);
}
@ -5959,6 +6091,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 20);
return error("message getdata size() = %u", vInv.size());
}
@ -6062,6 +6195,14 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
else if (strCommand == "tx" && !IsInitialBlockDownload(chainparams.GetConsensus()))
{
// Stop processing the transaction early if
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
{
LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id);
return true;
}
vector<uint256> vWorkQueue;
vector<uint256> vEraseQueue;
CTransaction tx;
@ -6160,7 +6301,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
assert(recentRejects);
recentRejects->insert(tx.GetHash());
if (pfrom->fWhitelisted) {
if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
// Always relay transactions received from whitelisted peers, even
// if they were already in the mempool or rejected from it due
// to policy, allowing the node to function as a gateway for
@ -6200,6 +6341,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 20);
return error("headers message size = %u", nCount);
}
@ -6397,7 +6539,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
if (pingUsecTime > 0) {
// Successful ping time measurement, replace previous
pfrom->nPingUsecTime = pingUsecTime;
pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime);
pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime.load(), pingUsecTime);
} else {
// This should never happen
sProblem = "Timing mishap";
@ -6460,6 +6602,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
// This isn't a Misbehaving(100) (immediate ban) because the
// peer might be an older or different implementation with
// a different signature key, etc.
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 10);
}
}
@ -6471,6 +6614,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
strCommand == "filteradd"))
{
if (pfrom->nVersion >= NO_BLOOM_VERSION) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 100);
return false;
} else if (GetBoolArg("-enforcenodebloom", DEFAULT_ENFORCENODEBLOOM)) {
@ -6486,16 +6630,19 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
vRecv >> filter;
if (!filter.IsWithinSizeConstraints())
{
// There is no excuse for sending a too-large filter
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 100);
}
else
{
LOCK(pfrom->cs_filter);
delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter(filter);
pfrom->pfilter->UpdateEmptyFull();
pfrom->fRelayTxes = true;
}
pfrom->fRelayTxes = true;
}
@ -6506,15 +6653,20 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
// Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
// and thus, the maximum size any matched object can have) in a filteradd message
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE)
{
Misbehaving(pfrom->GetId(), 100);
bool bad = false;
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
bad = true;
} else {
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
if (pfrom->pfilter) {
pfrom->pfilter->insert(vData);
else
Misbehaving(pfrom->GetId(), 100);
} else {
bad = true;
}
}
if (bad) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 100);
}
}

View File

@ -60,6 +60,10 @@ struct CNodeStateStats;
static const bool DEFAULT_ALERTS = true;
/** Maximum reorg length we will accept before we shut down and alert the user. */
static const unsigned int MAX_REORG_LENGTH = COINBASE_MATURITY - 1;
/** Default for DEFAULT_WHITELISTRELAY. */
static const bool DEFAULT_WHITELISTRELAY = true;
/** Default for DEFAULT_WHITELISTFORCERELAY. */
static const bool DEFAULT_WHITELISTFORCERELAY = true;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100;
//! -maxtxfee default

View File

@ -31,6 +31,8 @@
#include <boost/thread.hpp>
#include <rust/metrics.h>
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
#define DUMP_ADDRESSES_INTERVAL 900
@ -190,8 +192,9 @@ int GetnScore(const CService& addr)
// Is our peer's addrLocal potentially useful as an external IP source?
bool IsPeerAddrLocalGood(CNode *pnode)
{
return fDiscover && pnode->addr.IsRoutable() && pnode->addrLocal.IsRoutable() &&
!IsLimited(pnode->addrLocal.GetNetwork());
CService addrLocal = pnode->GetAddrLocal();
return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() &&
!IsLimited(addrLocal.GetNetwork());
}
// pushes our own address to a peer
@ -206,7 +209,7 @@ void AdvertizeLocal(CNode *pnode)
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0))
{
addrLocal.SetIP(pnode->addrLocal);
addrLocal.SetIP(pnode->GetAddrLocal());
}
if (addrLocal.IsRoutable())
{
@ -348,9 +351,11 @@ CNode* FindNode(const CSubNet& subNet)
CNode* FindNode(const std::string& addrName)
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
if (pnode->addrName == addrName)
for (CNode* pnode : vNodes) {
if (pnode->GetAddrName() == addrName) {
return (pnode);
}
}
return NULL;
}
@ -370,6 +375,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
return NULL;
// Look for an existing connection
LOCK(cs_vNodes);
CNode* pnode = FindNode((CService)addrConnect);
if (pnode)
{
@ -406,8 +412,6 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
vNodes.push_back(pnode);
}
pnode->nTimeConnected = GetTime();
return pnode;
} else if (!proxyConnectionFailed) {
// If connecting to the node failed, and failure is not caused by a problem connecting to
@ -441,10 +445,12 @@ static void DumpBanlist()
void CNode::CloseSocketDisconnect()
{
fDisconnect = true;
if (hSocket != INVALID_SOCKET)
{
LogPrint("net", "disconnecting peer=%d\n", id);
CloseSocket(hSocket);
LOCK(cs_hSocket);
if (hSocket != INVALID_SOCKET) {
LogPrint("net", "disconnecting peer=%d\n", id);
CloseSocket(hSocket);
}
}
// in case this fails, we'll empty the recv buffer when the CNode is deleted
@ -466,7 +472,7 @@ void CNode::PushVersion()
else
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
nLocalHostNonce, strSubVersion, nBestHeight, true);
nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY));
}
@ -634,21 +640,61 @@ void CNode::AddWhitelistedRange(const CSubNet &subnet) {
vWhitelistedRange.push_back(subnet);
}
std::string CNode::GetAddrName() const {
LOCK(cs_addrName);
return addrName;
}
void CNode::MaybeSetAddrName(const std::string& addrNameIn) {
LOCK(cs_addrName);
if (addrName.empty()) {
addrName = addrNameIn;
}
}
CService CNode::GetAddrLocal() const {
LOCK(cs_addrLocal);
return addrLocal;
}
void CNode::SetAddrLocal(const CService& addrLocalIn) {
LOCK(cs_addrLocal);
if (addrLocal.IsValid()) {
error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToString(), addrLocalIn.ToString());
} else {
addrLocal = addrLocalIn;
}
}
void CNode::copyStats(CNodeStats &stats)
{
stats.nodeid = this->GetId();
stats.nServices = nServices;
{
LOCK(cs_filter);
stats.fRelayTxes = fRelayTxes;
}
stats.nLastSend = nLastSend;
stats.nLastRecv = nLastRecv;
stats.nTimeConnected = nTimeConnected;
stats.nTimeOffset = nTimeOffset;
stats.addrName = addrName;
stats.addrName = GetAddrName();
stats.nVersion = nVersion;
stats.cleanSubVer = cleanSubVer;
{
LOCK(cs_SubVer);
stats.cleanSubVer = cleanSubVer;
}
stats.fInbound = fInbound;
stats.nStartingHeight = nStartingHeight;
stats.nSendBytes = nSendBytes;
stats.nRecvBytes = nRecvBytes;
{
LOCK(cs_vSend);
stats.nSendBytes = nSendBytes;
}
{
LOCK(cs_vRecv);
stats.nRecvBytes = nRecvBytes;
}
stats.fWhitelisted = fWhitelisted;
// It is common for nodes with good ping times to suddenly become lagged,
@ -667,7 +713,8 @@ void CNode::copyStats(CNodeStats &stats)
stats.dPingWait = (((double)nPingUsecWait) / 1e6);
// Leave string empty if addrLocal invalid (not filled in yet)
stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : "";
CService addrLocalUnlocked = GetAddrLocal();
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";
}
// requires LOCK(cs_vRecvMsg)
@ -702,6 +749,11 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
if (msg.complete()) {
msg.nTime = GetTimeMicros();
std::string strCommand = SanitizeString(msg.hdr.GetCommand());
MetricsIncrementCounter("zcash.net.in.messages", "command", strCommand.c_str());
MetricsCounter(
"zcash.net.in.bytes", msg.hdr.nMessageSize,
"command", strCommand.c_str());
messageHandlerCondition.notify_one();
}
}
@ -772,10 +824,19 @@ void SocketSendData(CNode *pnode)
while (it != pnode->vSendMsg.end()) {
const CSerializeData &data = *it;
assert(data.size() > pnode->nSendOffset);
int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
int nBytes = 0;
{
LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET)
break;
nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
}
if (nBytes > 0) {
pnode->nLastSend = GetTime();
pnode->nSendBytes += nBytes;
{
LOCK(pnode->cs_vSend);
pnode->nSendBytes += nBytes;
}
pnode->nSendOffset += nBytes;
pnode->RecordBytesSent(nBytes);
if (pnode->nSendOffset == data.size()) {
@ -1104,6 +1165,7 @@ void ThreadSocketHandler()
}
if (vNodesSize != nPrevNodeCount) {
nPrevNodeCount = vNodesSize;
MetricsGauge("zcash.net.peers", nPrevNodeCount);
uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
}
@ -1133,12 +1195,6 @@ void ThreadSocketHandler()
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
{
if (pnode->hSocket == INVALID_SOCKET)
continue;
FD_SET(pnode->hSocket, &fdsetError);
hSocketMax = max(hSocketMax, pnode->hSocket);
have_fds = true;
// Implement the following logic:
// * If there is data to send, select() for sending data. As this only
// happens when optimistic write failed, we choose to first drain the
@ -1154,19 +1210,35 @@ void ThreadSocketHandler()
// * We send some data.
// * We wait for data to be received (and disconnect after timeout).
// * We process a message in the buffer (message handler thread).
bool select_send;
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend && !pnode->vSendMsg.empty()) {
FD_SET(pnode->hSocket, &fdsetSend);
continue;
}
select_send = lockSend && !pnode->vSendMsg.empty();
}
bool select_recv;
{
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv && (
select_recv = lockRecv && (
pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() ||
pnode->GetTotalRecvSize() <= ReceiveFloodSize()))
FD_SET(pnode->hSocket, &fdsetRecv);
pnode->GetTotalRecvSize() <= ReceiveFloodSize());
}
LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET)
continue;
FD_SET(pnode->hSocket, &fdsetError);
hSocketMax = max(hSocketMax, pnode->hSocket);
have_fds = true;
if (select_send) {
FD_SET(pnode->hSocket, &fdsetSend);
continue;
}
if (select_recv) {
FD_SET(pnode->hSocket, &fdsetRecv);
}
}
}
@ -1219,9 +1291,18 @@ void ThreadSocketHandler()
//
// Receive
//
if (pnode->hSocket == INVALID_SOCKET)
continue;
if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError))
bool recvSet = false;
bool sendSet = false;
bool errorSet = false;
{
LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET)
continue;
recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv);
sendSet = FD_ISSET(pnode->hSocket, &fdsetSend);
errorSet = FD_ISSET(pnode->hSocket, &fdsetError);
}
if (recvSet || errorSet)
{
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv)
@ -1229,13 +1310,22 @@ void ThreadSocketHandler()
{
// typical socket buffer is 8K-64K
char pchBuf[0x10000];
int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
int nBytes = 0;
{
LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET)
continue;
nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
}
if (nBytes > 0)
{
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
pnode->CloseSocketDisconnect();
pnode->nLastRecv = GetTime();
pnode->nRecvBytes += nBytes;
{
LOCK(pnode->cs_vRecv);
pnode->nRecvBytes += nBytes;
}
pnode->RecordBytesRecv(nBytes);
}
else if (nBytes == 0)
@ -1263,9 +1353,7 @@ void ThreadSocketHandler()
//
// Send
//
if (pnode->hSocket == INVALID_SOCKET)
continue;
if (FD_ISSET(pnode->hSocket, &fdsetSend))
if (sendSet)
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend)
@ -1922,9 +2010,11 @@ public:
~CNetCleanup()
{
// Close sockets
for (CNode* pnode : vNodes)
for (CNode* pnode : vNodes) {
LOCK(pnode->cs_hSocket);
if (pnode->hSocket != INVALID_SOCKET)
CloseSocket(pnode->hSocket);
}
for (ListenSocket& hListenSocket : vhListenSocket)
if (hListenSocket.socket != INVALID_SOCKET)
if (!CloseSocket(hListenSocket.socket))
@ -2001,12 +2091,14 @@ void CNode::RecordBytesRecv(uint64_t bytes)
{
LOCK(cs_totalBytesRecv);
nTotalBytesRecv += bytes;
MetricsCounter("zcash.net.in.bytes.total", bytes);
}
void CNode::RecordBytesSent(uint64_t bytes)
{
LOCK(cs_totalBytesSent);
nTotalBytesSent += bytes;
MetricsCounter("zcash.net.out.bytes.total", bytes);
uint64_t now = GetTime();
if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now)
@ -2147,6 +2239,7 @@ unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAX
CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) :
ssSend(SER_NETWORK, INIT_PROTO_VERSION),
nTimeConnected(GetTime()),
addr(addrIn),
nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)),
addrKnown(5000, 0.001),
@ -2159,7 +2252,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
nLastRecv = 0;
nSendBytes = 0;
nRecvBytes = 0;
nTimeConnected = GetTime();
nTimeOffset = 0;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0;
@ -2218,11 +2310,11 @@ CNode::~CNode()
void CNode::ReloadTracingSpan()
{
if (fLogIPs) {
span = TracingSpanFields("info", "net", "Peer",
span = TracingSpan("info", "net", "Peer",
"id", idStr.c_str(),
"addr", addrName.c_str());
} else {
span = TracingSpanFields("info", "net", "Peer",
span = TracingSpan("info", "net", "Peer",
"id", idStr.c_str());
}
}
@ -2265,13 +2357,16 @@ void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSen
{
ENTER_CRITICAL_SECTION(cs_vSend);
assert(ssSend.size() == 0);
assert(strSendCommand.empty());
ssSend << CMessageHeader(Params().MessageStart(), pszCommand, 0);
LogPrint("net", "sending: %s ", SanitizeString(pszCommand));
strSendCommand = SanitizeString(pszCommand);
LogPrint("net", "sending: %s ", strSendCommand);
}
void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend)
{
ssSend.clear();
strSendCommand.clear();
LEAVE_CRITICAL_SECTION(cs_vSend);
@ -2280,6 +2375,7 @@ void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend)
void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
{
MetricsIncrementCounter("zcash.net.out.messages", "command", strSendCommand.c_str());
// The -*messagestest options are intentionally not documented in the help message,
// since they are only used during development to debug the networking code and are
// not intended for end-users.
@ -2313,6 +2409,10 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData());
ssSend.GetAndClear(*it);
nSendSize += (*it).size();
MetricsCounter(
"zcash.net.out.bytes", (*it).size(),
"command", strSendCommand.c_str());
strSendCommand.clear();
// If write queue empty, attempt "optimistic write"
if (it == vSendMsg.begin())

View File

@ -63,6 +63,8 @@ static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
/** The default for -maxuploadtarget. 0 = Unlimited */
static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
/** Default for blocks only*/
static const bool DEFAULT_BLOCKSONLY = false;
/**
* The period before a network upgrade activates, where connections to upgrading peers are preferred (in blocks).
* This was three days for upgrades up to and including Blossom, and is 1.5 days from Heartwood onward.
@ -189,6 +191,7 @@ class CNodeStats
public:
NodeId nodeid;
uint64_t nServices;
bool fRelayTxes;
int64_t nLastSend;
int64_t nLastRecv;
int64_t nTimeConnected;
@ -253,14 +256,17 @@ class CNode
{
public:
// socket
uint64_t nServices;
std::atomic<uint64_t> nServices;
SOCKET hSocket;
CDataStream ssSend;
std::string strSendCommand; // Current command being assembled in ssSend
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
uint64_t nSendBytes;
std::deque<CSerializeData> vSendMsg;
CCriticalSection cs_vSend;
CCriticalSection cs_hSocket;
CCriticalSection cs_vRecv;
std::deque<CInv> vRecvGetData;
std::deque<CNetMessage> vRecvMsg;
@ -268,19 +274,18 @@ public:
uint64_t nRecvBytes;
int nRecvVersion;
int64_t nLastSend;
int64_t nLastRecv;
int64_t nTimeConnected;
int64_t nTimeOffset;
std::atomic<int64_t> nLastSend;
std::atomic<int64_t> nLastRecv;
const int64_t nTimeConnected;
std::atomic<int64_t> nTimeOffset;
const CAddress addr;
std::string addrName;
CService addrLocal;
int nVersion;
// strSubVer is whatever byte array we read from the wire. However, this field is intended
// to be printed out, displayed to humans in various forms and so on. So we sanitize it and
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer;
CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
bool fWhitelisted; // This peer can bypass DoS banning.
bool fOneShot;
bool fClient;
@ -291,14 +296,14 @@ public:
// We use fRelayTxes for two purposes -
// a) it allows us to not relay tx invs before receiving the peer's version message
// b) the peer may tell us in its version message that we should not relay tx invs
// until it has initialized its bloom filter.
// unless it loads a bloom filter.
bool fRelayTxes;
bool fSentAddr;
CSemaphoreGrant grantOutbound;
CCriticalSection cs_filter;
CBloomFilter* pfilter;
int nRefCount;
NodeId id;
std::atomic<int> nRefCount;
const uint64_t nKeyedNetGroup;
@ -324,7 +329,7 @@ protected:
public:
uint256 hashContinue;
int nStartingHeight;
std::atomic<int> nStartingHeight;
// flood relay
std::vector<CAddress> vAddrToSend;
@ -341,15 +346,15 @@ public:
// Ping time measurement:
// The pong reply we're expecting, or 0 if no pong expected.
uint64_t nPingNonceSent;
std::atomic<uint64_t> nPingNonceSent;
// Time (in usec) the last ping was sent, or 0 if no ping was ever sent.
int64_t nPingUsecStart;
std::atomic<int64_t> nPingUsecStart;
// Last measured round-trip time.
int64_t nPingUsecTime;
std::atomic<int64_t> nPingUsecTime;
// Best measured round-trip time.
int64_t nMinPingUsecTime;
std::atomic<int64_t> nMinPingUsecTime;
// Whether a ping is requested.
bool fPingQueued;
std::atomic<bool> fPingQueued;
CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false);
~CNode();
@ -372,6 +377,12 @@ private:
static uint64_t CalculateKeyedNetGroup(const CAddress& ad);
mutable CCriticalSection cs_addrName;
std::string addrName;
CService addrLocal;
mutable CCriticalSection cs_addrLocal;
public:
// Regenerate the span for this CNode. This re-queries the log filter to see
@ -408,6 +419,10 @@ public:
msg.SetVersion(nVersionIn);
}
CService GetAddrLocal() const;
//! May not be called more than once
void SetAddrLocal(const CService& addrLocalIn);
CNode* AddRef()
{
nRefCount++;
@ -696,6 +711,9 @@ public:
//!response the time in seconds left in the current max outbound cycle
// in case of no limit, it will always respond with 0
static uint64_t GetMaxOutboundTimeLeftInCycle();
std::string GetAddrName() const;
//! Sets the addrName only if it was not previously set
void MaybeSetAddrName(const std::string& addrNameIn);
};

View File

@ -68,9 +68,8 @@ static void CopyNodeStats(std::vector<CNodeStats>& vstats)
LOCK(cs_vNodes);
vstats.reserve(vNodes.size());
for (CNode* pnode : vNodes) {
CNodeStats stats;
pnode->copyStats(stats);
vstats.push_back(stats);
vstats.emplace_back();
pnode->copyStats(vstats.back());
}
}
@ -87,6 +86,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp)
" \"addr\":\"host:port\", (string) The IP address and port of the peer\n"
" \"addrlocal\":\"ip:port\", (string) local address\n"
" \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n"
" \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n"
" \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n"
" \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n"
" \"bytessent\": n, (numeric) The total bytes sent\n"
@ -130,6 +130,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp)
if (!(stats.addrLocal.empty()))
obj.pushKV("addrlocal", stats.addrLocal);
obj.pushKV("services", strprintf("%016x", stats.nServices));
obj.pushKV("relaytxes", stats.fRelayTxes);
obj.pushKV("lastsend", stats.nLastSend);
obj.pushKV("lastrecv", stats.nLastRecv);
obj.pushKV("bytessent", stats.nSendBytes);

View File

@ -0,0 +1,156 @@
// Copyright (c) 2019 Will Wray https://keybase.io/willwray
// Copyright (c) 2020 The Zcash developers
//
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
//
// Repo: https://github.com/willwray/VA_OPT
#ifndef ZCASH_RUST_INCLUDE_RUST_VA_OPT_H
#define ZCASH_RUST_INCLUDE_RUST_VA_OPT_H
/*
VA_OPT.hpp
==========
Preprocessor utilities for testing emptiness of macro arguments
and for conditional expansion based on the emptiness of ARGS.
VA_OPT_SUPPORT(?)
1 if __VA_OPT__ support is detected else 0 (see platform note).
C++20's __VA_OPT__ finally provides a reliable test for empty ARGS.
The following macros use __VA_OPT__, if detected, otherwise they
provide 'polyfill' fallbacks (though with some failing edge cases).
IS_EMPTY(...)
1 if the ... ARGS is empty else 0.
IFN(...)
If ... ARGS are not empty then a trailing 'paren expression' (X)
is deparenthesized to X else the trailing (X) term is consumed.
E.g. IFN()(N) vanishes, while IFN(NotEmpty)(N) -> N
In other words, IFN(ARGS) is like __VA_OPT__, but with explicit
(ARGS) in place of an implicit __VA_ARGS__ check.
IFE(...)
If ... ARGS is empty expand trailing 'paren expression' (X) to X
else if ARGS are not empty consume the trailing paren expression.
E.g. IFE(NotEmpty)(E) vanishes, while IFE()(E) -> E
IFNE(...)(N,E...)
If ... ARGS are not empty expands to N else expands to E...
E.g. IFNE(ARG)(X,()) is equivalent to IFN(ARG)(X)IFE(ARG)(())
both put back a terminating () removed by the outer macro call.
Without VA_OPT_SUPPORT these 'emptiness' macros are not perfect;
IS_EMPTY, IFN, IFE, IFNE may cause a compile error ('too few args')
if the argument is a function-like macro name that expects ARG(s).
IBP(...)
IS_BEGIN_PARENS macro to test if an argument is parenthesised:
1 if ... ARGS begins with a 'paren expression' else 0.
Platform note: Current Sept 2019 __VA_OPT__ support:
-------------
Clang -std=c++2a enables it. GCC has it enabled without -std=c++2a
but warns "__VA_OPT__ is not available until C++2a" if another -std
flag is supplied along with -pedantic (dont know how to supress it).
MSVC TBD
Credits
-------
Props to pre-pro pioneers, particularly Paul Mensonides.
The 'emptiness' methods are adapted from BOOST_VMD_IS_EMPTY which,
. in turn, depends on BOOST Preprocessor's BOOST_PP_IS_BEGIN_PARENS
(adapted and exposed here as IBP 'Is Begin Parens'):
www.boost.org/doc/libs/1_71_0/libs/vmd
www.boost.org/doc/libs/1_71_0/libs/preprocessor
*/
#define VA_ARG1(A0,A1,...) A1
// VA_EMPTY works only if __VA_OPT__ is supported, else always -> 1
#define VA_EMPTY(...) VA_ARG1(__VA_OPT__(,)0,1,)
// VA_OPT_SUPPORT helper macro for __VA_OPT__ feature detection.
// Adapted from https://stackoverflow.com/a/48045656/7443483
// Use as #if VA_OPT_SUPPORT(?)
#define VA_OPT_SUPPORT ! VA_EMPTY
#if VA_OPT_SUPPORT(?)
# define IS_EMPTY(...) VA_EMPTY(__VA_ARGS__)
# define IFN(...) VA_EAT __VA_OPT__(()VA_IDENT)
# define IFE(...) VA_IDENT __VA_OPT__(()VA_EAT)
# define IFNE(...) VA_ARGTAIL __VA_OPT__((,)VA_ARG0)
#else
# define IS_EMPTY(...) IFP(IBP(__VA_ARGS__))(IE_GEN_0,IE_IBP)(__VA_ARGS__)
# define IFN(...) IFP(IBP(__VA_ARGS__))(GEN_IDENT,EAT_OR_IDENT)(__VA_ARGS__)
# define IFE(...) IFP(IBP(__VA_ARGS__))(GEN_EAT,IDENT_OR_EAT)(__VA_ARGS__)
# define IFNE(...) IFP(IBP(__VA_ARGS__))(GEN_ARGTAIL,ARG0_OR_TAIL)(__VA_ARGS__)
#endif
#define VA_EAT(...)
#define VA_IDENT(...) __VA_ARGS__
#define VA_ARG0_(A0,...) A0
#define VA_ARG0(...) VA_ARG0_(__VA_ARGS__)
#define VA_ARGTAIL_(A0,...) __VA_ARGS__
#define VA_ARGTAIL(...) VA_ARGTAIL_(__VA_ARGS__)
// IFP helper macros to test IBP for IFN and IS_EMPTY
#define IFP_0(T,...) __VA_ARGS__
#define IFP_1(T,...) T
#define IFP_CAT(A,...) A##__VA_ARGS__
#define IFP(BP) IFP_CAT(IFP_,BP)
// IS_BEGIN_PAREN helper macros adapted from BOOST VMD
#define IBP_CAT_(A,...) A##__VA_ARGS__
#define IBP_CAT(A,...) IBP_CAT_(A,__VA_ARGS__)
#define IBP_ARG0_(A,...) A
#define IBP_ARG0(...) IBP_ARG0_(__VA_ARGS__)
#define IBP_IS_ARGS(...) 1
#define IBP_1 1,
#define IBP_IBP_IS_ARGS 0,
// IBP IS_BEGIN_PAREN returns 1 or 0 if ... ARGS is parenthesised
#define IBP(...) IBP_ARG0(IBP_CAT(IBP_, IBP_IS_ARGS __VA_ARGS__))
// IFN, IFE, IFNE and IF_EMPTY helpers without __VA_OPT__ support
#if ! VA_OPT_SUPPORT(?)
# define IBP_(T,...) IBP_ARG0(IBP_CAT(IF##T##_, IBP_IS_ARGS __VA_ARGS__))
// IS_EMPTY helper macros, depend on IBP
# define IE_REDUCE_IBP(...) ()
# define IE_GEN_0(...) 0
# define IE_IBP(...) IBP(IE_REDUCE_IBP __VA_ARGS__ ())
# define GEN_IDENT(...) VA_IDENT
# define GEN_EAT(...) VA_EAT
# define GEN_ARGTAIL(...) VA_ARGTAIL
# define GEN_ARG0(...) VA_ARG0
// IFN, IFE, IFNE helper macros
# define EAT_OR_IDENT(...) IBP_(N,IE_REDUCE_IBP __VA_ARGS__ ())
# define IFN_1 VA_EAT,
# define IFN_IBP_IS_ARGS VA_IDENT,
# define IDENT_OR_EAT(...) IBP_(E,IE_REDUCE_IBP __VA_ARGS__ ())
# define IFE_1 VA_IDENT,
# define IFE_IBP_IS_ARGS VA_EAT,
# define ARG0_OR_TAIL(...) IBP_(NE,IE_REDUCE_IBP __VA_ARGS__ ())
# define IFNE_1 VA_ARGTAIL,
# define IFNE_IBP_IS_ARGS VA_ARG0,
#endif // IFN and IF_EMPTY defs
#endif // ZCASH_RUST_INCLUDE_RUST_VA_OPT_H

View File

@ -0,0 +1,34 @@
// Copyright (c) 2020 Jack Grigg
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_RUST_INCLUDE_RUST_HELPERS_H
#define ZCASH_RUST_INCLUDE_RUST_HELPERS_H
#include "rust/map.h"
#include "rust/VA_OPT.hpp"
//
// Helper macros
//
#define MAP_PAIR_LIST0(f, x, y, peek, ...) f(x, y) MAP_LIST_NEXT(peek, MAP_PAIR_LIST1)(f, peek, __VA_ARGS__)
#define MAP_PAIR_LIST1(f, x, y, peek, ...) f(x, y) MAP_LIST_NEXT(peek, MAP_PAIR_LIST0)(f, peek, __VA_ARGS__)
/// Applies the function macro `f` to each pair of the remaining parameters and
/// inserts commas between the results.
#define MAP_PAIR_LIST(f, ...) EVAL(MAP_PAIR_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#define T_FIELD_NAME(x, y) x
#define T_FIELD_VALUE(x, y) y
#define T_FIELD_NAMES(...) IFN(__VA_ARGS__)(MAP_PAIR_LIST(T_FIELD_NAME, __VA_ARGS__))
#define T_FIELD_VALUES(...) IFN(__VA_ARGS__)(MAP_PAIR_LIST(T_FIELD_VALUE, __VA_ARGS__))
#define T_DOUBLEESCAPE(a) #a
#define T_ESCAPEQUOTE(a) T_DOUBLEESCAPE(a)
// Computes the length of the given array. This is COUNT_OF from Chromium.
#define T_ARRLEN(x) ((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x])))))
#endif // ZCASH_RUST_INCLUDE_RUST_HELPERS_H

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 William Swanson
* Copyright (C) 2020 The Zcash developers
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@ -26,8 +27,8 @@
* prior written authorization from the authors.
*/
#ifndef ZCASH_RUST_INCLUDE_TRACING_MAP_H
#define ZCASH_RUST_INCLUDE_TRACING_MAP_H
#ifndef ZCASH_RUST_INCLUDE_RUST_MAP_H
#define ZCASH_RUST_INCLUDE_RUST_MAP_H
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
@ -67,4 +68,4 @@
*/
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#endif // ZCASH_RUST_INCLUDE_TRACING_MAP_H
#endif // ZCASH_RUST_INCLUDE_RUST_MAP_H

View File

@ -0,0 +1,314 @@
// Copyright (c) 2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_RUST_INCLUDE_RUST_METRICS_H
#define ZCASH_RUST_INCLUDE_RUST_METRICS_H
#include "rust/helpers.h"
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/// Initializes the metrics runtime and runs the Prometheus exporter in a new
/// thread.
///
/// bind_address is an IP address to bind to, or empty to use the default.
///
/// Returns false on any error.
bool metrics_run(
const char* bind_address,
const char* const* allow_ips,
size_t allow_ips_len,
uint16_t prometheus_port);
struct MetricsCallsite;
typedef struct MetricsCallsite MetricsCallsite;
struct MetricsKey;
typedef struct MetricsKey MetricsKey;
/// Creates a metrics callsite.
///
/// This API supports labels that MUST have static values. For non-static label
/// values, use `metrics_key`.
///
/// You should usually call one of the helper macros such as `MetricsCounter`
/// instead of calling this directly.
///
/// This MUST ONLY be called to assign a `static MetricsCallsite*`, and all
/// string arguments MUST be static `const char*` constants, and MUST be valid
/// UTF-8.
MetricsCallsite* metrics_callsite(
const char* name,
const char* const* label_names,
const char* const* label_values,
size_t labels_len);
/// Creates a metrics key.
///
/// This API supports labels that may not have static values, and is intended
/// to be called for each metrics callsite invocation. As such, it returns null
/// if a metrics recorder is not installed, to save on construction costs.
///
/// You should usually call one of the helper macros such as `MetricsCounter`
/// instead of calling this directly.
///
/// API requirements:
/// - label_names and label_values, if not null, MUST be the same length.
/// - All string arguments MUST be valid UTF-8.
MetricsKey* metrics_key(
const char* name,
const char* const* label_names,
const char* const* label_values,
size_t labels_len);
/// Increments a counter.
///
/// Counters represent a single monotonic value, which means the value can only
/// be incremented, not decremented, and always starts out with an initial value
/// of zero.
void metrics_static_increment_counter(const MetricsCallsite* callsite, uint64_t value);
/// Increments a counter.
///
/// Counters represent a single monotonic value, which means the value can only
/// be incremented, not decremented, and always starts out with an initial value
/// of zero.
void metrics_increment_counter(MetricsKey* key, uint64_t value);
/// Updates a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
void metrics_static_update_gauge(const MetricsCallsite* callsite, double value);
/// Updates a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
void metrics_update_gauge(MetricsKey* callsite, double value);
/// Increments a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
void metrics_static_increment_gauge(const MetricsCallsite* callsite, double value);
/// Increments a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
void metrics_increment_gauge(MetricsKey* callsite, double value);
/// Decrements a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
void metrics_static_decrement_gauge(const MetricsCallsite* callsite, double value);
/// Decrements a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
void metrics_decrement_gauge(MetricsKey* callsite, double value);
/// Records a histogram.
///
/// Histograms measure the distribution of values for a given set of
/// measurements, and start with no initial values.
void metrics_static_record_histogram(const MetricsCallsite* callsite, double value);
/// Records a histogram.
///
/// Histograms measure the distribution of values for a given set of
/// measurements, and start with no initial values.
void metrics_record_histogram(MetricsKey* callsite, double value);
#ifdef __cplusplus
}
#endif
//
// Helper macros
//
#ifdef __cplusplus
// Constructs a metrics callsite.
//
// The 'static constexpr' hack ensures that all arguments are compile-time
// constants with static storage duration. The output of this macro MUST be
// stored as a static MetricsCallsite*.
#define M_CALLSITE(name, label_names, label_values) ([&] { \
static constexpr const char* _m_name = name; \
static constexpr const char* const* _m_label_names = \
label_names; \
static constexpr const char* const* _m_label_values = \
label_values; \
return metrics_callsite( \
_m_name, _m_label_names, _m_label_values, \
T_ARRLEN(label_names)); \
}())
#else
// Constructs a metrics callsite.
//
// All arguments MUST be static constants, and the output of this macro MUST be
// stored as a static MetricsCallsite*.
#define M_CALLSITE(name, label_names, label_values) \
metrics_callsite(name, label_names, label_values, T_ARRLEN(label_names))
#endif
// Constructs a metrics key.
#define M_KEY(name, labels, values) \
metrics_key( \
name, \
labels, \
values, \
T_ARRLEN(labels))
//
// Metrics
//
/// Increments a counter.
///
/// Counters represent a single monotonic value, which means the value can only
/// be incremented, not decremented, and always starts out with an initial value
/// of zero.
///
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
#define MetricsCounter(name, value, ...) \
do { \
IFE(__VA_ARGS__) \
(static constexpr const char* const EMPTY[] = {}; \
static MetricsCallsite* CALLSITE = \
M_CALLSITE(name, EMPTY, EMPTY); \
metrics_static_increment_counter(CALLSITE, value);) \
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* M_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
MetricsKey* KEY = \
M_KEY(name, M_LABELS, M_VALUES); \
metrics_increment_counter(KEY, value);) \
} while (0)
/// Increments a counter by one.
///
/// Counters represent a single monotonic value, which means the value can only
/// be incremented, not decremented, and always starts out with an initial value
/// of zero.
///
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
#define MetricsIncrementCounter(name, ...) MetricsCounter(name, 1, __VA_ARGS__)
/// Updates a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
///
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
#define MetricsGauge(name, value, ...) \
do { \
IFE(__VA_ARGS__) \
(static constexpr const char* const EMPTY[] = {}; \
static MetricsCallsite* CALLSITE = \
M_CALLSITE(name, EMPTY, EMPTY); \
metrics_static_update_gauge(CALLSITE, value);) \
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* M_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
MetricsKey* KEY = \
M_KEY(name, M_LABELS, M_VALUES); \
metrics_update_gauge(KEY, value);) \
} while (0)
/// Updates a gauge with optional static labels.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
///
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
#define MetricsStaticGauge(name, value, ...) \
do { \
static constexpr const char* M_LABELS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
static constexpr const char* M_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
static MetricsCallsite* CALLSITE = \
M_CALLSITE(name, M_LABELS, M_VALUES); \
metrics_static_update_gauge(CALLSITE, value); \
} while (0)
/// Increments a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
///
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
#define MetricsIncrementGauge(name, value, ...) \
do { \
IFE(__VA_ARGS__) \
(static constexpr const char* const EMPTY[] = {}; \
static MetricsCallsite* CALLSITE = \
M_CALLSITE(name, EMPTY, EMPTY); \
metrics_static_increment_gauge(CALLSITE, value);) \
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* M_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
MetricsKey* KEY = \
M_KEY(name, M_LABELS, M_VALUES); \
metrics_increment_gauge(KEY, value);) \
} while (0)
/// Decrements a gauge.
///
/// Gauges represent a single value that can go up or down over time, and always
/// starts out with an initial value of zero.
///
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
#define MetricsDecrementGauge(name, value, ...) \
do { \
IFE(__VA_ARGS__) \
(static constexpr const char* const EMPTY[] = {}; \
static MetricsCallsite* CALLSITE = \
M_CALLSITE(name, EMPTY, EMPTY); \
metrics_static_decrement_gauge(CALLSITE, value);) \
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* M_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
MetricsKey* KEY = \
M_KEY(name, M_LABELS, M_VALUES); \
metrics_decrement_gauge(KEY, value);) \
} while (0)
/// Records a histogram.
///
/// Histograms measure the distribution of values for a given set of
/// measurements, and start with no initial values.
///
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
#define MetricsHistogram(name, value, ...) \
do { \
IFE(__VA_ARGS__) \
(static constexpr const char* const EMPTY[] = {}; \
static MetricsCallsite* CALLSITE = \
M_CALLSITE(name, EMPTY, EMPTY); \
metrics_static_record_histogram(CALLSITE, value);) \
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* M_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
MetricsKey* KEY = \
M_KEY(name, M_LABELS, M_VALUES); \
metrics_record_histogram(KEY, value);) \
} while (0)
#endif // ZCASH_RUST_INCLUDE_RUST_METRICS_H

View File

@ -5,8 +5,8 @@
#ifndef ZCASH_RUST_INCLUDE_TRACING_H
#define ZCASH_RUST_INCLUDE_TRACING_H
#include "rust/helpers.h"
#include "rust/types.h"
#include "tracing/map.h"
#include <stddef.h>
#include <stdint.h>
@ -107,25 +107,6 @@ void tracing_log(
// Helper macros
//
#define MAP_PAIR_LIST0(f, x, y, peek, ...) f(x, y) MAP_LIST_NEXT(peek, MAP_PAIR_LIST1)(f, peek, __VA_ARGS__)
#define MAP_PAIR_LIST1(f, x, y, peek, ...) f(x, y) MAP_LIST_NEXT(peek, MAP_PAIR_LIST0)(f, peek, __VA_ARGS__)
/// Applies the function macro `f` to each pair of the remaining parameters and
/// inserts commas between the results.
#define MAP_PAIR_LIST(f, ...) EVAL(MAP_PAIR_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#define T_FIELD_NAME(x, y) x
#define T_FIELD_VALUE(x, y) y
#define T_FIELD_NAMES(...) MAP_PAIR_LIST(T_FIELD_NAME, __VA_ARGS__)
#define T_FIELD_VALUES(...) MAP_PAIR_LIST(T_FIELD_VALUE, __VA_ARGS__)
#define T_DOUBLEESCAPE(a) #a
#define T_ESCAPEQUOTE(a) T_DOUBLEESCAPE(a)
// Computes the length of the given array. This is COUNT_OF from Chromium.
#define T_ARRLEN(x) ((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x])))))
#ifdef __cplusplus
// Constructs a tracing callsite.
//
@ -251,22 +232,6 @@ public:
};
} // namespace tracing
/// Expands to a `tracing::Span` object which is used to record a span.
/// The `Span::Enter` method on that object records that the span has been
/// entered, and returns a RAII guard object, which will exit the span when
/// dropped.
///
/// level, target, and name MUST be static constants, and MUST be valid UTF-8
/// strings.
#define TracingSpan(level, target, name) ([&] { \
static constexpr const char* const FIELDS[] = {}; \
const char* T_VALUES[] = {}; \
static TracingCallsite* CALLSITE = \
T_CALLSITE(name, target, level, FIELDS, true); \
return tracing::Span( \
CALLSITE, T_VALUES, T_ARRLEN(T_VALUES)); \
}())
/// Expands to a `tracing::Span` object which is used to record a span.
/// The `Span::Enter` method on that object records that the span has been
/// entered, and returns a RAII guard object, which will exit the span when
@ -276,15 +241,15 @@ public:
///
/// level, target, name, and all keys MUST be static constants, and MUST be
/// valid UTF-8 strings.
#define TracingSpanFields(level, target, name, ...) ([&] { \
static constexpr const char* const FIELDS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* T_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
static TracingCallsite* CALLSITE = \
T_CALLSITE(name, target, level, FIELDS, true); \
return tracing::Span( \
CALLSITE, T_VALUES, T_ARRLEN(T_VALUES)); \
#define TracingSpan(level, target, name, ...) ([&] { \
static constexpr const char* const FIELDS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* T_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
static TracingCallsite* CALLSITE = \
T_CALLSITE(name, target, level, FIELDS, true); \
return tracing::Span( \
CALLSITE, T_VALUES, T_ARRLEN(T_VALUES)); \
}())
#endif

244
src/rust/src/metrics_ffi.rs Normal file
View File

@ -0,0 +1,244 @@
use libc::{c_char, c_double};
use metrics::{try_recorder, GaugeValue, Key, KeyData, Label};
use metrics_exporter_prometheus::PrometheusBuilder;
use std::ffi::CStr;
use std::net::{IpAddr, SocketAddr};
use std::ptr;
use std::slice;
use tracing::error;
mod prometheus;
#[no_mangle]
pub extern "C" fn metrics_run(
bind_address: *const c_char,
allow_ips: *const *const c_char,
allow_ips_len: usize,
prometheus_port: u16,
) -> bool {
// Parse any allowed IPs.
let allow_ips = unsafe { slice::from_raw_parts(allow_ips, allow_ips_len) };
let mut allow_ips: Vec<ipnet::IpNet> = match allow_ips
.iter()
.map(|&p| unsafe { CStr::from_ptr(p) })
.map(|s| {
s.to_str().ok().and_then(|s| {
s.parse()
.map_err(|e| {
error!("Invalid -metricsallowip argument '{}': {}", s, e);
})
.ok()
})
})
.collect()
{
Some(ips) => ips,
None => {
return false;
}
};
// We always allow localhost.
allow_ips.extend(&["127.0.0.0/8".parse().unwrap(), "::1/128".parse().unwrap()]);
// Parse the address to bind to.
let bind_address = SocketAddr::new(
if allow_ips.is_empty() {
// Default to loopback if not allowing external IPs.
"127.0.0.1".parse::<IpAddr>().unwrap()
} else if bind_address.is_null() {
// No specific bind address specified, bind to any.
"0.0.0.0".parse::<IpAddr>().unwrap()
} else {
match unsafe { CStr::from_ptr(bind_address) }
.to_str()
.ok()
.and_then(|s| s.parse::<IpAddr>().ok())
{
Some(addr) => addr,
None => {
error!("Invalid -metricsbind argument");
return false;
}
}
},
prometheus_port,
);
prometheus::install(bind_address, PrometheusBuilder::new(), allow_ips).is_ok()
}
pub struct FfiCallsite {
key_data: KeyData,
}
#[no_mangle]
pub extern "C" fn metrics_callsite(
name: *const c_char,
label_names: *const *const c_char,
label_values: *const *const c_char,
labels_len: usize,
) -> *mut FfiCallsite {
let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
let labels = unsafe { slice::from_raw_parts(label_names, labels_len) };
let values = unsafe { slice::from_raw_parts(label_values, labels_len) };
let stringify = |s: &[_]| {
s.iter()
.map(|&p| unsafe { CStr::from_ptr(p) })
.map(|cs| cs.to_string_lossy().into_owned())
.collect::<Vec<_>>()
};
let labels = stringify(labels);
let values = stringify(values);
let labels: Vec<_> = labels
.into_iter()
.zip(values.into_iter())
.map(|(name, value)| Label::new(name, value))
.collect();
Box::into_raw(Box::new(FfiCallsite {
key_data: KeyData::from_parts(name, labels),
}))
}
pub struct FfiKey {
inner: Key,
}
#[no_mangle]
pub extern "C" fn metrics_key(
name: *const c_char,
label_names: *const *const c_char,
label_values: *const *const c_char,
labels_len: usize,
) -> *mut FfiKey {
if try_recorder().is_none() {
// No recorder is currently installed, so don't genenerate a key. We check for
// null inside each API that consumes an FfiKey, just in case a recorder was
// installed in a racy way.
ptr::null_mut()
} else {
let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
let labels = unsafe { slice::from_raw_parts(label_names, labels_len) };
let values = unsafe { slice::from_raw_parts(label_values, labels_len) };
let stringify = |s: &[_]| {
s.iter()
.map(|&p| unsafe { CStr::from_ptr(p) })
.map(|cs| cs.to_string_lossy().into_owned())
.collect::<Vec<_>>()
};
let labels = stringify(labels);
let values = stringify(values);
let labels: Vec<_> = labels
.into_iter()
.zip(values.into_iter())
.map(|(name, value)| Label::new(name, value))
.collect();
Box::into_raw(Box::new(FfiKey {
inner: Key::Owned(KeyData::from_parts(name, labels)),
}))
}
}
#[no_mangle]
pub extern "C" fn metrics_static_increment_counter(callsite: *const FfiCallsite, value: u64) {
if let Some(recorder) = try_recorder() {
let callsite = unsafe { callsite.as_ref().unwrap() };
recorder.increment_counter(Key::Borrowed(&callsite.key_data), value);
}
}
#[no_mangle]
pub extern "C" fn metrics_increment_counter(key: *mut FfiKey, value: u64) {
if let Some(recorder) = try_recorder() {
if !key.is_null() {
let key = unsafe { Box::from_raw(key) };
recorder.increment_counter(key.inner, value);
}
}
}
#[no_mangle]
pub extern "C" fn metrics_static_update_gauge(callsite: *const FfiCallsite, value: c_double) {
if let Some(recorder) = try_recorder() {
let callsite = unsafe { callsite.as_ref().unwrap() };
recorder.update_gauge(
Key::Borrowed(&callsite.key_data),
GaugeValue::Absolute(value),
);
}
}
#[no_mangle]
pub extern "C" fn metrics_update_gauge(key: *mut FfiKey, value: c_double) {
if let Some(recorder) = try_recorder() {
if !key.is_null() {
let key = unsafe { Box::from_raw(key) };
recorder.update_gauge(key.inner, GaugeValue::Absolute(value));
}
}
}
#[no_mangle]
pub extern "C" fn metrics_static_increment_gauge(callsite: *const FfiCallsite, value: c_double) {
if let Some(recorder) = try_recorder() {
let callsite = unsafe { callsite.as_ref().unwrap() };
recorder.update_gauge(
Key::Borrowed(&callsite.key_data),
GaugeValue::Increment(value),
);
}
}
#[no_mangle]
pub extern "C" fn metrics_increment_gauge(key: *mut FfiKey, value: c_double) {
if let Some(recorder) = try_recorder() {
if !key.is_null() {
let key = unsafe { Box::from_raw(key) };
recorder.update_gauge(key.inner, GaugeValue::Increment(value));
}
}
}
#[no_mangle]
pub extern "C" fn metrics_static_decrement_gauge(callsite: *const FfiCallsite, value: c_double) {
if let Some(recorder) = try_recorder() {
let callsite = unsafe { callsite.as_ref().unwrap() };
recorder.update_gauge(
Key::Borrowed(&callsite.key_data),
GaugeValue::Decrement(value),
);
}
}
#[no_mangle]
pub extern "C" fn metrics_decrement_gauge(key: *mut FfiKey, value: c_double) {
if let Some(recorder) = try_recorder() {
if !key.is_null() {
let key = unsafe { Box::from_raw(key) };
recorder.update_gauge(key.inner, GaugeValue::Decrement(value));
}
}
}
#[no_mangle]
pub extern "C" fn metrics_static_record_histogram(callsite: *const FfiCallsite, value: c_double) {
if let Some(recorder) = try_recorder() {
let callsite = unsafe { callsite.as_ref().unwrap() };
recorder.record_histogram(Key::Borrowed(&callsite.key_data), value);
}
}
#[no_mangle]
pub extern "C" fn metrics_record_histogram(key: *mut FfiKey, value: c_double) {
if let Some(recorder) = try_recorder() {
if !key.is_null() {
let key = unsafe { Box::from_raw(key) };
recorder.record_histogram(key.inner, value);
}
}
}

View File

@ -0,0 +1,124 @@
// This is mostly code copied from metrics_exporter_prometheus. The copied portions are
// licensed under the same terms as the zcash codebase (reproduced below from
// https://github.com/metrics-rs/metrics/blob/main/metrics-exporter-prometheus/LICENSE):
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
use hyper::{
server::{conn::AddrStream, Server},
service::{make_service_fn, service_fn},
{Body, Error as HyperError, Response, StatusCode},
};
use metrics::SetRecorderError;
use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusRecorder};
use std::future::Future;
use std::io;
use std::net::SocketAddr;
use std::thread;
use thiserror::Error as ThisError;
use tokio::{pin, runtime, select};
/// Errors that could occur while installing a Prometheus recorder/exporter.
#[derive(Debug, ThisError)]
pub enum InstallError {
/// Creating the networking event loop did not succeed.
#[error("failed to spawn Tokio runtime for endpoint: {0}")]
Io(#[from] io::Error),
/// Binding/listening to the given address did not succeed.
#[error("failed to bind to given listen address: {0}")]
Hyper(#[from] HyperError),
/// Installing the recorder did not succeed.
#[error("failed to install exporter as global recorder: {0}")]
Recorder(#[from] SetRecorderError),
}
/// A copy of `PrometheusBuilder::build_with_exporter` that adds support for an IP address
/// or subnet allowlist.
pub(super) fn build(
bind_address: SocketAddr,
builder: PrometheusBuilder,
allow_ips: Vec<ipnet::IpNet>,
) -> Result<
(
PrometheusRecorder,
impl Future<Output = Result<(), HyperError>> + Send + 'static,
),
InstallError,
> {
let recorder = builder.build();
let handle = recorder.handle();
let server = Server::try_bind(&bind_address)?;
let exporter = async move {
let make_svc = make_service_fn(move |socket: &AddrStream| {
let remote_addr = socket.remote_addr().ip();
let allowed = allow_ips.iter().any(|subnet| subnet.contains(&remote_addr));
let handle = handle.clone();
async move {
Ok::<_, HyperError>(service_fn(move |_| {
let handle = handle.clone();
async move {
if allowed {
let output = handle.render();
Ok(Response::new(Body::from(output)))
} else {
Response::builder()
.status(StatusCode::FORBIDDEN)
.body(Body::empty())
}
}
}))
}
});
server.serve(make_svc).await
};
Ok((recorder, exporter))
}
/// A copy of `PrometheusBuilder::install` that adds support for an IP address or subnet
/// allowlist.
pub(super) fn install(
bind_address: SocketAddr,
builder: PrometheusBuilder,
allow_ips: Vec<ipnet::IpNet>,
) -> Result<(), InstallError> {
let runtime = runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let (recorder, exporter) = {
let _guard = runtime.enter();
build(bind_address, builder, allow_ips)?
};
metrics::set_boxed_recorder(Box::new(recorder))?;
thread::Builder::new()
.name("zcash-prometheus".to_string())
.spawn(move || {
runtime.block_on(async move {
pin!(exporter);
loop {
select! {
_ = &mut exporter => {}
}
}
});
})?;
Ok(())
}

View File

@ -63,6 +63,7 @@ use zcash_history::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree}
mod blake2b;
mod ed25519;
mod metrics_ffi;
mod tracing_ffi;
#[cfg(test)]
@ -145,19 +146,18 @@ pub extern "C" fn librustzcash_init_zksnark_params(
);
// Load params
let (spend_params, spend_vk, output_params, output_vk, sprout_vk) =
load_parameters(spend_path, output_path, sprout_path);
let params = load_parameters(spend_path, output_path, sprout_path);
// Caller is responsible for calling this function once, so
// these global mutations are safe.
unsafe {
SAPLING_SPEND_PARAMS = Some(spend_params);
SAPLING_OUTPUT_PARAMS = Some(output_params);
SAPLING_SPEND_PARAMS = Some(params.spend_params);
SAPLING_OUTPUT_PARAMS = Some(params.output_params);
SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned());
SAPLING_SPEND_VK = Some(spend_vk);
SAPLING_OUTPUT_VK = Some(output_vk);
SPROUT_GROTH16_VK = sprout_vk;
SAPLING_SPEND_VK = Some(params.spend_vk);
SAPLING_OUTPUT_VK = Some(params.output_vk);
SPROUT_GROTH16_VK = params.sprout_vk;
}
}
@ -386,7 +386,7 @@ pub extern "C" fn librustzcash_sapling_compute_nf(
let vk = ViewingKey { ak, nk };
let nf = note.nf(&vk, position);
let result = unsafe { &mut *result };
result.copy_from_slice(&nf);
result.copy_from_slice(&nf.0);
true
}

View File

@ -29,7 +29,7 @@ fn test_key_agreement() {
// Grab ivk from our viewing key in serialized form
let ivk = vk.ivk();
let ivk_serialized = ivk.to_bytes();
let ivk_serialized = ivk.to_repr();
// Create random esk
let mut esk = [0u8; 32];

View File

@ -1,7 +1,7 @@
use group::GroupEncoding;
use zcash_primitives::{
constants::SPENDING_KEY_GENERATOR,
primitives::{Diversifier, ProofGenerationKey, Rseed},
primitives::{Diversifier, Nullifier, ProofGenerationKey, Rseed},
};
use crate::{
@ -27,7 +27,7 @@ fn key_components() {
note_cm: [u8; 32],
note_pos: u64,
note_nf: [u8; 32],
};
}
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
let test_vectors = vec![
@ -674,7 +674,7 @@ fn key_components() {
assert_eq!(&nk, &tv.nk);
}
assert_eq!(&fvk.ivk().to_bytes(), &tv.ivk);
assert_eq!(&fvk.ivk().to_repr(), &tv.ivk);
{
let mut ivk = [0u8; 32];
librustzcash_crh_ivk(&tv.ak, &tv.nk, &mut ivk);
@ -698,6 +698,6 @@ fn key_components() {
.unwrap();
assert_eq!(&note.cmu().to_bytes(), &tv.note_cm);
assert_eq!(note.nf(&fvk, tv.note_pos), tv.note_nf);
assert_eq!(note.nf(&fvk, tv.note_pos), Nullifier(tv.note_nf));
}
}

View File

@ -20,7 +20,7 @@ fn sapling_generators() {
wprb: [u8; 32],
vcvb: [u8; 32],
vcrb: [u8; 32],
};
}
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_generators.py
let sapling_generators = SaplingGenerators {

View File

@ -19,7 +19,7 @@ fn notes() {
note_cm: [u8; 32],
note_pos: u64,
note_nf: [u8; 32],
};
}
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
let test_vectors = vec![

View File

@ -14,7 +14,7 @@ fn redjubjub_signatures() {
m: [u8; 32],
sig: [u8; 64],
rsig: [u8; 64],
};
}
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_signatures.py
let test_vectors = vec![

View File

@ -69,6 +69,7 @@ void CScheduler::serviceQueue()
}
}
--nThreadsServicingQueue;
newTaskScheduled.notify_one();
}
void CScheduler::stop(bool drain)

View File

@ -55,11 +55,24 @@ private:
};
typedef std::vector<std::pair<void*, CLockLocation> > LockStack;
typedef std::map<std::pair<void*, void*>, LockStack> LockOrders;
typedef std::set<std::pair<void*, void*> > InvLockOrders;
static boost::mutex dd_mutex;
static std::map<std::pair<void*, void*>, LockStack> lockorders;
static boost::thread_specific_ptr<LockStack> lockstack;
struct LockData {
// Very ugly hack: as the global constructs and destructors run single
// threaded, we use this boolean to know whether LockData still exists,
// as DeleteLock can get called by global CCriticalSection destructors
// after LockData disappears.
bool available;
LockData() : available(true) {}
~LockData() { available = false; }
LockOrders lockorders;
InvLockOrders invlockorders;
boost::mutex dd_mutex;
} static lockdata;
boost::thread_specific_ptr<LockStack> lockstack;
static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
{
@ -116,7 +129,7 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry)
if (lockstack.get() == NULL)
lockstack.reset(new LockStack);
dd_mutex.lock();
boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
(*lockstack).push_back(std::make_pair(c, locklocation));
@ -126,23 +139,21 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry)
break;
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
if (lockorders.count(p1))
if (lockdata.lockorders.count(p1))
continue;
lockorders[p1] = (*lockstack);
lockdata.lockorders[p1] = (*lockstack);
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
if (lockorders.count(p2))
potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]);
lockdata.invlockorders.insert(p2);
if (lockdata.lockorders.count(p2))
potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]);
}
}
dd_mutex.unlock();
}
static void pop_lock()
{
dd_mutex.lock();
(*lockstack).pop_back();
dd_mutex.unlock();
}
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
@ -172,4 +183,26 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
abort();
}
void DeleteLock(void* cs)
{
if (!lockdata.available) {
// We're already shutting down.
return;
}
boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
std::pair<void*, void*> item = std::make_pair(cs, (void*)0);
LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
while (it != lockdata.lockorders.end() && it->first.first == cs) {
std::pair<void*, void*> invitem = std::make_pair(it->first.second, it->first.first);
lockdata.invlockorders.erase(invitem);
lockdata.lockorders.erase(it++);
}
InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
while (invit != lockdata.invlockorders.end() && invit->first == cs) {
std::pair<void*, void*> invinvitem = std::make_pair(invit->second, invit->first);
lockdata.lockorders.erase(invinvitem);
lockdata.invlockorders.erase(invit++);
}
}
#endif /* DEBUG_LOCKORDER */

View File

@ -71,30 +71,39 @@ public:
}
};
/**
* Wrapped boost mutex: supports recursive locking, but no waiting
* TODO: We should move away from using the recursive lock by default.
*/
typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection;
/** Wrapped boost mutex: supports waiting but not recursive locking */
typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
/** Just a typedef for boost::condition_variable, can be wrapped later if desired */
typedef boost::condition_variable CConditionVariable;
#ifdef DEBUG_LOCKORDER
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
void LeaveCritical();
std::string LocksHeld();
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
void DeleteLock(void* cs);
#else
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
void static inline LeaveCritical() {}
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
void static inline DeleteLock(void* cs) {}
#endif
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
/**
* Wrapped boost mutex: supports recursive locking, but no waiting
* TODO: We should move away from using the recursive lock by default.
*/
class CCriticalSection : public AnnotatedMixin<boost::recursive_mutex>
{
public:
~CCriticalSection() {
DeleteLock((void*)this);
}
};
typedef CCriticalSection CDynamicCriticalSection;
/** Wrapped boost mutex: supports waiting but not recursive locking */
typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
/** Just a typedef for boost::condition_variable, can be wrapped later if desired */
typedef boost::condition_variable CConditionVariable;
#ifdef DEBUG_LOCKCONTENTION
void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
#endif
@ -162,7 +171,10 @@ public:
typedef CMutexLock<CCriticalSection> CCriticalBlock;
#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__)
#define PASTE(x, y) x ## y
#define PASTE2(x, y) PASTE(x, y)
#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__)
#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true)

View File

@ -87,6 +87,7 @@ using namespace std;
const char * const BITCOIN_CONF_FILENAME = "zcash.conf";
const char * const BITCOIN_PID_FILENAME = "zcashd.pid";
CCriticalSection cs_args;
map<string, string> mapArgs;
map<string, vector<string> > mapMultiArgs;
bool fDebug = false;
@ -113,6 +114,7 @@ static void InterpretNegativeSetting(std::string& strKey, std::string& strValue)
void ParseParameters(int argc, const char* const argv[])
{
LOCK(cs_args);
mapArgs.clear();
mapMultiArgs.clear();
@ -148,6 +150,7 @@ void ParseParameters(int argc, const char* const argv[])
std::string GetArg(const std::string& strArg, const std::string& strDefault)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
return mapArgs[strArg];
return strDefault;
@ -155,6 +158,7 @@ std::string GetArg(const std::string& strArg, const std::string& strDefault)
int64_t GetArg(const std::string& strArg, int64_t nDefault)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
return atoi64(mapArgs[strArg]);
return nDefault;
@ -162,6 +166,7 @@ int64_t GetArg(const std::string& strArg, int64_t nDefault)
bool GetBoolArg(const std::string& strArg, bool fDefault)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
return InterpretBool(mapArgs[strArg]);
return fDefault;
@ -169,6 +174,7 @@ bool GetBoolArg(const std::string& strArg, bool fDefault)
bool SoftSetArg(const std::string& strArg, const std::string& strValue)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
return false;
mapArgs[strArg] = strValue;
@ -286,7 +292,7 @@ static fs::path ZC_GetDefaultBaseParamsDir()
const fs::path &ZC_GetParamsDir()
{
LOCK(csPathCached); // Reuse the same lock as upstream.
LOCK2(cs_args, csPathCached);
fs::path &path = zc_paramsPathCached;
@ -313,6 +319,7 @@ const fs::path &ZC_GetParamsDir()
const fs::path GetExportDir()
{
fs::path path;
LOCK(cs_args);
if (mapArgs.count("-exportdir")) {
path = fs::system_complete(mapArgs["-exportdir"]);
if (fs::exists(path) && !fs::is_directory(path)) {
@ -328,8 +335,7 @@ const fs::path GetExportDir()
const fs::path &GetDataDir(bool fNetSpecific)
{
LOCK(csPathCached);
LOCK2(cs_args, csPathCached);
fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
@ -357,6 +363,7 @@ const fs::path &GetDataDir(bool fNetSpecific)
void ClearDatadirCache()
{
LOCK(csPathCached);
pathCached = fs::path();
pathCachedNetSpecific = fs::path();
}
@ -389,6 +396,7 @@ void ReadConfigFile(const std::string& confPath,
"externalip",
"fundingstream",
"loadblock",
"metricsallowip",
"nuparams",
"onlynet",
"rpcallowip",
@ -401,23 +409,26 @@ void ReadConfigFile(const std::string& confPath,
};
set<string> unique_options;
for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
{
string strKey = string("-") + it->string_key;
string strValue = it->value[0];
if (find(allowed_duplicates.begin(), allowed_duplicates.end(), it->string_key) == allowed_duplicates.end())
LOCK(cs_args);
for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
{
if (!unique_options.insert(strKey).second) {
throw std::runtime_error(strprintf("Option '%s' is duplicated, which is not allowed.", strKey));
}
}
string strKey = string("-") + it->string_key;
string strValue = it->value[0];
InterpretNegativeSetting(strKey, strValue);
// Don't overwrite existing settings so command line settings override zcash.conf
if (mapSettingsRet.count(strKey) == 0)
mapSettingsRet[strKey] = strValue;
mapMultiSettingsRet[strKey].push_back(strValue);
if (find(allowed_duplicates.begin(), allowed_duplicates.end(), it->string_key) == allowed_duplicates.end())
{
if (!unique_options.insert(strKey).second) {
throw std::runtime_error(strprintf("Option '%s' is duplicated, which is not allowed.", strKey));
}
}
InterpretNegativeSetting(strKey, strValue);
// Don't overwrite existing settings so command line settings override zcash.conf
if (mapSettingsRet.count(strKey) == 0)
mapSettingsRet[strKey] = strValue;
mapMultiSettingsRet[strKey].push_back(strValue);
}
}
// If datadir is changed in .conf file:
ClearDatadirCache();

View File

@ -50,8 +50,9 @@ void MilliSleep(int64_t n)
std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime)
{
static std::locale classic(std::locale::classic());
// std::locale takes ownership of the pointer
std::locale loc(std::locale::classic(), new boost::posix_time::time_facet(pszFormat));
std::locale loc(classic, new boost::posix_time::time_facet(pszFormat));
std::stringstream ss;
ss.imbue(loc);
ss << boost::posix_time::from_time_t(nTime);

View File

@ -24,9 +24,6 @@
using namespace std;
unsigned int nWalletDBUpdated;
//
// CDB
//

View File

@ -24,8 +24,6 @@
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
static const bool DEFAULT_WALLET_PRIVDB = true;
extern unsigned int nWalletDBUpdated;
class CDBEnv
{
private:

View File

@ -4835,7 +4835,7 @@ bool CWallet::InitLoadWallet(bool clearWitnessCaches)
walletInstance->ScanForWalletTransactions(pindexRescan, true);
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
walletInstance->SetBestChain(chainActive.GetLocator());
nWalletDBUpdated++;
CWalletDB::IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")

View File

@ -20,18 +20,21 @@
#include <boost/scoped_ptr.hpp>
#include <boost/thread.hpp>
#include <atomic>
using namespace std;
static uint64_t nAccountingEntryNumber = 0;
static std::atomic<unsigned int> nWalletDBUpdateCounter;
//
// CWalletDB
//
bool CWalletDB::WriteName(const string& strAddress, const string& strName)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(make_pair(string("name"), strAddress), strName);
}
@ -39,37 +42,37 @@ bool CWalletDB::EraseName(const string& strAddress)
{
// This should only be used for sending addresses, never for receiving addresses,
// receiving addresses must always have an address book entry if they're not change return.
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(make_pair(string("name"), strAddress));
}
bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(make_pair(string("purpose"), strAddress), strPurpose);
}
bool CWalletDB::ErasePurpose(const string& strPurpose)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(make_pair(string("purpose"), strPurpose));
}
bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("tx"), hash), wtx);
}
bool CWalletDB::EraseTx(uint256 hash)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("tx"), hash));
}
bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta, false))
@ -89,7 +92,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
const CKeyMetadata &keyMeta)
{
const bool fEraseUnencryptedKey = true;
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta))
@ -111,7 +114,7 @@ bool CWalletDB::WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr,
const CKeyMetadata &keyMeta)
{
const bool fEraseUnencryptedKey = true;
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta))
return false;
@ -131,7 +134,7 @@ bool CWalletDB::WriteCryptedSaplingZKey(
const CKeyMetadata &keyMeta)
{
const bool fEraseUnencryptedKey = true;
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
auto ivk = extfvk.fvk.in_viewing_key();
if (!Write(std::make_pair(std::string("sapzkeymeta"), ivk), keyMeta))
@ -149,13 +152,13 @@ bool CWalletDB::WriteCryptedSaplingZKey(
bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}
bool CWalletDB::WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta))
return false;
@ -167,7 +170,7 @@ bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk,
const libzcash::SaplingExtendedSpendingKey &key,
const CKeyMetadata &keyMeta)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("sapzkeymeta"), ivk), keyMeta))
return false;
@ -179,58 +182,58 @@ bool CWalletDB::WriteSaplingPaymentAddress(
const libzcash::SaplingPaymentAddress &addr,
const libzcash::SaplingIncomingViewingKey &ivk)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("sapzaddr"), addr), ivk, false);
}
bool CWalletDB::WriteSproutViewingKey(const libzcash::SproutViewingKey &vk)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("vkey"), vk), '1');
}
bool CWalletDB::EraseSproutViewingKey(const libzcash::SproutViewingKey &vk)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("vkey"), vk));
}
bool CWalletDB::WriteSaplingExtendedFullViewingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("sapextfvk"), extfvk), '1');
}
bool CWalletDB::EraseSaplingExtendedFullViewingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("sapextfvk"), extfvk));
}
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
}
bool CWalletDB::WriteWatchOnly(const CScript &dest)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
}
bool CWalletDB::EraseWatchOnly(const CScript &dest)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
}
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::string("bestblock"), locator);
}
@ -241,19 +244,19 @@ bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::string("orderposnext"), nOrderPosNext);
}
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::string("defaultkey"), vchPubKey);
}
bool CWalletDB::WriteWitnessCacheSize(int64_t nWitnessCacheSize)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::string("witnesscachesize"), nWitnessCacheSize);
}
@ -264,13 +267,13 @@ bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::ErasePool(int64_t nPool)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("pool"), nPool));
}
@ -904,8 +907,8 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
bool fNoncriticalErrors = false;
DBErrors result = DB_LOAD_OK;
LOCK(pwallet->cs_wallet);
try {
LOCK(pwallet->cs_wallet);
int nMinVersion = 0;
if (Read((string)"minversion", nMinVersion))
{
@ -1105,20 +1108,20 @@ void ThreadFlushWalletDB(const string& strFile)
if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET))
return;
unsigned int nLastSeen = nWalletDBUpdated;
unsigned int nLastFlushed = nWalletDBUpdated;
unsigned int nLastSeen = CWalletDB::GetUpdateCounter();
unsigned int nLastFlushed = CWalletDB::GetUpdateCounter();
int64_t nLastWalletUpdate = GetTime();
while (true)
{
MilliSleep(500);
if (nLastSeen != nWalletDBUpdated)
if (nLastSeen != CWalletDB::GetUpdateCounter())
{
nLastSeen = nWalletDBUpdated;
nLastSeen = CWalletDB::GetUpdateCounter();
nLastWalletUpdate = GetTime();
}
if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2)
{
TRY_LOCK(bitdb.cs_db,lockDb);
if (lockDb)
@ -1139,7 +1142,7 @@ void ThreadFlushWalletDB(const string& strFile)
if (mi != bitdb.mapFileUseCount.end())
{
LogPrint("db", "Flushing %s\n", strFile);
nLastFlushed = nWalletDBUpdated;
nLastFlushed = CWalletDB::GetUpdateCounter();
int64_t nStart = GetTimeMillis();
// Flush wallet file so it's self contained
@ -1282,31 +1285,41 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename)
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
}
bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
}
bool CWalletDB::WriteHDSeed(const HDSeed& seed)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("hdseed"), seed.Fingerprint()), seed.RawSeed());
}
bool CWalletDB::WriteCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned char>& vchCryptedSecret)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("chdseed"), seedFp), vchCryptedSecret);
}
bool CWalletDB::WriteHDChain(const CHDChain& chain)
{
nWalletDBUpdated++;
nWalletDBUpdateCounter++;
return Write(std::string("hdchain"), chain);
}
void CWalletDB::IncrementUpdateCounter()
{
nWalletDBUpdateCounter++;
}
unsigned int CWalletDB::GetUpdateCounter()
{
return nWalletDBUpdateCounter;
}

View File

@ -203,6 +203,8 @@ public:
bool WriteSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk);
bool EraseSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk);
static void IncrementUpdateCounter();
static unsigned int GetUpdateCounter();
private:
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);

View File

@ -1,11 +1,11 @@
#!/usr/bin/env bash
#!/bin/sh
export LC_ALL=C
set -eu -o pipefail
set -eu
set +x
function cmd_pref() {
if type -p "$2" > /dev/null; then
cmd_pref() {
if command -v "$2" >/dev/null; then
eval "$1=$2"
else
eval "$1=$3"
@ -13,7 +13,7 @@ function cmd_pref() {
}
# If a g-prefixed version of the command exists, use it preferentially.
function gprefix() {
gprefix() {
cmd_pref "$1" "g$2" "$2"
}
@ -22,21 +22,21 @@ cd "$(dirname "$("$READLINK" -f "$0")")/.."
# Allow user overrides to $MAKE. Typical usage for users who need it:
# MAKE=gmake ./zcutil/build.sh -j$(nproc)
if [[ -z "${MAKE-}" ]]; then
if [ -z "${MAKE-}" ]; then
MAKE=make
fi
# Allow overrides to $BUILD and $HOST for porters. Most users will not need it.
# BUILD=i686-pc-linux-gnu ./zcutil/build.sh
if [[ -z "${BUILD-}" ]]; then
if [ -z "${BUILD-}" ]; then
BUILD="$(./depends/config.guess)"
fi
if [[ -z "${HOST-}" ]]; then
if [ -z "${HOST-}" ]; then
HOST="$BUILD"
fi
# Allow users to set arbitrary compile flags. Most users will not need this.
if [[ -z "${CONFIGURE_FLAGS-}" ]]; then
if [ -z "${CONFIGURE_FLAGS-}" ]; then
CONFIGURE_FLAGS=""
fi
@ -69,13 +69,13 @@ set -x
eval "$MAKE" --version
as --version
ENABLE_DEBUG_REGEX='^(.*\s)?--enable-debug(\s.*)?$'
if [[ "$CONFIGURE_FLAGS" =~ $ENABLE_DEBUG_REGEX ]]
then
case "$CONFIGURE_FLAGS" in
(*"--enable-debug"*)
DEBUG=1
else
;;
(*)
DEBUG=
fi
;;esac
HOST="$HOST" BUILD="$BUILD" "$MAKE" "$@" -C ./depends/ DEBUG="$DEBUG"

View File

@ -1,9 +1,11 @@
#!/usr/bin/env bash
#!/bin/sh
export LC_ALL=C
set -eu
if [[ "$OSTYPE" == "darwin"* ]]; then
uname_S=$(uname -s 2>/dev/null || echo not)
if [ "$uname_S" = "Darwin" ]; then
PARAMS_DIR="$HOME/Library/Application Support/ZcashParams"
else
PARAMS_DIR="$HOME/.zcash-params"
@ -19,7 +21,7 @@ DOWNLOAD_URL="https://download.z.cash/downloads"
IPFS_HASH="/ipfs/QmXRHVGLQBiKwvNq7c2vPxAKz1zRVmMYbmt7G5TQss7tY7"
SHA256CMD="$(command -v sha256sum || echo shasum)"
SHA256ARGS="$(command -v sha256sum >/dev/null || echo '-a 256')"
SHA256ARGS="$(command -v sha256sum >/dev/null || echo \"-a 256\")"
WGETCMD="$(command -v wget || echo '')"
IPFSCMD="$(command -v ipfs || echo '')"
@ -30,64 +32,57 @@ ZC_DISABLE_WGET="${ZC_DISABLE_WGET:-}"
ZC_DISABLE_IPFS="${ZC_DISABLE_IPFS:-}"
ZC_DISABLE_CURL="${ZC_DISABLE_CURL:-}"
function fetch_wget {
LOCKFILE=/tmp/fetch_params.lock
fetch_wget() {
if [ -z "$WGETCMD" ] || ! [ -z "$ZC_DISABLE_WGET" ]; then
return 1
fi
local filename="$1"
local dlname="$2"
cat <<EOF
Retrieving (wget): $DOWNLOAD_URL/$filename
Retrieving (wget): $DOWNLOAD_URL/$1
EOF
wget \
--progress=dot:giga \
--output-document="$dlname" \
--output-document="$2" \
--continue \
--retry-connrefused --waitretry=3 --timeout=30 \
"$DOWNLOAD_URL/$filename"
"$DOWNLOAD_URL/$1"
}
function fetch_ipfs {
fetch_ipfs() {
if [ -z "$IPFSCMD" ] || ! [ -z "$ZC_DISABLE_IPFS" ]; then
return 1
fi
local filename="$1"
local dlname="$2"
cat <<EOF
Retrieving (ipfs): $IPFS_HASH/$filename
Retrieving (ipfs): $IPFS_HASH/$1
EOF
ipfs get --output "$dlname" "$IPFS_HASH/$filename"
ipfs get --output "$2" "$IPFS_HASH/$1"
}
function fetch_curl {
fetch_curl() {
if [ -z "$CURLCMD" ] || ! [ -z "$ZC_DISABLE_CURL" ]; then
return 1
fi
local filename="$1"
local dlname="$2"
cat <<EOF
Retrieving (curl): $DOWNLOAD_URL/$filename
Retrieving (curl): $DOWNLOAD_URL/$1
EOF
curl \
--output "$dlname" \
--output "$2" \
-# -L -C - \
"$DOWNLOAD_URL/$filename"
"$DOWNLOAD_URL/$1"
}
function fetch_failure {
fetch_failure() {
cat >&2 <<EOF
Failed to fetch the Zcash zkSNARK parameters!
@ -101,11 +96,13 @@ EOF
exit 1
}
function fetch_params {
local filename="$1"
local output="$2"
local dlname="${output}.dl"
local expectedhash="$3"
fetch_params() {
# We only set these variables inside this function,
# and unset them at the end of the function.
filename="$1"
output="$2"
dlname="${output}.dl"
expectedhash="$3"
if ! [ -f "$output" ]
then
@ -143,33 +140,37 @@ EOF
exit 1
fi
fi
unset -v filename
unset -v output
unset -v dlname
unset -v expectedhash
}
# Use flock to prevent parallel execution.
function lock() {
local lockfile=/tmp/fetch_params.lock
if [[ "$OSTYPE" == "darwin"* ]]; then
if shlock -f ${lockfile} -p $$; then
lock() {
if [ "$uname_S" = "Darwin" ]; then
if shlock -f ${LOCKFILE} -p $$; then
return 0
else
return 1
fi
else
# create lock file
eval "exec 200>$lockfile"
eval "exec 9>$LOCKFILE"
# acquire the lock
flock -n 200 \
flock -n 9 \
&& return 0 \
|| return 1
fi
}
function exit_locked_error {
exit_locked_error() {
echo "Only one instance of fetch-params.sh can be run at a time." >&2
exit 1
}
function main() {
main() {
lock fetch-params.sh \
|| exit_locked_error
@ -232,5 +233,5 @@ then
fi
main
rm -f /tmp/fetch_params.lock
rm -f $LOCKFILE
exit 0