github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/network/extpool/pool.go (about) 1 package extpool 2 3 import ( 4 "container/list" 5 "errors" 6 "sync" 7 8 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 9 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 10 "github.com/nspcc-dev/neo-go/pkg/network/payload" 11 "github.com/nspcc-dev/neo-go/pkg/util" 12 ) 13 14 // Ledger is enough of Blockchain to satisfy Pool. 15 type Ledger interface { 16 BlockHeight() uint32 17 IsExtensibleAllowed(util.Uint160) bool 18 VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) (int64, error) 19 } 20 21 // Pool represents a pool of extensible payloads. 22 type Pool struct { 23 lock sync.RWMutex 24 verified map[util.Uint256]*list.Element 25 senders map[util.Uint160]*list.List 26 // singleCap represents the maximum number of payloads from a single sender. 27 singleCap int 28 chain Ledger 29 } 30 31 // New returns a new payload pool using the provided chain. 32 func New(bc Ledger, capacity int) *Pool { 33 if capacity <= 0 { 34 panic("invalid capacity") 35 } 36 37 return &Pool{ 38 verified: make(map[util.Uint256]*list.Element), 39 senders: make(map[util.Uint160]*list.List), 40 singleCap: capacity, 41 chain: bc, 42 } 43 } 44 45 var ( 46 errDisallowedSender = errors.New("disallowed sender") 47 errInvalidHeight = errors.New("invalid height") 48 ) 49 50 // Add adds an extensible payload to the pool. 51 // First return value specifies if the payload was new. 52 // Second one is nil if and only if the payload is valid. 53 func (p *Pool) Add(e *payload.Extensible) (bool, error) { 54 if ok, err := p.verify(e); err != nil || !ok { 55 return ok, err 56 } 57 58 p.lock.Lock() 59 defer p.lock.Unlock() 60 61 h := e.Hash() 62 if _, ok := p.verified[h]; ok { 63 return false, nil 64 } 65 66 lst, ok := p.senders[e.Sender] 67 if ok && lst.Len() >= p.singleCap { 68 value := lst.Remove(lst.Front()) 69 delete(p.verified, value.(*payload.Extensible).Hash()) 70 } else if !ok { 71 lst = list.New() 72 p.senders[e.Sender] = lst 73 } 74 75 p.verified[h] = lst.PushBack(e) 76 return true, nil 77 } 78 79 func (p *Pool) verify(e *payload.Extensible) (bool, error) { 80 if _, err := p.chain.VerifyWitness(e.Sender, e, &e.Witness, extensibleVerifyMaxGAS); err != nil { 81 return false, err 82 } 83 h := p.chain.BlockHeight() 84 if h < e.ValidBlockStart || e.ValidBlockEnd <= h { 85 // We can receive a consensus payload for the last or next block 86 // which leads to an unwanted node disconnect. 87 if e.ValidBlockEnd == h { 88 return false, nil 89 } 90 return false, errInvalidHeight 91 } 92 if !p.chain.IsExtensibleAllowed(e.Sender) { 93 return false, errDisallowedSender 94 } 95 return true, nil 96 } 97 98 // Get returns payload by hash. 99 func (p *Pool) Get(h util.Uint256) *payload.Extensible { 100 p.lock.RLock() 101 defer p.lock.RUnlock() 102 103 elem, ok := p.verified[h] 104 if !ok { 105 return nil 106 } 107 return elem.Value.(*payload.Extensible) 108 } 109 110 const extensibleVerifyMaxGAS = 6000000 111 112 // RemoveStale removes invalid payloads after block processing. 113 func (p *Pool) RemoveStale(index uint32) { 114 p.lock.Lock() 115 defer p.lock.Unlock() 116 117 for s, lst := range p.senders { 118 for elem := lst.Front(); elem != nil; { 119 e := elem.Value.(*payload.Extensible) 120 h := e.Hash() 121 old := elem 122 elem = elem.Next() 123 124 if e.ValidBlockEnd <= index || !p.chain.IsExtensibleAllowed(e.Sender) { 125 delete(p.verified, h) 126 lst.Remove(old) 127 continue 128 } 129 if _, err := p.chain.VerifyWitness(e.Sender, e, &e.Witness, extensibleVerifyMaxGAS); err != nil { 130 delete(p.verified, h) 131 lst.Remove(old) 132 continue 133 } 134 } 135 if lst.Len() == 0 { 136 delete(p.senders, s) 137 } 138 } 139 }