From 202ed765259a4f6434c8da949b2518930fb90356 Mon Sep 17 00:00:00 2001 From: facuMH Date: Mon, 16 Dec 2024 14:24:31 +0100 Subject: [PATCH] add usage for caution utils and catch errors --- cmd/sonictool/app/genesis.go | 27 +++++++++++---------- cmd/sonictool/app/heal.go | 9 +++---- cmd/sonictool/app/sign_genesis.go | 8 ++++--- cmd/sonictool/chain/export_events.go | 16 +++++++------ cmd/sonictool/chain/import_events.go | 21 ++++++++-------- cmd/sonictool/check/archive.go | 11 +++++---- cmd/sonictool/check/live.go | 11 +++++---- cmd/sonictool/db/heal.go | 36 +++++++++++++++++----------- cmd/sonictool/genesis/import.go | 31 ++++++++++++++---------- cmd/sonictool/genesis/signature.go | 35 +++++++++++++++++---------- config/config.go | 11 +++++---- evmcore/tx_journal.go | 17 +++++++++---- gossip/evmstore/statedb.go | 10 ++++---- gossip/evmstore/statedb_import.go | 36 +++++++++++++++------------- gossip/evmstore/statedb_verify.go | 11 +++++---- utils/errlock/errlock.go | 23 ++++++++++++------ 16 files changed, 184 insertions(+), 129 deletions(-) diff --git a/cmd/sonictool/app/genesis.go b/cmd/sonictool/app/genesis.go index a81076c2a..570dfccfe 100644 --- a/cmd/sonictool/app/genesis.go +++ b/cmd/sonictool/app/genesis.go @@ -11,6 +11,7 @@ import ( "github.com/Fantom-foundation/go-opera/integration/makefakegenesis" "github.com/Fantom-foundation/go-opera/opera/genesisstore" futils "github.com/Fantom-foundation/go-opera/utils" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/go-opera/utils/memory" "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/Fantom-foundation/lachesis-base/utils/cachescale" @@ -31,7 +32,7 @@ var ( } ) -func gfileGenesisImport(ctx *cli.Context) error { +func gfileGenesisImport(ctx *cli.Context) (err error) { if len(ctx.Args()) < 1 { return fmt.Errorf("this command requires an argument - the genesis file to import") } @@ -41,24 +42,24 @@ func gfileGenesisImport(ctx *cli.Context) error { } validatorMode, err := isValidatorModeSet(ctx) if err != nil { - return err + return } cacheRatio, err := cacheScaler(ctx) if err != nil { - return err + return } genesisReader, err := os.Open(ctx.Args().First()) if err != nil { return fmt.Errorf("failed to open the genesis file: %w", err) } - defer genesisReader.Close() + // note, genesisStore closes the reader, no need to defer close it here genesisStore, genesisHashes, err := genesisstore.OpenGenesisStore(genesisReader) if err != nil { return fmt.Errorf("failed to read genesis file: %w", err) } - defer genesisStore.Close() + defer caution.CloseAndReportError(&err, genesisStore, "failed to close the genesis store") if err := genesis.IsGenesisTrusted(genesisStore, genesisHashes); err != nil { if ctx.IsSet(ExperimentalFlag.Name) { log.Warn("Experimental genesis file is used", "err", err) @@ -76,7 +77,7 @@ func gfileGenesisImport(ctx *cli.Context) error { }) } -func jsonGenesisImport(ctx *cli.Context) error { +func jsonGenesisImport(ctx *cli.Context) (err error) { if len(ctx.Args()) < 1 { return fmt.Errorf("this command requires an argument - the genesis file to import") } @@ -89,11 +90,11 @@ func jsonGenesisImport(ctx *cli.Context) error { } validatorMode, err := isValidatorModeSet(ctx) if err != nil { - return err + return } cacheRatio, err := cacheScaler(ctx) if err != nil { - return err + return } genesisJson, err := makefakegenesis.LoadGenesisJson(ctx.Args().First()) @@ -105,7 +106,7 @@ func jsonGenesisImport(ctx *cli.Context) error { if err != nil { return fmt.Errorf("failed to prepare JSON genesis: %w", err) } - defer genesisStore.Close() + defer caution.CloseAndReportError(&err, genesisStore, "failed to close the genesis store") return genesis.ImportGenesisStore(genesis.ImportParams{ GenesisStore: genesisStore, DataDir: dataDir, @@ -116,7 +117,7 @@ func jsonGenesisImport(ctx *cli.Context) error { }) } -func fakeGenesisImport(ctx *cli.Context) error { +func fakeGenesisImport(ctx *cli.Context) (err error) { if len(ctx.Args()) < 1 { return fmt.Errorf("this command requires an argument - the number of validators in the fake network") } @@ -133,11 +134,11 @@ func fakeGenesisImport(ctx *cli.Context) error { } validatorMode, err := isValidatorModeSet(ctx) if err != nil { - return err + return } cacheRatio, err := cacheScaler(ctx) if err != nil { - return err + return } genesisStore := makefakegenesis.FakeGenesisStore( @@ -145,7 +146,7 @@ func fakeGenesisImport(ctx *cli.Context) error { futils.ToFtm(1000000000), futils.ToFtm(5000000), ) - defer genesisStore.Close() + defer caution.CloseAndReportError(&err, genesisStore, "failed to close the genesis store") return genesis.ImportGenesisStore(genesis.ImportParams{ GenesisStore: genesisStore, DataDir: dataDir, diff --git a/cmd/sonictool/app/heal.go b/cmd/sonictool/app/heal.go index 5702bf3f1..1a4a8faf4 100644 --- a/cmd/sonictool/app/heal.go +++ b/cmd/sonictool/app/heal.go @@ -17,6 +17,7 @@ import ( "github.com/Fantom-foundation/go-opera/cmd/sonictool/db" "github.com/Fantom-foundation/go-opera/config" "github.com/Fantom-foundation/go-opera/config/flags" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/log" "gopkg.in/urfave/cli.v1" @@ -93,7 +94,7 @@ func heal(ctx *cli.Context) error { return nil } -func healLiveFromArchive(ctx context.Context, carmenLiveDir, carmenArchiveDir string, recoveredBlock idx.Block) error { +func healLiveFromArchive(ctx context.Context, carmenLiveDir, carmenArchiveDir string, recoveredBlock idx.Block) (err error) { if err := os.RemoveAll(carmenLiveDir); err != nil { return fmt.Errorf("failed to remove broken live state: %w", err) } @@ -102,7 +103,7 @@ func healLiveFromArchive(ctx context.Context, carmenLiveDir, carmenArchiveDir st } reader, writer := io.Pipe() - defer reader.Close() + defer caution.CloseAndReportError(&err, reader, "failed to close reader") bufReader := bufio.NewReaderSize(reader, 100*1024*1024) // 100 MiB bufWriter := bufio.NewWriterSize(writer, 100*1024*1024) // 100 MiB @@ -111,14 +112,14 @@ func healLiveFromArchive(ctx context.Context, carmenLiveDir, carmenArchiveDir st wg.Add(1) go func() { defer wg.Done() - defer writer.Close() + defer caution.CloseAndReportError(&err, writer, "failed to close writer") exportErr = mptio.ExportBlockFromArchive(ctx, mptio.NewLog(), carmenArchiveDir, bufWriter, uint64(recoveredBlock)) if exportErr == nil { exportErr = bufWriter.Flush() } }() - err := mptio.ImportLiveDb(mptio.NewLog(), carmenLiveDir, bufReader) + err = mptio.ImportLiveDb(mptio.NewLog(), carmenLiveDir, bufReader) wg.Wait() return errors.Join(err, exportErr) diff --git a/cmd/sonictool/app/sign_genesis.go b/cmd/sonictool/app/sign_genesis.go index d8fe162c7..a7ed1dfe4 100644 --- a/cmd/sonictool/app/sign_genesis.go +++ b/cmd/sonictool/app/sign_genesis.go @@ -2,13 +2,15 @@ package app import ( "fmt" + "os" + "github.com/Fantom-foundation/go-opera/cmd/sonictool/genesis" ogenesis "github.com/Fantom-foundation/go-opera/opera/genesis" "github.com/Fantom-foundation/go-opera/opera/genesisstore" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" "gopkg.in/urfave/cli.v1" - "os" ) func signGenesis(ctx *cli.Context) error { @@ -59,16 +61,16 @@ func signGenesis(ctx *cli.Context) error { func getGenesisHeaderHashes(genesisFile string) (ogenesis.Header, ogenesis.Hashes, error) { genesisReader, err := os.Open(genesisFile) + // note, genesisStore closes the reader, no need to defer close it here if err != nil { return ogenesis.Header{}, nil, fmt.Errorf("failed to open the genesis file: %w", err) } - defer genesisReader.Close() genesisStore, genesisHashes, err := genesisstore.OpenGenesisStore(genesisReader) if err != nil { return ogenesis.Header{}, nil, fmt.Errorf("failed to read genesis file: %w", err) } - defer genesisStore.Close() + defer caution.CloseAndReportError(&err, genesisStore, "failed to close the genesis store") return genesisStore.Header(), genesisHashes, nil } diff --git a/cmd/sonictool/chain/export_events.go b/cmd/sonictool/chain/export_events.go index 492e404c7..24086148f 100644 --- a/cmd/sonictool/chain/export_events.go +++ b/cmd/sonictool/chain/export_events.go @@ -1,12 +1,14 @@ package chain import ( - "github.com/Fantom-foundation/go-opera/cmd/sonictool/db" - "github.com/Fantom-foundation/lachesis-base/utils/cachescale" "io" "path/filepath" "time" + "github.com/Fantom-foundation/go-opera/cmd/sonictool/db" + "github.com/Fantom-foundation/go-opera/utils/caution" + "github.com/Fantom-foundation/lachesis-base/utils/cachescale" + "github.com/Fantom-foundation/lachesis-base/hash" "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/common" @@ -28,9 +30,9 @@ func ExportEvents(gdbParams db.GossipDbParameters, w io.Writer, from, to idx.Epo chaindataDir := filepath.Join(gdbParams.DataDir, "chaindata") dbs, err := db.MakeDbProducer(chaindataDir, cachescale.Identity) if err != nil { - return err + return } - defer dbs.Close() + defer caution.CloseAndReportError(&err, dbs, "failed to close db producer") // Fill the rest of the params gdbParams.Dbs = dbs @@ -38,14 +40,14 @@ func ExportEvents(gdbParams db.GossipDbParameters, w io.Writer, from, to idx.Epo gdb, err := db.MakeGossipDb(gdbParams) if err != nil { - return err + return } - defer gdb.Close() + defer caution.CloseAndReportError(&err, gdb, "failed to close gossip db") // Write header and version _, err = w.Write(append(eventsFileHeader, eventsFileVersion...)) if err != nil { - return err + return } start, reported := time.Now(), time.Time{} diff --git a/cmd/sonictool/chain/import_events.go b/cmd/sonictool/chain/import_events.go index f08806630..261d81a05 100644 --- a/cmd/sonictool/chain/import_events.go +++ b/cmd/sonictool/chain/import_events.go @@ -17,6 +17,7 @@ import ( "github.com/Fantom-foundation/go-opera/gossip" "github.com/Fantom-foundation/go-opera/gossip/emitter" "github.com/Fantom-foundation/go-opera/inter" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/go-opera/utils/ioread" "github.com/Fantom-foundation/lachesis-base/hash" "github.com/Fantom-foundation/lachesis-base/inter/idx" @@ -85,7 +86,7 @@ func checkEventsFileHeader(reader io.Reader) error { return nil } -func importEventsFile(srv *gossip.Service, fn string) error { +func importEventsFile(srv *gossip.Service, filename string) (err error) { // Watch for Ctrl-C while the import is running. // If a signal is received, the import will stop. interrupt := make(chan os.Signal, 1) @@ -93,18 +94,18 @@ func importEventsFile(srv *gossip.Service, fn string) error { defer signal.Stop(interrupt) // Open the file handle and potentially unwrap the gzip stream - fh, err := os.Open(fn) + fileHandle, err := os.Open(filename) if err != nil { return err } - defer fh.Close() + defer caution.CloseAndReportError(&err, fileHandle, "failed to close file") - var reader io.Reader = fh - if strings.HasSuffix(fn, ".gz") { + var reader io.Reader = fileHandle + if strings.HasSuffix(filename, ".gz") { if reader, err = gzip.NewReader(reader); err != nil { return err } - defer reader.(*gzip.Reader).Close() + defer caution.CloseAndReportError(&err, reader.(*gzip.Reader), "failed to close gzip reader") } // Check file version and header @@ -153,17 +154,17 @@ func importEventsFile(srv *gossip.Service, fn string) error { if err == io.EOF { err = processBatch() if err != nil { - return err + return } break } if err != nil { - return err + return } if e.Epoch() != epoch || batchSize >= maxBatchSize { err = processBatch() if err != nil { - return err + return } } epoch = e.Epoch() @@ -173,7 +174,7 @@ func importEventsFile(srv *gossip.Service, fn string) error { events++ } srv.WaitBlockEnd() - log.Info("Events import is finished", "file", fn, "last", last.String(), "imported", events, "txs", txs, "elapsed", common.PrettyDuration(time.Since(start))) + log.Info("Events import is finished", "file", filename, "last", last.String(), "imported", events, "txs", txs, "elapsed", common.PrettyDuration(time.Since(start))) return nil } diff --git a/cmd/sonictool/check/archive.go b/cmd/sonictool/check/archive.go index 8ea8e82e4..cd62090ad 100644 --- a/cmd/sonictool/check/archive.go +++ b/cmd/sonictool/check/archive.go @@ -8,6 +8,7 @@ import ( "github.com/Fantom-foundation/Carmen/go/database/mpt" "github.com/Fantom-foundation/Carmen/go/database/mpt/io" carmen "github.com/Fantom-foundation/Carmen/go/state" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/lachesis-base/hash" "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/Fantom-foundation/lachesis-base/utils/cachescale" @@ -33,13 +34,13 @@ func CheckArchiveStateDb(ctx context.Context, dataDir string, cacheRatio cachesc return nil } -func checkArchiveBlockRoots(dataDir string, cacheRatio cachescale.Func) error { +func checkArchiveBlockRoots(dataDir string, cacheRatio cachescale.Func) (err error) { gdb, dbs, err := createGdb(dataDir, cacheRatio, carmen.S5Archive, false) if err != nil { - return err + return fmt.Errorf("failed to create gdb and db producer: %w", err) } - defer gdb.Close() - defer dbs.Close() + defer caution.CloseAndReportError(&err, gdb, "failed to close gossip db") + defer caution.CloseAndReportError(&err, dbs, "failed to close db producer") invalidBlocks := 0 lastBlockIdx := gdb.GetLatestBlockIndex() @@ -61,5 +62,5 @@ func checkArchiveBlockRoots(dataDir string, cacheRatio cachescale.Func) error { return fmt.Errorf("block root verification failed for %d blocks (from %d total blocks)", invalidBlocks, lastBlockIdx) } log.Info("Block root verification OK for all blocks", "blocks", lastBlockIdx) - return nil + return } diff --git a/cmd/sonictool/check/live.go b/cmd/sonictool/check/live.go index f3d204ee8..f86cfc648 100644 --- a/cmd/sonictool/check/live.go +++ b/cmd/sonictool/check/live.go @@ -8,6 +8,7 @@ import ( "github.com/Fantom-foundation/Carmen/go/database/mpt" "github.com/Fantom-foundation/Carmen/go/database/mpt/io" carmen "github.com/Fantom-foundation/Carmen/go/state" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/lachesis-base/hash" "github.com/Fantom-foundation/lachesis-base/utils/cachescale" "github.com/ethereum/go-ethereum/log" @@ -32,13 +33,13 @@ func CheckLiveStateDb(ctx context.Context, dataDir string, cacheRatio cachescale return nil } -func checkLiveBlockRoot(dataDir string, cacheRatio cachescale.Func) error { +func checkLiveBlockRoot(dataDir string, cacheRatio cachescale.Func) (err error) { gdb, dbs, err := createGdb(dataDir, cacheRatio, carmen.NoArchive, true) if err != nil { - return err + return fmt.Errorf("failed to create gdb and db producer: %w", err) } - defer gdb.Close() - defer dbs.Close() + defer caution.CloseAndReportError(&err, gdb, "failed to close gossip db") + defer caution.CloseAndReportError(&err, dbs, "failed to close db producer") lastBlockIdx := gdb.GetLatestBlockIndex() lastBlock := gdb.GetBlock(lastBlockIdx) @@ -50,5 +51,5 @@ func checkLiveBlockRoot(dataDir string, cacheRatio cachescale.Func) error { return fmt.Errorf("checking live state failed: %w", err) } log.Info("Live block root verification OK", "block", lastBlockIdx) - return nil + return } diff --git a/cmd/sonictool/db/heal.go b/cmd/sonictool/db/heal.go index 42455fa51..2a3b8fbf1 100644 --- a/cmd/sonictool/db/heal.go +++ b/cmd/sonictool/db/heal.go @@ -2,10 +2,14 @@ package db import ( "fmt" + "strings" + "time" + "github.com/Fantom-foundation/go-opera/config" "github.com/Fantom-foundation/go-opera/gossip" "github.com/Fantom-foundation/go-opera/integration" "github.com/Fantom-foundation/go-opera/inter/iblockproc" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/lachesis-base/abft" "github.com/Fantom-foundation/lachesis-base/common/bigendian" "github.com/Fantom-foundation/lachesis-base/hash" @@ -16,32 +20,33 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/syndtr/goleveldb/leveldb/opt" - "strings" - "time" ) -func HealChaindata(chaindataDir string, cacheRatio cachescale.Func, cfg *config.Config, lastCarmenBlock idx.Block) (idx.Block, error) { +func HealChaindata(chaindataDir string, cacheRatio cachescale.Func, cfg *config.Config, lastCarmenBlock idx.Block) (lastBlockId idx.Block, err error) { producer := &DummyScopedProducer{integration.GetRawDbProducer(chaindataDir, integration.DBCacheConfig{ Cache: cacheRatio.U64(480 * opt.MiB), Fdlimit: makeDatabaseHandles(), })} - defer producer.Close() + defer caution.CloseAndReportError(&err, producer, "failed to close db producer") log.Info("Healing gossip db...") - epochState, lastBlock, err := healGossipDb(producer, cfg.OperaStore, lastCarmenBlock) + epochState, lastBlockId, err := healGossipDb(producer, cfg.OperaStore, lastCarmenBlock) if err != nil { - return 0, err + err = fmt.Errorf("failed to heal gossip db: %w", err) + return } log.Info("Removing epoch DBs - will be recreated on next start") if err = dropAllEpochDbs(producer); err != nil { - return 0, err + err = fmt.Errorf("failed to drop epoch DBs: %w", err) + return } log.Info("Recreating consensus database") cMainDb, err := producer.OpenDB("lachesis") if err != nil { - return 0, fmt.Errorf("failed to open 'lachesis' database: %w", err) + err = fmt.Errorf("failed to open 'lachesis' database: %w", err) + return } cGetEpochDB := func(epoch idx.Epoch) kvdb.Store { name := fmt.Sprintf("lachesis-%d", epoch) @@ -56,18 +61,21 @@ func HealChaindata(chaindataDir string, cacheRatio cachescale.Func, cfg *config. Epoch: epochState.Epoch, Validators: epochState.Validators, }); err != nil { - return 0, fmt.Errorf("failed to init consensus database: %w", err) + err = fmt.Errorf("failed to init consensus database: %w", err) + return } if err = cdb.Close(); err != nil { - return 0, fmt.Errorf("failed to close consensus database: %w", err) + err = fmt.Errorf("failed to close consensus database: %w", err) + return } log.Info("Clearing DBs dirty flags") - if err := clearDirtyFlags(producer); err != nil { - return 0, fmt.Errorf("failed to write clean FlushID: %w", err) + if err = clearDirtyFlags(producer); err != nil { + err = fmt.Errorf("failed to clear dirty flags: %w", err) + return } - return lastBlock, nil + return } // healGossipDb reverts the gossip database into state, into which can be reverted carmen @@ -78,7 +86,7 @@ func healGossipDb(producer kvdb.FlushableDBProducer, cfg gossip.StoreConfig, las if err != nil { return nil, 0, err } - defer gdb.Close() + defer caution.CloseAndReportError(&err, gdb, "failed to close gossip db") // find the last closed epoch with the state available epochIdx, blockState, epochState := getLastEpochWithState(gdb, lastCarmenBlock) diff --git a/cmd/sonictool/genesis/import.go b/cmd/sonictool/genesis/import.go index 88886cf93..b0c73f891 100644 --- a/cmd/sonictool/genesis/import.go +++ b/cmd/sonictool/genesis/import.go @@ -2,15 +2,17 @@ package genesis import ( "fmt" + "path/filepath" + "github.com/Fantom-foundation/go-opera/cmd/sonictool/db" "github.com/Fantom-foundation/go-opera/opera/genesis" "github.com/Fantom-foundation/go-opera/opera/genesisstore" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/lachesis-base/abft" "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/Fantom-foundation/lachesis-base/kvdb" "github.com/Fantom-foundation/lachesis-base/utils/cachescale" "github.com/ethereum/go-ethereum/log" - "path/filepath" ) // ImportParams are parameters for ImportGenesisStore func. @@ -22,7 +24,7 @@ type ImportParams struct { LiveDbCache, ArchiveCache int64 // in bytes } -func ImportGenesisStore(params ImportParams) error { +func ImportGenesisStore(params ImportParams) (err error) { if err := db.AssertDatabaseNotInitialized(params.DataDir); err != nil { return fmt.Errorf("database in datadir is already initialized: %w", err) } @@ -33,9 +35,10 @@ func ImportGenesisStore(params ImportParams) error { chaindataDir := filepath.Join(params.DataDir, "chaindata") dbs, err := db.MakeDbProducer(chaindataDir, params.CacheRatio) if err != nil { - return err + err = fmt.Errorf("failed to create db producer: %w", err) + return } - defer dbs.Close() + defer caution.CloseAndReportError(&err, dbs, "failed to close db producer") setGenesisProcessing(chaindataDir) gdb, err := db.MakeGossipDb(db.GossipDbParameters{ @@ -47,18 +50,20 @@ func ImportGenesisStore(params ImportParams) error { ArchiveCache: params.ArchiveCache, }) if err != nil { - return err + err = fmt.Errorf("failed to create gossip db: %w", err) + return } - defer gdb.Close() + defer caution.CloseAndReportError(&err, gdb, "failed to close gossip db") err = gdb.ApplyGenesis(params.GenesisStore.Genesis()) if err != nil { - return fmt.Errorf("failed to write Gossip genesis state: %v", err) + return fmt.Errorf("failed to write Gossip genesis state: %w", err) } cMainDb, err := dbs.OpenDB("lachesis") if err != nil { - return err + err = fmt.Errorf("failed to open lachesis db: %w", err) + return } cGetEpochDB := func(epoch idx.Epoch) kvdb.Store { db, err := dbs.OpenDB(fmt.Sprintf("lachesis-%d", epoch)) @@ -71,23 +76,25 @@ func ImportGenesisStore(params ImportParams) error { panic(fmt.Errorf("lachesis store error: %w", err)) } cdb := abft.NewStore(cMainDb, cGetEpochDB, abftCrit, abft.DefaultStoreConfig(params.CacheRatio)) - defer cdb.Close() + defer caution.CloseAndReportError(&err, cdb, "failed to close consensus db") err = cdb.ApplyGenesis(&abft.Genesis{ Epoch: gdb.GetEpoch(), Validators: gdb.GetValidators(), }) if err != nil { - return fmt.Errorf("failed to write lachesis genesis state: %w", err) + err = fmt.Errorf("failed to write lachesis genesis state: %w", err) + return } err = gdb.Commit() if err != nil { - return err + err = fmt.Errorf("failed to commit gossip db: %w", err) + return } setGenesisComplete(chaindataDir) log.Info("Successfully imported genesis file") - return nil + return } func IsGenesisTrusted(genesisStore *genesisstore.Store, genesisHashes genesis.Hashes) error { diff --git a/cmd/sonictool/genesis/signature.go b/cmd/sonictool/genesis/signature.go index 2aa729ea1..bbf32640e 100644 --- a/cmd/sonictool/genesis/signature.go +++ b/cmd/sonictool/genesis/signature.go @@ -2,13 +2,15 @@ package genesis import ( "fmt" + "io" + "os" + "sort" + "github.com/Fantom-foundation/go-opera/opera/genesis" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/signer/core/apitypes" - "io" - "os" - "sort" ) func GetGenesisMetadata(header genesis.Header, genesisHashes genesis.Hashes) ([]byte, string, error) { @@ -80,33 +82,40 @@ func CheckGenesisSignature(hash []byte, signature []byte) error { return fmt.Errorf("genesis signature does not match any trusted signer (signer: %x)", address) } -func WriteSignatureIntoGenesisFile(header genesis.Header, signature []byte, file string) error { +func WriteSignatureIntoGenesisFile(header genesis.Header, signature []byte, file string) (err error) { out, err := os.OpenFile(file, os.O_RDWR, os.ModePerm) // avoid using O_APPEND for correct seek positions if err != nil { - return err + err = fmt.Errorf("failed to open genesis file: %w", err) + return } _, err = out.Seek(0, io.SeekEnd) if err != nil { - return err + err = fmt.Errorf("failed to seek genesis file: %w", err) + return } - defer out.Close() + defer caution.CloseAndReportError(&err, out, "failed to close genesis file") tmpDir, err := os.MkdirTemp("", "signing-genesis-tmp") if err != nil { - return err + err = fmt.Errorf("failed to create temporary directory: %w", err) + return } - defer os.RemoveAll(tmpDir) + defer caution.ExecuteAndReportError(&err, func() error { return os.RemoveAll(tmpDir) }, + "failed to remove temporary directory") writer := newUnitWriter(out) - if err := writer.Start(header, "signature", tmpDir); err != nil { - return err + if err = writer.Start(header, "signature", tmpDir); err != nil { + err = fmt.Errorf("failed to write start to genesis file: %w", err) + return } _, err = writer.Write(signature) if err != nil { - return err + err = fmt.Errorf("failed to write signature to genesis file: %w", err) + return } _, err = writer.Flush() - return err + err = fmt.Errorf("failed to flush genesis file: %w", err) + return } // TypedDataAndHash is a helper function that calculates a hash for typed data conforming to EIP-712. diff --git a/config/config.go b/config/config.go index 56ef4cce7..748619e91 100644 --- a/config/config.go +++ b/config/config.go @@ -30,6 +30,7 @@ import ( "github.com/Fantom-foundation/go-opera/gossip" "github.com/Fantom-foundation/go-opera/gossip/emitter" "github.com/Fantom-foundation/go-opera/integration" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/go-opera/utils/memory" "github.com/Fantom-foundation/go-opera/vecmt" ) @@ -81,12 +82,13 @@ func (c *Config) AppConfigs() integration.Configs { } } -func loadAllConfigs(file string, cfg *Config) error { +func loadAllConfigs(file string, cfg *Config) (err error) { f, err := os.Open(file) if err != nil { - return err + err = fmt.Errorf("failed to open config file %s: %w", file, err) + return } - defer f.Close() + defer caution.CloseAndReportError(&err, f, "failed to close config file") err = TomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg) // Add file name to errors that have a line number. @@ -94,12 +96,11 @@ func loadAllConfigs(file string, cfg *Config) error { err = errors.New(file + ", " + err.Error()) } if err != nil { - return fmt.Errorf("TOML config file error: %v.\n"+ "Use 'dumpconfig' command to get an example config file.\n"+ "If node was recently upgraded and a previous network config file is used, then check updates for the config file.", err) } - return err + return } func setBootnodes(ctx *cli.Context, urls []string, cfg *node.Config) { diff --git a/evmcore/tx_journal.go b/evmcore/tx_journal.go index 9e48e7880..2418cacc1 100644 --- a/evmcore/tx_journal.go +++ b/evmcore/tx_journal.go @@ -18,9 +18,11 @@ package evmcore import ( "errors" + "fmt" "io" "os" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -56,7 +58,7 @@ func newTxJournal(path string) *txJournal { // load parses a transaction journal dump from disk, loading its contents into // the specified pool. -func (journal *txJournal) load(add func([]*types.Transaction) []error) error { +func (journal *txJournal) load(add func([]*types.Transaction) []error) (err error) { // Skip the parsing if the journal file doesn't exist at all if _, err := os.Stat(journal.path); os.IsNotExist(err) { return nil @@ -64,9 +66,10 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error { // Open the journal for loading any past transactions input, err := os.Open(journal.path) if err != nil { - return err + err = fmt.Errorf("Failed to open transaction journal: %w", err) + return } - defer input.Close() + defer caution.CloseAndReportError(&err, input, "Failed to close transaction journal") // Temporarily discard any journal additions (don't double add on load) journal.writer = new(devNull) @@ -146,13 +149,17 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro for _, txs := range all { for _, tx := range txs { if err = rlp.Encode(replacement, tx); err != nil { - replacement.Close() + if closeErr := replacement.Close(); closeErr != nil { + return errors.Join(closeErr, fmt.Errorf("failed to close journal file: %w", closeErr)) + } return err } } journaled += len(txs) } - replacement.Close() + if err := replacement.Close(); err != nil { + return fmt.Errorf("failed to close journal file: %w", err) + } // Replace the live journal with the newly generated one if err = os.Rename(journal.path+".new", journal.path); err != nil { diff --git a/gossip/evmstore/statedb.go b/gossip/evmstore/statedb.go index a2218d36e..c01851b2a 100644 --- a/gossip/evmstore/statedb.go +++ b/gossip/evmstore/statedb.go @@ -2,14 +2,16 @@ package evmstore import ( "fmt" + "math/big" + cc "github.com/Fantom-foundation/Carmen/go/common" carmen "github.com/Fantom-foundation/Carmen/go/state" _ "github.com/Fantom-foundation/Carmen/go/state/gostate" "github.com/Fantom-foundation/go-opera/inter/state" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/lachesis-base/hash" "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/common" - "math/big" ) // GetLiveStateDb obtains StateDB for block processing - the live writable state @@ -71,7 +73,7 @@ func (s *Store) CheckLiveStateHash(blockNum idx.Block, root hash.Hash) error { } // CheckArchiveStateHash returns if the hash of the given archive StateDB hash matches -func (s *Store) CheckArchiveStateHash(blockNum idx.Block, root hash.Hash) error { +func (s *Store) CheckArchiveStateHash(blockNum idx.Block, root hash.Hash) (err error) { if s.carmenState == nil { return fmt.Errorf("unable to get live state - EvmStore is not open") } @@ -79,7 +81,7 @@ func (s *Store) CheckArchiveStateHash(blockNum idx.Block, root hash.Hash) error if err != nil { return fmt.Errorf("unable to get archive state: %w", err) } - defer archiveState.Close() + defer caution.CloseAndReportError(&err, archiveState, "failed to close archive state") stateHash, err := archiveState.GetHash() if err != nil { @@ -88,5 +90,5 @@ func (s *Store) CheckArchiveStateHash(blockNum idx.Block, root hash.Hash) error if cc.Hash(root) != stateHash { return fmt.Errorf("hash of the archive EVM state is incorrect: blockNum: %d expected: %x actual: %x", blockNum, root, stateHash) } - return nil + return } diff --git a/gossip/evmstore/statedb_import.go b/gossip/evmstore/statedb_import.go index 1c4b87c0d..3a3e97f57 100644 --- a/gossip/evmstore/statedb_import.go +++ b/gossip/evmstore/statedb_import.go @@ -14,6 +14,7 @@ import ( carmen "github.com/Fantom-foundation/Carmen/go/state" "github.com/Fantom-foundation/go-opera/opera/genesis" "github.com/Fantom-foundation/go-opera/utils/adapters/kvdb2ethdb" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/Fantom-foundation/lachesis-base/kvdb/nokeyiserr" "github.com/Fantom-foundation/lachesis-base/kvdb/pebble" "github.com/Fantom-foundation/lachesis-base/kvdb/table" @@ -99,22 +100,23 @@ func (s *Store) ExportArchiveWorldState(ctx context.Context, out io.Writer) erro return nil } -func (s *Store) ImportLegacyEvmData(evmItems genesis.EvmItems, blockNum uint64, root common.Hash) error { - if err := s.Open(); err != nil { - return fmt.Errorf("failed to open EvmStore for legacy EVM data import; %v", err) +func (s *Store) ImportLegacyEvmData(evmItems genesis.EvmItems, blockNum uint64, root common.Hash) (err error) { + if err = s.Open(); err != nil { + return fmt.Errorf("failed to open EvmStore for legacy EVM data import; %w", err) } - defer s.Close() + defer caution.CloseAndReportError(&err, s, "failed to close EvmStore after legacy EVM data import") carmenDir, err := os.MkdirTemp(s.parameters.Directory, "opera-tmp-import-legacy-genesis") if err != nil { - panic(fmt.Errorf("failed to create temporary dir for legacy EVM data import: %v", err)) + panic(fmt.Errorf("failed to create temporary dir for legacy EVM data import: %w", err)) } - defer os.RemoveAll(carmenDir) + defer caution.ExecuteAndReportError(&err, func() error { return os.RemoveAll(carmenDir) }, + "failed to remove temporary directory for legacy EVM data import") s.Log.Info("Unpacking legacy EVM data into a temporary directory", "dir", carmenDir) db, err := pebble.New(carmenDir, 1024, 100, nil, nil) if err != nil { - panic(fmt.Errorf("failed to open temporary database for legacy EVM data import: %v", err)) + panic(fmt.Errorf("failed to open temporary database for legacy EVM data import: %w", err)) } evmItems.ForEach(func(key, value []byte) bool { err := db.Put(key, value) @@ -142,26 +144,26 @@ func (s *Store) ImportLegacyEvmData(evmItems genesis.EvmItems, blockNum uint64, tdb := triedb.NewDatabase(chaindb, &triedb.Config{Preimages: false, IsVerkle: false}) t, err := trie.NewStateTrie(trie.StateTrieID(root), tdb) if err != nil { - return fmt.Errorf("failed to open trie; %v", err) + return fmt.Errorf("failed to open trie; %w", err) } preimages := table.New(db, []byte("secure-key-")) accIter, err := t.NodeIterator(nil) if err != nil { - return fmt.Errorf("failed to open accounts iterator; %v", err) + return fmt.Errorf("failed to open accounts iterator; %w", err) } for accIter.Next(true) { if accIter.Leaf() { addressBytes, err := preimages.Get(accIter.LeafKey()) if err != nil || addressBytes == nil { - return fmt.Errorf("missing preimage for account address hash %v; %v", accIter.LeafKey(), err) + return fmt.Errorf("missing preimage for account address hash %v; %w", accIter.LeafKey(), err) } address := cc.Address(common.BytesToAddress(addressBytes)) var acc types.StateAccount if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil { - return fmt.Errorf("invalid account encountered during traversal; %v", err) + return fmt.Errorf("invalid account encountered during traversal; %w", err) } bulk.CreateAccount(address) @@ -179,23 +181,23 @@ func (s *Store) ImportLegacyEvmData(evmItems genesis.EvmItems, blockNum uint64, if acc.Root != types.EmptyRootHash { storageTrie, err := trie.NewStateTrie(trie.StateTrieID(acc.Root), tdb) if err != nil { - return fmt.Errorf("failed to open storage trie for account %v; %v", address, err) + return fmt.Errorf("failed to open storage trie for account %v; %w", address, err) } storageIt, err := storageTrie.NodeIterator(nil) if err != nil { - return fmt.Errorf("failed to open storage iterator for account %v; %v", address, err) + return fmt.Errorf("failed to open storage iterator for account %v; %w", address, err) } for storageIt.Next(true) { if storageIt.Leaf() { keyBytes, err := preimages.Get(storageIt.LeafKey()) if err != nil || keyBytes == nil { - return fmt.Errorf("missing preimage for storage key hash %v; %v", storageIt.LeafKey(), err) + return fmt.Errorf("missing preimage for storage key hash %v; %w", storageIt.LeafKey(), err) } key := cc.Key(common.BytesToHash(keyBytes)) _, valueBytes, _, err := rlp.Split(storageIt.LeafBlob()) if err != nil { - return fmt.Errorf("failed to decode storage; %v", err) + return fmt.Errorf("failed to decode storage; %w", err) } value := cc.Value(common.BytesToHash(valueBytes)) @@ -207,7 +209,7 @@ func (s *Store) ImportLegacyEvmData(evmItems genesis.EvmItems, blockNum uint64, } } if storageIt.Error() != nil { - return fmt.Errorf("failed to iterate storage trie of account %v; %v", address, storageIt.Error()) + return fmt.Errorf("failed to iterate storage trie of account %v; %w", address, storageIt.Error()) } } @@ -218,7 +220,7 @@ func (s *Store) ImportLegacyEvmData(evmItems genesis.EvmItems, blockNum uint64, } } if accIter.Error() != nil { - return fmt.Errorf("failed to iterate accounts trie; %v", accIter.Error()) + return fmt.Errorf("failed to iterate accounts trie; %w", accIter.Error()) } if err := bulk.Close(); err != nil { diff --git a/gossip/evmstore/statedb_verify.go b/gossip/evmstore/statedb_verify.go index 3f7217d2a..36329e72a 100644 --- a/gossip/evmstore/statedb_verify.go +++ b/gossip/evmstore/statedb_verify.go @@ -9,6 +9,7 @@ import ( "github.com/Fantom-foundation/Carmen/go/database/mpt" "github.com/Fantom-foundation/Carmen/go/database/mpt/io" carmen "github.com/Fantom-foundation/Carmen/go/state" + "github.com/Fantom-foundation/go-opera/utils/caution" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) @@ -53,12 +54,12 @@ func (s *Store) VerifyWorldState(expectedBlockNum uint64, expectedHash common.Ha return nil } -func verifyLastState(params carmen.Parameters, expectedBlockNum uint64, expectedHash common.Hash) error { +func verifyLastState(params carmen.Parameters, expectedBlockNum uint64, expectedHash common.Hash) (err error) { liveState, err := carmen.NewState(params) if err != nil { return fmt.Errorf("failed to open carmen live state in %s: %w", params.Directory, err) } - defer liveState.Close() + defer caution.CloseAndReportError(&err, liveState, "failed to close carmen live state") if err := checkStateHash(liveState, expectedHash); err != nil { return fmt.Errorf("live state check failed; %w", err) } @@ -72,17 +73,17 @@ func verifyLastState(params carmen.Parameters, expectedBlockNum uint64, expected } if params.Archive == carmen.NoArchive { - return nil // skip archive checks when archive is not enabled + return // skip archive checks when archive is not enabled } archiveState, err := liveState.GetArchiveState(lastArchiveBlock) if err != nil { return fmt.Errorf("failed to get carmen archive state; %w", err) } - defer archiveState.Close() + defer caution.CloseAndReportError(&err, archiveState, "failed to close carmen archive state") if err := checkStateHash(archiveState, expectedHash); err != nil { return fmt.Errorf("archive state check failed; %w", err) } - return nil + return } func checkStateHash(state carmen.State, expectedHash common.Hash) error { diff --git a/utils/errlock/errlock.go b/utils/errlock/errlock.go index fc9699352..0ef1cd426 100644 --- a/utils/errlock/errlock.go +++ b/utils/errlock/errlock.go @@ -5,11 +5,17 @@ import ( "io" "os" "path" + + "github.com/Fantom-foundation/go-opera/utils/caution" ) // Check if errlock is written func Check() error { - locked, reason, eLockPath, _ := read(datadir) + locked, reason, eLockPath, err := read(datadir) + if err != nil { + return fmt.Errorf("Node isn't allowed to start due to an error reading lock file. Please fix the issue and then delete file \"%s\". Error message:\n%w", eLockPath, err) + } + if locked { return fmt.Errorf("Node isn't allowed to start due to a previous error. Please fix the issue and then delete file \"%s\". Error message:\n%s", eLockPath, reason) } @@ -47,22 +53,25 @@ func readAll(reader io.Reader, max int) ([]byte, error) { } // read errlock file -func read(dir string) (bool, string, string, error) { - eLockPath := path.Join(dir, "errlock") +func read(dir string) (locked bool, reason string, eLockPath string, err error) { + eLockPath = path.Join(dir, "errlock") data, err := os.Open(eLockPath) if err != nil { - return false, "", eLockPath, err + // if file doesn't exist, directory is not locked and it is not an error + return false, "", eLockPath, nil } - defer data.Close() + defer caution.CloseAndReportError(&err, data, "Failed to close errlock file") // read no more than N bytes maxFileLen := 5000 eLockBytes, err := readAll(data, maxFileLen) if err != nil { - return true, "", eLockPath, err + return true, "", eLockPath, fmt.Errorf("failed to read lock file %v: %w", eLockPath, err) } - return true, string(eLockBytes), eLockPath, nil + reason = string(eLockBytes) + locked = true + return } // write errlock file