Skip to content

feat(taiko-client): remove DELETE /preconfBlocks API #19537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -430,28 +430,6 @@ func (i *BlocksInserterPacaya) insertPreconfBlockFromExecutionPayload(
return i.rpc.L2.HeaderByHash(ctx, payload.BlockHash)
}

// RemovePreconfBlocks removes preconfirmation blocks from the L2 execution engine.
func (i *BlocksInserterPacaya) RemovePreconfBlocks(ctx context.Context, newLastBlockID uint64) error {
i.mutex.Lock()
defer i.mutex.Unlock()

newHead, err := i.rpc.L2.HeaderByNumber(ctx, new(big.Int).SetUint64(newLastBlockID))
if err != nil {
return err
}

fc := &engine.ForkchoiceStateV1{HeadBlockHash: newHead.Hash()}
fcRes, err := i.rpc.L2Engine.ForkchoiceUpdate(ctx, fc, nil)
if err != nil {
return err
}
if fcRes.PayloadStatus.Status != engine.VALID {
return fmt.Errorf("unexpected ForkchoiceUpdate response status: %s", fcRes.PayloadStatus.Status)
}

return nil
}

// IsBasedOnCanonicalChain checks if the given executable data is based on the canonical chain.
func (i *BlocksInserterPacaya) IsBasedOnCanonicalChain(
ctx context.Context,
Expand Down
28 changes: 0 additions & 28 deletions packages/taiko-client/driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,34 +429,6 @@ func (s *DriverTestSuite) TestInsertPreconfBlocks() {
s.Equal(l2Head2.Hash(), l1Origin2.L2BlockHash)
s.Equal(common.Hash{}, l1Origin2.L1BlockHash)
s.True(l1Origin2.IsPreconfBlock())

// Remove one preconfirmation block
res, err = resty.New().
R().
SetBody(&preconfblocks.RemovePreconfBlocksRequestBody{NewLastBlockID: l2Head3.Number().Uint64() - 1}).
Delete(s.preconfServerURL.String() + "/preconfBlocks")
s.Nil(err)
s.True(res.IsSuccess())

l2Head4, err := s.d.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)
s.Equal(l2Head2.Hash(), l2Head4.Hash())

// Propose 3 valid L2 blocks
s.ProposeAndInsertEmptyBlocks(s.p, s.d.ChainSyncer().EventSyncer())

l2Head5, err := s.d.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)
s.Equal(l2Head2.Number().Uint64()+2, l2Head5.Number().Uint64())
s.Equal(1, len(l2Head5.Transactions()))

l1Origin3, err := s.RPCClient.L2.L1OriginByID(context.Background(), l2Head5.Number())
s.Nil(err)
s.Equal(l2Head2.Number().Uint64()+2, l1Origin3.BlockID.Uint64())
s.Equal(l2Head5.Hash(), l1Origin3.L2BlockHash)
s.NotZero(l1Origin3.L1BlockHeight.Uint64())
s.NotEmpty(l1Origin3.L1BlockHash)
s.False(l1Origin3.IsPreconfBlock())
}

