github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/usif/fees.go (about)

     1  package usif
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/gob"
     6  	"os"
     7  	"sync"
     8  
     9  	"github.com/piotrnar/gocoin/client/common"
    10  	"github.com/piotrnar/gocoin/lib/btc"
    11  	"github.com/piotrnar/gocoin/lib/chain"
    12  )
    13  
    14  const (
    15  	BLKFES_FILE_NAME = "blkfees.gob"
    16  )
    17  
    18  var (
    19  	BlockFeesMutex sync.Mutex
    20  	BlockFees      map[uint32][][3]uint64 = make(map[uint32][][3]uint64) // [0]=Weight  [1]-Fee  [2]-Group
    21  	BlockFeesDirty bool                                                  // it true, clean up old data
    22  )
    23  
    24  func ProcessBlockFees(height uint32, bl *btc.Block) {
    25  	if len(bl.Txs) < 2 {
    26  		return
    27  	}
    28  
    29  	txs := make(map[[32]byte]int, len(bl.Txs)) // group_id -> transaciton_idx
    30  	txs[bl.Txs[0].Hash.Hash] = 0
    31  
    32  	fees := make([][3]uint64, len(bl.Txs)-1)
    33  
    34  	for i := 1; i < len(bl.Txs); i++ {
    35  		txs[bl.Txs[i].Hash.Hash] = i
    36  		fees[i-1][0] = uint64(3*bl.Txs[i].NoWitSize + bl.Txs[i].Size)
    37  		fees[i-1][1] = uint64(bl.Txs[i].Fee)
    38  		fees[i-1][2] = uint64(i)
    39  	}
    40  
    41  	for i := 1; i < len(bl.Txs); i++ {
    42  		for _, inp := range bl.Txs[i].TxIn {
    43  			if paretidx, yes := txs[inp.Input.Hash]; yes {
    44  				if fees[paretidx-1][2] < fees[i-1][2] { // only update it for a lower index
    45  					fees[i-1][2] = fees[paretidx-1][2]
    46  				}
    47  			}
    48  		}
    49  	}
    50  
    51  	BlockFeesMutex.Lock()
    52  	BlockFees[height] = fees
    53  	BlockFeesDirty = true
    54  	BlockFeesMutex.Unlock()
    55  }
    56  
    57  func ExpireBlockFees() {
    58  	var height uint32
    59  	common.Last.Lock()
    60  	height = common.Last.Block.Height
    61  	common.Last.Unlock()
    62  
    63  	if height <= 144 {
    64  		return
    65  	}
    66  	height -= 144
    67  
    68  	BlockFeesMutex.Lock()
    69  	if BlockFeesDirty {
    70  		for k, _ := range BlockFees {
    71  			if k < height {
    72  				delete(BlockFees, k)
    73  			}
    74  		}
    75  		BlockFeesDirty = false
    76  	}
    77  	BlockFeesMutex.Unlock()
    78  }
    79  
    80  func SaveBlockFees() {
    81  	f, er := os.Create(common.GocoinHomeDir + BLKFES_FILE_NAME)
    82  	if er != nil {
    83  		println("SaveBlockFees:", er.Error())
    84  		return
    85  	}
    86  
    87  	ExpireBlockFees()
    88  	buf := bufio.NewWriter(f)
    89  	er = gob.NewEncoder(buf).Encode(BlockFees)
    90  
    91  	if er != nil {
    92  		println("SaveBlockFees:", er.Error())
    93  	}
    94  
    95  	buf.Flush()
    96  	f.Close()
    97  
    98  }
    99  
   100  func LoadBlockFees() {
   101  	f, er := os.Open(common.GocoinHomeDir + BLKFES_FILE_NAME)
   102  	if er != nil {
   103  		println("LoadBlockFees:", er.Error())
   104  		return
   105  	}
   106  
   107  	buf := bufio.NewReader(f)
   108  	er = gob.NewDecoder(buf).Decode(&BlockFees)
   109  	if er != nil {
   110  		println("LoadBlockFees:", er.Error())
   111  	}
   112  
   113  	f.Close()
   114  }
   115  
   116  var (
   117  	AverageFeeMutex     sync.Mutex
   118  	AverageFeeBytes     uint64
   119  	AverageFeeTotal     uint64
   120  	AverageFee_SPB      float64
   121  	averageFeeLastBlock *chain.BlockTreeNode
   122  	averageFeeLastCount uint = 0xffffffff
   123  )
   124  
   125  func GetAverageFee() float64 {
   126  	common.Last.Mutex.Lock()
   127  	end := common.Last.Block
   128  	common.Last.Mutex.Unlock()
   129  
   130  	common.LockCfg()
   131  	blocks := common.CFG.Stat.FeesBlks
   132  	common.UnlockCfg()
   133  	if blocks <= 0 {
   134  		blocks = 1 // at leats one block
   135  	}
   136  
   137  	AverageFeeMutex.Lock()
   138  	defer AverageFeeMutex.Unlock()
   139  
   140  	if end == averageFeeLastBlock && averageFeeLastCount == blocks {
   141  		return AverageFee_SPB // we've already calculated for this block
   142  	}
   143  
   144  	averageFeeLastBlock = end
   145  	averageFeeLastCount = blocks
   146  
   147  	AverageFeeBytes = 0
   148  	AverageFeeTotal = 0
   149  
   150  	for blocks > 0 {
   151  		bl, _, e := common.BlockChain.Blocks.BlockGet(end.BlockHash)
   152  		if e != nil {
   153  			return 0
   154  		}
   155  		block, e := btc.NewBlockX(bl, end.BlockHash)
   156  		if e != nil {
   157  			return 0
   158  		}
   159  
   160  		rb, cbasetx := GetReceivedBlockX(block)
   161  		var fees_from_this_block int64
   162  		for o := range cbasetx.TxOut {
   163  			fees_from_this_block += int64(cbasetx.TxOut[o].Value)
   164  		}
   165  		fees_from_this_block -= int64(btc.GetBlockReward(end.Height))
   166  
   167  		if fees_from_this_block > 0 {
   168  			AverageFeeTotal += uint64(fees_from_this_block)
   169  		}
   170  
   171  		AverageFeeBytes += uint64(rb.ThePaidVSize)
   172  
   173  		blocks--
   174  		end = end.Parent
   175  	}
   176  	if AverageFeeBytes == 0 {
   177  		if AverageFeeTotal != 0 {
   178  			panic("Impossible that miner gest a fee with no transactions in the block")
   179  		}
   180  		AverageFee_SPB = 0
   181  	} else {
   182  		AverageFee_SPB = float64(AverageFeeTotal) / float64(AverageFeeBytes)
   183  	}
   184  	return AverageFee_SPB
   185  }