Skip to content
This repository was archived by the owner on Oct 28, 2021. It is now read-only.

Commit 312fff8

Browse files
authored
Updating host ENR (#5593)
Updating host ENR
2 parents feb3c57 + 4242f59 commit 312fff8

File tree

11 files changed

+319
-76
lines changed

11 files changed

+319
-76
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Added: [#5575](https://github.com/ethereum/aleth/pull/5575) Log active peer count and peer list every 30 seconds.
1010
- Added: [#5580](https://github.com/ethereum/aleth/pull/5580) Enable syncing from ETC nodes for blocks < dao hard fork block.
1111
- Added: [#5591](https://github.com/ethereum/aleth/pull/5591) Network logging bugfixes and improvements and add p2pcap log channel.
12+
- Added: [#5593](https://github.com/ethereum/aleth/pull/5593) Dynamically updating host ENR.
1213
- Changed: [#5559](https://github.com/ethereum/aleth/pull/5559) Update peer validation error messages.
1314
- Changed: [#5568](https://github.com/ethereum/aleth/pull/5568) Improve rlpx handshake log messages and create new rlpx log channel.
1415
- Changed: [#5576](https://github.com/ethereum/aleth/pull/5576) Moved sstore_combinations and static_Call50000_sha256 tests to stTimeConsuming test suite. (testeth runs them only with `--all` flag)

libp2p/ENR.cpp

Lines changed: 127 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace p2p
1212
namespace
1313
{
1414
constexpr char c_keyID[] = "id";
15-
constexpr char c_keySec256k1[] = "secp256k1";
15+
constexpr char c_keySecp256k1[] = "secp256k1";
1616
constexpr char c_keyIP[] = "ip";
1717
constexpr char c_keyTCP[] = "tcp";
1818
constexpr char c_keyUDP[] = "udp";
@@ -27,6 +27,14 @@ bytes addressToBytes(Address const& _address)
2727
auto const addressBytes = _address.to_bytes();
2828
return bytes(addressBytes.begin(), addressBytes.end());
2929
}
30+
31+
template <std::size_t N>
32+
std::array<byte, N> bytesToAddress(bytesConstRef _bytes)
33+
{
34+
std::array<byte, N> address;
35+
std::copy_n(_bytes.begin(), N, address.begin());
36+
return address;
37+
}
3038
} // namespace
3139

3240
ENR::ENR(RLP const& _rlp, VerifyFunction const& _verifyFunction)
@@ -48,18 +56,18 @@ ENR::ENR(RLP const& _rlp, VerifyFunction const& _verifyFunction)
4856
}
4957

5058
// transfer to map, this will order them
51-
m_map.insert(keyValuePairs.begin(), keyValuePairs.end());
59+
m_keyValuePairs.insert(keyValuePairs.begin(), keyValuePairs.end());
5260

53-
if (!std::equal(keyValuePairs.begin(), keyValuePairs.end(), m_map.begin()))
61+
if (!std::equal(keyValuePairs.begin(), keyValuePairs.end(), m_keyValuePairs.begin()))
5462
BOOST_THROW_EXCEPTION(ENRKeysAreNotUniqueSorted());
5563

56-
if (!_verifyFunction(m_map, dev::ref(m_signature), dev::ref(content())))
64+
if (!_verifyFunction(m_keyValuePairs, dev::ref(m_signature), dev::ref(content())))
5765
BOOST_THROW_EXCEPTION(ENRSignatureIsInvalid());
5866
}
5967

6068
ENR::ENR(uint64_t _seq, std::map<std::string, bytes> const& _keyValuePairs,
6169
SignFunction const& _signFunction)
62-
: m_seq{_seq}, m_map{_keyValuePairs}, m_signature{_signFunction(dev::ref(content()))}
70+
: m_seq{_seq}, m_keyValuePairs{_keyValuePairs}, m_signature{_signFunction(dev::ref(content()))}
6371
{
6472
}
6573

@@ -81,36 +89,99 @@ void ENR::streamRLP(RLPStream& _s) const
8189
void ENR::streamContent(RLPStream& _s) const
8290
{
8391
_s << m_seq;
84-
for (auto const& keyValue : m_map)
92+
for (auto const& keyValue : m_keyValuePairs)
8593
{
8694
_s << keyValue.first;
8795
_s.appendRaw(keyValue.second);
8896
}
8997
}
9098

91-
ENR createV4ENR(Secret const& _secret, boost::asio::ip::address const& _ip, uint16_t _tcpPort, uint16_t _udpPort)
99+
ENR ENR::update(
100+
std::map<std::string, bytes> const& _keyValuePairs, SignFunction const& _signFunction) const
92101
{
93-
ENR::SignFunction signFunction = [&_secret](bytesConstRef _data) {
94-
// dev::sign returns 65 bytes signature containing r,s,v values
95-
Signature s = dev::sign(_secret, sha3(_data));
96-
// The resulting 64-byte signature is encoded as the concatenation of the r and s signature values.
97-
return bytes(&s[0], &s[64]);
98-
};
102+
return ENR{m_seq + 1, _keyValuePairs, _signFunction};
103+
}
104+
105+
std::string ENR::id() const
106+
{
107+
auto itID = m_keyValuePairs.find(c_keyID);
108+
return itID == m_keyValuePairs.end() ? "" : RLP(itID->second).toString(RLP::VeryStrict);
109+
}
110+
111+
boost::asio::ip::address ENR::ip() const
112+
{
113+
auto itIP = m_keyValuePairs.find(c_keyIP);
114+
if (itIP == m_keyValuePairs.end())
115+
return {};
116+
117+
auto rlpAddress = RLP{itIP->second};
118+
auto const addressBytes = rlpAddress.toBytesConstRef();
119+
120+
if (rlpAddress.size() == 4)
121+
return ba::ip::address_v4{bytesToAddress<4>(addressBytes)};
122+
else if (rlpAddress.size() == 16)
123+
return ba::ip::address_v6{bytesToAddress<16>(addressBytes)};
124+
else
125+
BOOST_THROW_EXCEPTION(ENRUnsupportedIPAddress());
126+
}
99127

128+
uint16_t ENR::tcpPort() const
129+
{
130+
auto itTCP = m_keyValuePairs.find(c_keyTCP);
131+
return itTCP == m_keyValuePairs.end() ? 0 : RLP{itTCP->second}.toInt<uint16_t>(RLP::VeryStrict);
132+
}
133+
134+
uint16_t ENR::udpPort() const
135+
{
136+
auto itUDP = m_keyValuePairs.find(c_keyUDP);
137+
return itUDP == m_keyValuePairs.end() ? 0 : RLP{itUDP->second}.toInt<uint16_t>(RLP::VeryStrict);
138+
}
139+
140+
ENR IdentitySchemeV4::createENR(Secret const& _secret, boost::asio::ip::address const& _ip,
141+
uint16_t _tcpPort, uint16_t _udpPort)
142+
{
143+
ENR::SignFunction signFunction = [&_secret](
144+
bytesConstRef _data) { return sign(_data, _secret); };
145+
146+
auto const keyValuePairs = createKeyValuePairs(_secret, _ip, _tcpPort, _udpPort);
147+
148+
return ENR{1 /* sequence number */, keyValuePairs, signFunction};
149+
}
150+
151+
bytes IdentitySchemeV4::sign(bytesConstRef _data, Secret const& _secret)
152+
{
153+
// dev::sign returns 65 bytes signature containing r,s,v values
154+
Signature s = dev::sign(_secret, sha3(_data));
155+
// The resulting 64-byte signature is encoded as the concatenation of the r and s signature
156+
// values.
157+
return bytes(&s[0], &s[64]);
158+
}
159+
160+
std::map<std::string, bytes> IdentitySchemeV4::createKeyValuePairs(Secret const& _secret,
161+
boost::asio::ip::address const& _ip, uint16_t _tcpPort, uint16_t _udpPort)
162+
{
100163
PublicCompressed const publicKey = toPublicCompressed(_secret);
101164

102165
auto const address = _ip.is_v4() ? addressToBytes(_ip.to_v4()) : addressToBytes(_ip.to_v6());
103166

104167
// Values are of different types (string, bytes, uint16_t),
105168
// so we store them as RLP representation
106-
std::map<std::string, bytes> const keyValuePairs = {{c_keyID, rlp(c_IDV4)},
107-
{c_keySec256k1, rlp(publicKey.asBytes())}, {c_keyIP, rlp(address)},
108-
{c_keyTCP, rlp(_tcpPort)}, {c_keyUDP, rlp(_udpPort)}};
169+
return {{c_keyID, rlp(c_IDV4)}, {c_keySecp256k1, rlp(publicKey.asBytes())},
170+
{c_keyIP, rlp(address)}, {c_keyTCP, rlp(_tcpPort)}, {c_keyUDP, rlp(_udpPort)}};
171+
}
172+
173+
ENR IdentitySchemeV4::updateENR(ENR const& _enr, Secret const& _secret,
174+
boost::asio::ip::address const& _ip, uint16_t _tcpPort, uint16_t _udpPort)
175+
{
176+
ENR::SignFunction signFunction = [&_secret](
177+
bytesConstRef _data) { return sign(_data, _secret); };
109178

110-
return ENR{0 /* sequence number */, keyValuePairs, signFunction};
179+
auto const keyValuePairs = createKeyValuePairs(_secret, _ip, _tcpPort, _udpPort);
180+
181+
return _enr.update(keyValuePairs, signFunction);
111182
}
112183

113-
ENR parseV4ENR(RLP const& _rlp)
184+
ENR IdentitySchemeV4::parseENR(RLP const& _rlp)
114185
{
115186
ENR::VerifyFunction verifyFunction = [](std::map<std::string, bytes> const& _keyValuePairs,
116187
bytesConstRef _signature, bytesConstRef _data) {
@@ -121,7 +192,7 @@ ENR parseV4ENR(RLP const& _rlp)
121192
if (id != c_IDV4)
122193
return false;
123194

124-
auto itKey = _keyValuePairs.find(c_keySec256k1);
195+
auto itKey = _keyValuePairs.find(c_keySecp256k1);
125196
if (itKey == _keyValuePairs.end())
126197
return false;
127198

@@ -134,15 +205,47 @@ ENR parseV4ENR(RLP const& _rlp)
134205
return ENR{_rlp, verifyFunction};
135206
}
136207

208+
PublicCompressed IdentitySchemeV4::publicKey(ENR const& _enr)
209+
{
210+
auto const& keyValuePairs = _enr.keyValuePairs();
211+
212+
auto itID = keyValuePairs.find(c_keyID);
213+
if (itID == keyValuePairs.end() || RLP(itID->second).toString(RLP::VeryStrict) != c_IDV4)
214+
BOOST_THROW_EXCEPTION(ENRUnknownIdentityScheme());
215+
216+
auto itKey = keyValuePairs.find(c_keySecp256k1);
217+
if (itKey == keyValuePairs.end())
218+
BOOST_THROW_EXCEPTION(ENRSecp256k1NotFound());
219+
220+
return RLP{itKey->second}.toHash<PublicCompressed>();
221+
}
222+
137223
std::ostream& operator<<(std::ostream& _out, ENR const& _enr)
138224
{
139-
_out << "[ " << toHexPrefixed(_enr.signature()) << " seq=" << _enr.sequenceNumber() << " ";
140-
for (auto const& keyValue : _enr.keyValuePairs())
225+
_out << "[ seq=" << _enr.sequenceNumber() << " "
226+
<< "id=" << _enr.id() << " ";
227+
228+
try
141229
{
142-
_out << keyValue.first << "=";
143-
_out << toHexPrefixed(RLP{keyValue.second}.toBytes()) << " ";
230+
auto const pubKey = IdentitySchemeV4::publicKey(_enr);
231+
auto const address = _enr.ip();
232+
auto const tcp = _enr.tcpPort();
233+
auto const udp = _enr.udpPort();
234+
235+
_out << "key=" << pubKey.abridged() << " ip=" << address << " tcp=" << tcp
236+
<< " udp=" << udp;
144237
}
145-
_out << "]";
238+
catch (Exception const&)
239+
{
240+
// If failed to get V4 fields, just dump all values
241+
for (auto const& keyValue : _enr.keyValuePairs())
242+
{
243+
_out << keyValue.first << "=";
244+
_out << toHexPrefixed(RLP{keyValue.second}.toBytes()) << " ";
245+
}
246+
}
247+
248+
_out << " ]";
146249
return _out;
147250
}
148251

libp2p/ENR.h

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ namespace p2p
1313
DEV_SIMPLE_EXCEPTION(ENRIsTooBig);
1414
DEV_SIMPLE_EXCEPTION(ENRSignatureIsInvalid);
1515
DEV_SIMPLE_EXCEPTION(ENRKeysAreNotUniqueSorted);
16+
DEV_SIMPLE_EXCEPTION(ENRUnknownIdentityScheme);
17+
DEV_SIMPLE_EXCEPTION(ENRSecp256k1NotFound);
18+
DEV_SIMPLE_EXCEPTION(ENRUnsupportedIPAddress);
1619

1720
/// Class representing Ethereum Node Record - see EIP-778
1821
class ENR
@@ -21,40 +24,65 @@ class ENR
2124
// ENR class implementation is independent of Identity Scheme.
2225
// Identity Scheme specifics are passed to ENR as functions.
2326

24-
// Sign function gets serialized ENR contents and signs it according to some Identity Scheme
27+
/// Sign function gets serialized ENR contents and signs it according to some Identity Scheme
2528
using SignFunction = std::function<bytes(bytesConstRef)>;
26-
// Verify function gets ENR key-value pairs, signature, and serialized content and validates the
27-
// signature according to some Identity Scheme
29+
/// Verify function gets ENR key-value pairs, signature, and serialized content and validates the
30+
/// signature according to some Identity Scheme
2831
using VerifyFunction =
2932
std::function<bool(std::map<std::string, bytes> const&, bytesConstRef, bytesConstRef)>;
3033

31-
// Parse from RLP with given signature verification function
34+
/// Parse from RLP with given signature verification function
3235
ENR(RLP const& _rlp, VerifyFunction const& _verifyFunction);
33-
// Create with given sign function
34-
ENR(uint64_t _seq, std::map<std::string, bytes> const& _keyValues,
36+
/// Create with given sign function
37+
ENR(uint64_t _seq, std::map<std::string, bytes> const& _keyValuePairs,
3538
SignFunction const& _signFunction);
3639

3740
uint64_t sequenceNumber() const { return m_seq; }
38-
std::map<std::string, bytes> const& keyValuePairs() const { return m_map; }
3941
bytes const& signature() const { return m_signature; }
4042

41-
// Serialize to given RLP stream
43+
std::map<std::string, bytes> const& keyValuePairs() const { return m_keyValuePairs; }
44+
45+
/// Pre-defined keys
46+
std::string id() const;
47+
boost::asio::ip::address ip() const;
48+
uint16_t tcpPort() const;
49+
uint16_t udpPort() const;
50+
51+
/// Serialize to given RLP stream
4252
void streamRLP(RLPStream& _s) const;
4353

54+
/// Create new ENR succeeding current one with updated @a _keyValuePairs
55+
ENR update(
56+
std::map<std::string, bytes> const& _keyValuePair, SignFunction const& _signFunction) const;
57+
4458
private:
4559
uint64_t m_seq = 0;
46-
std::map<std::string, bytes> m_map;
60+
std::map<std::string, bytes> m_keyValuePairs;
4761
bytes m_signature;
4862

4963
bytes content() const;
50-
size_t contentRlpListItemCount() const { return m_map.size() * 2 + 1; }
64+
size_t contentRlpListItemCount() const { return m_keyValuePairs.size() * 2 + 1; }
5165
void streamContent(RLPStream& _s) const;
5266
};
5367

68+
class IdentitySchemeV4
69+
{
70+
public:
71+
static ENR createENR(Secret const& _secret, boost::asio::ip::address const& _ip,
72+
uint16_t _tcpPort, uint16_t _udpPort);
5473

55-
ENR createV4ENR(Secret const& _secret, boost::asio::ip::address const& _ip, uint16_t _tcpPort, uint16_t _udpPort);
74+
static ENR updateENR(ENR const& _enr, Secret const& _secret,
75+
boost::asio::ip::address const& _ip, uint16_t _tcpPort, uint16_t _udpPort);
5676

57-
ENR parseV4ENR(RLP const& _rlp);
77+
static ENR parseENR(RLP const& _rlp);
78+
79+
static PublicCompressed publicKey(ENR const& _enr);
80+
81+
private:
82+
static bytes sign(bytesConstRef _data, Secret const& _secret);
83+
static std::map<std::string, bytes> createKeyValuePairs(Secret const& _secret,
84+
boost::asio::ip::address const& _ip, uint16_t _tcpPort, uint16_t _udpPort);
85+
};
5886

5987
std::ostream& operator<<(std::ostream& _out, ENR const& _enr);
6088

0 commit comments

Comments
 (0)