Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ generate:
mockery --dir=storage --name=BlockIndexer --output=storage/mocks
mockery --dir=storage --name=ReceiptIndexer --output=storage/mocks
mockery --dir=storage --name=TransactionIndexer --output=storage/mocks
mockery --dir=storage --name=AccountIndexer --output=storage/mocks
mockery --dir=storage --name=TraceIndexer --output=storage/mocks
mockery --dir=storage --name=FeeParametersIndexer --output=storage/mocks
mockery --all --dir=services/traces --output=services/traces/mocks
mockery --all --dir=services/ingestion --output=services/ingestion/mocks
mockery --dir=models --name=Engine --output=models/mocks
Expand Down
38 changes: 34 additions & 4 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ type BlockChainAPI struct {
blocks storage.BlockIndexer
transactions storage.TransactionIndexer
receipts storage.ReceiptIndexer
feeParameters storage.FeeParametersIndexer
indexingResumedHeight uint64
rateLimiter RateLimiter
collector metrics.Collector
Expand All @@ -98,6 +99,7 @@ func NewBlockChainAPI(
blocks storage.BlockIndexer,
transactions storage.TransactionIndexer,
receipts storage.ReceiptIndexer,
feeParameters storage.FeeParametersIndexer,
rateLimiter RateLimiter,
collector metrics.Collector,
indexingResumedHeight uint64,
Expand All @@ -109,6 +111,7 @@ func NewBlockChainAPI(
blocks: blocks,
transactions: transactions,
receipts: receipts,
feeParameters: feeParameters,
indexingResumedHeight: indexingResumedHeight,
rateLimiter: rateLimiter,
collector: collector,
Expand Down Expand Up @@ -180,7 +183,13 @@ func (b *BlockChainAPI) SendRawTransaction(
return common.Hash{}, err
}

id, err := b.evm.SendRawTransaction(ctx, input)
feeParams, err := b.feeParameters.Get()
if err != nil {
b.logger.Warn().Err(err).Msg("fee parameters unavailable; falling back to base gas price")
feeParams = models.DefaultFeeParameters()
}

id, err := b.evm.SendRawTransaction(ctx, input, feeParams)
if err != nil {
return handleError[common.Hash](err, l, b.collector)
}
Expand Down Expand Up @@ -850,8 +859,17 @@ func (b *BlockChainAPI) FeeHistory(
maxCount := min(uint64(blockCount), lastBlockNumber)

blockRewards := make([]*hexutil.Big, len(rewardPercentiles))
gasPrice := b.config.GasPrice

feeParams, err := b.feeParameters.Get()
if err != nil {
b.logger.Warn().Err(err).Msg("fee parameters unavailable; falling back to base gas price")
} else {
gasPrice = feeParams.CalculateGasPrice(b.config.GasPrice)
}

for i := range rewardPercentiles {
blockRewards[i] = (*hexutil.Big)(b.config.GasPrice)
blockRewards[i] = (*hexutil.Big)(gasPrice)
}

for i := maxCount; i >= uint64(1); i-- {
Expand Down Expand Up @@ -1050,7 +1068,13 @@ func (b *BlockChainAPI) Coinbase(ctx context.Context) (common.Address, error) {

// GasPrice returns a suggestion for a gas price for legacy transactions.
func (b *BlockChainAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) {
return (*hexutil.Big)(b.config.GasPrice), nil
feeParams, err := b.feeParameters.Get()
if err != nil {
b.logger.Warn().Err(err).Msg("fee parameters unavailable; falling back to base gas price")
return (*hexutil.Big)(b.config.GasPrice), nil
}
gasPrice := feeParams.CalculateGasPrice(b.config.GasPrice)
return (*hexutil.Big)(gasPrice), nil
}

// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
Expand Down Expand Up @@ -1091,7 +1115,13 @@ func (b *BlockChainAPI) GetUncleByBlockNumberAndIndex(

// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic fee transactions.
func (b *BlockChainAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) {
return (*hexutil.Big)(b.config.GasPrice), nil
feeParams, err := b.feeParameters.Get()
if err != nil {
b.logger.Warn().Err(err).Msg("fee parameters unavailable; falling back to base gas price")
return (*hexutil.Big)(b.config.GasPrice), nil
}
gasPrice := feeParams.CalculateGasPrice(b.config.GasPrice)
return (*hexutil.Big)(gasPrice), nil
}

// Mining returns true if client is actively mining new blocks.
Expand Down
76 changes: 64 additions & 12 deletions bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package bootstrap

import (
"context"
_ "embed"
"errors"
"fmt"
"math"
"time"

pebbleDB "github.com/cockroachdb/pebble"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/onflow/cadence"
"github.com/onflow/flow-go-sdk/access"
"github.com/onflow/flow-go-sdk/access/grpc"
"github.com/onflow/flow-go/fvm/environment"
Expand Down Expand Up @@ -50,13 +52,19 @@ const (
DefaultResourceExhaustedMaxRetryDelay = 30 * time.Second
)

var (
//go:embed cadence/get_fees_surge_factor.cdc
getFeesSurgeFactor []byte
)

type Storages struct {
Storage *pebble.Storage
Registers *pebble.RegisterStorage
Blocks storage.BlockIndexer
Transactions storage.TransactionIndexer
Receipts storage.ReceiptIndexer
Traces storage.TraceIndexer
Storage *pebble.Storage
Registers *pebble.RegisterStorage
Blocks storage.BlockIndexer
Transactions storage.TransactionIndexer
Receipts storage.ReceiptIndexer
Traces storage.TraceIndexer
FeeParameters storage.FeeParametersIndexer
}

type Publishers struct {
Expand Down Expand Up @@ -191,6 +199,7 @@ func (b *Bootstrap) StartEventIngestion(ctx context.Context) error {
b.storages.Receipts,
b.storages.Transactions,
b.storages.Traces,
b.storages.FeeParameters,
b.publishers.Block,
b.publishers.Logs,
b.logger,
Expand Down Expand Up @@ -314,6 +323,7 @@ func (b *Bootstrap) StartAPIServer(ctx context.Context) error {
b.storages.Blocks,
b.storages.Transactions,
b.storages.Receipts,
b.storages.FeeParameters,
rateLimiter,
b.collector,
indexingResumedHeight,
Expand Down Expand Up @@ -645,6 +655,15 @@ func setupStorage(
// // TODO(JanezP): verify storage account owner is correct
// }

feeParameters := pebble.NewFeeParameters(store)
currentFeeParams, err := getNetworkFeeParams(context.Background(), config, client, logger)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch current fees surge factor: %w", err)
}
if err := feeParameters.Store(currentFeeParams, batch); err != nil {
return nil, nil, fmt.Errorf("failed to bootstrap fee parameters: %w", err)
}

if batch.Count() > 0 {
err = batch.Commit(pebbleDB.Sync)
if err != nil {
Expand All @@ -653,12 +672,13 @@ func setupStorage(
}

return db, &Storages{
Storage: store,
Blocks: blocks,
Registers: registerStore,
Transactions: pebble.NewTransactions(store),
Receipts: pebble.NewReceipts(store),
Traces: pebble.NewTraces(store),
Storage: store,
Blocks: blocks,
Registers: registerStore,
Transactions: pebble.NewTransactions(store),
Receipts: pebble.NewReceipts(store),
Traces: pebble.NewTraces(store),
FeeParameters: feeParameters,
}, nil
}

Expand Down Expand Up @@ -781,3 +801,35 @@ func (m *metricsWrapper) Stop() {
m.stopFN()
<-m.Done()
}

// getNetworkFeeParams returns the network's current Flow fees parameters
func getNetworkFeeParams(
ctx context.Context,
config config.Config,
client *requester.CrossSporkClient,
logger zerolog.Logger,
) (*models.FeeParameters, error) {
val, err := client.ExecuteScriptAtLatestBlock(
ctx,
requester.ReplaceAddresses(getFeesSurgeFactor, config.FlowNetworkID),
nil,
)
if err != nil {
return nil, err
}

// sanity check, should never occur
surgeFactor, ok := val.(cadence.UFix64)
if !ok {
return nil, fmt.Errorf("failed to convert surgeFactor %v to UFix64, got type: %T", val, val)
}

logger.Debug().
Uint64("surge-factor", uint64(surgeFactor)).
Msg("get current surge factor executed")

feeParameters := models.DefaultFeeParameters()
feeParameters.SurgeFactor = surgeFactor

return feeParameters, nil
}
5 changes: 5 additions & 0 deletions bootstrap/cadence/get_fees_surge_factor.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import FlowFees

access(all) fun main(): UFix64 {
return FlowFees.getFeeParameters().surgeFactor
}
29 changes: 27 additions & 2 deletions models/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (
)

const (
BlockExecutedQualifiedIdentifier = string(events.EventTypeBlockExecuted)
TransactionExecutedQualifiedIdentifier = string(events.EventTypeTransactionExecuted)
BlockExecutedQualifiedIdentifier = string(events.EventTypeBlockExecuted)
TransactionExecutedQualifiedIdentifier = string(events.EventTypeTransactionExecuted)
FeeParametersChangedQualifiedIdentifier = "FlowFees.FeeParametersChanged"
)

// isBlockExecutedEvent checks whether the given event contains block executed data.
Expand All @@ -33,6 +34,15 @@ func isTransactionExecutedEvent(event cadence.Event) bool {
return event.EventType.QualifiedIdentifier == TransactionExecutedQualifiedIdentifier
}

// isFeeParametersChangedEvent checks whether the given event contains updates
// to Flow fees parameters.
func isFeeParametersChangedEvent(event cadence.Event) bool {
if event.EventType == nil {
return false
}
return event.EventType.QualifiedIdentifier == FeeParametersChangedQualifiedIdentifier
}

// CadenceEvents contains Flow emitted events containing one or zero evm block executed event,
// and multiple or zero evm transaction events.
type CadenceEvents struct {
Expand All @@ -42,6 +52,7 @@ type CadenceEvents struct {
transactions []Transaction // transactions in the EVM block
txEventPayloads []events.TransactionEventPayload // EVM.TransactionExecuted event payloads
receipts []*Receipt // receipts for transactions
feeParameters *FeeParameters // updates to Flow fees parameters
}

// NewCadenceEvents decodes the events into evm types.
Expand Down Expand Up @@ -124,6 +135,15 @@ func decodeCadenceEvents(events flow.BlockEvents) (*CadenceEvents, error) {
e.txEventPayloads = append(e.txEventPayloads, *txEventPayload)
e.receipts = append(e.receipts, receipt)
}

if isFeeParametersChangedEvent(val) {
feeParameters, err := decodeFeeParametersChangedEvent(val)
if err != nil {
return nil, err
}

e.feeParameters = feeParameters
}
}

// safety check, we have a missing block in the events
Expand Down Expand Up @@ -182,6 +202,11 @@ func (c *CadenceEvents) Receipts() []*Receipt {
return c.receipts
}

// FeeParameters returns any updates to the Flow fees parameters.
func (c *CadenceEvents) FeeParameters() *FeeParameters {
return c.feeParameters
}

// Empty checks if there is an EVM block included in the events.
// If there are no evm block or transactions events this is a heartbeat event.
func (c *CadenceEvents) Empty() bool {
Expand Down
64 changes: 64 additions & 0 deletions models/fee_parameters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package models

import (
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/rlp"
"github.com/onflow/cadence"
)

const feeParamsPrecision = 100_000_000

var surgeFactorScale = big.NewInt(feeParamsPrecision)

func DefaultFeeParameters() *FeeParameters {
return &FeeParameters{
SurgeFactor: cadence.UFix64(feeParamsPrecision),
InclusionEffortCost: cadence.UFix64(feeParamsPrecision),
ExecutionEffortCost: cadence.UFix64(feeParamsPrecision),
}
}

type FeeParameters struct {
SurgeFactor cadence.UFix64 `cadence:"surgeFactor"`
InclusionEffortCost cadence.UFix64 `cadence:"inclusionEffortCost"`
ExecutionEffortCost cadence.UFix64 `cadence:"executionEffortCost"`
}

func (f *FeeParameters) ToBytes() ([]byte, error) {
return rlp.EncodeToBytes(f)
}

func (f *FeeParameters) CalculateGasPrice(currentGasPrice *big.Int) *big.Int {
if currentGasPrice == nil {
return new(big.Int) // zero
}

// gasPrice = (currentGasPrice * surgeFactor) / feeParamsPrecision
surgeFactor := new(big.Int).SetUint64(uint64(f.SurgeFactor))
gasPrice := new(big.Int).Mul(currentGasPrice, surgeFactor)
return new(big.Int).Quo(gasPrice, surgeFactorScale)
}

func NewFeeParametersFromBytes(data []byte) (*FeeParameters, error) {
feeParameters := &FeeParameters{}
if err := rlp.DecodeBytes(data, feeParameters); err != nil {
return nil, err
}

return feeParameters, nil
}

func decodeFeeParametersChangedEvent(event cadence.Event) (*FeeParameters, error) {
feeParameters := &FeeParameters{}
if err := cadence.DecodeFields(event, feeParameters); err != nil {
return nil, fmt.Errorf(
"failed to Cadence-decode FlowFees.FeeParametersChanged event [%s]: %w",
event.String(),
err,
)
}

return feeParameters, nil
}
Loading