github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/discover/table_util_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package discover 19 20 import ( 21 "bytes" 22 "crypto/ecdsa" 23 "encoding/hex" 24 "errors" 25 "fmt" 26 "math/rand" 27 "net" 28 "reflect" 29 "sort" 30 "sync" 31 32 "github.com/AigarNetwork/aigar/crypto" 33 "github.com/AigarNetwork/aigar/log" 34 "github.com/AigarNetwork/aigar/p2p/enode" 35 "github.com/AigarNetwork/aigar/p2p/enr" 36 ) 37 38 var nullNode *enode.Node 39 40 func init() { 41 var r enr.Record 42 r.Set(enr.IP{0, 0, 0, 0}) 43 nullNode = enode.SignNull(&r, enode.ID{}) 44 } 45 46 func newTestTable(t transport) (*Table, *enode.DB) { 47 db, _ := enode.OpenDB("") 48 tab, _ := newTable(t, db, nil, log.Root()) 49 go tab.loop() 50 return tab, db 51 } 52 53 // nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld. 54 func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node { 55 var r enr.Record 56 r.Set(enr.IP(ip)) 57 return wrapNode(enode.SignNull(&r, idAtDistance(base, ld))) 58 } 59 60 // idAtDistance returns a random hash such that enode.LogDist(a, b) == n 61 func idAtDistance(a enode.ID, n int) (b enode.ID) { 62 if n == 0 { 63 return a 64 } 65 // flip bit at position n, fill the rest with random bits 66 b = a 67 pos := len(a) - n/8 - 1 68 bit := byte(0x01) << (byte(n%8) - 1) 69 if bit == 0 { 70 pos++ 71 bit = 0x80 72 } 73 b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits 74 for i := pos + 1; i < len(a); i++ { 75 b[i] = byte(rand.Intn(255)) 76 } 77 return b 78 } 79 80 func intIP(i int) net.IP { 81 return net.IP{byte(i), 0, 2, byte(i)} 82 } 83 84 // fillBucket inserts nodes into the given bucket until it is full. 85 func fillBucket(tab *Table, n *node) (last *node) { 86 ld := enode.LogDist(tab.self().ID(), n.ID()) 87 b := tab.bucket(n.ID()) 88 for len(b.entries) < bucketSize { 89 b.entries = append(b.entries, nodeAtDistance(tab.self().ID(), ld, intIP(ld))) 90 } 91 return b.entries[bucketSize-1] 92 } 93 94 // fillTable adds nodes the table to the end of their corresponding bucket 95 // if the bucket is not full. The caller must not hold tab.mutex. 96 func fillTable(tab *Table, nodes []*node) { 97 for _, n := range nodes { 98 tab.addSeenNode(n) 99 } 100 } 101 102 type pingRecorder struct { 103 mu sync.Mutex 104 dead, pinged map[enode.ID]bool 105 records map[enode.ID]*enode.Node 106 n *enode.Node 107 } 108 109 func newPingRecorder() *pingRecorder { 110 var r enr.Record 111 r.Set(enr.IP{0, 0, 0, 0}) 112 n := enode.SignNull(&r, enode.ID{}) 113 114 return &pingRecorder{ 115 dead: make(map[enode.ID]bool), 116 pinged: make(map[enode.ID]bool), 117 records: make(map[enode.ID]*enode.Node), 118 n: n, 119 } 120 } 121 122 // setRecord updates a node record. Future calls to ping and 123 // requestENR will return this record. 124 func (t *pingRecorder) updateRecord(n *enode.Node) { 125 t.mu.Lock() 126 defer t.mu.Unlock() 127 t.records[n.ID()] = n 128 } 129 130 // Stubs to satisfy the transport interface. 131 func (t *pingRecorder) Self() *enode.Node { return nullNode } 132 func (t *pingRecorder) lookupSelf() []*enode.Node { return nil } 133 func (t *pingRecorder) lookupRandom() []*enode.Node { return nil } 134 func (t *pingRecorder) close() {} 135 136 // ping simulates a ping request. 137 func (t *pingRecorder) ping(n *enode.Node) (seq uint64, err error) { 138 t.mu.Lock() 139 defer t.mu.Unlock() 140 141 t.pinged[n.ID()] = true 142 if t.dead[n.ID()] { 143 return 0, errTimeout 144 } 145 if t.records[n.ID()] != nil { 146 seq = t.records[n.ID()].Seq() 147 } 148 return seq, nil 149 } 150 151 // requestENR simulates an ENR request. 152 func (t *pingRecorder) RequestENR(n *enode.Node) (*enode.Node, error) { 153 t.mu.Lock() 154 defer t.mu.Unlock() 155 156 if t.dead[n.ID()] || t.records[n.ID()] == nil { 157 return nil, errTimeout 158 } 159 return t.records[n.ID()], nil 160 } 161 162 func hasDuplicates(slice []*node) bool { 163 seen := make(map[enode.ID]bool) 164 for i, e := range slice { 165 if e == nil { 166 panic(fmt.Sprintf("nil *Node at %d", i)) 167 } 168 if seen[e.ID()] { 169 return true 170 } 171 seen[e.ID()] = true 172 } 173 return false 174 } 175 176 func checkNodesEqual(got, want []*enode.Node) error { 177 if reflect.DeepEqual(got, want) { 178 return nil 179 } 180 output := new(bytes.Buffer) 181 fmt.Fprintf(output, "got %d nodes:\n", len(got)) 182 for _, n := range got { 183 fmt.Fprintf(output, " %v %v\n", n.ID(), n) 184 } 185 fmt.Fprintf(output, "want %d:\n", len(want)) 186 for _, n := range want { 187 fmt.Fprintf(output, " %v %v\n", n.ID(), n) 188 } 189 return errors.New(output.String()) 190 } 191 192 func sortByID(nodes []*enode.Node) { 193 sort.Slice(nodes, func(i, j int) bool { 194 return string(nodes[i].ID().Bytes()) < string(nodes[j].ID().Bytes()) 195 }) 196 } 197 198 func sortedByDistanceTo(distbase enode.ID, slice []*node) bool { 199 return sort.SliceIsSorted(slice, func(i, j int) bool { 200 return enode.DistCmp(distbase, slice[i].ID(), slice[j].ID()) < 0 201 }) 202 } 203 204 func hexEncPrivkey(h string) *ecdsa.PrivateKey { 205 b, err := hex.DecodeString(h) 206 if err != nil { 207 panic(err) 208 } 209 key, err := crypto.ToECDSA(b) 210 if err != nil { 211 panic(err) 212 } 213 return key 214 } 215 216 func hexEncPubkey(h string) (ret encPubkey) { 217 b, err := hex.DecodeString(h) 218 if err != nil { 219 panic(err) 220 } 221 if len(b) != len(ret) { 222 panic("invalid length") 223 } 224 copy(ret[:], b) 225 return ret 226 }