func (s *DriverTestSuite) TestInsertPreconfBlocksNotReorg() {
Expand Down
114 changes: 0 additions & 114 deletions packages/taiko-client/driver/preconf_blocks/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"net/http"

"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
Expand Down Expand Up @@ -268,119 +267,6 @@ func (s *PreconfBlockAPIServer) BuildPreconfBlock(c echo.Context) error {
return c.JSON(http.StatusOK, BuildPreconfBlockResponseBody{BlockHeader: header})
}

// RemovePreconfBlocksRequestBody represents a request body when resetting the backend
// L2 execution engine preconfirmation head.
type RemovePreconfBlocksRequestBody struct {
// @param newLastBlockID uint64 New last block ID of the blockchain, it should
// @param not smaller than the canonical chain's highest block ID.
NewLastBlockID uint64 `json:"newLastBlockId"`
}

// RemovePreconfBlocksResponseBody represents a response body when resetting the backend
// L2 execution engine preconfirmation head.
type RemovePreconfBlocksResponseBody struct {
// @param lastBlockID uint64 Current highest block ID of the blockchain (including preconfirmation blocks)
LastBlockID uint64 `json:"lastBlockId"`
// @param lastProposedBlockID uint64 Highest block ID of the cnonical chain
LastProposedBlockID uint64 `json:"lastProposedBlockID"`
// @param headsRemoved uint64 Number of preconfirmation heads removed
HeadsRemoved uint64 `json:"headsRemoved"`
}

// RemovePreconfBlocks removes the backend L2 execution engine preconfirmation head.
//
// @Description Remove all preconfirmation blocks from the blockchain beyond the specified block height,
// @Description ensuring the latest block ID does not exceed the given height. This method will fail if
// @Description the block with an ID one greater than the specified height is not a preconfirmation block. If the
// @Description specified block height is greater than the latest preconfirmation block ID, the method will succeed
// @Description without modifying the blockchain.
// @Param body body RemovePreconfBlocksRequestBody true "preconfirmation blocks removing request body"
// @Accept json
// @Produce json
// @Success 200 {object} RemovePreconfBlocksResponseBody
// @Router /preconfBlocks [delete]
func (s *PreconfBlockAPIServer) RemovePreconfBlocks(c echo.Context) error {
if s.rpc.PacayaClients.TaikoWrapper != nil {
// Check if the preconfirmation is enabled.
preconfRouter, err := s.rpc.GetPreconfRouterPacaya(&bind.CallOpts{Context: c.Request().Context()})
if err != nil {
return s.returnError(c, http.StatusInternalServerError, err)
}
if preconfRouter == rpc.ZeroAddress {
log.Warn("Preconfirmation is disabled via taikoWrapper", "preconfRouter", preconfRouter.Hex())
return s.returnError(
c,
http.StatusInternalServerError,
errors.New("preconfirmation is disabled via taikoWrapper"),
)
}
}

// Parse the request body.
reqBody := new(RemovePreconfBlocksRequestBody)
if err := c.Bind(reqBody); err != nil {
return s.returnError(c, http.StatusUnprocessableEntity, err)
}

// Request body validation.
canonicalHeadL1Origin, err := s.rpc.L2.HeadL1Origin(c.Request().Context())
if err != nil && err.Error() != ethereum.NotFound.Error() {
return s.returnError(c, http.StatusInternalServerError, err)
}

currentHead, err := s.rpc.L2.HeaderByNumber(c.Request().Context(), nil)
if err != nil {
return s.returnError(c, http.StatusInternalServerError, err)
}

if canonicalHeadL1Origin != nil && reqBody.NewLastBlockID < canonicalHeadL1Origin.BlockID.Uint64() {
return s.returnError(
c,
http.StatusBadRequest,
errors.New("newLastBlockId must not be smaller than the canonical chain's highest block ID"),
)
}

log.Info(
"New preconfirmation block removing request",
"newLastBlockId", reqBody.NewLastBlockID,
"currentHead", currentHead.Number.Uint64(),
)

if err := s.chainSyncer.RemovePreconfBlocks(c.Request().Context(), reqBody.NewLastBlockID); err != nil {
return s.returnError(c, http.StatusBadRequest, err)
}

newHead, err := s.rpc.L2.HeaderByNumber(c.Request().Context(), nil)
if err != nil {
return s.returnError(c, http.StatusInternalServerError, err)
}

var lastBlockID uint64
if canonicalHeadL1Origin != nil {
lastBlockID = canonicalHeadL1Origin.BlockID.Uint64()
}

// If current block number is less than the highest unsafe L2 payload block ID,
// update the highest unsafe L2 payload block ID.
if newHead.Number.Uint64() < s.highestUnsafeL2PayloadBlockID {
s.updateHighestUnsafeL2Payload(newHead.Number.Uint64())
}

log.Debug(
"Removed preconfirmation blocks",
"newHead", newHead.Number.Uint64(),
"lastBlockID", lastBlockID,
"headsRemoved", currentHead.Number.Uint64()-newHead.Number.Uint64(),
)

return c.JSON(http.StatusOK, RemovePreconfBlocksResponseBody{
LastBlockID: newHead.Number.Uint64(),
LastProposedBlockID: lastBlockID,
HeadsRemoved: currentHead.Number.Uint64() - newHead.Number.Uint64(),
})
}

// HealthCheck is the endpoints for probes.
//
// @Summary Get current server health status
Expand Down
2 changes: 0 additions & 2 deletions packages/taiko-client/driver/preconf_blocks/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ var (
// preconfBlockChainSyncer is an interface for preconfirmation block chain syncer.
type preconfBlockChainSyncer interface {
InsertPreconfBlocksFromExecutionPayloads(context.Context, []*eth.ExecutionPayload, bool) ([]*types.Header, error)
RemovePreconfBlocks(ctx context.Context, newLastBlockID uint64) error
}

// @title Taiko Preconfirmation Block Server API
Expand Down Expand Up @@ -186,7 +185,6 @@ func (s *PreconfBlockAPIServer) configureRoutes() {
s.echo.GET("/healthz", s.HealthCheck)
s.echo.GET("/status", s.GetStatus)
s.echo.POST("/preconfBlocks", s.BuildPreconfBlock)
s.echo.DELETE("/preconfBlocks", s.RemovePreconfBlocks)

// WebSocket routes
s.echo.GET("/ws", s.ws.handleWebSocket)
Expand Down