Documentation
¶
Index ¶
- type BatchTxPool
- type CrossSporkClient
- func (c *CrossSporkClient) Close() error
- func (c *CrossSporkClient) CurrentSporkRootHeight() uint64
- func (c *CrossSporkClient) ExecuteScriptAtBlockHeight(ctx context.Context, height uint64, script []byte, arguments []cadence.Value) (cadence.Value, error)
- func (c *CrossSporkClient) GetBlockHeaderByHeight(ctx context.Context, height uint64) (*flow.BlockHeader, error)
- func (c *CrossSporkClient) GetEventsForBlockHeader(ctx context.Context, eventType string, blockHeader *flow.BlockHeader) ([]flow.BlockEvents, error)
- func (c *CrossSporkClient) GetEventsForHeightRange(ctx context.Context, eventType string, startHeight uint64, endHeight uint64) ([]flow.BlockEvents, error)
- func (c *CrossSporkClient) GetLatestHeightForSpork(ctx context.Context, height uint64) (uint64, error)
- func (c *CrossSporkClient) IsPastSpork(height uint64) bool
- func (c *CrossSporkClient) IsSporkRootBlockHeight(height uint64) bool
- func (c *CrossSporkClient) SubscribeBlockHeadersFromStartHeight(ctx context.Context, startHeight uint64, blockStatus flow.BlockStatus) (<-chan *flow.BlockHeader, <-chan error, error)
- func (c *CrossSporkClient) SubscribeEventsByBlockHeight(ctx context.Context, startHeight uint64, filter flow.EventFilter, ...) (<-chan flow.BlockEvents, <-chan error, error)
- type EVM
- func (e *EVM) Call(txArgs ethTypes.TransactionArgs, from common.Address, height uint64, ...) ([]byte, error)
- func (e *EVM) EstimateGas(txArgs ethTypes.TransactionArgs, from common.Address, height uint64, ...) (uint64, error)
- func (e *EVM) GetBalance(address common.Address, height uint64) (*big.Int, error)
- func (e *EVM) GetCode(address common.Address, height uint64) ([]byte, error)
- func (e *EVM) GetLatestEVMHeight(ctx context.Context) (uint64, error)
- func (e *EVM) GetNonce(address common.Address, height uint64) (uint64, error)
- func (e *EVM) GetStorageAt(address common.Address, hash common.Hash, height uint64) (common.Hash, error)
- func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash, error)
- type KMSKeySigner
- type LocalNonceProvider
- type NonceProvider
- type NonceView
- type OverridableBlocksProvider
- type RemoteCadenceArch
- type Requester
- type SingleTxPool
- type TxPool
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BatchTxPool ¶ added in v1.3.0
type BatchTxPool struct {
*SingleTxPool
// contains filtered or unexported fields
}
BatchTxPool is a TxPool implementation that collects and groups transactions by EOA signer, sorts them by nonce, and submits them as a batch via EVM.batchRun on each flush interval.
Problem ¶
Flow did not have a traditional EVM mempool. On standard EVM chains, when a wallet sends transactions out-of-nonce-sequence (e.g., nonces 5, 7, 6 in parallel), the mempool holds future-nonce transactions until the gap is filled. Flow EVM had no such pooling mechanism — a transaction whose nonce does not match the current account nonce is simply dropped.
The original BatchTxPool implementation partially addressed this by batching transactions that arrived from an EOA with "recent activity" (i.e., a prior transaction within TxBatchInterval). However, it still submitted the FIRST transaction from any burst immediately — before the rest of the burst had a chance to arrive. If that first transaction happened to carry a future nonce (due to parallel dispatch), it failed, and the gap it left caused all subsequent nonces in the batch to fail as well.
Fix ¶
For all incoming transactions, we are now inspecting the EOA's current nonce in the local state index, as well as the elapsed spacing from any recent submission: 1. If enough spacing has elapsed and the current nonce matches the transaction nonce, we submit it right away, and record this activity in the EOA's dedicated queue, for use in future submissions. 2. If enough spacing has elapsed and the transaction nonce is higher, we check for any recent submissions to see if we can form a valid sequence. This could happen from in-flight transaction submission, that have not yet been indexed by the local state index. In this case we optimistically submit right away and record this activity in the EOA's dedicated queue, for use in future submissions. 3. If none of the above 2 conditions are met, we enqueue the transaction in the pool. The flush timer (TxBatchInterval) is the sole submission trigger. This guarantees that parallel transactions from the same wallet accumulate in the pool before being sorted by nonce and submitted atomically.
func NewBatchTxPool ¶ added in v1.3.0
func NewBatchTxPool( ctx context.Context, client *CrossSporkClient, transactionsPublisher *models.Publisher[*gethTypes.Transaction], logger zerolog.Logger, config config.Config, collector metrics.Collector, keystore *keystore.KeyStore, nonceProvider NonceProvider, ) (*BatchTxPool, error)
func (*BatchTxPool) Add ¶ added in v1.3.0
func (t *BatchTxPool) Add( ctx context.Context, tx *gethTypes.Transaction, ) error
Add inspects the nonce of the incoming transaction, and either submits it right away or enqueues the transaction in the per-EOA pool, in which case it will be processed and submitted by the flush goroutine (processPooledTransactions) on every `TxBatchInterval` tick.
type CrossSporkClient ¶ added in v0.12.0
CrossSporkClient is a wrapper around the Flow AN client that can access different AN APIs based on the height boundaries of the sporks.
Each spork is defined with the last height included in that spork, based on the list we know which AN client to use when requesting the data.
Any API that supports cross-spork access must have a defined function that shadows the original access Client function.
func NewCrossSporkClient ¶ added in v0.12.0
func NewCrossSporkClient( currentSpork access.Client, pastSporks []access.Client, logger zerolog.Logger, chainID flowGo.ChainID, ) (*CrossSporkClient, error)
NewCrossSporkClient creates a new instance of the multi-spork client. It requires the current spork client and a slice of past spork clients.
func (*CrossSporkClient) Close ¶ added in v1.0.0
func (c *CrossSporkClient) Close() error
func (*CrossSporkClient) CurrentSporkRootHeight ¶ added in v1.5.5
func (c *CrossSporkClient) CurrentSporkRootHeight() uint64
CurrentSporkRootHeight returns the spork root block height of the current spork.
func (*CrossSporkClient) ExecuteScriptAtBlockHeight ¶ added in v0.12.0
func (*CrossSporkClient) GetBlockHeaderByHeight ¶ added in v0.12.0
func (c *CrossSporkClient) GetBlockHeaderByHeight( ctx context.Context, height uint64, ) (*flow.BlockHeader, error)
func (*CrossSporkClient) GetEventsForBlockHeader ¶ added in v1.5.5
func (c *CrossSporkClient) GetEventsForBlockHeader( ctx context.Context, eventType string, blockHeader *flow.BlockHeader, ) ([]flow.BlockEvents, error)
func (*CrossSporkClient) GetEventsForHeightRange ¶ added in v1.0.0
func (c *CrossSporkClient) GetEventsForHeightRange( ctx context.Context, eventType string, startHeight uint64, endHeight uint64, ) ([]flow.BlockEvents, error)
func (*CrossSporkClient) GetLatestHeightForSpork ¶ added in v0.12.0
func (c *CrossSporkClient) GetLatestHeightForSpork(ctx context.Context, height uint64) (uint64, error)
GetLatestHeightForSpork will determine the spork client in which the provided height is contained and then find the latest height in that spork.
func (*CrossSporkClient) IsPastSpork ¶ added in v0.12.0
func (c *CrossSporkClient) IsPastSpork(height uint64) bool
IsPastSpork will check if the provided height is contained in the previous sporks.
func (*CrossSporkClient) IsSporkRootBlockHeight ¶ added in v1.5.5
func (c *CrossSporkClient) IsSporkRootBlockHeight(height uint64) bool
IsSporkRootBlockHeight will check if the provided height is the spork root block height.
func (*CrossSporkClient) SubscribeBlockHeadersFromStartHeight ¶ added in v1.5.5
func (c *CrossSporkClient) SubscribeBlockHeadersFromStartHeight( ctx context.Context, startHeight uint64, blockStatus flow.BlockStatus, ) (<-chan *flow.BlockHeader, <-chan error, error)
func (*CrossSporkClient) SubscribeEventsByBlockHeight ¶ added in v0.12.0
func (c *CrossSporkClient) SubscribeEventsByBlockHeight( ctx context.Context, startHeight uint64, filter flow.EventFilter, opts ...access.SubscribeOption, ) (<-chan flow.BlockEvents, <-chan error, error)
type EVM ¶
type EVM struct {
// contains filtered or unexported fields
}
func NewEVM ¶
func NewEVM( registerStore *pebble.RegisterStorage, client *CrossSporkClient, config config.Config, logger zerolog.Logger, blocks storage.BlockIndexer, txPool TxPool, collector metrics.Collector, ) (*EVM, error)
func (*EVM) Call ¶
func (e *EVM) Call( txArgs ethTypes.TransactionArgs, from common.Address, height uint64, stateOverrides *ethTypes.StateOverride, blockOverrides *ethTypes.BlockOverrides, ) ([]byte, error)
func (*EVM) EstimateGas ¶
func (e *EVM) EstimateGas( txArgs ethTypes.TransactionArgs, from common.Address, height uint64, stateOverrides *ethTypes.StateOverride, blockOverrides *ethTypes.BlockOverrides, ) (uint64, error)
func (*EVM) GetBalance ¶
func (*EVM) GetLatestEVMHeight ¶ added in v0.6.0
func (*EVM) GetStorageAt ¶ added in v0.23.0
type KMSKeySigner ¶ added in v1.0.0
type KMSKeySigner struct {
// contains filtered or unexported fields
}
KMSKeySigner is a crypto signer that contains a `crypto.Signer`[1] object, which is tied to a Cloud KMS asymmetric signing key.
[1](https://github.com/onflow/flow-go-sdk/blob/master/crypto/cloudkms/signer.go#L37)
func NewKMSKeySigner ¶ added in v1.0.0
func NewKMSKeySigner( ctx context.Context, key cloudkms.Key, logger zerolog.Logger, ) (*KMSKeySigner, error)
NewKMSKeySigner returns a new KMSKeySigner for the given Cloud KMS key.
func (*KMSKeySigner) PublicKey ¶ added in v1.0.0
func (s *KMSKeySigner) PublicKey() crypto.PublicKey
PublicKey returns the current public key of the Cloud KMS key.
func (*KMSKeySigner) Sign ¶ added in v1.0.0
func (s *KMSKeySigner) Sign(message []byte) ([]byte, error)
Sign signs the given message using the KMS signing key for this signer.
Reference: https://cloud.google.com/kms/docs/create-validate-signatures
type LocalNonceProvider ¶ added in v1.5.2
type LocalNonceProvider struct {
// contains filtered or unexported fields
}
LocalNonceProvider reads the EOA nonce from the latest height of the local state index. It caches the built block view and reuses it while the indexed height is unchanged (see GetBlockView).
func NewLocalNonceProvider ¶ added in v1.5.2
func NewLocalNonceProvider( chainID flowGo.ChainID, registerStore *pebble.RegisterStorage, blocks storage.BlockIndexer, collector metrics.Collector, ) *LocalNonceProvider
func (*LocalNonceProvider) GetBlockView ¶ added in v1.5.2
func (p *LocalNonceProvider) GetBlockView() (NonceView, error)
GetBlockView returns a NonceView over the latest indexed EVM height. The view is cached and reused while the indexed height is unchanged, so a burst of reads within one block — many Add calls, or a collectDueBatches pass — builds the (expensive) view only once. It is rebuilt when a new block is indexed. Reuse is safe because an EOA's on-chain nonce cannot change without a new block being indexed.
func (*LocalNonceProvider) GetNextNonce ¶ added in v1.5.2
func (p *LocalNonceProvider) GetNextNonce(address gethCommon.Address) (uint64, error)
type NonceProvider ¶ added in v1.5.2
type NonceProvider interface {
// GetNextNonce returns the account nonce of the given EOA — its transaction
// count, i.e. the next nonce the EOA should use (matches eth_getTransactionCount).
//
// A non-nil error represents an EXCEPTION, not an expected condition:
// the underlying read is a local state-index lookup that should not
// fail under normal operation. Callers must therefore treat an error
// as a hard failure (reject the transaction / abort the operation)
// rather than a routine, recoverable condition to swallow.
GetNextNonce(address gethCommon.Address) (uint64, error)
// GetBlockView returns a NonceView over the latest indexed EVM state. A
// non-nil error is an EXCEPTION, same contract as GetNextNonce.
GetBlockView() (NonceView, error)
}
NonceProvider returns the next nonce of the given EOA address. The transaction mempool uses it to determine the expected next nonce.
type NonceView ¶ added in v1.5.2
type NonceView interface {
// GetNonce returns the EOA's account nonce (the next nonce to use) at this
// view's state. Named GetNonce — not GetNextNonce — because this interface is
// satisfied directly by flow-go's query.View, whose method is GetNonce.
GetNonce(address gethCommon.Address) (uint64, error)
}
NonceView reads EOA nonces at a single, fixed EVM state (one built block view). The mempool reads many EOAs' nonces from one view per flush tick rather than rebuilding the (expensive) view per address. It is an interface so tests can fake it without constructing a real query.View.
type OverridableBlocksProvider ¶ added in v1.0.2
type OverridableBlocksProvider struct {
// contains filtered or unexported fields
}
This OverridableBlocksProvider implementation is only used for the `eth_call` & `debug_traceCall` JSON-RPC endpoints. It accepts optional `Tracer` & `BlockOverrides` objects, which are used when constructing the `BlockContext` object.
func NewOverridableBlocksProvider ¶ added in v1.0.2
func NewOverridableBlocksProvider( blocks storage.BlockIndexer, chainID flowGo.ChainID, tracer *tracers.Tracer, ) *OverridableBlocksProvider
func (*OverridableBlocksProvider) GetSnapshotAt ¶ added in v1.0.2
func (bp *OverridableBlocksProvider) GetSnapshotAt(height uint64) ( evmTypes.BlockSnapshot, error, )
func (*OverridableBlocksProvider) WithBlockOverrides ¶ added in v1.0.2
func (bp *OverridableBlocksProvider) WithBlockOverrides( blockOverrides *ethTypes.BlockOverrides, ) *OverridableBlocksProvider
type RemoteCadenceArch ¶ added in v1.0.0
type RemoteCadenceArch struct {
// contains filtered or unexported fields
}
func NewRemoteCadenceArch ¶ added in v1.0.0
func NewRemoteCadenceArch( blockHeight uint64, client *CrossSporkClient, chainID flow.ChainID, ) *RemoteCadenceArch
func (*RemoteCadenceArch) Address ¶ added in v1.0.0
func (rca *RemoteCadenceArch) Address() evmTypes.Address
func (*RemoteCadenceArch) Name ¶ added in v1.3.0
func (rca *RemoteCadenceArch) Name() string
func (*RemoteCadenceArch) RequiredGas ¶ added in v1.0.0
func (rca *RemoteCadenceArch) RequiredGas(input []byte) uint64
type Requester ¶
type Requester interface {
// SendRawTransaction will submit signed transaction data to the network.
// The submitted EVM transaction hash is returned.
SendRawTransaction(ctx context.Context, data []byte) (common.Hash, error)
// GetBalance returns the amount of wei for the given address in the state of the
// given EVM block height.
GetBalance(address common.Address, height uint64) (*big.Int, error)
// Call executes the given signed transaction data on the state for the given EVM block height.
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
Call(
txArgs ethTypes.TransactionArgs,
from common.Address,
height uint64,
stateOverrides *ethTypes.StateOverride,
blockOverrides *ethTypes.BlockOverrides,
) ([]byte, error)
// EstimateGas executes the given signed transaction data on the state for the given EVM block height.
// Note, this function doesn't make any changes in the state/blockchain and is
// useful to executed and retrieve the gas consumption and possible failures.
EstimateGas(
txArgs ethTypes.TransactionArgs,
from common.Address,
height uint64,
stateOverrides *ethTypes.StateOverride,
blockOverrides *ethTypes.BlockOverrides,
) (uint64, error)
// GetNonce gets nonce from the network at the given EVM block height.
GetNonce(address common.Address, height uint64) (uint64, error)
// GetCode returns the code stored at the given address in
// the state for the given EVM block height.
GetCode(address common.Address, height uint64) ([]byte, error)
// GetStorageAt returns the storage from the state at the given address, key and block number.
GetStorageAt(address common.Address, hash common.Hash, height uint64) (common.Hash, error)
// GetLatestEVMHeight returns the latest EVM height of the network.
GetLatestEVMHeight(ctx context.Context) (uint64, error)
}
type SingleTxPool ¶ added in v1.3.0
type SingleTxPool struct {
// contains filtered or unexported fields
}
SingleTxPool is a simple implementation of the `TxPool` interface that submits transactions as soon as they arrive, without any delays or batching strategies.
func NewSingleTxPool ¶ added in v1.3.0
func (*SingleTxPool) Add ¶ added in v1.3.0
func (t *SingleTxPool) Add( ctx context.Context, tx *gethTypes.Transaction, ) error
Add creates a Cadence transaction that wraps the given EVM transaction in an `EVM.run` function call for execution.
The Cadence transaction is submitted to the Flow network right away.
If the transaction state validation is configured to run with the "tx-seal" strategy, the Cadence transaction status is awaited and an error is returned in case of a failure in submission or an EVM validation error. Until the Cadence transaction is sealed the transaction will stay in the pool and marked as pending.
If the transaction state validation is configured to run with the "local-index" strategy, the Cadence transaction status is not awaited, as the necessary EVM validation checks, such as nonce/balance checks, have been checked against the EVM state of the local index.