func txID(tx []byte) []byte {
	return types.Tx(tx).Hash()
}
txID is a hash of the Tx.
package mempool
import (
	"bytes"
	"container/list"
	"crypto/sha256"
	"fmt"
	"sync"
	"sync/atomic"
	abci "github.com/tendermint/tendermint/abci/types"
	cfg "github.com/tendermint/tendermint/config"
	auto "github.com/tendermint/tendermint/libs/autofile"
	"github.com/tendermint/tendermint/libs/clist"
	"github.com/tendermint/tendermint/libs/log"
	tmmath "github.com/tendermint/tendermint/libs/math"
	tmos "github.com/tendermint/tendermint/libs/os"
	tmsync "github.com/tendermint/tendermint/libs/sync"
	"github.com/tendermint/tendermint/p2p"
	"github.com/tendermint/tendermint/proxy"
	"github.com/tendermint/tendermint/types"
)
const TxKeySize = sha256.Size
var newline = []byte("\n")
type CListMempool struct {
	height   int64  
	txsBytes int64  
	notifiedTxsAvailable bool
	txsAvailable         chan struct{}  
	config *cfg.MempoolConfig
	updateMtx tmsync.RWMutex
	preCheck  PreCheckFunc
	postCheck PostCheckFunc
	wal          *auto.AutoFile  
	txs          *clist.CList    
	proxyAppConn proxy.AppConnMempool
	recheckCursor *clist.CElement  
	recheckEnd    *clist.CElement  
	txsMap sync.Map
	cache txCache
	logger log.Logger
	metrics *Metrics
}
var _ Mempool = &CListMempool{}
type CListMempoolOption func(*CListMempool)
func NewCListMempool(
	config *cfg.MempoolConfig,
	proxyAppConn proxy.AppConnMempool,
	height int64,
	options ...CListMempoolOption,
) *CListMempool {
	mempool := &CListMempool{
		config:        config,
		proxyAppConn:  proxyAppConn,
		txs:           clist.New(),
		height:        height,
		recheckCursor: nil,
		recheckEnd:    nil,
		logger:        log.NewNopLogger(),
		metrics:       NopMetrics(),
	}
	if config.CacheSize > 0 {
		mempool.cache = newMapTxCache(config.CacheSize)
	} else {
		mempool.cache = nopTxCache{}
	}
	proxyAppConn.SetResponseCallback(mempool.globalCb)
	for _, option := range options {
		option(mempool)
	}
	return mempool
}
func (mem *CListMempool) EnableTxsAvailable() {
	mem.txsAvailable = make(chan struct{}, 1)
}
func (mem *CListMempool) SetLogger(l log.Logger) {
	mem.logger = l
}
func WithPreCheck(f PreCheckFunc) CListMempoolOption {
	return func(mem *CListMempool) { mem.preCheck = f }
}
func WithPostCheck(f PostCheckFunc) CListMempoolOption {
	return func(mem *CListMempool) { mem.postCheck = f }
}
func WithMetrics(metrics *Metrics) CListMempoolOption {
	return func(mem *CListMempool) { mem.metrics = metrics }
}
func (mem *CListMempool) InitWAL() error {
	var (
		walDir  = mem.config.WalDir()
		walFile = walDir + "/wal"
	)
	const perm = 0700
	if err := tmos.EnsureDir(walDir, perm); err != nil {
		return err
	}
	af, err := auto.OpenAutoFile(walFile)
	if err != nil {
		return fmt.Errorf("can't open autofile %s: %w", walFile, err)
	}
	mem.wal = af
	return nil
}
func (mem *CListMempool) CloseWAL() {
	if err := mem.wal.Close(); err != nil {
		mem.logger.Error("Error closing WAL", "err", err)
	}
	mem.wal = nil
}
func (mem *CListMempool) Lock() {
	mem.updateMtx.Lock()
}
func (mem *CListMempool) Unlock() {
	mem.updateMtx.Unlock()
}
func (mem *CListMempool) Size() int {
	return mem.txs.Len()
}
func (mem *CListMempool) TxsBytes() int64 {
	return atomic.LoadInt64(&mem.txsBytes)
}
func (mem *CListMempool) FlushAppConn() error {
	return mem.proxyAppConn.FlushSync()
}
func (mem *CListMempool) Flush() {
	mem.updateMtx.RLock()
	defer mem.updateMtx.RUnlock()
	_ = atomic.SwapInt64(&mem.txsBytes, 0)
	mem.cache.Reset()
	for e := mem.txs.Front(); e != nil; e = e.Next() {
		mem.txs.Remove(e)
		e.DetachPrev()
	}
	mem.txsMap.Range(func(key, _ interface{}) bool {
		mem.txsMap.Delete(key)
		return true
	})
}
func (mem *CListMempool) TxsFront() *clist.CElement {
	return mem.txs.Front()
}
func (mem *CListMempool) TxsWaitChan() <-chan struct{} {
	return mem.txs.WaitChan()
}
func (mem *CListMempool) CheckTx(tx types.Tx, cb func(*abci.Response), txInfo TxInfo) error {
	mem.updateMtx.RLock()
	defer mem.updateMtx.RUnlock()
	txSize := len(tx)
	if err := mem.isFull(txSize); err != nil {
		return err
	}
	if txSize > mem.config.MaxTxBytes {
		return ErrTxTooLarge{mem.config.MaxTxBytes, txSize}
	}
	if mem.preCheck != nil {
		if err := mem.preCheck(tx); err != nil {
			return ErrPreCheck{err}
		}
	}
	if mem.wal != nil {
		_, err := mem.wal.Write(append([]byte(tx), newline...))
		if err != nil {
			return fmt.Errorf("wal.Write: %w", err)
		}
	}
	if err := mem.proxyAppConn.Error(); err != nil {
		return err
	}
	if !mem.cache.Push(tx) {
		if e, ok := mem.txsMap.Load(TxKey(tx)); ok {
			memTx := e.(*clist.CElement).Value.(*MempoolTx)
			memTx.senders.LoadOrStore(txInfo.SenderID, true)
		}
		return ErrTxInCache
	}
	reqRes := mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{Tx: tx})
	reqRes.SetCallback(mem.reqResCb(tx, txInfo.SenderID, txInfo.SenderP2PID, cb))
	return nil
}
func (mem *CListMempool) globalCb(req *abci.Request, res *abci.Response) {
	if mem.recheckCursor == nil {
		return
	}
	mem.metrics.RecheckTimes.Add(1)
	mem.resCbRecheck(req, res)
	mem.metrics.Size.Set(float64(mem.Size()))
}
func (mem *CListMempool) reqResCb(
	tx []byte,
	peerID uint16,
	peerP2PID p2p.ID,
	externalCb func(*abci.Response),
) func(res *abci.Response) {
	return func(res *abci.Response) {
		if mem.recheckCursor != nil {
			panic("recheck cursor is not nil in reqResCb")
		}
		mem.resCbFirstTime(tx, peerID, peerP2PID, res)
		mem.metrics.Size.Set(float64(mem.Size()))
		if externalCb != nil {
			externalCb(res)
		}
	}
}
func (mem *CListMempool) addTx(memTx *MempoolTx) {
	e := mem.txs.PushBack(memTx)
	mem.txsMap.Store(TxKey(memTx.Tx), e)
	atomic.AddInt64(&mem.txsBytes, int64(len(memTx.Tx)))
	mem.metrics.TxSizeBytes.Observe(float64(len(memTx.Tx)))
}
func (mem *CListMempool) removeTx(tx types.Tx, elem *clist.CElement, removeFromCache bool) {
	mem.txs.Remove(elem)
	elem.DetachPrev()
	mem.txsMap.Delete(TxKey(tx))
	atomic.AddInt64(&mem.txsBytes, int64(-len(tx)))
	if removeFromCache {
		mem.cache.Remove(tx)
	}
}
func (mem *CListMempool) RemoveTxByKey(txKey [TxKeySize]byte, removeFromCache bool) {
	if e, ok := mem.txsMap.Load(txKey); ok {
		memTx := e.(*clist.CElement).Value.(*MempoolTx)
		if memTx != nil {
			mem.removeTx(memTx.Tx, e.(*clist.CElement), removeFromCache)
		}
	}
}
func (mem *CListMempool) isFull(txSize int) error {
	var (
		memSize  = mem.Size()
		txsBytes = mem.TxsBytes()
	)
	if memSize >= mem.config.Size || int64(txSize)+txsBytes > mem.config.MaxTxsBytes {
		return ErrMempoolIsFull{
			memSize, mem.config.Size,
			txsBytes, mem.config.MaxTxsBytes,
		}
	}
	return nil
}
func (mem *CListMempool) resCbFirstTime(
	tx []byte,
	peerID uint16,
	peerP2PID p2p.ID,
	res *abci.Response,
) {
	switch r := res.Value.(type) {
	case *abci.Response_CheckTx:
		var postCheckErr error
		if mem.postCheck != nil {
			postCheckErr = mem.postCheck(tx, r.CheckTx)
		}
		if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
			if err := mem.isFull(len(tx)); err != nil {
				mem.cache.Remove(tx)
				mem.logger.Error(err.Error())
				return
			}
			memTx := &MempoolTx{
				height:    mem.height,
				gasWanted: r.CheckTx.GasWanted,
				Tx:        tx,
			}
			memTx.senders.Store(peerID, true)
			mem.addTx(memTx)
			mem.logger.Debug("added good transaction",
				"tx", txID(tx),
				"res", r,
				"height", memTx.height,
				"total", mem.Size(),
			)
			mem.notifyTxsAvailable()
		} else {
			mem.logger.Debug("rejected bad transaction",
				"tx", txID(tx), "peerID", peerP2PID, "res", r, "err", postCheckErr)
			mem.metrics.FailedTxs.Add(1)
			if !mem.config.KeepInvalidTxsInCache {
				mem.cache.Remove(tx)
			}
		}
	default:
	}
}
func (mem *CListMempool) resCbRecheck(req *abci.Request, res *abci.Response) {
	switch r := res.Value.(type) {
	case *abci.Response_CheckTx:
		tx := req.GetCheckTx().Tx
		memTx := mem.recheckCursor.Value.(*MempoolTx)
		if !bytes.Equal(tx, memTx.Tx) {
			panic(fmt.Sprintf(
				"Unexpected tx response from proxy during recheck\nExpected %X, got %X",
				memTx.Tx,
				tx))
		}
		var postCheckErr error
		if mem.postCheck != nil {
			postCheckErr = mem.postCheck(tx, r.CheckTx)
		}
		if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
		} else {
			mem.logger.Debug("tx is no longer valid", "tx", txID(tx), "res", r, "err", postCheckErr)
			mem.removeTx(tx, mem.recheckCursor, !mem.config.KeepInvalidTxsInCache)
		}
		if mem.recheckCursor == mem.recheckEnd {
			mem.recheckCursor = nil
		} else {
			mem.recheckCursor = mem.recheckCursor.Next()
		}
		if mem.recheckCursor == nil {
			mem.logger.Debug("done rechecking txs")
			if mem.Size() > 0 {
				mem.notifyTxsAvailable()
			}
		}
	default:
	}
}
func (mem *CListMempool) TxsAvailable() <-chan struct{} {
	return mem.txsAvailable
}
func (mem *CListMempool) notifyTxsAvailable() {
	if mem.Size() == 0 {
		panic("notified txs available but mempool is empty!")
	}
	if mem.txsAvailable != nil && !mem.notifiedTxsAvailable {
		mem.notifiedTxsAvailable = true
		select {
		case mem.txsAvailable <- struct{}{}:
		default:
		}
	}
}
func (mem *CListMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
	mem.updateMtx.RLock()
	defer mem.updateMtx.RUnlock()
	var totalGas int64
	txs := make([]types.Tx, 0, mem.txs.Len())
	for e := mem.txs.Front(); e != nil; e = e.Next() {
		memTx := e.Value.(*MempoolTx)
		dataSize := types.ComputeProtoSizeForTxs(append(txs, memTx.Tx))
		if maxBytes > -1 && dataSize > maxBytes {
			return txs
		}
		newTotalGas := totalGas + memTx.gasWanted
		if maxGas > -1 && newTotalGas > maxGas {
			return txs
		}
		totalGas = newTotalGas
		txs = append(txs, memTx.Tx)
	}
	return txs
}
func (mem *CListMempool) ReapMaxTxs(max int) types.Txs {
	mem.updateMtx.RLock()
	defer mem.updateMtx.RUnlock()
	if max < 0 {
		max = mem.txs.Len()
	}
	txs := make([]types.Tx, 0, tmmath.MinInt(mem.txs.Len(), max))
	for e := mem.txs.Front(); e != nil && len(txs) <= max; e = e.Next() {
		memTx := e.Value.(*MempoolTx)
		txs = append(txs, memTx.Tx)
	}
	return txs
}
func (mem *CListMempool) Update(
	height int64,
	txs types.Txs,
	deliverTxResponses []*abci.ResponseDeliverTx,
	preCheck PreCheckFunc,
	postCheck PostCheckFunc,
) error {
	mem.height = height
	mem.notifiedTxsAvailable = false
	if preCheck != nil {
		mem.preCheck = preCheck
	}
	if postCheck != nil {
		mem.postCheck = postCheck
	}
	for i, tx := range txs {
		if deliverTxResponses[i].Code == abci.CodeTypeOK {
			_ = mem.cache.Push(tx)
		} else if !mem.config.KeepInvalidTxsInCache {
			mem.cache.Remove(tx)
		}
		if e, ok := mem.txsMap.Load(TxKey(tx)); ok {
			mem.removeTx(tx, e.(*clist.CElement), false)
		}
	}
	if mem.Size() > 0 {
		if mem.config.Recheck {
			mem.logger.Debug("recheck txs", "numtxs", mem.Size(), "height", height)
			mem.recheckTxs()
		} else {
			mem.notifyTxsAvailable()
		}
	}
	mem.metrics.Size.Set(float64(mem.Size()))
	return nil
}
func (mem *CListMempool) recheckTxs() {
	if mem.Size() == 0 {
		panic("recheckTxs is called, but the mempool is empty")
	}
	mem.recheckCursor = mem.txs.Front()
	mem.recheckEnd = mem.txs.Back()
	for e := mem.txs.Front(); e != nil; e = e.Next() {
		memTx := e.Value.(*MempoolTx)
		mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{
			Tx:   memTx.Tx,
			Type: abci.CheckTxType_Recheck,
		})
	}
	mem.proxyAppConn.FlushAsync()
}
type MempoolTx struct {
	height    int64     
	gasWanted int64     
	Tx        types.Tx  
	senders sync.Map
}
func (memTx *MempoolTx) Height() int64 {
	return atomic.LoadInt64(&memTx.height)
}
type txCache interface {
	Reset()
	Push(tx types.Tx) bool
	Remove(tx types.Tx)
}
type mapTxCache struct {
	mtx      tmsync.Mutex
	size     int
	cacheMap map[[TxKeySize]byte]*list.Element
	list     *list.List
}
var _ txCache = (*mapTxCache)(nil)
func newMapTxCache(cacheSize int) *mapTxCache {
	return &mapTxCache{
		size:     cacheSize,
		cacheMap: make(map[[TxKeySize]byte]*list.Element, cacheSize),
		list:     list.New(),
	}
}
func (cache *mapTxCache) Reset() {
	cache.mtx.Lock()
	cache.cacheMap = make(map[[TxKeySize]byte]*list.Element, cache.size)
	cache.list.Init()
	cache.mtx.Unlock()
}
func (cache *mapTxCache) Push(tx types.Tx) bool {
	cache.mtx.Lock()
	defer cache.mtx.Unlock()
	txHash := TxKey(tx)
	if moved, exists := cache.cacheMap[txHash]; exists {
		cache.list.MoveToBack(moved)
		return false
	}
	if cache.list.Len() >= cache.size {
		popped := cache.list.Front()
		if popped != nil {
			poppedTxHash := popped.Value.([TxKeySize]byte)
			delete(cache.cacheMap, poppedTxHash)
			cache.list.Remove(popped)
		}
	}
	e := cache.list.PushBack(txHash)
	cache.cacheMap[txHash] = e
	return true
}
func (cache *mapTxCache) Remove(tx types.Tx) {
	cache.mtx.Lock()
	txHash := TxKey(tx)
	popped := cache.cacheMap[txHash]
	delete(cache.cacheMap, txHash)
	if popped != nil {
		cache.list.Remove(popped)
	}
	cache.mtx.Unlock()
}
type nopTxCache struct{}
var _ txCache = (*nopTxCache)(nil)
func (nopTxCache) Reset()             {}
func (nopTxCache) Push(types.Tx) bool { return true }
func (nopTxCache) Remove(types.Tx)    {}
func TxKey(tx types.Tx) [TxKeySize]byte {
	return sha256.Sum256(tx)
}