diff --git a/db/hash_provider.go b/db/hash_provider.go new file mode 100644 index 0000000..ef8353a --- /dev/null +++ b/db/hash_provider.go @@ -0,0 +1,216 @@ +// Copyright 2025 Sonic Labs +// This file is part of Aida Testing Infrastructure for Sonic +// +// Aida 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. +// +// Aida 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 Aida. If not, see . + +package db + +//go:generate mockgen -source hash_provider.go -destination hash_provider_mock.go -package db + +import ( + "bytes" + "encoding/binary" + "fmt" + "strconv" + "strings" + + "github.com/0xsoniclabs/substate/types" + "github.com/status-im/keycard-go/hexutils" +) + +const ( + StateRootHashPrefix = "dbh" + BlockHashPrefix = "bh" +) + +// ClientInterface defines the methods that an RPC client must implement. +type IRpcClient interface { + Call(result interface{}, method string, args ...interface{}) error +} + +type HashProvider interface { + GetStateRootHash(blockNumber int) (types.Hash, error) + GetBlockHash(blockNumber int) (types.Hash, error) +} + +func MakeHashProvider(db BaseDB) HashProvider { + return &hashProvider{db} +} + +type hashProvider struct { + db BaseDB +} + +func (p *hashProvider) GetBlockHash(number int) (types.Hash, error) { + blockHash, err := p.db.Get(BlockHashDBKey(uint64(number))) + if err != nil { + return types.Hash{}, err + } + + if blockHash == nil { + return types.Hash{}, nil + } + + if len(blockHash) != 32 { + return types.Hash{}, fmt.Errorf("invalid block hash length for block %d: expected 32 bytes, got %d bytes", number, len(blockHash)) + } + + return types.Hash(blockHash), nil +} + +func (p *hashProvider) GetStateRootHash(number int) (types.Hash, error) { + hex := strconv.FormatUint(uint64(number), 16) + stateRoot, err := p.db.Get([]byte(StateRootHashPrefix + "0x" + hex)) + if err != nil { + return types.Hash{}, err + } + + if stateRoot == nil { + return types.Hash{}, nil + } + + if len(stateRoot) != 32 { + return types.Hash{}, fmt.Errorf("invalid state root length for block %d: expected 32 bytes, got %d bytes", number, len(stateRoot)) + } + + return types.BytesToHash(stateRoot), nil +} + +// SaveStateRoot saves the state root hash to the database +func SaveStateRoot(db BaseDB, blockNumber string, stateRoot string) error { + fullPrefix := StateRootHashPrefix + blockNumber + err := db.Put([]byte(fullPrefix), hexutils.HexToBytes(strings.TrimPrefix(stateRoot, "0x"))) + if err != nil { + return fmt.Errorf("unable to put state hash for block %s: %v", blockNumber, err) + } + return nil +} + +// SaveBlockHash saves the block hash to the database +func SaveBlockHash(db BaseDB, blockNumber string, hash string) error { + bn, err := strconv.ParseUint(strings.TrimPrefix(blockNumber, "0x"), 16, 64) + if err != nil { + return fmt.Errorf("invalid block number %s: %v", blockNumber, err) + } + fullPrefix := BlockHashDBKey(bn) + err = db.Put(fullPrefix, hexutils.HexToBytes(strings.TrimPrefix(hash, "0x"))) + if err != nil { + return fmt.Errorf("unable to put state hash for block %s: %v", blockNumber, err) + } + return nil +} + +// getBlockByNumber get block from the rpc node +func GetBlockByNumber(client IRpcClient, blockNumber string) (map[string]interface{}, error) { + var block map[string]interface{} + err := client.Call(&block, "eth_getBlockByNumber", blockNumber, false) + if err != nil { + return nil, fmt.Errorf("failed to get block %s: %v", blockNumber, err) + } + return block, nil +} + +// StateHashKeyToUint64 converts a state hash key to a uint64 +func StateHashKeyToUint64(hexBytes []byte) (uint64, error) { + prefix := []byte(StateRootHashPrefix) + + if len(hexBytes) >= len(prefix) && bytes.HasPrefix(hexBytes, prefix) { + hexBytes = hexBytes[len(prefix):] + } + + res, err := strconv.ParseUint(string(hexBytes), 0, 64) + + if err != nil { + return 0, fmt.Errorf("cannot parse uint %v; %v", string(hexBytes), err) + } + return res, nil +} + +// GetFirstStateHash returns the first block number for which we have a state hash +func GetFirstStateHash(db BaseDB) (uint64, error) { + // TODO MATEJ will be fixed in future commit + //iter := db.NewIterator([]byte(StateRootHashPrefix), []byte("0x")) + // + //defer iter.Release() + // + //// start with writing first block + //if !iter.Next() { + // return 0, fmt.Errorf("no state hash found") + //} + // + //firstStateHashBlock, err := StateHashKeyToUint64(iter.Key()) + //if err != nil { + // return 0, err + //} + //return firstStateHashBlock, nil + return 0, fmt.Errorf("not implemented") +} + +// GetLastStateHash returns the last block number for which we have a state hash +func GetLastStateHash(db BaseDB) (uint64, error) { + // TODO MATEJ will be fixed in future commit + //return GetLastKey(db, StateRootHashPrefix) + return 0, fmt.Errorf("not implemented") +} + +// GetFirstBlockHash returns the first block number for which we have a block hash +func GetFirstBlockHash(db BaseDB) (uint64, error) { + iter := db.NewIterator([]byte(BlockHashPrefix), nil) + defer iter.Release() + + if !iter.Next() { + return 0, fmt.Errorf("no block hash found") + } + + firstBlock, err := DecodeBlockHashDBKey(iter.Key()) + if err != nil { + return 0, err + } + return firstBlock, nil +} + +// GetLastBlockHash returns the last block number for which we have a block hash +func GetLastBlockHash(db BaseDB) (uint64, error) { + iter := db.NewIterator([]byte(BlockHashPrefix), nil) + defer iter.Release() + + if !iter.Last() { + return 0, fmt.Errorf("no block hash found") + } + + lastBlock, err := DecodeBlockHashDBKey(iter.Key()) + if err != nil { + return 0, err + } + return lastBlock, nil +} + +func BlockHashDBKey(block uint64) []byte { + prefix := []byte(BlockHashPrefix) + blockByte := make([]byte, 8) + binary.BigEndian.PutUint64(blockByte[0:8], block) + return append(prefix, blockByte...) +} + +// DecodeBlockHashDBKey decodes a block hash key into a block number +func DecodeBlockHashDBKey(data []byte) (uint64, error) { + if len(data) < len(BlockHashPrefix)+8 { + return 0, fmt.Errorf("invalid length of block hash key, expected at least %d, got %d", len(BlockHashPrefix)+8, len(data)) + } + if !bytes.HasPrefix(data, []byte(BlockHashPrefix)) { + return 0, fmt.Errorf("invalid prefix of block hash key") + } + block := binary.BigEndian.Uint64(data[len(BlockHashPrefix):]) + return block, nil +} diff --git a/db/hash_provider_mock.go b/db/hash_provider_mock.go new file mode 100644 index 0000000..8f6fe63 --- /dev/null +++ b/db/hash_provider_mock.go @@ -0,0 +1,112 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: hash_provider.go +// +// Generated by this command: +// +// mockgen -source hash_provider.go -destination hash_provider_mock.go -package db +// + +// Package db is a generated GoMock package. +package db + +import ( + reflect "reflect" + + types "github.com/0xsoniclabs/substate/types" + gomock "go.uber.org/mock/gomock" +) + +// MockIRpcClient is a mock of IRpcClient interface. +type MockIRpcClient struct { + ctrl *gomock.Controller + recorder *MockIRpcClientMockRecorder +} + +// MockIRpcClientMockRecorder is the mock recorder for MockIRpcClient. +type MockIRpcClientMockRecorder struct { + mock *MockIRpcClient +} + +// NewMockIRpcClient creates a new mock instance. +func NewMockIRpcClient(ctrl *gomock.Controller) *MockIRpcClient { + mock := &MockIRpcClient{ctrl: ctrl} + mock.recorder = &MockIRpcClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIRpcClient) EXPECT() *MockIRpcClientMockRecorder { + return m.recorder +} + +// Call mocks base method. +func (m *MockIRpcClient) Call(result any, method string, args ...any) error { + m.ctrl.T.Helper() + varargs := []any{result, method} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Call", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Call indicates an expected call of Call. +func (mr *MockIRpcClientMockRecorder) Call(result, method any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{result, method}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockIRpcClient)(nil).Call), varargs...) +} + +// MockHashProvider is a mock of HashProvider interface. +type MockHashProvider struct { + ctrl *gomock.Controller + recorder *MockHashProviderMockRecorder +} + +// MockHashProviderMockRecorder is the mock recorder for MockHashProvider. +type MockHashProviderMockRecorder struct { + mock *MockHashProvider +} + +// NewMockHashProvider creates a new mock instance. +func NewMockHashProvider(ctrl *gomock.Controller) *MockHashProvider { + mock := &MockHashProvider{ctrl: ctrl} + mock.recorder = &MockHashProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHashProvider) EXPECT() *MockHashProviderMockRecorder { + return m.recorder +} + +// GetBlockHash mocks base method. +func (m *MockHashProvider) GetBlockHash(blockNumber int) (types.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlockHash", blockNumber) + ret0, _ := ret[0].(types.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBlockHash indicates an expected call of GetBlockHash. +func (mr *MockHashProviderMockRecorder) GetBlockHash(blockNumber any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockHash", reflect.TypeOf((*MockHashProvider)(nil).GetBlockHash), blockNumber) +} + +// GetStateRootHash mocks base method. +func (m *MockHashProvider) GetStateRootHash(blockNumber int) (types.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetStateRootHash", blockNumber) + ret0, _ := ret[0].(types.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetStateRootHash indicates an expected call of GetStateRootHash. +func (mr *MockHashProviderMockRecorder) GetStateRootHash(blockNumber any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStateRootHash", reflect.TypeOf((*MockHashProvider)(nil).GetStateRootHash), blockNumber) +} diff --git a/db/hash_provider_test.go b/db/hash_provider_test.go new file mode 100644 index 0000000..7960fb6 --- /dev/null +++ b/db/hash_provider_test.go @@ -0,0 +1,377 @@ +// Copyright 2025 Sonic Labs +// This file is part of Aida Testing Infrastructure for Sonic +// +// Aida 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. +// +// Aida 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 Aida. If not, see . + +package db + +import ( + "encoding/binary" + "errors" + "fmt" + "math/rand" + "strconv" + "testing" + + "github.com/0xsoniclabs/substate/types" + "github.com/stretchr/testify/assert" + "github.com/syndtr/goleveldb/leveldb" + "go.uber.org/mock/gomock" +) + +func TestStateHash_KeyToUint64(t *testing.T) { + type args struct { + hexBytes []byte + } + tests := []struct { + name string + args args + want uint64 + wantErr bool + }{ + {"testZeroConvert", args{[]byte(StateRootHashPrefix + "0x0")}, 0, false}, + {"testOneConvert", args{[]byte(StateRootHashPrefix + "0x1")}, 1, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := StateHashKeyToUint64(tt.args.hexBytes) + if (err != nil) != tt.wantErr { + t.Errorf("StateHashKeyToUint64() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("StateHashKeyToUint64() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestStateHash_GetStateRootHash(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // case success + mockDb := NewMockBaseDB(ctrl) + mockDb.EXPECT().Get(gomock.Any()).Return([]byte("abcdefghijabcdefghijabcdefghij32"), nil) + stateHash := MakeHashProvider(mockDb) + hash, err := stateHash.GetStateRootHash(1234) + assert.NoError(t, err) + assert.Equal(t, "0x6162636465666768696a6162636465666768696a6162636465666768696a3332", hash.String()) + + // case error + mockDb = NewMockBaseDB(ctrl) + mockDb.EXPECT().Get(gomock.Any()).Return(nil, leveldb.ErrNotFound) + stateHash = MakeHashProvider(mockDb) + hash, err = stateHash.GetStateRootHash(1234) + assert.Equal(t, leveldb.ErrNotFound, err) + assert.Equal(t, types.Hash{}, hash) + + // case empty + mockDb = NewMockBaseDB(ctrl) + mockDb.EXPECT().Get(gomock.Any()).Return(nil, nil) + stateHash = MakeHashProvider(mockDb) + hash, err = stateHash.GetStateRootHash(1234) + assert.NoError(t, err) + assert.Equal(t, types.Hash{}, hash) +} + +func TestStateHash_SaveStateRoot(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // case success + mockDb := NewMockBaseDB(ctrl) + mockDb.EXPECT().Put(gomock.Any(), gomock.Any()).Return(nil) + err := SaveStateRoot(mockDb, "0x1234", "0x5678") + assert.NoError(t, err) + + // case error + mockDb = NewMockBaseDB(ctrl) + mockDb.EXPECT().Put(gomock.Any(), gomock.Any()).Return(leveldb.ErrNotFound) + err = SaveStateRoot(mockDb, "0x1234", "0x5678") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "leveldb: not found") +} + +func TestStateHash_StateHashKeyToUint64(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // case success + output, err := StateHashKeyToUint64([]byte("dbh0x1234")) + assert.NoError(t, err) + assert.Equal(t, uint64(0x1234), output) + + // case error + output, err = StateHashKeyToUint64([]byte("ggggggg")) + assert.Equal(t, uint64(0), output) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid syntax") +} + +func TestStateHash_retrieveStateRoot(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // case success + client := NewMockIRpcClient(ctrl) + client.EXPECT().Call(gomock.Any(), "eth_getBlockByNumber", "0x1234", false).Return(nil) + output, err := GetBlockByNumber(client, "0x1234") + assert.NoError(t, err) + assert.Equal(t, map[string]interface{}(nil), output) + + // case error + mockErr := errors.New("error") + client = NewMockIRpcClient(ctrl) + client.EXPECT().Call(gomock.Any(), "eth_getBlockByNumber", "0x1234", false).Return(mockErr) + output, err = GetBlockByNumber(client, "0x1234") + assert.Error(t, err) + assert.Nil(t, output) +} + +func TestStateHash_GetFirstStateHash(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockDb := NewMockBaseDB(ctrl) + output, err := GetFirstStateHash(mockDb) + assert.Equal(t, uint64(0x0), output) + assert.Error(t, err) + +} + +func TestStateHash_GetLastStateHash(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockDb := NewMockBaseDB(ctrl) + output, err := GetLastStateHash(mockDb) + assert.Equal(t, uint64(0x0), output) + assert.Error(t, err) +} + +func TestStateHashProvider_GetStateRootHash(t *testing.T) { + ctrl := gomock.NewController(t) + blk := 10 + tests := []struct { + name string + expect func(mockAidaDb *MockBaseDB) + want types.Hash + }{ + { + name: "GetStatRootHash_OK", + expect: func(mockAidaDb *MockBaseDB) { + hex := strconv.FormatUint(uint64(blk), 16) + mockAidaDb.EXPECT().Get([]byte(StateRootHashPrefix+"0x"+hex)).Return(types.Hash{0x11}.Bytes(), nil) + }, + want: types.Hash{0x11}, + }, + { + name: "GetStatRootHash_NilHash", + expect: func(mockAidaDb *MockBaseDB) { + hex := strconv.FormatUint(uint64(blk), 16) + mockAidaDb.EXPECT().Get([]byte(StateRootHashPrefix+"0x"+hex)).Return(nil, nil) + }, + want: types.Hash{}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mockAidaDb := NewMockBaseDB(ctrl) + hp := MakeHashProvider(mockAidaDb) + test.expect(mockAidaDb) + got, err := hp.GetStateRootHash(blk) + assert.NoError(t, err) + assert.Equal(t, got, test.want) + }) + } +} + +func TestStateHashProvider_GetBlockHash(t *testing.T) { + ctrl := gomock.NewController(t) + + blk := 10 + tests := []struct { + name string + expect func(mockAidaDb *MockBaseDB) + wantHash types.Hash + wantError bool + }{ + { + name: "GetBlockHash_OK", + expect: func(mockAidaDb *MockBaseDB) { + mockAidaDb.EXPECT().Get(BlockHashDBKey(uint64(blk))).Return(types.Hash{0x11}.Bytes(), nil) + }, + wantHash: types.Hash{0x11}, + wantError: false, + }, + { + name: "GetBlockHash_NilHash", + expect: func(mockAidaDb *MockBaseDB) { + mockAidaDb.EXPECT().Get(BlockHashDBKey(uint64(blk))).Return(nil, nil) + }, + wantHash: types.Hash{}, + wantError: false, + }, + { + name: "GetBlockHash_DBError", + expect: func(mockAidaDb *MockBaseDB) { + mockAidaDb.EXPECT().Get(BlockHashDBKey(uint64(blk))).Return(nil, errors.New("db error")) + }, + wantHash: types.Hash{}, + wantError: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mockAidaDb := NewMockBaseDB(ctrl) + hp := MakeHashProvider(mockAidaDb) + test.expect(mockAidaDb) + got, err := hp.GetBlockHash(blk) + if test.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, got, test.wantHash) + }) + } +} + +func TestStateHash_GetFirstBlockHash(t *testing.T) { + testDb := generateTestBlockHashDb(t) + defer func() { + err := testDb.Close() + assert.NoError(t, err, "error closing test database") + }() + + keyFirst := fmt.Sprintf("0x%x", 1+rand.Intn(1000)) + err := SaveBlockHash(testDb, keyFirst, "0x1234") + assert.NoError(t, err, "error saving state root "+keyFirst) + keyLast := fmt.Sprintf("0x%x", 1000+rand.Intn(1000)) + err = SaveBlockHash(testDb, keyLast, "0x1234") + assert.NoError(t, err, "error saving state root "+keyLast) + + output, err := GetFirstBlockHash(testDb) + assert.NoError(t, err) + + assert.Equal(t, keyFirst, "0x"+strconv.FormatUint(output, 16)) +} + +func TestStateHash_GetLastBlockHash(t *testing.T) { + testDb := generateTestBlockHashDb(t) + defer func() { + err := testDb.Close() + assert.NoError(t, err, "error closing test database") + }() + + keyFirst := fmt.Sprintf("0x%x", 1+rand.Intn(1000)) + err := SaveBlockHash(testDb, keyFirst, "0x1234") + assert.NoError(t, err, "error saving state root "+keyFirst) + keyLast := fmt.Sprintf("0x%x", 1000+rand.Intn(1000)) + err = SaveBlockHash(testDb, keyLast, "0x1234") + assert.NoError(t, err, "error saving state root "+keyLast) + + output, err := GetLastBlockHash(testDb) + assert.NoError(t, err) + assert.Equal(t, keyLast, "0x"+strconv.FormatUint(output, 16)) +} + +func generateTestBlockHashDb(t *testing.T) BaseDB { + tmpDir := t.TempDir() + "/blockHashDb" + database, err := NewCodeDB(tmpDir, nil, nil, nil) + if err != nil { + t.Fatalf("error opening stateHash leveldb %s: %v", tmpDir, err) + } + + return database +} + +func TestDecodeBlockHashDBKey_Errors(t *testing.T) { + tests := []struct { + name string + key []byte + want uint64 + wantErr string + }{ + {"valid key", append([]byte(BlockHashPrefix), binary.BigEndian.AppendUint64(nil, uint64(2))...), 2, ""}, + {"invalid key", []byte("invalidkey"), 0, "invalid prefix of block hash key"}, + {"invalid key", []byte("shrt"), 0, "invalid length of block hash key, expected at least 10, got 4"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := DecodeBlockHashDBKey(tt.key) + + if err != nil { + if err.Error() != tt.wantErr { + t.Errorf("DecodeBlockHashDBKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + } else if tt.wantErr != "" { + t.Errorf("DecodeBlockHashDBKey() expected error %v, got nil", tt.wantErr) + return + } + + if got != tt.want { + t.Errorf("DecodeBlockHashDBKey() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetLastBlockHash_EmptyDb(t *testing.T) { + tmpDir := t.TempDir() + "/blockHashDb" + database, err := NewCodeDB(tmpDir, nil, nil, nil) + if err != nil { + t.Fatalf("error opening stateHash leveldb %s: %v", tmpDir, err) + } + defer func() { + err := database.Close() + assert.NoError(t, err, "error closing test database") + }() + + output, err := GetLastBlockHash(database) + if err == nil { + t.Fatalf("expected error when getting last block hash from empty db, but got nil") + } + assert.Equal(t, "no block hash found", err.Error()) + assert.Equal(t, uint64(0), output) +} + +func TestGetLastBlockHash_InvalidKey(t *testing.T) { + tmpDir := t.TempDir() + "/blockHashDb" + database, err := NewCodeDB(tmpDir, nil, nil, nil) + if err != nil { + t.Fatalf("error opening stateHash leveldb %s: %v", tmpDir, err) + } + + // Save an invalid block hash key + err = database.Put([]byte(BlockHashPrefix+"inv"), []byte("someValue")) + if err != nil { + t.Fatalf("error saving invalid block hash key: %v", err) + } + + defer func() { + err := database.Close() + assert.NoError(t, err, "error closing test database") + }() + + output, err := GetLastBlockHash(database) + if err == nil { + t.Fatalf("expected error when getting last block hash with invalid key, but got nil") + } + assert.Equal(t, "invalid length of block hash key, expected at least 10, got 5", err.Error()) + assert.Equal(t, uint64(0), output) +} diff --git a/go.mod b/go.mod index 51881f5..54d10d1 100644 --- a/go.mod +++ b/go.mod @@ -1,33 +1,34 @@ module github.com/0xsoniclabs/substate -go 1.24 +go 1.24.0 require ( github.com/holiman/uint256 v1.3.2 - github.com/stretchr/testify v1.9.0 + github.com/status-im/keycard-go v0.3.3 + github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/urfave/cli/v2 v2.25.7 + github.com/urfave/cli/v2 v2.27.5 go.uber.org/mock v0.5.0 golang.org/x/crypto v0.36.0 google.golang.org/protobuf v1.34.2 ) require ( - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/golang/snappy v1.0.0 // indirect github.com/nxadm/tail v1.4.4 // indirect github.com/onsi/ginkgo v1.14.0 // indirect github.com/onsi/gomega v1.10.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/net v0.38.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.23.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 893467f..64df172 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,11 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -15,8 +16,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -38,14 +39,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/status-im/keycard-go v0.3.3 h1:qk/JHSkT9sMka+lVXrTOIVSgHIY7lDm46wrUqTsNa4s= +github.com/status-im/keycard-go v0.3.3/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -68,8 +71,9 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -93,7 +97,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=