Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0038065
Added validation checks for required executors IDs for optimistic sync
UlyanaAndrukhiv Dec 4, 2025
dab8852
Added error handing. Added tests
UlyanaAndrukhiv Dec 5, 2025
aecdd15
Added error handling when criteria for exec result for optimistic syn…
UlyanaAndrukhiv Dec 5, 2025
c4f1593
Moved errors from common rpc to optimistic sync package
UlyanaAndrukhiv Dec 5, 2025
1382a76
Fixed info provider test
UlyanaAndrukhiv Dec 5, 2025
6c9e292
Small format and godoc refactoring
UlyanaAndrukhiv Dec 5, 2025
55f89ee
Merge branch 'feature/optimistic-sync' into UlianaAndrukhiv/8204-add-…
UlyanaAndrukhiv Dec 5, 2025
1491961
Udded Unwrap method to optimistic_sync errors
UlyanaAndrukhiv Dec 8, 2025
b6ff05d
Merge branch 'UlianaAndrukhiv/8204-add-executors-checks' of github.co…
UlyanaAndrukhiv Dec 8, 2025
79c056b
Updated execution result info impl for optimistic_sync by using all e…
UlyanaAndrukhiv Dec 8, 2025
9addcea
Added additional check for agreeing executors count. Updated error ha…
UlyanaAndrukhiv Dec 8, 2025
708500a
Refactored CriteriaNotMetError, added BlockFinalityMismatchError acco…
UlyanaAndrukhiv Dec 8, 2025
ed4f7d0
Updated godocs
UlyanaAndrukhiv Dec 8, 2025
c26a53e
Refactored check for Criteria not met, updated tests
UlyanaAndrukhiv Dec 10, 2025
440903f
Removed the validation check for required executors count
UlyanaAndrukhiv Dec 10, 2025
8e2e7e1
Moved is sealed logic for fork-aware execution result info into separ…
UlyanaAndrukhiv Dec 11, 2025
80888e7
Updated godocs
UlyanaAndrukhiv Dec 11, 2025
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
1 change: 1 addition & 0 deletions cmd/access/node_builder/access_node_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1784,6 +1784,7 @@ func (builder *FlowAccessNodeBuilder) buildExecutionResultInfoProvider() *FlowAc
node.Logger,
node.State,
node.Storage.Receipts,
node.Storage.Headers,
execNodeSelector,
operatorCriteria,
)
Expand Down
1 change: 1 addition & 0 deletions cmd/observer/node_builder/observer_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,7 @@ func (builder *ObserverServiceBuilder) buildExecutionResultInfoProvider() *Obser
node.Logger,
node.State,
node.Storage.Receipts,
node.Storage.Headers,
execNodeSelector,
operatorCriteria,
)
Expand Down
80 changes: 80 additions & 0 deletions engine/access/rpc/backend/accounts/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ func (a *Accounts) GetAccountAtLatestBlock(ctx context.Context, address flow.Add
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -181,6 +191,16 @@ func (a *Accounts) GetAccountAtBlockHeight(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -231,6 +251,16 @@ func (a *Accounts) GetAccountBalanceAtLatestBlock(ctx context.Context, address f
return 0, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return 0, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return 0, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return 0, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return 0, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return 0, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return 0, nil, access.NewInvalidRequestError(err)
default:
return 0, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -285,6 +315,16 @@ func (a *Accounts) GetAccountBalanceAtBlockHeight(
return 0, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return 0, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return 0, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return 0, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return 0, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return 0, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return 0, nil, access.NewInvalidRequestError(err)
default:
return 0, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -339,6 +379,16 @@ func (a *Accounts) GetAccountKeyAtLatestBlock(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -394,6 +444,16 @@ func (a *Accounts) GetAccountKeyAtBlockHeight(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -447,6 +507,16 @@ func (a *Accounts) GetAccountKeysAtLatestBlock(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -501,6 +571,16 @@ func (a *Accounts) GetAccountKeysAtBlockHeight(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down
30 changes: 30 additions & 0 deletions engine/access/rpc/backend/scripts/scripts.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ func (b *Scripts) ExecuteScriptAtLatestBlock(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -221,6 +231,16 @@ func (b *Scripts) ExecuteScriptAtBlockID(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down Expand Up @@ -284,6 +304,16 @@ func (b *Scripts) ExecuteScriptAtBlockHeight(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down
10 changes: 10 additions & 0 deletions engine/access/state_stream/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@ func (b *StateStreamBackend) GetRegisterValues(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down
10 changes: 10 additions & 0 deletions engine/access/state_stream/backend/backend_executiondata.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ func (b *ExecutionDataBackend) GetExecutionDataByBlockID(
return nil, nil, access.NewDataNotFoundError("execution data", err)
case common.IsInsufficientExecutionReceipts(err):
return nil, nil, access.NewDataNotFoundError("execution data", err)
case optimistic_sync.IsRequiredExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsAgreeingExecutorsCountExceededError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsUnknownRequiredExecutorError(err):
return nil, nil, access.NewInvalidRequestError(err)
case optimistic_sync.IsCriteriaNotMetError(err):
return nil, nil, access.NewPreconditionFailedError(err)
case optimistic_sync.IsBlockFinalityMismatchError(err):
return nil, nil, access.NewInvalidRequestError(err)
default:
return nil, nil, access.RequireNoError(ctx, err)
}
Expand Down
134 changes: 134 additions & 0 deletions module/executiondatasync/optimistic_sync/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package optimistic_sync

import (
"errors"
"fmt"

"github.com/onflow/flow-go/model/flow"
)

// RequiredExecutorsCountExceededError indicates that the requested number of required executors
// exceeds the total available execution nodes.
type RequiredExecutorsCountExceededError struct {
err error
}

func NewRequiredExecutorsCountExceededError(requiredExecutorsCount int, availableExecutorsCount int) *RequiredExecutorsCountExceededError {
return &RequiredExecutorsCountExceededError{
err: fmt.Errorf("required executors count exceeded: required %d, available %d", requiredExecutorsCount, availableExecutorsCount),
}
}

func (e RequiredExecutorsCountExceededError) Error() string {
return e.err.Error()
}

func (e RequiredExecutorsCountExceededError) Unwrap() error {
return e.err
}

func IsRequiredExecutorsCountExceededError(err error) bool {
var requiredExecutorsCountExceededError *RequiredExecutorsCountExceededError
return errors.As(err, &requiredExecutorsCountExceededError)
}

// AgreeingExecutorsCountExceededError indicates that the requested number of agreeing executors
// exceeds the total available execution nodes.
type AgreeingExecutorsCountExceededError struct {
err error
}

func NewAgreeingExecutorsCountExceededError(agreeingExecutorsCount uint, availableExecutorsCount int) *AgreeingExecutorsCountExceededError {
return &AgreeingExecutorsCountExceededError{
err: fmt.Errorf("agreeing executors count exceeded: provided %d, available %d", agreeingExecutorsCount, availableExecutorsCount),
}
}

func (e AgreeingExecutorsCountExceededError) Error() string {
return e.err.Error()
}

func (e AgreeingExecutorsCountExceededError) Unwrap() error {
return e.err
}

func IsAgreeingExecutorsCountExceededError(err error) bool {
var agreeingExecutorsCountExceededError *AgreeingExecutorsCountExceededError
return errors.As(err, &agreeingExecutorsCountExceededError)
}

// UnknownRequiredExecutorError indicates that a required executor ID is not present
// in the list of active execution nodes.
type UnknownRequiredExecutorError struct {
err error
}

func NewUnknownRequiredExecutorError(executorID flow.Identifier) *UnknownRequiredExecutorError {
return &UnknownRequiredExecutorError{
err: fmt.Errorf("unknown required executor ID %s", executorID.String()),
}
}

func (e UnknownRequiredExecutorError) Error() string {
return e.err.Error()
}

func (e UnknownRequiredExecutorError) Unwrap() error {
return e.err
}

func IsUnknownRequiredExecutorError(err error) bool {
var unknownRequiredExecutor *UnknownRequiredExecutorError
return errors.As(err, &unknownRequiredExecutor)
}

// CriteriaNotMetError indicates that the execution result criteria could not be
// satisfied for a given block, when the block is already sealed.
type CriteriaNotMetError struct {
err error
}

func NewCriteriaNotMetError(blockID flow.Identifier) *CriteriaNotMetError {
return &CriteriaNotMetError{
err: fmt.Errorf("the criteria for block %s is not met", blockID),
}
}

func (e CriteriaNotMetError) Error() string {
return e.err.Error()
}

func (e CriteriaNotMetError) Unwrap() error {
return e.err
}

func IsCriteriaNotMetError(err error) bool {
var criteriaNotMetError *CriteriaNotMetError
return errors.As(err, &criteriaNotMetError)
}

// BlockFinalityMismatchError indicates that the requested block does not match
// the finalized block at the same height. This means the block cannot belong
// to the canonical finalized chain.
type BlockFinalityMismatchError struct {
err error
}

func NewBlockFinalityMismatchError(blockID flow.Identifier, actualBlockID flow.Identifier) *BlockFinalityMismatchError {
return &BlockFinalityMismatchError{
err: fmt.Errorf("block %s is not the finalized block at its height (finalized block is %s)", blockID, actualBlockID),
}
}

func (e BlockFinalityMismatchError) Error() string {
return e.err.Error()
}

func (e BlockFinalityMismatchError) Unwrap() error {
return e.err
}

func IsBlockFinalityMismatchError(err error) bool {
var blockFinalityMismatchError *BlockFinalityMismatchError
return errors.As(err, &blockFinalityMismatchError)
}
Loading