/
elrewards.go
120 lines (108 loc) · 3.65 KB
/
elrewards.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package elrewards
import (
"context"
"fmt"
"log"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
)
func GetELRewardForBlock(block interfaces.SignedBeaconBlock, client *rpc.Client) (*big.Int, error) {
exec, err := block.Block().Body().Execution()
if err != nil {
return nil, err
}
txs, err := exec.Transactions()
if err != nil {
return nil, err
}
txHashes := []common.Hash{}
for _, tx := range txs {
var decTx gethTypes.Transaction
err := decTx.UnmarshalBinary([]byte(tx))
if err != nil {
return nil, err
}
txHashes = append(txHashes, decTx.Hash())
}
var txReceipts []*TxReceipt
for j := 0; j < 10; j++ { // retry up to 10 times
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
txReceipts, err = batchRequestReceipts(ctx, client, txHashes)
if err == nil {
cancel()
break
} else {
log.Printf("error doing batchRequestReceipts for slot %v: %v", block.Block().Slot(), err)
time.Sleep(time.Duration(j) * time.Second)
}
cancel()
}
if err != nil {
return nil, fmt.Errorf("error doing batchRequestReceipts for slot %v: %w", block.Block().Slot(), err)
}
totalTxFee := big.NewInt(0)
for _, r := range txReceipts {
if r.EffectiveGasPrice == nil {
return nil, fmt.Errorf("no EffectiveGasPrice for slot %v: %v", block.Block().Slot(), txHashes)
}
txFee := new(big.Int).Mul(r.EffectiveGasPrice.ToInt(), new(big.Int).SetUint64(uint64(r.GasUsed)))
totalTxFee.Add(totalTxFee, txFee)
}
// base fee per gas is stored little-endian but we need it
// big-endian for big.Int.
var baseFeePerGasBEBytes [32]byte
for i := 0; i < 32; i++ {
baseFeePerGasBEBytes[i] = exec.BaseFeePerGas()[32-1-i]
}
baseFeePerGas := new(big.Int).SetBytes(baseFeePerGasBEBytes[:])
burntFee := new(big.Int).Mul(baseFeePerGas, new(big.Int).SetUint64(exec.GasUsed()))
totalTxFee.Sub(totalTxFee, burntFee)
return totalTxFee, nil
}
func batchRequestReceipts(ctx context.Context, elClient *rpc.Client, txHashes []common.Hash) ([]*TxReceipt, error) {
elems := make([]rpc.BatchElem, 0, len(txHashes))
errors := make([]error, 0, len(txHashes))
txReceipts := make([]*TxReceipt, len(txHashes))
for i, h := range txHashes {
txReceipt := &TxReceipt{}
err := error(nil)
elems = append(elems, rpc.BatchElem{
Method: "eth_getTransactionReceipt",
Args: []interface{}{h.Hex()},
Result: txReceipt,
Error: err,
})
txReceipts[i] = txReceipt
errors = append(errors, err)
}
ioErr := elClient.BatchCallContext(ctx, elems)
if ioErr != nil {
return nil, fmt.Errorf("io-error when fetching tx-receipts: %w", ioErr)
}
for _, e := range errors {
if e != nil {
return nil, fmt.Errorf("error when fetching tx-receipts: %w", e)
}
}
return txReceipts, nil
}
type TxReceipt struct {
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
ContractAddress *common.Address `json:"contractAddress,omitempty"`
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed"`
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
From *common.Address `json:"from,omitempty"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
LogsBloom hexutil.Bytes `json:"logsBloom"`
Status hexutil.Uint64 `json:"status"`
To *common.Address `json:"to,omitempty"`
TransactionHash *common.Hash `json:"transactionHash"`
TransactionIndex hexutil.Uint64 `json:"transactionIndex"`
Type hexutil.Uint64 `json:"type"`
}