requester

package
v1.5.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: Apache-2.0 Imports: 48 Imported by: 0

Documentation

Index

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

type CrossSporkClient struct {
	access.Client
	// contains filtered or unexported fields
}

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 (c *CrossSporkClient) ExecuteScriptAtBlockHeight(
	ctx context.Context,
	height uint64,
	script []byte,
	arguments []cadence.Value,
) (cadence.Value, error)

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 (e *EVM) GetBalance(
	address common.Address,
	height uint64,
) (*big.Int, error)

func (*EVM) GetCode added in v0.2.0

func (e *EVM) GetCode(
	address common.Address,
	height uint64,
) ([]byte, error)

func (*EVM) GetLatestEVMHeight added in v0.6.0

func (e *EVM) GetLatestEVMHeight(ctx context.Context) (uint64, error)

func (*EVM) GetNonce added in v0.2.0

func (e *EVM) GetNonce(
	address common.Address,
	height uint64,
) (uint64, error)

func (*EVM) GetStorageAt added in v0.23.0

func (e *EVM) GetStorageAt(
	address common.Address,
	hash common.Hash,
	height uint64,
) (common.Hash, error)

func (*EVM) SendRawTransaction

func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash, error)

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

func (*RemoteCadenceArch) Run added in v1.0.0

func (rca *RemoteCadenceArch) Run(input []byte) ([]byte, error)

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 NewSingleTxPool(
	ctx context.Context,
	client *CrossSporkClient,
	transactionsPublisher *models.Publisher[*gethTypes.Transaction],
	logger zerolog.Logger,
	config config.Config,
	collector metrics.Collector,
	keystore *keystore.KeyStore,
) (*SingleTxPool, error)

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.

type TxPool added in v0.21.0

type TxPool interface {
	Add(ctx context.Context, tx *gethTypes.Transaction) error
}

TxPool is the minimum interface that needs to be implemented by the various transaction pool strategies.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL