mirror of
https://git.hush.is/hush/hush3.git
synced 2025-07-15 00:02:33 -04:00
Compare commits
13 Commits
fe9f1ef9e4
...
b6fc8b0149
Author | SHA1 | Date | |
---|---|---|---|
|
b6fc8b0149 | ||
|
0d79d11a95 | ||
|
2f8a138cce | ||
|
fc27972beb | ||
|
9519e3a6cd | ||
|
d5e9ef763d | ||
|
26064b4bf8 | ||
|
0ebb4c70c7 | ||
|
76b8de6b70 | ||
|
176a9e1c7a | ||
|
6f8bce9b3a | ||
|
9d2277c697 | ||
![]() |
512da314a5 |
@ -247,6 +247,9 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-man
|
||||
clean-local:
|
||||
rm -rf test_bitcoin.coverage/ total.coverage/
|
||||
|
||||
seeds:
|
||||
./contrib/seeds/generate-seeds.py contrib/seeds > src/chainparamsseeds.h
|
||||
|
||||
manpages:
|
||||
./util/gen-manpages.sh
|
||||
@echo "Please review the man pages changes to see if they look correct, then commit and push"
|
||||
|
34
configure.ac
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])
|
||||
|
@ -1,8 +1,10 @@
|
||||
### Seeds ###
|
||||
# Seeds
|
||||
|
||||
Utility to generate the seeds.txt list that is compiled into the client
|
||||
(see [src/chainparamsseeds.h](/src/chainparamsseeds.h) and other utilities in [contrib/seeds](/contrib/seeds)).
|
||||
(see [src/chainparamsseeds.h](hush/hush3/src/branch/master/src/chainparamsseeds.h) and other utilities in [contrib/seeds](hush/hush3/src/branch/master/contrib/seeds/)).
|
||||
|
||||
## Updating seeds
|
||||
|
||||
Update [contrib/seeds/nodes_main.txt](hush/hush3/src/branch/master/contrib/seeds/nodes_main.txt) and run `make seeds` in the root directory of this repo to update [src/chainparamsseeds.h](hush/hush3/src/branch/master/src/chainparamsseeds.h) then commit the result.
|
||||
|
||||
The 512 seeds compiled into the 0.10 release were created from sipa's DNS seed data, like this:
|
||||
|
||||
curl -s http://bitcoin.sipa.be/seeds.txt | makeseeds.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
|
||||
'''
|
||||
@ -13,43 +14,61 @@ argument:
|
||||
|
||||
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])
|
||||
class BIP155Network(Enum):
|
||||
IPV4 = 1
|
||||
IPV6 = 2
|
||||
TORV2 = 3 # no longer supported
|
||||
TORV3 = 4
|
||||
I2P = 5
|
||||
CJDNS = 6
|
||||
|
||||
def name_to_ipv6(addr):
|
||||
if len(addr)>6 and addr.endswith('.onion'):
|
||||
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)
|
||||
if host[0] == BIP155Network.TORV2:
|
||||
return None # TORV2 is no longer supported, so we ignore it
|
||||
else:
|
||||
return host + (port, )
|
||||
|
||||
def process_nodes(g, f, structname, defaultport):
|
||||
g.write('static SeedSpec6 %s[] = {\n' % structname)
|
||||
first = True
|
||||
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,39 @@ 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, update contrib/seeds/nodes_main.txt then run\n')
|
||||
g.write('// ./contrib/seeds/generate-seeds.py contrib/seeds > src/chainparamsseeds.h\n')
|
||||
g.write('// OR run: make 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()
|
||||
|
||||
|
@ -1,6 +1,18 @@
|
||||
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
|
||||
|
||||
# wtfistheinternet.hush.is
|
||||
107.174.70.251
|
||||
|
||||
# torv3
|
||||
56wqzfj6mhxgsv3h3nh3pdocguogxfxud55libqjhjsdh5alfsko2iqd.onion
|
||||
hushv3h6mbxd2pptj42reko3jcexcgnz5zvp3mqcu6myto3jhhn4yzyd.onion
|
||||
|
||||
# todo: i2p
|
||||
iljqq7nnmw2ij2ezl334cerwwmgzmmbmoc3n4saditd2xhi3xohq.b32.i2p
|
||||
|
@ -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
Normal file
161
doc/i2p.md
Normal file
@ -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.
|
297
doc/tor.md
297
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
|
||||
|
||||
1. Run Hush behind a Tor proxy
|
||||
-------------------------------
|
||||
- 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.
|
||||
|
||||
The first step is running Hush behind a Tor proxy. This will already make all
|
||||
outgoing connections be anonymized, but more is possible.
|
||||
- Tor removed v2 support beginning with version 0.4.6.
|
||||
|
||||
-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.
|
||||
## How to see information about your Tor configuration via Hush
|
||||
|
||||
-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.
|
||||
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`)
|
||||
|
||||
-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.
|
||||
You may set the `-debug=tor` config logging option to have additional
|
||||
information in the debug log about your Tor configuration.
|
||||
|
||||
-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.
|
||||
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`.
|
||||
|
||||
To fetch a number of onion addresses that your node knows, for example seven
|
||||
addresses, use the `getnodeaddresses 7 onion` RPC.
|
||||
|
||||
## 1. Run Hush behind a Tor proxy
|
||||
|
||||
The first step is running Hush behind a Tor proxy. This will already anonymize all
|
||||
outgoing connections, but more is possible.
|
||||
|
||||
-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.
|
||||
|
||||
-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.
|
||||
------------------------------------------------------------------
|
||||
|
||||
-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`.
|
||||
|
||||
### Control Port
|
||||
|
||||
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):
|
||||
|
||||
```
|
||||
ControlPort 9051
|
||||
CookieAuthentication 1
|
||||
CookieAuthFileGroupReadable 1
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
On some systems (such as Arch Linux), you may also need to add the following
|
||||
line:
|
||||
|
||||
```
|
||||
DataDirectoryGroupReadable 1
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
Connecting to Tor's control socket API requires one of two authentication
|
||||
methods to be configured: cookie authentication or hushd's `-torpassword`
|
||||
configuration option.
|
||||
|
||||
#### Cookie authentication
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
getent group | cut -d: -f1 | grep -i tor
|
||||
```
|
||||
|
||||
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`:
|
||||
|
||||
```
|
||||
TORGROUP=$(stat -c '%G' /run/tor/control.authcookie)
|
||||
```
|
||||
|
||||
Once you have determined the `${TORGROUP}` and selected the `${USER}` that will
|
||||
run hushd, run this as root:
|
||||
|
||||
```
|
||||
usermod -a -G ${TORGROUP} ${USER}
|
||||
```
|
||||
|
||||
Then restart the computer (or log out) and log in as the `${USER}` that will run
|
||||
hushd.
|
||||
|
||||
#### `torpassword` authentication
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
|
||||
## 3. Manually create a Hush onion service
|
||||
|
||||
2. Run 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):
|
||||
|
||||
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):
|
||||
HiddenServiceDir /var/lib/tor/hush-service/
|
||||
HiddenServicePort 18030 127.0.0.1:18032
|
||||
|
||||
HiddenServiceDir /var/lib/tor/hush-service/
|
||||
HiddenServicePort 18030 127.0.0.1:18030
|
||||
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).
|
||||
|
||||
The directory can be different of course, but (both) port numbers should be equal to
|
||||
your hushd's P2P listen port (18030 by default).
|
||||
-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.
|
||||
|
||||
-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).
|
||||
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.
|
||||
-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.
|
||||
-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=hushc0de123.onion -listen
|
||||
./hushd -proxy=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -listen
|
||||
|
||||
(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
|
||||
(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
|
||||
./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
|
||||
./hushd ... -discover
|
||||
|
||||
and open port 18030 on your firewall.
|
||||
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
|
||||
If you only want to use Tor to reach .onion addresses, but not use it as a proxy
|
||||
for normal IPv4/IPv6 communication, use:
|
||||
|
||||
./hushd -onion=127.0.0.1:9050 -externalip=hushc0de123.onion -discover
|
||||
./hushd -onion=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -discover
|
||||
|
||||
## 4. Privacy recommendations
|
||||
|
||||
3. Automatically listen on Tor
|
||||
--------------------------------
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
4. Connect to a Hush hidden server
|
||||
-----------------------------------
|
||||
|
||||
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:
|
||||
|
||||
./hushd -onion=127.0.0.1:9050 -connect=fuckzookoie6wxgio.onion
|
||||
|
||||
Now use hush-cli to verify there is only a single peer connection.
|
||||
|
||||
hush-cli getpeerinfo
|
||||
|
||||
[
|
||||
{
|
||||
"id" : 1,
|
||||
"addr" : "zcashhoneypot.onion:18030",
|
||||
...
|
||||
"version" : 1987420,
|
||||
"subver" : "/GoldenSandtrout:3.6.0/",
|
||||
...
|
||||
}
|
||||
]
|
||||
|
||||
To connect to multiple Tor nodes, use:
|
||||
|
||||
./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,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()
|
||||
|
@ -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)
|
||||
|
@ -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("hushv3h6mbxd2pptj42reko3jcexcgnz5zvp3mqcu6myto3jhhn4yzyd.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, "hushv3h6mbxd2pptj42reko3jcexcgnz5zvp3mqcu6myto3jhhn4yzyd.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)
|
||||
|
@ -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
Normal file
122
src/addrdb.cpp
Normal file
@ -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
Normal file
90
src/addrdb.h
Normal file
@ -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
|
319
src/addrman.cpp
319
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)
|
||||
@ -457,7 +481,12 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||
(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,7 +536,15 @@ 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;
|
||||
}
|
||||
@ -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;
|
||||
|
220
src/addrman.h
220
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_) EXCLUSIVE_LOCKS_REQUIRED(!cs)
|
||||
{
|
||||
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");
|
||||
std::vector<int>().swap(vRandom);
|
||||
|
||||
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
|
||||
// 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 nSize = 0;
|
||||
s >> nSize;
|
||||
for (int n = 0; n < nSize; n++) {
|
||||
int nIndex = 0;
|
||||
s >> nIndex;
|
||||
if (nIndex >= 0 && nIndex < nNew) {
|
||||
entryToBucket[nIndex] = bucket;
|
||||
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};
|
||||
|
||||
if (!restore_bucketing) {
|
||||
LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n");
|
||||
}
|
||||
|
||||
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++;
|
||||
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
|
||||
|
@ -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
Normal file
20
src/attributes.h
Normal file
@ -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
|
@ -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>
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1,37 +1,26 @@
|
||||
// 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, update contrib/seeds/nodes_main.txt then run
|
||||
// ./contrib/seeds/generate-seeds.py contrib/seeds > src/chainparamsseeds.h
|
||||
// OR run: make 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
|
||||
0x01,0x04,0x6b,0xae,0x46,0xfb,0x00,0x00, // 107.174.70.251
|
||||
0x04,0x20,0xef,0xad,0x0c,0x95,0x3e,0x61,0xee,0x69,0x57,0x67,0xdb,0x4f,0xb7,0x8d,0xc2,0x35,0x1c,0x6b,0x96,0xf4,0x1f,0x7a,0xb4,0x06,0x09,0x3a,0x64,0x33,0xf4,0x0b,0x2c,0x94,0x00,0x00, // 56wqzfj6mhxgsv3h3nh3pdocguogxfxud55libqjhjsdh5alfsko2iqd.onion
|
||||
0x04,0x20,0x3d,0x24,0x7a,0xec,0xfe,0x60,0x6e,0x3d,0x3d,0xf3,0x4f,0x35,0x12,0x29,0xdb,0x48,0x89,0x71,0x19,0xb9,0xee,0x6a,0xfd,0xb2,0x02,0xa7,0x99,0x89,0xbb,0x69,0x39,0xdb,0x00,0x00, // hushv3h6mbxd2pptj42reko3jcexcgnz5zvp3mqcu6myto3jhhn4yzyd.onion
|
||||
0x05,0x20,0x42,0xd3,0x08,0x7d,0xad,0x65,0xb4,0x84,0xe8,0x99,0x5e,0xf7,0xc1,0x12,0x36,0xb3,0x0d,0x96,0x30,0x2c,0x70,0xb6,0xde,0x48,0x03,0x44,0xc7,0xab,0x9d,0x1b,0xbb,0x8f,0x00,0x00, // iljqq7nnmw2ij2ezl334cerwwmgzmmbmoc3n4saditd2xhi3xohq.b32.i2p
|
||||
};
|
||||
|
||||
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
|
||||
|
@ -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
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);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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
Normal file
162
src/crypto/sha3.cpp
Normal file
@ -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
Normal file
42
src/crypto/sha3.h
Normal file
@ -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
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utilstrencodings.h"
|
||||
#include "util/strencodings.h"
|
||||
#include "version.h"
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
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]);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
Normal file
441
src/i2p.cpp
Normal file
@ -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
Normal file
256
src/i2p.h
Normal file
@ -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
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
687
src/net.cpp
687
src/net.cpp
File diff suppressed because it is too large
Load Diff
140
src/net.h
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
Normal file
1222
src/netaddress.cpp
Normal file
File diff suppressed because it is too large
Load Diff
534
src/netaddress.h
Normal file
534
src/netaddress.h
Normal file
@ -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
1212
src/netbase.cpp
File diff suppressed because it is too large
Load Diff
245
src/netbase.h
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 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout);
|
||||
|
||||
|
||||
|
||||
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);
|
||||
/**
|
||||
* Convert milliseconds to a struct timeval for e.g. select.
|
||||
*/
|
||||
struct timeval MillisToTimeval(int64_t nTimeout);
|
||||
|
||||
bool SanityCheckASMap(const std::vector<bool>& asmap);
|
||||
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
Normal file
37
src/netmessagemaker.h
Normal file
@ -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
|
245
src/prevector.h
245
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
value_type* data() {
|
||||
return item_ptr(0);
|
||||
}
|
||||
|
||||
const value_type* data() const {
|
||||
return item_ptr(0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HUSH_PREVECTOR_H
|
||||
|
@ -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"
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include "hash.h"
|
||||
#include "tinyformat.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "util/strencodings.h"
|
||||
|
||||
#include "librustzcash.h"
|
||||
|
||||
|
@ -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
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)
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
@ -599,7 +633,7 @@ UniValue setban(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr))
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
|
||||
|
||||
int64_t banTime = 0; //use standard bantime if not specified
|
||||
int64_t banTime = 60*60*24; //use standard bantime if not specified
|
||||
if (params.size() >= 3 && !params[2].isNull())
|
||||
banTime = params[2].get_int64();
|
||||
|
||||
@ -607,14 +641,14 @@ 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);
|
||||
fprintf(stderr,"%s: adding manual ban for %s with banTime=%ld absolute=%d isSubnet=%d\n", __func__, isSubnet ? subNet.ToString().c_str() : netAddr.ToString().c_str(), banTime, absolute, isSubnet);
|
||||
isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
|
||||
|
||||
//disconnect possible nodes
|
||||
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
|
||||
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) {
|
||||
bannedNode->fDisconnect = true;
|
||||
}
|
||||
else if(strCommand == "remove")
|
||||
{
|
||||
}
|
||||
} else if(strCommand == "remove") {
|
||||
if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) ))
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed");
|
||||
}
|
||||
@ -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 },
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
||||
|
306
src/serialize.h
306
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
|
||||
@ -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,9 +226,64 @@ 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
Normal file
252
src/span.h
Normal file
@ -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
|
@ -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),
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "netbase.h"
|
||||
#include "chainparams.h"
|
||||
#include "tinyformat.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "util/strencodings.h"
|
||||
|
||||
#define NODE_NONE 0
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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"));
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "sync.h"
|
||||
#include "ui_interface.h"
|
||||
#include "util.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "util/strencodings.h"
|
||||
|
||||
CTimeWarning timeWarning;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -20,7 +20,7 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "uint256.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "util/strencodings.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -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
Normal file
47
src/util/readwritefile.cpp
Normal file
@ -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
Normal file
29
src/util/readwritefile.h
Normal file
@ -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
Normal file
328
src/util/sock.cpp
Normal file
@ -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
Normal file
184
src/util/sock.h
Normal file
@ -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
Normal file
68
src/util/spanparsing.cpp
Normal file
@ -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
Normal file
50
src/util/spanparsing.h
Normal file
@ -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
Normal file
655
src/util/strencodings.cpp
Normal file
@ -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;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user