Skip to content
Merged
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
1 change: 1 addition & 0 deletions models/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var (
ErrDisconnected = NewRecoverableError(errors.New("disconnected"))
ErrMissingBlock = errors.New("missing block")
ErrMissingTransactions = errors.New("missing transactions")
ErrInvalidParentHash = errors.New("invalid parent block hash")

// Transaction errors

Expand Down
20 changes: 18 additions & 2 deletions services/ingestion/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ func TestSerialBlockIngestion(t *testing.T) {

storedCounter := 0
runs := uint64(20)
var prevBlock *models.Block
for i := latestHeight + 1; i < latestHeight+runs; i++ {
cadenceHeight := i + 10
blockCdc, block, blockEvent, err := newBlock(i, nil)
blockCdc, block, blockEvent, err := newBlockWithParent(i, nil, prevBlock)
require.NoError(t, err)

blocks.
Expand All @@ -107,6 +108,8 @@ func TestSerialBlockIngestion(t *testing.T) {
}},
Height: cadenceHeight,
})

prevBlock = block
}

close(eventsChan)
Expand Down Expand Up @@ -535,8 +538,21 @@ func TestBlockAndTransactionIngestion(t *testing.T) {
}

func newBlock(height uint64, txHashes []gethCommon.Hash) (cadence.Event, *models.Block, *events.Event, error) {
return newBlockWithParent(height, txHashes, nil)
}

func newBlockWithParent(height uint64, txHashes []gethCommon.Hash, parent *models.Block) (cadence.Event, *models.Block, *events.Event, error) {
parentHash := gethCommon.HexToHash("0x1")
if parent != nil {
var err error
parentHash, err = parent.Hash()
if err != nil {
return cadence.Event{}, nil, nil, err
}
}

gethBlock := types.NewBlock(
gethCommon.HexToHash("0x1"),
parentHash,
height,
uint64(1337),
big.NewInt(100),
Expand Down
19 changes: 19 additions & 0 deletions services/replayer/blocks_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

gethCommon "github.com/ethereum/go-ethereum/common"
"github.com/onflow/flow-evm-gateway/models"
errs "github.com/onflow/flow-evm-gateway/models/errors"
"github.com/onflow/flow-evm-gateway/storage"
"github.com/onflow/flow-go/fvm/evm/offchain/blocks"
evmTypes "github.com/onflow/flow-go/fvm/evm/types"
Expand Down Expand Up @@ -75,6 +76,24 @@ func (bp *BlocksProvider) OnBlockReceived(block *models.Block) error {
)
}

// Verify that the new block's parent hash matches the latest block's hash
if bp.latestBlock != nil {
latestHash, err := bp.latestBlock.Hash()
if err != nil {
return fmt.Errorf("failed to compute hash of latest block %d: %w", bp.latestBlock.Height, err)
}
if block.ParentBlockHash != latestHash {
return fmt.Errorf(
"%w: block %d has parent hash %s, but parent block %d has hash %s",
errs.ErrInvalidParentHash,
block.Height,
block.ParentBlockHash.Hex(),
bp.latestBlock.Height,
latestHash.Hex(),
)
}
}

bp.latestBlock = block

return nil
Expand Down
39 changes: 37 additions & 2 deletions services/replayer/blocks_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,52 @@ func TestOnBlockReceived(t *testing.T) {
)
})

t.Run("with new block non-sequential to latest block", func(t *testing.T) {
t.Run("with sequential blocks and valid parent hash", func(t *testing.T) {
_, blocks := setupBlocksDB(t)
blocksProvider := NewBlocksProvider(blocks, flowGo.Emulator)

block1 := mocks.NewBlock(10)
err := blocksProvider.OnBlockReceived(block1)
require.NoError(t, err)

block2 := mocks.NewBlock(11)
block2 := mocks.NewBlockWithParent(11, block1)
err = blocksProvider.OnBlockReceived(block2)
require.NoError(t, err)
})

t.Run("with valid parent hash linkage", func(t *testing.T) {
_, blocks := setupBlocksDB(t)
blocksProvider := NewBlocksProvider(blocks, flowGo.Emulator)

block1 := mocks.NewBlock(10)
err := blocksProvider.OnBlockReceived(block1)
require.NoError(t, err)

// Create block2 with correct parent hash pointing to block1
block2 := mocks.NewBlockWithParent(11, block1)
err = blocksProvider.OnBlockReceived(block2)
require.NoError(t, err)

// Create block3 with correct parent hash pointing to block2
block3 := mocks.NewBlockWithParent(12, block2)
err = blocksProvider.OnBlockReceived(block3)
require.NoError(t, err)
})

t.Run("with invalid parent hash", func(t *testing.T) {
_, blocks := setupBlocksDB(t)
blocksProvider := NewBlocksProvider(blocks, flowGo.Emulator)

block1 := mocks.NewBlock(10)
err := blocksProvider.OnBlockReceived(block1)
require.NoError(t, err)

// Create block2 with wrong parent hash (using NewBlock which generates arbitrary parent hash)
block2 := mocks.NewBlock(11)
err = blocksProvider.OnBlockReceived(block2)
require.Error(t, err)
assert.ErrorContains(t, err, "invalid parent block hash")
assert.ErrorContains(t, err, "block 11 has parent hash")
})
}

Expand Down
23 changes: 23 additions & 0 deletions storage/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,29 @@ func NewBlock(height uint64) *models.Block {
}
}

// NewBlockWithParent creates a new block at the given height with the parent hash
// correctly set to the hash of the provided parent block.
func NewBlockWithParent(height uint64, parent *models.Block) *models.Block {
var parentHash common.Hash
if parent != nil {
parentHash, _ = parent.Hash()
}

return &models.Block{
Block: &types.Block{
ParentBlockHash: parentHash,
Height: height,
Timestamp: uint64(time.Now().Second()),
TotalSupply: big.NewInt(1000),
ReceiptRoot: common.HexToHash(fmt.Sprintf("0x100%d", height)),
TransactionHashRoot: common.HexToHash(fmt.Sprintf("0x200%d", height)),
TotalGasUsed: uint64(30_000),
PrevRandao: common.HexToHash(fmt.Sprintf("0x300%d", height)),
},
TransactionHashes: make([]common.Hash, 0),
}
}

func NewReceipt(block *models.Block) *models.Receipt {
txHash := common.HexToHash(fmt.Sprintf("0xff%d", block.Height))
blockHash, _ := block.Hash()
Expand Down