Skip to content
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

check unsupported precompiles #3264

Merged
merged 12 commits into from
Feb 14, 2024
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ services:
zkevm-prover:
container_name: zkevm-prover
restart: unless-stopped
image: hermeznetwork/zkevm-prover:v4.0.4
image: hermeznetwork/zkevm-prover:v4.0.10
depends_on:
zkevm-state-db:
condition: service_healthy
Expand Down
3 changes: 3 additions & 0 deletions event/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const (
EventID_SynchronizerHalt EventID = "SYNCHRONIZER HALT"
// EventID_SequenceSenderHalt is triggered when the SequenceSender halts
EventID_SequenceSenderHalt EventID = "SEQUENCESENDER HALT"
// EventID_UnsupportedPrecompile is triggered when the executor returns an unsupported precompile error
EventID_UnsupportedPrecompile EventID = "UNSUPPORTED PRECOMPILE"

// EventID_NodeOOC is triggered when an OOC at node level is detected
EventID_NodeOOC EventID = "NODE OOC"
// Source_Node is the source of the event
Expand Down
14 changes: 14 additions & 0 deletions proto/src/proto/executor/v1/executor.proto
Original file line number Diff line number Diff line change
Expand Up @@ -854,4 +854,18 @@ enum ExecutorError {
EXECUTOR_ERROR_INVALID_L1_INFO_TREE_INDEX = 111;
// EXECUTOR_ERROR_INVALID_L1_INFO_TREE_SMT_PROOF_VALUE indicates that the ROM asked for an L1InfoTree SMT proof that was not present in the input
EXECUTOR_ERROR_INVALID_L1_INFO_TREE_SMT_PROOF_VALUE = 112;
// EXECUTOR_ERROR_INVALID_WITNESS indicates that the provided witness data is invalid
EXECUTOR_ERROR_INVALID_WITNESS = 113;
// EXECUTOR_ERROR_INVALID_CBOR indicates that the provided CBOR data is invalid
EXECUTOR_ERROR_INVALID_CBOR = 114;
// EXECUTOR_ERROR_INVALID_DATA_STREAM indicates that the provided data stream data is invalid
EXECUTOR_ERROR_INVALID_DATA_STREAM = 115;
// EXECUTOR_ERROR_INVALID_UPDATE_MERKLE_TREE indicates that the provided update merkle tree is invalid, e.g. because the executor is configured not to write to database
EXECUTOR_ERROR_INVALID_UPDATE_MERKLE_TREE = 116;
// EXECUTOR_ERROR_UNSUPPORTED_PRECOMPILED indicates that an unsupported precompiled has been used
EXECUTOR_ERROR_UNSUPPORTED_PRECOMPILED = 117;
// EXECUTOR_ERROR_OOG_2 indicates that an out of gas has occurred
EXECUTOR_ERROR_OOG_2 = 118;
// EXECUTOR_ERROR_CLOSE_BATCH indicates that batch must be closed
EXECUTOR_ERROR_CLOSE_BATCH = 119;
}
5 changes: 5 additions & 0 deletions sequencer/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ func (f *finalizer) openNewWIPBatch(ctx context.Context, batchNumber uint64, sta

// closeWIPBatch closes the current batch in the state
func (f *finalizer) closeWIPBatch(ctx context.Context) error {
// Sanity check: batch must not be empty (should have L2 blocks)
if f.wipBatch.isEmpty() {
f.Halt(ctx, fmt.Errorf("closing WIP batch %d without L2 blocks and should have at least 1", f.wipBatch.batchNumber), false)
}

usedResources := getUsedBatchResources(f.batchConstraints, f.wipBatch.imRemainingResources)
receipt := state.ProcessingReceipt{
BatchNumber: f.wipBatch.batchNumber,
Expand Down
70 changes: 52 additions & 18 deletions sequencer/finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sequencer

import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
Expand Down Expand Up @@ -283,14 +284,16 @@ func (f *finalizer) finalizeBatches(ctx context.Context) {
continue
}

closeWIPBatch := false
metrics.WorkerProcessingTime(time.Since(start))
if tx != nil {
showNotFoundTxLog = true

firstTxProcess := true

for {
_, err := f.processTransaction(ctx, tx, firstTxProcess)
var err error
_, closeWIPBatch, err = f.processTransaction(ctx, tx, firstTxProcess)
if err != nil {
if err == ErrEffectiveGasPriceReprocess {
firstTxProcess = false
Expand Down Expand Up @@ -325,7 +328,11 @@ func (f *finalizer) finalizeBatches(ctx context.Context) {
}

// Check if we must finalize the batch due to a closing reason (resources exhausted, max txs, timestamp resolution, forced batches deadline)
if finalize, closeReason := f.checkIfFinalizeBatch(); finalize {
finalize, closeReason := f.checkIfFinalizeBatch()
if closeWIPBatch || finalize {
if closeWIPBatch {
closeReason = "Executor close batch"
}
f.finalizeWIPBatch(ctx, closeReason)
}

Expand All @@ -337,7 +344,7 @@ func (f *finalizer) finalizeBatches(ctx context.Context) {
}

// processTransaction processes a single transaction.
func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, firstTxProcess bool) (errWg *sync.WaitGroup, err error) {
func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, firstTxProcess bool) (errWg *sync.WaitGroup, closeWIPBatch bool, err error) {
start := time.Now()
defer func() {
metrics.ProcessingTime(time.Since(start))
Expand Down Expand Up @@ -380,7 +387,7 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first
egp, err := f.effectiveGasPrice.CalculateEffectiveGasPrice(tx.RawTx, txGasPrice, tx.BatchResources.ZKCounters.GasUsed, tx.L1GasPrice, txL2GasPrice)
if err != nil {
if f.effectiveGasPrice.IsEnabled() {
return nil, err
return nil, false, err
} else {
log.Warnf("effectiveGasPrice is disabled, but failed to calculate effectiveGasPrice for tx %s, error: %v", tx.HashStr, err)
tx.EGPLog.Error = fmt.Sprintf("CalculateEffectiveGasPrice#1: %s", err)
Expand Down Expand Up @@ -408,7 +415,7 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first
egpPercentage, err := f.effectiveGasPrice.CalculateEffectiveGasPricePercentage(txGasPrice, tx.EffectiveGasPrice)
if err != nil {
if f.effectiveGasPrice.IsEnabled() {
return nil, err
return nil, false, err
} else {
log.Warnf("effectiveGasPrice is disabled, but failed to to calculate efftive gas price percentage (#1), error: %v", err)
tx.EGPLog.Error = fmt.Sprintf("%s; CalculateEffectiveGasPricePercentage#1: %s", tx.EGPLog.Error, err)
Expand All @@ -428,7 +435,7 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first

effectivePercentageAsDecodedHex, err := hex.DecodeHex(fmt.Sprintf("%x", tx.EGPPercentage))
if err != nil {
return nil, err
return nil, false, err
}

batchRequest.Transactions = append(batchRequest.Transactions, effectivePercentageAsDecodedHex...)
Expand All @@ -437,12 +444,34 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first

if err != nil && (errors.Is(err, runtime.ErrExecutorDBError) || errors.Is(err, runtime.ErrInvalidTxChangeL2BlockMinTimestamp)) {
log.Errorf("failed to process tx %s, error: %v", tx.HashStr, err)
return nil, err
return nil, false, err
} else if err == nil && !batchResponse.IsRomLevelError && len(batchResponse.BlockResponses) == 0 {
err = fmt.Errorf("executor returned no errors and no responses for tx %s", tx.HashStr)
f.Halt(ctx, err, false)
} else if err != nil {
log.Errorf("error received from executor, error: %v", err)

if errors.Is(err, runtime.ErrExecutorErrorUnsupportedPrecompile) {
payload, err := json.Marshal(batchRequest)
if err != nil {
log.Errorf("error marshaling payload, error: %v", err)
} else {
event := &event.Event{
ReceivedAt: time.Now(),
Source: event.Source_Node,
Component: event.Component_Sequencer,
Level: event.Level_Warning,
EventID: event.EventID_UnsupportedPrecompile,
Description: string(payload),
Json: batchRequest,
}
err = f.eventLog.LogEvent(ctx, event)
if err != nil {
log.Errorf("error storing payload, error: %v", err)
}
}
}

// Delete tx from the worker
f.workerIntf.DeleteTx(tx.Hash, tx.From)

Expand All @@ -454,14 +483,15 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first
} else {
metrics.TxProcessed(metrics.TxProcessedLabelInvalid, 1)
}
return nil, err
return nil, false, err
}

closeBatch := false
oldStateRoot := f.wipBatch.imStateRoot
if len(batchResponse.BlockResponses) > 0 {
errWg, err = f.handleProcessTransactionResponse(ctx, tx, batchResponse, oldStateRoot)
errWg, closeBatch, err = f.handleProcessTransactionResponse(ctx, tx, batchResponse, oldStateRoot)
if err != nil {
return errWg, err
return errWg, false, err
}
}

Expand All @@ -471,17 +501,17 @@ func (f *finalizer) processTransaction(ctx context.Context, tx *TxTracker, first
log.Infof("processed tx %s, batchNumber: %d, l2Block: [%d], newStateRoot: %s, oldStateRoot: %s, used counters: %s",
tx.HashStr, batchRequest.BatchNumber, f.wipL2Block.trackingNum, batchResponse.NewStateRoot.String(), batchRequest.OldStateRoot.String(), f.logZKCounters(batchResponse.UsedZkCounters))

return nil, nil
return nil, closeBatch, nil
}

// handleProcessTransactionResponse handles the response of transaction processing.
func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *TxTracker, result *state.ProcessBatchResponse, oldStateRoot common.Hash) (errWg *sync.WaitGroup, err error) {
func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *TxTracker, result *state.ProcessBatchResponse, oldStateRoot common.Hash) (errWg *sync.WaitGroup, closeWIPBatch bool, err error) {
// Handle Transaction Error
errorCode := executor.RomErrorCode(result.BlockResponses[0].TransactionResponses[0].RomError)
if !state.IsStateRootChanged(errorCode) {
// If intrinsic error or OOC error, we skip adding the transaction to the batch
errWg = f.handleProcessTransactionError(ctx, result, tx)
return errWg, result.BlockResponses[0].TransactionResponses[0].RomError
return errWg, false, result.BlockResponses[0].TransactionResponses[0].RomError
}

egpEnabled := f.effectiveGasPrice.IsEnabled()
Expand All @@ -496,7 +526,7 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx
if err != nil {
if egpEnabled {
log.Errorf("failed to calculate effective gas price with new gasUsed for tx %s, error: %v", tx.HashStr, err.Error())
return nil, err
return nil, false, err
} else {
log.Warnf("effectiveGasPrice is disabled, but failed to calculate effective gas price with new gasUsed for tx %s, error: %v", tx.HashStr, err.Error())
tx.EGPLog.Error = fmt.Sprintf("%s; CalculateEffectiveGasPrice#2: %s", tx.EGPLog.Error, err)
Expand All @@ -521,7 +551,7 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx
}

if errCompare != nil && egpEnabled {
return nil, errCompare
return nil, false, errCompare
}
}
}
Expand Down Expand Up @@ -557,12 +587,12 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx
log.Errorf("failed to update status to invalid in the pool for tx %s, error: %v", tx.Hash.String(), err)
}

return nil, ErrBatchResourceUnderFlow
return nil, false, ErrBatchResourceUnderFlow
} else {
start := time.Now()
f.workerIntf.UpdateTxZKCounters(result.BlockResponses[0].TransactionResponses[0].TxHash, tx.From, result.UsedZkCounters)
metrics.WorkerProcessingTime(time.Since(start))
return nil, ErrBatchResourceUnderFlow
return nil, false, ErrBatchResourceUnderFlow
}
}

Expand All @@ -583,7 +613,11 @@ func (f *finalizer) handleProcessTransactionResponse(ctx context.Context, tx *Tx

f.updateWorkerAfterSuccessfulProcessing(ctx, tx.Hash, tx.From, false, result)

return nil, nil
if result.CloseBatch_V2 {
return nil, true, nil
}

return nil, false, nil
}

// compareTxEffectiveGasPrice compares newEffectiveGasPrice with tx.EffectiveGasPrice.
Expand Down
3 changes: 2 additions & 1 deletion state/convertersV2.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (s *State) convertToProcessBatchResponseV2(batchResponse *executor.ProcessB
FlushID: batchResponse.FlushId,
StoredFlushID: batchResponse.StoredFlushId,
ProverID: batchResponse.ProverId,
IsExecutorLevelError: (batchResponse.Error != executor.ExecutorError_EXECUTOR_ERROR_NO_ERROR),
IsExecutorLevelError: (batchResponse.Error != executor.ExecutorError_EXECUTOR_ERROR_NO_ERROR && batchResponse.Error != executor.ExecutorError_EXECUTOR_ERROR_CLOSE_BATCH),
IsRomLevelError: isRomLevelError,
IsRomOOCError: isRomOOCError,
GasUsed_V2: batchResponse.GasUsed,
Expand All @@ -65,6 +65,7 @@ func (s *State) convertToProcessBatchResponseV2(batchResponse *executor.ProcessB
ForkID: batchResponse.ForkId,
InvalidBatch_V2: batchResponse.InvalidBatch != 0,
RomError_V2: executor.RomErr(batchResponse.ErrorRom),
CloseBatch_V2: batchResponse.Error == executor.ExecutorError_EXECUTOR_ERROR_CLOSE_BATCH,
}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion state/pgstatestorage/datastream.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (p *PostgresStorage) GetDSGenesisBlock(ctx context.Context, dbTx pgx.Tx) (*

// GetDSL2Blocks returns the L2 blocks
func (p *PostgresStorage) GetDSL2Blocks(ctx context.Context, firstBatchNumber, lastBatchNumber uint64, dbTx pgx.Tx) ([]*state.DSL2Block, error) {
const l2BlockSQL = `SELECT l2b.batch_num, l2b.block_num, l2b.received_at, b.global_exit_root, l2b.header->>'globalExitRoot' AS block_global_exit_root, l2b.header->>'miner' AS coinbase, f.fork_id, l2b.block_hash, l2b.state_root
const l2BlockSQL = `SELECT l2b.batch_num, l2b.block_num, l2b.received_at, b.global_exit_root, COALESCE(l2b.header->>'globalExitRoot', '') AS block_global_exit_root, l2b.header->>'miner' AS coinbase, f.fork_id, l2b.block_hash, l2b.state_root
FROM state.l2block l2b, state.batch b, state.fork_id f
WHERE l2b.batch_num BETWEEN $1 AND $2 AND l2b.batch_num = b.batch_num AND l2b.batch_num between f.from_batch_num AND f.to_batch_num
ORDER BY l2b.block_num ASC`
Expand Down
28 changes: 28 additions & 0 deletions state/runtime/executor/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,20 @@ func ExecutorErr(errorCode ExecutorError) error {
return runtime.ErrExecutorErrorInvalidL1InfoTreeIndex
case ExecutorError_EXECUTOR_ERROR_INVALID_L1_INFO_TREE_SMT_PROOF_VALUE:
return runtime.ErrExecutorErrorInvalidL1InfoTreeSmtProofValue
case ExecutorError_EXECUTOR_ERROR_INVALID_WITNESS:
return runtime.ErrExecutorErrorInvalidWitness
case ExecutorError_EXECUTOR_ERROR_INVALID_CBOR:
return runtime.ErrExecutorErrorInvalidCBOR
case ExecutorError_EXECUTOR_ERROR_INVALID_DATA_STREAM:
return runtime.ErrExecutorErrorInvalidDataStream
case ExecutorError_EXECUTOR_ERROR_INVALID_UPDATE_MERKLE_TREE:
return runtime.ErrExecutorErrorInvalidUpdateMerkleTree
case ExecutorError_EXECUTOR_ERROR_UNSUPPORTED_PRECOMPILED:
return runtime.ErrExecutorErrorUnsupportedPrecompile
case ExecutorError_EXECUTOR_ERROR_OOG_2:
return runtime.ErrExecutorErrorOOG2
case ExecutorError_EXECUTOR_ERROR_CLOSE_BATCH:
return runtime.ErrExecutorErrorCloseBatch
}
return ErrExecutorUnknown
}
Expand Down Expand Up @@ -678,6 +692,20 @@ func ExecutorErrorCode(err error) ExecutorError {
return ExecutorError_EXECUTOR_ERROR_INVALID_L1_INFO_TREE_INDEX
case runtime.ErrExecutorErrorInvalidL1InfoTreeSmtProofValue:
return ExecutorError_EXECUTOR_ERROR_INVALID_L1_INFO_TREE_SMT_PROOF_VALUE
case runtime.ErrExecutorErrorInvalidWitness:
return ExecutorError_EXECUTOR_ERROR_INVALID_WITNESS
case runtime.ErrExecutorErrorInvalidCBOR:
return ExecutorError_EXECUTOR_ERROR_INVALID_CBOR
case runtime.ErrExecutorErrorInvalidDataStream:
return ExecutorError_EXECUTOR_ERROR_INVALID_DATA_STREAM
case runtime.ErrExecutorErrorInvalidUpdateMerkleTree:
return ExecutorError_EXECUTOR_ERROR_INVALID_UPDATE_MERKLE_TREE
case runtime.ErrExecutorErrorUnsupportedPrecompile:
return ExecutorError_EXECUTOR_ERROR_UNSUPPORTED_PRECOMPILED
case runtime.ErrExecutorErrorOOG2:
return ExecutorError_EXECUTOR_ERROR_OOG_2
case runtime.ErrExecutorErrorCloseBatch:
return ExecutorError_EXECUTOR_ERROR_CLOSE_BATCH
}

return ErrCodeExecutorUnknown
Expand Down
Loading
Loading