github.com/0xsequence/ethkit@v1.25.0/ethreceipts/finalizer.go (about)

     1  package ethreceipts
     2  
     3  import (
     4  	"math/big"
     5  	"sort"
     6  	"sync"
     7  
     8  	"github.com/0xsequence/ethkit"
     9  )
    10  
    11  type finalizer struct {
    12  	queue               []finalTxn
    13  	txns                map[ethkit.Hash]struct{}
    14  	numBlocksToFinality *big.Int
    15  	mu                  sync.Mutex
    16  }
    17  
    18  type finalTxn struct {
    19  	receipt  Receipt
    20  	blockNum *big.Int
    21  }
    22  
    23  func (f *finalizer) len() int {
    24  	f.mu.Lock()
    25  	defer f.mu.Unlock()
    26  	return len(f.queue)
    27  }
    28  
    29  // func (f *finalizer) lastBlockNum() *big.Int {
    30  // 	f.mu.Lock()
    31  // 	defer f.mu.Unlock()
    32  // 	if len(f.queue) == 0 {
    33  // 		return big.NewInt(0)
    34  // 	}
    35  // 	return f.queue[0].blockNum
    36  // }
    37  
    38  func (f *finalizer) enqueue(filterID uint64, receipt Receipt, blockNum *big.Int) {
    39  	f.mu.Lock()
    40  	defer f.mu.Unlock()
    41  
    42  	if receipt.Final {
    43  		// do not enqueue if the receipt is already final
    44  		return
    45  	}
    46  
    47  	txnHash := receipt.TransactionHash()
    48  
    49  	// txn id based on the hash + filterID to ensure we get finalize callback for any unique filterID
    50  	txnID := txnHash
    51  	if filterID > 0 {
    52  		for i := 0; i < 8; i++ {
    53  			txnID[i] = txnID[i] + byte(filterID>>i)
    54  		}
    55  	}
    56  
    57  	if _, ok := f.txns[txnID]; ok {
    58  		// update the blockNum if we already have this txn, as it could have been included
    59  		// again after a reorg in a new block
    60  		for i, entry := range f.queue {
    61  			if entry.receipt.TransactionHash() == txnHash {
    62  				f.queue[i] = finalTxn{receipt, blockNum}
    63  			}
    64  		}
    65  		return
    66  	}
    67  
    68  	// append new
    69  	f.queue = append(f.queue, finalTxn{receipt, blockNum})
    70  	f.txns[txnID] = struct{}{}
    71  
    72  	// sort block order from oldest to newest in case of a reorg
    73  	if len(f.queue) >= 2 && f.queue[0].blockNum.Cmp(f.queue[1].blockNum) < 0 {
    74  		sort.SliceStable(f.queue, func(i, j int) bool {
    75  			return f.queue[i].blockNum.Cmp(f.queue[j].blockNum) < 0
    76  		})
    77  	}
    78  }
    79  
    80  func (f *finalizer) dequeue(currentBlockNum *big.Int) []finalTxn {
    81  	f.mu.Lock()
    82  	defer f.mu.Unlock()
    83  
    84  	finalTxns := []finalTxn{}
    85  
    86  	for _, txn := range f.queue {
    87  		if currentBlockNum.Cmp(big.NewInt(0).Add(txn.blockNum, f.numBlocksToFinality)) > 0 {
    88  			finalTxns = append(finalTxns, txn)
    89  		}
    90  	}
    91  
    92  	if len(finalTxns) > 0 {
    93  		f.queue = f.queue[len(finalTxns):]
    94  	}
    95  
    96  	return finalTxns
    97  }