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  }