github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/p2p/discover/table_util_test.go (about) 1 // Copyright 2018 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 discover 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 "encoding/hex" 23 "errors" 24 "fmt" 25 "math/rand" 26 "net" 27 "slices" 28 "sync" 29 30 "github.com/ethereum/go-ethereum/crypto" 31 "github.com/ethereum/go-ethereum/p2p/enode" 32 "github.com/ethereum/go-ethereum/p2p/enr" 33 ) 34 35 var nullNode *enode.Node 36 37 func init() { 38 var r enr.Record 39 r.Set(enr.IP{0, 0, 0, 0}) 40 nullNode = enode.SignNull(&r, enode.ID{}) 41 } 42 43 func newTestTable(t transport) (*Table, *enode.DB) { 44 cfg := Config{} 45 db, _ := enode.OpenDB("") 46 tab, _ := newTable(t, db, cfg) 47 go tab.loop() 48 return tab, db 49 } 50 51 // nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld. 52 func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node { 53 var r enr.Record 54 r.Set(enr.IP(ip)) 55 r.Set(enr.UDP(30303)) 56 return wrapNode(enode.SignNull(&r, idAtDistance(base, ld))) 57 } 58 59 // nodesAtDistance creates n nodes for which enode.LogDist(base, node.ID()) == ld. 60 func nodesAtDistance(base enode.ID, ld int, n int) []*enode.Node { 61 results := make([]*enode.Node, n) 62 for i := range results { 63 results[i] = unwrapNode(nodeAtDistance(base, ld, intIP(i))) 64 } 65 return results 66 } 67 68 func nodesToRecords(nodes []*enode.Node) []*enr.Record { 69 records := make([]*enr.Record, len(nodes)) 70 for i := range nodes { 71 records[i] = nodes[i].Record() 72 } 73 return records 74 } 75 76 // idAtDistance returns a random hash such that enode.LogDist(a, b) == n 77 func idAtDistance(a enode.ID, n int) (b enode.ID) { 78 if n == 0 { 79 return a 80 } 81 // flip bit at position n, fill the rest with random bits 82 b = a 83 pos := len(a) - n/8 - 1 84 bit := byte(0x01) << (byte(n%8) - 1) 85 if bit == 0 { 86 pos++ 87 bit = 0x80 88 } 89 b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits 90 for i := pos + 1; i < len(a); i++ { 91 b[i] = byte(rand.Intn(255)) 92 } 93 return b 94 } 95 96 func intIP(i int) net.IP { 97 return net.IP{byte(i), 0, 2, byte(i)} 98 } 99 100 // fillBucket inserts nodes into the given bucket until it is full. 101 func fillBucket(tab *Table, n *node) (last *node) { 102 ld := enode.LogDist(tab.self().ID(), n.ID()) 103 b := tab.bucket(n.ID()) 104 for len(b.entries) < bucketSize { 105 b.entries = append(b.entries, nodeAtDistance(tab.self().ID(), ld, intIP(ld))) 106 } 107 return b.entries[bucketSize-1] 108 } 109 110 // fillTable adds nodes the table to the end of their corresponding bucket 111 // if the bucket is not full. The caller must not hold tab.mutex. 112 func fillTable(tab *Table, nodes []*node, setLive bool) { 113 for _, n := range nodes { 114 if setLive { 115 n.livenessChecks = 1 116 } 117 tab.addSeenNode(n) 118 } 119 } 120 121 type pingRecorder struct { 122 mu sync.Mutex 123 dead, pinged map[enode.ID]bool 124 records map[enode.ID]*enode.Node 125 n *enode.Node 126 } 127 128 func newPingRecorder() *pingRecorder { 129 var r enr.Record 130 r.Set(enr.IP{0, 0, 0, 0}) 131 n := enode.SignNull(&r, enode.ID{}) 132 133 return &pingRecorder{ 134 dead: make(map[enode.ID]bool), 135 pinged: make(map[enode.ID]bool), 136 records: make(map[enode.ID]*enode.Node), 137 n: n, 138 } 139 } 140 141 // updateRecord updates a node record. Future calls to ping and 142 // RequestENR will return this record. 143 func (t *pingRecorder) updateRecord(n *enode.Node) { 144 t.mu.Lock() 145 defer t.mu.Unlock() 146 t.records[n.ID()] = n 147 } 148 149 // Stubs to satisfy the transport interface. 150 func (t *pingRecorder) Self() *enode.Node { return nullNode } 151 func (t *pingRecorder) lookupSelf() []*enode.Node { return nil } 152 func (t *pingRecorder) lookupRandom() []*enode.Node { return nil } 153 154 // ping simulates a ping request. 155 func (t *pingRecorder) ping(n *enode.Node) (seq uint64, err error) { 156 t.mu.Lock() 157 defer t.mu.Unlock() 158 159 t.pinged[n.ID()] = true 160 if t.dead[n.ID()] { 161 return 0, errTimeout 162 } 163 if t.records[n.ID()] != nil { 164 seq = t.records[n.ID()].Seq() 165 } 166 return seq, nil 167 } 168 169 // RequestENR simulates an ENR request. 170 func (t *pingRecorder) RequestENR(n *enode.Node) (*enode.Node, error) { 171 t.mu.Lock() 172 defer t.mu.Unlock() 173 174 if t.dead[n.ID()] || t.records[n.ID()] == nil { 175 return nil, errTimeout 176 } 177 return t.records[n.ID()], nil 178 } 179 180 func hasDuplicates(slice []*node) bool { 181 seen := make(map[enode.ID]bool, len(slice)) 182 for i, e := range slice { 183 if e == nil { 184 panic(fmt.Sprintf("nil *Node at %d", i)) 185 } 186 if seen[e.ID()] { 187 return true 188 } 189 seen[e.ID()] = true 190 } 191 return false 192 } 193 194 // checkNodesEqual checks whether the two given node lists contain the same nodes. 195 func checkNodesEqual(got, want []*enode.Node) error { 196 if len(got) == len(want) { 197 for i := range got { 198 if !nodeEqual(got[i], want[i]) { 199 goto NotEqual 200 } 201 } 202 } 203 return nil 204 205 NotEqual: 206 output := new(bytes.Buffer) 207 fmt.Fprintf(output, "got %d nodes:\n", len(got)) 208 for _, n := range got { 209 fmt.Fprintf(output, " %v %v\n", n.ID(), n) 210 } 211 fmt.Fprintf(output, "want %d:\n", len(want)) 212 for _, n := range want { 213 fmt.Fprintf(output, " %v %v\n", n.ID(), n) 214 } 215 return errors.New(output.String()) 216 } 217 218 func nodeEqual(n1 *enode.Node, n2 *enode.Node) bool { 219 return n1.ID() == n2.ID() && n1.IP().Equal(n2.IP()) 220 } 221 222 func sortByID(nodes []*enode.Node) { 223 slices.SortFunc(nodes, func(a, b *enode.Node) int { 224 return bytes.Compare(a.ID().Bytes(), b.ID().Bytes()) 225 }) 226 } 227 228 func sortedByDistanceTo(distbase enode.ID, slice []*node) bool { 229 return slices.IsSortedFunc(slice, func(a, b *node) int { 230 return enode.DistCmp(distbase, a.ID(), b.ID()) 231 }) 232 } 233 234 // hexEncPrivkey decodes h as a private key. 235 func hexEncPrivkey(h string) *ecdsa.PrivateKey { 236 b, err := hex.DecodeString(h) 237 if err != nil { 238 panic(err) 239 } 240 key, err := crypto.ToECDSA(b) 241 if err != nil { 242 panic(err) 243 } 244 return key 245 } 246 247 // hexEncPubkey decodes h as a public key. 248 func hexEncPubkey(h string) (ret encPubkey) { 249 b, err := hex.DecodeString(h) 250 if err != nil { 251 panic(err) 252 } 253 if len(b) != len(ret) { 254 panic("invalid length") 255 } 256 copy(ret[:], b) 257 return ret 258 }