Skip to content

Commit fabb97f

Browse files
committed
Track and index the latest Flow fees surge factor
1 parent 84dd9bd commit fabb97f

File tree

11 files changed

+296
-22
lines changed

11 files changed

+296
-22
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ generate:
9999
mockery --dir=storage --name=BlockIndexer --output=storage/mocks
100100
mockery --dir=storage --name=ReceiptIndexer --output=storage/mocks
101101
mockery --dir=storage --name=TransactionIndexer --output=storage/mocks
102-
mockery --dir=storage --name=AccountIndexer --output=storage/mocks
103102
mockery --dir=storage --name=TraceIndexer --output=storage/mocks
103+
mockery --dir=storage --name=FeeParametersIndexer --output=storage/mocks
104104
mockery --all --dir=services/traces --output=services/traces/mocks
105105
mockery --all --dir=services/ingestion --output=services/ingestion/mocks
106106
mockery --dir=models --name=Engine --output=models/mocks

bootstrap/bootstrap.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ const (
5151
)
5252

5353
type Storages struct {
54-
Storage *pebble.Storage
55-
Registers *pebble.RegisterStorage
56-
Blocks storage.BlockIndexer
57-
Transactions storage.TransactionIndexer
58-
Receipts storage.ReceiptIndexer
59-
Traces storage.TraceIndexer
54+
Storage *pebble.Storage
55+
Registers *pebble.RegisterStorage
56+
Blocks storage.BlockIndexer
57+
Transactions storage.TransactionIndexer
58+
Receipts storage.ReceiptIndexer
59+
Traces storage.TraceIndexer
60+
FeeParameters storage.FeeParametersIndexer
6061
}
6162

