mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-26 23:09:03 +02:00
Merge #20685: Add I2P support using I2P SAM
a701fcf01fnet: Do not skip the I2P network from GetNetworkNames() (Vasil Dimov)0181e24439net: recognize I2P from ParseNetwork() so that -onlynet=i2p works (Vasil Dimov)b905363fa8net: accept incoming I2P connections from CConnman (Vasil Dimov)0635233a1enet: make outgoing I2P connections from CConnman (Vasil Dimov)9559bd1404net: add I2P to the reachability map (Vasil Dimov)76c35c60f3init: introduce I2P connectivity options (Vasil Dimov)c22daa2ecfnet: implement the necessary parts of the I2P SAM protocol (Vasil Dimov)5bac7e45e1net: extend Sock with a method to check whether connected (Vasil Dimov)42c779f503net: extend Sock with methods for robust send & read until terminator (Vasil Dimov)ea1845315anet: extend Sock::Wait() to report a timeout (Vasil Dimov)78fdfbea66net: dedup MSG_NOSIGNAL and MSG_DONTWAIT definitions (Vasil Dimov)34bcfab562net: move the constant maxWait out of InterruptibleRecv() (Vasil Dimov)cff65c4a27net: extend CNetAddr::SetSpecial() to support I2P (Vasil Dimov)f6c267db3bnet: avoid unnecessary GetBindAddress() call (Vasil Dimov)7c224fdac4net: isolate the protocol-agnostic part of CConnman::AcceptConnection() (Vasil Dimov)1f75a653ddnet: get the bind address earlier in CConnman::AcceptConnection() (Vasil Dimov)25605895afnet: check for invalid socket earlier in CConnman::AcceptConnection() (Vasil Dimov)545bc5f81dutil: fix WriteBinaryFile() claiming success even if error occurred (Vasil Dimov)8b6e4b3b23util: fix ReadBinaryFile() returning partial contents (Vasil Dimov)4cba2fdafautil: extract {Read,Write}BinaryFile() to its own files (Vasil Dimov) Pull request description: Add I2P support by using the [I2P SAM](https://geti2p.net/en/docs/api/samv3) protocol. Unlike Tor, for incoming connections we get the I2P address of the peer (and they also receive ours when we are the connection initiator). Two new options are added: ``` -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. Notice that listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: true) ``` # Overview of the changes ## Make `ReadBinary()` and `WriteBinary()` reusable We would need to dump the I2P private key to a file and read it back later. Move those two functions out of `torcontrol.cpp`. ``` util: extract {Read,Write}BinaryFile() to its own files util: fix ReadBinaryFile() returning partial contents util: fix WriteBinaryFile() claiming success even if error occurred ``` ## Split `CConnman::AcceptConnection()` Most of `CConnman::AcceptConnection()` is agnostic of how the socket was accepted. The other part of it deals with the details of the `accept(2)` system call. Split those so that the protocol-agnostic part can be reused if we accept a socket by other means. ``` net: check for invalid socket earlier in CConnman::AcceptConnection() net: get the bind address earlier in CConnman::AcceptConnection() net: isolate the protocol-agnostic part of CConnman::AcceptConnection() net: avoid unnecessary GetBindAddress() call ``` ## Implement the I2P [SAM](https://geti2p.net/en/docs/api/samv3) protocol (not all of it) Just the parts that would enable us to make outgoing and accept incoming I2P connections. ``` net: extend CNetAddr::SetSpecial() to support I2P net: move the constant maxWait out of InterruptibleRecv() net: dedup MSG_NOSIGNAL and MSG_DONTWAIT definitions net: extend Sock::Wait() to report a timeout net: extend Sock with methods for robust send & read until terminator net: extend Sock with a method to check whether connected net: implement the necessary parts of the I2P SAM protocol ``` ## Use I2P SAM to connect to and accept connections from I2P peers Profit from all of the preceding commits. ``` init: introduce I2P connectivity options net: add I2P to the reachability map net: make outgoing I2P connections from CConnman net: accept incoming I2P connections from CConnman net: recognize I2P from ParseNetwork() so that -onlynet=i2p works net: Do not skip the I2P network from GetNetworkNames() ``` ACKs for top commit: laanwj: re-ACKa701fcf01fjonatack: re-ACKa701fcf01freviewed diff per `git range-diffad898122a7bb34 a701fcf`, debug built and launched bitcoind with i2pd v2.35 running a dual I2P+Torv3 service with the I2P config settings listed below (did not test `onlynet=i2p`); operation appears nominal (same as it has been these past weeks), and tested the bitcoind help outputs grepping for `-i i2p` and the rpc getpeerinfo and getnetworkinfo helps Tree-SHA512: de42090c9c0bf23b43b5839f5b4fc4b3a2657bde1e45c796b5f3c7bf83cb8ec6ca4278f8a89e45108ece92f9b573cafea3b42a06bc09076b40a196c909b6610e
This commit is contained in:
@@ -49,9 +49,10 @@ NET_UNROUTABLE = "not_publicly_routable"
|
||||
NET_IPV4 = "ipv4"
|
||||
NET_IPV6 = "ipv6"
|
||||
NET_ONION = "onion"
|
||||
NET_I2P = "i2p"
|
||||
|
||||
# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
|
||||
NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION})
|
||||
NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION, NET_I2P})
|
||||
|
||||
|
||||
class ProxyTest(BitcoinTestFramework):
|
||||
@@ -90,11 +91,15 @@ class ProxyTest(BitcoinTestFramework):
|
||||
self.serv3 = Socks5Server(self.conf3)
|
||||
self.serv3.start()
|
||||
|
||||
# We will not try to connect to this.
|
||||
self.i2p_sam = ('127.0.0.1', 7656)
|
||||
|
||||
# Note: proxies are not used to connect to local nodes. This is because the proxy to
|
||||
# use is based on CService.GetNetwork(), which returns NET_UNROUTABLE for localhost.
|
||||
args = [
|
||||
['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
|
||||
['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
|
||||
['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),
|
||||
'-i2psam=%s:%i' % (self.i2p_sam), '-i2pacceptincoming=0', '-proxyrandomize=0'],
|
||||
['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
|
||||
[]
|
||||
]
|
||||
@@ -199,9 +204,16 @@ class ProxyTest(BitcoinTestFramework):
|
||||
n0 = networks_dict(self.nodes[0].getnetworkinfo())
|
||||
assert_equal(NETWORKS, n0.keys())
|
||||
for net in NETWORKS:
|
||||
assert_equal(n0[net]['proxy'], '%s:%i' % (self.conf1.addr))
|
||||
assert_equal(n0[net]['proxy_randomize_credentials'], True)
|
||||
if net == NET_I2P:
|
||||
expected_proxy = ''
|
||||
expected_randomize = False
|
||||
else:
|
||||
expected_proxy = '%s:%i' % (self.conf1.addr)
|
||||
expected_randomize = True
|
||||
assert_equal(n0[net]['proxy'], expected_proxy)
|
||||
assert_equal(n0[net]['proxy_randomize_credentials'], expected_randomize)
|
||||
assert_equal(n0['onion']['reachable'], True)
|
||||
assert_equal(n0['i2p']['reachable'], False)
|
||||
|
||||
n1 = networks_dict(self.nodes[1].getnetworkinfo())
|
||||
assert_equal(NETWORKS, n1.keys())
|
||||
@@ -211,21 +223,36 @@ class ProxyTest(BitcoinTestFramework):
|
||||
assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr))
|
||||
assert_equal(n1['onion']['proxy_randomize_credentials'], False)
|
||||
assert_equal(n1['onion']['reachable'], True)
|
||||
assert_equal(n1['i2p']['proxy'], '%s:%i' % (self.i2p_sam))
|
||||
assert_equal(n1['i2p']['proxy_randomize_credentials'], False)
|
||||
assert_equal(n1['i2p']['reachable'], True)
|
||||
|
||||
n2 = networks_dict(self.nodes[2].getnetworkinfo())
|
||||
assert_equal(NETWORKS, n2.keys())
|
||||
for net in NETWORKS:
|
||||
assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr))
|
||||
assert_equal(n2[net]['proxy_randomize_credentials'], True)
|
||||
if net == NET_I2P:
|
||||
expected_proxy = ''
|
||||
expected_randomize = False
|
||||
else:
|
||||
expected_proxy = '%s:%i' % (self.conf2.addr)
|
||||
expected_randomize = True
|
||||
assert_equal(n2[net]['proxy'], expected_proxy)
|
||||
assert_equal(n2[net]['proxy_randomize_credentials'], expected_randomize)
|
||||
assert_equal(n2['onion']['reachable'], True)
|
||||
assert_equal(n2['i2p']['reachable'], False)
|
||||
|
||||
if self.have_ipv6:
|
||||
n3 = networks_dict(self.nodes[3].getnetworkinfo())
|
||||
assert_equal(NETWORKS, n3.keys())
|
||||
for net in NETWORKS:
|
||||
assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr))
|
||||
if net == NET_I2P:
|
||||
expected_proxy = ''
|
||||
else:
|
||||
expected_proxy = '[%s]:%i' % (self.conf3.addr)
|
||||
assert_equal(n3[net]['proxy'], expected_proxy)
|
||||
assert_equal(n3[net]['proxy_randomize_credentials'], False)
|
||||
assert_equal(n3['onion']['reachable'], False)
|
||||
assert_equal(n3['i2p']['reachable'], False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -105,7 +105,7 @@ class NetTest(BitcoinTestFramework):
|
||||
assert_equal(peer_info[1][1]['connection_type'], 'inbound')
|
||||
|
||||
# Check dynamically generated networks list in getpeerinfo help output.
|
||||
assert "(ipv4, ipv6, onion, not_publicly_routable)" in self.nodes[0].help("getpeerinfo")
|
||||
assert "(ipv4, ipv6, onion, i2p, not_publicly_routable)" in self.nodes[0].help("getpeerinfo")
|
||||
|
||||
def test_getnettotals(self):
|
||||
self.log.info("Test getnettotals")
|
||||
@@ -156,7 +156,7 @@ class NetTest(BitcoinTestFramework):
|
||||
assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"])
|
||||
|
||||
# Check dynamically generated networks list in getnetworkinfo help output.
|
||||
assert "(ipv4, ipv6, onion)" in self.nodes[0].help("getnetworkinfo")
|
||||
assert "(ipv4, ipv6, onion, i2p)" in self.nodes[0].help("getnetworkinfo")
|
||||
|
||||
def test_getaddednodeinfo(self):
|
||||
self.log.info("Test getaddednodeinfo")
|
||||
|
||||
Reference in New Issue
Block a user