github.com/ethereum/go-ethereum@v1.16.1/p2p/discover/v4wire/v4wire.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package v4wire implements the Discovery v4 Wire Protocol. 18 package v4wire 19 20 import ( 21 "bytes" 22 "crypto/ecdsa" 23 "crypto/elliptic" 24 "errors" 25 "fmt" 26 "math/big" 27 "net" 28 "net/netip" 29 "time" 30 31 "github.com/ethereum/go-ethereum/common/math" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/p2p/enode" 34 "github.com/ethereum/go-ethereum/p2p/enr" 35 "github.com/ethereum/go-ethereum/rlp" 36 ) 37 38 // RPC packet types 39 const ( 40 PingPacket = iota + 1 // zero is 'reserved' 41 PongPacket 42 FindnodePacket 43 NeighborsPacket 44 ENRRequestPacket 45 ENRResponsePacket 46 ) 47 48 // RPC request structures 49 type ( 50 Ping struct { 51 Version uint 52 From, To Endpoint 53 Expiration uint64 54 ENRSeq uint64 `rlp:"optional"` // Sequence number of local record, added by EIP-868. 55 56 // Ignore additional fields (for forward compatibility). 57 Rest []rlp.RawValue `rlp:"tail"` 58 } 59 60 // Pong is the reply to ping. 61 Pong struct { 62 // This field should mirror the UDP envelope address 63 // of the ping packet, which provides a way to discover the 64 // external address (after NAT). 65 To Endpoint 66 ReplyTok []byte // This contains the hash of the ping packet. 67 Expiration uint64 // Absolute timestamp at which the packet becomes invalid. 68 ENRSeq uint64 `rlp:"optional"` // Sequence number of local record, added by EIP-868. 69 70 // Ignore additional fields (for forward compatibility). 71 Rest []rlp.RawValue `rlp:"tail"` 72 } 73 74 // Findnode is a query for nodes close to the given target. 75 Findnode struct { 76 Target Pubkey 77 Expiration uint64 78 // Ignore additional fields (for forward compatibility). 79 Rest []rlp.RawValue `rlp:"tail"` 80 } 81 82 // Neighbors is the reply to findnode. 83 Neighbors struct { 84 Nodes []Node 85 Expiration uint64 86 // Ignore additional fields (for forward compatibility). 87 Rest []rlp.RawValue `rlp:"tail"` 88 } 89 90 // ENRRequest queries for the remote node's record. 91 ENRRequest struct { 92 Expiration uint64 93 // Ignore additional fields (for forward compatibility). 94 Rest []rlp.RawValue `rlp:"tail"` 95 } 96 97 // ENRResponse is the reply to ENRRequest. 98 ENRResponse struct { 99 ReplyTok []byte // Hash of the ENRRequest packet. 100 Record enr.Record 101 // Ignore additional fields (for forward compatibility). 102 Rest []rlp.RawValue `rlp:"tail"` 103 } 104 ) 105 106 // MaxNeighbors is the maximum number of neighbor nodes in a Neighbors packet. 107 const MaxNeighbors = 12 108 109 // This code computes the MaxNeighbors constant value. 110 111 // func init() { 112 // var maxNeighbors int 113 // p := Neighbors{Expiration: ^uint64(0)} 114 // maxSizeNode := Node{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} 115 // for n := 0; ; n++ { 116 // p.Nodes = append(p.Nodes, maxSizeNode) 117 // size, _, err := rlp.EncodeToReader(p) 118 // if err != nil { 119 // // If this ever happens, it will be caught by the unit tests. 120 // panic("cannot encode: " + err.Error()) 121 // } 122 // if headSize+size+1 >= 1280 { 123 // maxNeighbors = n 124 // break 125 // } 126 // } 127 // fmt.Println("maxNeighbors", maxNeighbors) 128 // } 129 130 // Pubkey represents an encoded 64-byte secp256k1 public key. 131 type Pubkey [64]byte 132 133 // ID returns the node ID corresponding to the public key. 134 func (e Pubkey) ID() enode.ID { 135 return enode.ID(crypto.Keccak256Hash(e[:])) 136 } 137 138 // Node represents information about a node. 139 type Node struct { 140 IP net.IP // len 4 for IPv4 or 16 for IPv6 141 UDP uint16 // for discovery protocol 142 TCP uint16 // for RLPx protocol 143 ID Pubkey 144 } 145 146 // Endpoint represents a network endpoint. 147 type Endpoint struct { 148 IP net.IP // len 4 for IPv4 or 16 for IPv6 149 UDP uint16 // for discovery protocol 150 TCP uint16 // for RLPx protocol 151 } 152 153 // NewEndpoint creates an endpoint. 154 func NewEndpoint(addr netip.AddrPort, tcpPort uint16) Endpoint { 155 var ip net.IP 156 if addr.Addr().Is4() || addr.Addr().Is4In6() { 157 ip4 := addr.Addr().As4() 158 ip = ip4[:] 159 } else { 160 ip = addr.Addr().AsSlice() 161 } 162 return Endpoint{IP: ip, UDP: addr.Port(), TCP: tcpPort} 163 } 164 165 type Packet interface { 166 // Name is the name of the package, for logging purposes. 167 Name() string 168 // Kind is the packet type, for logging purposes. 169 Kind() byte 170 } 171 172 func (req *Ping) Name() string { return "PING/v4" } 173 func (req *Ping) Kind() byte { return PingPacket } 174 175 func (req *Pong) Name() string { return "PONG/v4" } 176 func (req *Pong) Kind() byte { return PongPacket } 177 178 func (req *Findnode) Name() string { return "FINDNODE/v4" } 179 func (req *Findnode) Kind() byte { return FindnodePacket } 180 181 func (req *Neighbors) Name() string { return "NEIGHBORS/v4" } 182 func (req *Neighbors) Kind() byte { return NeighborsPacket } 183 184 func (req *ENRRequest) Name() string { return "ENRREQUEST/v4" } 185 func (req *ENRRequest) Kind() byte { return ENRRequestPacket } 186 187 func (req *ENRResponse) Name() string { return "ENRRESPONSE/v4" } 188 func (req *ENRResponse) Kind() byte { return ENRResponsePacket } 189 190 // Expired checks whether the given UNIX time stamp is in the past. 191 func Expired(ts uint64) bool { 192 return time.Unix(int64(ts), 0).Before(time.Now()) 193 } 194 195 // Encoder/decoder. 196 197 const ( 198 macSize = 32 199 sigSize = crypto.SignatureLength 200 headSize = macSize + sigSize // space of packet frame data 201 ) 202 203 var ( 204 ErrPacketTooSmall = errors.New("too small") 205 ErrBadHash = errors.New("bad hash") 206 ErrBadPoint = errors.New("invalid curve point") 207 ) 208 209 var headSpace = make([]byte, headSize) 210 211 // Decode reads a discovery v4 packet. 212 func Decode(input []byte) (Packet, Pubkey, []byte, error) { 213 if len(input) < headSize+1 { 214 return nil, Pubkey{}, nil, ErrPacketTooSmall 215 } 216 hash, sig, sigdata := input[:macSize], input[macSize:headSize], input[headSize:] 217 shouldhash := crypto.Keccak256(input[macSize:]) 218 if !bytes.Equal(hash, shouldhash) { 219 return nil, Pubkey{}, nil, ErrBadHash 220 } 221 fromKey, err := recoverNodeKey(crypto.Keccak256(input[headSize:]), sig) 222 if err != nil { 223 return nil, fromKey, hash, err 224 } 225 226 var req Packet 227 switch ptype := sigdata[0]; ptype { 228 case PingPacket: 229 req = new(Ping) 230 case PongPacket: 231 req = new(Pong) 232 case FindnodePacket: 233 req = new(Findnode) 234 case NeighborsPacket: 235 req = new(Neighbors) 236 case ENRRequestPacket: 237 req = new(ENRRequest) 238 case ENRResponsePacket: 239 req = new(ENRResponse) 240 default: 241 return nil, fromKey, hash, fmt.Errorf("unknown type: %d", ptype) 242 } 243 // Here we use NewStream to allow for additional data after the first 244 // RLP object (forward-compatibility). 245 s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) 246 err = s.Decode(req) 247 return req, fromKey, hash, err 248 } 249 250 // Encode encodes a discovery packet. 251 func Encode(priv *ecdsa.PrivateKey, req Packet) (packet, hash []byte, err error) { 252 b := new(bytes.Buffer) 253 b.Write(headSpace) 254 b.WriteByte(req.Kind()) 255 if err := rlp.Encode(b, req); err != nil { 256 return nil, nil, err 257 } 258 packet = b.Bytes() 259 sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv) 260 if err != nil { 261 return nil, nil, err 262 } 263 copy(packet[macSize:], sig) 264 // Add the hash to the front. Note: this doesn't protect the packet in any way. 265 hash = crypto.Keccak256(packet[macSize:]) 266 copy(packet, hash) 267 return packet, hash, nil 268 } 269 270 // recoverNodeKey computes the public key used to sign the given hash from the signature. 271 func recoverNodeKey(hash, sig []byte) (key Pubkey, err error) { 272 pubkey, err := crypto.Ecrecover(hash, sig) 273 if err != nil { 274 return key, err 275 } 276 copy(key[:], pubkey[1:]) 277 return key, nil 278 } 279 280 // EncodePubkey encodes a secp256k1 public key. 281 func EncodePubkey(key *ecdsa.PublicKey) Pubkey { 282 var e Pubkey 283 math.ReadBits(key.X, e[:len(e)/2]) 284 math.ReadBits(key.Y, e[len(e)/2:]) 285 return e 286 } 287 288 // DecodePubkey reads an encoded secp256k1 public key. 289 func DecodePubkey(curve elliptic.Curve, e Pubkey) (*ecdsa.PublicKey, error) { 290 p := &ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)} 291 half := len(e) / 2 292 p.X.SetBytes(e[:half]) 293 p.Y.SetBytes(e[half:]) 294 if !p.Curve.IsOnCurve(p.X, p.Y) { 295 return nil, ErrBadPoint 296 } 297 return p, nil 298 }