From 9d3551f8cf3f2835ba8212473024a609a53e24f7 Mon Sep 17 00:00:00 2001 From: Giulio rebuffo Date: Sun, 23 Jul 2023 19:10:24 +0200 Subject: [PATCH] prototype of ValidataChain (#7921) --- cmd/erigon-el-mock/server.go | 12 ++-- go.mod | 2 +- go.sum | 4 +- turbo/execution/eth1/eth1_test.go | 9 ++- turbo/execution/eth1/ethereum_execution.go | 70 ++++++++++++++++++++-- turbo/execution/eth1/inserters.go | 38 +++++++++--- 6 files changed, 113 insertions(+), 22 deletions(-) diff --git a/cmd/erigon-el-mock/server.go b/cmd/erigon-el-mock/server.go index 38e02df2d..7dc429e07 100644 --- a/cmd/erigon-el-mock/server.go +++ b/cmd/erigon-el-mock/server.go @@ -36,7 +36,7 @@ func NewEth1Execution(db kv.RwDB, blockReader services.FullBlockReader) *Eth1Exe } } -func (e *Eth1Execution) InsertHeaders(ctx context.Context, req *execution.InsertHeadersRequest) (*execution.EmptyMessage, error) { +func (e *Eth1Execution) InsertHeaders(ctx context.Context, req *execution.InsertHeadersRequest) (*execution.InsertionResult, error) { e.mu.Lock() defer e.mu.Unlock() tx, err := e.db.BeginRw(ctx) @@ -54,10 +54,12 @@ func (e *Eth1Execution) InsertHeaders(ctx context.Context, req *execution.Insert return nil, err } } - return &execution.EmptyMessage{}, tx.Commit() + return &execution.InsertionResult{ + Result: execution.ValidationStatus_Success, + }, tx.Commit() } -func (e *Eth1Execution) InsertBodies(ctx context.Context, req *execution.InsertBodiesRequest) (*execution.EmptyMessage, error) { +func (e *Eth1Execution) InsertBodies(ctx context.Context, req *execution.InsertBodiesRequest) (*execution.InsertionResult, error) { e.mu.Lock() defer e.mu.Unlock() tx, err := e.db.BeginRw(ctx) @@ -94,7 +96,9 @@ func (e *Eth1Execution) InsertBodies(ctx context.Context, req *execution.InsertB return nil, err } } - return &execution.EmptyMessage{}, tx.Commit() + return &execution.InsertionResult{ + Result: execution.ValidationStatus_Success, + }, tx.Commit() } type canonicalEntry struct { diff --git a/go.mod b/go.mod index 4731e9e8b..45ad66748 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/ledgerwatch/erigon go 1.19 require ( - github.com/ledgerwatch/erigon-lib v0.0.0-20230714001632-a0450f4fddd1 + github.com/ledgerwatch/erigon-lib v0.0.0-20230723163836-9e08d02f75b8 github.com/ledgerwatch/erigon-snapshot v1.2.1-0.20230622075030-1d69651854c2 github.com/ledgerwatch/log/v3 v3.8.0 github.com/ledgerwatch/secp256k1 v1.0.0 diff --git a/go.sum b/go.sum index fb9f463d8..1c238541f 100644 --- a/go.sum +++ b/go.sum @@ -415,8 +415,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758 h1:0D5M2HQSGD3PYPwICLl+/9oulQauOuETfgFvhBDffs0= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/ledgerwatch/erigon-lib v0.0.0-20230714001632-a0450f4fddd1 h1:dSsZV1wEVixmc+aXO6fv2XyZ05JOrj4gGIuxoTPIjYM= -github.com/ledgerwatch/erigon-lib v0.0.0-20230714001632-a0450f4fddd1/go.mod h1:S/p/qNKN8yFyBy0WjBVjATpm1uwjPmhdcrDmseWHBVQ= +github.com/ledgerwatch/erigon-lib v0.0.0-20230723163836-9e08d02f75b8 h1:P8CdtNh0DDKhzTVnwuCQY1lg8Jn1N21nmSHGOl4Kjxo= +github.com/ledgerwatch/erigon-lib v0.0.0-20230723163836-9e08d02f75b8/go.mod h1:ZkH1giM9x/HgdvjWzeCiOIx2lLDo98T/uvmRkG6xet0= github.com/ledgerwatch/erigon-snapshot v1.2.1-0.20230622075030-1d69651854c2 h1:Ls2itRGHMOr2PbHRDA4g1HH8HQdwfJhRVfMPEaLQe94= github.com/ledgerwatch/erigon-snapshot v1.2.1-0.20230622075030-1d69651854c2/go.mod h1:3AuPxZc85jkehh/HA9h8gabv5MSi3kb/ddtzBsTVJFo= github.com/ledgerwatch/log/v3 v3.8.0 h1:gCpp7uGtIerEz1jKVPeDnbIopFPud9ZnCpBLlLBGqPU= diff --git a/turbo/execution/eth1/eth1_test.go b/turbo/execution/eth1/eth1_test.go index fd3ee7a3b..14c155520 100644 --- a/turbo/execution/eth1/eth1_test.go +++ b/turbo/execution/eth1/eth1_test.go @@ -9,6 +9,7 @@ import ( "github.com/ledgerwatch/erigon-lib/gointerfaces" "github.com/ledgerwatch/erigon-lib/gointerfaces/execution" "github.com/ledgerwatch/erigon-lib/kv/memdb" + "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/turbo/execution/eth1" "github.com/stretchr/testify/require" @@ -20,7 +21,11 @@ func TestInsertGetterHeader(t *testing.T) { Difficulty: big.NewInt(0), Number: big.NewInt(int64(bn)), } - e := eth1.NewEthereumExecutionModule(nil, memdb.NewTestDB(t), nil, nil) + db := memdb.NewTestDB(t) + tx, _ := db.BeginRw(context.TODO()) + rawdb.WriteTd(tx, libcommon.Hash{}, 1, libcommon.Big0) + tx.Commit() + e := eth1.NewEthereumExecutionModule(nil, db, nil, nil, nil) _, err := e.InsertHeaders(context.TODO(), &execution.InsertHeadersRequest{ Headers: []*execution.Header{ eth1.HeaderToHeaderRPC(header), @@ -41,7 +46,7 @@ func TestInsertGetterBody(t *testing.T) { body := &types.RawBody{ Transactions: txs, } - e := eth1.NewEthereumExecutionModule(nil, memdb.NewTestDB(t), nil, nil) + e := eth1.NewEthereumExecutionModule(nil, memdb.NewTestDB(t), nil, nil, nil) _, err := e.InsertBodies(context.TODO(), &execution.InsertBodiesRequest{ Bodies: []*execution.BlockBody{ eth1.ConvertRawBlockBodyToRpc(body, bn, bhash), diff --git a/turbo/execution/eth1/ethereum_execution.go b/turbo/execution/eth1/ethereum_execution.go index 2aefd7e0d..9e9f1d865 100644 --- a/turbo/execution/eth1/ethereum_execution.go +++ b/turbo/execution/eth1/ethereum_execution.go @@ -3,18 +3,20 @@ package eth1 import ( "context" - "github.com/anacrolix/sync" libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/gointerfaces" "github.com/ledgerwatch/erigon-lib/gointerfaces/execution" types2 "github.com/ledgerwatch/erigon-lib/gointerfaces/types" "github.com/ledgerwatch/log/v3" + "golang.org/x/sync/semaphore" "github.com/ledgerwatch/erigon-lib/kv" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/eth/stagedsync" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" + "github.com/ledgerwatch/erigon/turbo/engineapi/engine_helpers" + "github.com/ledgerwatch/erigon/turbo/engineapi/engine_types" "github.com/ledgerwatch/erigon/turbo/services" ) @@ -25,20 +27,23 @@ type EthereumExecutionModule struct { // MDBX database db kv.RwDB // main database - lock sync.Mutex + semaphore *semaphore.Weighted executionPipeline *stagedsync.Sync + forkValidator *engine_helpers.ForkValidator logger log.Logger execution.UnimplementedExecutionServer } -func NewEthereumExecutionModule(blockReader services.FullBlockReader, db kv.RwDB, executionPipeline *stagedsync.Sync, logger log.Logger) *EthereumExecutionModule { +func NewEthereumExecutionModule(blockReader services.FullBlockReader, db kv.RwDB, executionPipeline *stagedsync.Sync, forkValidator *engine_helpers.ForkValidator, logger log.Logger) *EthereumExecutionModule { return &EthereumExecutionModule{ blockReader: blockReader, db: db, executionPipeline: executionPipeline, logger: logger, + forkValidator: forkValidator, + semaphore: semaphore.NewWeighted(1), } } @@ -71,8 +76,14 @@ func (e *EthereumExecutionModule) UpdateForkChoice(ctx context.Context, req *typ hash libcommon.Hash number uint64 } - e.lock.Lock() - defer e.lock.Unlock() + if !e.semaphore.TryAcquire(1) { + return &execution.ForkChoiceReceipt{ + LatestValidHash: gointerfaces.ConvertHashToH256(libcommon.Hash{}), + Status: execution.ValidationStatus_Busy, + }, nil + } + defer e.semaphore.Release(1) + tx, err := e.db.BeginRw(ctx) if err != nil { return nil, err @@ -169,4 +180,53 @@ func (e *EthereumExecutionModule) UpdateForkChoice(ctx context.Context, req *typ }, tx.Commit() } +func (e *EthereumExecutionModule) ValidateChain(ctx context.Context, req *execution.ValidationRequest) (*execution.ValidationReceipt, error) { + if !e.semaphore.TryAcquire(1) { + return &execution.ValidationReceipt{ + LatestValidHash: gointerfaces.ConvertHashToH256(libcommon.Hash{}), + ValidationStatus: execution.ValidationStatus_Busy, + }, nil + } + defer e.semaphore.Release(1) + tx, err := e.db.BeginRw(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + blockHash := gointerfaces.ConvertH256ToHash(req.Hash) + header, err := e.blockReader.Header(ctx, tx, blockHash, req.Number) + if err != nil { + return nil, err + } + + body, err := e.blockReader.BodyWithTransactions(ctx, tx, blockHash, req.Number) + if err != nil { + return nil, err + } + if header == nil || body == nil { + return &execution.ValidationReceipt{ + LatestValidHash: gointerfaces.ConvertHashToH256(libcommon.Hash{}), + MissingHash: req.Hash, + ValidationStatus: execution.ValidationStatus_MissingSegment, + }, nil + } + status, lvh, validationError, criticalError := e.forkValidator.ValidatePayload(tx, header, body.RawBody(), false) + if criticalError != nil { + return nil, criticalError + } + validationStatus := execution.ValidationStatus_Success + if status == engine_types.AcceptedStatus { + validationStatus = execution.ValidationStatus_MissingSegment + } + if status == engine_types.InvalidStatus || status == engine_types.InvalidBlockHashStatus || validationError != nil { + e.logger.Warn("ethereumExecutionModule.ValidateChain: chain %x is invalid. reason %s", blockHash, err) + validationStatus = execution.ValidationStatus_BadBlock + } + return &execution.ValidationReceipt{ + ValidationStatus: validationStatus, + LatestValidHash: gointerfaces.ConvertHashToH256(lvh), + MissingHash: gointerfaces.ConvertHashToH256(libcommon.Hash{}), // TODO: implement + }, nil +} + // Missing: NewPayload, AssembleBlock diff --git a/turbo/execution/eth1/inserters.go b/turbo/execution/eth1/inserters.go index 9652fa0f3..ba89959c8 100644 --- a/turbo/execution/eth1/inserters.go +++ b/turbo/execution/eth1/inserters.go @@ -9,9 +9,13 @@ import ( "github.com/ledgerwatch/erigon/core/rawdb" ) -func (e *EthereumExecutionModule) InsertBodies(ctx context.Context, req *execution.InsertBodiesRequest) (*execution.EmptyMessage, error) { - e.lock.Lock() - defer e.lock.Unlock() +func (e *EthereumExecutionModule) InsertBodies(ctx context.Context, req *execution.InsertBodiesRequest) (*execution.InsertionResult, error) { + if !e.semaphore.TryAcquire(1) { + return &execution.InsertionResult{ + Result: execution.ValidationStatus_Busy, + }, nil + } + defer e.semaphore.Release(1) tx, err := e.db.BeginRw(ctx) if err != nil { return nil, fmt.Errorf("ethereumExecutionModule.InsertBodies: could not begin transaction: %s", err) @@ -26,12 +30,18 @@ func (e *EthereumExecutionModule) InsertBodies(ctx context.Context, req *executi return nil, fmt.Errorf("ethereumExecutionModule.InsertBodies: could not commit: %s", err) } - return &execution.EmptyMessage{}, tx.Commit() + return &execution.InsertionResult{ + Result: execution.ValidationStatus_Success, + }, tx.Commit() } -func (e *EthereumExecutionModule) InsertHeaders(ctx context.Context, req *execution.InsertHeadersRequest) (*execution.EmptyMessage, error) { - e.lock.Lock() - defer e.lock.Unlock() +func (e *EthereumExecutionModule) InsertHeaders(ctx context.Context, req *execution.InsertHeadersRequest) (*execution.InsertionResult, error) { + if !e.semaphore.TryAcquire(1) { + return &execution.InsertionResult{ + Result: execution.ValidationStatus_Busy, + }, nil + } + defer e.semaphore.Release(1) tx, err := e.db.BeginRw(ctx) if err != nil { return nil, fmt.Errorf("ethereumExecutionModule.InsertHeaders: could not begin transaction: %s", err) @@ -42,13 +52,25 @@ func (e *EthereumExecutionModule) InsertHeaders(ctx context.Context, req *execut if err != nil { return nil, fmt.Errorf("ethereumExecutionModule.InsertHeaders: cannot convert headers: %s", err) } + // Parent's total difficulty + parentTd, err := rawdb.ReadTd(tx, header.ParentHash, header.Number.Uint64()-1) + if err != nil || parentTd == nil { + return nil, fmt.Errorf("parent's total difficulty not found with hash %x and height %d: %v", header.ParentHash, header.Number.Uint64()-1, err) + } + // Sum TDs. + td := parentTd.Add(parentTd, header.Difficulty) if err := rawdb.WriteHeader(tx, header); err != nil { return nil, fmt.Errorf("ethereumExecutionModule.InsertHeaders: could not insert: %s", err) } + if err := rawdb.WriteTd(tx, header.Hash(), header.Number.Uint64(), td); err != nil { + return nil, fmt.Errorf("ethereumExecutionModule.InsertHeaders: could not insert: %s", err) + } } if err := tx.Commit(); err != nil { return nil, fmt.Errorf("ethereumExecutionModule.InsertHeaders: could not commit: %s", err) } - return &execution.EmptyMessage{}, tx.Commit() + return &execution.InsertionResult{ + Result: execution.ValidationStatus_Success, + }, tx.Commit() }