mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
core/rawdb: scaffold era database
This commit is contained in:
parent
1695182b55
commit
228e410d4c
7 changed files with 170 additions and 0 deletions
131
core/rawdb/eradb/eradb.go
Normal file
131
core/rawdb/eradb/eradb.go
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright 2025 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package eradb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EraDatabase struct {
|
||||||
|
datadir string
|
||||||
|
table map[uint64]*era.Era
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new EraDatabase instance.
|
||||||
|
func New(datadir string) (*EraDatabase, error) {
|
||||||
|
// Ensure the datadir is not a symbolic link if it exists.
|
||||||
|
if info, err := os.Lstat(datadir); !os.IsNotExist(err) {
|
||||||
|
if info == nil {
|
||||||
|
log.Warn("Could not Lstat the database", "path", datadir)
|
||||||
|
return nil, errors.New("lstat failed")
|
||||||
|
}
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
log.Warn("Symbolic link erastore is not supported", "path", datadir)
|
||||||
|
return nil, errors.New("symbolic link datadir is not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(datadir, 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Info("Opened erastore", "datadir", datadir)
|
||||||
|
return &EraDatabase{datadir: datadir, table: make(map[uint64]*era.Era)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan returns a list of all era1 files in the datadir.
|
||||||
|
func (db *EraDatabase) scan() ([]string, error) {
|
||||||
|
entries, err := os.ReadDir(db.datadir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var files []string
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() || entry.Type()&os.ModeSymlink != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Skip files that are not era1 files.
|
||||||
|
if path.Ext(entry.Name()) != ".era1" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
files = append(files, entry.Name())
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *EraDatabase) openEra(name string) (*era.Era, error) {
|
||||||
|
e, err := era.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Assign an epoch to the table.
|
||||||
|
if e.Count() != uint64(era.MaxEra1Size) {
|
||||||
|
return nil, fmt.Errorf("pre-merge era1 files must be full. Want: %d, have: %d", era.MaxEra1Size, e.Count())
|
||||||
|
}
|
||||||
|
if e.Start()%uint64(era.MaxEra1Size) != 0 {
|
||||||
|
return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxEra1Size)
|
||||||
|
}
|
||||||
|
epoch := e.Start() / uint64(era.MaxEra1Size)
|
||||||
|
db.mu.Lock()
|
||||||
|
db.table[epoch] = e
|
||||||
|
db.mu.Unlock()
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *EraDatabase) Close() {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
for _, e := range db.table {
|
||||||
|
if err := e.Close(); err != nil {
|
||||||
|
log.Warn("Failed to close era", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.table = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *EraDatabase) GetBlockByNumber(number uint64) (*types.Block, error) {
|
||||||
|
files, err := db.scan()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
_, err := db.openEra(path.Join(db.datadir, file))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup the table by epoch.
|
||||||
|
epoch := number / uint64(era.MaxEra1Size)
|
||||||
|
db.mu.RLock()
|
||||||
|
defer db.mu.RUnlock()
|
||||||
|
if e, ok := db.table[epoch]; ok {
|
||||||
|
block, err := e.GetBlockByNumber(number)
|
||||||
|
if err == nil {
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("block not found")
|
||||||
|
}
|
||||||
39
core/rawdb/eradb/eradb_test.go
Normal file
39
core/rawdb/eradb/eradb_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2025 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package eradb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEraDatabase_Scan(t *testing.T) {
|
||||||
|
// Create the database
|
||||||
|
db, err := New("testdata")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Scan for era files
|
||||||
|
files, err := db.scan()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log(files)
|
||||||
|
block, err := db.GetBlockByNumber(15000)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
require.Equal(t, uint64(15000), block.NumberU64())
|
||||||
|
}
|
||||||
BIN
core/rawdb/eradb/testdata/sepolia-00000-643a00f7.era1
vendored
Normal file
BIN
core/rawdb/eradb/testdata/sepolia-00000-643a00f7.era1
vendored
Normal file
Binary file not shown.
BIN
core/rawdb/eradb/testdata/sepolia-00001-f3ea493e.era1
vendored
Normal file
BIN
core/rawdb/eradb/testdata/sepolia-00001-f3ea493e.era1
vendored
Normal file
Binary file not shown.
BIN
core/rawdb/eradb/testdata/sepolia-00002-dcb01f4d.era1
vendored
Normal file
BIN
core/rawdb/eradb/testdata/sepolia-00002-dcb01f4d.era1
vendored
Normal file
Binary file not shown.
BIN
core/rawdb/eradb/testdata/sepolia-00003-18b32584.era1
vendored
Normal file
BIN
core/rawdb/eradb/testdata/sepolia-00003-18b32584.era1
vendored
Normal file
Binary file not shown.
BIN
core/rawdb/eradb/testdata/sepolia-00004-e93b3f11.era1
vendored
Normal file
BIN
core/rawdb/eradb/testdata/sepolia-00004-e93b3f11.era1
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue