Browse Source

BIP155 (addrv2)

Tor v3 + i2p
pull/256/head
zanzibar 1 year ago
parent
commit
512da314a5
  1. 34
      configure.ac
  2. 165
      contrib/seeds/generate-seeds.py
  3. 17
      contrib/seeds/nodes_main.txt
  4. 10
      contrib/seeds/nodes_test.txt
  5. 161
      doc/i2p.md
  6. 281
      doc/tor.md
  7. 4
      qa/rpc-tests/httpbasics.py
  8. 4
      qa/rpc-tests/p2p-acceptblock.py
  9. 22
      qa/rpc-tests/proxy_test.py
  10. 39
      src/Makefile.am
  11. 122
      src/addrdb.cpp
  12. 90
      src/addrdb.h
  13. 323
      src/addrman.cpp
  14. 222
      src/addrman.h
  15. 2
      src/arith_uint256.cpp
  16. 20
      src/attributes.h
  17. 2
      src/bitcoin-cli.cpp
  18. 25
      src/cc/Makefile
  19. 6
      src/chainparams.cpp
  20. 9
      src/chainparams.h
  21. 43
      src/chainparamsseeds.h
  22. 3
      src/coins.cpp
  23. 12
      src/compat.h
  24. 2
      src/core_read.cpp
  25. 2
      src/core_write.cpp
  26. 7
      src/crypto/common.h
  27. 2
      src/crypto/equihash.h
  28. 162
      src/crypto/sha3.cpp
  29. 42
      src/crypto/sha3.h
  30. 2
      src/gtest/json_test_vectors.h
  31. 2
      src/gtest/test_deprecation.cpp
  32. 2
      src/gtest/test_merkletree.cpp
  33. 2
      src/gtest/test_rpc.cpp
  34. 2
      src/gtest/test_txid.cpp
  35. 67
      src/hash.h
  36. 2
      src/httprpc.cpp
  37. 57
      src/httpserver.cpp
  38. 2
      src/hush-tx.cpp
  39. 441
      src/i2p.cpp
  40. 256
      src/i2p.h
  41. 77
      src/init.cpp
  42. 2
      src/key_io.cpp
  43. 189
      src/main.cpp
  44. 3
      src/main.h
  45. 2
      src/merkleblock.cpp
  46. 2
      src/metrics.cpp
  47. 683
      src/net.cpp
  48. 140
      src/net.h
  49. 1222
      src/netaddress.cpp
  50. 534
      src/netaddress.h
  51. 1212
      src/netbase.cpp
  52. 245
      src/netbase.h
  53. 37
      src/netmessagemaker.h
  54. 243
      src/prevector.h
  55. 2
      src/primitives/block.cpp
  56. 2
      src/primitives/transaction.cpp
  57. 93
      src/protocol.cpp
  58. 313
      src/protocol.h
  59. 2
      src/random.cpp
  60. 2
      src/rest.cpp
  61. 65
      src/rpc/net.cpp
  62. 2
      src/rpc/protocol.cpp
  63. 2
      src/rpc/server.cpp
  64. 2
      src/script/script.cpp
  65. 2
      src/script/standard.cpp
  66. 320
      src/serialize.h
  67. 252
      src/span.h
  68. 15
      src/stratum.cpp
  69. 96
      src/streams.h
  70. 2
      src/sync.cpp
  71. 2
      src/test-hush/test_addrman.cpp
  72. 2
      src/test-hush/testutils.cpp
  73. 2
      src/test/base32_tests.cpp
  74. 2
      src/test/base58_tests.cpp
  75. 2
      src/test/base64_tests.cpp
  76. 2
      src/test/bip32_tests.cpp
  77. 2
      src/test/bloom_tests.cpp
  78. 2
      src/test/coins_tests.cpp
  79. 2
      src/test/convertbits_tests.cpp
  80. 2
      src/test/crypto_tests.cpp
  81. 2
      src/test/hash_tests.cpp
  82. 2
      src/test/key_tests.cpp
  83. 11
      src/test/netbase_tests.cpp
  84. 2
      src/test/rpc_tests.cpp
  85. 2
      src/test/serialize_tests.cpp
  86. 2
      src/test/util_tests.cpp
  87. 2
      src/timedata.cpp
  88. 80
      src/torcontrol.cpp
  89. 5
      src/torcontrol.h
  90. 2
      src/uint256.cpp
  91. 2
      src/util.cpp
  92. 47
      src/util/readwritefile.cpp
  93. 29
      src/util/readwritefile.h
  94. 328
      src/util/sock.cpp
  95. 184
      src/util/sock.h
  96. 68
      src/util/spanparsing.cpp
  97. 50
      src/util/spanparsing.h
  98. 655
      src/util/strencodings.cpp
  99. 302
      src/util/strencodings.h
  100. 6
      src/util/string.cpp
  101. 99
      src/util/string.h
  102. 2
      src/utilmoneystr.cpp
  103. 2
      src/utilstrencodings.cpp
  104. 28
      src/utiltime.cpp
  105. 11
      src/utiltime.h
  106. 2
      src/wallet/db.cpp
  107. 2
      src/wallet/wallet.h
  108. 1
      src/zcash/Note.cpp

34
configure.ac

@ -221,6 +221,29 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wdeprecated-register],[CXXFLAGS="$CXXFLAGS -Wno-deprecated-register"],,[[$CXXFLAG_WERROR]])
fi
TEMP_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $SSE42_CXXFLAGS"
AC_MSG_CHECKING(for assembler crc32 support)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <stdint.h>
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(__GNUC__) && defined(__SSE4_2__)
#include <nmmintrin.h>
#endif
]],[[
uint64_t l = 0;
l = _mm_crc32_u8(l, 0);
l = _mm_crc32_u32(l, 0);
l = _mm_crc32_u64(l, 0);
return l;
]])],
[ AC_MSG_RESULT(yes); enable_hwcrc32=yes],
[ AC_MSG_RESULT(no)]
)
CXXFLAGS="$TEMP_CXXFLAGS"
CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS"
AC_ARG_WITH([utils],
@ -241,6 +264,16 @@ AC_ARG_WITH([daemon],
[build_bitcoind=$withval],
[build_bitcoind=yes])
GCC_TARGET=`$CC -dumpmachine 2>&1`
case $GCC_TARGET in
arm*-*-*)
have_arm=true
;;
aarch64*-*-*)
have_arm=true
;;
esac
use_pkgconfig=yes
case $host in
*mingw*)
@ -815,6 +848,7 @@ AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
AM_CONDITIONAL([ARCH_ARM], [test x$have_arm = xtrue])
AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes])
AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes])
AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes])

165
contrib/seeds/generate-seeds.py

@ -1,5 +1,6 @@
#!/usr/bin/env python
# Copyright (c) 2014 Wladimir J. van der Laan
#!/usr/bin/env python3
# Copyright (c) 2016-2022 The Hush developers
# Copyright (c) 2014-2021 The Bitcoin Core developers
# Distributed under the GPLv3 software license, see the accompanying
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
'''
@ -11,45 +12,63 @@ argument:
nodes_main.txt
nodes_test.txt
These files must consist of lines in the format
These files must consist of lines in the format
<ip>
<ip>:<port>
[<ipv6>]
<ip>
[<ipv6>]:<port>
[<ipv6>]
<onion>.onion:<port>
<onion>.onion
0xDDBBCCAA (IPv4 little-endian old pnSeeds format)
<i2p>.b32.i2p:<port>
<i2p>.b32.i2p
The output will be two data structures with the peers in binary format:
static SeedSpec6 pnSeed6_main[]={
...
}
static SeedSpec6 pnSeed6_test[]={
static const uint8_t chainparams_seed_{main,test}[]={
...
}
These should be pasted into `src/chainparamsseeds.h`.
To update the generated code :
./contrib/seeds/generate-seeds.py contrib/seeds > src/chainparamsseeds.h
'''
from __future__ import print_function, division
from base64 import b32decode
from binascii import a2b_hex
import sys, os
from enum import Enum
import struct
import sys
import os
import re
# ipv4 in ipv6 prefix
pchIPv4 = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff])
# tor-specific ipv6 prefix
pchOnionCat = bytearray([0xFD,0x87,0xD8,0x7E,0xEB,0x43])
def name_to_ipv6(addr):
if len(addr)>6 and addr.endswith('.onion'):
class BIP155Network(Enum):
IPV4 = 1
IPV6 = 2
TORV2 = 3 # no longer supported
TORV3 = 4
I2P = 5
CJDNS = 6
def name_to_bip155(addr):
'''Convert address string to BIP155 (networkID, addr) tuple.'''
if addr.endswith('.onion'):
vchAddr = b32decode(addr[0:-6], True)
if len(vchAddr) != 16-len(pchOnionCat):
raise ValueError('Invalid onion %s' % s)
return pchOnionCat + vchAddr
if len(vchAddr) == 35:
assert vchAddr[34] == 3
return (BIP155Network.TORV3, vchAddr[:32])
elif len(vchAddr) == 10:
return (BIP155Network.TORV2, vchAddr)
else:
raise ValueError('Invalid onion %s' % vchAddr)
elif addr.endswith('.b32.i2p'):
vchAddr = b32decode(addr[0:-8] + '====', True)
if len(vchAddr) == 32:
return (BIP155Network.I2P, vchAddr)
else:
raise ValueError(f'Invalid I2P {vchAddr}')
elif '.' in addr: # IPv4
return pchIPv4 + bytearray((int(x) for x in addr.split('.')))
return (BIP155Network.IPV4, bytes((int(x) for x in addr.split('.'))))
elif ':' in addr: # IPv6
sub = [[], []] # prefix, suffix
x = 0
@ -66,14 +85,13 @@ def name_to_ipv6(addr):
sub[x].append(val & 0xff)
nullbytes = 16 - len(sub[0]) - len(sub[1])
assert((x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0))
return bytearray(sub[0] + ([0] * nullbytes) + sub[1])
elif addr.startswith('0x'): # IPv4-in-little-endian
return pchIPv4 + bytearray(reversed(a2b_hex(addr[2:])))
return (BIP155Network.IPV6, bytes(sub[0] + ([0] * nullbytes) + sub[1]))
else:
raise ValueError('Could not parse address %s' % addr)
def parse_spec(s, defaultport):
match = re.match('\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s)
def parse_spec(s):
'''Convert endpoint string to BIP155 (networkID, addr, port) tuple.'''
match = re.match(r'\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s)
if match: # ipv6
host = match.group(1)
port = match.group(2)
@ -84,17 +102,42 @@ def parse_spec(s, defaultport):
(host,_,port) = s.partition(':')
if not port:
port = defaultport
port = 0
else:
port = int(port)
host = name_to_ipv6(host)
host = name_to_bip155(host)
return (host,port)
def process_nodes(g, f, structname, defaultport):
g.write('static SeedSpec6 %s[] = {\n' % structname)
first = True
if host[0] == BIP155Network.TORV2:
return None # TORV2 is no longer supported, so we ignore it
else:
return host + (port, )
def ser_compact_size(l):
r = b""
if l < 253:
r = struct.pack("B", l)
elif l < 0x10000:
r = struct.pack("<BH", 253, l)
elif l < 0x100000000:
r = struct.pack("<BI", 254, l)
else:
r = struct.pack("<BQ", 255, l)
return r
def bip155_serialize(spec):
'''
Serialize (networkID, addr, port) tuple to BIP155 binary format.
'''
r = b""
r += struct.pack('B', spec[0].value)
r += ser_compact_size(len(spec[1]))
r += spec[1]
r += struct.pack('>H', spec[2])
return r
def process_nodes(g, f, structname):
g.write('static const uint8_t %s[] = {\n' % structname)
for line in f:
comment = line.find('#')
if comment != -1:
@ -102,37 +145,37 @@ def process_nodes(g, f, structname, defaultport):
line = line.strip()
if not line:
continue
if not first:
g.write(',\n')
first = False
(host,port) = parse_spec(line, defaultport)
hoststr = ','.join(('0x%02x' % b) for b in host)
g.write(' {{%s}, %i}' % (hoststr, port))
g.write('\n};\n')
spec = parse_spec(line)
if spec is None: # ignore this entry (e.g. no longer supported addresses like TORV2)
continue
blob = bip155_serialize(spec)
hoststr = ','.join(('0x%02x' % b) for b in blob)
g.write(f' {hoststr}, // {line}\n')
g.write('};\n')
def main():
if len(sys.argv)<2:
print(('Usage: %s <path_to_nodes_txt>' % sys.argv[0]), file=sys.stderr)
exit(1)
sys.exit(1)
g = sys.stdout
indir = sys.argv[1]
g.write('#ifndef BITCOIN_CHAINPARAMSSEEDS_H\n')
g.write('#define BITCOIN_CHAINPARAMSSEEDS_H\n')
g.write('/**\n')
g.write(' * List of fixed seed nodes for the bitcoin network\n')
g.write(' * AUTOGENERATED by contrib/seeds/generate-seeds.py\n')
g.write(' *\n')
g.write(' * Each line contains a 16-byte IPv6 address and a port.\n')
g.write(' * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly.\n')
g.write(' */\n')
with open(os.path.join(indir,'nodes_main.txt'),'r') as f:
process_nodes(g, f, 'pnSeed6_main', 8233)
g.write('// Copyright (c) 2016-2022 The Hush developers\n')
g.write('// Distributed under the GPLv3 software license, see the accompanying\n')
g.write('// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html\n')
g.write('// THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY\n')
g.write('// Instead, run: ./contrib/seeds/generate-seeds.py contrib/seeds\n')
g.write('#ifndef HUSH_CHAINPARAMSSEEDS_H\n')
g.write('#define HUSH_CHAINPARAMSSEEDS_H\n')
g.write('// List of fixed seed nodes for the Hush network\n')
g.write('// Each line contains a BIP155 serialized address.\n')
g.write('//\n')
with open(os.path.join(indir,'nodes_main.txt'), 'r', encoding="utf8") as f:
process_nodes(g, f, 'chainparams_seed_main')
g.write('\n')
with open(os.path.join(indir,'nodes_test.txt'),'r') as f:
process_nodes(g, f, 'pnSeed6_test', 18233)
g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n')
with open(os.path.join(indir,'nodes_test.txt'), 'r', encoding="utf8") as f:
process_nodes(g, f, 'chainparams_seed_test')
g.write('#endif // HUSH_CHAINPARAMSSEEDS_H\n')
if __name__ == '__main__':
main()

17
contrib/seeds/nodes_main.txt

@ -1,6 +1,11 @@
185.25.48.236:27485
185.25.48.236:27487
185.64.105.111:27485
185.64.105.111:27487
185.25.48.72:27485
185.25.48.72:27487
# node1.hush.land
185.241.61.43
# node2.hush.is
137.74.4.198
# lite.hushpool.is
149.28.102.219
# todo: torv3
# todo: i2p

10
contrib/seeds/nodes_test.txt

@ -1,11 +1,5 @@
# List of fixed seed nodes for testnet
# note: File must be non-empty to compile
1.2.3.4
# Onion nodes
thfsmmn2jbitcoin.onion
it2pj4f7657g3rhi.onion
nkf5e6b7pl4jfd4a.onion
4zhkir2ofl7orfom.onion
t6xj6wilh4ytvcs7.onion
i6y6ivorwakd7nw3.onion
ubqj4rsu3nqtxmtp.onion

161
doc/i2p.md

@ -0,0 +1,161 @@
# I2P support in Hush
It is possible to run a Hush or HSC full node as an
[I2P (Invisible Internet Project)](https://en.wikipedia.org/wiki/I2P)
service and connect to such services.
This [glossary](https://geti2p.net/en/about/glossary) may be useful to get
started with I2P terminology.
## Run with an I2P router (proxy)
A running I2P router (proxy) with [SAM](https://geti2p.net/en/docs/api/samv3)
enabled is required. Options include:
- [i2prouter (I2P Router)](https://geti2p.net), the official implementation in
Java
- [i2pd (I2P Daemon)](https://github.com/PurpleI2P/i2pd)
([documentation](https://i2pd.readthedocs.io/en/latest)), a lighter
alternative in C++ (successfully tested with version 2.23 and up; version 2.36
or later recommended)
- [i2p-zero](https://github.com/i2p-zero/i2p-zero)
- [other alternatives](https://en.wikipedia.org/wiki/I2P#Routers)
Note the IP address and port the SAM proxy is listening to; usually, it is
`127.0.0.1:7656`.
Once an I2P router with SAM enabled is up and running, use the following
configuration options:
```
-i2psam=<ip:port>
I2P SAM proxy to reach I2P peers and accept I2P connections (default:
none)
-i2pacceptincoming
If set and -i2psam is also set then incoming I2P connections are
accepted via the SAM proxy. If this is not set but -i2psam is set
then only outgoing connections will be made to the I2P network.
Ignored if -i2psam is not set. Listening for incoming I2P
connections is done through the SAM proxy, not by binding to a
local address and port (default: 1)
```
In a typical situation, this suffices:
```
hushd -i2psam=127.0.0.1:7656
```
The first time hushd connects to the I2P router, if
`-i2pacceptincoming=1`, then it will automatically generate a persistent I2P
address and its corresponding private key. The private key will be saved in a
file named `i2p_private_key` in the Hush data directory. The persistent
I2P address is used for accepting incoming connections and for making outgoing
connections if `-i2pacceptincoming=1`. If `-i2pacceptincoming=0` then only
outbound I2P connections are made and a different transient I2P address is used
for each connection to improve privacy.
## Persistent vs transient I2P addresses
In I2P connections, the connection receiver sees the I2P address of the
connection initiator. This is unlike the Tor network where the recipient does
not know who is connecting to them and can't tell if two connections are from
the same peer or not.
If an I2P node is not accepting incoming connections, then Hush uses
random, one-time, transient I2P addresses for itself for outbound connections
to make it harder to discriminate, fingerprint or analyze it based on its I2P
address.
## Additional configuration options related to I2P
```
-debug=i2p
```
Set the `debug=i2p` config logging option to see additional information in the
debug log about your I2P configuration and connections. Run `hush-cli help
logging` for more information.
```
-onlynet=i2p
```
Make automatic outbound connections only to I2P addresses. Inbound and manual
connections are not affected by this option. It can be specified multiple times
to allow multiple networks, e.g. onlynet=onion, onlynet=i2p.
I2P support was added to Hush in version 3.9.3 and there may be fewer I2P
peers than Tor or IP ones. Therefore, using I2P alone without other networks may
make a node more susceptible to [Sybil
attacks](https://en.bitcoin.it/wiki/Weaknesses#Sybil_attack). You can use
`hush-cli -addrinfo` to see the number of I2P addresses known to your node.
Another consideration with `onlynet=i2p` is that the initial blocks download
phase when syncing up a new node can be very slow. This phase can be sped up by
using other networks, for instance `onlynet=onion`, at the same time.
In general, a node can be run with both onion and I2P hidden services (or
any/all of IPv4/IPv6/onion/I2P/CJDNS), which can provide a potential fallback if
one of the networks has issues.
## I2P-related information
There are several ways to see your I2P address if accepting
incoming I2P connections (`-i2pacceptincoming`):
- in the "Local addresses" output of CLI `-netinfo`
- in the "localaddresses" output of RPC `getnetworkinfo`
- in the debug log (grep for `AddLocal`; the I2P address ends in `.b32.i2p`)
To see which I2P peers your node is connected to, use `hush-cli -netinfo 4`
or the `getpeerinfo` RPC (e.g. `hush-cli getpeerinfo`).
To see which I2P addresses your node knows, use the `getnodeaddresses 0 i2p`
RPC.
## Compatibility
Hush uses the [SAM v3.1](https://geti2p.net/en/docs/api/samv3) protocol
to connect to the I2P network. Any I2P router that supports it can be used.
## Ports in I2P and Hush
Hush uses the [SAM v3.1](https://geti2p.net/en/docs/api/samv3)
protocol. One particularity of SAM v3.1 is that it does not support ports,
unlike newer versions of SAM (v3.2 and up) that do support them and default the
port numbers to 0. From the point of view of peers that use newer versions of
SAM or other protocols that support ports, a SAM v3.1 peer is connecting to them
on port 0, from source port 0.
To allow future upgrades to newer versions of SAM, Hush sets its
listening port to 0 when listening for incoming I2P connections and advertises
its own I2P address with port 0. Furthermore, it will not attempt to connect to
I2P addresses with a non-zero port number because with SAM v3.1 the destination
port (`TO_PORT`) is always set to 0 and is not in the control of Hush.
## Bandwidth
I2P routers may route a large amount of general network traffic with their
default settings. Check your router's configuration to limit the amount of this
traffic relayed, if desired.
With `i2pd`, the amount of bandwidth being shared with the wider network can be
adjusted with the `bandwidth`, `share` and `transittunnels` options in your
`i2pd.conf` file. For example, to limit total I2P traffic to 256KB/s and share
50% of this limit for a maximum of 20 transit tunnels:
```
bandwidth = 256
share = 50
[limits]
transittunnels = 20
```
If you prefer not to relay any public I2P traffic and only permit I2P traffic
from programs which are connecting via the SAM proxy, e.g. Hush, you
can set the `notransit` option to `true`.
Similar bandwidth configuration options for the Java I2P router can be found in
`http://127.0.0.1:7657/config` under the "Bandwidth" tab.

281
doc/tor.md

@ -1,154 +1,225 @@
# Warning
# Tor
Do not assume Tor support works perfectly in Hush; better Tor support is currently being worked on.
# Hush + Tor
It is possible to run Hush as a Tor hidden service, and connect to such services.
It is possible to run Hush as a Tor onion service, and connect to such services.
The following directions assume you have a Tor proxy running on port 9050. Many distributions default to having a SOCKS proxy listening on port 9050, but others may not. In particular, the Tor Browser Bundle defaults to listening on port 9150. See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort) for how to properly
configure Tor.
## Compatibility
- Starting with version 3.9.3, Hush only supports Tor version 3 hidden
services (Tor v3). Tor v2 addresses are ignored by Hush and neither
relayed nor stored.
- Tor removed v2 support beginning with version 0.4.6.
## How to see information about your Tor configuration via Hush
There are several ways to see your local onion address in Hush:
- in the "Local addresses" output of CLI `-netinfo`
- in the "localaddresses" output of RPC `getnetworkinfo`
- in the debug log (grep for "AddLocal"; the Tor address ends in `.onion`)
You may set the `-debug=tor` config logging option to have additional
information in the debug log about your Tor configuration.
CLI `-addrinfo` returns the number of addresses known to your node per
network. This can be useful to see how many onion peers your node knows,
e.g. for `-onlynet=onion`.
1. Run Hush behind a Tor proxy
-------------------------------
To fetch a number of onion addresses that your node knows, for example seven
addresses, use the `getnodeaddresses 7 onion` RPC.
The first step is running Hush behind a Tor proxy. This will already make all
outgoing connections be anonymized, but more is possible.
## 1. Run Hush behind a Tor proxy
-proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy
server will be used to try to reach .onion addresses as well.
The first step is running Hush behind a Tor proxy. This will already anonymize all
outgoing connections, but more is possible.
-onion=ip:port Set the proxy server to use for Tor hidden services. You do not
need to set this if it's the same as -proxy. You can use -noonion
to explicitly disable access to hidden service.
-proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy
server will be used to try to reach .onion addresses as well.
You need to use -noonion or -onion=0 to explicitly disable
outbound access to onion services.
-listen When using -proxy, listening is disabled by default. If you want
to run a hidden service (see next section), you'll need to enable
it explicitly.
-onion=ip:port Set the proxy server to use for Tor onion services. You do not
need to set this if it's the same as -proxy. You can use -onion=0
to explicitly disable access to onion services.
------------------------------------------------------------------
Note: Only the -proxy option sets the proxy for DNS requests;
with -onion they will not route over Tor, so use -proxy if you
have privacy concerns.
------------------------------------------------------------------
-connect=X When behind a Tor proxy, you can specify .onion addresses instead
-addnode=X of IP addresses or hostnames in these parameters. It requires
-seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with
other P2P nodes.
-listen When using -proxy, listening is disabled by default. If you want
to manually configure an onion service (see section 3), you'll
need to enable it explicitly.
-connect=X When behind a Tor proxy, you can specify .onion addresses instead
-addnode=X of IP addresses or hostnames in these parameters. It requires
-seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with
other P2P nodes.
-onlynet=onion Make automatic outbound connections only to .onion addresses.
Inbound and manual connections are not affected by this option.
It can be specified multiple times to allow multiple networks,
e.g. onlynet=onion, onlynet=i2p, onlynet=cjdns.
In a typical situation, this suffices to run behind a Tor proxy:
./hushd -proxy=127.0.0.1:9050
./hushd -proxy=127.0.0.1:9050
If using the Tor Browser Bundle:
## 2. Automatically create a Hush onion service
./hushd -proxy=127.0.0.1:9150
Hush makes use of Tor's control socket API to create and destroy
ephemeral onion services programmatically. This means that if Tor is running and
proper authentication has been configured, Hush automatically creates an
onion service to listen on. The goal is to increase the number of available
onion nodes.
This feature is enabled by default if Hush is listening (`-listen`) and
it requires a Tor connection to work. It can be explicitly disabled with
`-listenonion=0`. If it is not disabled, it can be configured using the
`-torcontrol` and `-torpassword` settings.
To see verbose Tor information in the hushd debug log, pass `-debug=tor`.
2. Run a Hush hidden server
----------------------------
### Control Port
If you configure your Tor system accordingly, it is possible to make your node also
reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent
config file):
You may need to set up the Tor Control Port. On Linux distributions there may be
some or all of the following settings in `/etc/tor/torrc`, generally commented
out by default (if not, add them):
HiddenServiceDir /var/lib/tor/hush-service/
HiddenServicePort 18030 127.0.0.1:18030
```
ControlPort 9051
CookieAuthentication 1
CookieAuthFileGroupReadable 1
```
The directory can be different of course, but (both) port numbers should be equal to
your hushd's P2P listen port (18030 by default).
Add or uncomment those, save, and restart Tor (usually `systemctl restart tor`
or `sudo systemctl restart tor` on most systemd-based systems, including recent
Debian and Ubuntu, or just restart the computer).
-externalip=X You can tell Hush about its publicly reachable address using
this option, and this can be a .onion address. Given the above
configuration, you can find your onion address in
/var/lib/tor/hush-service/hostname. Onion addresses are given
preference for your node to advertize itself with, for connections
coming from unroutable addresses (such as 127.0.0.1, where the
Tor proxy typically runs).
On some systems (such as Arch Linux), you may also need to add the following
line:
-listen You'll need to enable listening for incoming connections, as this
is off by default behind a proxy.
```
DataDirectoryGroupReadable 1
```
-discover When -externalip is specified, no attempt is made to discover local
IPv4 or IPv6 addresses. If you want to run a dual stack, reachable
from both Tor and IPv4 (or IPv6), you'll need to either pass your
other addresses using -externalip, or explicitly enable -discover.
Note that both addresses of a dual-stack system may be easily
linkable using traffic analysis.
### Authentication
In a typical situation, where you're only reachable via Tor, this should suffice:
Connecting to Tor's control socket API requires one of two authentication
methods to be configured: cookie authentication or hushd's `-torpassword`
configuration option.
./hushd -proxy=127.0.0.1:9050 -externalip=hushc0de123.onion -listen
#### Cookie authentication
(obviously, replace the Onion address with your own). Currently only v2 HS's are supported.
It should be noted that you still listen on all devices and another node could establish a clearnet connection, when knowing
your address. To mitigate this, additionally bind the address of your Tor proxy:
For cookie authentication, the user running hushd must have read access to
the `CookieAuthFile` specified in the Tor configuration. In some cases this is
preconfigured and the creation of an onion service is automatic. Don't forget to
use the `-debug=tor` hushd configuration option to enable Tor debug logging.
./hushd ... -bind=127.0.0.1
If a permissions problem is seen in the debug log, e.g. `tor: Authentication
cookie /run/tor/control.authcookie could not be opened (check permissions)`, it
can be resolved by adding both the user running Tor and the user running
hushd to the same Tor group and setting permissions appropriately.
If you don't care too much about hiding your node, and want to be reachable on IPv4
as well, use `discover` instead:
On Debian-derived systems, the Tor group will likely be `debian-tor` and one way
to verify could be to list the groups and grep for a "tor" group name:
./hushd ... -discover
```
getent group | cut -d: -f1 | grep -i tor
```
and open port 18030 on your firewall.
You can also check the group of the cookie file. On most Linux systems, the Tor
auth cookie will usually be `/run/tor/control.authcookie`:
If you only want to use Tor to reach onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use:
```
TORGROUP=$(stat -c '%G' /run/tor/control.authcookie)
```
./hushd -onion=127.0.0.1:9050 -externalip=hushc0de123.onion -discover
Once you have determined the `${TORGROUP}` and selected the `${USER}` that will
run hushd, run this as root:
```
usermod -a -G ${TORGROUP} ${USER}
```
3. Automatically listen on Tor
--------------------------------
Then restart the computer (or log out) and log in as the `${USER}` that will run
hushd.
Starting with Tor version 0.2.7.1 it is possible, through Tor's control socket
API, to create and destroy 'ephemeral' hidden services programmatically.
Hush has been updated to make use of this.
#### `torpassword` authentication
This means that if Tor is running (and proper authentication has been configured),
Hush automatically creates a hidden service to listen on. Hush will also use Tor
automatically to connect to other .onion nodes if the control socket can be
successfully opened. This will positively affect the number of available .onion
nodes and their usage.
For the `-torpassword=password` option, the password is the clear text form that
was used when generating the hashed password for the `HashedControlPassword`
option in the Tor configuration file.
This new feature is enabled by default if Hush is listening (`-listen`), and
requires a Tor connection to work. It can be explicitly disabled with `-listenonion=0`
and, if not disabled, configured using the `-torcontrol` and `-torpassword` settings.
To show verbose debugging information, pass `-debug=tor`.
The hashed password can be obtained with the command `tor --hash-password
password` (refer to the [Tor Dev
Manual](https://2019.www.torproject.org/docs/tor-manual.html.en) for more
details).
Connecting to Tor's control socket API requires one of two authentication methods to be
configured. For cookie authentication the user running hushd must have write access
to the `CookieAuthFile` specified in Tor configuration. In some cases this is
preconfigured and the creation of a hidden service is automatic. If permission problems
are seen with `-debug=tor` they can be resolved by adding both the user running tor and
the user running hushd to the same group and setting permissions appropriately. On
Debian-based systems the user running hushd can be added to the debian-tor group,
which has the appropriate permissions. An alternative authentication method is the use
of the `-torpassword` flag and a `hash-password` which can be enabled and specified in
Tor configuration.
## 3. Manually create a Hush onion service
4. Connect to a Hush hidden server
-----------------------------------
You can also manually configure your node to be reachable from the Tor network.
Add these lines to your `/etc/tor/torrc` (or equivalent config file):
To test your set-up, you might want to try connecting via Tor on a different computer to just a
a single Hush hidden server. Launch hushd as follows:
HiddenServiceDir /var/lib/tor/hush-service/
HiddenServicePort 18030 127.0.0.1:18032
./hushd -onion=127.0.0.1:9050 -connect=fuckzookoie6wxgio.onion
The directory can be different of course, but virtual port numbers should be equal to
your hushd's P2P listen port (18030 by default), and target addresses and ports
should be equal to binding address and port for inbound Tor connections (127.0.0.1:18032 by default).
Now use hush-cli to verify there is only a single peer connection.
-externalip=X You can tell hush about its publicly reachable addresses using
this option, and this can be an onion address. Given the above
configuration, you can find your onion address in
/var/lib/tor/hush-service/hostname. For connections
coming from unroutable addresses (such as 127.0.0.1, where the
Tor proxy typically runs), onion addresses are given
preference for your node to advertise itself with.
hush-cli getpeerinfo
You can set multiple local addresses with -externalip. The
one that will be rumoured to a particular peer is the most
compatible one and also using heuristics, e.g. the address
with the most incoming connections, etc.
-listen You'll need to enable listening for incoming connections, as this
is off by default behind a proxy.
-discover When -externalip is specified, no attempt is made to discover local
IPv4 or IPv6 addresses. If you want to run a dual stack, reachable
from both Tor and IPv4 (or IPv6), you'll need to either pass your
other addresses using -externalip, or explicitly enable -discover.
Note that both addresses of a dual-stack system may be easily
linkable using traffic analysis.
In a typical situation, where you're only reachable via Tor, this should suffice:
./hushd -proxy=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -listen
(obviously, replace the .onion address with your own). It should be noted that you still
listen on all devices and another node could establish a clearnet connection, when knowing
your address. To mitigate this, additionally bind the address of your Tor proxy:
./hushd ... -bind=127.0.0.1
If you don't care too much about hiding your node, and want to be reachable on IPv4
as well, use `discover` instead:
./hushd ... -discover
and open port 18030 on your firewall (or use port mapping, i.e., `-upnp` or `-natpmp`).
If you only want to use Tor to reach .onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use:
[
{
"id" : 1,
"addr" : "zcashhoneypot.onion:18030",
...
"version" : 1987420,
"subver" : "/GoldenSandtrout:3.6.0/",
...
}
]
./hushd -onion=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -discover
To connect to multiple Tor nodes, use:
## 4. Privacy recommendations
./hushd -onion=127.0.0.1:9050 -addnode=hushbeef123.onion -dnsseed=0 -onlynet=onion
- Do not add anything but Hush ports to the onion service created in section 3.
If you run a web service too, create a new onion service for that.
Otherwise it is trivial to link them, which may reduce privacy. Onion
services created automatically (as in section 2) always have only one port
open.

4
qa/rpc-tests/httpbasics.py

@ -4,9 +4,7 @@
# Distributed under the GPLv3 software license, see the accompanying
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#
# Test rpc http basics
#
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, start_nodes
@ -97,7 +95,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert_equal('"error":null' in out1, True)
assert_equal(conn.sock!=None, True) # connection must be closed because bitcoind should use keep-alive by default
assert_equal(conn.sock!=None, True) # connection must be closed because hushd should use keep-alive by default
if __name__ == '__main__':
HTTPBasicsTest().main()

4
qa/rpc-tests/p2p-acceptblock.py

@ -116,8 +116,8 @@ class TestNode(NodeConnCB):
class AcceptBlockTest(BitcoinTestFramework):
def add_options(self, parser):
parser.add_option("--testbinary", dest="testbinary",
default=os.getenv("BITCOIND", "bitcoind"),
help="bitcoind binary to test")
default=os.getenv("BITCOIND", "hushd"),
help="hushd binary to test")
def setup_chain(self):
initialize_chain_clean(self.options.tmpdir, 2)

22
qa/rpc-tests/proxy_test.py

@ -13,10 +13,10 @@ import os
'''
Test plan:
- Start bitcoind's with different proxy configurations
- Start hushd's with different proxy configurations
- Use addnode to initiate connections
- Verify that proxies are connected to, and the right connection command is given
- Proxy configurations to test on bitcoind side:
- Proxy configurations to test on hushd side:
- `-proxy` (proxy everything)
- `-onion` (proxy just onions)
- `-proxyrandomize` Circuit randomization
@ -26,8 +26,8 @@ Test plan:
- proxy on IPv6
- Create various proxies (as threads)
- Create bitcoinds that connect to them
- Manipulate the bitcoinds using addnode (onetry) an observe effects
- Create hushds that connect to them
- Manipulate the hushds using addnode (onetry) an observe effects
addnode connect to IPv4
addnode connect to IPv6
@ -78,7 +78,7 @@ class ProxyTest(BitcoinTestFramework):
node.addnode("15.61.23.23:1234", "onetry")
cmd = proxies[0].queue.get()
assert(isinstance(cmd, Socks5Command))
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
# Note: hushd's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, "15.61.23.23")
assert_equal(cmd.port, 1234)
@ -91,7 +91,7 @@ class ProxyTest(BitcoinTestFramework):
node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry")
cmd = proxies[1].queue.get()
assert(isinstance(cmd, Socks5Command))
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
# Note: hushd's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, "1233:3432:2434:2343:3234:2345:6546:4534")
assert_equal(cmd.port, 5443)
@ -102,24 +102,24 @@ class ProxyTest(BitcoinTestFramework):
if test_onion:
# Test: outgoing onion connection through node
node.addnode("bitcoinostk4e4re.onion:8333", "onetry")
node.addnode("hushostk4e4re.onion:18030", "onetry")
cmd = proxies[2].queue.get()
assert(isinstance(cmd, Socks5Command))
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, "bitcoinostk4e4re.onion")
assert_equal(cmd.port, 8333)
assert_equal(cmd.addr, "hushostk4e4re.onion")
assert_equal(cmd.port, 18030)
if not auth:
assert_equal(cmd.username, None)
assert_equal(cmd.password, None)
rv.append(cmd)
# Test: outgoing DNS name connection through node
node.addnode("node.noumenon:8333", "onetry")
node.addnode("node.noumenon:18030", "onetry")
cmd = proxies[3].queue.get()
assert(isinstance(cmd, Socks5Command))
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
assert_equal(cmd.addr, "node.noumenon")
assert_equal(cmd.port, 8333)
assert_equal(cmd.port, 18030)
if not auth:
assert_equal(cmd.username, None)
assert_equal(cmd.password, None)

39
src/Makefile.am

@ -9,6 +9,12 @@ AM_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS)
AM_CPPFLAGS = $(HARDENED_CPPFLAGS)
EXTRA_LIBRARIES =
if ARCH_ARM
PLATFORM_VARIANT = armv8.1-a+crypto
else
PLATFORM_VARIANT = x86-64
endif
if EMBEDDED_LEVELDB
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv
@ -28,6 +34,7 @@ BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config
BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += -I$(srcdir)/cc/includes
BITCOIN_INCLUDES += -I$(srcdir)/cryptoconditions/include
BITCOIN_INCLUDES += -I$(srcdir)/cryptoconditions/src
BITCOIN_INCLUDES += -I$(srcdir)/cryptoconditions/src/asn
@ -63,13 +70,13 @@ LIBBITCOIN_WALLET=libbitcoin_wallet.a
endif
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -g "
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=$(PLATFORM_VARIANT) -g "
$(LIBUNIVALUE): $(wildcard univalue/lib/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -g "
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=$(PLATFORM_VARIANT) -g "
$(LIBCRYPTOCONDITIONS): $(wildcard cryptoconditions/src/*) $(wildcard cryptoconditions/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -g "
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) OPTFLAGS="-O2 -march=$(PLATFORM_VARIANT) -g "
# Make is not made aware of per-object dependencies to avoid limiting building parallelization
# But to build the less dependent modules first, we manually select their order here:
@ -122,6 +129,8 @@ BITCOIN_CORE_H = \
addressindex.h \
spentindex.h \
addrman.h \
attributes.h \
addrdb.h \
amount.h \
amqp/amqpabstractnotifier.h \
amqp/amqpconfig.h \
@ -160,6 +169,7 @@ BITCOIN_CORE_H = \
hash.h \
httprpc.h \
httpserver.h \
i2p.h \
init.h \
key.h \
key_io.h \
@ -174,10 +184,13 @@ BITCOIN_CORE_H = \
mruset.h \
net.h \
netbase.h \
netaddress.h \
netmessagemaker.h \
noui.h \
policy/fees.h \
pow.h \
prevector.h \
span.h \
primitives/block.h \
primitives/transaction.h \
protocol.h \
@ -217,8 +230,13 @@ BITCOIN_CORE_H = \
uint252.h \
undo.h \
util.h \
util/readwritefile.h \
util/sock.h \
util/string.h \
util/spanparsing.h \
util/strencodings.h \
utilmoneystr.h \
utilstrencodings.h \
# utilstrencodings.h \
utiltime.h \
validationinterface.h \
version.h \
@ -249,6 +267,7 @@ libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrman.cpp \
addrdb.cpp \
asyncrpcoperation.cpp \
asyncrpcqueue.cpp \
bloom.cpp \
@ -283,6 +302,7 @@ libbitcoin_server_a_SOURCES = \
deprecation.cpp \
httprpc.cpp \
httpserver.cpp \
i2p.cpp \
init.cpp \
dbwrapper.cpp \
main.cpp \
@ -357,6 +377,8 @@ crypto_libbitcoin_crypto_a_SOURCES = \
crypto/ripemd160.h \
crypto/sha1.cpp \
crypto/sha1.h \
crypto/sha3.cpp \
crypto/sha3.h \
crypto/sha256.cpp \
crypto/sha256.h \
crypto/sha512.cpp \
@ -397,6 +419,7 @@ libbitcoin_common_a_SOURCES = \
key.cpp \
key_io.cpp \
keystore.cpp \
netaddress.cpp \
netbase.cpp \
metrics.cpp \
primitives/block.cpp \
@ -435,9 +458,13 @@ libbitcoin_util_a_SOURCES = \
uint256.cpp \
util.cpp \
utilmoneystr.cpp \
utilstrencodings.cpp \
utiltime.cpp \
util/strencodings.cpp \
util/asmap.cpp \
util/sock.cpp \
util/spanparsing.cpp \
util/string.cpp \
util/readwritefile.cpp \
$(BITCOIN_CORE_H) \
$(LIBZCASH_H)
@ -667,6 +694,8 @@ clean-local:
-$(MAKE) -C univalue clean
rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno
-rm -f config.h
-rm -f *.a
-rm -f *.so
.rc.o:
@test -f $(WINDRES)

122
src/addrdb.cpp

@ -0,0 +1,122 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "addrdb.h"
#include "addrman.h"
#include "chainparams.h"
#include "clientversion.h"
#include "fs.h"
#include "hash.h"
#include "random.h"
#include "streams.h"
#include "tinyformat.h"
#include "util.h"
namespace {
template <typename Stream, typename Data>
bool SerializeDB(Stream& stream, const Data& data)
{
// Write and commit header, data
try {
CHashWriter hasher(stream.GetType(), stream.GetVersion());
stream << FLATDATA(Params().MessageStart()) << data;
hasher << FLATDATA(Params().MessageStart()) << data;
stream << hasher.GetHash();
} catch (const std::exception& e) {
return error("%s: Serialize or I/O error - %s", __func__, e.what());
}
return true;
}
template <typename Data>
bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
{
// Generate random temporary filename
unsigned short randv = 0;
GetRandBytes((unsigned char*)&randv, sizeof(randv));
std::string tmpfn = strprintf("%s.%04x", prefix, randv);
// open temp output file, and associate with CAutoFile
fs::path pathTmp = GetDataDir() / tmpfn;
FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s: Failed to open file %s", __func__, pathTmp.string());
// Serialize
if (!SerializeDB(fileout, data)) return false;
FileCommit(fileout.Get());
fileout.fclose();
// replace existing file, if any, with new file
if (!RenameOver(pathTmp, path))
return error("%s: Rename-into-place failed", __func__);
return true;
}
template <typename Stream, typename Data>
bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
{
try {
CHashVerifier<Stream> verifier(&stream);
// de-serialize file header (network specific magic number) and ..
unsigned char pchMsgTmp[4];
verifier >> FLATDATA(pchMsgTmp);
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
return error("%s: Invalid network magic number", __func__);
// de-serialize data
verifier >> data;
// verify checksum
if (fCheckSum) {
uint256 hashTmp;
stream >> hashTmp;
if (hashTmp != verifier.GetHash()) {
return error("%s: Checksum mismatch, data corrupted", __func__);
}
}
}
catch (const std::exception& e) {
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
}
return true;
}
template <typename Data>
bool DeserializeFileDB(const fs::path& path, Data& data)
{
// open input file, and associate with CAutoFile
FILE *file = fsbridge::fopen(path, "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("%s: Failed to open file %s", __func__, path.string());
return DeserializeDB(filein, data);
}
}
CBanDB::CBanDB()
{
pathBanlist = GetDataDir() / "banlist.dat";
}
bool CBanDB::Write(const banmap_t& banSet)
{
return SerializeFileDB("banlist", pathBanlist, banSet);
}
bool CBanDB::Read(banmap_t& banSet)
{
return DeserializeFileDB(pathBanlist, banSet);
}

90
src/addrdb.h

@ -0,0 +1,90 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef HUSH_ADDRDB_H
#define HUSH_ADDRDB_H
#include "fs.h"
#include "serialize.h"
#include <string>
#include <map>
class CSubNet;
class CAddrMan;
class CDataStream;
typedef enum BanReason
{
BanReasonUnknown = 0,
BanReasonNodeMisbehaving = 1,
BanReasonManuallyAdded = 2
} BanReason;
class CBanEntry
{
public:
static const int CURRENT_VERSION=1;
int nVersion;
int64_t nCreateTime;
int64_t nBanUntil;
uint8_t banReason;
CBanEntry()
{
SetNull();
}
explicit CBanEntry(int64_t nCreateTimeIn)
{
SetNull();
nCreateTime = nCreateTimeIn;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
READWRITE(nCreateTime);
READWRITE(nBanUntil);
READWRITE(banReason);
}
void SetNull()
{
nVersion = CBanEntry::CURRENT_VERSION;
nCreateTime = 0;
nBanUntil = 0;
banReason = BanReasonUnknown;
}
std::string banReasonToString() const
{
switch (banReason) {
case BanReasonNodeMisbehaving:
return "node misbehaving";
case BanReasonManuallyAdded:
return "manually added";
default:
return "unknown";
}
}
};
typedef std::map<CSubNet, CBanEntry> banmap_t;
/** Access to the banlist database (banlist.dat) */
class CBanDB
{
private:
fs::path pathBanlist;
public:
CBanDB();
bool Write(const banmap_t& banSet);
bool Read(banmap_t& banSet);
};
#endif // HUSH_ADDRDB_H

323
src/addrman.cpp

@ -23,6 +23,7 @@
#include "hash.h"
#include "serialize.h"
#include "streams.h"
#include "init.h"
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
{
@ -53,6 +54,9 @@ int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) co
bool CAddrInfo::IsTerrible(int64_t nNow) const
{
if (fLocal) //never remove local addresses
return false;
if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
return false;
@ -71,6 +75,14 @@ bool CAddrInfo::IsTerrible(int64_t nNow) const
return false;
}
bool CAddrInfo::IsJustTried(int64_t nNow) const
{
if (nLastTry && nLastTry >= nNow - 60)
return true;
return false;
}
double CAddrInfo::GetChance(int64_t nNow) const
{
double fChance = 1.0;
@ -95,24 +107,30 @@ double CAddrInfo::GetChance(int64_t nNow) const
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
{
std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
AssertLockHeld(cs);
const auto it = mapAddr.find(addr);
if (it == mapAddr.end())
return NULL;
return nullptr;
if (pnId)
*pnId = (*it).second;
std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
const auto it2 = mapInfo.find((*it).second);
if (it2 != mapInfo.end())
return &(*it2).second;
return NULL;
return nullptr;
}
CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
int nId = nIdCount++;
AssertLockHeld(cs);
int nId = nIdCount;
mapInfo[nId] = CAddrInfo(addr, addrSource);
mapAddr[addr] = nId;
mapInfo[nId].nRandomPos = vRandom.size();
vRandom.push_back(nId);
nNew++;
nIdCount++;
if (pnId)
*pnId = nId;
return &mapInfo[nId];
@ -120,6 +138,8 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
{
AssertLockHeld(cs);
if (nRndPos1 == nRndPos2)
return;
@ -128,11 +148,13 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
int nId1 = vRandom[nRndPos1];
int nId2 = vRandom[nRndPos2];
assert(mapInfo.count(nId1) == 1);
assert(mapInfo.count(nId2) == 1);
const auto it_1{mapInfo.find(nId1)};
const auto it_2{mapInfo.find(nId2)};
assert(it_1 != mapInfo.end());
assert(it_2 != mapInfo.end());
mapInfo[nId1].nRandomPos = nRndPos2;
mapInfo[nId2].nRandomPos = nRndPos1;
it_1->second.nRandomPos = nRndPos2;
it_2->second.nRandomPos = nRndPos1;
vRandom[nRndPos1] = nId2;
vRandom[nRndPos2] = nId1;
@ -140,41 +162,57 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
void CAddrMan::Delete(int nId)
{
assert(mapInfo.count(nId) != 0);
CAddrInfo& info = mapInfo[nId];
assert(!info.fInTried);
assert(info.nRefCount == 0);
AssertLockHeld(cs);
const auto it{mapInfo.find(nId)};
if (it != mapInfo.end()) {
CAddrInfo& info = (*it).second;
assert(!info.fInTried);
assert(info.nRefCount == 0);
SwapRandom(info.nRandomPos, vRandom.size() - 1);
vRandom.pop_back();
mapAddr.erase(info);
mapInfo.erase(nId);
nNew--;
}
SwapRandom(info.nRandomPos, vRandom.size() - 1);
vRandom.pop_back();
mapAddr.erase(info);
mapInfo.erase(nId);
nNew--;
}
void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
{
AssertLockHeld(cs);
// if there is an entry in the specified bucket, delete it.
if (vvNew[nUBucket][nUBucketPos] != -1) {
int nIdDelete = vvNew[nUBucket][nUBucketPos];
CAddrInfo& infoDelete = mapInfo[nIdDelete];
assert(infoDelete.nRefCount > 0);
infoDelete.nRefCount--;
vvNew[nUBucket][nUBucketPos] = -1;
if (infoDelete.nRefCount == 0) {
Delete(nIdDelete);
const auto it{mapInfo.find(nIdDelete)};
if (it != mapInfo.end()) {
CAddrInfo& infoDelete = (*it).second;
assert(infoDelete.nRefCount > 0);
infoDelete.nRefCount--;
vvNew[nUBucket][nUBucketPos] = -1;
if (infoDelete.nRefCount == 0) {
Delete(nIdDelete);
}
}
}
}
void CAddrMan::MakeTried(CAddrInfo& info, int nId)
{
AssertLockHeld(cs);
// remove the entry from all new buckets
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
int pos = info.GetBucketPosition(nKey, true, bucket);
const int start_bucket{info.GetNewBucket(nKey, m_asmap)};
for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {
const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};
const int pos{info.GetBucketPosition(nKey, true, bucket)};
if (vvNew[bucket][pos] == nId) {
vvNew[bucket][pos] = -1;
info.nRefCount--;
if (info.nRefCount == 0) break;
}
}
nNew--;
@ -215,67 +253,6 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
info.fInTried = true;
}
void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime) {
int nId;
CAddrInfo* pinfo = Find(addr, &nId);
// if not found, bail out
if (!pinfo)
return;
CAddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
return;
// update info
info.nLastSuccess = nTime;
info.nLastTry = nTime;
info.nAttempts = 0;
// nTime is not updated here, to avoid leaking information about
// currently-connected peers.
// if it is already in the tried set, don't do anything else
if (info.fInTried)
return;
// find a bucket it is in now
int nRnd = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucket = -1;
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
int nBpos = info.GetBucketPosition(nKey, true, nB);
if (vvNew[nB][nBpos] == nId) {
nUBucket = nB;
break;
}
}
// if no bucket is found, something bad happened;
// TODO: maybe re-add the node, but for now, just bail out
if (nUBucket == -1)
return;
// which tried bucket to move the entry to
int tried_bucket = info.GetTriedBucket(nKey,m_asmap);
int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
// Will moving this address into tried evict another entry?
if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
LogPrint("addrman", "Collision inserting element into tried table, moving %s to m_tried_collisions=%d\n", addr.ToString(), m_tried_collisions.size());
if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
m_tried_collisions.insert(nId);
}
} else {
LogPrint("addrman", "Moving %s to tried\n", addr.ToString());
printf("%s: Moving %s to tried\n", __func__, addr.ToString().c_str() );
// move nId to the tried tables
MakeTried(info, nId);
}
}
void CAddrMan::ResolveCollisions_() {
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
int id_new = *it;
@ -351,13 +328,59 @@ CAddrInfo CAddrMan::SelectTriedCollision_() {
return mapInfo[id_old];
}
void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime) {
int nId;
CAddrInfo* pinfo = Find(addr, &nId);
// if not found, bail out
if (!pinfo)
return;
CAddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
return;
// update info
info.nLastSuccess = nTime;
info.nLastTry = nTime;
info.nAttempts = 0;
// nTime is not updated here, to avoid leaking information about
// currently-connected peers.
// if it is already in the tried set, don't do anything else
if (info.fInTried)
return;
// find a bucket it is in now
int nRnd = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucket = -1;
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
int nBpos = info.GetBucketPosition(nKey, true, nB);
if (vvNew[nB][nBpos] == nId) {
nUBucket = nB;
break;
}
}
// if no bucket is found, something bad happened;
// TODO: maybe re-add the node, but for now, just bail out
if (nUBucket == -1)
return;
LogPrint("addrman", "Moving %s to tried\n", addr.ToString());
// move nId to the tried tables
MakeTried(info, nId);
}
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
if (!addr.IsRoutable())
return false;
bool fNew = false;
int nId;
CAddrInfo* pinfo = Find(addr, &nId);
@ -392,19 +415,20 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
} else {
pinfo = Create(addr, source, &nId);
pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
nNew++;
fNew = true;
}
int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap);
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (vvNew[nUBucket][nUBucketPos] != nId) {
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (!fInsert) {
CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
// Overwrite the existing new table entry.
fInsert = true;
const auto it{mapInfo.find(vvNew[nUBucket][nUBucketPos])};
if (it != mapInfo.end()) {
CAddrInfo& infoExisting = (*it).second;
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
// Overwrite the existing new table entry.
fInsert = true;
}
}
}
if (fInsert) {
@ -417,7 +441,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
}
}
}
return fNew;
return fInsert;
}
void CAddrMan::Attempt_(const CService& addr, int64_t nTime)
@ -454,10 +478,15 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
// Use a 50% chance for choosing between tried and new table entries.
if (!newOnly &&
(nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
(nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
// use a tried node
double fChanceFactor = 1.0;
double fReachableFactor = 1.0;
double fJustTried = 1.0;
while (1) {
if (ShutdownRequested()) //break loop on shutdown request
return CAddrInfo();
int i = 0;
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
@ -472,14 +501,27 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nId = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nId) == 1);
CAddrInfo& info = mapInfo[nId];
if (RandomInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
if (info.IsReachableNetwork()) {
//deprioritize unreachable networks
fReachableFactor = 0.25;
}
if (info.IsJustTried()) {
//deprioritize entries just tried
fJustTried = 0.10;
}
if (RandomInt(1 << 30) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
} else {
// use a new node
double fChanceFactor = 1.0;
double fReachableFactor = 1.0;
double fJustTried = 1.0;
while (1) {
if (ShutdownRequested()) //break loop on shutdown request
return CAddrInfo();
int i = 0;
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
@ -494,12 +536,20 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
int nId = vvNew[nUBucket][nUBucketPos];
assert(mapInfo.count(nId) == 1);
CAddrInfo& info = mapInfo[nId];
if (RandomInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
if (info.IsReachableNetwork()) {
//deprioritize unreachable networks
fReachableFactor = 0.25;
}
if (info.IsJustTried()) {
//deprioritize entries just tried
fJustTried = 0.10;
}
if (RandomInt(1 << 30) < fChanceFactor * fReachableFactor * fJustTried * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
}
}
return CAddrInfo();
}
@ -581,24 +631,59 @@ int CAddrMan::Check_()
}
#endif
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, bool wants_addrv2)
{
unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100;
if (nNodes > ADDRMAN_GETADDR_MAX)
nNodes = ADDRMAN_GETADDR_MAX;
int addrv2Nodes = nNodes/5;
int ipv4Nodes = 0;
int ipv6Nodes = 0;
int torNodes = 0;
int i2pNodes = 0;
int cjdnsNodes = 0;
// Randomize Nodes
for (unsigned int n = 0; n < vRandom.size(); n++) {
int nRndPos = RandomInt(vRandom.size() - n) + n;
SwapRandom(n, nRndPos);
}
// gather a list of random nodes, skipping those of low quality
for (unsigned int n = 0; n < vRandom.size(); n++) {
if (vAddr.size() >= nNodes)
break;
int nRndPos = RandomInt(vRandom.size() - n) + n;
SwapRandom(n, nRndPos);
assert(mapInfo.count(vRandom[n]) == 1);
const CAddrInfo& ai = mapInfo[vRandom[n]];
if (!ai.IsTerrible())
vAddr.push_back(ai);
if (!ai.IsTerrible()) {
if (!wants_addrv2) {
vAddr.push_back(ai);
} else {
if (ai.IsIPv4() && ipv4Nodes <= addrv2Nodes) {
vAddr.push_back(ai);
ipv4Nodes++;
}
if (ai.IsIPv6() && ipv6Nodes <= addrv2Nodes) {
vAddr.push_back(ai);
ipv6Nodes++;
}
if (ai.IsCJDNS() && cjdnsNodes <= addrv2Nodes) {
vAddr.push_back(ai);
cjdnsNodes++;
}
if (ai.IsTor() && torNodes <= addrv2Nodes) {
vAddr.push_back(ai);
torNodes++;
}
if (ai.IsI2P() && i2pNodes <= addrv2Nodes) {
vAddr.push_back(ai);
i2pNodes++;
}
}
}
}
}
@ -622,10 +707,36 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
info.nTime = nTime;
}
void CAddrMan::SetLocal_(const CService& addr)
{
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
CAddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
return;
// update info
info.fLocal = true;
}
int CAddrMan::RandomInt(int nMax){
return GetRandInt(nMax);
}
void CAddrMan::GetAllPeers(std::map<std::string, int64_t> &info) {
for(std::map<int, CAddrInfo>::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
info[(*it).second.ToStringIPPort()] = (*it).second.GetLastSuccess();
}
return;
}
std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
{
std::vector<bool> bits;

222
src/addrman.h

@ -24,6 +24,7 @@
#include "protocol.h"
#include "random.h"
#include "sync.h"
#include "streams.h"
#include "timedata.h"
#include "util.h"
#include "fs.h"
@ -64,18 +65,17 @@ private:
//! position in vRandom
int nRandomPos;
//! Address is local
bool fLocal;
friend class CAddrMan;
public:
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(*(CAddress*)this);
READWRITE(source);
READWRITE(nLastSuccess);
READWRITE(nAttempts);
SERIALIZE_METHODS(CAddrInfo, obj)
{
READWRITEAS(CAddress, obj);
READ_WRITE(obj.source, obj.nLastSuccess, obj.nAttempts);
}
void Init()
@ -86,6 +86,7 @@ public:
nRefCount = 0;
fInTried = false;
nRandomPos = -1;
fLocal = false;
}
CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
@ -116,9 +117,15 @@ public:
//! Determine whether the statistics about this entry are bad enough so that it can just be deleted
bool IsTerrible(int64_t nNow = GetTime()) const;
//Determine if this entry was just tried
bool IsJustTried(int64_t nNow = GetTime()) const;
//! Calculate the relative chance this entry should be given when selecting nodes to connect to
double GetChance(int64_t nNow = GetTime()) const;
//Returns the last successful connection
int64_t GetLastSuccess() {return nTime;}
};
/** Stochastic address manager
@ -199,8 +206,30 @@ private:
//! critical section to protect the inner data structures
mutable CCriticalSection cs;
//! Serialization versions.
enum Format : uint8_t {
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
V1_DETERMINISTIC = 1, //!< for pre-asmap files
V2_ASMAP = 2, //!< for files including asmap version
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
};
//! The maximum format this software knows it can unserialize. Also, we always serialize
//! in this format.
//! The format (first byte in the serialized stream) can be higher than this and
//! still this software may be able to unserialize the file - if the second byte
//! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
static constexpr Format FILE_FORMAT = Format::V3_BIP155;
//! The initial value of a field that is incremented every time an incompatible format
//! change is made (such that old software versions would not be able to parse and
//! understand the new file format). This is 32 because we overtook the "key size"
//! field which was 32 historically.
//! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
//! last used nId
int nIdCount;
int nIdCount GUARDED_BY(cs){0};
//! table with information about all nIds
std::map<int, CAddrInfo> mapInfo;
@ -280,12 +309,16 @@ protected:
#endif
//! Select several addresses at once.
void GetAddr_(std::vector<CAddress> &vAddr);
void GetAddr_(std::vector<CAddress> &vAddr, bool wants_addrv2);
//! Mark an entry as currently-connected-to.
void Connected_(const CService &addr, int64_t nTime);
//! Mark an entry as local
void SetLocal_(const CService &addr);
public:
void GetAllPeers(std::map<std::string, int64_t> &info);
// Compressed IP->ASN mapping, loaded from a file when a node starts.
// Should be always empty if no file was provided.
// This mapping is then used for bucketing nodes in Addrman.
@ -336,13 +369,22 @@ public:
* very little in common.
*/
template<typename Stream>
void Serialize(Stream &s) const
void Serialize(Stream &s_) const
EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
unsigned char nVersion = 2;
s << nVersion;
s << ((unsigned char)32);
// Always serialize in the latest version (FILE_FORMAT).
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
s << static_cast<uint8_t>(FILE_FORMAT);
// Increment `lowest_compatible` iff a newly introduced format is incompatible with
// the previous one.
static constexpr uint8_t lowest_compatible = Format::V3_BIP155;
s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
s << nKey;
s << nNew;
s << nTried;
@ -393,22 +435,40 @@ public:
}
template<typename Stream>
void Unserialize(Stream& s)
void Unserialize(Stream& s_)
{
LOCK(cs);
Clear();
unsigned char nVersion;
s >> nVersion;
unsigned char nKeySize;
s >> nKeySize;
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
assert(vRandom.empty());
Format format;
s_ >> Using<CustomUintFormatter<1>>(format);
int stream_version = s_.GetVersion();
if (format >= Format::V3_BIP155) {
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
// unserialize methods know that an address in addrv2 format is coming.
stream_version |= ADDRV2_FORMAT;
}
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
uint8_t compat;
s >> compat;
const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
if (lowest_compatible > FILE_FORMAT) {
throw std::ios_base::failure(strprintf(
"Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
"but the maximum supported by this version of %s is %u.",
format, lowest_compatible, PACKAGE_NAME, static_cast<uint8_t>(FILE_FORMAT)));
}
s >> nKey;
s >> nNew;
s >> nTried;
int nUBuckets = 0;
s >> nUBuckets;
if (nVersion != 0) {
if (format >= Format::V1_DETERMINISTIC) {
nUBuckets ^= (1 << 30);
}
@ -422,7 +482,7 @@ public:
// Deserialize entries from the new table.
for (int n = 0; n < nNew; n++) {
CAddrInfo &info = mapInfo[n];
CAddrInfo& info = mapInfo[n];
s >> info;
mapAddr[info] = n;
info.nRandomPos = vRandom.size();
@ -437,7 +497,7 @@ public:
s >> info;
int nKBucket = info.GetTriedBucket(nKey, m_asmap);
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
if (vvTried[nKBucket][nKBucketPos] == -1) {
if (info.IsValid() && vvTried[nKBucket][nKBucketPos] == -1) {
info.nRandomPos = vRandom.size();
info.fInTried = true;
vRandom.push_back(nIdCount);
@ -452,60 +512,84 @@ public:
nTried -= nLost;
// Store positions in the new table buckets to apply later (if possible).
std::map<int, int> entryToBucket; // Represents which entry belonged to which bucket when serializing
for (int bucket = 0; bucket < nUBuckets; bucket++) {
int nSize = 0;
s >> nSize;
for (int n = 0; n < nSize; n++) {
int nIndex = 0;
s >> nIndex;
if (nIndex >= 0 && nIndex < nNew) {
entryToBucket[nIndex] = bucket;
// An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
// so we store all bucket-entry_index pairs to iterate through later.
std::vector<std::pair<int, int>> bucket_entries;
for (int bucket = 0; bucket < nUBuckets; ++bucket) {
int num_entries{0};
s >> num_entries;
for (int n = 0; n < num_entries; ++n) {
int entry_index{0};
s >> entry_index;
if (entry_index >= 0 && entry_index < nNew) {
bucket_entries.emplace_back(bucket, entry_index);
}
}
}
uint256 supplied_asmap_version;
// If the bucket count and asmap checksum haven't changed, then attempt
// to restore the entries to the buckets/positions they were in before
// serialization.
uint256 supplied_asmap_checksum;
if (m_asmap.size() != 0) {
supplied_asmap_version = SerializeHash(m_asmap);
supplied_asmap_checksum = SerializeHash(m_asmap);
}
uint256 serialized_asmap_version;
if (nVersion > 1) {
s >> serialized_asmap_version;
uint256 serialized_asmap_checksum;
if (format >= Format::V2_ASMAP) {
s >> serialized_asmap_checksum;
}
const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
serialized_asmap_checksum == supplied_asmap_checksum};
for (int n = 0; n < nNew; n++) {
CAddrInfo &info = mapInfo[n];
int bucket = entryToBucket[n];
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
// Bucketing has not changed, using existing bucket positions for the new table
vvNew[bucket][nUBucketPos] = n;
info.nRefCount++;
} else {
// In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
// try to give them a reference based on their primary source address.
LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n");
bucket = info.GetNewBucket(nKey, m_asmap);
nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
if (vvNew[bucket][nUBucketPos] == -1) {
vvNew[bucket][nUBucketPos] = n;
info.nRefCount++;
if (!restore_bucketing) {
LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n");
}
for (auto bucket_entry : bucket_entries) {
int bucket{bucket_entry.first};
const int entry_index{bucket_entry.second};
// CAddrInfo& info = mapInfo[entry_index];
const auto it{mapInfo.find(entry_index)};
if (it != mapInfo.end()) {
CAddrInfo& info = (*it).second;
// Don't store the entry in the new bucket if it's not a valid address for our addrman
if (!info.IsValid()) continue;
// The entry shouldn't appear in more than
// ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
// this bucket_entry.
if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
int bucket_position = info.GetBucketPosition(nKey, true, bucket);
if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
// Bucketing has not changed, using existing bucket positions for the new table
vvNew[bucket][bucket_position] = entry_index;
++info.nRefCount;
} else {
// In case the new table data cannot be used (bucket count wrong or new asmap),
// try to give them a reference based on their primary source address.
bucket = info.GetNewBucket(nKey, m_asmap);
bucket_position = info.GetBucketPosition(nKey, true, bucket);
if (vvNew[bucket][bucket_position] == -1) {
vvNew[bucket][bucket_position] = entry_index;
++info.nRefCount;
}
}
}
}
// Prune new entries with refcount 0 (as a result of collisions).
int nLostUnk = 0;
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
if (it->second.fInTried == false && it->second.nRefCount == 0) {
std::map<int, CAddrInfo>::const_iterator itCopy = it++;
const auto itCopy = it++;
Delete(itCopy->first);
nLostUnk++;
++nLostUnk;
} else {
it++;
++it;
}
}
if (nLost + nLostUnk > 0) {
@ -531,7 +615,6 @@ public:
}
}
nIdCount = 0;
nTried = 0;
nNew = 0;
mapInfo.clear();
@ -657,13 +740,13 @@ public:
}
//! Return a bunch of addresses, selected at random.
std::vector<CAddress> GetAddr()
std::vector<CAddress> GetAddr(bool wants_addrv2 = false)
{
Check();
std::vector<CAddress> vAddr;
{
LOCK(cs);
GetAddr_(vAddr);
GetAddr_(vAddr, wants_addrv2);
}
Check();
return vAddr;
@ -680,6 +763,17 @@ public:
}
}
//! Mark an entry as currently-connected-to.
void SetLocal(const CService &addr)
{
{
LOCK(cs);
Check();
SetLocal_(addr);
Check();
}
}
};
#endif // HUSH_ADDRMAN_H

2
src/arith_uint256.cpp

@ -19,7 +19,7 @@
******************************************************************************/
#include "arith_uint256.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "crypto/common.h"
#include <stdio.h>
#include <string.h>

20
src/attributes.h

@ -0,0 +1,20 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef HUSH_ATTRIBUTES_H
#define HUSH_ATTRIBUTES_H
#if defined(__clang__)
# if __has_attribute(lifetimebound)
# define LIFETIMEBOUND [[clang::lifetimebound]]
# else
# define LIFETIMEBOUND
# endif
#else
# define LIFETIMEBOUND
#endif
#endif // HUSH_ATTRIBUTES_H

2
src/bitcoin-cli.cpp

@ -22,7 +22,7 @@
#include "rpc/client.h"
#include "rpc/protocol.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <boost/filesystem/operations.hpp>
#include <stdio.h>
#include <event2/buffer.h>

25
src/cc/Makefile

@ -2,9 +2,10 @@ SHELL = /bin/sh
CC = gcc
CC_DARWIN = g++-6
CC_WIN = x86_64-w64-mingw32-gcc-posix
CFLAGS_DARWIN = -std=c++11 -arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -c -Wl,-undefined -Wl,dynamic_lookup -dynamiclib
CFLAGS = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c
CFLAGS_WIN = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c
CFLAGS = -arch x86_64
CXXFLAGS_DARWIN = -std=c++11 -arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -c -Wl,-undefined -Wl,dynamic_lookup -dynamiclib
CXXFLAGS = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c
CXXFLAGS_WIN = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c
DEBUGFLAGS = -O0 -D _DEBUG
RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program
$(info $(OS))
@ -13,21 +14,27 @@ $(info $(OS))
TARGET = ../libcc.so
TARGET_DARWIN = ../libcc.dylib
TARGET_WIN = ../libcc.dll
SOURCES = cclib.cpp
#HEADERS = $(shell echo ../cryptoconditions/include/*.h)
SOURCES = cclib.cpp ../cJSON.c
OBJS = cclib.o ../cJSON.o
all: $(TARGET)
$(TARGET): $(SOURCES)
%.o: %.c
$(CC) -o $@ $< $(CFLAGS) $(DEBUGFLAGS)
%.o: %.cpp
$(CC) -o $@ $< $(CXXFLAGS) $(DEBUGFLAGS)
$(TARGET): $(OBJS)
$(info Building cclib to src/)
ifeq ($(OS),Darwin)
$(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) $(SOURCES)
$(CC_DARWIN) $(CXXFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) $(OBJS)
else ifeq ($(OS),Linux)
$(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) $(SOURCES)
$(CC) $(CXXFLAGS) $(DEBUGFLAGS) -o $(TARGET) $(OBJS)
#else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host
else
$(info WINDOWS)
$(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) $(SOURCES)
$(CC_WIN) $(CXXFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) $(OBJS)
endif
clean:

6
src/chainparams.cpp

@ -24,7 +24,7 @@
#include "main.h"
#include "crypto/equihash.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <assert.h>
#include <boost/assign/list_of.hpp>
#include "chainparamsseeds.h"
@ -205,7 +205,7 @@ public:
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-main";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_main), std::end(chainparams_seed_main));
fMiningRequiresPeers = true;
fDefaultConsistencyChecks = false;
@ -313,7 +313,7 @@ public:
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-test";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_test), std::end(chainparams_seed_test));
//fRequireRPCPassword = true;
fMiningRequiresPeers = false;//true;

9
src/chainparams.h

@ -36,11 +36,6 @@ struct CDNSSeedData {
CDNSSeedData(const std::string &strName, const std::string &strHost) : name(strName), host(strHost) {}
};
struct SeedSpec6 {
uint8_t addr[16];
uint16_t port;
};
typedef std::map<int, uint256> MapCheckpoints;
@ -108,7 +103,7 @@ public:
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
const std::string& Bech32HRP(Bech32Type type) const { return bech32HRPs[type]; }
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const std::vector<uint8_t>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
/** Return the founder's reward address and script for a given block height */
std::string GetFoundersRewardAddressAtHeight(int height) const;
@ -146,7 +141,7 @@ protected:
std::string strCurrencyUnits;
uint32_t bip44CoinType;
CBlock genesis;
std::vector<SeedSpec6> vFixedSeeds;
std::vector<uint8_t> vFixedSeeds;
bool fMiningRequiresPeers = false;
bool fDefaultConsistencyChecks = false;
bool fRequireStandard = false;

43
src/chainparamsseeds.h

@ -1,37 +1,20 @@
// Copyright (c) 2016-2022 The Hush developers
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
// THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY
// Instead, run: ./contrib/seeds/generate-seeds.py contrib/seeds
#ifndef HUSH_CHAINPARAMSSEEDS_H
#define HUSH_CHAINPARAMSSEEDS_H
/**
* List of fixed seed nodes for the bitcoin network
* AUTOGENERATED by contrib/seeds/generate-seeds.py
*
* Each line contains a 16-byte IPv6 address and a port.
* IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly.
*/
static SeedSpec6 pnSeed6_main[] = {
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27485},
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xec}, 27487},
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27485},
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x69,0x6f}, 27487},
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27485},
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x48}, 27487}
// List of fixed seed nodes for the Hush network
// Each line contains a BIP155 serialized address.
//
static const uint8_t chainparams_seed_main[] = {
0x01,0x04,0xb9,0xf1,0x3d,0x2b,0x00,0x00, // 185.241.61.43
0x01,0x04,0x89,0x4a,0x04,0xc6,0x00,0x00, // 137.74.4.198
0x01,0x04,0x95,0x1c,0x66,0xdb,0x00,0x00, // 149.28.102.219
};
static SeedSpec6 pnSeed6_test[] = {
static const uint8_t chainparams_seed_test[] = {
0x01,0x04,0x01,0x02,0x03,0x04,0x00,0x00, // 1.2.3.4
};
#endif // HUSH_CHAINPARAMSSEEDS_H

3
src/coins.cpp

@ -24,6 +24,9 @@
#include "hush_defs.h"
#include "importcoin.h"
#include <assert.h>
#include "util.h"
extern bool fZdebug;
/**
* calculate number of bytes for the bitmask, and its number of non-zero bytes
* each bit in the bitmask represents the availability of one output, but the

12
src/compat.h

@ -78,6 +78,8 @@ typedef u_int SOCKET;
#define SOCKET_ERROR -1
#endif
#define WSAEAGAIN EAGAIN
#ifdef _WIN32
#ifndef S_IRUSR
#define S_IRUSR 0400
@ -109,8 +111,14 @@ typedef u_int SOCKET;
size_t strnlen( const char *start, size_t max_len);
#endif // HAVE_DECL_STRNLEN
bool static inline IsSelectableSocket(SOCKET s) {
#ifdef _WIN32
#ifndef WIN32
typedef void* sockopt_arg_type;
#else
typedef char* sockopt_arg_type;
#endif
bool static inline IsSelectableSocket(const SOCKET& s) {
#ifdef WIN32
return true;
#else
return (s < FD_SETSIZE);

2
src/core_read.cpp

@ -27,7 +27,7 @@
#include "streams.h"
#include <univalue.h>
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "version.h"
#include <boost/algorithm/string/classification.hpp>

2
src/core_write.cpp

@ -29,7 +29,7 @@
#include <univalue.h>
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>

7
src/crypto/common.h

@ -62,6 +62,13 @@ void static inline WriteLE64(unsigned char* ptr, uint64_t x)
memcpy(ptr, (char*)&v, 8);
}
uint16_t static inline ReadBE16(const unsigned char* ptr)
{
uint16_t x;
memcpy((char*)&x, ptr, 2);
return be16toh(x);
}
uint32_t static inline ReadBE32(const unsigned char* ptr)
{
uint32_t x;

2
src/crypto/equihash.h

@ -8,7 +8,7 @@
#define HUSH_EQUIHASH_H
#include "crypto/sha256.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "sodium.h"
#include "hush_nk.h"
#include <cstring>

162
src/crypto/sha3.cpp

@ -0,0 +1,162 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
// Based on https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
// by Markku-Juhani O. Saarinen <mjos@iki.fi>
#include "crypto/sha3.h"
#include "crypto/common.h"
#include "span.h"
#include <algorithm>
#include <array> // For std::begin and std::end.
#include <stdint.h>
// Internal implementation code.
namespace
{
uint64_t Rotl(uint64_t x, int n) { return (x << n) | (x >> (64 - n)); }
} // namespace
void KeccakF(uint64_t (&st)[25])
{
static constexpr uint64_t RNDC[24] = {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};
static constexpr int ROUNDS = 24;
for (int round = 0; round < ROUNDS; ++round) {
uint64_t bc0, bc1, bc2, bc3, bc4, t;
// Theta
bc0 = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20];
bc1 = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21];
bc2 = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22];
bc3 = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
bc4 = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
t = bc4 ^ Rotl(bc1, 1); st[0] ^= t; st[5] ^= t; st[10] ^= t; st[15] ^= t; st[20] ^= t;
t = bc0 ^ Rotl(bc2, 1); st[1] ^= t; st[6] ^= t; st[11] ^= t; st[16] ^= t; st[21] ^= t;
t = bc1 ^ Rotl(bc3, 1); st[2] ^= t; st[7] ^= t; st[12] ^= t; st[17] ^= t; st[22] ^= t;
t = bc2 ^ Rotl(bc4, 1); st[3] ^= t; st[8] ^= t; st[13] ^= t; st[18] ^= t; st[23] ^= t;
t = bc3 ^ Rotl(bc0, 1); st[4] ^= t; st[9] ^= t; st[14] ^= t; st[19] ^= t; st[24] ^= t;
// Rho Pi
t = st[1];
bc0 = st[10]; st[10] = Rotl(t, 1); t = bc0;
bc0 = st[7]; st[7] = Rotl(t, 3); t = bc0;
bc0 = st[11]; st[11] = Rotl(t, 6); t = bc0;
bc0 = st[17]; st[17] = Rotl(t, 10); t = bc0;
bc0 = st[18]; st[18] = Rotl(t, 15); t = bc0;
bc0 = st[3]; st[3] = Rotl(t, 21); t = bc0;
bc0 = st[5]; st[5] = Rotl(t, 28); t = bc0;
bc0 = st[16]; st[16] = Rotl(t, 36); t = bc0;
bc0 = st[8]; st[8] = Rotl(t, 45); t = bc0;
bc0 = st[21]; st[21] = Rotl(t, 55); t = bc0;
bc0 = st[24]; st[24] = Rotl(t, 2); t = bc0;
bc0 = st[4]; st[4] = Rotl(t, 14); t = bc0;
bc0 = st[15]; st[15] = Rotl(t, 27); t = bc0;
bc0 = st[23]; st[23] = Rotl(t, 41); t = bc0;
bc0 = st[19]; st[19] = Rotl(t, 56); t = bc0;
bc0 = st[13]; st[13] = Rotl(t, 8); t = bc0;
bc0 = st[12]; st[12] = Rotl(t, 25); t = bc0;
bc0 = st[2]; st[2] = Rotl(t, 43); t = bc0;
bc0 = st[20]; st[20] = Rotl(t, 62); t = bc0;
bc0 = st[14]; st[14] = Rotl(t, 18); t = bc0;
bc0 = st[22]; st[22] = Rotl(t, 39); t = bc0;
bc0 = st[9]; st[9] = Rotl(t, 61); t = bc0;
bc0 = st[6]; st[6] = Rotl(t, 20); t = bc0;
st[1] = Rotl(t, 44);
// Chi Iota
bc0 = st[0]; bc1 = st[1]; bc2 = st[2]; bc3 = st[3]; bc4 = st[4];
st[0] = bc0 ^ (~bc1 & bc2) ^ RNDC[round];
st[1] = bc1 ^ (~bc2 & bc3);
st[2] = bc2 ^ (~bc3 & bc4);
st[3] = bc3 ^ (~bc4 & bc0);
st[4] = bc4 ^ (~bc0 & bc1);
bc0 = st[5]; bc1 = st[6]; bc2 = st[7]; bc3 = st[8]; bc4 = st[9];
st[5] = bc0 ^ (~bc1 & bc2);
st[6] = bc1 ^ (~bc2 & bc3);
st[7] = bc2 ^ (~bc3 & bc4);
st[8] = bc3 ^ (~bc4 & bc0);
st[9] = bc4 ^ (~bc0 & bc1);
bc0 = st[10]; bc1 = st[11]; bc2 = st[12]; bc3 = st[13]; bc4 = st[14];
st[10] = bc0 ^ (~bc1 & bc2);
st[11] = bc1 ^ (~bc2 & bc3);
st[12] = bc2 ^ (~bc3 & bc4);
st[13] = bc3 ^ (~bc4 & bc0);
st[14] = bc4 ^ (~bc0 & bc1);
bc0 = st[15]; bc1 = st[16]; bc2 = st[17]; bc3 = st[18]; bc4 = st[19];
st[15] = bc0 ^ (~bc1 & bc2);
st[16] = bc1 ^ (~bc2 & bc3);
st[17] = bc2 ^ (~bc3 & bc4);
st[18] = bc3 ^ (~bc4 & bc0);
st[19] = bc4 ^ (~bc0 & bc1);
bc0 = st[20]; bc1 = st[21]; bc2 = st[22]; bc3 = st[23]; bc4 = st[24];
st[20] = bc0 ^ (~bc1 & bc2);
st[21] = bc1 ^ (~bc2 & bc3);
st[22] = bc2 ^ (~bc3 & bc4);
st[23] = bc3 ^ (~bc4 & bc0);
st[24] = bc4 ^ (~bc0 & bc1);
}
}
SHA3_256_& SHA3_256_::Write(Span<const unsigned char> data)
{
if (m_bufsize && m_bufsize + data.size() >= sizeof(m_buffer)) {
// Fill the buffer and process it.
std::copy(data.begin(), data.begin() + sizeof(m_buffer) - m_bufsize, m_buffer + m_bufsize);
data = data.subspan(sizeof(m_buffer) - m_bufsize);
m_state[m_pos++] ^= ReadLE64(m_buffer);
m_bufsize = 0;
if (m_pos == RATE_BUFFERS) {
KeccakF(m_state);
m_pos = 0;
}
}
while (data.size() >= sizeof(m_buffer)) {
// Process chunks directly from the buffer.
m_state[m_pos++] ^= ReadLE64(data.data());
data = data.subspan(8);
if (m_pos == RATE_BUFFERS) {
KeccakF(m_state);
m_pos = 0;
}
}
if (data.size()) {
// Keep the remainder in the buffer.
std::copy(data.begin(), data.end(), m_buffer + m_bufsize);
m_bufsize += data.size();
}
return *this;
}
SHA3_256_& SHA3_256_::Finalize(Span<unsigned char> output)
{
assert(output.size() == OUTPUT_SIZE);
std::fill(m_buffer + m_bufsize, m_buffer + sizeof(m_buffer), 0);
m_buffer[m_bufsize] ^= 0x06;
m_state[m_pos] ^= ReadLE64(m_buffer);
m_state[RATE_BUFFERS - 1] ^= 0x8000000000000000;
KeccakF(m_state);
for (unsigned i = 0; i < 4; ++i) {
WriteLE64(output.data() + 8 * i, m_state[i]);
}
return *this;
}
SHA3_256_& SHA3_256_::Reset()
{
m_bufsize = 0;
m_pos = 0;
std::fill(std::begin(m_state), std::end(m_state), 0);
return *this;
}

42
src/crypto/sha3.h

@ -0,0 +1,42 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef HUSH_CRYPTO_SHA3_H
#define HUSH_CRYPTO_SHA3_H
#include "span.h"
#include <stdint.h>
#include <stdlib.h>
//! The Keccak-f[1600] transform.
void KeccakF(uint64_t (&st)[25]);
class SHA3_256_
{
private:
uint64_t m_state[25] = {0};
unsigned char m_buffer[8];
unsigned m_bufsize = 0;
unsigned m_pos = 0;
//! Sponge rate in bits.
static constexpr unsigned RATE_BITS = 1088;
//! Sponge rate expressed as a multiple of the buffer size.
static constexpr unsigned RATE_BUFFERS = RATE_BITS / (8 * sizeof(m_buffer));
static_assert(RATE_BITS % (8 * sizeof(m_buffer)) == 0, "Rate must be a multiple of 8 bytes");
public:
static constexpr size_t OUTPUT_SIZE = 32;
SHA3_256_() {}
SHA3_256_& Write(Span<const unsigned char> data);
SHA3_256_& Finalize(Span<unsigned char> output);
SHA3_256_& Reset();
};
#endif // HUSH_CRYPTO_SHA3_H

2
src/gtest/json_test_vectors.h

@ -3,7 +3,7 @@
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <gtest/gtest.h>
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "version.h"
#include "serialize.h"
#include "streams.h"

2
src/gtest/test_deprecation.cpp

@ -11,7 +11,7 @@
#include "init.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <boost/filesystem/operations.hpp>
#include <fstream>

2
src/gtest/test_merkletree.cpp

@ -19,7 +19,7 @@
#include <stdexcept>
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "version.h"
#include "serialize.h"
#include "streams.h"

2
src/gtest/test_rpc.cpp

@ -10,7 +10,7 @@
#include "primitives/block.h"
#include "rpc/server.h"
#include "streams.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);

2
src/gtest/test_txid.cpp

@ -8,7 +8,7 @@
#include "streams.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
/*
Test that removing #1144 succeeded by verifying the hash of a transaction is over the entire serialized form.

67
src/hash.h

@ -28,9 +28,7 @@
#include "serialize.h"
#include "uint256.h"
#include "version.h"
#include "sodium.h"
#include <vector>
typedef uint256 ChainCode;
@ -48,6 +46,18 @@ public:
sha.Reset().Write(buf, sha.OUTPUT_SIZE).Finalize(hash);
}
CHash256& Write(Span<const unsigned char> input) {
sha.Write(input.data(), input.size());
return *this;
}
void Finalize(Span<unsigned char> output) {
assert(output.size() == OUTPUT_SIZE);
unsigned char buf[CSHA256::OUTPUT_SIZE];
sha.Finalize(buf);
sha.Reset().Write(buf, CSHA256::OUTPUT_SIZE).Finalize(output.data());
}
CHash256& Write(const unsigned char *data, size_t len) {
sha.Write(data, len);
return *this;
@ -120,6 +130,23 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end,
return result;
}
/** Compute the 256-bit hash of an object. */
template<typename T>
inline uint256 Hash(const T& in1)
{
uint256 result;
CHash256().Write(MakeUCharSpan(in1)).Finalize(result);
return result;
}
/** Compute the 256-bit hash of the concatenation of two objects. */
template<typename T1, typename T2>
inline uint256 Hash(const T1& in1, const T2& in2) {
uint256 result;
CHash256().Write(MakeUCharSpan(in1)).Write(MakeUCharSpan(in2)).Finalize(result);
return result;
}
/** Compute the 160-bit hash an object. */
template<typename T1>
inline uint160 Hash160(const T1 pbegin, const T1 pend)
@ -178,6 +205,40 @@ public:
}
};
/** Reads data from an underlying stream, while hashing the read data. */
template<typename Source>
class CHashVerifier : public CHashWriter
{
private:
Source* source;
public:
explicit CHashVerifier(Source* source_) : CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {}
void read(char* pch, size_t nSize)
{
source->read(pch, nSize);
this->write(pch, nSize);
}
void ignore(size_t nSize)
{
char data[1024];
while (nSize > 0) {
size_t now = std::min<size_t>(nSize, 1024);
read(data, now);
nSize -= now;
}
}
template<typename T>
CHashVerifier<Source>& operator>>(T&& obj)
{
// Unserialize from this stream
::Unserialize(*this, obj);
return (*this);
}
};
/** A writer stream (for serialization) that computes a 256-bit BLAKE2b hash. */
class CBLAKE2bWriter
@ -221,6 +282,7 @@ public:
}
};
/** Compute the 256-bit hash of an object's serialization. */
template<typename T>
uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
@ -230,6 +292,7 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL
return ss.GetHash();
}
unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash);
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);

2
src/httprpc.cpp

@ -24,7 +24,7 @@
#include "random.h"
#include "sync.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "ui_interface.h"
#include <boost/algorithm/string.hpp> // boost::trim

57
src/httpserver.cpp

@ -7,11 +7,13 @@
#include "chainparamsbase.h"
#include "compat.h"
#include "util.h"
#include "util/strencodings.h"
#include "netbase.h"
#include "rpc/protocol.h" // For HTTP status codes
#include "sync.h"
#include "ui_interface.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -22,6 +24,7 @@
#include <event2/http.h>
#include <event2/thread.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>
@ -157,6 +160,7 @@ public:
boost::unique_lock<boost::mutex> lock(cs);
return queue.size();
}
size_t MaxDepth()
{
boost::unique_lock<boost::mutex> lock(cs);
@ -167,6 +171,7 @@ public:
boost::unique_lock<boost::mutex> lock(cs);
return numThreads;
}
};
struct HTTPPathHandler
@ -196,7 +201,6 @@ std::vector<HTTPPathHandler> pathHandlers;
//! Bound listening sockets
std::vector<evhttp_bound_socket *> boundSockets;
int getWorkQueueDepth()
{
return workQueue->Depth();
@ -227,12 +231,17 @@ static bool ClientAllowed(const CNetAddr& netaddr)
static bool InitHTTPAllowList()
{
rpc_allow_subnets.clear();
rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
CNetAddr localv4;
CNetAddr localv6;
LookupHost("127.0.0.1", localv4, false);
LookupHost("::1", localv6, false);
rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet
rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost
if (mapMultiArgs.count("-rpcallowip")) {
const std::vector<std::string>& vAllow = mapMultiArgs["-rpcallowip"];
BOOST_FOREACH (std::string strAllow, vAllow) {
CSubNet subnet(strAllow);
CSubNet subnet;
LookupSubNet(strAllow.c_str(), subnet);
if (!subnet.IsValid()) {
uiInterface.ThreadSafeMessageBox(
strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
@ -273,6 +282,16 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m)
/** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg)
{
// Disable reading to work around a libevent bug, fixed in 2.2.0.
if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
evhttp_connection* conn = evhttp_request_get_connection(req);
if (conn) {
bufferevent* bev = evhttp_connection_get_bufferevent(conn);
if (bev) {
bufferevent_disable(bev, EV_READ);
}
}
}
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
// Early address-based allow check
@ -315,11 +334,10 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
if (i != iend) {
std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(hreq.release(), path, i->handler));
assert(workQueue);
if (workQueue->Enqueue(item.get())) {
if (workQueue->Enqueue(item.get()))
item.release(); /* if true, queue took ownership */
} else {
item->req->WriteReply(HTTP_INTERNAL, strprintf("Work queue depth %d exceeded", workQueue->Depth() ));
}
else
item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded");
} else {
hreq->WriteReply(HTTP_NOTFOUND);
}
@ -541,7 +559,7 @@ struct event_base* EventBase()
static void httpevent_callback_fn(evutil_socket_t, short, void* data)
{
// Static handler: simply call inner handler
HTTPEvent *self = ((HTTPEvent*)data);
HTTPEvent *self = static_cast<HTTPEvent*>(data);
self->handler();
if (self->deleteWhenTriggered)
delete self;
@ -628,8 +646,21 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent* ev = new HTTPEvent(eventBase, true,
boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL));
auto req_copy = req;
HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
evhttp_send_reply(req_copy, nStatus, (const char*)NULL, (struct evbuffer *)NULL);
// Re-enable reading from the socket. This is the second part of the libevent
// workaround above.
if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
evhttp_connection* conn = evhttp_request_get_connection(req_copy);
if (conn) {
bufferevent* bev = evhttp_connection_get_bufferevent(conn);
if (bev) {
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
}
}
});
ev->trigger(0);
replySent = true;
req = 0; // transferred back to main thread
@ -644,7 +675,7 @@ CService HTTPRequest::GetPeer()
const char* address = "";
uint16_t port = 0;
evhttp_connection_get_peer(con, (char**)&address, &port);
peer = CService(address, port);
peer = LookupNumeric(address, port);
}
return peer;
}

2
src/hush-tx.cpp

@ -29,7 +29,7 @@
#include <univalue.h>
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <stdio.h>
#include <boost/algorithm/string.hpp>
#include <boost/assign/list_of.hpp>

441
src/i2p.cpp

@ -0,0 +1,441 @@
// Copyright (c) 2020-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <init.h>
#include <chainparams.h>
#include <compat.h>
#include <compat/endian.h>
#include <crypto/sha256.h>
#include <fs.h>
#include <i2p.h>
#include <netbase.h>
#include <random.h>
#include <util/strencodings.h>
#include <tinyformat.h>
#include <util/readwritefile.h>
#include <util/sock.h>
#include <util/spanparsing.h>
#include <util.h>
#include <chrono>
#include <memory>
#include <stdexcept>
#include <string>
namespace i2p {
/**
* Swap Standard Base64 <-> I2P Base64.
* Standard Base64 uses `+` and `/` as last two characters of its alphabet.
* I2P Base64 uses `-` and `~` respectively.
* So it is easy to detect in which one is the input and convert to the other.
* @param[in] from Input to convert.
* @return converted `from`
*/
static std::string SwapBase64(const std::string& from)
{
std::string to;
to.resize(from.size());
for (size_t i = 0; i < from.size(); ++i) {
switch (from[i]) {
case '-':
to[i] = '+';
break;
case '~':
to[i] = '/';
break;
case '+':
to[i] = '-';
break;
case '/':
to[i] = '~';
break;
default:
to[i] = from[i];
break;
}
}
return to;
}
/**
* Decode an I2P-style Base64 string.
* @param[in] i2p_b64 I2P-style Base64 string.
* @return decoded `i2p_b64`
* @throw std::runtime_error if decoding fails
*/
static Binary DecodeI2PBase64(const std::string& i2p_b64)
{
const std::string& std_b64 = SwapBase64(i2p_b64);
bool invalid;
Binary decoded = DecodeBase64(std_b64.c_str(), &invalid);
if (invalid) {
throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64));
}
return decoded;
}
/**
* Derive the .b32.i2p address of an I2P destination (binary).
* @param[in] dest I2P destination.
* @return the address that corresponds to `dest`
* @throw std::runtime_error if conversion fails
*/
static CNetAddr DestBinToAddr(const Binary& dest)
{
CSHA256 hasher;
hasher.Write(dest.data(), dest.size());
unsigned char hash[CSHA256::OUTPUT_SIZE];
hasher.Finalize(hash);
CNetAddr addr;
const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p";
if (!addr.SetSpecial(addr_str)) {
throw std::runtime_error(strprintf("Cannot parse I2P address: \"%s\"", addr_str));
}
return addr;
}
/**
* Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
* @param[in] dest I2P destination.
* @return the address that corresponds to `dest`
* @throw std::runtime_error if conversion fails
*/
static CNetAddr DestB64ToAddr(const std::string& dest)
{
const Binary& decoded = DecodeI2PBase64(dest);
return DestBinToAddr(decoded);
}
namespace sam {
Session::Session(const fs::path& private_key_file,
const CService& control_host)
: m_private_key_file(private_key_file), m_control_host(control_host),
m_control_sock(std::unique_ptr<Sock>(new Sock(INVALID_SOCKET)))
{
}
Session::~Session()
{
}
bool Session::Check()
{
try {
LOCK(cs_i2p);
CreateIfNotCreatedAlready();
return true;
} catch (const std::runtime_error& e) {
LogPrint("i2p","I2P: Error Checking Session: %s\n", e.what());
CheckControlSock();
}
return false;
}
bool Session::Listen(Connection& conn)
{
try {
LOCK(cs_i2p);
CreateIfNotCreatedAlready();
conn.me = m_my_addr;
conn.sock = StreamAccept();
return true;
} catch (const std::runtime_error& e) {
LogPrint("i2p","I2P: Error listening: %s\n", e.what());
CheckControlSock();
}
return false;
}
bool Session::Accept(Connection& conn)
{
try {
while (true) {
// boost::this_thread::interruption_point();
if (ShutdownRequested()) {
Disconnect();
return false;
}
Sock::Event occurred;
if (!conn.sock->Wait(std::chrono::milliseconds{MAX_WAIT_FOR_IO}, Sock::RECV, &occurred)) {
throw std::runtime_error("wait on socket failed");
}
if ((occurred & Sock::RECV) == 0) {
// Timeout, no incoming connections within MAX_WAIT_FOR_IO.
continue;
}
const std::string& peer_dest =
conn.sock->RecvUntilTerminator('\n', std::chrono::milliseconds{MAX_WAIT_FOR_IO}, MAX_MSG_SIZE);
if (ShutdownRequested()) {
Disconnect();
return false;
}
conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort());
return true;
}
} catch (const std::runtime_error& e) {
LogPrint("i2p","I2P: Error accepting: %s\n", e.what());
CheckControlSock();
}
return false;
}
bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
{
proxy_error = true;
std::string session_id;
std::unique_ptr<Sock> sock;
conn.peer = to;
try {
{
LOCK(cs_i2p);
CreateIfNotCreatedAlready();
session_id = m_session_id;
conn.me = m_my_addr;
sock = Hello();
}
const Reply& lookup_reply =
SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));
const std::string& dest = lookup_reply.Get("VALUE");
const Reply& connect_reply = SendRequestAndGetReply(
*sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
false);
const std::string& result = connect_reply.Get("RESULT");
if (result == "OK") {
conn.sock = std::move(sock);
return true;
}
if (result == "INVALID_ID") {
LOCK(cs_i2p);
Disconnect();
throw std::runtime_error("Invalid session id");
}
if (result == "CANT_REACH_PEER" || result == "TIMEOUT" || "KEY_NOT_FOUND") {
proxy_error = false;
}
throw std::runtime_error(strprintf("\"%s\"", connect_reply.full));
} catch (const std::runtime_error& e) {
LogPrint("i2p","I2P: Error connecting to %s: %s\n", to.ToString(), e.what());
CheckControlSock();
return false;
}
}
// Private methods
std::string Session::Reply::Get(const std::string& key) const
{
const auto& pos = keys.find(key);
if (pos == keys.end() || !pos->second.has_value()) {
throw std::runtime_error(
strprintf("Missing %s= in the reply to \"%s\": \"%s\"", key, request, full));
}
return pos->second.value();
}
Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
const std::string& request,
bool check_result_ok) const
{
sock.SendComplete(request + "\n", std::chrono::milliseconds{MAX_WAIT_FOR_IO});
Reply reply;
// Don't log the full "SESSION CREATE ..." because it contains our private key.
reply.request = request.substr(0, 14) == "SESSION CREATE" ? "SESSION CREATE ..." : request;
// It could take a few minutes for the I2P router to reply as it is querying the I2P network
// (when doing name lookup, for example).
reply.full = sock.RecvUntilTerminator('\n', std::chrono::minutes{3}, MAX_MSG_SIZE);
for (const auto& kv : spanparsing::Split(reply.full, ' ')) {
const auto& pos = std::find(kv.begin(), kv.end(), '=');
if (pos != kv.end()) {
reply.keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()});
} else {
reply.keys.emplace(std::string{kv.begin(), kv.end()}, boost::none);
}
}
LogPrint("i2p","I2P: Handshake reply %s\n", reply.full);
if (check_result_ok && reply.Get("RESULT") != "OK") {
if (!ShutdownRequested()) {
throw std::runtime_error(strprintf("Unexpected reply to \"%s\": \"%s\"", request, reply.full));
}
}
return reply;
}
std::unique_ptr<Sock> Session::Hello() const
{
auto sock = CreateSock(m_control_host);
if (!sock) {
throw std::runtime_error("Cannot create socket");
}
if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout)) {
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
}
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
return sock;
}
void Session::CheckControlSock()
{
LOCK(cs_i2p);
std::string errmsg;
if (!m_control_sock->IsConnected(errmsg)) {
LogPrint("i2p","I2P: Control socket error: %s\n", errmsg);
Disconnect();
}
}
void Session::DestGenerate(const Sock& sock)
{
// https://geti2p.net/spec/common-structures#key-certificates
// "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
// Use "7" because i2pd <2.24.0 does not recognize the textual form.
const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);
m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
}
void Session::GenerateAndSavePrivateKey(const Sock& sock)
{
DestGenerate(sock);
// umask is set to 077 in init.cpp, which is ok (unless -sysperms is given)
if (!WriteBinaryFile(m_private_key_file,
std::string(m_private_key.begin(), m_private_key.end()))) {
throw std::runtime_error(
strprintf("Cannot save I2P private key to %s", m_private_key_file));
}
}
Binary Session::MyDestination() const
{
// From https://geti2p.net/spec/common-structures#destination:
// "They are 387 bytes plus the certificate length specified at bytes 385-386, which may be
// non-zero"
static constexpr size_t DEST_LEN_BASE = 387;
static constexpr size_t CERT_LEN_POS = 385;
uint16_t cert_len;
memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
cert_len = be16toh(cert_len);
const size_t dest_len = DEST_LEN_BASE + cert_len;
return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
}
void Session::CreateIfNotCreatedAlready()
{
LOCK(cs_i2p);
std::string errmsg;
if (m_control_sock->IsConnected(errmsg)) {
return;
}
LogPrint("i2p","I2P: Creating SAM session with %s\n", m_control_host.ToString());
auto sock = Hello();
const std::pair<bool,std::string> i2pRead = ReadBinaryFile(m_private_key_file);
if (i2pRead.first) {
m_private_key.assign(i2pRead.second.begin(), i2pRead.second.end());
} else {
GenerateAndSavePrivateKey(*sock);
}
const std::string& session_id = GetRandHash().GetHex().substr(0, 10); // full is an overkill, too verbose in the logs
const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
SendRequestAndGetReply(*sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
session_id, private_key_b64));
m_my_addr = CService(DestBinToAddr(MyDestination()), Params().GetDefaultPort());
m_session_id = session_id;
m_control_sock = std::move(sock);
LogPrint("i2p","I2P: SAM session created: session id=%s, my address=%s\n", m_session_id,
m_my_addr.ToString());
}
std::unique_ptr<Sock> Session::StreamAccept()
{
auto sock = Hello();
const Reply& reply = SendRequestAndGetReply(
*sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);
const std::string& result = reply.Get("RESULT");
if (result == "OK") {
return sock;
}
if (result == "INVALID_ID") {
// If our session id is invalid, then force session re-creation on next usage.
Disconnect();
}
throw std::runtime_error(strprintf("\"%s\"", reply.full));
}
void Session::Disconnect()
{
LOCK(cs_i2p);
try
{
if (m_control_sock->Get() != INVALID_SOCKET) {
if (m_session_id.empty()) {
LogPrint("i2p","I2P: Destroying incomplete session\n");
} else {
LogPrint("i2p","I2P: Destroying session %s\n", m_session_id);
}
}
m_control_sock->Reset();
m_session_id.clear();
}
catch(std::bad_alloc&)
{
// when the node is shutting down, the call above might use invalid memory resulting in a
// std::bad_alloc exception when instantiating internal objs for handling log category
LogPrintf("(node is probably shutting down) Destroying session=%d\n", m_session_id);
}
}
} // namespace sam
} // namespace i2p

256
src/i2p.h

@ -0,0 +1,256 @@
// Copyright (c) 2020-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef HUSH_I2P_H
#define HUSH_I2P_H
#include <compat.h>
#include <fs.h>
#include <netaddress.h>
#include <sync.h>
#include <util/sock.h>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace i2p {
/**
* Binary data.
*/
using Binary = std::vector<uint8_t>;
/**
* An established connection with another peer.
*/
struct Connection {
/** Connected socket. */
std::unique_ptr<Sock> sock;
/** Our I2P address. */
CService me;
/** The peer's I2P address. */
CService peer;
};
namespace sam {
/**
* The maximum size of an incoming message from the I2P SAM proxy (in bytes).
* Used to avoid a runaway proxy from sending us an "unlimited" amount of data without a terminator.
* The longest known message is ~1400 bytes, so this is high enough not to be triggered during
* normal operation, yet low enough to avoid a malicious proxy from filling our memory.
*/
static constexpr size_t MAX_MSG_SIZE{65536};
/**
* I2P SAM session.
*/
class Session
{
public:
/**
* Construct a session. This will not initiate any IO, the session will be lazily created
* later when first used.
* @param[in] private_key_file Path to a private key file. If the file does not exist then the
* private key will be generated and saved into the file.
* @param[in] control_host Location of the SAM proxy.
*/
Session(const fs::path& private_key_file,
const CService& control_host);
/**
* Destroy the session, closing the internally used sockets. The sockets that have been
* returned by `Accept()` or `Connect()` will not be closed, but they will be closed by
* the SAM proxy because the session is destroyed. So they will return an error next time
* we try to read or write to them.
*/
~Session();
/**
* Check the control sock and restart if needed
*/
bool Check();
/**
* Start listening for an incoming connection.
* @param[out] conn Upon successful completion the `sock` and `me` members will be set
* to the listening socket and address.
* @return true on success
*/
bool Listen(Connection& conn);
/**
* Wait for and accept a new incoming connection.
* @param[in,out] conn The `sock` member is used for waiting and accepting. Upon successful
* completion the `peer` member will be set to the address of the incoming peer.
* @return true on success
*/
bool Accept(Connection& conn);
/**
* Connect to an I2P peer.
* @param[in] to Peer to connect to.
* @param[out] conn Established connection. Only set if `true` is returned.
* @param[out] proxy_error If an error occurs due to proxy or general network failure, then
* this is set to `true`. If an error occurs due to unreachable peer (likely peer is down), then
* it is set to `false`. Only set if `false` is returned.
* @return true on success
*/
bool Connect(const CService& to, Connection& conn, bool& proxy_error);
protected:
CCriticalSection cs_i2p;
private:
/**
* A reply from the SAM proxy.
*/
struct Reply {
/**
* Full, unparsed reply.
*/
std::string full;
/**
* Request, used for detailed error reporting.
*/
std::string request;
/**
* A map of keywords from the parsed reply.
* For example, if the reply is "A=X B C=YZ", then the map will be
* keys["A"] == "X"
* keys["B"] == (empty std::optional)
* keys["C"] == "YZ"
*/
std::unordered_map<std::string, boost::optional<std::string>> keys;
/**
* Get the value of a given key.
* For example if the reply is "A=X B" then:
* Value("A") -> "X"
* Value("B") -> throws
* Value("C") -> throws
* @param[in] key Key whose value to retrieve
* @returns the key's value
* @throws std::runtime_error if the key is not present or if it has no value
*/
std::string Get(const std::string& key) const;
};
/**
* Send request and get a reply from the SAM proxy.
* @param[in] sock A socket that is connected to the SAM proxy.
* @param[in] request Raw request to send, a newline terminator is appended to it.
* @param[in] check_result_ok If true then after receiving the reply a check is made
* whether it contains "RESULT=OK" and an exception is thrown if it does not.
* @throws std::runtime_error if an error occurs
*/
Reply SendRequestAndGetReply(const Sock& sock,
const std::string& request,
bool check_result_ok = true) const;
/**
* Open a new connection to the SAM proxy.
* @return a connected socket
* @throws std::runtime_error if an error occurs
*/
std::unique_ptr<Sock> Hello() const EXCLUSIVE_LOCKS_REQUIRED(cs_i2p);
/**
* Check the control socket for errors and possibly disconnect.
*/
void CheckControlSock();
/**
* Generate a new destination with the SAM proxy and set `m_private_key` to it.
* @param[in] sock Socket to use for talking to the SAM proxy.
* @throws std::runtime_error if an error occurs
*/
void DestGenerate(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(cs_i2p);
/**
* Generate a new destination with the SAM proxy, set `m_private_key` to it and save
* it on disk to `m_private_key_file`.
* @param[in] sock Socket to use for talking to the SAM proxy.
* @throws std::runtime_error if an error occurs
*/
void GenerateAndSavePrivateKey(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(cs_i2p);
/**
* Derive own destination from `m_private_key`.
* @see https://geti2p.net/spec/common-structures#destination
* @return an I2P destination
*/
Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(cs_i2p);
/**
* Create the session if not already created. Reads the private key file and connects to the
* SAM proxy.
* @throws std::runtime_error if an error occurs
*/
void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(cs_i2p);
/**
* Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing
* session id.
* @return the idle socket that is waiting for a peer to connect to us
* @throws std::runtime_error if an error occurs
*/
std::unique_ptr<Sock> StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(cs_i2p);
/**
* Destroy the session, closing the internally used sockets.
*/
void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(cs_i2p);
/**
* The name of the file where this peer's private key is stored (in binary).
*/
const fs::path m_private_key_file;
/**
* The host and port of the SAM control service.
*/
const CService m_control_host;
/**
* The private key of this peer.
* @see The reply to the "DEST GENERATE" command in https://geti2p.net/en/docs/api/samv3
*/
Binary m_private_key GUARDED_BY(cs_i2p);
/**
* SAM control socket.
* Used to connect to the I2P SAM service and create a session
* ("SESSION CREATE"). With the established session id we later open
* other connections to the SAM service to accept incoming I2P
* connections and make outgoing ones.
* See https://geti2p.net/en/docs/api/samv3
*/
std::unique_ptr<Sock> m_control_sock GUARDED_BY(cs_i2p);
/**
* Our .b32.i2p address.
* Derived from `m_private_key`.
*/
CService m_my_addr GUARDED_BY(cs_i2p);
/**
* SAM session id.
*/
std::string m_session_id GUARDED_BY(cs_i2p);
};
} // namespace sam
} // namespace i2p
#endif /* HUSH_I2P_H */

77
src/init.cpp

@ -246,14 +246,22 @@ void Shutdown()
if (pcoinsTip != NULL) {
FlushStateToDisk();
}
delete pcoinsTip;
pcoinsTip = NULL;
delete pcoinscatcher;
pcoinscatcher = NULL;
delete pcoinsdbview;
pcoinsdbview = NULL;
delete pblocktree;
pblocktree = NULL;
if (pcoinsTip != NULL) {
delete pcoinsTip;
pcoinsTip = NULL;
}
if (pcoinscatcher != NULL) {
delete pcoinscatcher;
pcoinscatcher = NULL;
}
if (pcoinsdbview != NULL) {
delete pcoinsdbview;
pcoinsdbview = NULL;
}
if (pblocktree != NULL) {
delete pblocktree;
pblocktree = NULL;
}
}
#ifdef ENABLE_WALLET
if (pwalletMain)
@ -274,7 +282,7 @@ void Shutdown()
#endif
globalVerifyHandle.reset();
ECC_Stop();
CNode::NetCleanup();
// CNode::NetCleanup();
LogPrintf("%s: done\n", __func__);
}
@ -325,7 +333,7 @@ bool static InitWarning(const std::string &str)
}
bool static Bind(const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr))
if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
return false;
std::string strError;
if (!BindListenPort(addr, strError, (flags & BF_ALLOWLIST) != 0)) {
@ -416,7 +424,14 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), 5000));
strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), 1000));
strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy"));
strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"));
strUsage += HelpMessageOpt("-nspv_msg", strprintf(_("Enable NSPV messages processing (default: true when -ac_private=1, otherwise false)")));
strUsage += HelpMessageOpt("-i2psam=<ip:port>", strprintf(_("I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)")));
strUsage += HelpMessageOpt("-i2pacceptincoming", strprintf(_("If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)")));
strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6, onion or i2p)"));
strUsage += HelpMessageOpt("-disableipv4", _("Disable Ipv4 network connections") + " " + _("(default: 0)"));
strUsage += HelpMessageOpt("-disableipv6", _("Disable Ipv6 network connections") + " " + _("(default: 0)"));
strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), 1));
strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with Bloom filters (default: %u)"), 1));
if (showDebug)
@ -1609,14 +1624,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net))
SetLimited(net);
SetReachable(net, false);
}
}
//fprintf(stderr,"%s tik19\n", __FUNCTION__);
if (mapArgs.count("-allowlist")) {
BOOST_FOREACH(const std::string& net, mapMultiArgs["-allowlist"]) {
CSubNet subnet(net);
CSubNet subnet;
LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid())
return InitError(strprintf(_("Invalid netmask specified in -allowlist: '%s'"), net));
CNode::AddAllowlistedRange(subnet);
@ -1627,9 +1643,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
std::string proxyArg = GetArg("-proxy", "");
SetLimited(NET_ONION);
SetReachable(NET_ONION,false);
if (proxyArg != "" && proxyArg != "0") {
proxyType addrProxy = proxyType(CService(proxyArg, 9050), proxyRandomize);
CService resolved(LookupNumeric(proxyArg.c_str(), 9050));
proxyType addrProxy = proxyType(resolved, proxyRandomize);
if (!addrProxy.IsValid())
return InitError(strprintf(_("Invalid -proxy address: '%s'"), proxyArg));
@ -1637,9 +1654,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_ONION, addrProxy);
SetNameProxy(addrProxy);
SetLimited(NET_ONION, false); // by default, -proxy sets onion as reachable, unless -noonion later
SetReachable(NET_ONION, true); // by default, -proxy sets onion as reachable, unless -noonion later
}
const std::string& i2psam_arg = GetArg("-i2psam", "");
if (!i2psam_arg.empty()) {
CService addr;
if (!Lookup(i2psam_arg.c_str(), addr, 7656, fNameLookup) || !addr.IsValid()) {
return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
}
SetReachable(NET_I2P, true);
SetProxy(NET_I2P, proxyType{addr});
} else {
SetReachable(NET_I2P, false);
}
//fprintf(stderr,"%s tik20\n", __FUNCTION__);
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
// -noonion (or -onion=0) disables connecting to .onion entirely
@ -1647,13 +1675,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
std::string onionArg = GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
SetLimited(NET_ONION); // set onions as unreachable
SetReachable(NET_ONION,false); // set onions as unreachable
} else {
proxyType addrOnion = proxyType(CService(onionArg, 9050), proxyRandomize);
CService resolved(LookupNumeric(onionArg.c_str(), 9050));
proxyType addrOnion = proxyType(resolved, proxyRandomize);
if (!addrOnion.IsValid())
return InitError(strprintf(_("Invalid -onion address: '%s'"), onionArg));
SetProxy(NET_ONION, addrOnion);
SetLimited(NET_ONION, false);
SetReachable(NET_ONION, true);
}
}
@ -1692,10 +1721,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (mapArgs.count("-externalip")) {
BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-externalip"]) {
CService addrLocal(strAddr, GetListenPort(), fNameLookup);
if (!addrLocal.IsValid())
CService addrLocal;
if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) {
AddLocal(addrLocal, LOCAL_MANUAL);
} else {
return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr));
AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
}
}
}

2
src/key_io.cpp

@ -9,7 +9,7 @@
#include <base58.h>
#include <bech32.h>
#include <script/script.h>
#include <utilstrencodings.h>
#include <util/strencodings.h>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>

189
src/main.cpp

@ -33,6 +33,7 @@
#include "metrics.h"
#include "notarizationdb.h"
#include "net.h"
#include "netmessagemaker.h"
#include "pow.h"
#include "script/interpreter.h"
#include "txdb.h"
@ -981,7 +982,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight)
if (!::IsStandard(txout.scriptPubKey, whichType))
{
reason = "scriptpubkey";
//fprintf(stderr,">>>>>>>>>>>>>>> vout.%d nDataout.%d\n",v,nDataOut);
//fprintf(stderr," vout.%d nDataout.%d\n",v,nDataOut);
return false;
}
@ -3770,7 +3771,7 @@ void static UpdateTip(CBlockIndex *pindexNew) {
mempool.AddTransactionsUpdated(1);
HUSH_NEWBLOCKS++;
double progress;
if ( SMART_CHAIN_SYMBOL[0] == 0 ) {
if ( ishush3 ) {
progress = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.LastTip());
} else {
int32_t longestchain = hush_longestchain();
@ -4330,6 +4331,9 @@ bool ActivateBestChain(bool fSkipdpow, CValidationState &state, CBlock *pblock)
do {
boost::this_thread::interruption_point();
if (ShutdownRequested())
break;
bool fInitialDownload;
{
LOCK(cs_main);
@ -6773,7 +6777,7 @@ void static ProcessGetData(CNode* pfrom)
//for (z=31; z>=0; z--)
// fprintf(stderr,"%02x",((uint8_t *)&hash)[z]);
//fprintf(stderr," send block %d\n",hush_block2height(&block));
pfrom->PushMessage("block", block);
pfrom->PushMessage(NetMsgType::BLOCK, block);
}
else // MSG_FILTERED_BLOCK)
{
@ -6781,7 +6785,7 @@ void static ProcessGetData(CNode* pfrom)
if (pfrom->pfilter)
{
CMerkleBlock merkleBlock(block, *pfrom->pfilter);
pfrom->PushMessage("merkleblock", merkleBlock);
pfrom->PushMessage(NetMsgType::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
// Note that there is currently no way for a node to request any single transactions we didn't send here -
@ -6791,7 +6795,7 @@ void static ProcessGetData(CNode* pfrom)
typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second)))
pfrom->PushMessage("tx", block.vtx[pair.first]);
pfrom->PushMessage(NetMsgType::TX, block.vtx[pair.first]);
}
// else
// no response
@ -6805,7 +6809,7 @@ void static ProcessGetData(CNode* pfrom)
// wait for other stuff first.
vector<CInv> vInv;
vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
pfrom->PushMessage("inv", vInv);
pfrom->PushMessage(NetMsgType::INV, vInv);
pfrom->hashContinue.SetNull();
}
}
@ -6828,7 +6832,7 @@ void static ProcessGetData(CNode* pfrom)
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << tx;
pfrom->PushMessage("tx", ss);
pfrom->PushMessage(NetMsgType::TX, ss);
pushed = true;
}
}
@ -6855,7 +6859,7 @@ void static ProcessGetData(CNode* pfrom)
// do that because they want to know about (and store and rebroadcast and
// risk analyze) the dependencies of transactions relevant to them, without
// having to download the entire memory pool.
pfrom->PushMessage("notfound", vNotFound);
pfrom->PushMessage(NetMsgType::NOTFOUND, vNotFound);
}
}
@ -6882,7 +6886,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if(p2pdebug)
fprintf(stderr,"%s: netmsg: %s from %s\n", __func__, strCommand.c_str(), pfrom->addr.ToString().c_str() );
if (strCommand == "version") {
if (strCommand == NetMsgType::VERSION) {
// Feeler connections exist only to verify if node is online
if (pfrom->fFeeler) {
assert(pfrom->fInbound == false);
@ -6894,7 +6898,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
Misbehaving(pfrom->GetId(), 1);
return false;
}
@ -6912,7 +6916,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
{
// disconnect from peers older than this proto version
LogPrintf("Disconnecting peer=%d at %s using obsolete version %i < %i\n", pfrom->id, pfrom->addr.ToString().c_str(), nVersion, minVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", minVersion));
pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", minVersion));
pfrom->fDisconnect = true;
return false;
}
@ -6923,7 +6927,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (nVersion < params.vUpgrades[currentEpoch].nProtocolVersion)
{
LogPrintf("Disconnecting peer=%d at %s using obsolete epoch version %i\n", pfrom->id, pfrom->addr.ToString().c_str(), nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater",
params.vUpgrades[currentEpoch].nProtocolVersion));
pfrom->fDisconnect = true;
@ -6967,6 +6971,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Potentially mark this peer as a preferred download peer.
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
//Ask for Address Format Version 2
pfrom->PushMessage(NetMsgType::SENDADDRV2);
// Change version
pfrom->PushMessage("verack");
pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
@ -6991,7 +6998,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Get recent addresses
if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000)
{
pfrom->PushMessage("getaddr");
pfrom->PushMessage(NetMsgType::GETADDR);
pfrom->fGetAddr = true;
}
addrman.Good(pfrom->addr);
@ -7022,7 +7029,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Must have a version message before anything else
Misbehaving(pfrom->GetId(), 1);
return false;
} else if ( strCommand == "events" ) {
} else if ( strCommand == NetMsgType::EVENTS ) {
if ( ASSETCHAINS_CCLIB != "gamescc" )
{
Misbehaving(pfrom->GetId(), 1);
@ -7032,7 +7039,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
vRecv >> payload;
hush_netevent(payload);
return(true);
} else if (strCommand == "verack") {
} else if (strCommand == NetMsgType::VERACK) {
pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
if ( HUSH_NSPV_SUPERLITE )
@ -7048,6 +7055,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (pfrom->fNetworkNode) {
LOCK(cs_main);
State(pfrom->GetId())->fCurrentlyConnected = true;
AddressCurrentlyConnected(State(pfrom->GetId())->address);
}
}
@ -7056,15 +7064,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// 2. Peer version is below the minimum version for the current epoch
else if (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion) {
LogPrintf("Disconnecting peer=%d at %s using obsolete version %i vs %d\n", pfrom->id, pfrom->addr.ToString().c_str(), pfrom->nVersion,(int32_t)chainparams.GetConsensus().vUpgrades[CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater",
chainparams.GetConsensus().vUpgrades[
CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion));
pfrom->fDisconnect = true;
return false;
} else if (strCommand == "addr") {
} else if (strCommand == NetMsgType::SENDADDRV2) {
pfrom->m_wants_addrv2 = true;
return true;
} else if (strCommand == NetMsgType::ADDR || strCommand == NetMsgType::ADDRV2) {
int stream_version = vRecv.GetVersion();
int tempStream_version = vRecv.GetVersion();
tempStream_version |= ADDRV2_FORMAT;
if (strCommand == NetMsgType::ADDRV2) {
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
// unserialize methods know that an address in v2 format is coming.
stream_version |= ADDRV2_FORMAT;
}
OverrideStream<CDataStream> s(&vRecv, vRecv.GetType(), stream_version);
vector<CAddress> vAddr;
vRecv >> vAddr;
s >> vAddr;
if(p2pdebug)
fprintf(stderr,"%s: processing add with vAddr.size=%lu\n", __func__, vAddr.size() );
@ -7074,7 +7096,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (vAddr.size() > 1000)
{
Misbehaving(pfrom->GetId(), 20);
return error("message addr size() = %u", vAddr.size());
return error("%s message size() = %u", strCommand, vAddr.size());
}
// Store the new addresses
@ -7133,7 +7155,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fGetAddr = false;
if (pfrom->fOneShot)
pfrom->fDisconnect = true;
} else if (strCommand == "ping") {
} else if (strCommand == NetMsgType::PING) {
if (pfrom->nVersion > BIP0031_VERSION)
{
uint64_t nonce = 0;
@ -7149,9 +7171,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// it, if the remote node sends a ping once per second and this node takes 5
// seconds to respond to each, the 5th ping the remote sends would appear to
// return very quickly.
pfrom->PushMessage("pong", nonce);
pfrom->PushMessage(NetMsgType::PONG, nonce);
}
} else if (strCommand == "pong") {
} else if (strCommand == NetMsgType::PONG) {
int64_t pingUsecEnd = nTimeReceived;
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
@ -7204,6 +7226,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
if (bPingFinished) {
pfrom->nPingNonceSent = 0;
pfrom->nPingRetry = 0;
}
}
// This asymmetric behavior for inbound and outbound connections was introduced
@ -7211,7 +7234,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// to users' AddrMan and later request them by sending getaddr messages.
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
else if ((strCommand == "getaddr") && (pfrom->fInbound))
else if ((strCommand == NetMsgType::GETADDR) && (pfrom->fInbound))
{
// Only send one GetAddr response per connection to reduce resource waste
// and discourage addr stamping of INV announcements.
@ -7222,10 +7245,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fSentAddr = true;
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
vector<CAddress> vAddr = addrman.GetAddr(pfrom->m_wants_addrv2);
BOOST_FOREACH(const CAddress &addr, vAddr)
pfrom->PushAddress(addr);
} else if (strCommand == "getnSPV") {
} else if (GetBoolArg("-nspv_msg", DEFAULT_NSPV_PROCESSING) &&
(strCommand == NetMsgType::GETNSPV)) {
if ( HUSH_NSPV == 0 )//&& HUSH_INSYNC != 0 )
{
std::vector<uint8_t> payload;
@ -7233,7 +7257,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
hush_nSPVreq(pfrom,payload);
}
return(true);
} else if (strCommand == "nSPV") {
} else if (GetBoolArg("-nspv_msg", DEFAULT_NSPV_PROCESSING) &&
(strCommand == NetMsgType::GETNSPV)) {
if ( HUSH_NSPV_SUPERLITE )
{
std::vector<uint8_t> payload;
@ -7241,10 +7266,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
hush_nSPVresp(pfrom,payload);
}
return(true);
}
else if ( HUSH_NSPV_SUPERLITE )
} else if ( HUSH_NSPV_SUPERLITE ) {
return(true);
else if (strCommand == "inv") {
} else if (strCommand == NetMsgType::INV) {
vector<CInv> vInv;
vRecv >> vInv;
if(p2pdebug)
@ -7283,7 +7307,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// time the block arrives, the header chain leading up to it is already validated. Not
// doing this will result in the received block being rejected as an orphan in case it is
// not a direct successor.
pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash);
pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash);
CNodeState *nodestate = State(pfrom->GetId());
if (chainActive.Tip()->GetBlockTime() > GetTime() - chainparams.GetConsensus().nPowTargetSpacing * 20 &&
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
@ -7306,8 +7330,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
if (!vToFetch.empty())
pfrom->PushMessage("getdata", vToFetch);
} else if (strCommand == "getdata") {
pfrom->PushMessage(NetMsgType::GETDATA, vToFetch);
} else if (strCommand == NetMsgType::GETDATA) {
vector<CInv> vInv;
vRecv >> vInv;
if(p2pdebug)
@ -7326,7 +7350,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom);
} else if (strCommand == "getblocks") {
} else if (strCommand == NetMsgType::GETBLOCKS) {
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
@ -7358,7 +7382,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
break;
}
}
} else if (strCommand == "getheaders") {
} else if (strCommand == NetMsgType::GETHEADERS) {
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
@ -7409,7 +7433,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
pfrom->PushMessage("headers", vHeaders);
pfrom->PushMessage(NetMsgType::HEADERS, vHeaders);
}
/*else if ( IS_HUSH_NOTARY != 0 )
{
@ -7417,7 +7441,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if ( counter++ < 3 )
fprintf(stderr,"you can ignore redundant getheaders from peer.%d %d prev.%d\n",(int32_t)pfrom->id,(int32_t)(pindex ? pindex->GetHeight() : -1),pfrom->lasthdrsreq);
}*/
} else if (strCommand == "tx") {
} else if (strCommand == NetMsgType::TX) {
if (IsInitialBlockDownload())
return true;
@ -7545,12 +7569,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(),
pfrom->id, pfrom->cleanSubVer,
state.GetRejectReason());
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
pfrom->PushMessage(NetMsgType::REJECT, strCommand, state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
}
} else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing
} else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
{
std::vector<CBlockHeader> headers;
@ -7608,12 +7632,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
{
pfrom->sendhdrsreq = (int32_t)pindexLast->GetHeight();
LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->GetHeight(), pfrom->id, pfrom->nStartingHeight);
pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256());
pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256());
}
}
CheckBlockIndex();
} else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing
} else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
{
CBlock block;
vRecv >> block;
@ -7632,7 +7656,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
ProcessNewBlock(0,0,state, pfrom, &block, forceProcessing, NULL);
int nDoS;
if (state.IsInvalid(nDoS)) {
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
pfrom->PushMessage(NetMsgType::REJECT, strCommand, state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0) {
LOCK(cs_main);
@ -7640,7 +7664,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
} else if (strCommand == "mempool") {
} else if (strCommand == NetMsgType::MEMPOOL) {
LOCK2(cs_main, pfrom->cs_filter);
//LogPrintf("%s: mempool request from %s",__func__, pfrom->addr.ToString().c_str());
// TODO: limit mempool requests per time and per peer
@ -7658,18 +7682,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
pfrom->PushMessage("inv", vInv);
pfrom->PushMessage(NetMsgType::INV, vInv);
vInv.clear();
}
}
if (vInv.size() > 0)
pfrom->PushMessage("inv", vInv);
} else if (strCommand == "alert") {
// Do not process alert p2p messages
// Misbehaving(pfrom->GetId(), 10);
pfrom->PushMessage(NetMsgType::INV, vInv);
} else if (strCommand == NetMsgType::ALERT) {
// Small DoS penalty so peers that send us lots of
// duplicate/expired/invalid-signature/whatever alerts
// eventually get banned.
Misbehaving(pfrom->GetId(), 10);
} else if (!(nLocalServices & NODE_BLOOM) &&
(strCommand == "filterload" ||
strCommand == "filteradd")) {
(strCommand == NetMsgType::FILTERLOAD ||
strCommand == NetMsgType::FILTERADD)) {
if (pfrom->nVersion >= NO_BLOOM_VERSION) {
Misbehaving(pfrom->GetId(), 100);
return false;
@ -7677,7 +7703,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fDisconnect = true;
return false;
}
} else if (strCommand == "filterload") {
} else if (strCommand == NetMsgType::FILTERLOAD) {
CBloomFilter filter;
vRecv >> filter;
@ -7692,7 +7718,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->pfilter->UpdateEmptyFull();
}
pfrom->fRelayTxes = true;
} else if (strCommand == "filteradd") {
} else if (strCommand == NetMsgType::FILTERADD) {
vector<unsigned char> vData;
vRecv >> vData;
@ -7708,14 +7734,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
else
Misbehaving(pfrom->GetId(), 100);
}
} else if (strCommand == "filterclear") {
} else if (strCommand == NetMsgType::FILTERCLEAR) {
LOCK(pfrom->cs_filter);
if (nLocalServices & NODE_BLOOM) {
delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter();
}
pfrom->fRelayTxes = true;
} else if (strCommand == "reject") {
} else if (strCommand == NetMsgType::REJECT) {
if (fDebug) {
try {
string strMsg; unsigned char ccode; string strReason;
@ -7736,7 +7762,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("net", "Unparseable reject message received\n");
}
}
} else if (strCommand == "notfound") {
} else if (strCommand == NetMsgType::NOTFOUND) {
// We do not care about the NOTFOUND message, but logging an Unknown Command
// message would be undesirable as we transmit it ourselves.
} else {
@ -7829,7 +7855,7 @@ bool ProcessMessages(CNode* pfrom)
}
catch (const std::ios_base::failure& e)
{
pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message"));
pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message"));
if (strstr(e.what(), "end of data"))
{
// Allow exceptions from under-length message on vRecv
@ -7881,9 +7907,12 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
// RPC ping request by user
pingSend = true;
}
if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
if (pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
// Ping automatically sent as a latency probe & keepalive.
pingSend = true;
if (pto->nPingNonceSent != 0) {
pto->nPingRetry++;
}
}
if (pingSend) {
uint64_t nonce = 0;
@ -7894,12 +7923,12 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->nPingUsecStart = GetTimeMicros();
if (pto->nVersion > BIP0031_VERSION) {
pto->nPingNonceSent = nonce;
pto->PushMessage("ping", nonce);
} else {
pto->PushMessage(NetMsgType::PING, nonce);
} // else {
// Peer is too old to support ping command with nonce, pong will never arrive.
pto->nPingNonceSent = 0;
pto->PushMessage("ping");
}
// pto->nPingNonceSent = 0;
// pto->PushMessage("ping");
// }
}
TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
@ -7935,17 +7964,29 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
{
pto->addrKnown.insert(addr.GetKey());
vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000)
if (vAddr.size() >= MAX_ADDR_TO_SEND)
{
pto->PushMessage("addr", vAddr);
vAddr.clear();
// Should be impossible since we always check size before adding to
// vAddrToSend. Recover by trimming the vector.
vAddr.resize(MAX_ADDR_TO_SEND);
}
const char* msg_type;
int make_flags;
if (pto->m_wants_addrv2) {
msg_type = NetMsgType::ADDRV2;
make_flags = ADDRV2_FORMAT;
} else {
msg_type = NetMsgType::ADDR;
make_flags = 0;
}
pto->PushAddrMessage(CNetMsgMaker(std::min(pto->nVersion, PROTOCOL_VERSION)).Make(make_flags, msg_type, pto->vAddrToSend));
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
pto->PushMessage("addr", vAddr);
vAddr.clear();
}
CNodeState &state = *State(pto->GetId());
@ -7958,7 +7999,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
else
{
CNode::Ban(pto->addr);
CNode::Ban(pto->addr, BanReasonNodeMisbehaving);
}
}
state.fShouldBan = false;
@ -7969,7 +8010,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
return(true);
}
BOOST_FOREACH(const CBlockReject& reject, state.rejects)
pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock);
pto->PushMessage(NetMsgType::REJECT, (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock);
state.rejects.clear();
// Start block sync
@ -7983,7 +8024,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
nSyncStarted++;
CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader;
LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->GetHeight(), pto->id, pto->nStartingHeight);
pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256());
pto->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256());
}
}
@ -8031,7 +8072,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
vInv.push_back(inv);
if (vInv.size() >= 1000)
{
pto->PushMessage("inv", vInv);
pto->PushMessage(NetMsgType::INV, vInv);
vInv.clear();
}
}
@ -8039,7 +8080,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->vInventoryToSend = vInvWait;
}
if (!vInv.empty())
pto->PushMessage("inv", vInv);
pto->PushMessage(NetMsgType::INV, vInv);
// Detect whether we're stalling
int64_t nNow = GetTimeMicros();
@ -8104,7 +8145,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
pto->PushMessage("getdata", vGetData);
pto->PushMessage(NetMsgType::GETDATA, vGetData);
vGetData.clear();
}
} else {
@ -8114,7 +8155,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vGetData.empty())
pto->PushMessage("getdata", vGetData);
pto->PushMessage(NetMsgType::GETDATA, vGetData);
}
return true;

3
src/main.h

@ -114,6 +114,9 @@ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
// NSPV enabled?
static const bool DEFAULT_NSPV_PROCESSING = false;
//static const bool DEFAULT_ADDRESSINDEX = false;
//static const bool DEFAULT_SPENTINDEX = false;
#define DEFAULT_ADDRESSINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0)

2
src/merkleblock.cpp

@ -23,7 +23,7 @@
#include "hash.h"
#include "consensus/consensus.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "hush_defs.h"
using namespace std;

2
src/metrics.cpp

@ -24,7 +24,7 @@
#include "util.h"
#include "utiltime.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <boost/thread.hpp>
#include <boost/thread/synchronized_value.hpp>
#include <string>

683
src/net.cpp

File diff suppressed because it is too large

140
src/net.h

@ -20,9 +20,11 @@
#ifndef HUSH_NET_H
#define HUSH_NET_H
#include "addrdb.h"
#include "bloom.h"
#include "compat.h"
#include "hash.h"
#include "i2p.h"
#include "limitedmap.h"
#include "mruset.h"
#include "netbase.h"
@ -31,7 +33,7 @@
#include "streams.h"
#include "sync.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "util.h"
#include <deque>
#include <stdint.h>
@ -62,6 +64,8 @@ namespace boost {
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
static const int PING_INTERVAL = 2 * 60;
/** Retry Time between pings automatically sent out for latency probing and keepalive (in seconds). */
static const int MAX_PING_RETRY = 20;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
static const int TIMEOUT_INTERVAL = 20 * 60;
/** The maximum number of entries in an 'inv' protocol message */
@ -84,6 +88,7 @@ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 384;
static const int NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD = 24 * 24 * 3;
/** Run the feeler connection loop once every 120 seconds. **/
static const int FEELER_INTERVAL = 120;
extern std::atomic<bool> fNetworkActive;
unsigned int ReceiveFloodSize();
unsigned int SendBufferSize();
@ -99,6 +104,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fAllowlisted = false);
void LoadPeers();
void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler);
bool StopNode();
void SocketSendData(CNode *pnode);
@ -108,11 +114,56 @@ X509 *generate_x509(EVP_PKEY *pkey);
bool write_to_disk(EVP_PKEY *pkey, X509 *x509);
void configure_context(SSL_CTX *ctx, bool server_side);
// OpenSSL related variables for metrics.cpp
static std::string routingsecrecy;
static std::string cipherdescription;
static std::string securitylevel;
static std::string validationdescription;
void GetBanned(banmap_t &banmap);
void SetBanned(const banmap_t &banmap);
//!check is the banlist has unwritten changes
bool BannedSetIsDirty();
//!set the "dirty" flag for the banlist
void SetBannedSetDirty(bool dirty=true);
//!clean unused entries (if bantime has expired)
void SweepBanned();
void CreateNodeFromAcceptedSocket(SOCKET hSocket,
bool whitelisted,
const CAddress& addr_bind,
const CAddress& addr);
typedef int NodeId;
enum NumConnections {
CONNECTIONS_NONE = 0,
CONNECTIONS_IN = (1U << 0),
CONNECTIONS_OUT = (1U << 1),
CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT),
};
size_t GetNodeCount(NumConnections num);
bool GetNetworkActive();
void SetNetworkActive(bool active);
class CNodeStats;
void CopyNodeStats(std::vector<CNodeStats>& vstats);
struct CSerializedNetMsg
{
CSerializedNetMsg() = default;
CSerializedNetMsg(CSerializedNetMsg&&) = default;
CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default;
// No copying, only moves.
CSerializedNetMsg(const CSerializedNetMsg& msg) = delete;
CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete;
std::vector<unsigned char> data;
std::string m_type;
};
struct CombinerAll
{
typedef bool result_type;
@ -155,17 +206,21 @@ enum
bool IsPeerAddrLocalGood(CNode *pnode);
void AdvertizeLocal(CNode *pnode);
void SetLimited(enum Network net, bool fLimited = true);
bool IsLimited(enum Network net);
bool IsLimited(const CNetAddr& addr);
bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
bool RemoveLocal(const CService& addr);
bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
/**
* Mark a network as reachable or unreachable (no automatic connects to it)
* @note Networks are reachable by default
*/
void SetReachable(enum Network net, bool reachable);
/** @returns true if the network is reachable, false otherwise */
bool IsReachable(enum Network net);
bool IsReachable(const CNetAddr &addr);
/** @returns true if the address is in a reachable network, false otherwise */
bool IsReachable(const CNetAddr& addr);
CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
@ -190,12 +245,14 @@ extern CCriticalSection cs_vAddedNodes;
extern NodeId nLastNodeId;
extern CCriticalSection cs_nLastNodeId;
/** Subversion as sent to the P2P network in `version` messages */
extern std::string strSubVersion;
extern SSL_CTX *tls_ctx_server;
extern SSL_CTX *tls_ctx_client;
extern std::unique_ptr<i2p::sam::Session> m_i2p_sam_session;
/** Subversion as sent to the P2P network in `version` messages */
extern std::string strSubVersion;
struct LocalServiceInfo {
int nScore;
int nPort;
@ -204,6 +261,8 @@ struct LocalServiceInfo {
extern CCriticalSection cs_mapLocalHost;
extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
class CNodeStats
{
public:
@ -228,12 +287,19 @@ public:
bool fFeeler; // If true this node is being used as a short lived feeler.
double dPingTime;
double dPingWait;
double dMinPing;
std::string addrLocal;
// Address of this peer
CAddress addr;
// Bind address of our side of the connection
// CAddress addrBind; // https://github.com/bitcoin/bitcoin/commit/a7e3c2814c8e49197889a4679461be42254e5c51
uint32_t m_mapped_as; // Mapped ASN for this address
uint32_t m_mapped_as;
/**
* Whether the peer has signaled support for receiving ADDRv2 (BIP155)
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
*/
bool m_wants_addrv2;
};
@ -277,6 +343,22 @@ public:
int readData(const char *pch, unsigned int nBytes);
};
/** The TransportSerializer prepares messages for the network transport
*/
class TransportSerializer {
public:
// prepare message for transport (header construction, error-correction computation, payload encryption, etc.)
virtual void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) = 0;
virtual ~TransportSerializer() {}
};
class V1TransportSerializer : public TransportSerializer {
public:
void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) override;
};
/** Information about a peer */
class CNode
{
@ -284,6 +366,8 @@ public:
// TLS via WolfSSL
SSL *ssl;
std::string tls_cipher;
//Message Transport Serializer
std::unique_ptr<TransportSerializer> m_serializer;
// socket
uint64_t nServices;
@ -342,12 +426,19 @@ public:
CBloomFilter* pfilter;
int nRefCount;
NodeId id;
/**
* Whether the peer has signaled support for receiving ADDRv2 (BIP155)
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
*/
bool m_wants_addrv2{false};
protected:
// Denial-of-service detection/prevention
// Key is IP address, value is banned-until-time
static std::map<CSubNet, int64_t> setBanned;
static CCriticalSection cs_setBanned;
// static std::map<CSubNet, int64_t> setBanned;
// static CCriticalSection cs_setBanned;
// Allowlisted ranges. Any node connecting from these is automatically
// allowlisted (as well as those connecting to allowlisted binds).
@ -393,6 +484,8 @@ public:
int64_t nMinPingUsecTime;
// Whether a ping is requested.
bool fPingQueued;
// Times has ping been retried
int64_t nPingRetry;
CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false, SSL *sslIn = NULL);
~CNode();
@ -407,6 +500,8 @@ private:
CNode(const CNode&);
void operator=(const CNode&);
mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend);
public:
NodeId GetId() const {
@ -452,21 +547,26 @@ public:
void AddAddressKnown(const CAddress& addr)
void AddAddressKnown(const CAddress& _addr)
{
addrKnown.insert(addr.GetKey());
addrKnown.insert(_addr.GetKey());
}
void PushAddress(const CAddress& addr)
void PushAddress(const CAddress& _addr)
{
// Whether the peer supports the address in `_addr`. For example,
// nodes that do not implement BIP155 cannot receive Tor v3 addresses
// because they require ADDRv2 (BIP155) encoding.
const bool addr_format_supported = m_wants_addrv2 || _addr.IsAddrV1Compatible();
// Known checking here is only to save space from duplicates.
// SendMessages will filter it again for knowns that were added
// after addresses were pushed.
if (addr.IsValid() && !addrKnown.contains(addr.GetKey())) {
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey()) && addr_format_supported) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand() % vAddrToSend.size()] = addr;
vAddrToSend[insecure_rand() % vAddrToSend.size()] = _addr;
} else {
vAddrToSend.push_back(addr);
vAddrToSend.push_back(_addr);
}
}
}
@ -500,6 +600,8 @@ public:
// TODO: Document the precondition of this function. Is cs_vSend locked?
void EndMessage() UNLOCK_FUNCTION(cs_vSend);
void PushAddrMessage(CSerializedNetMsg&& msg);
void PushVersion();
@ -682,8 +784,8 @@ public:
static void ClearBanned(); // needed for unit testing
static bool IsBanned(CNetAddr ip);
static bool IsBanned(CSubNet subnet);
static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static void Ban(const CNetAddr &ip, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static void Ban(const CSubNet &subNet, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static bool Unban(const CNetAddr &ip);
static bool Unban(const CSubNet &ip);
static void GetBanned(std::map<CSubNet, int64_t> &banmap);

1222
src/netaddress.cpp

File diff suppressed because it is too large

534
src/netaddress.h

@ -0,0 +1,534 @@
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef HUSH_NETADDRESS_H
#define HUSH_NETADDRESS_H
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "attributes.h"
#include "compat.h"
#include "prevector.h"
#include "serialize.h"
#include "tinyformat.h"
#include "util/strencodings.h"
#include "util/string.h"
#include <array>
#include <cstdint>
#include <ios>
#include <string>
#include <vector>
/**
* A flag that is ORed into the protocol version to designate that addresses
* should be serialized in (unserialized from) v2 format (BIP155).
* Make sure that this does not collide with any of the values in `version.h`
* or with `SERIALIZE_TRANSACTION_NO_WITNESS`.
*/
static const int ADDRV2_FORMAT = 0x20000000;
/**
* A network type.
* @note An address may belong to more than one network, for example `10.0.0.1`
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`.
* Keep these sequential starting from 0 and `NET_MAX` as the last entry.
* We have loops like `for (int i = 0; i < NET_MAX; i++)` that expect to iterate
* over all enum values and also `GetExtNetwork()` "extends" this enum by
* introducing standalone constants starting from `NET_MAX`.
*/
enum Network
{
/// Addresses from these networks are not publicly routable on the global Internet.
NET_UNROUTABLE = 0,
/// IPv4
NET_IPV4,
/// IPv6
NET_IPV6,
/// TOR (v2 or v3)
NET_ONION,
/// I2P
NET_I2P,
/// CJDNS
NET_CJDNS,
/// A set of addresses that represent the hash of a string or FQDN. We use
/// them in CAddrMan to keep track of which DNS seeds were used.
NET_INTERNAL,
/// Dummy value to indicate the number of NET_* constants.
NET_MAX,
};
/// Prefix of an IPv6 address when it contains an embedded IPv4 address.
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
};
/// Prefix of an IPv6 address when it contains an embedded TORv2 address.
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{
0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43
};
/// Prefix of an IPv6 address when it contains an embedded "internal" address.
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
/// The prefix comes from 0xFD + SHA256("bitcoin")[0:5].
/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{
0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
};
/// Size of IPv4 address (in bytes).
static constexpr uint64_t ADDR_IPV4_SIZE = 4;
/// Size of IPv6 address (in bytes).
static constexpr uint64_t ADDR_IPV6_SIZE = 16;
/// Size of TORv2 address (in bytes).
static constexpr uint64_t ADDR_TORV2_SIZE = 10;
/// Size of TORv3 address (in bytes). This is the length of just the address
/// as used in BIP155, without the checksum and the version byte.
static constexpr uint64_t ADDR_TORV3_SIZE = 32;
/// Size of I2P address (in bytes).
static constexpr uint64_t ADDR_I2P_SIZE = 32;
/// Size of CJDNS address (in bytes).
static constexpr uint64_t ADDR_CJDNS_SIZE = 16;
/// Size of "internal" (NET_INTERNAL) address (in bytes).
static constexpr uint64_t ADDR_INTERNAL_SIZE = 10;
/**
* Network address.
*/
class CNetAddr
{
protected:
/**
* Raw representation of the network address.
* In network byte order (big endian) for IPv4 and IPv6.
*/
prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0};
/**
* Network to which this address belongs.
*/
Network m_net{NET_IPV6};
uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
public:
CNetAddr();
explicit CNetAddr(const struct in_addr& ipv4Addr);
void SetIP(const CNetAddr& ip);
private:
/**
* Set from a legacy IPv6 address.
* Legacy IPv6 address may be a normal IPv6 address, or another address
* (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
* `addr` encoding.
*/
void SetLegacyIPv6(Span<const uint8_t> ipv6);
public:
/** check whether a given address is in a network we can probably connect to */
bool IsReachableNetwork();
bool SetInternal(const std::string& name);
bool SetSpecial(const std::string &strName); // for Tor addresses
bool IsBindAny() const; // INADDR_ANY equivalent
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
bool IsRFC2544() const; // IPv4 inter-network communcations (192.18.0.0/15)
bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10)
bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
bool IsRFC4843() const; // IPv6 ORCHID (deprecated) (2001:10::/28)
bool IsRFC7343() const; // IPv6 ORCHIDv2 (2001:20::/28)
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
bool IsTor() const;
bool IsI2P() const;
bool IsCJDNS() const;
bool IsLocal() const;
bool IsRoutable() const;
bool IsInternal() const;
bool IsValid() const;
/**
* Check if the current object can be serialized in pre-ADDRv2/BIP155 format.
*/
bool IsAddrV1Compatible() const;
enum Network GetNetwork() const;
std::string ToString() const;
std::string ToStringIP() const;
uint64_t GetHash() const;
bool GetInAddr(struct in_addr* pipv4Addr) const;
uint32_t GetNetClass() const;
//! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
uint32_t GetLinkedIPv4() const;
//! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
bool HasLinkedIPv4() const;
// The AS on the BGP path to the node we use to diversify
// peers in AddrMan bucketing based on the AS infrastructure.
// The ip->AS mapping depends on how asmap is constructed.
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetAddrBytes() const;
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
friend bool operator==(const CNetAddr& a, const CNetAddr& b);
friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
friend bool operator<(const CNetAddr& a, const CNetAddr& b);
/**
* Serialize to a stream.
*/
template <typename Stream>
void Serialize(Stream& s) const
{
if (s.GetVersion() & ADDRV2_FORMAT) {
SerializeV2Stream(s);
} else {
SerializeV1Stream(s);
}
}
/**
* Unserialize from a stream.
*/
template <typename Stream>
void Unserialize(Stream& s)
{
if (s.GetVersion() & ADDRV2_FORMAT) {
UnserializeV2Stream(s);
} else {
UnserializeV1Stream(s);
}
}
friend class CSubNet;
private:
/**
* Parse a Tor address and set this object to it.
* @param[in] addr Address to parse, must be a valid C string, for example
* pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.
* @returns Whether the operation was successful.
* @see CNetAddr::IsTor()
*/
bool SetTor(const std::string& addr);
/**
* Parse an I2P address and set this object to it.
* @param[in] addr Address to parse, must be a valid C string, for example
* ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
* @returns Whether the operation was successful.
* @see CNetAddr::IsI2P()
*/
bool SetI2P(const std::string& addr);
/**
* BIP155 network ids recognized by this software.
*/
enum BIP155Network : uint8_t {
IPV4 = 1,
IPV6 = 2,
TORV2 = 3,
TORV3 = 4,
I2P = 5,
CJDNS = 6,
};
/**
* Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
*/
static constexpr uint64_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
/**
* Maximum size of an address as defined in BIP155 (in bytes).
* This is only the size of the address, not the entire CNetAddr object
* when serialized.
*/
static constexpr uint64_t MAX_ADDRV2_SIZE = 512;
/**
* Get the BIP155 network id of this address.
* Must not be called for IsInternal() objects.
* @returns BIP155 network id
*/
BIP155Network GetBIP155Network() const;
/**
* Set `m_net` from the provided BIP155 network id and size after validation.
* @retval true the network was recognized, is valid and `m_net` was set
* @retval false not recognised (from future?) and should be silently ignored
* @throws std::ios_base::failure if the network is one of the BIP155 founding
* networks (id 1..6) with wrong address size.
*/
bool SetNetFromBIP155Network(uint8_t possible_bip155_net, uint64_t address_size);
/**
* Serialize in pre-ADDRv2/BIP155 format to an array.
*/
void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
{
uint64_t prefix_size;
switch (m_net) {
case NET_IPV6:
assert(m_addr.size() == sizeof(arr));
memcpy(arr, m_addr.data(), m_addr.size());
return;
case NET_IPV4:
prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
case NET_ONION:
if (m_addr.size() == ADDR_TORV3_SIZE) {
break;
}
prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
case NET_INTERNAL:
prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
case NET_I2P:
break;
case NET_CJDNS:
break;
case NET_UNROUTABLE:
case NET_MAX:
assert(false);
} // no default case, so the compiler can warn about missing cases
// Serialize TORv3, I2P and CJDNS as all-zeros.
memset(arr, 0x0, V1_SERIALIZATION_SIZE);
}
/**
* Serialize in pre-ADDRv2/BIP155 format to a stream.
*/
template <typename Stream>
void SerializeV1Stream(Stream& s) const
{
uint8_t serialized[V1_SERIALIZATION_SIZE];
SerializeV1Array(serialized);
s << serialized;
}
/**
* Serialize as ADDRv2 / BIP155.
*/
template <typename Stream>
void SerializeV2Stream(Stream& s) const
{
if (IsInternal()) {
// Serialize NET_INTERNAL as embedded in IPv6. We need to
// serialize such addresses from addrman.
s << static_cast<uint8_t>(BIP155Network::IPV6);
s << COMPACTSIZE(ADDR_IPV6_SIZE);
SerializeV1Stream(s);
return;
}
s << static_cast<uint8_t>(GetBIP155Network());
s << m_addr;
}
/**
* Unserialize from a pre-ADDRv2/BIP155 format from an array.
*/
void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
{
// Use SetLegacyIPv6() so that m_net is set correctly. For example
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
SetLegacyIPv6(arr);
}
/**
* Unserialize from a pre-ADDRv2/BIP155 format from a stream.
*/
template <typename Stream>
void UnserializeV1Stream(Stream& s)
{
uint8_t serialized[V1_SERIALIZATION_SIZE];
s >> serialized;
UnserializeV1Array(serialized);
}
/**
* Unserialize from a ADDRv2 / BIP155 format.
*/
template <typename Stream>
void UnserializeV2Stream(Stream& s)
{
uint8_t bip155_net;
s >> bip155_net;
uint64_t address_size;
s >> COMPACTSIZE(address_size);
if (address_size > MAX_ADDRV2_SIZE) {
throw std::ios_base::failure(strprintf(
"Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE));
}
scopeId = 0;
if (SetNetFromBIP155Network(bip155_net, address_size)) {
m_addr.resize(address_size);
s >> MakeSpan(m_addr);
if (m_net != NET_IPV6) {
return;
}
// Do some special checks on IPv6 addresses.
// Recognize NET_INTERNAL embedded in IPv6, such addresses are not
// gossiped but could be coming from addrman, when unserializing from
// disk.
if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
m_net = NET_INTERNAL;
memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
ADDR_INTERNAL_SIZE);
m_addr.resize(ADDR_INTERNAL_SIZE);
return;
}
if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) &&
!HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) {
return;
}
// IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
// encoding). Unserialize as !IsValid(), thus ignoring them.
} else {
// If we receive an unknown BIP155 network id (from the future?) then
// ignore the address - unserialize as !IsValid().
s.ignore(address_size);
}
// Mimic a default-constructed CNetAddr object which is !IsValid() and thus
// will not be gossiped, but continue reading next addresses from the stream.
m_net = NET_IPV6;
m_addr.assign(ADDR_IPV6_SIZE, 0x0);
}
};
class CSubNet
{
protected:
/// Network (base) address
CNetAddr network;
/// Netmask, in network byte order
uint8_t netmask[16];
/// Is this value valid? (only used to signal parse errors)
bool valid;
public:
CSubNet();
CSubNet(const CNetAddr& addr, uint8_t mask);
CSubNet(const CNetAddr& addr, const CNetAddr& mask);
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
explicit CSubNet(const CNetAddr& addr);
bool Match(const CNetAddr &addr) const;
std::string ToString() const;
bool IsValid() const;
friend bool operator==(const CSubNet& a, const CSubNet& b);
friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); }
friend bool operator<(const CSubNet& a, const CSubNet& b);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(network);
READWRITE(FLATDATA(netmask));
READWRITE(FLATDATA(valid));
}
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
class CService : public CNetAddr
{
protected:
unsigned short port; // host order
public:
CService();
CService(const CNetAddr& ip, unsigned short port);
CService(const struct in_addr& ipv4Addr, unsigned short port);
explicit CService(const struct sockaddr_in& addr);
unsigned short GetPort() const;
bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
bool SetSockAddr(const struct sockaddr* paddr);
friend bool operator==(const CService& a, const CService& b);
friend bool operator!=(const CService& a, const CService& b) { return !(a == b); }
friend bool operator<(const CService& a, const CService& b);
std::vector<unsigned char> GetKey() const;
std::string ToString() const;
std::string ToStringPort() const;
std::string ToStringIPPort() const;
CService(const struct in6_addr& ipv6Addr, unsigned short port);
CService(const struct sockaddr_in6& addr);
SERIALIZE_METHODS(CService, obj)
{
READWRITEAS(CNetAddr, obj);
READ_WRITE(Using<BigEndianFormatter<2>>(obj.port));
}
};
bool SanityCheckASMap(const std::vector<bool>& asmap);
#endif // HUSH_NETADDRESS_H

1212
src/netbase.cpp

File diff suppressed because it is too large

245
src/netbase.h

@ -1,23 +1,8 @@
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Copyright (c) 2009-2013 The Bitcoin Core developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
#ifndef HUSH_NETBASE_H
#define HUSH_NETBASE_H
@ -26,8 +11,9 @@
#endif
#include "compat.h"
#include "netaddress.h"
#include "serialize.h"
#include "util/asmap.h"
#include "util/sock.h"
#include <stdint.h>
#include <string>
@ -36,177 +22,16 @@
extern int nConnectTimeout;
extern bool fNameLookup;
/** -timeout default */
static const int DEFAULT_CONNECT_TIMEOUT = 5000;
#ifdef _WIN32
// In MSVC, this is defined as a macro, undefine it to prevent a compile and link error
#undef SetPort
#endif
enum Network
{
NET_UNROUTABLE = 0,
NET_IPV4,
NET_IPV6,
NET_ONION,
NET_INTERNAL,
NET_MAX,
};
/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */
class CNetAddr
{
protected:
unsigned char ip[16]; // in network byte order
public:
CNetAddr();
CNetAddr(const struct in_addr& ipv4Addr);
explicit CNetAddr(const char *pszIp, bool fAllowLookup = false);
explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false);
void Init();
void SetIP(const CNetAddr& ip);
/**
* Set raw IPv4 or IPv6 address (in network byte order)
* @note Only NET_IPV4 and NET_IPV6 are allowed for network.
*/
void SetRaw(Network network, const uint8_t *data);
bool SetSpecial(const std::string &strName); // for Tor addresses
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
bool IsRFC2544() const; // IPv4 inter-network communications (192.18.0.0/15)
bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10)
bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28)
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
bool IsTor() const;
bool IsLocal() const;
bool IsRoutable() const;
bool IsInternal() const;
bool IsValid() const;
bool IsMulticast() const;
enum Network GetNetwork() const;
std::string ToString() const;
std::string ToStringIP() const;
unsigned int GetByte(int n) const;
uint64_t GetHash() const;
bool GetInAddr(struct in_addr* pipv4Addr) const;
uint32_t GetNetClass() const;
//! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
uint32_t GetLinkedIPv4() const;
//! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
bool HasLinkedIPv4() const;
// The AS on the BGP path to the node we use to diversify
// peers in AddrMan bucketing based on the AS infrastructure.
// The ip->AS mapping depends on how asmap is constructed.
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const;
CNetAddr(const struct in6_addr& pipv6Addr);
bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
friend bool operator==(const CNetAddr& a, const CNetAddr& b);
friend bool operator!=(const CNetAddr& a, const CNetAddr& b);
friend bool operator<(const CNetAddr& a, const CNetAddr& b);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(FLATDATA(ip));
}
friend class CSubNet;
};
class CSubNet
{
protected:
/// Network (base) address
CNetAddr network;
/// Netmask, in network byte order
uint8_t netmask[16];
/// Is this value valid? (only used to signal parse errors)
bool valid;
public:
CSubNet();
explicit CSubNet(const std::string &strSubnet, bool fAllowLookup = false);
bool Match(const CNetAddr &addr) const;
std::string ToString() const;
bool IsValid() const;
friend bool operator==(const CSubNet& a, const CSubNet& b);
friend bool operator!=(const CSubNet& a, const CSubNet& b);
friend bool operator<(const CSubNet& a, const CSubNet& b);
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
class CService : public CNetAddr
{
protected:
unsigned short port; // host order
public:
CService();
CService(const CNetAddr& ip, unsigned short port);
CService(const struct in_addr& ipv4Addr, unsigned short port);
CService(const struct sockaddr_in& addr);
explicit CService(const char *pszIpPort, int portDefault, bool fAllowLookup = false);
explicit CService(const char *pszIpPort, bool fAllowLookup = false);
explicit CService(const std::string& strIpPort, int portDefault, bool fAllowLookup = false);
explicit CService(const std::string& strIpPort, bool fAllowLookup = false);
void Init();
void SetPort(unsigned short portIn);
unsigned short GetPort() const;
bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
bool SetSockAddr(const struct sockaddr* paddr);
friend bool operator==(const CService& a, const CService& b);
friend bool operator!=(const CService& a, const CService& b);
friend bool operator<(const CService& a, const CService& b);
std::vector<unsigned char> GetKey() const;
std::string ToString() const;
std::string ToStringPort() const;
std::string ToStringIPPort() const;
CService(const struct in6_addr& ipv6Addr, unsigned short port);
CService(const struct sockaddr_in6& addr);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(FLATDATA(ip));
unsigned short portN = htons(port);
READWRITE(FLATDATA(portN));
if (ser_action.ForRead())
port = ntohs(portN);
}
};
//! -timeout default
static const int DEFAULT_CONNECT_TIMEOUT = 60000;
//! -dns default
static const int DEFAULT_NAME_LOOKUP = true;
class proxyType
{
public:
proxyType(): randomize_credentials(false) {}
proxyType(const CService &proxy, bool randomize_credentials=false): proxy(proxy), randomize_credentials(randomize_credentials) {}
proxyType(const CService &_proxy, bool _randomize_credentials=false): proxy(_proxy), randomize_credentials(_randomize_credentials) {}
bool IsValid() const { return proxy.IsValid(); }
@ -216,29 +41,51 @@ public:
enum Network ParseNetwork(std::string net);
std::string GetNetworkName(enum Network net);
void SplitHostPort(std::string in, int &portOut, std::string &hostOut);
bool SetProxy(enum Network net, const proxyType &addrProxy);
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
bool SetNameProxy(const proxyType &addrProxy);
bool HaveNameProxy();
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true);
bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true);
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0);
bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0);
bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = 0);
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = 0);
/** Return readable error string for a network error code */
std::string NetworkErrorString(int err);
/** Close socket and set hSocket to INVALID_SOCKET */
bool CloseSocket(SOCKET& hSocket);
/** Disable or enable blocking-mode for a socket */
bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking);
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup);
bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup);
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
CService LookupNumeric(const char *pszName, int portDefault = 0);
bool LookupSubNet(const char *pszName, CSubNet& subnet);
/**
* Create a TCP socket in the given address family.
* @param[in] address_family The socket is created in the same address family as this address.
* @return pointer to the created Sock object or unique_ptr that owns nothing in case of failure
*/
std::unique_ptr<Sock> CreateSockTCP(const CService& address_family);
/**
* Socket factory. Defaults to `CreateSockTCP()`, but can be overridden by unit tests.
*/
extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock;
/**
* Convert milliseconds to a struct timeval for e.g. select.
* Try to connect to the specified service on the specified socket.
*
* @param addrConnect The service to which to connect.
* @param sock The socket on which to connect.
* @param nTimeout Wait this many milliseconds for the connection to be
* established.
* @param manual_connection Whether or not the connection was manually requested
* (e.g. through the addnode RPC)
*
* @returns Whether or not a connection was successfully made.
*/
struct timeval MillisToTimeval(int64_t nTimeout);
bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout);
bool SanityCheckASMap(const std::vector<bool>& asmap);
bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = 0);
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = 0);
bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking);
/** Set the TCP_NODELAY flag on a socket */
bool SetSocketNoDelay(const SOCKET& hSocket);
#endif // HUSH_NETBASE_H

37
src/netmessagemaker.h

@ -0,0 +1,37 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef HUSH_NETMESSAGEMAKER_H
#define HUSH_NETMESSAGEMAKER_H
#include <net.h>
#include <serialize.h>
class CNetMsgMaker
{
public:
explicit CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){}
template <typename... Args>
CSerializedNetMsg Make(int nFlags, std::string msg_type, Args&&... args) const
{
CSerializedNetMsg msg;
msg.m_type = std::move(msg_type);
CVectorWriter{ SER_NETWORK, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... };
return msg;
}
template <typename... Args>
CSerializedNetMsg Make(std::string msg_type, Args&&... args) const
{
return Make(0, std::move(msg_type), std::forward<Args>(args)...);
}
private:
const int nVersion;
};
#endif // HUSH_NETMESSAGEMAKER_H

243
src/prevector.h

@ -1,17 +1,21 @@
// Copyright (c) 2015-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef _HUSH_PREVECTOR_H_
#define _HUSH_PREVECTOR_H_
#include <util.h>
#ifndef HUSH_PREVECTOR_H
#define HUSH_PREVECTOR_H
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <iterator>
#pragma pack(push, 1)
#include <algorithm>
#include <cstddef>
#include <type_traits>
#include <utility>
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
* used to store element counts, and can be any unsigned + signed type.
@ -130,7 +134,7 @@ public:
typedef const T* pointer;
typedef const T& reference;
typedef std::bidirectional_iterator_tag iterator_category;
const_reverse_iterator(T* ptr_) : ptr(ptr_) {}
const_reverse_iterator(const T* ptr_) : ptr(ptr_) {}
const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {}
const T& operator*() const { return *ptr; }
const T* operator->() const { return ptr; }
@ -143,19 +147,25 @@ public:
};
private:
size_type _size;
union {
#pragma pack(push, 1)
union direct_or_indirect {
char direct[sizeof(T) * N];
struct {
size_type capacity;
char* indirect;
};
} _union;
size_type capacity;
} indirect_contents;
};
#pragma pack(pop)
alignas(char*) direct_or_indirect _union = {};
size_type _size = 0;
static_assert(alignof(char*) % alignof(size_type) == 0 && sizeof(char*) % alignof(size_type) == 0, "size_type cannot have more restrictive alignment requirement than pointer");
static_assert(alignof(char*) % alignof(T) == 0, "value_type T cannot have more restrictive alignment requirement than pointer");
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; }
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; }
T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect) + pos; }
const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect) + pos; }
T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect_contents.indirect) + pos; }
const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect_contents.indirect) + pos; }
bool is_direct() const { return _size <= N; }
void change_capacity(size_type new_capacity) {
@ -173,17 +183,17 @@ private:
/* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert
success. These should instead use an allocator or new/delete so that handlers
are called as necessary, but performance would be slightly degraded by doing so. */
_union.indirect = static_cast<char*>(realloc(_union.indirect, ((size_t)sizeof(T)) * new_capacity));
if (!_union.indirect) { new_handler_terminate(); }
_union.capacity = new_capacity;
_union.indirect_contents.indirect = static_cast<char*>(realloc(_union.indirect_contents.indirect, ((size_t)sizeof(T)) * new_capacity));
assert(_union.indirect_contents.indirect);
_union.indirect_contents.capacity = new_capacity;
} else {
char* new_indirect = static_cast<char*>(malloc(((size_t)sizeof(T)) * new_capacity));
if (!new_indirect) { new_handler_terminate(); }
assert(new_indirect);
T* src = direct_ptr(0);
T* dst = reinterpret_cast<T*>(new_indirect);
memcpy(dst, src, size() * sizeof(T));
_union.indirect = new_indirect;
_union.capacity = new_capacity;
_union.indirect_contents.indirect = new_indirect;
_union.indirect_contents.capacity = new_capacity;
_size += N + 1;
}
}
@ -192,16 +202,27 @@ private:
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
void fill(T* dst, ptrdiff_t count, const T& value = T{}) {
std::fill_n(dst, count, value);
}
template<typename InputIterator>
void fill(T* dst, InputIterator first, InputIterator last) {
while (first != last) {
new(static_cast<void*>(dst)) T(*first);
++dst;
++first;
}
}
public:
void assign(size_type n, const T& val) {
clear();
if (capacity() < n) {
change_capacity(n);
}
while (size() < n) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(val);
}
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
@ -211,60 +232,51 @@ public:
if (capacity() < n) {
change_capacity(n);
}
while (first != last) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
++first;
}
_size += n;
fill(item_ptr(0), first, last);
}
prevector() : _size(0) {}
prevector() {}
explicit prevector(size_type n) : _size(0) {
explicit prevector(size_type n) {
resize(n);
}
explicit prevector(size_type n, const T& val = T()) : _size(0) {
explicit prevector(size_type n, const T& val) {
change_capacity(n);
while (size() < n) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(val);
}
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
prevector(InputIterator first, InputIterator last) : _size(0) {
prevector(InputIterator first, InputIterator last) {
size_type n = last - first;
change_capacity(n);
while (first != last) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
++first;
}
_size += n;
fill(item_ptr(0), first, last);
}
prevector(const prevector<N, T, Size, Diff>& other) : _size(0) {
change_capacity(other.size());
const_iterator it = other.begin();
while (it != other.end()) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
++it;
}
prevector(const prevector<N, T, Size, Diff>& other) {
size_type n = other.size();
change_capacity(n);
_size += n;
fill(item_ptr(0), other.begin(), other.end());
}
prevector(prevector<N, T, Size, Diff>&& other) {
swap(other);
}
prevector& operator=(const prevector<N, T, Size, Diff>& other) {
if (&other == this) {
return *this;
}
resize(0);
change_capacity(other.size());
const_iterator it = other.begin();
while (it != other.end()) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
++it;
}
assign(other.begin(), other.end());
return *this;
}
prevector& operator=(prevector<N, T, Size, Diff>&& other) {
swap(other);
return *this;
}
@ -290,7 +302,7 @@ public:
if (is_direct()) {
return N;
} else {
return _union.capacity;
return _union.indirect_contents.capacity;
}
}
@ -303,17 +315,20 @@ public:
}
void resize(size_type new_size) {
while (size() > new_size) {
item_ptr(size() - 1)->~T();
_size--;
size_type cur_size = size();
if (cur_size == new_size) {
return;
}
if (cur_size > new_size) {
erase(item_ptr(new_size), end());
return;
}
if (new_size > capacity()) {
change_capacity(new_size);
}
while (size() < new_size) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T();
}
ptrdiff_t increase = new_size - cur_size;
fill(item_ptr(cur_size), increase);
_size += increase;
}
void reserve(size_type new_capacity) {
@ -336,10 +351,11 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T));
T* ptr = item_ptr(p);
memmove(ptr + 1, ptr, (size() - p) * sizeof(T));
_size++;
new(static_cast<void*>(item_ptr(p))) T(value);
return iterator(item_ptr(p));
new(static_cast<void*>(ptr)) T(value);
return iterator(ptr);
}
void insert(iterator pos, size_type count, const T& value) {
@ -348,11 +364,10 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
T* ptr = item_ptr(p);
memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
for (size_type i = 0; i < count; i++) {
new(static_cast<void*>(item_ptr(p + i))) T(value);
}
fill(item_ptr(p), count, value);
}
template<typename InputIterator>
@ -363,45 +378,69 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
T* ptr = item_ptr(p);
memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
while (first != last) {
new(static_cast<void*>(item_ptr(p))) T(*first);
++p;
++first;
fill(ptr, first, last);
}
inline void resize_uninitialized(size_type new_size) {
// resize_uninitialized changes the size of the prevector but does not initialize it.
// If size < new_size, the added elements must be initialized explicitly.
if (capacity() < new_size) {
change_capacity(new_size);
_size += new_size - size();
return;
}
if (new_size < size()) {
erase(item_ptr(new_size), end());
} else {
_size += new_size - size();
}
}
iterator erase(iterator pos) {
(*pos).~T();
memmove(&(*pos), &(*pos) + 1, ((char*)&(*end())) - ((char*)(1 + &(*pos))));
_size--;
return pos;
return erase(pos, pos + 1);
}
iterator erase(iterator first, iterator last) {
// Erase is not allowed to the change the object's capacity. That means
// that when starting with an indirectly allocated prevector with
// size and capacity > N, the result may be a still indirectly allocated
// prevector with size <= N and capacity > N. A shrink_to_fit() call is
// necessary to switch to the (more efficient) directly allocated
// representation (with capacity N and size <= N).
iterator p = first;
char* endp = (char*)&(*end());
while (p != last) {
(*p).~T();
_size--;
++p;
if (!std::is_trivially_destructible<T>::value) {
while (p != last) {
(*p).~T();
_size--;
++p;
}
} else {
_size -= last - p;
}
memmove(&(*first), &(*last), endp - ((char*)(&(*last))));
return first;
}
void push_back(const T& value) {
template<typename... Args>
void emplace_back(Args&&... args) {
size_type new_size = size() + 1;
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
new(item_ptr(size())) T(value);
new(item_ptr(size())) T(std::forward<Args>(args)...);
_size++;
}
void push_back(const T& value) {
emplace_back(value);
}
void pop_back() {
_size--;
erase(end() - 1, end());
}
T& front() {
@ -421,20 +460,17 @@ public:
}
void swap(prevector<N, T, Size, Diff>& other) {
if (_size & other._size & 1) {
std::swap(_union.capacity, other._union.capacity);
std::swap(_union.indirect, other._union.indirect);
} else {
std::swap(_union, other._union);
}
std::swap(_union, other._union);
std::swap(_size, other._size);
}
~prevector() {
clear();
if (!std::is_trivially_destructible<T>::value) {
clear();
}
if (!is_direct()) {
free(_union.indirect);
_union.indirect = NULL;
free(_union.indirect_contents.indirect);
_union.indirect_contents.indirect = nullptr;
}
}
@ -486,10 +522,17 @@ public:
if (is_direct()) {
return 0;
} else {
return ((size_t)(sizeof(T))) * _union.capacity;
return ((size_t)(sizeof(T))) * _union.indirect_contents.capacity;
}
}
value_type* data() {
return item_ptr(0);
}
const value_type* data() const {
return item_ptr(0);
}
};
#pragma pack(pop)
#endif
#endif // HUSH_PREVECTOR_H

2
src/primitives/block.cpp

@ -20,7 +20,7 @@
#include "primitives/block.h"
#include "hash.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "crypto/common.h"
#include "hush_defs.h"

2
src/primitives/transaction.cpp

@ -23,7 +23,7 @@
#include "hash.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "librustzcash.h"

93
src/protocol.cpp

@ -18,9 +18,11 @@
* *
******************************************************************************/
#include "main.h"
#include "protocol.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#ifndef _WIN32
# include <arpa/inet.h>
@ -34,6 +36,93 @@ static const char* ppszTypeName[] =
"filtered block"
};
namespace NetMsgType {
const char *VERSION="version"; //used
const char *VERACK="verack"; //used
const char *ADDR="addr"; //used
const char *ADDRV2="addrv2"; //used
const char *SENDADDRV2="sendaddrv2";
const char *INV="inv"; //used
const char *GETDATA="getdata"; //used
const char *MERKLEBLOCK="merkleblock";
const char *GETBLOCKS="getblocks"; //used
const char *GETHEADERS="getheaders"; //used
const char *TX="tx"; //used
const char *HEADERS="headers"; //used
const char *BLOCK="block"; //used
const char *GETADDR="getaddr"; //used
const char *MEMPOOL="mempool"; //used
const char *PING="ping"; //used
const char *PONG="pong"; //used
const char *NOTFOUND="notfound"; //used
const char *FILTERLOAD="filterload"; //used
const char *FILTERADD="filteradd"; //used
const char *FILTERCLEAR="filterclear"; //used
const char *SENDHEADERS="sendheaders";
const char *FEEFILTER="feefilter";
const char *SENDCMPCT="sendcmpct";
const char *CMPCTBLOCK="cmpctblock";
const char *GETBLOCKTXN="getblocktxn";
const char *BLOCKTXN="blocktxn";
const char *GETCFILTERS="getcfilters";
const char *CFILTER="cfilter";
const char *GETCFHEADERS="getcfheaders";
const char *CFHEADERS="cfheaders";
const char *GETCFCHECKPT="getcfcheckpt";
const char *CFCHECKPT="cfcheckpt";
const char *WTXIDRELAY="wtxidrelay";
const char *EVENTS="events"; //used
const char *GETNSPV="getnSPV"; //used
const char *NSPV="nSPV"; //used
const char *ALERT="alert"; //used
const char *REJECT="reject"; //used
} // namespace NetMsgType
/** All known message types. Keep this in the same order as the list of
* messages above and in protocol.h.
*/
const static std::string allNetMessageTypes[] = {
NetMsgType::VERSION,
NetMsgType::VERACK,
NetMsgType::ADDR,
NetMsgType::ADDRV2,
NetMsgType::SENDADDRV2,
NetMsgType::INV,
NetMsgType::GETDATA,
NetMsgType::MERKLEBLOCK,
NetMsgType::GETBLOCKS,
NetMsgType::GETHEADERS,
NetMsgType::TX,
NetMsgType::HEADERS,
NetMsgType::BLOCK,
NetMsgType::GETADDR,
NetMsgType::MEMPOOL,
NetMsgType::PING,
NetMsgType::PONG,
NetMsgType::NOTFOUND,
NetMsgType::FILTERLOAD,
NetMsgType::FILTERADD,
NetMsgType::FILTERCLEAR,
NetMsgType::SENDHEADERS,
NetMsgType::FEEFILTER,
NetMsgType::SENDCMPCT,
NetMsgType::CMPCTBLOCK,
NetMsgType::GETBLOCKTXN,
NetMsgType::BLOCKTXN,
NetMsgType::GETCFILTERS,
NetMsgType::CFILTER,
NetMsgType::GETCFHEADERS,
NetMsgType::CFHEADERS,
NetMsgType::GETCFCHECKPT,
NetMsgType::CFCHECKPT,
NetMsgType::WTXIDRELAY,
NetMsgType::EVENTS,
NetMsgType::GETNSPV,
NetMsgType::NSPV,
NetMsgType::ALERT,
NetMsgType::REJECT,
};
CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn)
{
memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE);
@ -101,7 +190,7 @@ CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn)
void CAddress::Init()
{
nServices = NODE_NETWORK | NODE_NSPV;
nServices = GetBoolArg("-nspv_msg", DEFAULT_NSPV_PROCESSING) ? NODE_NETWORK | NODE_NSPV : NODE_NETWORK;
nTime = 100000000;
}

313
src/protocol.h

@ -26,6 +26,7 @@
#include "netbase.h"
#include "serialize.h"
#include "streams.h"
#include "uint256.h"
#include "version.h"
@ -79,8 +80,224 @@ public:
unsigned int nChecksum;
};
/**
* Bitcoin protocol message types. When adding new message types, don't forget
* to update allNetMessageTypes in protocol.cpp.
*/
namespace NetMsgType {
/**
* The version message provides information about the transmitting node to the
* receiving node at the beginning of a connection.
*/
extern const char* VERSION;
/**
* The verack message acknowledges a previously-received version message,
* informing the connecting node that it can begin to send other messages.
*/
extern const char* VERACK;
/**
* The addr (IP address) message relays connection information for peers on the
* network.
*/
extern const char* ADDR;
/**
* The addrv2 message relays connection information for peers on the network just
* like the addr message, but is extended to allow gossiping of longer node
* addresses (see BIP155).
*/
extern const char *ADDRV2;
/**
* The sendaddrv2 message signals support for receiving ADDRV2 messages (BIP155).
* It also implies that its sender can encode as ADDRV2 and would send ADDRV2
* instead of ADDR to a peer that has signaled ADDRV2 support by sending SENDADDRV2.
*/
extern const char *SENDADDRV2;
/**
* The inv message (inventory message) transmits one or more inventories of
* objects known to the transmitting peer.
*/
extern const char* INV;
/**
* The getdata message requests one or more data objects from another node.
*/
extern const char* GETDATA;
/**
* The merkleblock message is a reply to a getdata message which requested a
* block using the inventory type MSG_MERKLEBLOCK.
* @since protocol version 70001 as described by BIP37.
*/
extern const char* MERKLEBLOCK;
/**
* The getblocks message requests an inv message that provides block header
* hashes starting from a particular point in the block chain.
*/
extern const char* GETBLOCKS;
/**
* The getheaders message requests a headers message that provides block
* headers starting from a particular point in the block chain.
* @since protocol version 31800.
*/
extern const char* GETHEADERS;
/**
* The tx message transmits a single transaction.
*/
extern const char* TX;
/**
* The headers message sends one or more block headers to a node which
* previously requested certain headers with a getheaders message.
* @since protocol version 31800.
*/
extern const char* HEADERS;
/**
* The block message transmits a single serialized block.
*/
extern const char* BLOCK;
/**
* The getaddr message requests an addr message from the receiving node,
* preferably one with lots of IP addresses of other receiving nodes.
*/
extern const char* GETADDR;
/**
* The mempool message requests the TXIDs of transactions that the receiving
* node has verified as valid but which have not yet appeared in a block.
* @since protocol version 60002.
*/
extern const char* MEMPOOL;
/**
* The ping message is sent periodically to help confirm that the receiving
* peer is still connected.
*/
extern const char* PING;
/**
* The pong message replies to a ping message, proving to the pinging node that
* the ponging node is still alive.
* @since protocol version 60001 as described by BIP31.
*/
extern const char* PONG;
/**
* The notfound message is a reply to a getdata message which requested an
* object the receiving node does not have available for relay.
* @since protocol version 70001.
*/
extern const char* NOTFOUND;
/**
* The filterload message tells the receiving peer to filter all relayed
* transactions and requested merkle blocks through the provided filter.
* @since protocol version 70001 as described by BIP37.
* Only available with service bit NODE_BLOOM since protocol version
* 70011 as described by BIP111.
*/
extern const char* FILTERLOAD;
/**
* The filteradd message tells the receiving peer to add a single element to a
* previously-set bloom filter, such as a new public key.
* @since protocol version 70001 as described by BIP37.
* Only available with service bit NODE_BLOOM since protocol version
* 70011 as described by BIP111.
*/
extern const char* FILTERADD;
/**
* The filterclear message tells the receiving peer to remove a previously-set
* bloom filter.
* @since protocol version 70001 as described by BIP37.
* Only available with service bit NODE_BLOOM since protocol version
* 70011 as described by BIP111.
*/
extern const char* FILTERCLEAR;
/**
* Indicates that a node prefers to receive new block announcements via a
* "headers" message rather than an "inv".
* @since protocol version 70012 as described by BIP130.
*/
extern const char* SENDHEADERS;
/**
* The feefilter message tells the receiving peer not to inv us any txs
* which do not meet the specified min fee rate.
* @since protocol version 70013 as described by BIP133
*/
extern const char* FEEFILTER;
/**
* Contains a 1-byte bool and 8-byte LE version number.
* Indicates that a node is willing to provide blocks via "cmpctblock" messages.
* May indicate that a node prefers to receive new block announcements via a
* "cmpctblock" message rather than an "inv", depending on message contents.
* @since protocol version 70014 as described by BIP 152
*/
extern const char* SENDCMPCT;
/**
* Contains a CBlockHeaderAndShortTxIDs object - providing a header and
* list of "short txids".
* @since protocol version 70014 as described by BIP 152
*/
extern const char* CMPCTBLOCK;
/**
* Contains a BlockTransactionsRequest
* Peer should respond with "blocktxn" message.
* @since protocol version 70014 as described by BIP 152
*/
extern const char* GETBLOCKTXN;
/**
* Contains a BlockTransactions.
* Sent in response to a "getblocktxn" message.
* @since protocol version 70014 as described by BIP 152
*/
extern const char* BLOCKTXN;
/**
* getcfilters requests compact filters for a range of blocks.
* Only available with service bit NODE_COMPACT_FILTERS as described by
* BIP 157 & 158.
*/
extern const char* GETCFILTERS;
/**
* cfilter is a response to a getcfilters request containing a single compact
* filter.
*/
extern const char* CFILTER;
/**
* getcfheaders requests a compact filter header and the filter hashes for a
* range of blocks, which can then be used to reconstruct the filter headers
* for those blocks.
* Only available with service bit NODE_COMPACT_FILTERS as described by
* BIP 157 & 158.
*/
extern const char* GETCFHEADERS;
/**
* cfheaders is a response to a getcfheaders request containing a filter header
* and a vector of filter hashes for each subsequent block in the requested range.
*/
extern const char* CFHEADERS;
/**
* getcfcheckpt requests evenly spaced compact filter headers, enabling
* parallelized download and validation of the headers between them.
* Only available with service bit NODE_COMPACT_FILTERS as described by
* BIP 157 & 158.
*/
extern const char* GETCFCHECKPT;
/**
* cfcheckpt is a response to a getcfcheckpt request containing a vector of
* evenly spaced filter headers for blocks on the requested chain.
*/
extern const char* CFCHECKPT;
/**
* Indicates that a node prefers to relay transactions via wtxid, rather than
* txid.
* @since protocol version 70016 as described by BIP 339.
*/
extern const char* WTXIDRELAY;
extern const char* EVENTS;
extern const char* GETNSPV;
extern const char* NSPV;
extern const char* ALERT;
extern const char* REJECT;
}; // namespace NetMsgType
/* Get a vector of all valid message types (see above) */
const std::vector<std::string>& getAllNetMessageTypes();
/** nServices flags */
enum {
enum ServiceFlags : uint64_t {
// NODE_NETWORK means that the node is capable of serving the block chain. It is currently
// set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want
// network services but don't provide them.
@ -106,27 +323,93 @@ enum {
/** A CService with information about it as peer */
class CAddress : public CService
{
static constexpr uint32_t TIME_INIT{100000000};
/** Historically, CAddress disk serialization stored the CLIENT_VERSION, optionally OR'ed with
* the ADDRV2_FORMAT flag to indicate V2 serialization. The first field has since been
* disentangled from client versioning, and now instead:
* - The low bits (masked by DISK_VERSION_IGNORE_MASK) store the fixed value DISK_VERSION_INIT,
* (in case any code exists that treats it as a client version) but are ignored on
* deserialization.
* - The high bits (masked by ~DISK_VERSION_IGNORE_MASK) store actual serialization information.
* Only 0 or DISK_VERSION_ADDRV2 (equal to the historical ADDRV2_FORMAT) are valid now, and
* any other value triggers a deserialization failure. Other values can be added later if
* needed.
*
* For disk deserialization, ADDRV2_FORMAT in the stream version signals that ADDRV2
* deserialization is permitted, but the actual format is determined by the high bits in the
* stored version field. For network serialization, the stream version having ADDRV2_FORMAT or
* not determines the actual format used (as it has no embedded version number).
*/
static constexpr uint32_t DISK_VERSION_INIT{220000};
static constexpr uint32_t DISK_VERSION_IGNORE_MASK{0b00000000000001111111111111111111};
/** The version number written in disk serialized addresses to indicate V2 serializations.
* It must be exactly 1<<29, as that is the value that historical versions used for this
* (they used their internal ADDRV2_FORMAT flag here). */
static constexpr uint32_t DISK_VERSION_ADDRV2{1 << 29};
static_assert((DISK_VERSION_INIT & ~DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_INIT must be covered by DISK_VERSION_IGNORE_MASK");
static_assert((DISK_VERSION_ADDRV2 & DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_ADDRV2 must not be covered by DISK_VERSION_IGNORE_MASK");
public:
CAddress();
explicit CAddress(CService ipIn, uint64_t nServicesIn = NODE_NETWORK);
void Init();
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
SERIALIZE_METHODS(CAddress, obj)
{
if (ser_action.ForRead())
Init();
int nVersion = s.GetVersion();
if (s.GetType() & SER_DISK)
READWRITE(nVersion);
if ((s.GetType() & SER_DISK) ||
(nVersion >= CADDR_TIME_VERSION && !(s.GetType() & SER_GETHASH)))
READWRITE(nTime);
READWRITE(nServices);
READWRITE(*(CService*)this);
// CAddress has a distinct network serialization and a disk serialization, but it should never
// be hashed (except through CHashWriter in addrdb.cpp, which sets SER_DISK), and it's
// ambiguous what that would mean. Make sure no code relying on that is introduced:
assert(!(s.GetType() & SER_GETHASH));
bool use_v2;
bool store_time;
if (s.GetType() & SER_DISK) {
// In the disk serialization format, the encoding (v1 or v2) is determined by a flag version
// that's part of the serialization itself. ADDRV2_FORMAT in the stream version only determines
// whether V2 is chosen/permitted at all.
uint32_t stored_format_version = DISK_VERSION_INIT;
if (s.GetVersion() & ADDRV2_FORMAT) stored_format_version |= DISK_VERSION_ADDRV2;
READ_WRITE(stored_format_version);
stored_format_version &= ~DISK_VERSION_IGNORE_MASK; // ignore low bits
if (stored_format_version == 0) {
use_v2 = false;
} else if (stored_format_version == DISK_VERSION_ADDRV2 && (s.GetVersion() & ADDRV2_FORMAT)) {
// Only support v2 deserialization if ADDRV2_FORMAT is set.
use_v2 = true;
} else {
throw std::ios_base::failure("Unsupported CAddress disk format version");
}
store_time = true;
} else {
// In the network serialization format, the encoding (v1 or v2) is determined directly by
// the value of ADDRV2_FORMAT in the stream version, as no explicitly encoded version
// exists in the stream.
assert(s.GetType() & SER_NETWORK);
use_v2 = s.GetVersion() & ADDRV2_FORMAT;
// The only time we serialize a CAddress object without nTime is in
// the initial VERSION messages which contain two CAddress records.
// At that point, the serialization version is INIT_PROTO_VERSION.
// After the version handshake, serialization version is >=
// MIN_PEER_PROTO_VERSION and all ADDR messages are serialized with
// nTime.
store_time = s.GetVersion() != INIT_PROTO_VERSION;
}
SER_READ(obj, obj.nTime = TIME_INIT);
if (store_time) READWRITE(obj.nTime);
// nServices is serialized as CompactSize in V2; as uint64_t in V1.
if (use_v2) {
uint64_t services_tmp;
SER_WRITE(obj, services_tmp = obj.nServices);
READ_WRITE(Using<CompactSizeFormatter<false>>(services_tmp));
SER_READ(obj, obj.nServices = static_cast<ServiceFlags>(services_tmp));
} else {
READ_WRITE(Using<CustomUintFormatter<8>>(obj.nServices));
}
// Invoke V1/V2 serializer for CService parent object.
OverrideStream<Stream> os(&s, s.GetType(), use_v2 ? ADDRV2_FORMAT : 0);
SerReadWriteMany(os, ser_action, ReadWriteAsHelper<CService>(obj));
}
// TODO: make private (improves encapsulation)

2
src/random.cpp

@ -27,7 +27,7 @@
#endif
#include "serialize.h" // for begin_ptr(vec)
#include "util.h" // for LogPrint()
#include "utilstrencodings.h" // for GetTime()
#include "util/strencodings.h" // for GetTime()
#include <limits>

2
src/rest.cpp

@ -25,7 +25,7 @@
#include "streams.h"
#include "sync.h"
#include "txmempool.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "version.h"
#include <boost/algorithm/string.hpp>
#include <boost/dynamic_bitset.hpp>

65
src/rpc/net.cpp

@ -20,6 +20,7 @@
#include "clientversion.h"
#include "main.h"
#include "net.h"
#include "addrman.h"
#include "netbase.h"
#include "protocol.h"
#include "sync.h"
@ -75,6 +76,36 @@ UniValue ping(const UniValue& params, bool fHelp, const CPubKey& mypk)
return NullUniValue;
}
UniValue getpeerlist(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"getpeerlist\n"
"\nReturns a list of connected network node addresses that have connected to the\n"
"in the last 30 days as a json array of objects.\n"
"\nbResult:\n"
"[\n"
" \"host:port\", (string) The ip address and port of the peer\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("getpeerlist", "")
+ HelpExampleRpc("getpeerlist", "")
);
LOCK(cs_main);
UniValue ret(UniValue::VARR);
std::map<std::string, int64_t> info;
addrman.GetAllPeers(info);
int64_t nCutOff = GetTime() - (60 * 60 * 24 * 30); //Connected within last 30 days.
for (std::map<std::string, int64_t>::iterator it = info.begin(); it != info.end(); it++) {
if ((*it).second >= nCutOff)
ret.push_back((*it).first);
}
return ret;
}
UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 0)
@ -159,6 +190,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
// corrupting or modifying the JSON output by putting special characters in
// their ver message.
obj.push_back(Pair("subver", stats.cleanSubVer));
obj.push_back(Pair("addrv2", stats.m_wants_addrv2));
obj.push_back(Pair("inbound", stats.fInbound));
obj.push_back(Pair("startingheight", stats.nStartingHeight));
if (fStateStats) {
@ -447,13 +479,12 @@ static UniValue GetNetworksInfo()
for(int n=0; n<NET_MAX; ++n)
{
enum Network network = static_cast<enum Network>(n);
if(network == NET_UNROUTABLE)
if(network == NET_UNROUTABLE || network == NET_INTERNAL)
continue;
proxyType proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
obj.push_back(Pair("name", GetNetworkName(network)));
obj.push_back(Pair("limited", IsLimited(network)));
obj.push_back(Pair("reachable", IsReachable(network)));
obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()));
obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials));
@ -586,10 +617,13 @@ UniValue setban(const UniValue& params, bool fHelp, const CPubKey& mypk)
if (params[0].get_str().find("/") != string::npos)
isSubnet = true;
if (!isSubnet)
netAddr = CNetAddr(params[0].get_str());
else
subNet = CSubNet(params[0].get_str());
if (!isSubnet) {
CNetAddr resolved;
LookupHost(params[0].get_str().c_str(), resolved, false);
netAddr = resolved;
} else {
LookupSubNet(params[0].get_str().c_str(), subNet);
}
if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet");
@ -607,7 +641,7 @@ UniValue setban(const UniValue& params, bool fHelp, const CPubKey& mypk)
if (params.size() == 4 && params[3].isTrue())
absolute = true;
isSubnet ? CNode::Ban(subNet, banTime, absolute) : CNode::Ban(netAddr, banTime, absolute);
isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
//disconnect possible nodes
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
@ -633,17 +667,21 @@ UniValue listbanned(const UniValue& params, bool fHelp, const CPubKey& mypk)
+ HelpExampleRpc("listbanned", "")
);
std::map<CSubNet, int64_t> banMap;
CNode::GetBanned(banMap);
const int64_t current_time{GetTime()};
banmap_t banMap;
GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR);
for (std::map<CSubNet, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++)
const int64_t current_time{GetTime()};
for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
{
CBanEntry banEntry = (*it).second;
UniValue rec(UniValue::VOBJ);
rec.push_back(Pair("address", (*it).first.ToString()));
rec.push_back(Pair("banned_until", (*it).second));
rec.push_back(Pair("time_remaining", (*it).second - current_time));
rec.push_back(Pair("banned_until", banEntry.nBanUntil));
rec.push_back(Pair("time_remaining", banEntry.nBanUntil - current_time));
rec.push_back(Pair("ban_created", banEntry.nCreateTime));
rec.push_back(Pair("ban_reason", banEntry.banReasonToString()));
bannedAddresses.push_back(rec);
}
@ -672,6 +710,7 @@ static const CRPCCommand commands[] =
{ "network", "getconnectioncount", &getconnectioncount, true },
{ "network", "getdeprecationinfo", &getdeprecationinfo, true },
{ "network", "ping", &ping, true },
{ "network", "getpeerlist", &getpeerlist, true },
{ "network", "getpeerinfo", &getpeerinfo, true },
{ "network", "addnode", &addnode, true },
{ "network", "disconnectnode", &disconnectnode, true },

2
src/rpc/protocol.cpp

@ -24,7 +24,7 @@
#include "random.h"
#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "utiltime.h"
#include "version.h"

2
src/rpc/server.cpp

@ -24,7 +24,7 @@
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "asyncrpcqueue.h"
#include <memory>
#include <univalue.h>

2
src/script/script.cpp

@ -22,7 +22,7 @@
#include "script.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "script/cc.h"
#include "cc/eval.h"
#include "cryptoconditions/include/cryptoconditions.h"

2
src/script/standard.cpp

@ -21,7 +21,7 @@
#include "pubkey.h"
#include "script/script.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "script/cc.h"
#include <boost/foreach.hpp>

320
src/serialize.h

@ -1,6 +1,6 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2009-2014 The Hush developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
@ -42,6 +42,7 @@
#include <boost/optional.hpp>
#include "prevector.h"
#include "span.h"
static const unsigned int MAX_SIZE = 0x02000000;
@ -59,6 +60,12 @@ static const unsigned int MAX_SIZE = 0x02000000;
struct deserialize_type {};
constexpr deserialize_type deserialize {};
//! Safely convert odd char pointer types to standard ones.
inline char* CharCast(char* c) { return c; }
inline char* CharCast(unsigned char* c) { return (char*)c; }
inline const char* CharCast(const char* c) { return c; }
inline const char* CharCast(const unsigned char* c) { return (const char*)c; }
/**
* Used to bypass the rule against non-const reference to temporary
* where it makes sense with wrappers such as CFlatData or CTxDB
@ -79,7 +86,7 @@ inline T* NCONST_PTR(const T* val)
return const_cast<T*>(val);
}
/**
/**
* Get begin pointer of vector (non-const version).
* @note These functions avoid the undefined case of indexing into an empty
* vector, as well as that of indexing after the end of the vector.
@ -121,6 +128,11 @@ template<typename Stream> inline void ser_writedata16(Stream &s, uint16_t obj)
obj = htole16(obj);
s.write((char*)&obj, 2);
}
template<typename Stream> inline void ser_writedata16be(Stream &s, uint16_t obj)
{
obj = htobe16(obj);
s.write((char*)&obj, 2);
}
template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
{
obj = htole32(obj);
@ -148,6 +160,12 @@ template<typename Stream> inline uint16_t ser_readdata16(Stream &s)
s.read((char*)&obj, 2);
return le16toh(obj);
}
template<typename Stream> inline uint16_t ser_readdata16be(Stream &s)
{
uint16_t obj;
s.read((char*)&obj, 2);
return be16toh(obj);
}
template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
{
uint32_t obj;
@ -208,14 +226,69 @@ enum
SER_GETHASH = (1 << 2),
};
//! Convert the reference base type to X, without changing constness or reference type.
template<typename X> X& ReadWriteAsHelper(X& x) { return x; }
template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; }
#define READWRITE(obj) (::SerReadWrite(s, (obj), ser_action))
#define READWRITEMANY(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
/**
#define READ_WRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
#define READWRITEAS(type, obj) (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj)))
#define SER_READ(obj, code) ::SerRead(s, ser_action, obj, [&](Stream& s, typename std::remove_const<Type>::type& obj) { code; })
#define SER_WRITE(obj, code) ::SerWrite(s, ser_action, obj, [&](Stream& s, const Type& obj) { code; })
/**
* Implement the Ser and Unser methods needed for implementing a formatter (see Using below).
*
* Both Ser and Unser are delegated to a single static method SerializationOps, which is polymorphic
* in the serialized/deserialized type (allowing it to be const when serializing, and non-const when
* deserializing).
*
* Example use:
* struct FooFormatter {
* FORMATTER_METHODS(Class, obj) { READWRITE(obj.val1, VARINT(obj.val2)); }
* }
* would define a class FooFormatter that defines a serialization of Class objects consisting
* of serializing its val1 member using the default serialization, and its val2 member using
* VARINT serialization. That FooFormatter can then be used in statements like
* READWRITE(Using<FooFormatter>(obj.bla)).
*/
#define FORMATTER_METHODS(cls, obj) \
template<typename Stream> \
static void Ser(Stream& s, const cls& obj) { SerializationOps(obj, s, CSerActionSerialize()); } \
template<typename Stream> \
static void Unser(Stream& s, cls& obj) { SerializationOps(obj, s, CSerActionUnserialize()); } \
template<typename Stream, typename Type, typename Operation> \
static inline void SerializationOps(Type& obj, Stream& s, Operation ser_action) \
/**
* Implement the Serialize and Unserialize methods by delegating to a single templated
* static method that takes the to-be-(de)serialized object as a parameter. This approach
* has the advantage that the constness of the object becomes a template parameter, and
* thus allows a single implementation that sees the object as const for serializing
* and non-const for deserializing, without casts.
*/
#define SERIALIZE_METHODS(cls, obj) \
template<typename Stream> \
void Serialize(Stream& s) const \
{ \
static_assert(std::is_same<const cls&, decltype(*this)>::value, "Serialize type mismatch"); \
Ser(s, *this); \
} \
template<typename Stream> \
void Unserialize(Stream& s) \
{ \
static_assert(std::is_same<cls&, decltype(*this)>::value, "Unserialize type mismatch"); \
Unser(s, *this); \
} \
FORMATTER_METHODS(cls, obj)
/**
* Implement three methods for serializable objects. These are actually wrappers over
* "SerializationOp" template, which implements the body of each class' serialization
* code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be
* added as members.
* added as members.
*/
#define ADD_SERIALIZE_METHODS \
template<typename Stream> \
@ -227,7 +300,9 @@ enum
SerializationOp(s, CSerActionUnserialize()); \
}
#ifndef CHAR_EQUALS_INT8
template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
#endif
template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); }
@ -236,10 +311,17 @@ template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_wri
template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); }
template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); }
template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
template<typename Stream> inline void Serialize(Stream& s, float a ) { ser_writedata32(s, ser_float_to_uint32(a)); }
template<typename Stream> inline void Serialize(Stream& s, double a ) { ser_writedata64(s, ser_double_to_uint64(a)); }
#ifndef CHAR_EQUALS_INT8
template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char
#endif
template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); }
@ -248,6 +330,10 @@ template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a =
template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); }
template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); }
template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(CharCast(span.data()), span.size()); }
template<typename Stream> inline void Unserialize(Stream& s, float& a ) { a = ser_uint32_to_float(ser_readdata32(s)); }
template<typename Stream> inline void Unserialize(Stream& s, double& a ) { a = ser_uint64_to_double(ser_readdata64(s)); }
@ -301,7 +387,7 @@ void WriteCompactSize(Stream& os, uint64_t nSize)
}
template<typename Stream>
uint64_t ReadCompactSize(Stream& is)
uint64_t ReadCompactSize(Stream& is, bool range_check = true)
{
uint8_t chSize = ser_readdata8(is);
uint64_t nSizeRet = 0;
@ -327,8 +413,9 @@ uint64_t ReadCompactSize(Stream& is)
if (nSizeRet < 0x100000000ULL)
throw std::ios_base::failure("non-canonical ReadCompactSize()");
}
if (nSizeRet > (uint64_t)MAX_SIZE)
if (range_check && nSizeRet > MAX_SIZE) {
throw std::ios_base::failure("ReadCompactSize(): size too large");
}
return nSizeRet;
}
@ -338,16 +425,16 @@ uint64_t ReadCompactSize(Stream& is)
* sure the encoding is one-to-one, one is subtracted from all but the last digit.
* Thus, the byte sequence a[] with length len, where all but the last byte
* has bit 128 set, encodes the number:
*
*
* (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1))
*
*
* Properties:
* * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes)
* * Every integer has exactly one encoding
* * Encoding does not depend on size of original integer type
* * No redundancy: every (infinite) byte sequence corresponds to a list
* of encoded integers.
*
*
* 0: [0x00] 256: [0x81 0x00]
* 1: [0x01] 16383: [0xFE 0x7F]
* 127: [0x7F] 16384: [0xFF 0x00]
@ -408,7 +495,7 @@ I ReadVarInt(Stream& is)
#define COMPACTSIZE(obj) REF(CCompactSize(REF(obj)))
#define LIMITED_STRING(obj,n) REF(LimitedString< n >(REF(obj)))
/**
/**
* Wrapper for serializing arrays and POD.
*/
class CFlatData
@ -517,6 +604,191 @@ public:
template<typename I>
CVarInt<I> WrapVarInt(I& n) { return CVarInt<I>(n); }
/** Simple wrapper class to serialize objects using a formatter; used by Using(). */
template<typename Formatter, typename T>
class Wrapper
{
static_assert(std::is_lvalue_reference<T>::value, "Wrapper needs an lvalue reference type T");
protected:
T m_object;
public:
explicit Wrapper(T obj) : m_object(obj) {}
template<typename Stream> void Serialize(Stream &s) const { Formatter().Ser(s, m_object); }
template<typename Stream> void Unserialize(Stream &s) { Formatter().Unser(s, m_object); }
};
/** Cause serialization/deserialization of an object to be done using a specified formatter class.
*
* To use this, you need a class Formatter that has public functions Ser(stream, const object&) for
* serialization, and Unser(stream, object&) for deserialization. Serialization routines (inside
* READWRITE, or directly with << and >> operators), can then use Using<Formatter>(object).
*
* This works by constructing a Wrapper<Formatter, T>-wrapped version of object, where T is
* const during serialization, and non-const during deserialization, which maintains const
* correctness.
*/
template<typename Formatter, typename T>
static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&>(t); }
// #define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj)
// #define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
// #define COMPACTSIZE(obj) Using<CompactSizeFormatter<true>>(obj)
// #define LIMITED_STRING(obj,n) Using<LimitedStringFormatter<n>>(obj)
//
// /** Serialization wrapper class for integers in VarInt format. */
// template<VarIntMode Mode>
// struct VarIntFormatter
// {
// template<typename Stream, typename I> void Ser(Stream &s, I v)
// {
// WriteVarInt<Stream,Mode,typename std::remove_cv<I>::type>(s, v);
// }
//
// template<typename Stream, typename I> void Unser(Stream& s, I& v)
// {
// v = ReadVarInt<Stream,Mode,typename std::remove_cv<I>::type>(s);
// }
// };
/** Serialization wrapper class for custom integers and enums.
*
* It permits specifying the serialized size (1 to 8 bytes) and endianness.
*
* Use the big endian mode for values that are stored in memory in native
* byte order, but serialized in big endian notation. This is only intended
* to implement serializers that are compatible with existing formats, and
* its use is not recommended for new data structures.
*/
template<int Bytes, bool BigEndian = false>
struct CustomUintFormatter
{
static_assert(Bytes > 0 && Bytes <= 8, "CustomUintFormatter Bytes out of range");
static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes));
template <typename Stream, typename I> void Ser(Stream& s, I v)
{
if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
if (BigEndian) {
uint64_t raw = htobe64(v);
s.write(((const char*)&raw) + 8 - Bytes, Bytes);
} else {
uint64_t raw = htole64(v);
s.write((const char*)&raw, Bytes);
}
}
template <typename Stream, typename I> void Unser(Stream& s, I& v)
{
using U = typename std::conditional<std::is_enum<I>::value, std::underlying_type<I>, std::common_type<I>>::type::type;
static_assert(std::numeric_limits<U>::max() >= MAX && std::numeric_limits<U>::min() <= 0, "Assigned type too small");
uint64_t raw = 0;
if (BigEndian) {
s.read(((char*)&raw) + 8 - Bytes, Bytes);
v = static_cast<I>(be64toh(raw));
} else {
s.read((char*)&raw, Bytes);
v = static_cast<I>(le64toh(raw));
}
}
};
template<int Bytes> using BigEndianFormatter = CustomUintFormatter<Bytes, true>;
/** Formatter for integers in CompactSize format. */
template<bool RangeCheck>
struct CompactSizeFormatter
{
template<typename Stream, typename I>
void Unser(Stream& s, I& v)
{
uint64_t n = ReadCompactSize<Stream>(s, RangeCheck);
if (n < std::numeric_limits<I>::min() || n > std::numeric_limits<I>::max()) {
throw std::ios_base::failure("CompactSize exceeds limit of type");
}
v = n;
}
template<typename Stream, typename I>
void Ser(Stream& s, I v)
{
static_assert(std::is_unsigned<I>::value, "CompactSize only supported for unsigned integers");
static_assert(std::numeric_limits<I>::max() <= std::numeric_limits<uint64_t>::max(), "CompactSize only supports 64-bit integers and below");
WriteCompactSize<Stream>(s, v);
}
};
// template<size_t Limit>
// struct LimitedStringFormatter
// {
// template<typename Stream>
// void Unser(Stream& s, std::string& v)
// {
// size_t size = ReadCompactSize(s);
// if (size > Limit) {
// throw std::ios_base::failure("String length limit exceeded");
// }
// v.resize(size);
// if (size != 0) s.read((char*)v.data(), size);
// }
//
// template<typename Stream>
// void Ser(Stream& s, const std::string& v)
// {
// s << v;
// }
// };
/** Formatter to serialize/deserialize vector elements using another formatter
*
* Example:
* struct X {
* std::vector<uint64_t> v;
* SERIALIZE_METHODS(X, obj) { READWRITE(Using<VectorFormatter<VarInt>>(obj.v)); }
* };
* will define a struct that contains a vector of uint64_t, which is serialized
* as a vector of VarInt-encoded integers.
*
* V is not required to be an std::vector type. It works for any class that
* exposes a value_type, size, reserve, emplace_back, back, and const iterators.
*/
// template<class Formatter>
// struct VectorFormatter
// {
// template<typename Stream, typename V>
// void Ser(Stream& s, const V& v)
// {
// Formatter formatter;
// WriteCompactSize(s, v.size());
// for (const typename V::value_type& elem : v) {
// formatter.Ser(s, elem);
// }
// }
//
// template<typename Stream, typename V>
// void Unser(Stream& s, V& v)
// {
// Formatter formatter;
// v.clear();
// size_t size = ReadCompactSize(s);
// size_t allocated = 0;
// while (allocated < size) {
// // For DoS prevention, do not blindly allocate as much as the stream claims to contain.
// // Instead, allocate in 5MiB batches, so that an attacker actually needs to provide
// // X MiB of data to make us allocate X+5 Mib.
// static_assert(sizeof(typename V::value_type) <= MAX_VECTOR_ALLOCATE, "Vector element size too large");
// allocated = std::min(size, allocated + MAX_VECTOR_ALLOCATE / sizeof(typename V::value_type));
// v.reserve(allocated);
// while (v.size() < allocated) {
// v.emplace_back();
// formatter.Unser(s, v.back());
// }
// }
// };
// };
/**
* Forward declarations
*/
@ -1083,17 +1355,39 @@ inline void UnserializeMany(Stream& s, Arg& arg, Args&... args)
}
template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, Args&&... args)
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, const Args&... args)
{
::SerializeMany(s, std::forward<Args>(args)...);
::SerializeMany(s, args...);
}
template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&... args)
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&&... args)
{
::UnserializeMany(s, args...);
}
template<typename Stream, typename Type, typename Fn>
inline void SerRead(Stream& s, CSerActionSerialize ser_action, Type&&, Fn&&)
{
}
template<typename Stream, typename Type, typename Fn>
inline void SerRead(Stream& s, CSerActionUnserialize ser_action, Type&& obj, Fn&& fn)
{
fn(s, std::forward<Type>(obj));
}
template<typename Stream, typename Type, typename Fn>
inline void SerWrite(Stream& s, CSerActionSerialize ser_action, Type&& obj, Fn&& fn)
{
fn(s, std::forward<Type>(obj));
}
template<typename Stream, typename Type, typename Fn>
inline void SerWrite(Stream& s, CSerActionUnserialize ser_action, Type&&, Fn&&)
{
}
template<typename I>
inline void WriteVarInt(CSizeComputer &s, I n)
{

252
src/span.h

@ -0,0 +1,252 @@
// Copyright (c) 2018-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef BITCOIN_SPAN_H
#define BITCOIN_SPAN_H
#include <type_traits>
#include <cstddef>
#include <algorithm>
#include <assert.h>
#ifdef DEBUG
#define CONSTEXPR_IF_NOT_DEBUG
#define ASSERT_IF_DEBUG(x) assert((x))
#else
#define CONSTEXPR_IF_NOT_DEBUG constexpr
#define ASSERT_IF_DEBUG(x)
#endif
#if defined(__clang__)
#if __has_attribute(lifetimebound)
#define SPAN_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
#else
#define SPAN_ATTR_LIFETIMEBOUND
#endif
#else
#define SPAN_ATTR_LIFETIMEBOUND
#endif
/** A Span is an object that can refer to a contiguous sequence of objects.
*
* It implements a subset of C++20's std::span.
*
* Things to be aware of when writing code that deals with Spans:
*
* - Similar to references themselves, Spans are subject to reference lifetime
* issues. The user is responsible for making sure the objects pointed to by
* a Span live as long as the Span is used. For example:
*
* std::vector<int> vec{1,2,3,4};
* Span<int> sp(vec);
* vec.push_back(5);
* printf("%i\n", sp.front()); // UB!
*
* may exhibit undefined behavior, as increasing the size of a vector may
* invalidate references.
*
* - One particular pitfall is that Spans can be constructed from temporaries,
* but this is unsafe when the Span is stored in a variable, outliving the
* temporary. For example, this will compile, but exhibits undefined behavior:
*
* Span<const int> sp(std::vector<int>{1, 2, 3});
* printf("%i\n", sp.front()); // UB!
*
* The lifetime of the vector ends when the statement it is created in ends.
* Thus the Span is left with a dangling reference, and using it is undefined.
*
* - Due to Span's automatic creation from range-like objects (arrays, and data
* types that expose a data() and size() member function), functions that
* accept a Span as input parameter can be called with any compatible
* range-like object. For example, this works:
*
* void Foo(Span<const int> arg);
*
* Foo(std::vector<int>{1, 2, 3}); // Works
*
* This is very useful in cases where a function truly does not care about the
* container, and only about having exactly a range of elements. However it
* may also be surprising to see automatic conversions in this case.
*
* When a function accepts a Span with a mutable element type, it will not
* accept temporaries; only variables or other references. For example:
*
* void FooMut(Span<int> arg);
*
* FooMut(std::vector<int>{1, 2, 3}); // Does not compile
* std::vector<int> baz{1, 2, 3};
* FooMut(baz); // Works
*
* This is similar to how functions that take (non-const) lvalue references
* as input cannot accept temporaries. This does not work either:
*
* void FooVec(std::vector<int>& arg);
* FooVec(std::vector<int>{1, 2, 3}); // Does not compile
*
* The idea is that if a function accepts a mutable reference, a meaningful
* result will be present in that variable after the call. Passing a temporary
* is useless in that context.
*/
template<typename C>
class Span
{
C* m_data;
std::size_t m_size;
template <class T>
struct is_Span_int : public std::false_type {};
template <class T>
struct is_Span_int<Span<T>> : public std::true_type {};
template <class T>
struct is_Span : public is_Span_int<typename std::remove_cv<T>::type>{};
public:
constexpr Span() noexcept : m_data(nullptr), m_size(0) {}
/** Construct a span from a begin pointer and a size.
*
* This implements a subset of the iterator-based std::span constructor in C++20,
* which is hard to implement without std::address_of.
*/
template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
constexpr Span(T* begin, std::size_t size) noexcept : m_data(begin), m_size(size) {}
/** Construct a span from a begin and end pointer.
*
* This implements a subset of the iterator-based std::span constructor in C++20,
* which is hard to implement without std::address_of.
*/
template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
CONSTEXPR_IF_NOT_DEBUG Span(T* begin, T* end) noexcept : m_data(begin), m_size(end - begin)
{
ASSERT_IF_DEBUG(end >= begin);
}
/** Implicit conversion of spans between compatible types.
*
* Specifically, if a pointer to an array of type O can be implicitly converted to a pointer to an array of type
* C, then permit implicit conversion of Span<O> to Span<C>. This matches the behavior of the corresponding
* C++20 std::span constructor.
*
* For example this means that a Span<T> can be converted into a Span<const T>.
*/
template <typename O, typename std::enable_if<std::is_convertible<O (*)[], C (*)[]>::value, int>::type = 0>
constexpr Span(const Span<O>& other) noexcept : m_data(other.m_data), m_size(other.m_size) {}
/** Default copy constructor. */
constexpr Span(const Span&) noexcept = default;
/** Default assignment operator. */
Span& operator=(const Span& other) noexcept = default;
/** Construct a Span from an array. This matches the corresponding C++20 std::span constructor. */
template <int N>
constexpr Span(C (&a)[N]) noexcept : m_data(a), m_size(N) {}
/** Construct a Span for objects with .data() and .size() (std::string, std::array, std::vector, ...).
*
* This implements a subset of the functionality provided by the C++20 std::span range-based constructor.
*
* To prevent surprises, only Spans for constant value types are supported when passing in temporaries.
* Note that this restriction does not exist when converting arrays or other Spans (see above).
*/
template <typename V>
constexpr Span(V& other SPAN_ATTR_LIFETIMEBOUND,
typename std::enable_if<!is_Span<V>::value &&
std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value &&
std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr)
: m_data(other.data()), m_size(other.size()){}
template <typename V>
constexpr Span(const V& other SPAN_ATTR_LIFETIMEBOUND,
typename std::enable_if<!is_Span<V>::value &&
std::is_convertible<typename std::remove_pointer<decltype(std::declval<const V&>().data())>::type (*)[], C (*)[]>::value &&
std::is_convertible<decltype(std::declval<const V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr)
: m_data(other.data()), m_size(other.size()){}
constexpr C* data() const noexcept { return m_data; }
constexpr C* begin() const noexcept { return m_data; }
constexpr C* end() const noexcept { return m_data + m_size; }
CONSTEXPR_IF_NOT_DEBUG C& front() const noexcept
{
ASSERT_IF_DEBUG(size() > 0);
return m_data[0];
}
CONSTEXPR_IF_NOT_DEBUG C& back() const noexcept
{
ASSERT_IF_DEBUG(size() > 0);
return m_data[m_size - 1];
}
constexpr std::size_t size() const noexcept { return m_size; }
constexpr bool empty() const noexcept { return size() == 0; }
CONSTEXPR_IF_NOT_DEBUG C& operator[](std::size_t pos) const noexcept
{
ASSERT_IF_DEBUG(size() > pos);
return m_data[pos];
}
CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset) const noexcept
{
ASSERT_IF_DEBUG(size() >= offset);
return Span<C>(m_data + offset, m_size - offset);
}
CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset, std::size_t count) const noexcept
{
ASSERT_IF_DEBUG(size() >= offset + count);
return Span<C>(m_data + offset, count);
}
CONSTEXPR_IF_NOT_DEBUG Span<C> first(std::size_t count) const noexcept
{
ASSERT_IF_DEBUG(size() >= count);
return Span<C>(m_data, count);
}
CONSTEXPR_IF_NOT_DEBUG Span<C> last(std::size_t count) const noexcept
{
ASSERT_IF_DEBUG(size() >= count);
return Span<C>(m_data + m_size - count, count);
}
friend constexpr bool operator==(const Span& a, const Span& b) noexcept { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); }
friend constexpr bool operator!=(const Span& a, const Span& b) noexcept { return !(a == b); }
friend constexpr bool operator<(const Span& a, const Span& b) noexcept { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); }
friend constexpr bool operator<=(const Span& a, const Span& b) noexcept { return !(b < a); }
friend constexpr bool operator>(const Span& a, const Span& b) noexcept { return (b < a); }
friend constexpr bool operator>=(const Span& a, const Span& b) noexcept { return !(a < b); }
template <typename O> friend class Span;
};
// MakeSpan helps constructing a Span of the right type automatically.
/** MakeSpan for arrays: */
template <typename A, int N> Span<A> constexpr MakeSpan(A (&a)[N]) { return Span<A>(a, N); }
/** MakeSpan for temporaries / rvalue references, only supporting const output. */
template <typename V> constexpr auto MakeSpan(V&& v SPAN_ATTR_LIFETIMEBOUND) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); }
/** MakeSpan for (lvalue) references, supporting mutable output. */
template <typename V> constexpr auto MakeSpan(V& v SPAN_ATTR_LIFETIMEBOUND) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; }
/** Pop the last element off a span, and return a reference to that element. */
template <typename T>
T& SpanPopBack(Span<T>& span)
{
size_t size = span.size();
ASSERT_IF_DEBUG(size > 0);
T& back = span[size - 1];
span = Span<T>(span.data(), size - 1);
return back;
}
// Helper functions to safely cast to unsigned char pointers.
inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; }
inline unsigned char* UCharCast(unsigned char* c) { return c; }
inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; }
inline const unsigned char* UCharCast(const unsigned char* c) { return c; }
// Helper function to safely convert a Span to a Span<[const] unsigned char>.
template <typename T> constexpr auto UCharSpanCast(Span<T> s) -> Span<typename std::remove_pointer<decltype(UCharCast(s.data()))>::type> { return {UCharCast(s.data()), s.size()}; }
/** Like MakeSpan, but for (const) unsigned char member types only. Only works for (un)signed char containers. */
template <typename V> constexpr auto MakeUCharSpan(V&& v) -> decltype(UCharSpanCast(MakeSpan(std::forward<V>(v)))) { return UCharSpanCast(MakeSpan(std::forward<V>(v))); }
#endif

15
src/stratum.cpp

@ -24,7 +24,7 @@
#include "txmempool.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <univalue.h>
#include <algorithm> // for std::reverse
#include <string>
@ -114,12 +114,19 @@ namespace { // better to use anonymous namespace for helper routines
static bool InitStratumAllowList(std::vector<CSubNet>& allowed_subnets)
{
allowed_subnets.clear();
allowed_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
allowed_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
CNetAddr localv4, localv6;
LookupHost("127.0.0.1", localv4, false);
LookupHost("::1", localv6, false);
allowed_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet
allowed_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost
if (mapMultiArgs.count("-stratumallowip")) {
const std::vector<std::string>& vAllow = mapMultiArgs["-stratumallowip"];
for(const std::string& strAllow : vAllow) {
CSubNet subnet(strAllow);
CNetAddr netaddr;
// todo: v6
LookupHost(strAllow.c_str(), netaddr, false);
CSubNet subnet(netaddr);
if (!subnet.IsValid()) {
uiInterface.ThreadSafeMessageBox(
strprintf("Invalid -stratumallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),

96
src/streams.h

@ -77,6 +77,8 @@ public:
int GetVersion() const { return nVersion; }
int GetType() const { return nType; }
size_t size() const { return stream->size(); }
void ignore(size_t size) { return stream->ignore(size); }
};
template<typename S>
@ -85,6 +87,70 @@ OverrideStream<S> WithVersion(S* s, int nVersion)
return OverrideStream<S>(s, s->GetType(), nVersion);
}
/* Minimal stream for overwriting and/or appending to an existing byte vector
*
* The referenced vector will grow as necessary
*/
class CVectorWriter
{
public:
/*
* @param[in] nTypeIn Serialization Type
* @param[in] nVersionIn Serialization Version (including any flags)
* @param[in] vchDataIn Referenced byte vector to overwrite/append
* @param[in] nPosIn Starting position. Vector index where writes should start. The vector will initially
* grow as necessary to max(nPosIn, vec.size()). So to append, use vec.size().
*/
CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn)
{
if(nPos > vchData.size())
vchData.resize(nPos);
}
/*
* (other params same as above)
* @param[in] args A list of items to serialize starting at nPosIn.
*/
template <typename... Args>
CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn, Args&&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn)
{
::SerializeMany(*this, std::forward<Args>(args)...);
}
void write(const char* pch, size_t nSize)
{
assert(nPos <= vchData.size());
size_t nOverwrite = std::min(nSize, vchData.size() - nPos);
if (nOverwrite) {
memcpy(vchData.data() + nPos, reinterpret_cast<const unsigned char*>(pch), nOverwrite);
}
if (nOverwrite < nSize) {
vchData.insert(vchData.end(), reinterpret_cast<const unsigned char*>(pch) + nOverwrite, reinterpret_cast<const unsigned char*>(pch) + nSize);
}
nPos += nSize;
}
template<typename T>
CVectorWriter& operator<<(const T& obj)
{
// Serialize to this stream
::Serialize(*this, obj);
return (*this);
}
int GetVersion() const
{
return nVersion;
}
int GetType() const
{
return nType;
}
private:
const int nType;
const int nVersion;
std::vector<unsigned char>& vchData;
size_t nPos;
};
/** Double ended buffer combining vector and stream-like interfaces.
*
* >> and << read and write unformatted data using the above serialization templates.
@ -355,7 +421,7 @@ public:
}
template<typename T>
CBaseDataStream& operator>>(T& obj)
CBaseDataStream& operator>>(T&& obj)
{
// Unserialize from this stream
::Unserialize(*this, obj);
@ -547,19 +613,20 @@ protected:
readNow = nAvail;
if (readNow == 0)
return false;
size_t read = fread((void*)&vchBuf[pos], 1, readNow, src);
if (read == 0) {
size_t nBytes = fread((void*)&vchBuf[pos], 1, readNow, src);
if (nBytes == 0) {
throw std::ios_base::failure(feof(src) ? "CBufferedFile::Fill: end of file" : "CBufferedFile::Fill: fread failed");
} else {
nSrcPos += read;
return true;
}
nSrcPos += nBytes;
return true;
}
public:
CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) :
nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0)
{
if (nRewindIn >= nBufSize)
throw std::ios_base::failure("Rewind limit must be less than buffer size");
src = fileIn;
}
@ -594,8 +661,6 @@ public:
if (nSize + nReadPos > nReadLimit)
throw std::ios_base::failure("Read attempted past buffer limit");
if (nSize + nRewind > vchBuf.size())
throw std::ios_base::failure("Read larger than buffer size");
while (nSize > 0) {
if (nReadPos == nSrcPos)
Fill();
@ -619,16 +684,19 @@ public:
// rewind to a given reading position
bool SetPos(uint64_t nPos) {
nReadPos = nPos;
if (nReadPos + nRewind < nSrcPos) {
nReadPos = nSrcPos - nRewind;
size_t bufsize = vchBuf.size();
if (nPos + bufsize < nSrcPos) {
// rewinding too far, rewind as far as possible
nReadPos = nSrcPos - bufsize;
return false;
} else if (nReadPos > nSrcPos) {
}
if (nPos > nSrcPos) {
// can't go this far forward, go as far as possible
nReadPos = nSrcPos;
return false;
} else {
return true;
}
nReadPos = nPos;
return true;
}
bool Seek(uint64_t nPos) {

2
src/sync.cpp

@ -20,7 +20,7 @@
#include "sync.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <stdio.h>
#include <boost/foreach.hpp>
#include <boost/thread.hpp>

2
src/test-hush/test_addrman.cpp

@ -12,7 +12,7 @@
#include "netbase.h"
#include "chainparams.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#define NODE_NONE 0

2
src/test-hush/testutils.cpp

@ -14,7 +14,7 @@
#include "rpc/protocol.h"
#include "txdb.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "utiltime.h"
#include "consensus/validation.h"
#include "primitives/transaction.h"

2
src/test/base32_tests.cpp

@ -3,7 +3,7 @@
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include <boost/test/unit_test.hpp>

2
src/test/base58_tests.cpp

@ -15,7 +15,7 @@
#include "test/test_bitcoin.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <univalue.h>

2
src/test/base64_tests.cpp

@ -3,7 +3,7 @@
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include <boost/test/unit_test.hpp>

2
src/test/bip32_tests.cpp

@ -9,7 +9,7 @@
#include "key_io.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include <string>

2
src/test/bloom_tests.cpp

@ -14,7 +14,7 @@
#include "streams.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include <vector>

2
src/test/coins_tests.cpp

@ -7,7 +7,7 @@
#include "random.h"
#include "script/standard.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include "consensus/validation.h"
#include "main.h"

2
src/test/convertbits_tests.cpp

@ -3,7 +3,7 @@
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <utilstrencodings.h>
#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <zcash/NoteEncryption.hpp>

2
src/test/crypto_tests.cpp

@ -10,7 +10,7 @@
#include "crypto/hmac_sha256.h"
#include "crypto/hmac_sha512.h"
#include "random.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include <vector>

2
src/test/hash_tests.cpp

@ -4,7 +4,7 @@
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "hash.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include <vector>

2
src/test/key_tests.cpp

@ -9,7 +9,7 @@
#include "script/script.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include "zcash/Address.hpp"
#include <string>

11
src/test/netbase_tests.cpp

@ -121,17 +121,6 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
BOOST_CHECK(TestParse(":::", ""));
}
BOOST_AUTO_TEST_CASE(onioncat_test)
{
// values from https://web.archive.org/web/20121122003543/http://www.cypherpunk.at/onioncat/wiki/OnionCat
CNetAddr addr1("5wyqrzbvrdsumnok.onion");
CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca");
BOOST_CHECK(addr1 == addr2);
BOOST_CHECK(addr1.IsTor());
BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
BOOST_CHECK(addr1.IsRoutable());
}
BOOST_AUTO_TEST_CASE(subnet_test)
{
BOOST_CHECK(CSubNet("1.2.3.0/24") == CSubNet("1.2.3.0/255.255.255.0"));

2
src/test/rpc_tests.cpp

@ -7,7 +7,7 @@
#include "rpc/client.h"
#include "key_io.h"
#include "netbase.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "test/test_bitcoin.h"
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>

2
src/test/serialize_tests.cpp

@ -7,7 +7,7 @@
#include "streams.h"
#include "hash.h"
#include "test/test_bitcoin.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <array>
#include <stdint.h>

2
src/test/util_tests.cpp

@ -8,7 +8,7 @@
#include "primitives/transaction.h"
#include "random.h"
#include "sync.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "utilmoneystr.h"
#include "test/test_bitcoin.h"
#include <stdint.h>

2
src/timedata.cpp

@ -25,7 +25,7 @@
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
CTimeWarning timeWarning;

80
src/torcontrol.cpp

@ -5,9 +5,12 @@
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "torcontrol.h"
#include "utilstrencodings.h"
#include "chainparams.h"
#include "util/strencodings.h"
#include "net.h"
#include "util.h"
#include "util/readwritefile.h"
#include "utiltime.h"
#include "crypto/hmac_sha256.h"
#include <vector>
#include <deque>
@ -133,7 +136,7 @@ TorControlConnection::~TorControlConnection()
void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
{
TorControlConnection *self = (TorControlConnection*)ctx;
TorControlConnection *self = static_cast<TorControlConnection*>(ctx);
struct evbuffer *input = bufferevent_get_input(bev);
size_t n_read_out = 0;
char *line;
@ -178,7 +181,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ctx)
{
TorControlConnection *self = (TorControlConnection*)ctx;
TorControlConnection *self = static_cast<TorControlConnection*>(ctx);
if (what & BEV_EVENT_CONNECTED) {
LogPrint("tor", "tor: Successfully connected!\n");
self->connected(*self);
@ -356,52 +359,6 @@ static std::map<std::string,std::string> ParseTorReplyMapping(const std::string
return mapping;
}
/** Read full contents of a file and return them in a std::string.
* Returns a pair <status, string>.
* If an error occured, status will be false, otherwise status will be true and the data will be returned in string.
*
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/
static std::pair<bool,std::string> ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits<size_t>::max())
{
FILE *f = fopen(filename.c_str(), "rb");
if (f == NULL)
return std::make_pair(false,"");
std::string retval;
char buffer[128];
size_t n;
while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) {
// Check for reading errors so we don't return any data if we couldn't
// read the entire file (or up to maxsize)
if (ferror(f)) {
fclose(f);
return std::make_pair(false,"");
}
retval.append(buffer, buffer+n);
if (retval.size() > maxsize)
break;
}
fclose(f);
return std::make_pair(true,retval);
}
/** Write contents of std::string to a file.
* @return true on success.
*/
static bool WriteBinaryFile(const std::string &filename, const std::string &data)
{
FILE *f = fopen(filename.c_str(), "wb");
if (f == NULL)
return false;
if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
fclose(f);
return false;
}
fclose(f);
return true;
}
/****** Bitcoin specific TorController implementation ********/
/** Controller that connects to Tor control socket, authenticate, then create
@ -502,8 +459,8 @@ void TorController::add_onion_cb(TorControlConnection& conn, const TorControlRep
return;
}
service = CService(service_id+".onion", GetListenPort(), false);
LogPrintf("tor: Got service ID %s, advertizing service %s\n", service_id, service.ToString());
service = LookupNumeric(std::string(service_id+".onion").c_str(), GetListenPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile());
} else {
@ -526,14 +483,16 @@ void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& r
// Now that we know Tor is running setup the proxy for onion addresses
// if -onion isn't set to something else.
if (GetArg("-onion", "") == "") {
proxyType addrOnion = proxyType(CService("127.0.0.1", 9050), true);
CService resolved(LookupNumeric("127.0.0.1", 9050));
proxyType addrOnion = proxyType(resolved, true);
SetProxy(NET_ONION, addrOnion);
SetLimited(NET_ONION, false);
SetReachable(NET_ONION, true);
}
// Finally - now create the service
if (private_key.empty()) // No private key, generate one
private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214
if (private_key.empty()) { // No private key, generate one
private_key = "NEW:ED25519-V3"; // Explicitly request key type - see issue #9214
}
// Request hidden service, redirect port.
// Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient
// choice. TODO; refactor the shutdown sequence some day.
@ -564,10 +523,10 @@ static std::vector<uint8_t> ComputeResponse(const std::string &key, const std::v
{
CHMAC_SHA256 computeHash((const uint8_t*)key.data(), key.size());
std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
computeHash.Write(begin_ptr(cookie), cookie.size());
computeHash.Write(begin_ptr(clientNonce), clientNonce.size());
computeHash.Write(begin_ptr(serverNonce), serverNonce.size());
computeHash.Finalize(begin_ptr(computedHash));
computeHash.Write(cookie.data(), cookie.size());
computeHash.Write(clientNonce.data(), clientNonce.size());
computeHash.Write(serverNonce.data(), serverNonce.size());
computeHash.Finalize(computedHash.data());
return computedHash;
}
@ -724,7 +683,7 @@ std::string TorController::GetPrivateKeyFile()
void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
{
TorController *self = (TorController*)arg;
TorController *self = static_cast<TorController*>(arg);
self->Reconnect();
}
@ -772,4 +731,3 @@ void StopTorControl()
gBase = 0;
}
}

5
src/torcontrol.h

@ -12,10 +12,7 @@
#include "scheduler.h"
extern const std::string DEFAULT_TOR_CONTROL;
// Most users don't have Tor, those that do can turn it on
// This help reduce CPU usage, thread contention and helps
// low resource devices
static const bool DEFAULT_LISTEN_ONION = false;
static const bool DEFAULT_LISTEN_ONION = true;
void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler);
void InterruptTorControl();

2
src/uint256.cpp

@ -20,7 +20,7 @@
******************************************************************************/
#include "uint256.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <stdio.h>
#include <string.h>

2
src/util.cpp

@ -29,7 +29,7 @@
#include "random.h"
#include "serialize.h"
#include "sync.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "utiltime.h"
#include "hush_defs.h"

47
src/util/readwritefile.cpp

@ -0,0 +1,47 @@
// Copyright (c) 2015-2020 The Bitcoin Core developers
// Copyright (c) 2017 The Zcash developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <fs.h>
#include <limits>
#include <stdio.h>
#include <string>
#include <utility>
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
{
FILE *f = fsbridge::fopen(filename, "rb");
if (f == nullptr)
return std::make_pair(false,"");
std::string retval;
char buffer[128];
do {
const size_t n = fread(buffer, 1, sizeof(buffer), f);
// Check for reading errors so we don't return any data if we couldn't
// read the entire file (or up to maxsize)
if (ferror(f)) {
fclose(f);
return std::make_pair(false,"");
}
retval.append(buffer, buffer+n);
} while (!feof(f) && retval.size() <= maxsize);
fclose(f);
return std::make_pair(true,retval);
}
bool WriteBinaryFile(const fs::path &filename, const std::string &data)
{
FILE *f = fsbridge::fopen(filename, "wb");
if (f == nullptr)
return false;
if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
fclose(f);
return false;
}
if (fclose(f) != 0) {
return false;
}
return true;
}

29
src/util/readwritefile.h

@ -0,0 +1,29 @@
// Copyright (c) 2015-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef BITCOIN_UTIL_READWRITEFILE_H
#define BITCOIN_UTIL_READWRITEFILE_H
#include <fs.h>
#include <limits>
#include <string>
#include <utility>
/** Read full contents of a file and return them in a std::string.
* Returns a pair <status, string>.
* If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
*
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max());
/** Write contents of std::string to a file.
* @return true on success.
*/
bool WriteBinaryFile(const fs::path &filename, const std::string &data);
#endif /* BITCOIN_UTIL_READWRITEFILE_H */

328
src/util/sock.cpp

@ -0,0 +1,328 @@
// Copyright (c) 2020-2021 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <compat.h>
#include <tinyformat.h>
#include <util/sock.h>
#include <utiltime.h>
#include <util.h>
#include <codecvt>
#include <cwchar>
#include <locale>
#include <stdexcept>
#include <string>
#ifdef USE_POLL
#include <poll.h>
#endif
static inline bool IOErrorIsPermanent(int err)
{
return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
}
Sock::Sock() : m_socket(INVALID_SOCKET) {}
Sock::Sock(SOCKET s) : m_socket(s) {}
Sock::Sock(Sock&& other)
{
m_socket = other.m_socket;
other.m_socket = INVALID_SOCKET;
}
Sock::~Sock() { Reset(); }
Sock& Sock::operator=(Sock&& other)
{
Reset();
m_socket = other.m_socket;
other.m_socket = INVALID_SOCKET;
return *this;
}
SOCKET Sock::Get() const { return m_socket; }
SOCKET Sock::Release()
{
const SOCKET s = m_socket;
m_socket = INVALID_SOCKET;
return s;
}
void Sock::Reset() { CloseSocket(m_socket); }
ssize_t Sock::Send(const void* data, size_t len, int flags) const
{
return send(m_socket, static_cast<const char*>(data), len, flags);
}
ssize_t Sock::Recv(void* buf, size_t len, int flags) const
{
return recv(m_socket, static_cast<char*>(buf), len, flags);
}
int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
{
return connect(m_socket, addr, addr_len);
}
int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
{
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
}
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
#ifdef USE_POLL
pollfd fd;
fd.fd = m_socket;
fd.events = 0;
if (requested & RECV) {
fd.events |= POLLIN;
}
if (requested & SEND) {
fd.events |= POLLOUT;
}
if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) {
return false;
}
if (occurred != nullptr) {
*occurred = 0;
if (fd.revents & POLLIN) {
*occurred |= RECV;
}
if (fd.revents & POLLOUT) {
*occurred |= SEND;
}
}
return true;
#else
if (!IsSelectableSocket(m_socket)) {
return false;
}
fd_set fdset_recv;
fd_set fdset_send;
FD_ZERO(&fdset_recv);
FD_ZERO(&fdset_send);
if (requested & RECV) {
FD_SET(m_socket, &fdset_recv);
}
if (requested & SEND) {
FD_SET(m_socket, &fdset_send);
}
timeval timeout_struct = MillisToTimeval(timeout.count());
if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) {
return false;
}
if (occurred != nullptr) {
*occurred = 0;
if (FD_ISSET(m_socket, &fdset_recv)) {
*occurred |= RECV;
}
if (FD_ISSET(m_socket, &fdset_send)) {
*occurred |= SEND;
}
}
return true;
#endif /* USE_POLL */
}
void Sock::SendComplete(const std::string& data,
std::chrono::milliseconds timeout) const
{
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
size_t sent{0};
for (;;) {
const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
if (ret > 0) {
sent += static_cast<size_t>(ret);
if (sent == data.size()) {
break;
}
} else {
const int err{WSAGetLastError()};
if (IOErrorIsPermanent(err)) {
throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
}
}
const auto now = GetTime<std::chrono::milliseconds>();
if (now >= deadline) {
throw std::runtime_error(strprintf(
"Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
}
// Wait for a short while (or the socket to become ready for sending) before retrying
// if nothing was sent.
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
(void)Wait(wait_time, SEND);
}
}
std::string Sock::RecvUntilTerminator(uint8_t terminator,
std::chrono::milliseconds timeout,
size_t max_data) const
{
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
std::string data;
bool terminator_found{false};
// We must not consume any bytes past the terminator from the socket.
// One option is to read one byte at a time and check if we have read a terminator.
// However that is very slow. Instead, we peek at what is in the socket and only read
// as many bytes as possible without crossing the terminator.
// Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
// one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
// at a time is about 50 times slower.
for (;;) {
if (data.size() >= max_data) {
throw std::runtime_error(
strprintf("Received too many bytes without a terminator (%u)", data.size()));
}
char buf[512];
const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
switch (peek_ret) {
case -1: {
const int err{WSAGetLastError()};
if (IOErrorIsPermanent(err)) {
throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
}
break;
}
case 0:
throw std::runtime_error("Connection unexpectedly closed by peer");
default:
auto end = buf + peek_ret;
auto terminator_pos = std::find(buf, end, terminator);
terminator_found = terminator_pos != end;
const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
static_cast<size_t>(peek_ret)};
const ssize_t read_ret{Recv(buf, try_len, 0)};
if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
throw std::runtime_error(
strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
"peek claimed %u bytes are available",
read_ret, try_len, peek_ret));
}
// Don't include the terminator in the output.
const size_t append_len{terminator_found ? try_len - 1 : try_len};
data.append(buf, buf + append_len);
if (terminator_found) {
return data;
}
}
const auto now = GetTime<std::chrono::milliseconds>();
if (now >= deadline) {
throw std::runtime_error(strprintf(
"Receive timeout (received %u bytes without terminator before that)", data.size()));
}
// Wait for a short while (or the socket to become ready for reading) before retrying.
const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
(void)Wait(wait_time, RECV);
}
}
bool Sock::IsConnected(std::string& errmsg) const
{
if (m_socket == INVALID_SOCKET) {
errmsg = "not connected";
return false;
}
char c;
switch (Recv(&c, sizeof(c), MSG_PEEK)) {
case -1: {
const int err = WSAGetLastError();
if (IOErrorIsPermanent(err)) {
errmsg = NetworkErrorString(err);
return false;
}
return true;
}
case 0:
errmsg = "closed";
return false;
default:
return true;
}
}
#ifdef WIN32
std::string NetworkErrorString(int err)
{
wchar_t buf[256];
buf[0] = 0;
if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, ARRAYSIZE(buf), nullptr))
{
return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
}
else
{
return strprintf("Unknown error (%d)", err);
}
}
#else
std::string NetworkErrorString(int err)
{
char buf[256];
buf[0] = 0;
/* Too bad there are two incompatible implementations of the
* thread-safe strerror. */
const char *s;
#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
s = strerror_r(err, buf, sizeof(buf));
#else /* POSIX variant always returns message in buffer */
s = buf;
if (strerror_r(err, buf, sizeof(buf)))
buf[0] = 0;
#endif
return strprintf("%s (%d)", s, err);
}
#endif
bool CloseSocket(SOCKET& hSocket)
{
if (hSocket == INVALID_SOCKET)
return false;
#ifdef WIN32
int ret = closesocket(hSocket);
#else
int ret = close(hSocket);
#endif
if (ret) {
LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
}
hSocket = INVALID_SOCKET;
return ret != SOCKET_ERROR;
}

184
src/util/sock.h

@ -0,0 +1,184 @@
// Copyright (c) 2020-2021 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef BITCOIN_UTIL_SOCK_H
#define BITCOIN_UTIL_SOCK_H
#include <compat.h>
// #include <threadinterrupt.h>
#include <utiltime.h>
#include <chrono>
#include <string>
/**
* Maximum time to wait for I/O readiness.
* It will take up until this time to break off in case of an interruption.
*/
static constexpr auto MAX_WAIT_FOR_IO = 1000;
/**
* RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it
* contains a socket and closes it automatically when it goes out of scope.
*/
class Sock
{
public:
/**
* Default constructor, creates an empty object that does nothing when destroyed.
*/
Sock();
/**
* Take ownership of an existent socket.
*/
explicit Sock(SOCKET s);
/**
* Copy constructor, disabled because closing the same socket twice is undesirable.
*/
Sock(const Sock&) = delete;
/**
* Move constructor, grab the socket from another object and close ours (if set).
*/
Sock(Sock&& other);
/**
* Destructor, close the socket or do nothing if empty.
*/
virtual ~Sock();
/**
* Copy assignment operator, disabled because closing the same socket twice is undesirable.
*/
Sock& operator=(const Sock&) = delete;
/**
* Move assignment operator, grab the socket from another object and close ours (if set).
*/
virtual Sock& operator=(Sock&& other);
/**
* Get the value of the contained socket.
* @return socket or INVALID_SOCKET if empty
*/
virtual SOCKET Get() const;
/**
* Get the value of the contained socket and drop ownership. It will not be closed by the
* destructor after this call.
* @return socket or INVALID_SOCKET if empty
*/
virtual SOCKET Release();
/**
* Close if non-empty.
*/
virtual void Reset();
/**
* send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
virtual ssize_t Send(const void* data, size_t len, int flags) const;
/**
* recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
virtual ssize_t Recv(void* buf, size_t len, int flags) const;
/**
* connect(2) wrapper. Equivalent to `connect(this->Get(), addr, addrlen)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
/**
* getsockopt(2) wrapper. Equivalent to
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
virtual int GetSockOpt(int level,
int opt_name,
void* opt_val,
socklen_t* opt_len) const;
using Event = uint8_t;
/**
* If passed to `Wait()`, then it will wait for readiness to read from the socket.
*/
static constexpr Event RECV = 0b01;
/**
* If passed to `Wait()`, then it will wait for readiness to send to the socket.
*/
static constexpr Event SEND = 0b10;
/**
* Wait for readiness for input (recv) or output (send).
* @param[in] timeout Wait this much for at least one of the requested events to occur.
* @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`.
* @param[out] occurred If not nullptr and `true` is returned, then upon return this
* indicates which of the requested events occurred. A timeout is indicated by return
* value of `true` and `occurred` being set to 0.
* @return true on success and false otherwise
*/
virtual bool Wait(std::chrono::milliseconds timeout,
Event requested,
Event* occurred = nullptr) const;
/* Higher level, convenience, methods. These may throw. */
/**
* Send the given data, retrying on transient errors.
* @param[in] data Data to send.
* @param[in] timeout Timeout for the entire operation.
* @param[in] interrupt If this is signaled then the operation is canceled.
* @throws std::runtime_error if the operation cannot be completed. In this case only some of
* the data will be written to the socket.
*/
virtual void SendComplete(const std::string& data,
std::chrono::milliseconds timeout) const;
/**
* Read from socket until a terminator character is encountered. Will never consume bytes past
* the terminator from the socket.
* @param[in] terminator Character up to which to read from the socket.
* @param[in] timeout Timeout for the entire operation.
* @param[in] interrupt If this is signaled then the operation is canceled.
* @param[in] max_data The maximum amount of data (in bytes) to receive. If this many bytes
* are received and there is still no terminator, then this method will throw an exception.
* @return The data that has been read, without the terminating character.
* @throws std::runtime_error if the operation cannot be completed. In this case some bytes may
* have been consumed from the socket.
*/
virtual std::string RecvUntilTerminator(uint8_t terminator,
std::chrono::milliseconds timeout,
size_t max_data) const;
/**
* Check if still connected.
* @param[out] errmsg The error string, if the socket has been disconnected.
* @return true if connected
*/
virtual bool IsConnected(std::string& errmsg) const;
protected:
/**
* Contained socket. `INVALID_SOCKET` designates the object is empty.
*/
SOCKET m_socket;
};
/** Return readable error string for a network error code */
std::string NetworkErrorString(int err);
/** Close socket and set hSocket to INVALID_SOCKET */
bool CloseSocket(SOCKET& hSocket);
#endif // BITCOIN_UTIL_SOCK_H

68
src/util/spanparsing.cpp

@ -0,0 +1,68 @@
// Copyright (c) 2018-2019 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <util/spanparsing.h>
#include <span.h>
#include <string>
#include <vector>
namespace spanparsing {
bool Const(const std::string& str, Span<const char>& sp)
{
if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) {
sp = sp.subspan(str.size());
return true;
}
return false;
}
bool Func(const std::string& str, Span<const char>& sp)
{
if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) {
sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2);
return true;
}
return false;
}
Span<const char> Expr(Span<const char>& sp)
{
int level = 0;
auto it = sp.begin();
while (it != sp.end()) {
if (*it == '(' || *it == '{') {
++level;
} else if (level && (*it == ')' || *it == '}')) {
--level;
} else if (level == 0 && (*it == ')' || *it == '}' || *it == ',')) {
break;
}
++it;
}
Span<const char> ret = sp.first(it - sp.begin());
sp = sp.subspan(it - sp.begin());
return ret;
}
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
{
std::vector<Span<const char>> ret;
auto it = sp.begin();
auto start = it;
while (it != sp.end()) {
if (*it == sep) {
ret.emplace_back(start, it);
start = it + 1;
}
++it;
}
ret.emplace_back(start, it);
return ret;
}
} // namespace spanparsing

50
src/util/spanparsing.h

@ -0,0 +1,50 @@
// Copyright (c) 2018-2019 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef BITCOIN_UTIL_SPANPARSING_H
#define BITCOIN_UTIL_SPANPARSING_H
#include <span.h>
#include <string>
#include <vector>
namespace spanparsing {
/** Parse a constant.
*
* If sp's initial part matches str, sp is updated to skip that part, and true is returned.
* Otherwise sp is unmodified and false is returned.
*/
bool Const(const std::string& str, Span<const char>& sp);
/** Parse a function call.
*
* If sp's initial part matches str + "(", and sp ends with ")", sp is updated to be the
* section between the braces, and true is returned. Otherwise sp is unmodified and false
* is returned.
*/
bool Func(const std::string& str, Span<const char>& sp);
/** Extract the expression that sp begins with.
*
* This function will return the initial part of sp, up to (but not including) the first
* comma or closing brace, skipping ones that are surrounded by braces. So for example,
* for "foo(bar(1),2),3" the initial part "foo(bar(1),2)" will be returned. sp will be
* updated to skip the initial part that is returned.
*/
Span<const char> Expr(Span<const char>& sp);
/** Split a string on every instance of sep, returning a vector.
*
* If sep does not occur in sp, a singleton with the entirety of sp is returned.
*
* Note that this function does not care about braces, so splitting
* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
*/
std::vector<Span<const char>> Split(const Span<const char>& sp, char sep);
} // namespace spanparsing
#endif // BITCOIN_UTIL_SPANPARSING_H

655
src/util/strencodings.cpp

@ -0,0 +1,655 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "util/strencodings.h"
#include "util/string.h"
#include <tinyformat.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <errno.h>
#include <limits>
using namespace std;
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static const std::string SAFE_CHARS[] =
{
CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
};
std::string SanitizeString(const std::string& str, int rule)
{
std::string strResult;
for (std::string::size_type i = 0; i < str.size(); i++)
{
if (SAFE_CHARS[rule].find(str[i]) != std::string::npos)
strResult.push_back(str[i]);
}
return strResult;
}
string SanitizeFilename(const string& str)
{
/**
* safeChars chosen to restrict filename, keeping it simple to avoid cross-platform issues.
* http://stackoverflow.com/a/2306003
*/
static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890");
string strResult;
for (std::string::size_type i = 0; i < str.size(); i++)
{
if (safeChars.find(str[i]) != std::string::npos)
strResult.push_back(str[i]);
}
return strResult;
}
std::string HexInt(uint32_t val)
{
std::stringstream ss;
ss << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << val;
return ss.str();
}
uint32_t ParseHexToUInt32(const std::string& str) {
std::istringstream converter(str);
uint32_t value;
converter >> std::hex >> value;
return value;
}
const signed char p_util_hexdigit[256] =
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
signed char HexDigit(char c)
{
return p_util_hexdigit[(unsigned char)c];
}
bool IsHex(const std::string& str)
{
for(std::string::const_iterator it(str.begin()); it != str.end(); ++it)
{
if (HexDigit(*it) < 0)
return false;
}
return (str.size() > 0) && (str.size()%2 == 0);
}
bool IsHexNumber(const std::string& str)
{
size_t starting_location = 0;
if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') {
starting_location = 2;
}
for (const char c : str.substr(starting_location)) {
if (HexDigit(c) < 0) return false;
}
// Return false for empty string or "0x".
return (str.size() > starting_location);
}
std::vector<unsigned char> ParseHex(const char* psz)
{
// convert hex dump to vector
std::vector<unsigned char> vch;
while (true)
{
while (IsSpace(*psz))
psz++;
signed char c = HexDigit(*psz++);
if (c == (signed char)-1)
break;
unsigned char n = (c << 4);
c = HexDigit(*psz++);
if (c == (signed char)-1)
break;
n |= c;
vch.push_back(n);
}
return vch;
}
std::vector<unsigned char> ParseHex(const std::string& str)
{
return ParseHex(str.c_str());
}
void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
size_t colon = in.find_last_of(':');
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
bool fHaveColon = colon != in.npos;
bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos);
if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) {
int32_t n;
if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) {
in = in.substr(0, colon);
portOut = n;
}
}
if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']')
hostOut = in.substr(1, in.size()-2);
else
hostOut = in;
}
std::string EncodeBase64(Span<const unsigned char> input)
{
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string str;
str.reserve(((input.size() + 2) / 3) * 4);
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
while (str.size() % 4) str += '=';
return str;
}
std::string EncodeBase64(const unsigned char* pch, size_t len)
{
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string str;
str.reserve(((len + 2) / 3) * 4);
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len);
while (str.size() % 4) str += '=';
return str;
}
std::string EncodeBase64(const std::string& str)
{
return EncodeBase64((const unsigned char*)str.data(), str.size());
}
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid)
{
static const int decode64_table[256] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
const char* e = p;
std::vector<uint8_t> val;
val.reserve(strlen(p));
while (*p != 0) {
int x = decode64_table[(unsigned char)*p];
if (x == -1) break;
val.push_back(x);
++p;
}
std::vector<unsigned char> ret;
ret.reserve((val.size() * 3) / 4);
bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
const char* q = p;
while (valid && *p != 0) {
if (*p != '=') {
valid = false;
break;
}
++p;
}
valid = valid && (p - e) % 4 == 0 && p - q < 4;
if (pf_invalid) *pf_invalid = !valid;
return ret;
}
std::string DecodeBase64(const std::string& str, bool* pf_invalid)
{
if (!ValidAsCString(str)) {
if (pf_invalid) {
*pf_invalid = true;
}
return {};
}
std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size());
}
std::string EncodeBase32(Span<const unsigned char> input, bool pad)
{
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
std::string str;
str.reserve(((input.size() + 4) / 5) * 8);
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
if (pad) {
while (str.size() % 8) {
str += '=';
}
}
return str;
}
std::string EncodeBase32(const std::string& str, bool pad)
{
return EncodeBase32(MakeUCharSpan(str), pad);
}
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
{
static const int decode32_table[256] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2,
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
const char* e = p;
std::vector<uint8_t> val;
val.reserve(strlen(p));
while (*p != 0) {
int x = decode32_table[(unsigned char)*p];
if (x == -1) break;
val.push_back(x);
++p;
}
std::vector<unsigned char> ret;
ret.reserve((val.size() * 5) / 8);
bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
const char* q = p;
while (valid && *p != 0) {
if (*p != '=') {
valid = false;
break;
}
++p;
}
valid = valid && (p - e) % 8 == 0 && p - q < 8;
if (pf_invalid) *pf_invalid = !valid;
return ret;
}
std::string DecodeBase32(const std::string& str, bool* pf_invalid)
{
if (!ValidAsCString(str)) {
if (pf_invalid) {
*pf_invalid = true;
}
return {};
}
std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size());
}
static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
return false;
if (!ValidAsCString(str)) // No embedded NUL characters allowed
return false;
return true;
}
bool ParseInt32(const std::string& str, int32_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = nullptr;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int32_t)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int32_t>::min() &&
n <= std::numeric_limits<int32_t>::max();
}
bool ParseInt64(const std::string& str, int64_t *out)
{
if (!ParsePrechecks(str))
return false;
char *endp = nullptr;
errno = 0; // strtoll will not set errno if valid
long long int n = strtoll(str.c_str(), &endp, 10);
if(out) *out = (int64_t)n;
// Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *int64_t*.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int64_t>::min() &&
n <= std::numeric_limits<int64_t>::max();
}
bool ParseUInt8(const std::string& str, uint8_t *out)
{
uint32_t u32;
if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
return false;
}
if (out != nullptr) {
*out = static_cast<uint8_t>(u32);
}
return true;
}
bool ParseUInt32(const std::string& str, uint32_t *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
return false;
char *endp = nullptr;
errno = 0; // strtoul will not set errno if valid
unsigned long int n = strtoul(str.c_str(), &endp, 10);
if(out) *out = (uint32_t)n;
// Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n <= std::numeric_limits<uint32_t>::max();
}
bool ParseUInt64(const std::string& str, uint64_t *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
return false;
char *endp = nullptr;
errno = 0; // strtoull will not set errno if valid
unsigned long long int n = strtoull(str.c_str(), &endp, 10);
if(out) *out = (uint64_t)n;
// Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
// we still have to check that the returned value is within the range of an *uint64_t*.
return endp && *endp == 0 && !errno &&
n <= std::numeric_limits<uint64_t>::max();
}
bool ParseDouble(const std::string& str, double *out)
{
if (!ParsePrechecks(str))
return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false;
std::istringstream text(str);
text.imbue(std::locale::classic());
double result;
text >> result;
if(out) *out = result;
return text.eof() && !text.fail();
}
std::string FormatParagraph(const std::string& in, size_t width, size_t indent)
{
std::stringstream out;
size_t ptr = 0;
size_t indented = 0;
while (ptr < in.size())
{
size_t lineend = in.find_first_of('\n', ptr);
if (lineend == std::string::npos) {
lineend = in.size();
}
const size_t linelen = lineend - ptr;
const size_t rem_width = width - indented;
if (linelen <= rem_width) {
out << in.substr(ptr, linelen + 1);
ptr = lineend + 1;
indented = 0;
} else {
size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
if (finalspace == std::string::npos || finalspace < ptr) {
// No place to break; just include the entire word and move on
finalspace = in.find_first_of("\n ", ptr);
if (finalspace == std::string::npos) {
// End of the string, just add it and break
out << in.substr(ptr);
break;
}
}
out << in.substr(ptr, finalspace - ptr) << "\n";
if (in[finalspace] == '\n') {
indented = 0;
} else if (indent) {
out << std::string(indent, ' ');
indented = indent;
}
ptr = finalspace + 1;
}
}
return out.str();
}
std::string i64tostr(int64_t n)
{
return strprintf("%d", n);
}
std::string itostr(int n)
{
return strprintf("%d", n);
}
int64_t atoi64(const char* psz)
{
#ifdef _MSC_VER
return _atoi64(psz);
#else
return strtoll(psz, nullptr, 10);
#endif
}
int64_t atoi64(const std::string& str)
{
#ifdef _MSC_VER
return _atoi64(str.c_str());
#else
return strtoll(str.c_str(), nullptr, 10);
#endif
}
int atoi(const std::string& str)
{
return atoi(str.c_str());
}
/** Upper bound for mantissa.
* 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
* Larger integers cannot consist of arbitrary combinations of 0-9:
*
* 999999999999999999 1^18-1
* 9223372036854775807 (1<<63)-1 (max int64_t)
* 9999999999999999999 1^19-1 (would overflow)
*/
static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
/** Helper function for ParseFixedPoint */
static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
{
if(ch == '0')
++mantissa_tzeros;
else {
for (int i=0; i<=mantissa_tzeros; ++i) {
if (mantissa > (UPPER_BOUND / 10LL))
return false; /* overflow */
mantissa *= 10;
}
mantissa += ch - '0';
mantissa_tzeros = 0;
}
return true;
}
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
{
int64_t mantissa = 0;
int64_t exponent = 0;
int mantissa_tzeros = 0;
bool mantissa_sign = false;
bool exponent_sign = false;
int ptr = 0;
int end = val.size();
int point_ofs = 0;
if (ptr < end && val[ptr] == '-') {
mantissa_sign = true;
++ptr;
}
if (ptr < end)
{
if (val[ptr] == '0') {
/* pass single 0 */
++ptr;
} else if (val[ptr] >= '1' && val[ptr] <= '9') {
while (ptr < end && IsDigit(val[ptr])) {
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
return false; /* overflow */
++ptr;
}
} else return false; /* missing expected digit */
} else return false; /* empty string or loose '-' */
if (ptr < end && val[ptr] == '.')
{
++ptr;
if (ptr < end && IsDigit(val[ptr]))
{
while (ptr < end && IsDigit(val[ptr])) {
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
return false; /* overflow */
++ptr;
++point_ofs;
}
} else return false; /* missing expected digit */
}
if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
{
++ptr;
if (ptr < end && val[ptr] == '+')
++ptr;
else if (ptr < end && val[ptr] == '-') {
exponent_sign = true;
++ptr;
}
if (ptr < end && IsDigit(val[ptr])) {
while (ptr < end && IsDigit(val[ptr])) {
if (exponent > (UPPER_BOUND / 10LL))
return false; /* overflow */
exponent = exponent * 10 + val[ptr] - '0';
++ptr;
}
} else return false; /* missing expected digit */
}
if (ptr != end)
return false; /* trailing garbage */
/* finalize exponent */
if (exponent_sign)
exponent = -exponent;
exponent = exponent - point_ofs + mantissa_tzeros;
/* finalize mantissa */
if (mantissa_sign)
mantissa = -mantissa;
/* convert to one 64-bit fixed-point value */
exponent += decimals;
if (exponent < 0)
return false; /* cannot represent values smaller than 10^-decimals */
if (exponent >= 18)
return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
for (int i=0; i < exponent; ++i) {
if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
return false; /* overflow */
mantissa *= 10;
}
if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
return false; /* overflow */
if (amount_out)
*amount_out = mantissa;
return true;
}
std::string ToLower(const std::string& str)
{
std::string r;
for (auto ch : str) r += ToLower((unsigned char)ch);
return r;
}
std::string ToUpper(const std::string& str)
{
std::string r;
for (auto ch : str) r += ToUpper((unsigned char)ch);
return r;
}
std::string Capitalize(std::string str)
{
if (str.empty()) return str;
str[0] = ToUpper(str.front());
return str;
}
std::string HexStr(const Span<const uint8_t> s)
{
std::string rv;
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
rv.reserve(s.size() * 2);
for (uint8_t v: s) {
rv.push_back(hexmap[v >> 4]);
rv.push_back(hexmap[v & 15]);
}
return rv;
}

302
src/util/strencodings.h

@ -0,0 +1,302 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
/**
* Utilities for converting data from/to strings.
*/
#ifndef BITCOIN_UTIL_STRENCODINGS_H
#define BITCOIN_UTIL_STRENCODINGS_H
#include "attributes.h"
#include "span.h"
#include <cstdint>
#include <iterator>
#include <string>
#include <vector>
#define BEGIN(a) ((char*)&(a))
#define END(a) ((char*)&((&(a))[1]))
#define UBEGIN(a) ((unsigned char*)&(a))
#define UEND(a) ((unsigned char*)&((&(a))[1]))
#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0]))
/** This is needed because the foreach macro can't get over the comma in pair<t1, t2> */
#define PAIRTYPE(t1, t2) std::pair<t1, t2>
/** Used by SanitizeString() */
enum SafeChars
{
SAFE_CHARS_DEFAULT, //!< The full set of allowed chars
SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset
SAFE_CHARS_FILENAME, //!< Chars allowed in filenames
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
};
std::string SanitizeFilename(const std::string& str);
/**
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email
* addresses, but avoid anything even possibly remotely dangerous like & or >
* @param[in] str The string to sanitize
* @param[in] rule The set of safe chars to choose (default: least restrictive)
* @return A new string without unsafe chars
*/
std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT);
std::string HexInt(uint32_t val);
uint32_t ParseHexToUInt32(const std::string& str);
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
signed char HexDigit(char c);
/* Returns true if each character in str is a hex character, and has an even
* number of hex digits.*/
bool IsHex(const std::string& str);
/**
* Return true if the string is a hex number, optionally prefixed with "0x"
*/
bool IsHexNumber(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr);
std::string EncodeBase64(Span<const unsigned char> input);
std::string EncodeBase64(const unsigned char* pch, size_t len);
std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
/**
* Base32 encode.
* If `pad` is true, then the output will be padded with '=' so that its length
* is a multiple of 8.
*/
std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
/**
* Base32 encode.
* If `pad` is true, then the output will be padded with '=' so that its length
* is a multiple of 8.
*/
std::string EncodeBase32(const std::string& str, bool pad = true);
void SplitHostPort(std::string in, int& portOut, std::string& hostOut);
std::string i64tostr(int64_t n);
std::string itostr(int n);
int64_t atoi64(const char* psz);
int64_t atoi64(const std::string& str);
int atoi(const std::string& str);
/**
* Tests if the given character is a decimal digit.
* @param[in] c character to test
* @return true if the argument is a decimal digit; otherwise false.
*/
constexpr bool IsDigit(char c)
{
return c >= '0' && c <= '9';
}
/**
* Tests if the given character is a whitespace character. The whitespace characters
* are: space, form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
* tab ('\t'), and vertical tab ('\v').
*
* This function is locale independent. Under the C locale this function gives the
* same result as std::isspace.
*
* @param[in] c character to test
* @return true if the argument is a whitespace character; otherwise false
*/
constexpr inline bool IsSpace(char c) noexcept {
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
}
/**
* Convert string to signed 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseInt32(const std::string& str, int32_t *out);
/**
* Convert string to signed 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseInt64(const std::string& str, int64_t *out);
/**
* Convert decimal string to unsigned 8-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseUInt8(const std::string& str, uint8_t *out);
/**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseUInt32(const std::string& str, uint32_t *out);
/**
* Convert decimal string to unsigned 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseUInt64(const std::string& str, uint64_t *out);
/**
* Convert string to double with strict parse error feedback.
* @returns true if the entire string could be parsed as valid double,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
bool ParseDouble(const std::string& str, double *out);
template<typename T>
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
{
std::string rv;
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
rv.reserve((itend-itbegin)*3);
for(T it = itbegin; it < itend; ++it)
{
unsigned char val = (unsigned char)(*it);
if(fSpaces && it != itbegin)
rv.push_back(' ');
rv.push_back(hexmap[val>>4]);
rv.push_back(hexmap[val&15]);
}
return rv;
}
template<typename T>
inline std::string HexStr(const T& vch, bool fSpaces=false)
{
return HexStr(vch.begin(), vch.end(), fSpaces);
}
/**
* Convert a span of bytes to a lower-case hexadecimal string.
*/
std::string HexStr(const Span<const uint8_t> s);
inline std::string HexStr(const Span<const char> s) { return HexStr(MakeUCharSpan(s)); }
/**
* Format a paragraph of text to a fixed width, adding spaces for
* indentation to any added line.
*/
std::string FormatParagraph(const std::string& in, size_t width = 79, size_t indent = 0);
/**
* Timing-attack-resistant comparison.
* Takes time proportional to length
* of first argument.
*/
template <typename T>
bool TimingResistantEqual(const T& a, const T& b)
{
if (b.size() == 0) return a.size() == 0;
size_t accumulator = a.size() ^ b.size();
for (size_t i = 0; i < a.size(); i++)
accumulator |= a[i] ^ b[i%b.size()];
return accumulator == 0;
}
/** Parse number as fixed point according to JSON number syntax.
* See http://json.org/number.gif
* @returns true on success, false on error.
* @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger.
*/
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad, typename O, typename I>
bool ConvertBits(const O& outfn, I it, I end) {
size_t acc = 0;
size_t bits = 0;
constexpr size_t maxv = (1 << tobits) - 1;
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
while (it != end) {
acc = ((acc << frombits) | *it) & max_acc;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
outfn((acc >> bits) & maxv);
}
++it;
}
if (pad) {
if (bits) outfn((acc << (tobits - bits)) & maxv);
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
return false;
}
return true;
}
/**
* Converts the given character to its lowercase equivalent.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] c the character to convert to lowercase.
* @return the lowercase equivalent of c; or the argument
* if no conversion is possible.
*/
constexpr char ToLower(char c)
{
return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c);
}
/**
* Returns the lowercase equivalent of the given string.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to convert to lowercase.
* @returns lowercased equivalent of str
*/
std::string ToLower(const std::string& str);
/**
* Converts the given character to its uppercase equivalent.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] c the character to convert to uppercase.
* @return the uppercase equivalent of c; or the argument
* if no conversion is possible.
*/
constexpr char ToUpper(char c)
{
return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c);
}
/**
* Returns the uppercase equivalent of the given string.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to convert to uppercase.
* @returns UPPERCASED EQUIVALENT OF str
*/
std::string ToUpper(const std::string& str);
/**
* Capitalizes the first character of the given string.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to capitalize.
* @returns string with the first letter capitalized.
*/
std::string Capitalize(std::string str);
#endif // BITCOIN_UTIL_STRENCODINGS_H

6
src/util/string.cpp

@ -0,0 +1,6 @@
// Copyright (c) 2019 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "util/string.h"

99
src/util/string.h

@ -0,0 +1,99 @@
// Copyright (c) 2019-2020 The Bitcoin Core developers
// Copyright (c) 2016-2022 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef BITCOIN_UTIL_STRING_H
#define BITCOIN_UTIL_STRING_H
#include "attributes.h"
#include <algorithm>
#include <array>
#include <cstring>
#include <locale>
#include <sstream>
#include <string>
#include <vector>
[[nodiscard]] inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v")
{
std::string::size_type front = str.find_first_not_of(pattern);
if (front == std::string::npos) {
return std::string();
}
std::string::size_type end = str.find_last_not_of(pattern);
return str.substr(front, end - front + 1);
}
[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix)
{
if (str.substr(0, prefix.size()) == prefix) {
return str.substr(prefix.size());
}
return str;
}
/**
* Join a list of items
*
* @param list The list to join
* @param separator The separator
* @param unary_op Apply this operator to each item in the list
*/
template <typename T, typename BaseType, typename UnaryOp>
auto Join(const std::vector<T>& list, const BaseType& separator, UnaryOp unary_op)
-> decltype(unary_op(list.at(0)))
{
decltype(unary_op(list.at(0))) ret;
for (size_t i = 0; i < list.size(); ++i) {
if (i > 0) ret += separator;
ret += unary_op(list.at(i));
}
return ret;
}
template <typename T>
T Join(const std::vector<T>& list, const T& separator)
{
return Join(list, separator, [](const T& i) { return i; });
}
// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above.
inline std::string Join(const std::vector<std::string>& list, const std::string& separator)
{
return Join<std::string>(list, separator);
}
/**
* Check if a string does not contain any embedded NUL (\0) characters
*/
[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept
{
return str.size() == strlen(str.c_str());
}
/**
* Locale-independent version of std::to_string
*/
template <typename T>
std::string ToString(const T& t)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << t;
return oss.str();
}
/**
* Check whether a container begins with the given prefix.
*/
template <typename T1, size_t PREFIX_LEN>
[[nodiscard]] inline bool HasPrefix(const T1& obj,
const std::array<uint8_t, PREFIX_LEN>& prefix)
{
return obj.size() >= PREFIX_LEN &&
std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
}
#endif // BITCOIN_UTIL_STRENCODINGS_H

2
src/utilmoneystr.cpp

@ -6,7 +6,7 @@
#include "utilmoneystr.h"
#include "primitives/transaction.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
using namespace std;

2
src/utilstrencodings.cpp

@ -4,7 +4,7 @@
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "tinyformat.h"

28
src/utiltime.cpp

@ -25,6 +25,21 @@ int64_t GetTime()
return time(NULL);
}
template <typename T>
T GetTime()
{
const std::chrono::seconds mocktime{nMockTime};
return std::chrono::duration_cast<T>(
mocktime.count() ?
mocktime :
std::chrono::microseconds{GetTimeMicros()});
}
template std::chrono::seconds GetTime();
template std::chrono::milliseconds GetTime();
template std::chrono::microseconds GetTime();
void SetMockTime(int64_t nMockTimeIn)
{
nMockTime = nMockTimeIn;
@ -42,6 +57,11 @@ int64_t GetTimeMicros()
std::chrono::system_clock::now().time_since_epoch()).count();
}
int64_t GetSystemTimeInSeconds()
{
return GetTimeMicros()/1000000;
}
void MilliSleep(int64_t n)
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(n));
@ -56,3 +76,11 @@ std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime)
ss << boost::posix_time::from_time_t(nTime);
return ss.str();
}
struct timeval MillisToTimeval(int64_t nTimeout)
{
struct timeval timeout;
timeout.tv_sec = nTimeout / 1000;
timeout.tv_usec = (nTimeout % 1000) * 1000;
return timeout;
}

11
src/utiltime.h

@ -11,11 +11,22 @@
#include <string>
int64_t GetTime();
/** Return system time (or mocked time, if set) */
template <typename T>
T GetTime();
int64_t GetTimeMillis();
int64_t GetTimeMicros();
int64_t GetSystemTimeInSeconds(); // Like GetTime(), but not mockable
void SetMockTime(int64_t nMockTimeIn);
void MilliSleep(int64_t n);
std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime);
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
struct timeval MillisToTimeval(int64_t nTimeout);
#endif // HUSH_UTILTIME_H

2
src/wallet/db.cpp

@ -23,7 +23,7 @@
#include "hash.h"
#include "protocol.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include <stdint.h>
#ifndef _WIN32

2
src/wallet/wallet.h

@ -33,7 +33,7 @@
#include "tinyformat.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "util/strencodings.h"
#include "validationinterface.h"
#include "wallet/crypter.h"
#include "wallet/wallet_ismine.h"

1
src/zcash/Note.cpp

@ -12,6 +12,7 @@
#include "zcash/util.h"
#include "librustzcash.h"
#include <boost/thread.hpp>
using namespace libzcash;

Loading…
Cancel
Save