@@ -12,7 +12,7 @@ namespace p2p
1212namespace
1313{
1414constexpr char c_keyID[] = " id" ;
15- constexpr char c_keySec256k1 [] = " secp256k1" ;
15+ constexpr char c_keySecp256k1 [] = " secp256k1" ;
1616constexpr char c_keyIP[] = " ip" ;
1717constexpr char c_keyTCP[] = " tcp" ;
1818constexpr 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
3240ENR::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
6068ENR::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
8189void 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+
137223std::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
0 commit comments