Skip to content

handle error #59

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 2 commits into from
Jun 3, 2025
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
25 changes: 19 additions & 6 deletions internal/espressoreader/espresso_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ func (e *EspressoReader) Run(ctx context.Context, ready chan<- struct{}) error {
slog.Debug("Espresso:", "app", appAddress, "currentBlockHeight", currentEspressoBlockHeight)

var l1FinalizedTimestamp uint64
lastProcessedL1Block, l1FinalizedTimestamp = e.readL1(ctx, app, currentEspressoBlockHeight, lastProcessedL1Block)
lastProcessedL1Block, l1FinalizedTimestamp, err = e.readL1(ctx, app, currentEspressoBlockHeight, lastProcessedL1Block)
if err != nil {
slog.Error("failed reading L1", "error", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it ok to just skip?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right. This should be a break

break
}
e.readEspresso(ctx, app, currentEspressoBlockHeight, namespace, lastProcessedL1Block, l1FinalizedTimestamp)
}
}
Expand Down Expand Up @@ -172,14 +176,19 @@ func (e *EspressoReader) bootstrap(ctx context.Context, app evmreader.TypeExport
err = json.Unmarshal(nsTableBytes, &nsTables)
if err != nil {
slog.Error("failed fetching ns tables", "error", err, "ns table", nsTables)
return err
} else {
for index, nsTable := range nsTables {
nsTableBytes, _ := base64.StdEncoding.DecodeString(nsTable)
ns := e.espressoHelper.extractNS(nsTableBytes)
currentEspressoBlock := batchStartingBlock + uint64(index)
if slices.Contains(ns, uint32(namespace)) {
slog.Debug("found namespace contained in", "block", currentEspressoBlock)
l1FinalizedHeight, l1FinalizedTimestamp = e.readL1(ctx, app, currentEspressoBlock, l1FinalizedHeight)
l1FinalizedHeight, l1FinalizedTimestamp, err = e.readL1(ctx, app, currentEspressoBlock, l1FinalizedHeight)
if err != nil {
slog.Error("failed reading L1", "error", err)
return err
}
e.readEspresso(ctx, app, currentEspressoBlock, namespace, l1FinalizedHeight, l1FinalizedTimestamp)
} else {
err = e.repository.UpdateLastProcessedEspressoBlock(ctx, app.IApplicationAddress, currentEspressoBlock)
Expand All @@ -197,7 +206,7 @@ func (e *EspressoReader) bootstrap(ctx context.Context, app evmreader.TypeExport
return nil
}

func (e *EspressoReader) readL1(ctx context.Context, app evmreader.TypeExportApplication, currentBlockHeight uint64, lastProcessedL1Block uint64) (uint64, uint64) {
func (e *EspressoReader) readL1(ctx context.Context, app evmreader.TypeExportApplication, currentBlockHeight uint64, lastProcessedL1Block uint64) (uint64, uint64, error) {
l1FinalizedLatestHeight, l1FinalizedTimestamp := e.espressoHelper.getL1FinalizedHeight(ctx, currentBlockHeight, e.maxDelay, e.url)
// read L1 if there might be update
if l1FinalizedLatestHeight > lastProcessedL1Block {
Expand All @@ -207,12 +216,16 @@ func (e *EspressoReader) readL1(ctx context.Context, app evmreader.TypeExportApp
apps = append(apps, app) // make app into 1-element array

// start reading from the block after the prev height
e.evmReader.ReadAndStoreInputs(ctx, lastProcessedL1Block, l1FinalizedLatestHeight, apps)
err := e.evmReader.ReadAndStoreInputs(ctx, lastProcessedL1Block, l1FinalizedLatestHeight, apps)
if err != nil {
slog.Error("failed to read and store L1 inputs", "error", err)
return 0, 0, err
}
// check for claim status and output execution
// e.evmReader.CheckForClaimStatus(ctx, apps, l1FinalizedLatestHeight) // checked by the node
// e.evmReader.CheckForOutputExecution(ctx, apps, l1FinalizedLatestHeight) // checked by the node
}
return l1FinalizedLatestHeight, l1FinalizedTimestamp
return l1FinalizedLatestHeight, l1FinalizedTimestamp, nil
}

type EspressoInput struct {
Expand Down Expand Up @@ -441,7 +454,7 @@ func (e *EspressoReader) readEspresso(ctx context.Context, appEvmType evmreader.
)
if err != nil {
slog.Error("could not store Espresso input", "err", err)
continue
return
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning here exits the entire readEspresso loop instead of skipping the failed log; consider using continue or returning an error to the caller.

Suggested change
return
continue

Copilot uses AI. Check for mistakes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. The loop should be exited

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. The loop should be exited

In Go, it's common practice to return the error to the caller rather than relying on nil.
You can return the error itself if needed.

}

// update nonce
Expand Down
2 changes: 1 addition & 1 deletion internal/espressoreader/espresso_reader_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,7 @@ func (s *EspressoReaderUnitTestSuite) TestEdgeCaseSkippingL1Blocks() {

appContractAddress := common.HexToAddress("0x5a205fcb6947e200615b75c409ac0aa486d77649")
indexValue := new(big.Int).SetUint64(1)
inputData := []byte{0x01, 0x02, 0x03, 0x4a, 0x5b, 0x6c}
inputData, _ := hex.DecodeString("6968957800000000000000000000000000000000000000000000000000000000000000010000000000000000000000001234567890abcdef1234567890abcdef12345678000000000000000000000000fedcba9876543210fedcba9876543210fedcba980000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000006322c8000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000")
rawLog := eth_types.Log{
Address: common.HexToAddress("0x5a205fcb6947e200615b75c409ac0aa486d77649"),
Topics: []common.Hash{common.HexToHash("0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789")},
Expand Down
94 changes: 62 additions & 32 deletions internal/espressoreader/espressoreader_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,24 @@ func (s *EspressoReaderService) requestNonce(w http.ResponseWriter, r *http.Requ
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set the Content-Type header before writing JSON, e.g., w.Header().Set("Content-Type", "application/json").

Suggested change
w.WriteHeader(http.StatusMethodNotAllowed)
w.WriteHeader(http.StatusMethodNotAllowed)
w.Header().Set("Content-Type", "application/json")

Copilot uses AI. Check for mistakes.

json.NewEncoder(w).Encode(map[string]string{"error": "Only POST method is allowed"})
return
}

body, err := io.ReadAll(r.Body)
if err != nil {
slog.Error("could not read body", "err", err)
// TODO: error?
slog.Error("could not read request body", "error", err)
http.Error(w, "Failed to read request body", http.StatusBadRequest)
return
}
defer r.Body.Close()

nonceRequest := &NonceRequest{}
if err := json.Unmarshal(body, nonceRequest); err != nil {
slog.Error("could not unmarshal", "err", err)
// ?
slog.Error("could not unmarshal request body", "error", err, "body", string(body))
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}

senderAddress := common.HexToAddress(nonceRequest.MsgSender)
Expand All @@ -128,9 +134,15 @@ func (s *EspressoReaderService) requestNonce(w http.ResponseWriter, r *http.Requ
if nonceCache[applicationAddress] == nil {
nonceCache[applicationAddress] = make(map[common.Address]uint64)
}
if nonceCache[applicationAddress][senderAddress] == 0 {
_, exists := nonceCache[applicationAddress][senderAddress]
if !exists {
ctx := r.Context()
nonce = s.queryNonceFromDb(ctx, senderAddress, applicationAddress)
nonce, err = s.queryNonceFromDb(ctx, senderAddress, applicationAddress)
if err != nil {
slog.Error("failed to query nonce from database", "error", err, "senderAddress", senderAddress, "applicationAddress", applicationAddress)
http.Error(w, "Failed to retrieve nonce", http.StatusInternalServerError)
return
}
nonceCache[applicationAddress][senderAddress] = nonce
} else {
nonce = nonceCache[applicationAddress][senderAddress]
Expand All @@ -139,10 +151,6 @@ func (s *EspressoReaderService) requestNonce(w http.ResponseWriter, r *http.Requ
slog.Debug("got nonce request", "senderAddress", senderAddress, "applicationAddress", applicationAddress)

nonceResponse := NonceResponse{Nonce: nonce}
if err != nil {
slog.Error("error json marshal nonce response", "err", err)
// ?
}

err = json.NewEncoder(w).Encode(nonceResponse)
if err != nil {
Expand All @@ -156,14 +164,14 @@ func (s *EspressoReaderService) requestNonce(w http.ResponseWriter, r *http.Requ
func (s *EspressoReaderService) queryNonceFromDb(
ctx context.Context,
senderAddress common.Address,
applicationAddress common.Address) uint64 {
applicationAddress common.Address) (uint64, error) {
nonce, err := s.database.GetEspressoNonce(ctx, senderAddress.Hex(), applicationAddress.Hex())
if err != nil {
slog.Error("failed to get espresso nonce", "error", err)
// TODO: error?
return 0, err
}

return nonce
return nonce, nil
}

type SubmitResponse struct {
Expand All @@ -175,20 +183,23 @@ func (s *EspressoReaderService) submit(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
if r.Method != http.MethodPost {
// TODO: error?
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]string{"error": "Only POST method is allowed"})
return
}

body, err := io.ReadAll(r.Body)
if err != nil {
slog.Error("could not read body", "err", err)
slog.Error("could not read request body", "error", err)
http.Error(w, "Failed to read request body", http.StatusBadRequest)
return
}
slog.Debug("got submit request", "request body", string(body))

msgSender, typedData, sigHash, err := ExtractSigAndData(string(body))
if err != nil {
slog.Error("transaction not correctly formatted", "error", err)
// TODO: error?
http.Error(w, "Invalid transaction format", http.StatusBadRequest)
return
}
submitResponse := SubmitResponse{Id: sigHash}
Expand All @@ -198,43 +209,62 @@ func (s *EspressoReaderService) submit(w http.ResponseWriter, r *http.Request) {
client := client.NewClient(s.EspressoBaseUrl)
ctx := r.Context()
_, namespace, err := getEspressoConfig(ctx, appAddress, s.database, s.blockchainHttpEndpoint)
if err != nil {
slog.Error("failed to get espresso config", "error", err, "appAddress", appAddress)
http.Error(w, "Failed to get application configuration", http.StatusInternalServerError)
return
}
var tx types.Transaction
tx.Namespace = namespace
tx.Payload = body
_, err = client.SubmitTransaction(ctx, tx)
if err != nil {
slog.Error("espresso tx submit error", "err", err)
// TODO: error?
slog.Error("espresso tx submit error", "error", err)
http.Error(w, "Failed to submit transaction to Espresso", http.StatusInternalServerError)
return
}

err = json.NewEncoder(w).Encode(submitResponse)
if err != nil {
slog.Info("Internal server error",
"service", "espresso submit endpoint",
"err", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
// TODO: error?
if err := json.NewEncoder(w).Encode(submitResponse); err != nil {
slog.Error("failed to encode submit response", "error", err, "submitResponse", submitResponse)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}

// update nonce cache
if nonceCache[appAddress] == nil {
slog.Error("Should query nonce before submit")
// TODO: error?
slog.Error("Should query nonce before submit", "appAddress", appAddress)
http.Error(w, "Nonce not initialized for this application. Please request a nonce first.", http.StatusBadRequest)
return
}
nonceInRequestFloat, ok := typedData.Message["nonce"].(float64)
if !ok {
slog.Error("Nonce not found or invalid type in request", "typedData", typedData)
http.Error(w, "Invalid or missing nonce in request", http.StatusBadRequest)
return
}
nonceInRequest := uint64(typedData.Message["nonce"].(float64))
if nonceCache[appAddress][msgSender] == 0 {
nonceInRequest := uint64(nonceInRequestFloat)

cachedNonce, exists := nonceCache[appAddress][msgSender]
if !exists {
ctx := r.Context()
nonceInDb := s.queryNonceFromDb(ctx, msgSender, appAddress)
nonceInDb, err := s.queryNonceFromDb(ctx, msgSender, appAddress)
if err != nil {
slog.Error("failed to query nonce from database during submit", "error", err, "senderAddress", msgSender, "applicationAddress", appAddress)
http.Error(w, "Failed to retrieve nonce for validation", http.StatusInternalServerError)
return
}
if nonceInRequest != nonceInDb {
slog.Error("Nonce in request is incorrect")
// TODO: error?
slog.Error("Nonce in request is incorrect", "nonceInRequest", nonceInRequest, "nonceInDb", nonceInDb, "senderAddress", msgSender, "applicationAddress", appAddress)
http.Error(w, "Incorrect nonce", http.StatusBadRequest)
return
}
nonceCache[appAddress][msgSender] = nonceInDb + 1
} else {
if nonceInRequest != cachedNonce {
slog.Error("Nonce in request is incorrect", "requestNonce", nonceInRequest, "cachedNonce", cachedNonce, "senderAddress", msgSender, "applicationAddress", appAddress)
http.Error(w, "Incorrect nonce", http.StatusBadRequest)
return
}
nonceCache[appAddress][msgSender]++
}
}
Expand Down
14 changes: 8 additions & 6 deletions internal/evmreader/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,23 @@ func (r *EvmReader) ReadAndStoreInputs(
combinedIndex, err := r.repository.GetInputIndexWithTx(ctx, dbTx, address.Hex())
if err != nil {
slog.Error("evmreader: failed to read index", "app", address, "error", err)
return errors.New("evmreader: failed to read index")
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap and return the original error to preserve context, e.g., fmt.Errorf("evmreader: failed to read index: %w", err).

Suggested change
return errors.New("evmreader: failed to read index")
return fmt.Errorf("evmreader: failed to read index: %w", err)

Copilot uses AI. Check for mistakes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't seem necessary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't seem necessary

The only drawback here is that the original error is not preserved in the tracker.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original error message is printed as error message. So should be fine

}
if combinedIndex != input.Index && r.shouldModifyIndex == true {
slog.Info("evmreader: Overriding input index", "onchain-index", input.Index, "new-index", combinedIndex)
input.Index = combinedIndex
modifiedRawData, err := r.modifyIndexInRaw(input.RawData, combinedIndex)
if err == nil {
input.RawData = modifiedRawData
if err != nil {
slog.Error("evmreader: failed to modify index", "app", address, "error", err)
return errors.New("evmreader: failed to modify index")
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap the original error when returning, e.g., fmt.Errorf("evmreader: failed to modify index: %w", err).

Suggested change
return errors.New("evmreader: failed to modify index")
return fmt.Errorf("evmreader: failed to modify index: %w", err)

Copilot uses AI. Check for mistakes.

}
input.RawData = modifiedRawData
}
// update input index
err = r.repository.UpdateInputIndexWithTx(ctx, dbTx, address.Hex())
if err != nil {
slog.Error("failed to update index", "app", address, "error", err)
return errors.New("evmreader: failed to update index")
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap the repository error to keep the original error context, e.g., fmt.Errorf("evmreader: failed to update index: %w", err).

Suggested change
return errors.New("evmreader: failed to update index")
return fmt.Errorf("evmreader: failed to update index: %w", err)

Copilot uses AI. Check for mistakes.

}
epochInputMap[currentEpoch] = append(currentInputs, input)

Expand Down Expand Up @@ -201,15 +205,13 @@ func (r *EvmReader) ReadAndStoreInputs(
"address", address,
"error", err,
)
dbTx.Rollback(ctx)
continue
return errors.New("evmreader: Error storing inputs and epochs")
Copy link
Preview

Copilot AI May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use consistent lowercase for error messages and wrap the original error, e.g., fmt.Errorf("evmreader: error storing inputs and epochs: %w", err).

Suggested change
return errors.New("evmreader: Error storing inputs and epochs")
return fmt.Errorf("evmreader: error storing inputs and epochs: %w", err)

Copilot uses AI. Check for mistakes.

}
// Commit transaction
err = dbTx.Commit(ctx)
if err != nil {
slog.Error("could not commit db tx", "err", err)
dbTx.Rollback(ctx)
continue
return errors.New("evmreader: could not commit db tx")
}

// Store everything
Expand Down