6263
type Publishers struct {
@@ -191,6 +192,7 @@ func (b *Bootstrap) StartEventIngestion(ctx context.Context) error {
191192
b.storages.Receipts,
192193
b.storages.Transactions,
193194
b.storages.Traces,
195+
b.storages.FeeParameters,
194196
b.publishers.Block,
195197
b.publishers.Logs,
196198
b.logger,
@@ -645,6 +647,13 @@ func setupStorage(
645647
// // TODO(JanezP): verify storage account owner is correct
646648
// }
647649

650+
feeParameters := pebble.NewFeeParameters(store)
651+
if _, err = feeParameters.Get(); errors.Is(err, errs.ErrEntityNotFound) {
652+
if err := feeParameters.Store(models.DefaultFeeParameters, batch); err != nil {
653+
return nil, nil, fmt.Errorf("failed to bootstrap fee parameters: %w", err)
654+
}
655+
}
656+
648657
if batch.Count() > 0 {
649658
err = batch.Commit(pebbleDB.Sync)
650659
if err != nil {
@@ -653,12 +662,13 @@ func setupStorage(
653662
}
654663

655664
return db, &Storages{
656-
Storage: store,
657-
Blocks: blocks,
658-
Registers: registerStore,
659-
Transactions: pebble.NewTransactions(store),
660-
Receipts: pebble.NewReceipts(store),
661-
Traces: pebble.NewTraces(store),
665+
Storage: store,
666+
Blocks: blocks,
667+
Registers: registerStore,
668+
Transactions: pebble.NewTransactions(store),
669+
Receipts: pebble.NewReceipts(store),
670+
Traces: pebble.NewTraces(store),
671+
FeeParameters: feeParameters,
662672
}, nil
663673
}
664674

models/events.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import (
1313
)
1414

1515
const (
16-
BlockExecutedQualifiedIdentifier = string(events.EventTypeBlockExecuted)
17-
TransactionExecutedQualifiedIdentifier = string(events.EventTypeTransactionExecuted)
16+
BlockExecutedQualifiedIdentifier = string(events.EventTypeBlockExecuted)
17+
TransactionExecutedQualifiedIdentifier = string(events.EventTypeTransactionExecuted)
18+
FeeParametersChangedQualifiedIdentifier = "FlowFees.FeeParametersChanged"
1819
)
1920

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

37+
// isFeeParametersChangedEvent checks whether the given event contains updates
38+
// to Flow fees parameters.
39+
func isFeeParametersChangedEvent(event cadence.Event) bool {
40+
if event.EventType == nil {
41+
return false
42+
}
43+
return event.EventType.QualifiedIdentifier == FeeParametersChangedQualifiedIdentifier
44+
}
45+
3646
// CadenceEvents contains Flow emitted events containing one or zero evm block executed event,
3747
// and multiple or zero evm transaction events.
3848
type CadenceEvents struct {
@@ -42,6 +52,7 @@ type CadenceEvents struct {
4252
transactions []Transaction // transactions in the EVM block
4353
txEventPayloads []events.TransactionEventPayload // EVM.TransactionExecuted event payloads
4454
receipts []*Receipt // receipts for transactions
55+
feeParameters *FeeParameters // updates to Flow fees parameters
4556
}
4657

4758
// NewCadenceEvents decodes the events into evm types.
@@ -124,6 +135,15 @@ func decodeCadenceEvents(events flow.BlockEvents) (*CadenceEvents, error) {
124135
e.txEventPayloads = append(e.txEventPayloads, *txEventPayload)
125136
e.receipts = append(e.receipts, receipt)
126137
}
138+
139+
if isFeeParametersChangedEvent(val) {
140+
feeParameters, err := decodeFeeParametersChangedEvent(val)
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
e.feeParameters = feeParameters
146+
}
127147
}
128148

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

205+
// FeeParameters returns any updates to the Flow fees parameters.
206+
func (c *CadenceEvents) FeeParameters() *FeeParameters {
207+
return c.feeParameters
208+
}
209+
185210
// Empty checks if there is an EVM block included in the events.
186211
// If there are no evm block or transactions events this is a heartbeat event.
187212
func (c *CadenceEvents) Empty() bool {

models/fee_parameters.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package models
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/ethereum/go-ethereum/rlp"
7+
"github.com/onflow/cadence"
8+
)
9+
10+
var DefaultFeeParameters = &FeeParameters{
11+
SurgeFactor: cadence.UFix64(100_000_000),
12+
InclusionEffortCost: cadence.UFix64(100_000_000),
13+
ExecutionEffortCost: cadence.UFix64(100_000_000),
14+
}
15+
16+
type FeeParameters struct {
17+
SurgeFactor cadence.UFix64 `cadence:"surgeFactor"`
18+
InclusionEffortCost cadence.UFix64 `cadence:"inclusionEffortCost"`
19+
ExecutionEffortCost cadence.UFix64 `cadence:"executionEffortCost"`
20+
}
21+
22+
func (f *FeeParameters) ToBytes() ([]byte, error) {
23+
return rlp.EncodeToBytes(f)
24+
}
25+
26+
func NewFeeParametersFromBytes(data []byte) (*FeeParameters, error) {
27+
feeParameters := &FeeParameters{}
28+
if err := rlp.DecodeBytes(data, feeParameters); err != nil {
29+
return nil, err
30+
}
31+
32+
return feeParameters, nil
33+
}
34+
35+
func decodeFeeParametersChangedEvent(event cadence.Event) (*FeeParameters, error) {
36+
feeParameters := &FeeParameters{}
37+
if err := cadence.DecodeFields(event, feeParameters); err != nil {
38+
return nil, fmt.Errorf(
39+
"failed to Cadence-decode FlowFees.FeeParametersChanged event [%s]: %w",
40+
event.String(),
41+
err,
42+
)
43+
}
44+
45+
return feeParameters, nil
46+
}

services/ingestion/engine.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type Engine struct {
4848
receipts storage.ReceiptIndexer
4949
transactions storage.TransactionIndexer
5050
traces storage.TraceIndexer
51+
feeParameters storage.FeeParametersIndexer
5152
log zerolog.Logger
5253
evmLastHeight *models.SequentialHeight
5354
blocksPublisher *models.Publisher[*models.Block]
@@ -65,6 +66,7 @@ func NewEventIngestionEngine(
6566
receipts storage.ReceiptIndexer,
6667
transactions storage.TransactionIndexer,
6768
traces storage.TraceIndexer,
69+
feeParameters storage.FeeParametersIndexer,
6870
blocksPublisher *models.Publisher[*models.Block],
6971
logsPublisher *models.Publisher[[]*gethTypes.Log],
7072
log zerolog.Logger,
@@ -84,6 +86,7 @@ func NewEventIngestionEngine(
8486
receipts: receipts,
8587
transactions: transactions,
8688
traces: traces,
89+
feeParameters: feeParameters,
8790
log: log,
8891
blocksPublisher: blocksPublisher,
8992
logsPublisher: logsPublisher,
@@ -217,6 +220,16 @@ func (e *Engine) processEvents(events *models.CadenceEvents) error {
217220

218221
// indexEvents will replay the evm transactions using the block events and index all results.
219222
func (e *Engine) indexEvents(events *models.CadenceEvents, batch *pebbleDB.Batch) error {
223+
if events.FeeParameters() != nil {
224+
if err := e.feeParameters.Store(events.FeeParameters(), batch); err != nil {
225+
return fmt.Errorf(
226+
"failed to update fee parameters for height: %d, during events ingestion: %w",
227+
events.CadenceHeight(),
228+
err,
229+
)
230+
}
231+
}
232+
220233
// if heartbeat interval with no data still update the cadence height
221234
if events.Empty() {
222235
if err := e.blocks.SetLatestCadenceHeight(events.CadenceHeight(), batch); err != nil {

services/ingestion/engine_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ func TestSerialBlockIngestion(t *testing.T) {
5454
Once() // make sure this isn't called multiple times
5555

5656
traces := &storageMock.TraceIndexer{}
57+
feeParams := &storageMock.FeeParametersIndexer{}
5758

5859
eventsChan := make(chan models.BlockEvents)
5960

@@ -73,6 +74,7 @@ func TestSerialBlockIngestion(t *testing.T) {
7374
receipts,
7475
transactions,
7576
traces,
77+
feeParams,
7678
models.NewPublisher[*models.Block](),
7779
models.NewPublisher[[]*gethTypes.Log](),
7880
zerolog.Nop(),
@@ -134,6 +136,7 @@ func TestSerialBlockIngestion(t *testing.T) {
134136
Once() // make sure this isn't called multiple times
135137

136138
traces := &storageMock.TraceIndexer{}
139+
feeParams := &storageMock.FeeParametersIndexer{}
137140

138141
eventsChan := make(chan models.BlockEvents)
139142
subscriber := &mocks.EventSubscriber{}
@@ -152,6 +155,7 @@ func TestSerialBlockIngestion(t *testing.T) {
152155
receipts,
153156
transactions,
154157
traces,
158+
feeParams,
155159
models.NewPublisher[*models.Block](),
156160
models.NewPublisher[[]*gethTypes.Log](),
157161
zerolog.Nop(),
@@ -264,6 +268,8 @@ func TestBlockAndTransactionIngestion(t *testing.T) {
264268
return nil
265269
})
266270

271+
feeParams := &storageMock.FeeParametersIndexer{}
272+
267273
engine := NewEventIngestionEngine(
268274
subscriber,
269275
replayer.NewBlocksProvider(blocks, flowGo.Emulator, nil),
@@ -273,6 +279,7 @@ func TestBlockAndTransactionIngestion(t *testing.T) {
273279
receipts,
274280
transactions,
275281
traces,
282+
feeParams,
276283
models.NewPublisher[*models.Block](),
277284
models.NewPublisher[[]*gethTypes.Log](),
278285
zerolog.Nop(),
@@ -372,6 +379,8 @@ func TestBlockAndTransactionIngestion(t *testing.T) {
372379
return nil
373380
})
374381

382+
feeParams := &storageMock.FeeParametersIndexer{}
383+
375384
engine := NewEventIngestionEngine(
376385
subscriber,
377386
replayer.NewBlocksProvider(blocks, flowGo.Emulator, nil),
@@ -381,6 +390,7 @@ func TestBlockAndTransactionIngestion(t *testing.T) {
381390
receipts,
382391
transactions,
383392
traces,
393+
feeParams,
384394
models.NewPublisher[*models.Block](),
385395
models.NewPublisher[[]*gethTypes.Log](),
386396
zerolog.Nop(),
@@ -456,6 +466,7 @@ func TestBlockAndTransactionIngestion(t *testing.T) {
456466
Once() // make sure this isn't called multiple times
457467

458468
traces := &storageMock.TraceIndexer{}
469+
feeParams := &storageMock.FeeParametersIndexer{}
459470

460471
eventsChan := make(chan models.BlockEvents)
461472
subscriber := &mocks.EventSubscriber{}
@@ -475,6 +486,7 @@ func TestBlockAndTransactionIngestion(t *testing.T) {
475486
receipts,
476487
transactions,
477488
traces,
489+
feeParams,
478490
models.NewPublisher[*models.Block](),
479491
models.NewPublisher[[]*gethTypes.Log](),
480492
zerolog.Nop(),

services/ingestion/event_subscriber.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func (r *RPCEventSubscriber) subscribe(ctx context.Context, height uint64) <-cha
146146
blockEventsStream, errChan, err = r.client.SubscribeEventsByBlockHeight(
147147
ctx,
148148
height,
149-
blocksFilter(r.chain),
149+
blocksEventFilter(r.chain),
150150
access.WithHeartbeatInterval(1),
151151
)
152152

@@ -471,7 +471,7 @@ func (r *RPCEventSubscriber) fetchMissingData(
471471
// remove existing events
472472
blockEvents.Events = nil
473473

474-
for _, eventType := range blocksFilter(r.chain).EventTypes {
474+
for _, eventType := range evmEventFilter(r.chain).EventTypes {
475475
recoveredEvents, err := r.client.GetEventsForHeightRange(
476476
ctx,
477477
eventType,
@@ -552,11 +552,37 @@ func (r *RPCEventSubscriber) recover(
552552
return models.NewBlockEventsError(err)
553553
}
554554

555-
// blockFilter define events we subscribe to:
556-
// A.{evm}.EVM.BlockExecuted and A.{evm}.EVM.TransactionExecuted,
557-
// where {evm} is EVM deployed contract address, which depends on the chain ID we configure.
558-
func blocksFilter(chainId flowGo.ChainID) flow.EventFilter {
559-
evmAddress := common.Address(systemcontracts.SystemContractsForChain(chainId).EVMContract.Address)
555+
// blocksEventFilter defines the full set of events we subscribe to:
556+
// - A.{evm}.EVM.BlockExecuted
557+
// - A.{evm}.EVM.TransactionExecuted,
558+
// - A.{flow_fees}.FlowFees.FeeParametersChanged,
559+
// where {evm} is the EVM deployed contract address, which depends on the
560+
// configured chain ID and {flow_fees} is the FlowFees deployed contract
561+
// address for the configured chain ID.
562+
func blocksEventFilter(chainID flowGo.ChainID) flow.EventFilter {
563+
contracts := systemcontracts.SystemContractsForChain(chainID)
564+
flowFeesAddress := common.Address(contracts.FlowFees.Address)
565+
eventFilter := evmEventFilter(chainID)
566+
567+
feeParametersChangedEvent := common.NewAddressLocation(
568+
nil,
569+
flowFeesAddress,
570+
models.FeeParametersChangedQualifiedIdentifier,
571+
).ID()
572+
573+
eventFilter.EventTypes = append(eventFilter.EventTypes, feeParametersChangedEvent)
574+
575+
return eventFilter
576+
}
577+
578+
// evmEventFilter defines the EVM-related events we subscribe to:
579+
// - A.{evm}.EVM.BlockExecuted,
580+
// - A.{evm}.EVM.TransactionExecuted,
581+
// where {evm} is the EVM deployed contract address, which depends on the
582+
// configured chain ID.
583+
func evmEventFilter(chainID flowGo.ChainID) flow.EventFilter {
584+
contracts := systemcontracts.SystemContractsForChain(chainID)
585+
evmAddress := common.Address(contracts.EVMContract.Address)
560586

561587
blockExecutedEvent := common.NewAddressLocation(
562588
nil,

storage/index.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,9 @@ type TraceIndexer interface {
102102
// GetTransaction will retrieve transaction trace by the transaction ID.
103103
GetTransaction(ID common.Hash) (json.RawMessage, error)
104104
}
105+
106+
type FeeParametersIndexer interface {
107+
Store(feeParameters *models.FeeParameters, batch *pebble.Batch) error
108+
109+
Get() (*models.FeeParameters, error)
110+
}

0 commit comments

Comments
 (0)