From 61fe18028281b0a0563e5935ab06efdc63e2814b Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Fri, 8 Dec 2023 12:43:07 +0900 Subject: [PATCH 01/63] [API] Fixed legacy non-working LevelDB APIs --- api/api_private_debug.go | 42 +++++++++++----------------- storage/database/badger_database.go | 9 ++++++ storage/database/db_manager.go | 39 ++++++++++++++++++++++++++ storage/database/dynamodb.go | 14 ++++++++-- storage/database/interface.go | 21 ++++++++++++++ storage/database/leveldb_database.go | 14 ++++++++-- storage/database/sharded_database.go | 40 ++++++++++++++++++++++++-- 7 files changed, 147 insertions(+), 32 deletions(-) diff --git a/api/api_private_debug.go b/api/api_private_debug.go index b9c51c3d6f..9572d3b9cb 100644 --- a/api/api_private_debug.go +++ b/api/api_private_debug.go @@ -24,13 +24,12 @@ import ( "context" "errors" "fmt" - "strings" + "time" "github.com/davecgh/go-spew/spew" + "github.com/klaytn/klaytn/common" "github.com/klaytn/klaytn/networks/rpc" "github.com/klaytn/klaytn/storage/database" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/util" ) // PrivateDebugAPI is the collection of Klaytn APIs exposed over the private @@ -52,32 +51,23 @@ func (api *PrivateDebugAPI) GetDBProperty(dt database.DBEntryType, name string) // ChaindbProperty returns leveldb properties of the chain database. func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { - ldb, ok := api.b.ChainDB().(interface { - LDB() *leveldb.DB - }) - if !ok { - return "", fmt.Errorf("chaindbProperty does not work for memory databases") - } - if property == "" { - property = "leveldb.stats" - } else if !strings.HasPrefix(property, "leveldb.") { - property = "leveldb." + property - } - return ldb.LDB().GetProperty(property) + return api.b.ChainDB().Stat(property) } -// ChaindbCompact compacts the chain database if successful, otherwise it returns nil. +// ChaindbCompact flattens the entire key-value database into a single level, +// removing all unused slots and merging all keys. func (api *PrivateDebugAPI) ChaindbCompact() error { - ldb, ok := api.b.ChainDB().(interface { - LDB() *leveldb.DB - }) - if !ok { - return fmt.Errorf("chaindbCompact does not work for memory databases") - } - for b := byte(0); b < 255; b++ { - logger.Info("Compacting chain database", "range", fmt.Sprintf("0x%0.2X-0x%0.2X", b, b+1)) - err := ldb.LDB().CompactRange(util.Range{Start: []byte{b}, Limit: []byte{b + 1}}) - if err != nil { + cstart := time.Now() + for b := 0; b <= 255; b++ { + var ( + start = []byte{byte(b)} + end = []byte{byte(b + 1)} + ) + if b == 255 { + end = nil + } + logger.Info("Compacting database", "range", fmt.Sprintf("%#X-%#X", start, end), "elapsed", common.PrettyDuration(time.Since(cstart))) + if err := api.b.ChainDB().Compact(start, end); err != nil { logger.Error("Database compaction failed", "err", err) return err } diff --git a/storage/database/badger_database.go b/storage/database/badger_database.go index 7971bee0c3..4b6cdcdad4 100644 --- a/storage/database/badger_database.go +++ b/storage/database/badger_database.go @@ -23,6 +23,7 @@ import ( "github.com/dgraph-io/badger" "github.com/klaytn/klaytn/log" + "github.com/pkg/errors" ) const ( @@ -257,3 +258,11 @@ func (b *badgerBatch) Replay(w KeyValueWriter) error { logger.CritWithStack("Replay is not implemented in badgerBatch!") return nil } + +func (bg *badgerDB) Stat(property string) (string, error) { + return "", errors.New("unknown property") +} + +func (bg *badgerDB) Compact(start []byte, limit []byte) error { + return nil +} diff --git a/storage/database/db_manager.go b/storage/database/db_manager.go index a35247d327..a557972c1c 100644 --- a/storage/database/db_manager.go +++ b/storage/database/db_manager.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "encoding/json" + "fmt" "math/big" "os" "path/filepath" @@ -306,6 +307,9 @@ type DBManager interface { ReadChainDataFetcherCheckpoint() (uint64, error) TryCatchUpWithPrimary() error + + Stat(string) (string, error) + Compact([]byte, []byte) error } type DBEntryType uint8 @@ -882,6 +886,41 @@ func (dbm *databaseManager) TryCatchUpWithPrimary() error { return nil } +func (dbm *databaseManager) Stat(property string) (string, error) { + stats := "" + errs := "" + for idx, db := range dbm.dbs { + if db != nil { + stat, err := db.Stat(property) + headInfo := fmt.Sprintf(" [%s:%s]\n", DBEntryType(idx), db.Type()) + if err == nil { + stats += headInfo + stat + } else { + errs += headInfo + err.Error() + } + } + } + if errs == "" { + return stats, nil + } else { + return stats, errors.New(errs) + } +} + +func (dbm *databaseManager) Compact(start []byte, limit []byte) error { + errs := "" + for _, db := range dbm.dbs { + if db != nil { + db.Compact(start, limit) + } + } + if errs == "" { + return nil + } else { + return errors.New(errs) + } +} + func (dbm *databaseManager) GetMemDB() *MemDB { if dbm.config.DBType == MemoryDB { if memDB, ok := dbm.dbs[0].(*MemDB); ok { diff --git a/storage/database/dynamodb.go b/storage/database/dynamodb.go index 78a36ca1db..c613c16719 100644 --- a/storage/database/dynamodb.go +++ b/storage/database/dynamodb.go @@ -69,8 +69,10 @@ const ( ) // batch write -const WorkerNum = 10 -const itemChanSize = WorkerNum * 2 +const ( + WorkerNum = 10 + itemChanSize = WorkerNum * 2 +) var ( dynamoDBClient *dynamodb.DynamoDB // handles dynamoDB connections @@ -629,3 +631,11 @@ func (batch *dynamoBatch) Replay(w KeyValueWriter) error { logger.CritWithStack("Replay should not be called when using dynamodb batch") return nil } + +func (dynamo *dynamoDB) Stat(property string) (string, error) { + return "", errors.New("unknown property") +} + +func (dynamo *dynamoDB) Compact(start []byte, limit []byte) error { + return nil +} diff --git a/storage/database/interface.go b/storage/database/interface.go index 8bfef3b783..340d008602 100644 --- a/storage/database/interface.go +++ b/storage/database/interface.go @@ -68,9 +68,30 @@ type KeyValueWriter interface { Delete(key []byte) error } +// KeyValueStater wraps the Stat method of a backing data store. +type KeyValueStater interface { + // Stat returns a particular internal stat of the database. + Stat(property string) (string, error) +} + +// Compacter wraps the Compact method of a backing data store. +type Compacter interface { + // Compact flattens the underlying data store for the given key range. In essence, + // deleted and overwritten versions are discarded, and the data is rearranged to + // reduce the cost of operations needed to access them. + // + // A nil start is treated as a key before all keys in the data store; a nil limit + // is treated as a key after all keys in the data store. If both is nil then it + // will compact entire data store. + Compact(start []byte, limit []byte) error +} + // Database wraps all database operations. All methods are safe for concurrent use. type Database interface { KeyValueWriter + KeyValueStater + Compacter + Get(key []byte) ([]byte, error) Has(key []byte) (bool, error) Close() diff --git a/storage/database/leveldb_database.go b/storage/database/leveldb_database.go index 2ebfa6021f..f908fbdbbb 100644 --- a/storage/database/leveldb_database.go +++ b/storage/database/leveldb_database.go @@ -22,6 +22,7 @@ package database import ( "fmt" + "strings" "sync" "time" @@ -340,8 +341,17 @@ func (db *levelDB) Close() { } } -func (db *levelDB) LDB() *leveldb.DB { - return db.db +func (db *levelDB) Stat(property string) (string, error) { + if property == "" { + property = "leveldb.stats" + } else if !strings.HasPrefix(property, "leveldb.") { + property = "leveldb." + property + } + return db.db.GetProperty(property) +} + +func (db *levelDB) Compact(start []byte, limit []byte) error { + return db.db.CompactRange(util.Range{Start: start, Limit: limit}) } // Meter configures the database metrics collectors and diff --git a/storage/database/sharded_database.go b/storage/database/sharded_database.go index 620760c0a9..9228e46f68 100644 --- a/storage/database/sharded_database.go +++ b/storage/database/sharded_database.go @@ -26,6 +26,7 @@ import ( "sync" "github.com/klaytn/klaytn/common" + "github.com/pkg/errors" ) var errKeyLengthZero = fmt.Errorf("database key for sharded database should be greater than 0") @@ -173,8 +174,10 @@ func (db *shardedDB) Close() { } // Not enough size of channel slows down the iterator -const shardedDBCombineChanSize = 1024 // Size of resultCh -const shardedDBSubChannelSize = 128 // Size of each sub-channel of resultChs +const ( + shardedDBCombineChanSize = 1024 // Size of resultCh + shardedDBSubChannelSize = 128 // Size of each sub-channel of resultChs +) // shardedDBIterator iterates all items of each shardDB. // This is useful when you want to get items in serial in binary-alphabetigcal order. @@ -530,3 +533,36 @@ func (sdbBatch *shardedDBBatch) Replay(w KeyValueWriter) error { } return nil } + +func (db *shardedDB) Stat(property string) (string, error) { + stats := "" + errs := "" + for idx, shard := range db.shards { + stat, err := shard.Stat(property) + if err == nil { + headInfo := fmt.Sprintf(" [shrad%d:%s]\n", idx, shard.Type()) + stats += headInfo + stat + } else { + errs += fmt.Sprintf("shard[%d]: %s", idx, err.Error()) + } + } + if errs == "" { + return stats, nil + } else { + return stats, errors.New(errs) + } +} + +func (db *shardedDB) Compact(start []byte, limit []byte) error { + errs := "" + for idx, shard := range db.shards { + if err := shard.Compact(start, limit); err != nil { + errs += fmt.Sprintf("shard[%d]: %s", idx, err.Error()) + } + } + if errs == "" { + return nil + } else { + return errors.New(errs) + } +} From 0478766b05da5983ddbbcd6420d0c05cf9a015e1 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 12 Dec 2023 10:20:43 +0900 Subject: [PATCH 02/63] [SetHead] Delete additional snapshot database via flag --- blockchain/blockchain.go | 53 ++++++++++++++------------- blockchain/blockchain_sethead_test.go | 8 ++-- node/cn/api_backend.go | 2 +- node/cn/api_backend_test.go | 2 +- node/cn/backend.go | 2 +- work/mocks/blockchain_mock.go | 8 ++-- work/work.go | 2 +- 7 files changed, 40 insertions(+), 37 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 6921b9fa66..79d27a00f4 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -301,7 +301,7 @@ func NewBlockChain(db database.DBManager, cacheConfig *CacheConfig, chainConfig if diskRoot != (common.Hash{}) { logger.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash(), "snaproot", diskRoot) - snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), diskRoot, true) + snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), diskRoot, true, false) if err != nil { return nil, err } @@ -314,7 +314,7 @@ func NewBlockChain(db database.DBManager, cacheConfig *CacheConfig, chainConfig // Dangling block without a state associated, init from scratch logger.Warn("Head state missing, repairing chain", "number", head.NumberU64(), "hash", head.Hash().String()) - if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true); err != nil { + if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true, false); err != nil { return nil, err } } @@ -327,7 +327,7 @@ func NewBlockChain(db database.DBManager, cacheConfig *CacheConfig, chainConfig // make sure the headerByNumber (if present) is in our current canonical chain if headerByNumber != nil && headerByNumber.Hash() == header.Hash() { logger.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash) - bc.SetHead(header.Number.Uint64() - 1) + bc.SetHead(header.Number.Uint64()-1, false) logger.Error("Chain rewind was successful, resuming normal operation") } } @@ -424,7 +424,7 @@ func (bc *BlockChain) SetCanonicalBlock(blockNum uint64) { // Dangling block without a state associated, init from scratch logger.Warn("Head state missing, repairing chain", "number", head.NumberU64(), "hash", head.Hash().String()) - if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true); err != nil { + if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true, false); err != nil { logger.Error("Repairing chain is failed", "number", head.NumberU64(), "hash", head.Hash().String(), "err", err) return } @@ -513,7 +513,7 @@ func (bc *BlockChain) loadLastState() error { // SetHead rewinds the local chain to a new head with the extra condition // that the rewind must pass the specified state root. The method will try to // delete minimal data from disk whilst retaining chain consistency. -func (bc *BlockChain) SetHead(head uint64) error { +func (bc *BlockChain) SetHead(head uint64, deleteSnapshot bool) error { // With the live pruning enabled, an attempt to SetHead into a state-pruned block number // may result in an infinite loop, trying to find the existing block (probably the genesis block). // If the target `head` is below the surviving block numbers, SetHead early exits with an error. @@ -523,7 +523,7 @@ func (bc *BlockChain) SetHead(head uint64) error { lastPruned, head) } } - _, err := bc.setHeadBeyondRoot(head, common.Hash{}, false) + _, err := bc.setHeadBeyondRoot(head, common.Hash{}, false, deleteSnapshot) return err } @@ -535,7 +535,7 @@ func (bc *BlockChain) SetHead(head uint64) error { // retaining chain consistency. // // The method returns the block number where the requested root cap was found. -func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bool) (uint64, error) { +func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair, deleteSnpashot bool) (uint64, error) { bc.mu.Lock() defer bc.mu.Unlock() @@ -645,25 +645,28 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo } } - // Delete istanbul snapshot database further two epochs - var ( - curBlkNum = bc.CurrentBlock().Number().Uint64() - epoch = bc.Config().Istanbul.Epoch - votingEpoch = curBlkNum - (curBlkNum % epoch) - ) - if votingEpoch == 0 { - votingEpoch = 1 - } - // Delete the snapshot state beyond the block number of the previous epoch on the right - for i := curBlkNum; i >= votingEpoch; i-- { - if params.IsCheckpointInterval(i) { - // delete from sethead number to previous two epoch block nums - // to handle a block that contains non-empty vote data to make sure - // the `HandleGovernanceVote()` cannot be skipped - bc.db.DeleteIstanbulSnapshot(bc.GetBlockByNumber(i).Hash()) + // Snapshot delete operation is invoked only if the sethead was originated from explicit API call + if deleteSnpashot { + // Delete istanbul snapshot database further two epochs + var ( + curBlkNum = bc.CurrentBlock().Number().Uint64() + epoch = bc.Config().Istanbul.Epoch + votingEpoch = curBlkNum - (curBlkNum % epoch) + ) + if votingEpoch == 0 { + votingEpoch = 1 + } + // Delete the snapshot state beyond the block number of the previous epoch on the right + for i := curBlkNum; i >= votingEpoch; i-- { + if params.IsCheckpointInterval(i) { + // delete from sethead number to previous two epoch block nums + // to handle a block that contains non-empty vote data to make sure + // the `HandleGovernanceVote()` cannot be skipped + bc.db.DeleteIstanbulSnapshot(bc.GetBlockByNumber(i).Hash()) + } } + logger.Trace("[SetHead] Snapshot database deleted", "from", originLatestBlkNum, "to", votingEpoch) } - logger.Trace("[SetHead] Snapshot database deleted", "from", originLatestBlkNum, "to", votingEpoch) // Clear out any stale content from the caches bc.futureBlocks.Purge() @@ -784,7 +787,7 @@ func (bc *BlockChain) Reset() error { // specified genesis state. func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { // Dump the entire block chain and purge the caches - if err := bc.SetHead(0); err != nil { + if err := bc.SetHead(0, false); err != nil { return err } bc.mu.Lock() diff --git a/blockchain/blockchain_sethead_test.go b/blockchain/blockchain_sethead_test.go index bf208d921a..79d18f339d 100644 --- a/blockchain/blockchain_sethead_test.go +++ b/blockchain/blockchain_sethead_test.go @@ -155,7 +155,7 @@ func testSetHead(t *testing.T, tt *rewindTest) { } // Set the head of the chain back to the requested number - chain.SetHead(tt.setheadBlock) + chain.SetHead(tt.setheadBlock, true) // Iterate over all the remaining blocks and ensure there are no gaps verifyNoGaps(t, chain, true, canonblocks) @@ -301,11 +301,11 @@ func testSetHeadEarlyExit(t *testing.T, tt *rewindTest) { } db.WriteLastPrunedBlockNumber(tt.setheadBlock + 1) - assert.Error(t, chain.SetHead(tt.setheadBlock)) + assert.Error(t, chain.SetHead(tt.setheadBlock, true)) db.WriteLastPrunedBlockNumber(tt.setheadBlock) - assert.Error(t, chain.SetHead(tt.setheadBlock)) + assert.Error(t, chain.SetHead(tt.setheadBlock, true)) db.WriteLastPrunedBlockNumber(tt.setheadBlock - 1) - assert.Nil(t, chain.SetHead(tt.setheadBlock)) + assert.Nil(t, chain.SetHead(tt.setheadBlock, true)) } diff --git a/node/cn/api_backend.go b/node/cn/api_backend.go index 6d3375fdfb..c2b190bb62 100644 --- a/node/cn/api_backend.go +++ b/node/cn/api_backend.go @@ -80,7 +80,7 @@ func (b *CNAPIBackend) CurrentBlock() *types.Block { } func doSetHead(bc work.BlockChain, cn consensus.Engine, gov governance.Engine, targetBlkNum uint64) error { - if err := bc.SetHead(targetBlkNum); err != nil { + if err := bc.SetHead(targetBlkNum, true); err != nil { return err } // Initialize snapshot cache, staking info cache, and governance cache diff --git a/node/cn/api_backend_test.go b/node/cn/api_backend_test.go index 2b9c07830b..d721f9afba 100644 --- a/node/cn/api_backend_test.go +++ b/node/cn/api_backend_test.go @@ -182,7 +182,7 @@ func TestCNAPIBackend_SetHead(t *testing.T) { api.cn.governance = testGov() number := uint64(123) - mockBlockChain.EXPECT().SetHead(number).Times(1) + mockBlockChain.EXPECT().SetHead(number, true).Times(1) api.SetHead(number) block := newBlock(int(number)) diff --git a/node/cn/backend.go b/node/cn/backend.go index 70c1c12ae6..fef90481b0 100644 --- a/node/cn/backend.go +++ b/node/cn/backend.go @@ -320,7 +320,7 @@ func New(ctx *node.ServiceContext, config *Config) (*CN, error) { // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { logger.Error("Rewinding chain to upgrade configuration", "err", compat) - cn.blockchain.SetHead(compat.RewindTo) + cn.blockchain.SetHead(compat.RewindTo, false) chainDB.WriteChainConfig(genesisHash, cn.chainConfig) } cn.bloomIndexer.Start(cn.blockchain) diff --git a/work/mocks/blockchain_mock.go b/work/mocks/blockchain_mock.go index d834188cc8..c4e46fb02c 100644 --- a/work/mocks/blockchain_mock.go +++ b/work/mocks/blockchain_mock.go @@ -746,17 +746,17 @@ func (mr *MockBlockChainMockRecorder) SaveTrieNodeCacheToDisk() *gomock.Call { } // SetHead mocks base method. -func (m *MockBlockChain) SetHead(arg0 uint64) error { +func (m *MockBlockChain) SetHead(arg0 uint64, arg1 bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetHead", arg0) + ret := m.ctrl.Call(m, "SetHead", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SetHead indicates an expected call of SetHead. -func (mr *MockBlockChainMockRecorder) SetHead(arg0 interface{}) *gomock.Call { +func (mr *MockBlockChainMockRecorder) SetHead(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHead", reflect.TypeOf((*MockBlockChain)(nil).SetHead), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHead", reflect.TypeOf((*MockBlockChain)(nil).SetHead), arg0, arg1) } // Snapshots mocks base method. diff --git a/work/work.go b/work/work.go index 59c737a048..876e8993ea 100644 --- a/work/work.go +++ b/work/work.go @@ -258,7 +258,7 @@ type BlockChain interface { StateCache() state.Database SubscribeChainEvent(ch chan<- blockchain.ChainEvent) event.Subscription - SetHead(head uint64) error + SetHead(head uint64, delteSnapshot bool) error Stop() SubscribeRemovedLogsEvent(ch chan<- blockchain.RemovedLogsEvent) event.Subscription From 8ba2d876b564bdea802e2d6b44a5c21b60f0b575 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 12 Dec 2023 10:28:27 +0900 Subject: [PATCH 03/63] [SetHead] Description revised --- blockchain/blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 79d27a00f4..00f55a6d3b 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -645,9 +645,9 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair, d } } - // Snapshot delete operation is invoked only if the sethead was originated from explicit API call if deleteSnpashot { // Delete istanbul snapshot database further two epochs + // Invoked only if the sethead was originated from explicit API call var ( curBlkNum = bc.CurrentBlock().Number().Uint64() epoch = bc.Config().Istanbul.Epoch From c24b3030802a1a5e857f47d7e926457c3bfe73bc Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 12 Dec 2023 12:31:08 +0900 Subject: [PATCH 04/63] [Golang] Version bumpup --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9aa71d695a..3e6b58e6bf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/klaytn/klaytn -go 1.18 +go 1.20 require ( github.com/Shopify/sarama v1.26.4 From ac9a9d45fa05d9fb0e8a87ebd644aa5dd5ddbd15 Mon Sep 17 00:00:00 2001 From: sjnam Date: Thu, 14 Dec 2023 13:37:02 +0900 Subject: [PATCH 05/63] remove tests for unused methods --- networks/rpc/websocket_test.go | 140 ++++++++++++++++----------------- 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/networks/rpc/websocket_test.go b/networks/rpc/websocket_test.go index c1d9bdb498..f8ac10e8d4 100644 --- a/networks/rpc/websocket_test.go +++ b/networks/rpc/websocket_test.go @@ -81,77 +81,75 @@ func TestWebsocketLargeCall(t *testing.T) { assert.Error(t, client.Call(&result, method, arg, 1), "no error for too large call") } -func newTestListener() net.Listener { - ln, err := net.Listen("tcp", "localhost:0") - if err != nil { - panic(err) - } - return ln -} - -/* -func TestWSServer_MaxConnections(t *testing.T) { - // create server - var ( - srv = newTestServer("service", new(Service)) - ln = newTestListener() - ) - defer srv.Stop() - defer ln.Close() - - go NewWSServer([]string{"*"}, srv).Serve(ln) - time.Sleep(100 * time.Millisecond) - - // set max websocket connections - MaxWebsocketConnections = 3 - testWebsocketMaxConnections(t, "ws://"+ln.Addr().String(), int(MaxWebsocketConnections)) -} -*/ - -func TestFastWSServer_MaxConnections(t *testing.T) { - // create server - var ( - srv = newTestServer("service", new(Service)) - ln = newTestListener() - ) - defer srv.Stop() - defer ln.Close() - - go NewFastWSServer([]string{"*"}, srv).Serve(ln) - time.Sleep(100 * time.Millisecond) - - // set max websocket connections - MaxWebsocketConnections = 3 - testWebsocketMaxConnections(t, "ws://"+ln.Addr().String(), int(MaxWebsocketConnections)) -} - -func testWebsocketMaxConnections(t *testing.T, addr string, maxConnections int) { - var closers []*Client - - for i := 0; i <= maxConnections; i++ { - client, err := DialWebsocket(context.Background(), addr, "") - if err != nil { - t.Fatal(err) - } - closers = append(closers, client) - - var result echoResult - method := "service_echo" - arg := strings.Repeat("x", i) - err = client.Call(&result, method, arg, 1) - if i < int(MaxWebsocketConnections) { - assert.NoError(t, err) - assert.Equal(t, arg, result.String, "wrong string echoed") - } else { - assert.Error(t, err) - // assert.Equal(t, "EOF", err.Error()) - } - } - - for _, client := range closers { - client.Close() - } -} +// func newTestListener() net.Listener { +// ln, err := net.Listen("tcp", "localhost:0") +// if err != nil { +// panic(err) +// } +// return ln +// } + +// func TestWSServer_MaxConnections(t *testing.T) { +// // create server +// var ( +// srv = newTestServer("service", new(Service)) +// ln = newTestListener() +// ) +// defer srv.Stop() +// defer ln.Close() + +// go NewWSServer([]string{"*"}, srv).Serve(ln) +// time.Sleep(100 * time.Millisecond) + +// // set max websocket connections +// MaxWebsocketConnections = 3 +// testWebsocketMaxConnections(t, "ws://"+ln.Addr().String(), int(MaxWebsocketConnections)) +// } + +// func TestFastWSServer_MaxConnections(t *testing.T) { +// // create server +// var ( +// srv = newTestServer("service", new(Service)) +// ln = newTestListener() +// ) +// defer srv.Stop() +// defer ln.Close() + +// go NewFastWSServer([]string{"*"}, srv).Serve(ln) +// time.Sleep(100 * time.Millisecond) + +// // set max websocket connections +// MaxWebsocketConnections = 3 +// testWebsocketMaxConnections(t, "ws://"+ln.Addr().String(), int(MaxWebsocketConnections)) +// } + +// func testWebsocketMaxConnections(t *testing.T, addr string, maxConnections int) { +// var closers []*Client + +// for i := 0; i <= maxConnections; i++ { +// client, err := DialWebsocket(context.Background(), addr, "") +// if err != nil { +// t.Fatal(err) +// } +// closers = append(closers, client) + +// var result echoResult +// method := "service_echo" +// arg := strings.Repeat("x", i) +// err = client.Call(&result, method, arg, 1) +// if i < int(MaxWebsocketConnections) { +// assert.NoError(t, err) +// assert.Equal(t, arg, result.String, "wrong string echoed") +// } else { +// assert.Error(t, err) +// // assert.Equal(t, "EOF", err.Error()) +// } +// } + +// for _, client := range closers { +// client.Close() +// } +// } func TestWebsocketClientHeaders(t *testing.T) { t.Parallel() From 874c3fa53c1204eb805cc283e99d9faf57491e2d Mon Sep 17 00:00:00 2001 From: sjnam Date: Thu, 14 Dec 2023 13:37:24 +0900 Subject: [PATCH 06/63] fix consolecmd test cases --- cmd/utils/nodecmd/consolecmd_test.go | 12 ++++++------ console/console.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/utils/nodecmd/consolecmd_test.go b/cmd/utils/nodecmd/consolecmd_test.go index 0cad658061..c4c17dcdc9 100644 --- a/cmd/utils/nodecmd/consolecmd_test.go +++ b/cmd/utils/nodecmd/consolecmd_test.go @@ -60,9 +60,9 @@ func TestConsoleWelcome(t *testing.T) { klay.Expect(` Welcome to the Klaytn JavaScript console! -instance: Klaytn/{{klayver}}/{{goos}}-{{goarch}}/{{gover}} - datadir: {{datadir}} - modules: {{apis}} + instance: Klaytn/{{klayver}}/{{goos}}-{{goarch}}/{{gover}} + datadir: {{datadir}} + modules: {{apis}} > {{.InputLine "exit"}} `) @@ -139,9 +139,9 @@ func testAttachWelcome(t *testing.T, klay *testklay, endpoint, apis string) { attach.Expect(` Welcome to the Klaytn JavaScript console! -instance: Klaytn/{{klayver}}/{{goos}}-{{goarch}}/{{gover}}{{if ipc}} - datadir: {{datadir}}{{end}} - modules: {{apis}} + instance: Klaytn/{{klayver}}/{{goos}}-{{goarch}}/{{gover}}{{if ipc}} + datadir: {{datadir}}{{end}} + modules: {{apis}} > {{.InputLine "exit" }} `) diff --git a/console/console.go b/console/console.go index 16c768b524..b573401916 100644 --- a/console/console.go +++ b/console/console.go @@ -330,7 +330,7 @@ func (c *Console) Welcome() { modules = append(modules, fmt.Sprintf("%s:%s", api, version)) } sort.Strings(modules) - fmt.Fprintln(c.printer, " modules:", strings.Join(modules, " ")) + fmt.Fprintln(c.printer, " modules:", strings.Join(modules, " ")) } fmt.Fprintln(c.printer) } From e776d045d4ad8643aa2ae2627eb12766c9c0728b Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Thu, 14 Dec 2023 16:29:39 +0900 Subject: [PATCH 07/63] [API] Completed implementation of `klay_getProof` and `eth_getProof` --- api/api_ethereum.go | 90 ++++++++++++++++++++++++++++++------ api/api_public_blockchain.go | 4 ++ console/web3ext/web3ext.go | 6 +++ 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/api/api_ethereum.go b/api/api_ethereum.go index ea07327eeb..be516088b1 100644 --- a/api/api_ethereum.go +++ b/api/api_ethereum.go @@ -27,6 +27,7 @@ import ( "time" "github.com/klaytn/klaytn/rlp" + "github.com/klaytn/klaytn/storage/statedb" "github.com/klaytn/klaytn/blockchain" "github.com/klaytn/klaytn/blockchain/state" @@ -392,37 +393,98 @@ type EthStorageResult struct { Proof []string `json:"proof"` } -// GetProof returns the Merkle-proof for a given account and optionally some storage keys. -// This feature is not supported in Klaytn yet. It just returns account information from state trie. -func (api *EthereumAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*EthAccountResult, error) { - state, _, err := api.publicKlayAPI.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) +// proofList implements KeyValueWriter and collects the proofs as +// hex-strings for delivery to rpc-caller. +type proofList []string + +func (n *proofList) Put(key []byte, value []byte) error { + *n = append(*n, hexutil.Encode(value)) + return nil +} + +func (n *proofList) Delete(key []byte) error { + panic("not supported") +} + +func (n *proofList) WriteMerkleProof(key, value []byte) { + n.Put(key, value) +} + +func doGetProof(ctx context.Context, b Backend, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*EthAccountResult, error) { + var ( + keys = make([]common.Hash, len(storageKeys)) + keyLengths = make([]int, len(storageKeys)) + storageProof = make([]EthStorageResult, len(storageKeys)) + ) + state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } - storageTrie := state.StorageTrie(address) - storageHash := types.EmptyRootHashOriginal codeHash := state.GetCodeHash(address) - storageProof := make([]EthStorageResult, len(storageKeys)) + + contractStorageRootExt, err := state.GetContractStorageRoot(address) + if err != nil { + return nil, err + } + contractStorageRoot := contractStorageRootExt.Unextend() // if we have a storageTrie, (which means the account exists), we can update the storagehash - if storageTrie != nil { - storageHash = storageTrie.Hash() - } else { - // no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray. - codeHash = crypto.Keccak256Hash(nil) + if len(keys) > 0 { + storageTrie, err := statedb.NewTrie(contractStorageRoot, state.Database().TrieDB(), nil) + if err != nil { + return nil, err + } + // Create the proofs for the storageKeys. + for i, key := range keys { + // Output key encoding is a bit special: if the input was a 32-byte hash, it is + // returned as such. Otherwise, we apply the QUANTITY encoding mandated by the + // JSON-RPC spec for getProof. This behavior exists to preserve backwards + // compatibility with older client versions. + var outputKey string + if keyLengths[i] != 32 { + outputKey = hexutil.EncodeBig(key.Big()) + } else { + outputKey = hexutil.Encode(key[:]) + } + if storageTrie == nil { + storageProof[i] = EthStorageResult{outputKey, &hexutil.Big{}, []string{}} + continue + } + var proof proofList + if err := storageTrie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof); err != nil { + return nil, err + } + value := (*hexutil.Big)(state.GetState(address, key).Big()) + storageProof[i] = EthStorageResult{outputKey, value, proof} + } + } + + // Create the accountProof. + trie, err := statedb.NewTrie(header.Root, state.Database().TrieDB(), nil) + if err != nil { + return nil, err + } + var accountProof proofList + if err := trie.Prove(crypto.Keccak256(address.Bytes()), 0, &accountProof); err != nil { + return nil, err } return &EthAccountResult{ Address: address, - AccountProof: []string{}, + AccountProof: accountProof, Balance: (*hexutil.Big)(state.GetBalance(address)), CodeHash: codeHash, Nonce: hexutil.Uint64(state.GetNonce(address)), - StorageHash: storageHash, + StorageHash: contractStorageRoot, StorageProof: storageProof, }, state.Error() } +// GetProof returns the Merkle-proof for a given account and optionally some storage keys +func (api *EthereumAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*EthAccountResult, error) { + return doGetProof(ctx, api.publicBlockChainAPI.b, address, storageKeys, blockNrOrHash) +} + // GetHeaderByNumber returns the requested canonical block header. // * When blockNr is -1 the chain head is returned. // * When blockNr is -2 the pending chain head is returned. diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index d79ca6e051..2559039e61 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -466,6 +466,10 @@ func (s *PublicBlockChainAPI) CreateAccessList(ctx context.Context, args EthTran return doCreateAccessList(ctx, s.b, args, blockNrOrHash) } +func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*EthAccountResult, error) { + return doGetProof(ctx, s.b, address, storageKeys, blockNrOrHash) +} + // StructLogRes stores a structured log emitted by the EVM while replaying a // transaction in debug mode type StructLogRes struct { diff --git a/console/web3ext/web3ext.go b/console/web3ext/web3ext.go index 25da6b2241..7c60451fa0 100644 --- a/console/web3ext/web3ext.go +++ b/console/web3ext/web3ext.go @@ -1070,6 +1070,12 @@ web3._extend({ params: 3, inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, null] }), + new web3._extend.Method({ + name: 'getProof', + call: 'klay_getProof', + params: 3, + inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter] + }), ], properties: [ new web3._extend.Property({ From 7683eb6ef6f1839c462af0517aba28f9bd30cb41 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Fri, 15 Dec 2023 09:31:15 +0900 Subject: [PATCH 08/63] [API] Decode storage hexkey --- api/api_ethereum.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/api/api_ethereum.go b/api/api_ethereum.go index be516088b1..6051bde8bb 100644 --- a/api/api_ethereum.go +++ b/api/api_ethereum.go @@ -19,6 +19,7 @@ package api import ( "context" "encoding/binary" + "encoding/hex" "errors" "fmt" "math/big" @@ -410,12 +411,39 @@ func (n *proofList) WriteMerkleProof(key, value []byte) { n.Put(key, value) } +// decodeHash parses a hex-encoded 32-byte hash. The input may optionally +// be prefixed by 0x and can have a byte length up to 32. +func decodeHash(s string) (h common.Hash, inputLength int, err error) { + if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") { + s = s[2:] + } + if (len(s) & 1) > 0 { + s = "0" + s + } + b, err := hex.DecodeString(s) + if err != nil { + return common.Hash{}, 0, errors.New("hex string invalid") + } + if len(b) > 32 { + return common.Hash{}, len(b), errors.New("hex string too long, want at most 32 bytes") + } + return common.BytesToHash(b), len(b), nil +} + func doGetProof(ctx context.Context, b Backend, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*EthAccountResult, error) { var ( keys = make([]common.Hash, len(storageKeys)) keyLengths = make([]int, len(storageKeys)) storageProof = make([]EthStorageResult, len(storageKeys)) ) + // Deserialize all keys. This prevents state access on invalid input. + for i, hexKey := range storageKeys { + var err error + keys[i], keyLengths[i], err = decodeHash(hexKey) + if err != nil { + return nil, err + } + } state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err From 9e6d9dcbe6f39dabd0fbd5dfa8a64edc0bf605af Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 17 Dec 2023 20:34:52 +0800 Subject: [PATCH 09/63] Lower the log level of doEstimate Warn logs to Trace --- blockchain/evm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockchain/evm.go b/blockchain/evm.go index a0d27f23fd..075f9f12af 100644 --- a/blockchain/evm.go +++ b/blockchain/evm.go @@ -183,14 +183,14 @@ func DoEstimateGas(ctx context.Context, gasLimit, rpcGasCap uint64, txValue, gas // If the allowance is larger than maximum uint64, skip checking if allowance.IsUint64() && hi > allowance.Uint64() { - logger.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, + logger.Trace("Gas estimation capped by limited funds", "original", hi, "balance", balance, "sent", txValue, "maxFeePerGas", gasPrice, "fundable", allowance) hi = allowance.Uint64() } } // Recap the highest gas allowance with specified gascap. if rpcGasCap != 0 && hi > rpcGasCap { - logger.Warn("Caller gas above allowance, capping", "requested", hi, "cap", rpcGasCap) + logger.Trace("Caller gas above allowance, capping", "requested", hi, "cap", rpcGasCap) hi = rpcGasCap } cap = hi From a6b5712c111039eb7a23fcdb350f47e5133e7a79 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Mon, 18 Dec 2023 14:01:19 +0900 Subject: [PATCH 10/63] [API] Receipt-nil check added --- consensus/istanbul/backend/api.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/consensus/istanbul/backend/api.go b/consensus/istanbul/backend/api.go index cdfdf3c7a4..ca3b423896 100644 --- a/consensus/istanbul/backend/api.go +++ b/consensus/istanbul/backend/api.go @@ -317,8 +317,10 @@ func (api *APIExtension) makeRPCBlockOutput(b *types.Block, // make transactions numTxs := len(transactions) rpcTransactions := make([]map[string]interface{}, numTxs) - for i, tx := range transactions { - rpcTransactions[i] = klaytnApi.RpcOutputReceipt(head, tx, hash, head.Number.Uint64(), uint64(i), receipts[i]) + if len(receipts) == len(transactions) { + for i, tx := range transactions { + rpcTransactions[i] = klaytnApi.RpcOutputReceipt(head, tx, hash, head.Number.Uint64(), uint64(i), receipts[i]) + } } r["committee"] = cInfo.Committee From 783855416e14c5e4abcc58184d90733409860da1 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Mon, 18 Dec 2023 14:23:32 +0900 Subject: [PATCH 11/63] [API] Fill transactions only when receipt is nil --- api/api_public_blockchain.go | 4 ++++ consensus/istanbul/backend/api.go | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index d79ca6e051..8f47526900 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -608,6 +608,10 @@ func getFrom(tx *types.Transaction) common.Address { return from } +func NewRPCTransaction(b *types.Block, tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) map[string]interface{} { + return newRPCTransaction(b, tx, blockHash, blockNumber, index) +} + // newRPCTransaction returns a transaction that will serialize to the RPC // representation, with the given location metadata set (if available). func newRPCTransaction(b *types.Block, tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) map[string]interface{} { diff --git a/consensus/istanbul/backend/api.go b/consensus/istanbul/backend/api.go index ca3b423896..468be007cc 100644 --- a/consensus/istanbul/backend/api.go +++ b/consensus/istanbul/backend/api.go @@ -317,9 +317,12 @@ func (api *APIExtension) makeRPCBlockOutput(b *types.Block, // make transactions numTxs := len(transactions) rpcTransactions := make([]map[string]interface{}, numTxs) - if len(receipts) == len(transactions) { - for i, tx := range transactions { + for i, tx := range transactions { + if len(receipts) == len(transactions) { rpcTransactions[i] = klaytnApi.RpcOutputReceipt(head, tx, hash, head.Number.Uint64(), uint64(i), receipts[i]) + } else { + // fill the transaction output if receipt is not found + rpcTransactions[i] = klaytnApi.NewRPCTransaction(b, tx, hash, head.Number.Uint64(), uint64(i)) } } From 55359a9544b3623bb8de99e58f06925e23e1b5fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:36:15 +0000 Subject: [PATCH 12/63] Bump golang.org/x/crypto from 0.14.0 to 0.17.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 9aa71d695a..0190c683a2 100644 --- a/go.mod +++ b/go.mod @@ -58,9 +58,9 @@ require ( github.com/urfave/cli/v2 v2.25.7 github.com/valyala/fasthttp v1.34.0 go.uber.org/zap v1.13.0 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.17.0 golang.org/x/net v0.17.0 - golang.org/x/sys v0.13.0 + golang.org/x/sys v0.15.0 golang.org/x/tools v0.6.0 google.golang.org/grpc v1.56.3 gopkg.in/DataDog/dd-trace-go.v1 v1.42.0 @@ -143,7 +143,7 @@ require ( golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/go.sum b/go.sum index 15e3ef223d..594c2e381f 100644 --- a/go.sum +++ b/go.sum @@ -600,8 +600,8 @@ golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -730,8 +730,8 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -741,8 +741,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= From be58d98916b3015df83a108fb800498b16c7b37c Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 19 Dec 2023 09:26:02 +0900 Subject: [PATCH 13/63] [Snapshot] Snapshot recovery added --- consensus/istanbul/backend/backend.go | 2 ++ consensus/istanbul/backend/engine.go | 42 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index 775242a65a..bede7bf4bf 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -143,6 +143,8 @@ type backend struct { // Node type nodetype common.ConnType + + isRestoring atomic.Bool } func (sb *backend) NodeType() common.ConnType { diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index c465d9fb23..efb11a92a4 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -921,10 +921,52 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com logger.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) } + sb.regen(chain, headers, writable) + sb.recents.Add(snap.Hash, snap) return snap, err } +// regen commits snapshot data to database +func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header, writable bool) { + if !sb.isRestoring.Load() && len(headers) > 1 { + sb.isRestoring.Store(true) + defer func() { + sb.isRestoring.Store(false) + }() + + var ( + from = headers[0].Number + to = headers[len(headers)-1].Number + start = time.Now() + commitTried = false + ) + for _, header := range headers { + var ( + hn = header.Number.Uint64() + hh = header.Hash() + ) + if params.IsCheckpointInterval(hn) { + snap, err := sb.snapshot(chain, hn, hh, nil, writable) + if err != nil { + logger.Warn("[Snapshot] Snapshot restoring failed", "len(headers)", len(headers), "from", from, "to", to, "headerNumber", hn) + continue + } + // Store snapshot data if it was not committed before + if loadSnap, _ := sb.db.ReadIstanbulSnapshot(hh); loadSnap == nil { + if err = snap.store(sb.db); err != nil { + logger.Warn("[Snapshot] Snapshot restoring failed", "len(headers)", len(headers), "from", from, "to", to, "headerNumber", hn) + } + commitTried = true + } + } + } + if commitTried { // This prevents push too many logs by potential DoS attack + logger.Trace("[Snapshot] Snapshot restoring completed", "len(headers)", len(headers), "from", from, "to", to, "end", time.Since(start)) + } + } +} + // FIXME: Need to update this for Istanbul // sigHash returns the hash which is used as input for the Istanbul // signing. It is the hash of the entire header apart from the 65 byte signature From 780a7a14feeee7d4f11cf57204cbb5e84dad385a Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 19 Dec 2023 09:46:37 +0900 Subject: [PATCH 14/63] [Snapshot] Fixed `writable` to `false` --- consensus/istanbul/backend/engine.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index efb11a92a4..07d4463671 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -921,14 +921,14 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com logger.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) } - sb.regen(chain, headers, writable) + sb.regen(chain, headers) sb.recents.Add(snap.Hash, snap) return snap, err } // regen commits snapshot data to database -func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header, writable bool) { +func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { if !sb.isRestoring.Load() && len(headers) > 1 { sb.isRestoring.Store(true) defer func() { @@ -947,7 +947,7 @@ func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header, w hh = header.Hash() ) if params.IsCheckpointInterval(hn) { - snap, err := sb.snapshot(chain, hn, hh, nil, writable) + snap, err := sb.snapshot(chain, hn, hh, nil, false) if err != nil { logger.Warn("[Snapshot] Snapshot restoring failed", "len(headers)", len(headers), "from", from, "to", to, "headerNumber", hn) continue From 8335e59e5f2db9248c2f057b575c4d8d0daa0794 Mon Sep 17 00:00:00 2001 From: sjnam Date: Tue, 19 Dec 2023 09:49:14 +0900 Subject: [PATCH 15/63] unsafe debug apis are available if attached via IPC even thougn --rpc.unsafe-debug.disable --- api/backend.go | 20 ++++++++------------ networks/rpc/endpoints.go | 4 ++-- networks/rpc/types.go | 9 +++++---- node/node.go | 16 ++++++---------- 4 files changed, 21 insertions(+), 28 deletions(-) diff --git a/api/backend.go b/api/backend.go index d0c244dc1c..0f389f5a90 100644 --- a/api/backend.go +++ b/api/backend.go @@ -39,9 +39,10 @@ import ( "github.com/klaytn/klaytn/storage/database" ) -//go:generate mockgen -destination=api/mocks/backend_mock.go github.com/klaytn/klaytn/api Backend // Backend interface provides the common API services (that are provided by // both full and light clients) with access to necessary functions. +// +//go:generate mockgen -destination=api/mocks/backend_mock.go github.com/klaytn/klaytn/api Backend type Backend interface { // General Klaytn API Progress() klaytn.SyncProgress @@ -149,19 +150,14 @@ func GetAPIs(apiBackend Backend, disableUnsafeDebug bool) ([]rpc.API, *EthereumA Version: "1.0", Service: NewPrivateAccountAPI(apiBackend, nonceLock), Public: false, + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPrivateDebugAPI(apiBackend), + Public: false, + DisableUnsafeDebug: disableUnsafeDebug, }, } - privateDebugApi := []rpc.API{ - { - Namespace: "debug", - Version: "1.0", - Service: NewPrivateDebugAPI(apiBackend), - Public: false, - }, - } - if !disableUnsafeDebug { - rpcApi = append(rpcApi, privateDebugApi...) - } return rpcApi, ethAPI } diff --git a/networks/rpc/endpoints.go b/networks/rpc/endpoints.go index e376301680..ba0455a679 100644 --- a/networks/rpc/endpoints.go +++ b/networks/rpc/endpoints.go @@ -34,7 +34,7 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str // Register all the APIs exposed by the services handler := NewServer() for _, api := range apis { - if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if !api.DisableUnsafeDebug && (whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public)) { if err := handler.RegisterName(api.Namespace, api.Service); err != nil { return nil, nil, err } @@ -92,7 +92,7 @@ func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins [] // Register all the APIs exposed by the services handler := NewServer() for _, api := range apis { - if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if !api.DisableUnsafeDebug && (exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public)) { if err := handler.RegisterName(api.Namespace, api.Service); err != nil { return nil, nil, err } diff --git a/networks/rpc/types.go b/networks/rpc/types.go index 186f20eff9..800e5ab19b 100644 --- a/networks/rpc/types.go +++ b/networks/rpc/types.go @@ -33,10 +33,11 @@ import ( // API describes the set of methods offered over the RPC interface type API struct { - Namespace string // namespace under which the rpc methods of Service are exposed - Version string // api version for DApp's - Service interface{} // receiver instance which holds the methods - Public bool // indication if the methods must be considered safe for public use + Namespace string // namespace under which the rpc methods of Service are exposed + Version string // api version for DApp's + Service interface{} // receiver instance which holds the methods + Public bool // indication if the methods must be considered safe for public use + DisableUnsafeDebug bool // disable unsafe debug APIs } // Error wraps RPC errors, which contain an error code in addition to the message. diff --git a/node/node.go b/node/node.go index abc79c049a..a946a51e66 100644 --- a/node/node.go +++ b/node/node.go @@ -784,18 +784,14 @@ func (n *Node) apis() []rpc.API { Version: "1.0", Service: NewPublicKlayAPI(n), Public: true, + }, { + Namespace: "debug", + Version: "1.0", + Service: debug.Handler, + DisableUnsafeDebug: n.config.DisableUnsafeDebug, }, } - debugRpcApi := []rpc.API{ - { - Namespace: "debug", - Version: "1.0", - Service: debug.Handler, - }, - } - if !n.config.DisableUnsafeDebug { - rpcApi = append(rpcApi, debugRpcApi...) - } + return rpcApi } From bb3659c63f51d2a35135bd389fbad74781b4981d Mon Sep 17 00:00:00 2001 From: sjnam Date: Tue, 19 Dec 2023 09:51:14 +0900 Subject: [PATCH 16/63] remove fastHTTP and fastWS --- cmd/kbn/node.go | 68 ++++++++++++------------ networks/rpc/endpoints.go | 108 +++++++++++++++++++------------------- node/node.go | 68 ++++++++++++------------ 3 files changed, 122 insertions(+), 122 deletions(-) diff --git a/cmd/kbn/node.go b/cmd/kbn/node.go index ef73d9f63f..53c40e95d6 100644 --- a/cmd/kbn/node.go +++ b/cmd/kbn/node.go @@ -232,23 +232,23 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors } // startFastHTTP initializes and starts the HTTP RPC endpoint. -func (n *Node) startFastHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error { - // Short circuit if the HTTP endpoint isn't being exposed - if endpoint == "" { - return nil - } - listener, handler, err := rpc.StartFastHTTPEndpoint(endpoint, apis, modules, cors, vhosts, n.config.HTTPTimeouts) - if err != nil { - return err - } - n.logger.Info("FastHTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) - // All listeners booted successfully - n.httpEndpoint = endpoint - n.httpListener = listener - n.httpHandler = handler - - return nil -} +// func (n *Node) startFastHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error { +// // Short circuit if the HTTP endpoint isn't being exposed +// if endpoint == "" { +// return nil +// } +// listener, handler, err := rpc.StartFastHTTPEndpoint(endpoint, apis, modules, cors, vhosts, n.config.HTTPTimeouts) +// if err != nil { +// return err +// } +// n.logger.Info("FastHTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) +// // All listeners booted successfully +// n.httpEndpoint = endpoint +// n.httpListener = listener +// n.httpHandler = handler + +// return nil +// } // stopHTTP terminates the HTTP RPC endpoint. func (n *Node) stopHTTP() { @@ -284,23 +284,23 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig } // startFastWS initializes and starts the websocket RPC endpoint. -func (n *Node) startFastWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { - // Short circuit if the WS endpoint isn't being exposed - if endpoint == "" { - return nil - } - listener, handler, err := rpc.StartFastWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) - if err != nil { - return err - } - n.logger.Info("FastWebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) - // All listeners booted successfully - n.wsEndpoint = endpoint - n.wsListener = listener - n.wsHandler = handler - - return nil -} +// func (n *Node) startFastWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { +// // Short circuit if the WS endpoint isn't being exposed +// if endpoint == "" { +// return nil +// } +// listener, handler, err := rpc.StartFastWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) +// if err != nil { +// return err +// } +// n.logger.Info("FastWebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) +// // All listeners booted successfully +// n.wsEndpoint = endpoint +// n.wsListener = listener +// n.wsHandler = handler + +// return nil +// } // stopWS terminates the websocket RPC endpoint. func (n *Node) stopWS() { diff --git a/networks/rpc/endpoints.go b/networks/rpc/endpoints.go index ba0455a679..b633f7477b 100644 --- a/networks/rpc/endpoints.go +++ b/networks/rpc/endpoints.go @@ -54,33 +54,33 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str } // StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules -func StartFastHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := NewServer() - for _, api := range apis { - if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return nil, nil, err - } - logger.Debug("FastHTTP registered", "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp4", endpoint); err != nil { - return nil, nil, err - } - go NewFastHTTPServer(cors, vhosts, timeouts, handler).Serve(listener) - return listener, handler, err -} +// func StartFastHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { +// // Generate the whitelist based on the allowed modules +// whitelist := make(map[string]bool) +// for _, module := range modules { +// whitelist[module] = true +// } +// // Register all the APIs exposed by the services +// handler := NewServer() +// for _, api := range apis { +// if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { +// if err := handler.RegisterName(api.Namespace, api.Service); err != nil { +// return nil, nil, err +// } +// logger.Debug("FastHTTP registered", "namespace", api.Namespace) +// } +// } +// // All APIs registered, start the HTTP listener +// var ( +// listener net.Listener +// err error +// ) +// if listener, err = net.Listen("tcp4", endpoint); err != nil { +// return nil, nil, err +// } +// go NewFastHTTPServer(cors, vhosts, timeouts, handler).Serve(listener) +// return listener, handler, err +// } // StartWSEndpoint starts a websocket endpoint func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { @@ -111,33 +111,33 @@ func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins [] return listener, handler, err } -func StartFastWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { - // Generate the whitelist based on the allowed modules - whitelist := make(map[string]bool) - for _, module := range modules { - whitelist[module] = true - } - // Register all the APIs exposed by the services - handler := NewServer() - for _, api := range apis { - if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { - if err := handler.RegisterName(api.Namespace, api.Service); err != nil { - return nil, nil, err - } - logger.Debug("FastWebSocket registered", "service", api.Service, "namespace", api.Namespace) - } - } - // All APIs registered, start the HTTP listener - var ( - listener net.Listener - err error - ) - if listener, err = net.Listen("tcp4", endpoint); err != nil { - return nil, nil, err - } - go NewFastWSServer(wsOrigins, handler).Serve(listener) - return listener, handler, err -} +// func StartFastWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { +// // Generate the whitelist based on the allowed modules +// whitelist := make(map[string]bool) +// for _, module := range modules { +// whitelist[module] = true +// } +// // Register all the APIs exposed by the services +// handler := NewServer() +// for _, api := range apis { +// if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { +// if err := handler.RegisterName(api.Namespace, api.Service); err != nil { +// return nil, nil, err +// } +// logger.Debug("FastWebSocket registered", "service", api.Service, "namespace", api.Namespace) +// } +// } +// // All APIs registered, start the HTTP listener +// var ( +// listener net.Listener +// err error +// ) +// if listener, err = net.Listen("tcp4", endpoint); err != nil { +// return nil, nil, err +// } +// go NewFastWSServer(wsOrigins, handler).Serve(listener) +// return listener, handler, err +// } // StartIPCEndpoint starts an IPC endpoint. func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) { diff --git a/node/node.go b/node/node.go index a946a51e66..ee60d47eee 100644 --- a/node/node.go +++ b/node/node.go @@ -468,23 +468,23 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors } // startFastHTTP initializes and starts the HTTP RPC endpoint. -func (n *Node) startFastHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error { - // Short circuit if the HTTP endpoint isn't being exposed - if endpoint == "" { - return nil - } - listener, handler, err := rpc.StartFastHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts) - if err != nil { - return err - } - n.logger.Info("FastHTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) - // All listeners booted successfully - n.httpEndpoint = endpoint - n.httpListener = listener - n.httpHandler = handler - - return nil -} +// func (n *Node) startFastHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error { +// // Short circuit if the HTTP endpoint isn't being exposed +// if endpoint == "" { +// return nil +// } +// listener, handler, err := rpc.StartFastHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts) +// if err != nil { +// return err +// } +// n.logger.Info("FastHTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) +// // All listeners booted successfully +// n.httpEndpoint = endpoint +// n.httpListener = listener +// n.httpHandler = handler + +// return nil +// } // stopHTTP terminates the HTTP RPC endpoint. func (n *Node) stopHTTP() { @@ -520,23 +520,23 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig } // startFastWS initializes and starts the websocket RPC endpoint. -func (n *Node) startFastWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { - // Short circuit if the WS endpoint isn't being exposed - if endpoint == "" { - return nil - } - listener, handler, err := rpc.StartFastWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) - if err != nil { - return err - } - n.logger.Info("FastWebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) - // All listeners booted successfully - n.wsEndpoint = endpoint - n.wsListener = listener - n.wsHandler = handler - - return nil -} +// func (n *Node) startFastWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { +// // Short circuit if the WS endpoint isn't being exposed +// if endpoint == "" { +// return nil +// } +// listener, handler, err := rpc.StartFastWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) +// if err != nil { +// return err +// } +// n.logger.Info("FastWebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) +// // All listeners booted successfully +// n.wsEndpoint = endpoint +// n.wsListener = listener +// n.wsHandler = handler + +// return nil +// } // stopWS terminates the websocket RPC endpoint. func (n *Node) stopWS() { From 8866eefeef6328017080bc620ef9be8b2cbddae2 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 19 Dec 2023 16:22:02 +0900 Subject: [PATCH 17/63] [API] Added error log --- storage/database/db_manager.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/storage/database/db_manager.go b/storage/database/db_manager.go index a557972c1c..f43caf553b 100644 --- a/storage/database/db_manager.go +++ b/storage/database/db_manager.go @@ -909,9 +909,12 @@ func (dbm *databaseManager) Stat(property string) (string, error) { func (dbm *databaseManager) Compact(start []byte, limit []byte) error { errs := "" - for _, db := range dbm.dbs { + for idx, db := range dbm.dbs { if db != nil { - db.Compact(start, limit) + if err := db.Compact(start, limit); err != nil { + headInfo := fmt.Sprintf(" [%s:%s]\n", DBEntryType(idx), db.Type()) + errs = headInfo + err.Error() + } } } if errs == "" { From 6303ebcc749d48eed9c9793f6ebda90cde5d3623 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 19 Dec 2023 16:56:47 +0900 Subject: [PATCH 18/63] [SetHead] Remove `deleteSnapshot` param --- blockchain/blockchain.go | 18 ++++++++---------- blockchain/blockchain_sethead_test.go | 8 ++++---- node/cn/api_backend.go | 2 +- node/cn/api_backend_test.go | 2 +- node/cn/backend.go | 2 +- work/mocks/blockchain_mock.go | 8 ++++---- work/work.go | 2 +- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 00f55a6d3b..bdeb585696 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -301,7 +301,7 @@ func NewBlockChain(db database.DBManager, cacheConfig *CacheConfig, chainConfig if diskRoot != (common.Hash{}) { logger.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash(), "snaproot", diskRoot) - snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), diskRoot, true, false) + snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), diskRoot, true) if err != nil { return nil, err } @@ -314,7 +314,7 @@ func NewBlockChain(db database.DBManager, cacheConfig *CacheConfig, chainConfig // Dangling block without a state associated, init from scratch logger.Warn("Head state missing, repairing chain", "number", head.NumberU64(), "hash", head.Hash().String()) - if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true, false); err != nil { + if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true); err != nil { return nil, err } } @@ -327,7 +327,7 @@ func NewBlockChain(db database.DBManager, cacheConfig *CacheConfig, chainConfig // make sure the headerByNumber (if present) is in our current canonical chain if headerByNumber != nil && headerByNumber.Hash() == header.Hash() { logger.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash) - bc.SetHead(header.Number.Uint64()-1, false) + bc.SetHead(header.Number.Uint64() - 1) logger.Error("Chain rewind was successful, resuming normal operation") } } @@ -424,7 +424,7 @@ func (bc *BlockChain) SetCanonicalBlock(blockNum uint64) { // Dangling block without a state associated, init from scratch logger.Warn("Head state missing, repairing chain", "number", head.NumberU64(), "hash", head.Hash().String()) - if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true, false); err != nil { + if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true); err != nil { logger.Error("Repairing chain is failed", "number", head.NumberU64(), "hash", head.Hash().String(), "err", err) return } @@ -513,7 +513,7 @@ func (bc *BlockChain) loadLastState() error { // SetHead rewinds the local chain to a new head with the extra condition // that the rewind must pass the specified state root. The method will try to // delete minimal data from disk whilst retaining chain consistency. -func (bc *BlockChain) SetHead(head uint64, deleteSnapshot bool) error { +func (bc *BlockChain) SetHead(head uint64) error { // With the live pruning enabled, an attempt to SetHead into a state-pruned block number // may result in an infinite loop, trying to find the existing block (probably the genesis block). // If the target `head` is below the surviving block numbers, SetHead early exits with an error. @@ -523,7 +523,7 @@ func (bc *BlockChain) SetHead(head uint64, deleteSnapshot bool) error { lastPruned, head) } } - _, err := bc.setHeadBeyondRoot(head, common.Hash{}, false, deleteSnapshot) + _, err := bc.setHeadBeyondRoot(head, common.Hash{}, false) return err } @@ -535,7 +535,7 @@ func (bc *BlockChain) SetHead(head uint64, deleteSnapshot bool) error { // retaining chain consistency. // // The method returns the block number where the requested root cap was found. -func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair, deleteSnpashot bool) (uint64, error) { +func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bool) (uint64, error) { bc.mu.Lock() defer bc.mu.Unlock() @@ -643,9 +643,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair, d if err := bc.hc.SetHead(head, updateFn, delFn); err != nil { return 0, err } - } - if deleteSnpashot { // Delete istanbul snapshot database further two epochs // Invoked only if the sethead was originated from explicit API call var ( @@ -787,7 +785,7 @@ func (bc *BlockChain) Reset() error { // specified genesis state. func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { // Dump the entire block chain and purge the caches - if err := bc.SetHead(0, false); err != nil { + if err := bc.SetHead(0); err != nil { return err } bc.mu.Lock() diff --git a/blockchain/blockchain_sethead_test.go b/blockchain/blockchain_sethead_test.go index 79d18f339d..bf208d921a 100644 --- a/blockchain/blockchain_sethead_test.go +++ b/blockchain/blockchain_sethead_test.go @@ -155,7 +155,7 @@ func testSetHead(t *testing.T, tt *rewindTest) { } // Set the head of the chain back to the requested number - chain.SetHead(tt.setheadBlock, true) + chain.SetHead(tt.setheadBlock) // Iterate over all the remaining blocks and ensure there are no gaps verifyNoGaps(t, chain, true, canonblocks) @@ -301,11 +301,11 @@ func testSetHeadEarlyExit(t *testing.T, tt *rewindTest) { } db.WriteLastPrunedBlockNumber(tt.setheadBlock + 1) - assert.Error(t, chain.SetHead(tt.setheadBlock, true)) + assert.Error(t, chain.SetHead(tt.setheadBlock)) db.WriteLastPrunedBlockNumber(tt.setheadBlock) - assert.Error(t, chain.SetHead(tt.setheadBlock, true)) + assert.Error(t, chain.SetHead(tt.setheadBlock)) db.WriteLastPrunedBlockNumber(tt.setheadBlock - 1) - assert.Nil(t, chain.SetHead(tt.setheadBlock, true)) + assert.Nil(t, chain.SetHead(tt.setheadBlock)) } diff --git a/node/cn/api_backend.go b/node/cn/api_backend.go index c2b190bb62..6d3375fdfb 100644 --- a/node/cn/api_backend.go +++ b/node/cn/api_backend.go @@ -80,7 +80,7 @@ func (b *CNAPIBackend) CurrentBlock() *types.Block { } func doSetHead(bc work.BlockChain, cn consensus.Engine, gov governance.Engine, targetBlkNum uint64) error { - if err := bc.SetHead(targetBlkNum, true); err != nil { + if err := bc.SetHead(targetBlkNum); err != nil { return err } // Initialize snapshot cache, staking info cache, and governance cache diff --git a/node/cn/api_backend_test.go b/node/cn/api_backend_test.go index d721f9afba..2b9c07830b 100644 --- a/node/cn/api_backend_test.go +++ b/node/cn/api_backend_test.go @@ -182,7 +182,7 @@ func TestCNAPIBackend_SetHead(t *testing.T) { api.cn.governance = testGov() number := uint64(123) - mockBlockChain.EXPECT().SetHead(number, true).Times(1) + mockBlockChain.EXPECT().SetHead(number).Times(1) api.SetHead(number) block := newBlock(int(number)) diff --git a/node/cn/backend.go b/node/cn/backend.go index fef90481b0..70c1c12ae6 100644 --- a/node/cn/backend.go +++ b/node/cn/backend.go @@ -320,7 +320,7 @@ func New(ctx *node.ServiceContext, config *Config) (*CN, error) { // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { logger.Error("Rewinding chain to upgrade configuration", "err", compat) - cn.blockchain.SetHead(compat.RewindTo, false) + cn.blockchain.SetHead(compat.RewindTo) chainDB.WriteChainConfig(genesisHash, cn.chainConfig) } cn.bloomIndexer.Start(cn.blockchain) diff --git a/work/mocks/blockchain_mock.go b/work/mocks/blockchain_mock.go index c4e46fb02c..d834188cc8 100644 --- a/work/mocks/blockchain_mock.go +++ b/work/mocks/blockchain_mock.go @@ -746,17 +746,17 @@ func (mr *MockBlockChainMockRecorder) SaveTrieNodeCacheToDisk() *gomock.Call { } // SetHead mocks base method. -func (m *MockBlockChain) SetHead(arg0 uint64, arg1 bool) error { +func (m *MockBlockChain) SetHead(arg0 uint64) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetHead", arg0, arg1) + ret := m.ctrl.Call(m, "SetHead", arg0) ret0, _ := ret[0].(error) return ret0 } // SetHead indicates an expected call of SetHead. -func (mr *MockBlockChainMockRecorder) SetHead(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockBlockChainMockRecorder) SetHead(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHead", reflect.TypeOf((*MockBlockChain)(nil).SetHead), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHead", reflect.TypeOf((*MockBlockChain)(nil).SetHead), arg0) } // Snapshots mocks base method. diff --git a/work/work.go b/work/work.go index 876e8993ea..59c737a048 100644 --- a/work/work.go +++ b/work/work.go @@ -258,7 +258,7 @@ type BlockChain interface { StateCache() state.Database SubscribeChainEvent(ch chan<- blockchain.ChainEvent) event.Subscription - SetHead(head uint64, delteSnapshot bool) error + SetHead(head uint64) error Stop() SubscribeRemovedLogsEvent(ch chan<- blockchain.RemovedLogsEvent) event.Subscription From bc2d198159ad993d518f8ff6d10c6b29bb05489e Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 20 Dec 2023 11:29:23 +0900 Subject: [PATCH 19/63] [Legacy] Removed useless getters and interface --- api/api_private_debug.go | 6 ------ storage/database/badger_database.go | 4 ---- storage/database/db_manager.go | 5 ----- storage/database/dynamodb.go | 4 ---- storage/database/interface.go | 1 - storage/database/leveldb_database.go | 4 ---- storage/database/memory_database.go | 4 ---- storage/database/rocksdb_database.go | 4 ---- storage/database/sharded_database.go | 8 -------- 9 files changed, 40 deletions(-) diff --git a/api/api_private_debug.go b/api/api_private_debug.go index 9572d3b9cb..099561b3ff 100644 --- a/api/api_private_debug.go +++ b/api/api_private_debug.go @@ -29,7 +29,6 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/klaytn/klaytn/common" "github.com/klaytn/klaytn/networks/rpc" - "github.com/klaytn/klaytn/storage/database" ) // PrivateDebugAPI is the collection of Klaytn APIs exposed over the private @@ -44,11 +43,6 @@ func NewPrivateDebugAPI(b Backend) *PrivateDebugAPI { return &PrivateDebugAPI{b: b} } -// GetDBProperty returns the value of the given property of the given database. -func (api *PrivateDebugAPI) GetDBProperty(dt database.DBEntryType, name string) string { - return api.b.ChainDB().GetProperty(dt, name) -} - // ChaindbProperty returns leveldb properties of the chain database. func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { return api.b.ChainDB().Stat(property) diff --git a/storage/database/badger_database.go b/storage/database/badger_database.go index 4b6cdcdad4..e292abd095 100644 --- a/storage/database/badger_database.go +++ b/storage/database/badger_database.go @@ -203,10 +203,6 @@ func (bg *badgerDB) Meter(prefix string) { logger.Warn("badgerDB does not support metrics!") } -func (bg *badgerDB) GetProperty(name string) string { - return "" -} - func (bg *badgerDB) TryCatchUpWithPrimary() error { return nil } diff --git a/storage/database/db_manager.go b/storage/database/db_manager.go index f43caf553b..db2b33817b 100644 --- a/storage/database/db_manager.go +++ b/storage/database/db_manager.go @@ -69,7 +69,6 @@ type DBManager interface { GetStateTrieMigrationDB() Database GetMiscDB() Database GetSnapshotDB() Database - GetProperty(dt DBEntryType, name string) string // from accessors_chain.go ReadCanonicalHash(number uint64) common.Hash @@ -871,10 +870,6 @@ func (dbm *databaseManager) GetSnapshotDB() Database { return dbm.getDatabase(SnapshotDB) } -func (dbm *databaseManager) GetProperty(dt DBEntryType, name string) string { - return dbm.getDatabase(dt).GetProperty(name) -} - func (dbm *databaseManager) TryCatchUpWithPrimary() error { for _, db := range dbm.dbs { if db != nil { diff --git a/storage/database/dynamodb.go b/storage/database/dynamodb.go index c613c16719..684e34c094 100644 --- a/storage/database/dynamodb.go +++ b/storage/database/dynamodb.go @@ -446,10 +446,6 @@ func (dynamo *dynamoDB) Meter(prefix string) { dynamoBatchWriteTimeMeter = metrics.NewRegisteredMeter(prefix+"batchwrite/time", nil) } -func (dynamo *dynamoDB) GetProperty(name string) string { - return "" -} - func (dynamo *dynamoDB) TryCatchUpWithPrimary() error { return nil } diff --git a/storage/database/interface.go b/storage/database/interface.go index 340d008602..f059da7f17 100644 --- a/storage/database/interface.go +++ b/storage/database/interface.go @@ -100,7 +100,6 @@ type Database interface { Meter(prefix string) Iteratee - GetProperty(name string) string TryCatchUpWithPrimary() error } diff --git a/storage/database/leveldb_database.go b/storage/database/leveldb_database.go index f908fbdbbb..9fae992b55 100644 --- a/storage/database/leveldb_database.go +++ b/storage/database/leveldb_database.go @@ -514,10 +514,6 @@ func (db *levelDB) updateLevelStats(s *leveldb.DBStats, lv int) { db.levelDurationsGauge[lv].Update(int64(s.LevelDurations[lv])) } -func (db *levelDB) GetProperty(name string) string { - return db.GetProperty(name) -} - func (db *levelDB) TryCatchUpWithPrimary() error { return nil } diff --git a/storage/database/memory_database.go b/storage/database/memory_database.go index 2bca8c46a7..6e3e197c0c 100644 --- a/storage/database/memory_database.go +++ b/storage/database/memory_database.go @@ -194,10 +194,6 @@ func (db *MemDB) Meter(prefix string) { logger.Warn("MemDB does not support metrics!") } -func (db *MemDB) GetProperty(name string) string { - return "" -} - func (db *MemDB) TryCatchUpWithPrimary() error { return nil } diff --git a/storage/database/rocksdb_database.go b/storage/database/rocksdb_database.go index 34af4aca0c..abb17f9a25 100644 --- a/storage/database/rocksdb_database.go +++ b/storage/database/rocksdb_database.go @@ -216,10 +216,6 @@ func (db *rocksDB) Delete(key []byte) error { return db.db.Delete(db.wo, key) } -func (db *rocksDB) GetProperty(name string) string { - return db.db.GetProperty(name) -} - func (db *rocksDB) TryCatchUpWithPrimary() error { return db.db.TryCatchUpWithPrimary() } diff --git a/storage/database/sharded_database.go b/storage/database/sharded_database.go index 9228e46f68..e2b0fe0541 100644 --- a/storage/database/sharded_database.go +++ b/storage/database/sharded_database.go @@ -441,14 +441,6 @@ func (db *shardedDB) Meter(prefix string) { } } -func (db *shardedDB) GetProperty(name string) string { - var buf bytes.Buffer - for index, shard := range db.shards { - buf.WriteString(fmt.Sprintf("shard %d: %s\n", index, shard.GetProperty(name))) - } - return buf.String() -} - func (db *shardedDB) TryCatchUpWithPrimary() error { for _, shard := range db.shards { if err := shard.TryCatchUpWithPrimary(); err != nil { From fb7a85d56cdef6ab767b08556d6ef41553db9977 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 20 Dec 2023 11:31:35 +0900 Subject: [PATCH 20/63] [API] Replaced with `debug.chaindbProperty` --- console/web3ext/web3ext.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/console/web3ext/web3ext.go b/console/web3ext/web3ext.go index 25da6b2241..041971e542 100644 --- a/console/web3ext/web3ext.go +++ b/console/web3ext/web3ext.go @@ -600,12 +600,6 @@ web3._extend({ call: 'debug_startCollectingTrieStats', params: 1, }), - new web3._extend.Method({ - name: 'getDBProperty', - call: 'debug_getDBProperty', - params: 2, - outputFormatter: console.log - }), new web3._extend.Method({ name: 'chaindbProperty', call: 'debug_chaindbProperty', From 232e0e59241beb50618c952d84f3754c4864de1c Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 20 Dec 2023 15:28:30 +0800 Subject: [PATCH 21/63] print a long when datadog api trace is enabled --- networks/rpc/http_datadog.go | 1 + 1 file changed, 1 insertion(+) diff --git a/networks/rpc/http_datadog.go b/networks/rpc/http_datadog.go index 1bfc971c72..d56ac3514f 100644 --- a/networks/rpc/http_datadog.go +++ b/networks/rpc/http_datadog.go @@ -57,6 +57,7 @@ func newDatadogTracer() *DatadogTracer { } tracer.Start() + logger.Info("Datadog APM trace is enabled", "serviceName", service) return &DatadogTracer{tags, service, klaytnResponse} } From 020fde4ceb70740ebf3c9cdeb7480943410ea685 Mon Sep 17 00:00:00 2001 From: sjnam Date: Thu, 21 Dec 2023 08:54:20 +0900 Subject: [PATCH 22/63] change file name; IPCOnly --- api/backend.go | 10 +++++----- networks/rpc/endpoints.go | 4 ++-- networks/rpc/types.go | 10 +++++----- node/node.go | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/backend.go b/api/backend.go index 0f389f5a90..1c86c65928 100644 --- a/api/backend.go +++ b/api/backend.go @@ -151,11 +151,11 @@ func GetAPIs(apiBackend Backend, disableUnsafeDebug bool) ([]rpc.API, *EthereumA Service: NewPrivateAccountAPI(apiBackend, nonceLock), Public: false, }, { - Namespace: "debug", - Version: "1.0", - Service: NewPrivateDebugAPI(apiBackend), - Public: false, - DisableUnsafeDebug: disableUnsafeDebug, + Namespace: "debug", + Version: "1.0", + Service: NewPrivateDebugAPI(apiBackend), + Public: false, + IPCOnly: disableUnsafeDebug, }, } diff --git a/networks/rpc/endpoints.go b/networks/rpc/endpoints.go index b633f7477b..3491561aca 100644 --- a/networks/rpc/endpoints.go +++ b/networks/rpc/endpoints.go @@ -34,7 +34,7 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str // Register all the APIs exposed by the services handler := NewServer() for _, api := range apis { - if !api.DisableUnsafeDebug && (whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public)) { + if !api.IPCOnly && (whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public)) { if err := handler.RegisterName(api.Namespace, api.Service); err != nil { return nil, nil, err } @@ -92,7 +92,7 @@ func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins [] // Register all the APIs exposed by the services handler := NewServer() for _, api := range apis { - if !api.DisableUnsafeDebug && (exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public)) { + if !api.IPCOnly && (exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public)) { if err := handler.RegisterName(api.Namespace, api.Service); err != nil { return nil, nil, err } diff --git a/networks/rpc/types.go b/networks/rpc/types.go index 800e5ab19b..5a7400d914 100644 --- a/networks/rpc/types.go +++ b/networks/rpc/types.go @@ -33,11 +33,11 @@ import ( // API describes the set of methods offered over the RPC interface type API struct { - Namespace string // namespace under which the rpc methods of Service are exposed - Version string // api version for DApp's - Service interface{} // receiver instance which holds the methods - Public bool // indication if the methods must be considered safe for public use - DisableUnsafeDebug bool // disable unsafe debug APIs + Namespace string // namespace under which the rpc methods of Service are exposed + Version string // api version for DApp's + Service interface{} // receiver instance which holds the methods + Public bool // indication if the methods must be considered safe for public use + IPCOnly bool // only accessible to IPC } // Error wraps RPC errors, which contain an error code in addition to the message. diff --git a/node/node.go b/node/node.go index ee60d47eee..e34e5dcdb0 100644 --- a/node/node.go +++ b/node/node.go @@ -785,10 +785,10 @@ func (n *Node) apis() []rpc.API { Service: NewPublicKlayAPI(n), Public: true, }, { - Namespace: "debug", - Version: "1.0", - Service: debug.Handler, - DisableUnsafeDebug: n.config.DisableUnsafeDebug, + Namespace: "debug", + Version: "1.0", + Service: debug.Handler, + IPCOnly: n.config.DisableUnsafeDebug, }, } From e87e3b97fdd93e5fbd9a593bcd7d2c13c02f78e6 Mon Sep 17 00:00:00 2001 From: sjnam Date: Thu, 21 Dec 2023 08:54:46 +0900 Subject: [PATCH 23/63] unsafe tracer API --- node/cn/backend.go | 33 +++++++++-------- node/cn/tracers/api.go | 80 ++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 58 deletions(-) diff --git a/node/cn/backend.go b/node/cn/backend.go index 70c1c12ae6..2223332144 100644 --- a/node/cn/backend.go +++ b/node/cn/backend.go @@ -535,23 +535,8 @@ func (s *CN) APIs() []rpc.API { ethAPI.SetGovernanceKlayAPI(governanceKlayAPI) ethAPI.SetGovernanceAPI(governanceAPI) - var tracerAPI *tracers.API - if s.config.DisableUnsafeDebug { - tracerAPI = tracers.NewAPIUnsafeDisabled(s.APIBackend) - } else { - tracerAPI = tracers.NewAPI(s.APIBackend) - apis = append(apis, []rpc.API{ - { - Namespace: "debug", - Version: "1.0", - Service: NewPrivateDebugAPI(s.chainConfig, s), - Public: false, - }, - }...) - } - // Append all the local APIs and return - return append(apis, []rpc.API{ + apis = append(apis, []rpc.API{ { Namespace: "klay", Version: "1.0", @@ -588,8 +573,14 @@ func (s *CN) APIs() []rpc.API { }, { Namespace: "debug", Version: "1.0", - Service: tracerAPI, + Service: tracers.NewAPI(s.APIBackend), + Public: false, + }, { + Namespace: "debug", + Version: "1.0", + Service: tracers.NewUnsafeAPI(s.APIBackend), Public: false, + IPCOnly: true, }, { Namespace: "net", Version: "1.0", @@ -610,8 +601,16 @@ func (s *CN) APIs() []rpc.API { Version: "1.0", Service: ethAPI, Public: true, + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPrivateDebugAPI(s.chainConfig, s), + Public: false, + IPCOnly: s.config.DisableUnsafeDebug, }, }...) + + return apis } func (s *CN) ResetWithGenesisBlock(gb *types.Block) { diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index fd53309201..851edf0051 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -98,22 +98,36 @@ type Backend interface { StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (blockchain.Message, vm.BlockContext, vm.TxContext, *state.StateDB, error) } -// API is the collection of tracing APIs exposed over the private debugging endpoint. -type API struct { +// CommonAPI is the collection of tracing APIs exposed over the private debugging endpoint. +type CommonAPI struct { backend Backend unsafeTrace bool } -// NewAPIUnsafeDisabled creates a new API definition for the tracing methods of the CN service, +// API is the safe APIs +type API struct { + CommonAPI +} + +// UNsafeAPI is the unsafe APIs +type UnsafeAPI struct { + CommonAPI +} + +// NewUnsafeAPI creates a new API definition for the tracing methods of the CN service, // only allowing predefined tracers. -func NewAPIUnsafeDisabled(backend Backend) *API { - return &API{backend: backend, unsafeTrace: false} +func NewUnsafeAPI(backend Backend) *UnsafeAPI { + return &UnsafeAPI{ + CommonAPI{backend: backend, unsafeTrace: false}, + } } // NewAPI creates a new API definition for the tracing methods of the CN service, // allowing both predefined tracers and Javascript snippet based tracing. func NewAPI(backend Backend) *API { - return &API{backend: backend, unsafeTrace: true} + return &API{ + CommonAPI{backend: backend, unsafeTrace: true}, + } } type chainContext struct { @@ -148,19 +162,19 @@ func newChainContext(ctx context.Context, backend Backend) blockchain.ChainConte // blockByNumber is the wrapper of the chain access function offered by the backend. // It will return an error if the block is not found. -func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { +func (api *CommonAPI) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { return api.backend.BlockByNumber(ctx, number) } // blockByHash is the wrapper of the chain access function offered by the backend. // It will return an error if the block is not found. -func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { +func (api *CommonAPI) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { return api.backend.BlockByHash(ctx, hash) } // blockByNumberAndHash is the wrapper of the chain access function offered by // the backend. It will return an error if the block is not found. -func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) { +func (api *CommonAPI) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) { block, err := api.blockByNumber(ctx, number) if err != nil { return nil, err @@ -218,7 +232,7 @@ type txTraceTask struct { index int // Transaction offset in the block } -func checkRangeAndReturnBlock(api *API, ctx context.Context, start, end rpc.BlockNumber) (*types.Block, *types.Block, error) { +func checkRangeAndReturnBlock(api *CommonAPI, ctx context.Context, start, end rpc.BlockNumber) (*types.Block, *types.Block, error) { // Fetch the block interval that we want to trace from, err := api.blockByNumber(ctx, start) if err != nil { @@ -244,11 +258,8 @@ func checkRangeAndReturnBlock(api *API, ctx context.Context, start, end rpc.Bloc // TraceChain returns the structured logs created during the execution of EVM // between two blocks (excluding start) and returns them as a JSON object. -func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { - if !api.unsafeTrace { - return nil, errors.New("TraceChain is disabled") - } - from, to, err := checkRangeAndReturnBlock(api, ctx, start, end) +func (api *UnsafeAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { + from, to, err := checkRangeAndReturnBlock(&api.CommonAPI, ctx, start, end) if err != nil { return nil, err } @@ -257,8 +268,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf if !supported { return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported } - var sub *rpc.Subscription - sub = notifier.CreateSubscription() + sub := notifier.CreateSubscription() _, err = api.traceChain(from, to, config, notifier, sub) return sub, err } @@ -268,7 +278,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf // The traceChain operates in two modes: subscription mode and rpc mode // - if notifier and sub is not nil, it works as a subscription mode and returns nothing // - if those parameters are nil, it works as a rpc mode and returns the block trace results, so it can pass the result through rpc-call -func (api *API) traceChain(start, end *types.Block, config *TraceConfig, notifier *rpc.Notifier, sub *rpc.Subscription) (map[uint64]*blockTraceResult, error) { +func (api *CommonAPI) traceChain(start, end *types.Block, config *TraceConfig, notifier *rpc.Notifier, sub *rpc.Subscription) (map[uint64]*blockTraceResult, error) { // Prepare all the states for tracing. Note this procedure can take very // long time. Timeout mechanism is necessary. reexec := defaultTraceReexec @@ -480,13 +490,10 @@ func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, // TraceBlockByNumberRange returns the ranged blocks tracing results // TODO-tracer: limit the result by the size of the return -func (api *API) TraceBlockByNumberRange(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (map[uint64]*blockTraceResult, error) { - if !api.unsafeTrace { - return nil, fmt.Errorf("TraceBlockByNumberRange is disabled") - } +func (api *UnsafeAPI) TraceBlockByNumberRange(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (map[uint64]*blockTraceResult, error) { // When the block range is [start,end], the actual tracing block would be [start+1,end] // this is the reason why we change the block range to [start-1, end] so that we can trace [start,end] blocks - from, to, err := checkRangeAndReturnBlock(api, ctx, start-1, end) + from, to, err := checkRangeAndReturnBlock(&api.CommonAPI, ctx, start-1, end) if err != nil { return nil, err } @@ -505,7 +512,7 @@ func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config * // TraceBlock returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) { +func (api *CommonAPI) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) { block := new(types.Block) if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { return nil, fmt.Errorf("could not decode block: %v", err) @@ -515,10 +522,7 @@ func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *Trac // TraceBlockFromFile returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { - if !api.unsafeTrace { - return nil, errors.New("TraceBlockFromFile is disabled") - } +func (api *UnsafeAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { blob, err := os.ReadFile(file) if err != nil { return nil, fmt.Errorf("could not read file: %v", err) @@ -545,10 +549,7 @@ func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *Tra // StandardTraceBlockToFile dumps the structured logs created during the // execution of EVM to the local file system and returns a list of files // to the caller. -func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { - if !api.unsafeTrace { - return nil, errors.New("StandardTraceBlockToFile is disabled") - } +func (api *UnsafeAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { block, err := api.blockByHash(ctx, hash) if err != nil { return nil, fmt.Errorf("block %#x not found", hash) @@ -559,10 +560,7 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, // StandardTraceBadBlockToFile dumps the structured logs created during the // execution of EVM against a block pulled from the pool of bad ones to the // local file system and returns a list of files to the caller. -func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { - if !api.unsafeTrace { - return nil, errors.New("StandardTraceBadBlockToFile is disabled") - } +func (api *UnsafeAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { blocks, err := api.backend.ChainDB().ReadAllBadBlocks() if err != nil { return nil, err @@ -578,7 +576,7 @@ func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Has // traceBlock configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item // per transaction, dependent on the requestd tracer. -func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { +func (api *CommonAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { if !api.unsafeTrace { if atomic.LoadInt32(&heavyAPIRequestCount) >= HeavyAPIRequestLimit { return nil, fmt.Errorf("heavy debug api requests exceed the limit: %d", int64(HeavyAPIRequestLimit)) @@ -678,7 +676,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac // standardTraceBlockToFile configures a new tracer which uses standard JSON output, // and traces either a full block or an individual transaction. The return value will // be one filename per transaction traced. -func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { +func (api *CommonAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { // If we're tracing a single transaction, make sure it's present if config != nil && !common.EmptyHash(config.TxHash) { if !containsTx(block, config.TxHash) { @@ -787,7 +785,7 @@ func containsTx(block *types.Block, hash common.Hash) bool { // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { +func (api *CommonAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { if !api.unsafeTrace { if atomic.LoadInt32(&heavyAPIRequestCount) >= HeavyAPIRequestLimit { return nil, fmt.Errorf("heavy debug api requests exceed the limit: %d", int64(HeavyAPIRequestLimit)) @@ -823,7 +821,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * // TraceCall lets you trace a given klay_call. It collects the structured logs // created during the execution of EVM if the given transaction was added on // top of the provided block and returns them as a JSON object. -func (api *API) TraceCall(ctx context.Context, args klaytnapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { +func (api *CommonAPI) TraceCall(ctx context.Context, args klaytnapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { if !api.unsafeTrace { if atomic.LoadInt32(&heavyAPIRequestCount) >= HeavyAPIRequestLimit { return nil, fmt.Errorf("heavy debug api requests exceed the limit: %d", int64(HeavyAPIRequestLimit)) @@ -886,7 +884,7 @@ func (api *API) TraceCall(ctx context.Context, args klaytnapi.CallArgs, blockNrO // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, message blockchain.Message, blockCtx vm.BlockContext, txCtx vm.TxContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *CommonAPI) traceTx(ctx context.Context, message blockchain.Message, blockCtx vm.BlockContext, txCtx vm.TxContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( tracer vm.Tracer From 257bd69259a6f048b773d6ea66303ab83f544784 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Thu, 21 Dec 2023 11:25:05 +0900 Subject: [PATCH 24/63] [SC] Log-level down of Anchoring tx decode failure --- node/sc/main_event_handler.go | 2 +- node/sc/sub_bridge_handler.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/sc/main_event_handler.go b/node/sc/main_event_handler.go index 3607350b25..c9cee873af 100644 --- a/node/sc/main_event_handler.go +++ b/node/sc/main_event_handler.go @@ -89,7 +89,7 @@ func (mce *MainChainEventHandler) decodeAndWriteAnchoringTx(tx *types.Transactio } decodedData, err := types.DecodeAnchoringData(data) if err != nil { - logger.Error("failed to decode anchoring tx", "txHash", tx.Hash().String(), "err", err) + logger.Warn("failed to decode anchoring tx", "txHash", tx.Hash().String(), "err", err) return } mce.mainbridge.chainDB.WriteChildChainTxHash(decodedData.GetBlockHash(), tx.Hash()) diff --git a/node/sc/sub_bridge_handler.go b/node/sc/sub_bridge_handler.go index 015ca3049c..28b9728ecd 100644 --- a/node/sc/sub_bridge_handler.go +++ b/node/sc/sub_bridge_handler.go @@ -443,7 +443,7 @@ func (sbh *SubBridgeHandler) writeServiceChainTxReceipts(bc *blockchain.BlockCha } decodedData, err := types.DecodeAnchoringData(data) if err != nil { - logger.Error("failed to decode anchoring tx", "txHash", txHash.String(), "err", err) + logger.Warn("failed to decode anchoring tx", "txHash", txHash.String(), "err", err) continue } sbh.WriteReceiptFromParentChain(decodedData.GetBlockHash(), (*types.Receipt)(receipt)) From 53f2b8be2c3aced4c7bc2ac35bd33b4693e7646b Mon Sep 17 00:00:00 2001 From: sjnam Date: Fri, 22 Dec 2023 10:06:10 +0900 Subject: [PATCH 25/63] allow unsafe APIs via RPC --- node/cn/backend.go | 4 ++-- node/cn/tracers/api.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/cn/backend.go b/node/cn/backend.go index 2223332144..de20cfbbe7 100644 --- a/node/cn/backend.go +++ b/node/cn/backend.go @@ -578,9 +578,9 @@ func (s *CN) APIs() []rpc.API { }, { Namespace: "debug", Version: "1.0", - Service: tracers.NewUnsafeAPI(s.APIBackend), + Service: tracers.NewUnsafeAPI(s.APIBackend, !s.config.DisableUnsafeDebug), Public: false, - IPCOnly: true, + IPCOnly: s.config.DisableUnsafeDebug, }, { Namespace: "net", Version: "1.0", diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index 851edf0051..fbd8fb069e 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -116,9 +116,9 @@ type UnsafeAPI struct { // NewUnsafeAPI creates a new API definition for the tracing methods of the CN service, // only allowing predefined tracers. -func NewUnsafeAPI(backend Backend) *UnsafeAPI { +func NewUnsafeAPI(backend Backend, unsafeTrace bool) *UnsafeAPI { return &UnsafeAPI{ - CommonAPI{backend: backend, unsafeTrace: false}, + CommonAPI{backend: backend, unsafeTrace: unsafeTrace}, } } From 521f8cad21441204b0c2dd12cd426a20cc093c0f Mon Sep 17 00:00:00 2001 From: sjnam Date: Tue, 26 Dec 2023 21:39:29 +0900 Subject: [PATCH 26/63] fix bug --- node/cn/backend.go | 4 ++-- node/cn/tracers/api.go | 4 ++-- node/cn/tracers/api_test.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/node/cn/backend.go b/node/cn/backend.go index de20cfbbe7..65e7cbacc9 100644 --- a/node/cn/backend.go +++ b/node/cn/backend.go @@ -573,12 +573,12 @@ func (s *CN) APIs() []rpc.API { }, { Namespace: "debug", Version: "1.0", - Service: tracers.NewAPI(s.APIBackend), + Service: tracers.NewAPI(s.APIBackend, !s.config.DisableUnsafeDebug), Public: false, }, { Namespace: "debug", Version: "1.0", - Service: tracers.NewUnsafeAPI(s.APIBackend, !s.config.DisableUnsafeDebug), + Service: tracers.NewUnsafeAPI(s.APIBackend, true), Public: false, IPCOnly: s.config.DisableUnsafeDebug, }, { diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index fbd8fb069e..7740322840 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -124,9 +124,9 @@ func NewUnsafeAPI(backend Backend, unsafeTrace bool) *UnsafeAPI { // NewAPI creates a new API definition for the tracing methods of the CN service, // allowing both predefined tracers and Javascript snippet based tracing. -func NewAPI(backend Backend) *API { +func NewAPI(backend Backend, unsafeTrace bool) *API { return &API{ - CommonAPI{backend: backend, unsafeTrace: true}, + CommonAPI{backend: backend, unsafeTrace: unsafeTrace}, } } diff --git a/node/cn/tracers/api_test.go b/node/cn/tracers/api_test.go index 555d9d8058..5abd68c3e6 100644 --- a/node/cn/tracers/api_test.go +++ b/node/cn/tracers/api_test.go @@ -201,7 +201,7 @@ func TestTraceCall(t *testing.T) { tx, err := types.SignTx(types.NewTransaction(uint64(i), accounts[0].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[1].key) assert.NoError(t, err) b.AddTx(tx) - })) + }), true) testSuite := []struct { blockNumber rpc.BlockNumber @@ -311,7 +311,7 @@ func TestTraceTransaction(t *testing.T) { tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil), signer, accounts[0].key) b.AddTx(tx) target = tx.Hash() - })) + }), true) result, err := api.TraceTransaction(context.Background(), target, nil) if err != nil { t.Errorf("Failed to trace transaction %v", err) @@ -344,7 +344,7 @@ func TestTraceBlock(t *testing.T) { // fee: 0 peb tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) b.AddTx(tx) - })) + }), true) testSuite := []struct { blockNumber rpc.BlockNumber From 01b8ac812628104e91e6f48aedfeea7063ea162d Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 27 Dec 2023 15:15:59 +0900 Subject: [PATCH 27/63] [Snapshot] Added shortcut and function description --- consensus/istanbul/backend/backend.go | 2 +- consensus/istanbul/backend/engine.go | 30 +++++++++++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index bede7bf4bf..79ea8ccae3 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -144,7 +144,7 @@ type backend struct { // Node type nodetype common.ConnType - isRestoring atomic.Bool + isRestoringSnapshots atomic.Bool } func (sb *backend) NodeType() common.ConnType { diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 07d4463671..d241cea40c 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -921,26 +921,38 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com logger.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) } - sb.regen(chain, headers) - sb.recents.Add(snap.Hash, snap) return snap, err } // regen commits snapshot data to database +// If the last header number is larger than the last snapshot number, regenerate handler gets triggered. +// Triggered: +// | ^ ^ ^ ^ ...| +// SI SI*(last snapshot) SI SI +// | header1, .. headerN | +// Not triggered: (Guaranteed SI* was committed before ) +// | ^ ^ ^ ^ ...| +// SI SI*(last snapshot) SI SI +// | header1, .. headerN | func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { - if !sb.isRestoring.Load() && len(headers) > 1 { - sb.isRestoring.Store(true) - defer func() { - sb.isRestoring.Store(false) - }() - + if !sb.isRestoringSnapshots.Load() && len(headers) > 1 { var ( from = headers[0].Number to = headers[len(headers)-1].Number start = time.Now() commitTried = false ) + + // Shortcut: No missing snaoshot data to be processed. + if to.Uint64()-(to.Uint64()%uint64(params.CheckpointInterval)) < from.Uint64() { + return + } + + sb.isRestoringSnapshots.Store(true) + defer func() { + sb.isRestoringSnapshots.Store(false) + }() for _, header := range headers { var ( hn = header.Number.Uint64() @@ -962,7 +974,7 @@ func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { } } if commitTried { // This prevents push too many logs by potential DoS attack - logger.Trace("[Snapshot] Snapshot restoring completed", "len(headers)", len(headers), "from", from, "to", to, "end", time.Since(start)) + logger.Trace("[Snapshot] Snapshot restoring completed", "len(headers)", len(headers), "from", from, "to", to, "elapsed", time.Since(start)) } } } From d996ded84c22c3e3f73d51e078aa5dbf3d14451c Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 27 Dec 2023 15:19:32 +0900 Subject: [PATCH 28/63] [Snapshot] Mistakenly deleted at previous commit. Recovered --- consensus/istanbul/backend/engine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index d241cea40c..8f135a7486 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -921,6 +921,8 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com logger.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) } + sb.regen(chain, headers) + sb.recents.Add(snap.Hash, snap) return snap, err } From f94f128d1c18781c8ec966d15d346ddf349d65f2 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 27 Dec 2023 15:33:58 +0900 Subject: [PATCH 29/63] [Snapshot] Refactored --- consensus/istanbul/backend/engine.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 8f135a7486..2bc5e2349a 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -940,14 +940,14 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { if !sb.isRestoringSnapshots.Load() && len(headers) > 1 { var ( - from = headers[0].Number - to = headers[len(headers)-1].Number + from = headers[0].Number.Uint64() + to = headers[len(headers)-1].Number.Uint64() start = time.Now() commitTried = false ) - // Shortcut: No missing snaoshot data to be processed. - if to.Uint64()-(to.Uint64()%uint64(params.CheckpointInterval)) < from.Uint64() { + // Shortcut: No missing snapshot data to be processed. + if to-(to%uint64(params.CheckpointInterval)) < from { return } From 1a967d6215e50cceafe1a48cc306ea2acd26117d Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 27 Dec 2023 15:50:08 +0900 Subject: [PATCH 30/63] [Linter] Chnaged to multiline comment --- consensus/istanbul/backend/engine.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 2bc5e2349a..2a82063887 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -929,14 +929,15 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com // regen commits snapshot data to database // If the last header number is larger than the last snapshot number, regenerate handler gets triggered. -// Triggered: -// | ^ ^ ^ ^ ...| -// SI SI*(last snapshot) SI SI -// | header1, .. headerN | -// Not triggered: (Guaranteed SI* was committed before ) -// | ^ ^ ^ ^ ...| -// SI SI*(last snapshot) SI SI -// | header1, .. headerN | +/* + Triggered: + | ^ ^ ^ ^ ...| + SI SI*(last snapshot) SI SI + | header1, .. headerN | + Not triggered: (Guaranteed SI* was committed before ) + | ^ ^ ^ ^ ...| + SI SI*(last snapshot) SI SI +*/ func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { if !sb.isRestoringSnapshots.Load() && len(headers) > 1 { var ( From 520e11225066d1de233e69a2921d3ba6989d8ace Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Thu, 28 Dec 2023 09:01:36 +0900 Subject: [PATCH 31/63] [Snapshot] Description updated --- consensus/istanbul/backend/engine.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 2a82063887..e745fb52e2 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -928,7 +928,8 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com } // regen commits snapshot data to database -// If the last header number is larger than the last snapshot number, regenerate handler gets triggered. +// regen is triggered if there is any checkpoint block in the `headers`. +// For each checkpoint block, this function verifies the existence of its snapshot in DB and stores one if missing. /* Triggered: | ^ ^ ^ ^ ...| @@ -937,6 +938,7 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com Not triggered: (Guaranteed SI* was committed before ) | ^ ^ ^ ^ ...| SI SI*(last snapshot) SI SI + | header1, .. headerN | */ func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { if !sb.isRestoringSnapshots.Load() && len(headers) > 1 { @@ -976,7 +978,7 @@ func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { } } } - if commitTried { // This prevents push too many logs by potential DoS attack + if commitTried { // This prevents pushing too many logs by potential DoS attack logger.Trace("[Snapshot] Snapshot restoring completed", "len(headers)", len(headers), "from", from, "to", to, "elapsed", time.Since(start)) } } From 95eed1738f59115563a36d1e3642308cb5befe3a Mon Sep 17 00:00:00 2001 From: sjnam Date: Tue, 2 Jan 2024 10:51:26 +0900 Subject: [PATCH 32/63] remove tests for unused methods --- networks/rpc/websocket_test.go | 70 ---------------------------------- 1 file changed, 70 deletions(-) diff --git a/networks/rpc/websocket_test.go b/networks/rpc/websocket_test.go index f8ac10e8d4..f7423c403c 100644 --- a/networks/rpc/websocket_test.go +++ b/networks/rpc/websocket_test.go @@ -81,76 +81,6 @@ func TestWebsocketLargeCall(t *testing.T) { assert.Error(t, client.Call(&result, method, arg, 1), "no error for too large call") } -// func newTestListener() net.Listener { -// ln, err := net.Listen("tcp", "localhost:0") -// if err != nil { -// panic(err) -// } -// return ln -// } - -// func TestWSServer_MaxConnections(t *testing.T) { -// // create server -// var ( -// srv = newTestServer("service", new(Service)) -// ln = newTestListener() -// ) -// defer srv.Stop() -// defer ln.Close() - -// go NewWSServer([]string{"*"}, srv).Serve(ln) -// time.Sleep(100 * time.Millisecond) - -// // set max websocket connections -// MaxWebsocketConnections = 3 -// testWebsocketMaxConnections(t, "ws://"+ln.Addr().String(), int(MaxWebsocketConnections)) -// } - -// func TestFastWSServer_MaxConnections(t *testing.T) { -// // create server -// var ( -// srv = newTestServer("service", new(Service)) -// ln = newTestListener() -// ) -// defer srv.Stop() -// defer ln.Close() - -// go NewFastWSServer([]string{"*"}, srv).Serve(ln) -// time.Sleep(100 * time.Millisecond) - -// // set max websocket connections -// MaxWebsocketConnections = 3 -// testWebsocketMaxConnections(t, "ws://"+ln.Addr().String(), int(MaxWebsocketConnections)) -// } - -// func testWebsocketMaxConnections(t *testing.T, addr string, maxConnections int) { -// var closers []*Client - -// for i := 0; i <= maxConnections; i++ { -// client, err := DialWebsocket(context.Background(), addr, "") -// if err != nil { -// t.Fatal(err) -// } -// closers = append(closers, client) - -// var result echoResult -// method := "service_echo" -// arg := strings.Repeat("x", i) -// err = client.Call(&result, method, arg, 1) -// if i < int(MaxWebsocketConnections) { -// assert.NoError(t, err) -// assert.Equal(t, arg, result.String, "wrong string echoed") -// } else { -// assert.Error(t, err) -// // assert.Equal(t, "EOF", err.Error()) -// } -// } - -// for _, client := range closers { -// client.Close() -// } -// } - func TestWebsocketClientHeaders(t *testing.T) { t.Parallel() From 8cd830a3e5a16cb0c4c5fa759eebd85c0b035cb1 Mon Sep 17 00:00:00 2001 From: sjnam Date: Tue, 2 Jan 2024 16:46:35 +0900 Subject: [PATCH 33/63] delete unused methods --- cmd/kbn/node.go | 38 ----------------------- networks/rpc/endpoints.go | 57 ---------------------------------- networks/rpc/websocket_test.go | 19 ------------ node/node.go | 38 ----------------------- 4 files changed, 152 deletions(-) diff --git a/cmd/kbn/node.go b/cmd/kbn/node.go index 53c40e95d6..5f8b3f71f7 100644 --- a/cmd/kbn/node.go +++ b/cmd/kbn/node.go @@ -231,25 +231,6 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors return nil } -// startFastHTTP initializes and starts the HTTP RPC endpoint. -// func (n *Node) startFastHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error { -// // Short circuit if the HTTP endpoint isn't being exposed -// if endpoint == "" { -// return nil -// } -// listener, handler, err := rpc.StartFastHTTPEndpoint(endpoint, apis, modules, cors, vhosts, n.config.HTTPTimeouts) -// if err != nil { -// return err -// } -// n.logger.Info("FastHTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) -// // All listeners booted successfully -// n.httpEndpoint = endpoint -// n.httpListener = listener -// n.httpHandler = handler - -// return nil -// } - // stopHTTP terminates the HTTP RPC endpoint. func (n *Node) stopHTTP() { if n.httpListener != nil { @@ -283,25 +264,6 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig return nil } -// startFastWS initializes and starts the websocket RPC endpoint. -// func (n *Node) startFastWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { -// // Short circuit if the WS endpoint isn't being exposed -// if endpoint == "" { -// return nil -// } -// listener, handler, err := rpc.StartFastWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) -// if err != nil { -// return err -// } -// n.logger.Info("FastWebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) -// // All listeners booted successfully -// n.wsEndpoint = endpoint -// n.wsListener = listener -// n.wsHandler = handler - -// return nil -// } - // stopWS terminates the websocket RPC endpoint. func (n *Node) stopWS() { if n.wsListener != nil { diff --git a/networks/rpc/endpoints.go b/networks/rpc/endpoints.go index 3491561aca..59b51d3ad7 100644 --- a/networks/rpc/endpoints.go +++ b/networks/rpc/endpoints.go @@ -53,35 +53,6 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str return listener, handler, err } -// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules -// func StartFastHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { -// // Generate the whitelist based on the allowed modules -// whitelist := make(map[string]bool) -// for _, module := range modules { -// whitelist[module] = true -// } -// // Register all the APIs exposed by the services -// handler := NewServer() -// for _, api := range apis { -// if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { -// if err := handler.RegisterName(api.Namespace, api.Service); err != nil { -// return nil, nil, err -// } -// logger.Debug("FastHTTP registered", "namespace", api.Namespace) -// } -// } -// // All APIs registered, start the HTTP listener -// var ( -// listener net.Listener -// err error -// ) -// if listener, err = net.Listen("tcp4", endpoint); err != nil { -// return nil, nil, err -// } -// go NewFastHTTPServer(cors, vhosts, timeouts, handler).Serve(listener) -// return listener, handler, err -// } - // StartWSEndpoint starts a websocket endpoint func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { // Generate the whitelist based on the allowed modules @@ -111,34 +82,6 @@ func StartWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins [] return listener, handler, err } -// func StartFastWSEndpoint(endpoint string, apis []API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *Server, error) { -// // Generate the whitelist based on the allowed modules -// whitelist := make(map[string]bool) -// for _, module := range modules { -// whitelist[module] = true -// } -// // Register all the APIs exposed by the services -// handler := NewServer() -// for _, api := range apis { -// if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { -// if err := handler.RegisterName(api.Namespace, api.Service); err != nil { -// return nil, nil, err -// } -// logger.Debug("FastWebSocket registered", "service", api.Service, "namespace", api.Namespace) -// } -// } -// // All APIs registered, start the HTTP listener -// var ( -// listener net.Listener -// err error -// ) -// if listener, err = net.Listen("tcp4", endpoint); err != nil { -// return nil, nil, err -// } -// go NewFastWSServer(wsOrigins, handler).Serve(listener) -// return listener, handler, err -// } - // StartIPCEndpoint starts an IPC endpoint. func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) { // Register all the APIs exposed by the services. diff --git a/networks/rpc/websocket_test.go b/networks/rpc/websocket_test.go index c1d9bdb498..c611cf9c62 100644 --- a/networks/rpc/websocket_test.go +++ b/networks/rpc/websocket_test.go @@ -89,25 +89,6 @@ func newTestListener() net.Listener { return ln } -/* -func TestWSServer_MaxConnections(t *testing.T) { - // create server - var ( - srv = newTestServer("service", new(Service)) - ln = newTestListener() - ) - defer srv.Stop() - defer ln.Close() - - go NewWSServer([]string{"*"}, srv).Serve(ln) - time.Sleep(100 * time.Millisecond) - - // set max websocket connections - MaxWebsocketConnections = 3 - testWebsocketMaxConnections(t, "ws://"+ln.Addr().String(), int(MaxWebsocketConnections)) -} -*/ - func TestFastWSServer_MaxConnections(t *testing.T) { // create server var ( diff --git a/node/node.go b/node/node.go index e34e5dcdb0..7f55ea4ea9 100644 --- a/node/node.go +++ b/node/node.go @@ -467,25 +467,6 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors return nil } -// startFastHTTP initializes and starts the HTTP RPC endpoint. -// func (n *Node) startFastHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error { -// // Short circuit if the HTTP endpoint isn't being exposed -// if endpoint == "" { -// return nil -// } -// listener, handler, err := rpc.StartFastHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts) -// if err != nil { -// return err -// } -// n.logger.Info("FastHTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) -// // All listeners booted successfully -// n.httpEndpoint = endpoint -// n.httpListener = listener -// n.httpHandler = handler - -// return nil -// } - // stopHTTP terminates the HTTP RPC endpoint. func (n *Node) stopHTTP() { if n.httpListener != nil { @@ -519,25 +500,6 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig return nil } -// startFastWS initializes and starts the websocket RPC endpoint. -// func (n *Node) startFastWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { -// // Short circuit if the WS endpoint isn't being exposed -// if endpoint == "" { -// return nil -// } -// listener, handler, err := rpc.StartFastWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) -// if err != nil { -// return err -// } -// n.logger.Info("FastWebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) -// // All listeners booted successfully -// n.wsEndpoint = endpoint -// n.wsListener = listener -// n.wsHandler = handler - -// return nil -// } - // stopWS terminates the websocket RPC endpoint. func (n *Node) stopWS() { if n.wsListener != nil { From cdc1a44ad9e74342499a78a1f1271f68af1f1851 Mon Sep 17 00:00:00 2001 From: sjnam Date: Tue, 2 Jan 2024 17:34:57 +0900 Subject: [PATCH 34/63] update some comments --- node/cn/tracers/api.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index 7740322840..21a39a140e 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -98,32 +98,33 @@ type Backend interface { StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (blockchain.Message, vm.BlockContext, vm.TxContext, *state.StateDB, error) } -// CommonAPI is the collection of tracing APIs exposed over the private debugging endpoint. +// CommonAPI contains +// - public methods that change behavior depending on `.unsafeTrace` flag. +// For instance, TraceTransaction and TraceCall may or may not support custom tracers. +// - private helper methods such as traceTx type CommonAPI struct { backend Backend unsafeTrace bool } -// API is the safe APIs +// API contains public methods that are considered "safe" to expose in public RPC. type API struct { CommonAPI } -// UNsafeAPI is the unsafe APIs +// UnsafeAPI contains public methods that are considered "unsafe" to expose in public RPC. type UnsafeAPI struct { CommonAPI } -// NewUnsafeAPI creates a new API definition for the tracing methods of the CN service, -// only allowing predefined tracers. +// NewUnsafeAPI creates a new UnsafeAPI definition func NewUnsafeAPI(backend Backend, unsafeTrace bool) *UnsafeAPI { return &UnsafeAPI{ CommonAPI{backend: backend, unsafeTrace: unsafeTrace}, } } -// NewAPI creates a new API definition for the tracing methods of the CN service, -// allowing both predefined tracers and Javascript snippet based tracing. +// NewAPI creates a new API definition func NewAPI(backend Backend, unsafeTrace bool) *API { return &API{ CommonAPI{backend: backend, unsafeTrace: unsafeTrace}, From 429ef758f4564b6277d484f995ccebbdb98f196e Mon Sep 17 00:00:00 2001 From: ayo-klaytn Date: Tue, 2 Jan 2024 09:35:18 +0100 Subject: [PATCH 35/63] readme updated --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 53f9ca4c78..f854410185 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ # Klaytn -Official golang implementation of the Klaytn protocol. Please visit [KlaytnDocs](https://docs.klaytn.com/) for more details on Klaytn design, node operation guides and application development resources. +Official golang implementation of the Klaytn protocol. Please visit [KlaytnDocs](https://docs.klaytn.foundation/) for more details on Klaytn design, node operation guides and application development resources. ## Building from Sources @@ -41,20 +41,20 @@ node (retaining all historical state). Core Cell (CC) is a set of one consensus node (CN) and one or more proxy nodes (PNs). Core Cell plays a role of generating blocks in the Klaytn network. We recommend to visit -the [CC Operation Guide](https://docs.klaytn.com/node/core-cell) +the [CC Operation Guide](https://docs.klaytn.foundation/docs/nodes/core-cell/) for the details of CC bootstrapping process. ## Running an Endpoint Node Endpoint Node (EN) is an entry point from the outside of the network in order to interact with the Klaytn network. Currently, two official networks are available: **Baobab** (testnet) and **Cypress** (mainnet). Please visit the official -[EN Operation Guide](https://docs.klaytn.com/node/endpoint-node). +[EN Operation Guide](https://docs.klaytn.foundation/docs/nodes/endpoint-node/). ## Running a Service Chain Node Service chain node is a node for Service Chain which is an auxiliary blockchain independent from the main chain tailored for individual service requiring special node configurations, customized security levels, and scalability for high throughput services. Service Chain expands and augments Klaytn by providing a data integrity mechanism and supporting token transfers between different chains. -Although the service chain feature is under development, we provide the operation guide for testing purposes. Please visit the official document [Service Chain Operation Guide](https://docs.klaytn.com/node/service-chain). -Furthermore, for those who are interested in the Klaytn Service Chain, please check out [Klaytn - Scaling Solutions](https://docs.klaytn.com/klaytn/scaling-solutions). +Although the service chain feature is under development, we provide the operation guide for testing purposes. Please visit the official document [Service Chain Operation Guide](https://docs.klaytn.foundation/docs/nodes/service-chain/). +Furthermore, for those who are interested in the Klaytn Service Chain, please check out [Klaytn - Scaling Solutions](https://docs.klaytn.foundation/docs/learn/scaling-solutions/). ## License From 1e96cfd7db156a1b6b82c921fabcf1310454827b Mon Sep 17 00:00:00 2001 From: sjnam Date: Wed, 3 Jan 2024 16:14:38 +0900 Subject: [PATCH 36/63] remove a useless boolean flag --- node/cn/backend.go | 4 ++-- node/cn/tracers/api.go | 8 ++++---- node/cn/tracers/api_test.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/node/cn/backend.go b/node/cn/backend.go index 65e7cbacc9..402cfc39cc 100644 --- a/node/cn/backend.go +++ b/node/cn/backend.go @@ -573,12 +573,12 @@ func (s *CN) APIs() []rpc.API { }, { Namespace: "debug", Version: "1.0", - Service: tracers.NewAPI(s.APIBackend, !s.config.DisableUnsafeDebug), + Service: tracers.NewAPI(s.APIBackend), Public: false, }, { Namespace: "debug", Version: "1.0", - Service: tracers.NewUnsafeAPI(s.APIBackend, true), + Service: tracers.NewUnsafeAPI(s.APIBackend), Public: false, IPCOnly: s.config.DisableUnsafeDebug, }, { diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index 21a39a140e..6cd19b3a39 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -118,16 +118,16 @@ type UnsafeAPI struct { } // NewUnsafeAPI creates a new UnsafeAPI definition -func NewUnsafeAPI(backend Backend, unsafeTrace bool) *UnsafeAPI { +func NewUnsafeAPI(backend Backend) *UnsafeAPI { return &UnsafeAPI{ - CommonAPI{backend: backend, unsafeTrace: unsafeTrace}, + CommonAPI{backend: backend, unsafeTrace: true}, } } // NewAPI creates a new API definition -func NewAPI(backend Backend, unsafeTrace bool) *API { +func NewAPI(backend Backend) *API { return &API{ - CommonAPI{backend: backend, unsafeTrace: unsafeTrace}, + CommonAPI{backend: backend, unsafeTrace: false}, } } diff --git a/node/cn/tracers/api_test.go b/node/cn/tracers/api_test.go index 5abd68c3e6..555d9d8058 100644 --- a/node/cn/tracers/api_test.go +++ b/node/cn/tracers/api_test.go @@ -201,7 +201,7 @@ func TestTraceCall(t *testing.T) { tx, err := types.SignTx(types.NewTransaction(uint64(i), accounts[0].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[1].key) assert.NoError(t, err) b.AddTx(tx) - }), true) + })) testSuite := []struct { blockNumber rpc.BlockNumber @@ -311,7 +311,7 @@ func TestTraceTransaction(t *testing.T) { tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil), signer, accounts[0].key) b.AddTx(tx) target = tx.Hash() - }), true) + })) result, err := api.TraceTransaction(context.Background(), target, nil) if err != nil { t.Errorf("Failed to trace transaction %v", err) @@ -344,7 +344,7 @@ func TestTraceBlock(t *testing.T) { // fee: 0 peb tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) b.AddTx(tx) - }), true) + })) testSuite := []struct { blockNumber rpc.BlockNumber From c03103abf963129a212d75d50997469085bf3e5c Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Thu, 4 Jan 2024 11:05:27 +0800 Subject: [PATCH 37/63] api: get klay namespace input field work again --- api/api_public_blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index d79ca6e051..4ed3b88a51 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -718,5 +718,5 @@ func (args *CallArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, intrinsic if args.AccessList != nil { accessList = *args.AccessList } - return types.NewMessage(addr, args.To, 0, value, gas, gasPrice, args.Data, false, intrinsicGas, accessList), nil + return types.NewMessage(addr, args.To, 0, value, gas, gasPrice, args.InputData(), false, intrinsicGas, accessList), nil } From 7f09a63cf805fd957e069af45cd2f532aa0d4d52 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 10 Jan 2024 10:14:25 +0900 Subject: [PATCH 38/63] [API] Print correct start and elapsed time --- api/api_private_debug.go | 5 +++-- storage/database/sharded_database.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/api/api_private_debug.go b/api/api_private_debug.go index 099561b3ff..5fa8232d32 100644 --- a/api/api_private_debug.go +++ b/api/api_private_debug.go @@ -51,8 +51,8 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { // ChaindbCompact flattens the entire key-value database into a single level, // removing all unused slots and merging all keys. func (api *PrivateDebugAPI) ChaindbCompact() error { - cstart := time.Now() for b := 0; b <= 255; b++ { + cstart := time.Now() var ( start = []byte{byte(b)} end = []byte{byte(b + 1)} @@ -60,11 +60,12 @@ func (api *PrivateDebugAPI) ChaindbCompact() error { if b == 255 { end = nil } - logger.Info("Compacting database", "range", fmt.Sprintf("%#X-%#X", start, end), "elapsed", common.PrettyDuration(time.Since(cstart))) + logger.Info("Compacting database started", "range", fmt.Sprintf("%#X-%#X", start, end)) if err := api.b.ChainDB().Compact(start, end); err != nil { logger.Error("Database compaction failed", "err", err) return err } + logger.Info("Compacting database completed", "range", fmt.Sprintf("%#X-%#X", start, end), "elapsed", common.PrettyDuration(time.Since(cstart))) } return nil } diff --git a/storage/database/sharded_database.go b/storage/database/sharded_database.go index e2b0fe0541..5d63d89743 100644 --- a/storage/database/sharded_database.go +++ b/storage/database/sharded_database.go @@ -532,7 +532,7 @@ func (db *shardedDB) Stat(property string) (string, error) { for idx, shard := range db.shards { stat, err := shard.Stat(property) if err == nil { - headInfo := fmt.Sprintf(" [shrad%d:%s]\n", idx, shard.Type()) + headInfo := fmt.Sprintf(" [shard%d:%s]\n", idx, shard.Type()) stats += headInfo + stat } else { errs += fmt.Sprintf("shard[%d]: %s", idx, err.Error()) From c1ebae2066abed6a19754a5c60fb3c5e1ca7ed28 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 10 Jan 2024 10:16:55 +0900 Subject: [PATCH 39/63] [API] Relocated timer start --- api/api_private_debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api_private_debug.go b/api/api_private_debug.go index 5fa8232d32..4efe8c34b9 100644 --- a/api/api_private_debug.go +++ b/api/api_private_debug.go @@ -52,7 +52,6 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { // removing all unused slots and merging all keys. func (api *PrivateDebugAPI) ChaindbCompact() error { for b := 0; b <= 255; b++ { - cstart := time.Now() var ( start = []byte{byte(b)} end = []byte{byte(b + 1)} @@ -61,6 +60,7 @@ func (api *PrivateDebugAPI) ChaindbCompact() error { end = nil } logger.Info("Compacting database started", "range", fmt.Sprintf("%#X-%#X", start, end)) + cstart := time.Now() if err := api.b.ChainDB().Compact(start, end); err != nil { logger.Error("Database compaction failed", "err", err) return err From 5a2aca9bb2913f130f10c84779a6a56e4273b78d Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 10 Jan 2024 12:41:44 +0900 Subject: [PATCH 40/63] [Snapshot] (1) Use `CompareAndSwap` instead of `Load()` and `Store()`. (2). Early check if the snapshot is not in database --- consensus/istanbul/backend/engine.go | 76 +++++++++++++++------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index e745fb52e2..80f93d38d3 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -941,46 +941,50 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com | header1, .. headerN | */ func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { - if !sb.isRestoringSnapshots.Load() && len(headers) > 1 { - var ( - from = headers[0].Number.Uint64() - to = headers[len(headers)-1].Number.Uint64() - start = time.Now() - commitTried = false - ) + // Prevent nested call. Ignore header length one + // because it was handled before the `regen` called. + defer func() { + sb.isRestoringSnapshots.CompareAndSwap(true, false) + }() + if !sb.isRestoringSnapshots.CompareAndSwap(false, true) || len(headers) <= 1 { + return + } - // Shortcut: No missing snapshot data to be processed. - if to-(to%uint64(params.CheckpointInterval)) < from { - return - } + var ( + from = headers[0].Number.Uint64() + to = headers[len(headers)-1].Number.Uint64() + start = time.Now() + commitTried = false + ) - sb.isRestoringSnapshots.Store(true) - defer func() { - sb.isRestoringSnapshots.Store(false) - }() - for _, header := range headers { - var ( - hn = header.Number.Uint64() - hh = header.Hash() - ) - if params.IsCheckpointInterval(hn) { - snap, err := sb.snapshot(chain, hn, hh, nil, false) - if err != nil { - logger.Warn("[Snapshot] Snapshot restoring failed", "len(headers)", len(headers), "from", from, "to", to, "headerNumber", hn) - continue - } - // Store snapshot data if it was not committed before - if loadSnap, _ := sb.db.ReadIstanbulSnapshot(hh); loadSnap == nil { - if err = snap.store(sb.db); err != nil { - logger.Warn("[Snapshot] Snapshot restoring failed", "len(headers)", len(headers), "from", from, "to", to, "headerNumber", hn) - } - commitTried = true - } + // Shortcut: No missing snapshot data to be processed. + if to-(to%uint64(params.CheckpointInterval)) < from { + return + } + + for _, header := range headers { + var ( + hn = header.Number.Uint64() + hh = header.Hash() + ) + if params.IsCheckpointInterval(hn) { + // Store snapshot data if it was not committed before + if loadSnap, _ := sb.db.ReadIstanbulSnapshot(hh); loadSnap != nil { + continue } + snap, err := sb.snapshot(chain, hn, hh, nil, false) + if err != nil { + logger.Warn("[Snapshot] Snapshot restoring failed", "len(headers)", len(headers), "from", from, "to", to, "headerNumber", hn) + continue + } + if err = snap.store(sb.db); err != nil { + logger.Warn("[Snapshot] Snapshot restoring failed", "len(headers)", len(headers), "from", from, "to", to, "headerNumber", hn) + } + commitTried = true } - if commitTried { // This prevents pushing too many logs by potential DoS attack - logger.Trace("[Snapshot] Snapshot restoring completed", "len(headers)", len(headers), "from", from, "to", to, "elapsed", time.Since(start)) - } + } + if commitTried { // This prevents pushing too many logs by potential DoS attack + logger.Trace("[Snapshot] Snapshot restoring completed", "len(headers)", len(headers), "from", from, "to", to, "elapsed", time.Since(start)) } } From e838902e6ee6cb95342d9d4078dbe2c6d35e3b6b Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Thu, 11 Jan 2024 13:10:07 +0900 Subject: [PATCH 41/63] [API] Added committers' corresponding public keys --- cmd/utils/nodecmd/util.go | 2 ++ consensus/istanbul/backend/api.go | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cmd/utils/nodecmd/util.go b/cmd/utils/nodecmd/util.go index 37b7a55d58..3df44effd0 100644 --- a/cmd/utils/nodecmd/util.go +++ b/cmd/utils/nodecmd/util.go @@ -30,6 +30,7 @@ import ( "github.com/klaytn/klaytn/common" "github.com/klaytn/klaytn/common/hexutil" "github.com/klaytn/klaytn/consensus/istanbul" + "github.com/klaytn/klaytn/consensus/istanbul/backend" "github.com/klaytn/klaytn/crypto" "github.com/klaytn/klaytn/crypto/sha3" "github.com/klaytn/klaytn/governance" @@ -208,6 +209,7 @@ func decodeExtra(headerFile string) (map[string]interface{}, error) { m["validators"] = validators m["seal"] = hexutil.Encode(istanbulExtra.Seal) m["committedSeal"] = cSeals + m["committers"] = backend.ParseCommitteedSeals(header) m["validatorSize"] = len(validators) m["committedSealSize"] = len(cSeals) m["proposer"] = proposer.String() diff --git a/consensus/istanbul/backend/api.go b/consensus/istanbul/backend/api.go index 468be007cc..fc81d13748 100644 --- a/consensus/istanbul/backend/api.go +++ b/consensus/istanbul/backend/api.go @@ -30,8 +30,10 @@ import ( "github.com/klaytn/klaytn/blockchain" "github.com/klaytn/klaytn/blockchain/types" "github.com/klaytn/klaytn/common" + "github.com/klaytn/klaytn/common/hexutil" "github.com/klaytn/klaytn/consensus" "github.com/klaytn/klaytn/consensus/istanbul" + istanbulCore "github.com/klaytn/klaytn/consensus/istanbul/core" "github.com/klaytn/klaytn/networks/rpc" ) @@ -327,14 +329,32 @@ func (api *APIExtension) makeRPCBlockOutput(b *types.Block, } r["committee"] = cInfo.Committee + r["committers"] = ParseCommitteedSeals(head) r["proposer"] = cInfo.Proposer r["round"] = cInfo.Round r["originProposer"] = cInfo.OriginProposer r["transactions"] = rpcTransactions - return r } +func ParseCommitteedSeals(header *types.Header) map[string]string { + if header == nil { + return nil + } + committers := make(map[string]string) + if istanbulExtra, err := types.ExtractIstanbulExtra(header); err == nil { + for _, cs := range istanbulExtra.CommittedSeal { + committer, err := istanbul.GetSignatureAddress(istanbulCore.PrepareCommittedSeal(header.Hash()), cs) + if err != nil { + committers[committer.String()] = err.Error() + } else { + committers[committer.String()] = hexutil.Encode(cs) + } + } + } + return committers +} + // TODO-Klaytn: This API functions should be managed with API functions with namespace "klay" func (api *APIExtension) GetBlockWithConsensusInfoByNumber(number *rpc.BlockNumber) (map[string]interface{}, error) { b, ok := api.chain.(*blockchain.BlockChain) From 3df5bec43bbf09c887da97d2dcee87a5389234f1 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Thu, 11 Jan 2024 13:24:31 +0900 Subject: [PATCH 42/63] [API] Added consensus round property --- cmd/utils/nodecmd/util.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/utils/nodecmd/util.go b/cmd/utils/nodecmd/util.go index 3df44effd0..967ab2f74a 100644 --- a/cmd/utils/nodecmd/util.go +++ b/cmd/utils/nodecmd/util.go @@ -213,6 +213,7 @@ func decodeExtra(headerFile string) (map[string]interface{}, error) { m["validatorSize"] = len(validators) m["committedSealSize"] = len(cSeals) m["proposer"] = proposer.String() + m["round"] = header.Round() return m, nil } From 4e3eb40081bf831a9b5247ebb43450ab1596de07 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Fri, 12 Jan 2024 16:33:27 +0900 Subject: [PATCH 43/63] [API] Include committer address only --- cmd/utils/nodecmd/util.go | 17 ++++++++------- consensus/istanbul/backend/api.go | 35 ++++++++++++++++++------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/cmd/utils/nodecmd/util.go b/cmd/utils/nodecmd/util.go index 967ab2f74a..482fe22aca 100644 --- a/cmd/utils/nodecmd/util.go +++ b/cmd/utils/nodecmd/util.go @@ -190,26 +190,29 @@ func decodeExtra(headerFile string) (map[string]interface{}, error) { if err != nil { return nil, err } - validators := make([]string, len(istanbulExtra.Validators)) for idx, addr := range istanbulExtra.Validators { validators[idx] = addr.String() } - cSeals := make([]string, len(istanbulExtra.CommittedSeal)) - for idx, cSeal := range istanbulExtra.CommittedSeal { - cSeals[idx] = hexutil.Encode(cSeal) - } - proposer, err := istanbul.GetSignatureAddress(hash.Bytes(), istanbulExtra.Seal) if err != nil { return nil, err } + committers, cSealsBytes, err := backend.ParseCommitteedSeals(header) + if err != nil { + return nil, err + } + cSeals := make([]string, len(istanbulExtra.CommittedSeal)) + for i := 0; i < len(cSeals); i++ { + cSeals[i] = hexutil.Encode(cSealsBytes[i]) + } + m := make(map[string]interface{}) m["hash"] = hash m["validators"] = validators m["seal"] = hexutil.Encode(istanbulExtra.Seal) m["committedSeal"] = cSeals - m["committers"] = backend.ParseCommitteedSeals(header) + m["committers"] = committers m["validatorSize"] = len(validators) m["committedSealSize"] = len(cSeals) m["proposer"] = proposer.String() diff --git a/consensus/istanbul/backend/api.go b/consensus/istanbul/backend/api.go index fc81d13748..6ee9ada006 100644 --- a/consensus/istanbul/backend/api.go +++ b/consensus/istanbul/backend/api.go @@ -30,7 +30,6 @@ import ( "github.com/klaytn/klaytn/blockchain" "github.com/klaytn/klaytn/blockchain/types" "github.com/klaytn/klaytn/common" - "github.com/klaytn/klaytn/common/hexutil" "github.com/klaytn/klaytn/consensus" "github.com/klaytn/klaytn/consensus/istanbul" istanbulCore "github.com/klaytn/klaytn/consensus/istanbul/core" @@ -328,8 +327,15 @@ func (api *APIExtension) makeRPCBlockOutput(b *types.Block, } } + committers, _, err := ParseCommitteedSeals(head) + if err != nil { + parseErr := make(map[string]interface{}) + parseErr["ERROR"] = err + return parseErr + } + r["committee"] = cInfo.Committee - r["committers"] = ParseCommitteedSeals(head) + r["committers"] = committers r["proposer"] = cInfo.Proposer r["round"] = cInfo.Round r["originProposer"] = cInfo.OriginProposer @@ -337,22 +343,23 @@ func (api *APIExtension) makeRPCBlockOutput(b *types.Block, return r } -func ParseCommitteedSeals(header *types.Header) map[string]string { +func ParseCommitteedSeals(header *types.Header) ([]common.Address, [][]byte, error) { if header == nil { - return nil + return nil, nil, errors.New("Empty header") } - committers := make(map[string]string) - if istanbulExtra, err := types.ExtractIstanbulExtra(header); err == nil { - for _, cs := range istanbulExtra.CommittedSeal { - committer, err := istanbul.GetSignatureAddress(istanbulCore.PrepareCommittedSeal(header.Hash()), cs) - if err != nil { - committers[committer.String()] = err.Error() - } else { - committers[committer.String()] = hexutil.Encode(cs) - } + istanbulExtra, err := types.ExtractIstanbulExtra(header) + if err != nil { + return nil, nil, err + } + committers := make([]common.Address, len(istanbulExtra.CommittedSeal)) + for idx, cs := range istanbulExtra.CommittedSeal { + committer, err := istanbul.GetSignatureAddress(istanbulCore.PrepareCommittedSeal(header.Hash()), cs) + if err != nil { + return nil, nil, err } + committers[idx] = committer } - return committers + return committers, istanbulExtra.CommittedSeal, nil } // TODO-Klaytn: This API functions should be managed with API functions with namespace "klay" From 3831a34696e97abdce73fcde7613cad0b7844bf6 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Mon, 15 Jan 2024 10:24:09 +0900 Subject: [PATCH 44/63] [API] Added new two fields, computationCostTotal and computationCostUsed --- api/api_public_blockchain.go | 36 ++++++++++++++++++++---------------- blockchain/vm/logger.go | 25 +++++++++++++------------ node/cn/tracers/api.go | 2 +- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index d3279a1665..3d91269a32 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -473,19 +473,21 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre // StructLogRes stores a structured log emitted by the EVM while replaying a // transaction in debug mode type StructLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Depth int `json:"depth"` - Error error `json:"error,omitempty"` - Stack *[]string `json:"stack,omitempty"` - Memory *[]string `json:"memory,omitempty"` - Storage *map[string]string `json:"storage,omitempty"` + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Depth int `json:"depth"` + Error error `json:"error,omitempty"` + Stack *[]string `json:"stack,omitempty"` + Memory *[]string `json:"memory,omitempty"` + Storage *map[string]string `json:"storage,omitempty"` + ComputationCostTotal uint64 `json:"computationCostTotal"` + ComputationCostUsed uint64 `json:"computationCostUsed"` } // formatLogs formats EVM returned structured logs for json output -func FormatLogs(timeout time.Duration, logs []vm.StructLog) ([]StructLogRes, error) { +func FormatLogs(timeout time.Duration, logs []vm.StructLog, cc uint64) ([]StructLogRes, error) { logTimeout := false deadlineCtx, cancel := context.WithTimeout(context.Background(), timeout) go func() { @@ -501,12 +503,14 @@ func FormatLogs(timeout time.Duration, logs []vm.StructLog) ([]StructLogRes, err return nil, fmt.Errorf("trace logger timeout") } formatted[index] = StructLogRes{ - Pc: trace.Pc, - Op: trace.Op.String(), - Gas: trace.Gas, - GasCost: trace.GasCost, - Depth: trace.Depth, - Error: trace.Err, + Pc: trace.Pc, + Op: trace.Op.String(), + Gas: trace.Gas, + GasCost: trace.GasCost, + Depth: trace.Depth, + Error: trace.Err, + ComputationCostTotal: cc, + ComputationCostUsed: trace.ComputationCostUsed, } if trace.Stack != nil { stack := make([]string, len(trace.Stack)) diff --git a/blockchain/vm/logger.go b/blockchain/vm/logger.go index 84a0757175..5be74532f8 100644 --- a/blockchain/vm/logger.go +++ b/blockchain/vm/logger.go @@ -59,17 +59,18 @@ type LogConfig struct { // StructLog is emitted to the EVM each cycle and lists information about the current internal state // prior to the execution of the statement. type StructLog struct { - Pc uint64 `json:"pc"` - Op OpCode `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Memory []byte `json:"memory"` - MemorySize int `json:"memSize"` - Stack []*big.Int `json:"stack"` - Storage map[common.Hash]common.Hash `json:"-"` - Depth int `json:"depth"` - RefundCounter uint64 `json:"refund"` - Err error `json:"-"` + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Memory []byte `json:"memory"` + MemorySize int `json:"memSize"` + Stack []*big.Int `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth int `json:"depth"` + RefundCounter uint64 `json:"refund"` + Err error `json:"-"` + ComputationCostUsed uint64 `json:computationCostUsed` } // overrides for gencodec @@ -191,7 +192,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui storage = l.changedValues[contract.Address()].Copy() } // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err} + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err, env.GetOpCodeComputationCost()} l.logs = append(l.logs, log) } diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index 6cd19b3a39..5b9e1dac99 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -948,7 +948,7 @@ func (api *CommonAPI) traceTx(ctx context.Context, message blockchain.Message, b return nil, err } } - if logs, err := klaytnapi.FormatLogs(loggerTimeout, tracer.StructLogs()); err == nil { + if logs, err := klaytnapi.FormatLogs(loggerTimeout, tracer.StructLogs(), vmenv.Config.ComputationCostLimit); err == nil { return &klaytnapi.ExecutionResult{ Gas: ret.UsedGas, Failed: ret.Failed(), From 1811524d25faffdc2caab273c520afbcb82abff5 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Mon, 15 Jan 2024 10:45:15 +0900 Subject: [PATCH 45/63] [API] Syntax error fixed --- blockchain/vm/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain/vm/logger.go b/blockchain/vm/logger.go index 5be74532f8..052b0a82d8 100644 --- a/blockchain/vm/logger.go +++ b/blockchain/vm/logger.go @@ -70,7 +70,7 @@ type StructLog struct { Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` Err error `json:"-"` - ComputationCostUsed uint64 `json:computationCostUsed` + ComputationCostUsed uint64 `json:"computationCostUsed"` } // overrides for gencodec From 63c9743302eb328c736e9eb08b4897fc151844c5 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Mon, 15 Jan 2024 11:14:46 +0900 Subject: [PATCH 46/63] [API] Field semantic changed. `Computation`: residual CC. `ComputationCost`: CC per opcode --- api/api_public_blockchain.go | 38 ++++++++++++++++++------------------ blockchain/vm/logger.go | 31 +++++++++++++++++------------ 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index 3d91269a32..e58c53c983 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -473,17 +473,17 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre // StructLogRes stores a structured log emitted by the EVM while replaying a // transaction in debug mode type StructLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Depth int `json:"depth"` - Error error `json:"error,omitempty"` - Stack *[]string `json:"stack,omitempty"` - Memory *[]string `json:"memory,omitempty"` - Storage *map[string]string `json:"storage,omitempty"` - ComputationCostTotal uint64 `json:"computationCostTotal"` - ComputationCostUsed uint64 `json:"computationCostUsed"` + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Depth int `json:"depth"` + Error error `json:"error,omitempty"` + Stack *[]string `json:"stack,omitempty"` + Memory *[]string `json:"memory,omitempty"` + Storage *map[string]string `json:"storage,omitempty"` + Computation uint64 `json:"computation"` + ComputationCost uint64 `json:"computationCost"` } // formatLogs formats EVM returned structured logs for json output @@ -503,14 +503,14 @@ func FormatLogs(timeout time.Duration, logs []vm.StructLog, cc uint64) ([]Struct return nil, fmt.Errorf("trace logger timeout") } formatted[index] = StructLogRes{ - Pc: trace.Pc, - Op: trace.Op.String(), - Gas: trace.Gas, - GasCost: trace.GasCost, - Depth: trace.Depth, - Error: trace.Err, - ComputationCostTotal: cc, - ComputationCostUsed: trace.ComputationCostUsed, + Pc: trace.Pc, + Op: trace.Op.String(), + Gas: trace.Gas, + GasCost: trace.GasCost, + Depth: trace.Depth, + Error: trace.Err, + Computation: trace.Computation, + ComputationCost: trace.ComputationCost, } if trace.Stack != nil { stack := make([]string, len(trace.Stack)) diff --git a/blockchain/vm/logger.go b/blockchain/vm/logger.go index 052b0a82d8..e951067581 100644 --- a/blockchain/vm/logger.go +++ b/blockchain/vm/logger.go @@ -59,18 +59,19 @@ type LogConfig struct { // StructLog is emitted to the EVM each cycle and lists information about the current internal state // prior to the execution of the statement. type StructLog struct { - Pc uint64 `json:"pc"` - Op OpCode `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Memory []byte `json:"memory"` - MemorySize int `json:"memSize"` - Stack []*big.Int `json:"stack"` - Storage map[common.Hash]common.Hash `json:"-"` - Depth int `json:"depth"` - RefundCounter uint64 `json:"refund"` - Err error `json:"-"` - ComputationCostUsed uint64 `json:"computationCostUsed"` + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Memory []byte `json:"memory"` + MemorySize int `json:"memSize"` + Stack []*big.Int `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth int `json:"depth"` + RefundCounter uint64 `json:"refund"` + Computation uint64 `json:"computation` + ComputationCost uint64 `json:"computationCost"` + Err error `json:"-"` } // overrides for gencodec @@ -192,7 +193,11 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui storage = l.changedValues[contract.Address()].Copy() } // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err, env.GetOpCodeComputationCost()} + var ( + availableCC = env.Config.ComputationCostLimit - env.opcodeComputationCostSum + opcodeCC = env.interpreter.cfg.JumpTable[op].computationCost + ) + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), availableCC, opcodeCC, err} l.logs = append(l.logs, log) } From c541f47b558224349dfa35fe2c6cbf4ebb135443 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Mon, 15 Jan 2024 11:26:22 +0900 Subject: [PATCH 47/63] [API] Syntax fixed --- blockchain/vm/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain/vm/logger.go b/blockchain/vm/logger.go index e951067581..bc35b1d527 100644 --- a/blockchain/vm/logger.go +++ b/blockchain/vm/logger.go @@ -69,7 +69,7 @@ type StructLog struct { Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` - Computation uint64 `json:"computation` + Computation uint64 `json:"computation"` ComputationCost uint64 `json:"computationCost"` Err error `json:"-"` } From 8c11573867ebec88aee74dbc21fde26719a4a3a5 Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Tue, 16 Jan 2024 15:32:24 +0800 Subject: [PATCH 48/63] ci: support blst portable build --- build/ci.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/ci.go b/build/ci.go index 32a0559420..83a872c6ba 100644 --- a/build/ci.go +++ b/build/ci.go @@ -241,6 +241,9 @@ func goToolArch(arch string, cc string, subcmd string, args ...string) *exec.Cmd if cc != "" { cmd.Env = append(cmd.Env, "CC="+cc) } + // CKZG by default is not portable, append the necessary build flags to make + // it not rely on modern CPU instructions and enable linking against. + cmd.Env = append(cmd.Env, "CGO_CFLAGS=-O2 -g -D__BLST_PORTABLE__") for _, e := range os.Environ() { if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") { continue From c66d07dc964917d6a3ea0f930be4ee65cab18495 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Tue, 16 Jan 2024 20:05:07 +0900 Subject: [PATCH 49/63] [API] Variable renamed --- api/api_public_blockchain.go | 2 +- blockchain/vm/logger.go | 6 +++--- node/cn/tracers/api.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index e58c53c983..266c4ab71a 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -487,7 +487,7 @@ type StructLogRes struct { } // formatLogs formats EVM returned structured logs for json output -func FormatLogs(timeout time.Duration, logs []vm.StructLog, cc uint64) ([]StructLogRes, error) { +func FormatLogs(timeout time.Duration, logs []vm.StructLog) ([]StructLogRes, error) { logTimeout := false deadlineCtx, cancel := context.WithTimeout(context.Background(), timeout) go func() { diff --git a/blockchain/vm/logger.go b/blockchain/vm/logger.go index bc35b1d527..82c5c3d200 100644 --- a/blockchain/vm/logger.go +++ b/blockchain/vm/logger.go @@ -194,10 +194,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui } // create a new snapshot of the EVM. var ( - availableCC = env.Config.ComputationCostLimit - env.opcodeComputationCostSum - opcodeCC = env.interpreter.cfg.JumpTable[op].computationCost + remainingComputationCost = env.Config.ComputationCostLimit - env.opcodeComputationCostSum + opcodeComputationCost = env.interpreter.cfg.JumpTable[op].computationCost ) - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), availableCC, opcodeCC, err} + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), remainingComputationCost, opcodeComputationCost, err} l.logs = append(l.logs, log) } diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index 5b9e1dac99..6cd19b3a39 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -948,7 +948,7 @@ func (api *CommonAPI) traceTx(ctx context.Context, message blockchain.Message, b return nil, err } } - if logs, err := klaytnapi.FormatLogs(loggerTimeout, tracer.StructLogs(), vmenv.Config.ComputationCostLimit); err == nil { + if logs, err := klaytnapi.FormatLogs(loggerTimeout, tracer.StructLogs()); err == nil { return &klaytnapi.ExecutionResult{ Gas: ret.UsedGas, Failed: ret.Failed(), From 03fddabef695c4deeb5dc958a8c0520e35ca9e1f Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 17 Jan 2024 11:23:18 +0900 Subject: [PATCH 50/63] [Snapshot] Unconditionally store `false` when `regen` finishes --- consensus/istanbul/backend/engine.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 80f93d38d3..708fc734f4 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -943,12 +943,12 @@ func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash com func (sb *backend) regen(chain consensus.ChainReader, headers []*types.Header) { // Prevent nested call. Ignore header length one // because it was handled before the `regen` called. - defer func() { - sb.isRestoringSnapshots.CompareAndSwap(true, false) - }() if !sb.isRestoringSnapshots.CompareAndSwap(false, true) || len(headers) <= 1 { return } + defer func() { + sb.isRestoringSnapshots.Store(false) + }() var ( from = headers[0].Number.Uint64() From d8c7cfd0ab364a428b8999a20f3f80107b6940d9 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Wed, 17 Jan 2024 17:57:49 +0900 Subject: [PATCH 51/63] [RANDAO] Try code retreival once again if missing trie node error appears --- consensus/istanbul/backend/randao.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/consensus/istanbul/backend/randao.go b/consensus/istanbul/backend/randao.go index 4a050acd69..d6c2bd22a8 100644 --- a/consensus/istanbul/backend/randao.go +++ b/consensus/istanbul/backend/randao.go @@ -15,6 +15,7 @@ import ( "github.com/klaytn/klaytn/crypto" "github.com/klaytn/klaytn/crypto/bls" "github.com/klaytn/klaytn/params" + "github.com/klaytn/klaytn/storage/statedb" ) // For testing without KIP-113 contract setup @@ -80,7 +81,13 @@ func (p *ChainBlsPubkeyProvider) getAllCached(chain consensus.ChainReader, num * var err error kip113Addr, err = system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, parentNum) if err != nil { - return nil, err + if _, ok := err.(*statedb.MissingNodeError); ok { + parentNum = nil + kip113Addr, err = system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, parentNum) + if err != nil { + return nil, err + } + } } } else { return nil, errors.New("Cannot read KIP113 address from registry before Randao fork") From e2ee5d5f5859c47c188afe0b1a5c98e331c54f82 Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Fri, 5 Jan 2024 15:51:58 +0800 Subject: [PATCH 52/63] api: remove computationcost limit at call/estimate apis --- api/api_ethereum.go | 2 +- api/api_public_blockchain.go | 6 +++--- blockchain/vm/interpreter.go | 11 +++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/api/api_ethereum.go b/api/api_ethereum.go index 6051bde8bb..40c8c22d94 100644 --- a/api/api_ethereum.go +++ b/api/api_ethereum.go @@ -1436,7 +1436,7 @@ func EthDoCall(ctx context.Context, b Backend, args EthTransactionArgs, blockNrO if msg.Gas() < intrinsicGas { return nil, fmt.Errorf("%w: msg.gas %d, want %d", blockchain.ErrIntrinsicGas, msg.Gas(), intrinsicGas) } - evm, vmError, err := b.GetEVM(ctx, msg, state, header, vm.Config{}) + evm, vmError, err := b.GetEVM(ctx, msg, state, header, vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}) if err != nil { return nil, err } diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index d3279a1665..fa9c15370b 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -382,7 +382,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOr if rpcGasCap := s.b.RPCGasCap(); rpcGasCap != nil { gasCap = rpcGasCap } - result, _, err := DoCall(ctx, s.b, args, blockNrOrHash, vm.Config{}, s.b.RPCEVMTimeout(), gasCap) + result, _, err := DoCall(ctx, s.b, args, blockNrOrHash, vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}, s.b.RPCEVMTimeout(), gasCap) if err != nil { return nil, err } @@ -398,7 +398,7 @@ func (s *PublicBlockChainAPI) EstimateComputationCost(ctx context.Context, args if rpcGasCap := s.b.RPCGasCap(); rpcGasCap != nil { gasCap = rpcGasCap } - _, computationCost, err := DoCall(ctx, s.b, args, blockNrOrHash, vm.Config{}, s.b.RPCEVMTimeout(), gasCap) + _, computationCost, err := DoCall(ctx, s.b, args, blockNrOrHash, vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}, s.b.RPCEVMTimeout(), gasCap) return (hexutil.Uint64)(computationCost), err } @@ -428,7 +428,7 @@ func (s *PublicBlockChainAPI) DoEstimateGas(ctx context.Context, b Backend, args // Create a helper to check if a gas allowance results in an executable transaction executable := func(gas uint64) (bool, *blockchain.ExecutionResult, error) { args.Gas = hexutil.Uint64(gas) - result, _, err := DoCall(ctx, b, args, rpc.NewBlockNumberOrHashWithNumber(rpc.LatestBlockNumber), vm.Config{}, 0, gasCap) + result, _, err := DoCall(ctx, b, args, rpc.NewBlockNumberOrHashWithNumber(rpc.LatestBlockNumber), vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}, 0, gasCap) if err != nil { if errors.Is(err, blockchain.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit diff --git a/blockchain/vm/interpreter.go b/blockchain/vm/interpreter.go index f08df8d449..97c061d822 100644 --- a/blockchain/vm/interpreter.go +++ b/blockchain/vm/interpreter.go @@ -123,7 +123,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { cfg.JumpTable = jt } - // Enable the opcode computation cost limit + // Set the opcode computation cost limit by the default value if cfg.ComputationCostLimit == 0 { switch { case evm.chainRules.IsCancun: @@ -131,11 +131,10 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { default: cfg.ComputationCostLimit = uint64(params.OpcodeComputationCostLimit) } - } - - // It is an experimental feature. - if params.OpcodeComputationCostLimitOverride != 0 { - cfg.ComputationCostLimit = params.OpcodeComputationCostLimitOverride + // It is an experimental feature. + if params.OpcodeComputationCostLimitOverride != 0 { + cfg.ComputationCostLimit = params.OpcodeComputationCostLimitOverride + } } return &EVMInterpreter{ From 0dc1a1132f16aac78445f5c9eec78968b04bd228 Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Wed, 10 Jan 2024 16:49:52 +0800 Subject: [PATCH 53/63] enhance comment about the priority when deciding computationCostLimit value --- blockchain/vm/interpreter.go | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/blockchain/vm/interpreter.go b/blockchain/vm/interpreter.go index 97c061d822..eb6bfc9289 100644 --- a/blockchain/vm/interpreter.go +++ b/blockchain/vm/interpreter.go @@ -123,24 +123,26 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { cfg.JumpTable = jt } - // Set the opcode computation cost limit by the default value - if cfg.ComputationCostLimit == 0 { - switch { - case evm.chainRules.IsCancun: - cfg.ComputationCostLimit = uint64(params.OpcodeComputationCostLimitCancun) - default: - cfg.ComputationCostLimit = uint64(params.OpcodeComputationCostLimit) - } - // It is an experimental feature. - if params.OpcodeComputationCostLimitOverride != 0 { - cfg.ComputationCostLimit = params.OpcodeComputationCostLimitOverride - } + // When setting computation cost limit value, priority is given to the original value, override experimental value, + // and then the limit value specified for each hard fork. If the original value is not infinite or + // there is no override value, the next priority value is used. + // Cautious, the infinite value is only applicable for specific API calls. (e.g. call/estimateGas/estimateComputationGas) + if cfg.ComputationCostLimit == params.OpcodeComputationCostLimitInfinite { + return &EVMInterpreter{evm: evm, cfg: cfg} } - - return &EVMInterpreter{ - evm: evm, - cfg: cfg, + // Override the computation cost with an experiment value + if params.OpcodeComputationCostLimitOverride != 0 { + cfg.ComputationCostLimit = params.OpcodeComputationCostLimitOverride + return &EVMInterpreter{evm: evm, cfg: cfg} } + // Set the opcode computation cost limit by the default value + switch { + case evm.chainRules.IsCancun: + cfg.ComputationCostLimit = uint64(params.OpcodeComputationCostLimitCancun) + default: + cfg.ComputationCostLimit = uint64(params.OpcodeComputationCostLimit) + } + return &EVMInterpreter{evm: evm, cfg: cfg} } // count values and execution time of the opcodes are collected until the node is turned off. @@ -241,11 +243,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err } // We limit tx's execution time using the sum of computation cost of opcodes. - if in.evm.Config.ComputationCostLimit != 0 { - in.evm.opcodeComputationCostSum += operation.computationCost - if in.evm.opcodeComputationCostSum > in.evm.Config.ComputationCostLimit { - return nil, ErrOpcodeComputationCostLimitReached - } + in.evm.opcodeComputationCostSum += operation.computationCost + if in.evm.opcodeComputationCostSum > in.evm.Config.ComputationCostLimit { + return nil, ErrOpcodeComputationCostLimitReached } var memorySize uint64 var extraSize uint64 From 2318506ff85dec2c7047641b05611f7543c12d1a Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Mon, 15 Jan 2024 15:16:59 +0800 Subject: [PATCH 54/63] reimpose the evmtimeout on the estimateGas api --- api/api_ethereum.go | 2 +- api/api_public_blockchain.go | 2 +- api/backend.go | 6 +++--- cmd/utils/flags.go | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/api_ethereum.go b/api/api_ethereum.go index 40c8c22d94..2b52cb029d 100644 --- a/api/api_ethereum.go +++ b/api/api_ethereum.go @@ -1491,7 +1491,7 @@ func EthDoEstimateGas(ctx context.Context, b Backend, args EthTransactionArgs, b executable := func(gas uint64) (bool, *blockchain.ExecutionResult, error) { args.Gas = (*hexutil.Uint64)(&gas) - result, err := EthDoCall(ctx, b, args, rpc.NewBlockNumberOrHashWithNumber(rpc.LatestBlockNumber), nil, 0, gasCap) + result, err := EthDoCall(ctx, b, args, rpc.NewBlockNumberOrHashWithNumber(rpc.LatestBlockNumber), nil, b.RPCEVMTimeout(), gasCap) if err != nil { if errors.Is(err, blockchain.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index fa9c15370b..fad321c9d0 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -428,7 +428,7 @@ func (s *PublicBlockChainAPI) DoEstimateGas(ctx context.Context, b Backend, args // Create a helper to check if a gas allowance results in an executable transaction executable := func(gas uint64) (bool, *blockchain.ExecutionResult, error) { args.Gas = hexutil.Uint64(gas) - result, _, err := DoCall(ctx, b, args, rpc.NewBlockNumberOrHashWithNumber(rpc.LatestBlockNumber), vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}, 0, gasCap) + result, _, err := DoCall(ctx, b, args, rpc.NewBlockNumberOrHashWithNumber(rpc.LatestBlockNumber), vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}, s.b.RPCEVMTimeout(), gasCap) if err != nil { if errors.Is(err, blockchain.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit diff --git a/api/backend.go b/api/backend.go index 1c86c65928..8f176ac09d 100644 --- a/api/backend.go +++ b/api/backend.go @@ -54,9 +54,9 @@ type Backend interface { ChainDB() database.DBManager EventMux() *event.TypeMux AccountManager() accounts.AccountManager - RPCEVMTimeout() time.Duration // global timeout for klay_call - RPCGasCap() *big.Int // global gas cap for klay_call over rpc: DoS protection - RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs + RPCEVMTimeout() time.Duration // global timeout for eth/klay_call/estimateGas/estimateComputationCost + RPCGasCap() *big.Int // global gas cap for eth/klay_call/estimateGas/estimateComputationCost + RPCTxFeeCap() float64 // global tx fee cap in eth_signTransaction Engine() consensus.Engine FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index bccfd3ed90..b5cbde3b88 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -830,21 +830,21 @@ var ( } RPCGlobalGasCap = &cli.Uint64Flag{ Name: "rpc.gascap", - Usage: "Sets a cap on gas that can be used in klay_call/estimateGas", + Usage: "Sets a cap on gas in {eth,klay}_{call,estimateGas,estimateComputationCost} (0 = no cap)", Aliases: []string{"http-rpc.gascap"}, EnvVars: []string{"KLAYTN_RPC_GASCAP"}, Category: "API AND CONSOLE", } RPCGlobalEVMTimeoutFlag = &cli.DurationFlag{ Name: "rpc.evmtimeout", - Usage: "Sets a timeout used for eth_call (0=infinite)", + Usage: "Sets a timeout in {eth,klay}_{call,estimateGas,estimateComputationCost}. (0 = apply http-rpc.execution.timeout)", Aliases: []string{"http-rpc.evmtimeout"}, EnvVars: []string{"KLAYTN_RPC_EVMTIMEOUT"}, Category: "API AND CONSOLE", } RPCGlobalEthTxFeeCapFlag = &cli.Float64Flag{ Name: "rpc.ethtxfeecap", - Usage: "Sets a cap on transaction fee (in klay) that can be sent via the eth namespace RPC APIs (0 = no cap)", + Usage: "Sets a cap on transaction fee (=gasLimit*gasPrice) (in klay) in eth_signTransaction (0 = no cap)", Aliases: []string{"http-rpc.eth-tx-feecap"}, EnvVars: []string{"KLAYTN_RPC_ETHTXFEECAP"}, Category: "API AND CONSOLE", From 3483b4c3336a57bb728f9c44ea06c687f5281a6d Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Mon, 15 Jan 2024 15:41:32 +0800 Subject: [PATCH 55/63] add expect value of RPCEVMTimeout() at testEstimateGas --- api/api_ethereum_test.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/api/api_ethereum_test.go b/api/api_ethereum_test.go index 81329780fd..ddb0a1be57 100644 --- a/api/api_ethereum_test.go +++ b/api/api_ethereum_test.go @@ -9,14 +9,12 @@ import ( "math/big" "reflect" "testing" + "time" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/klaytn/klaytn/consensus" - "github.com/klaytn/klaytn/crypto" - "github.com/klaytn/klaytn/governance" - - "github.com/golang/mock/gomock" "github.com/klaytn/klaytn/accounts" mock_accounts "github.com/klaytn/klaytn/accounts/mocks" mock_api "github.com/klaytn/klaytn/api/mocks" @@ -27,13 +25,15 @@ import ( "github.com/klaytn/klaytn/blockchain/vm" "github.com/klaytn/klaytn/common" "github.com/klaytn/klaytn/common/hexutil" + "github.com/klaytn/klaytn/consensus" "github.com/klaytn/klaytn/consensus/gxhash" "github.com/klaytn/klaytn/consensus/mocks" + "github.com/klaytn/klaytn/crypto" + "github.com/klaytn/klaytn/governance" "github.com/klaytn/klaytn/networks/rpc" "github.com/klaytn/klaytn/params" "github.com/klaytn/klaytn/rlp" "github.com/klaytn/klaytn/storage/database" - "github.com/stretchr/testify/assert" ) var dummyChainConfigForEthereumAPITest = ¶ms.ChainConfig{ @@ -2489,6 +2489,7 @@ func testEstimateGas(t *testing.T, mockBackend *mock_api.MockBackend, fnEstimate } mockBackend.EXPECT().ChainConfig().Return(chainConfig).AnyTimes() mockBackend.EXPECT().RPCGasCap().Return(common.Big0).AnyTimes() + mockBackend.EXPECT().RPCEVMTimeout().Return(5 * time.Second).AnyTimes() mockBackend.EXPECT().StateAndHeaderByNumber(any, any).DoAndReturn(getStateAndHeader).AnyTimes() mockBackend.EXPECT().StateAndHeaderByNumberOrHash(any, any).DoAndReturn(getStateAndHeader).AnyTimes() mockBackend.EXPECT().GetEVM(any, any, any, any, any).DoAndReturn(getEVM).AnyTimes() From 9067faf82a70d19659ebca5e4a4713cb79cade35 Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Mon, 15 Jan 2024 15:48:36 +0800 Subject: [PATCH 56/63] fix linter problem --- api/api_ethereum_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/api_ethereum_test.go b/api/api_ethereum_test.go index ddb0a1be57..45b58b4643 100644 --- a/api/api_ethereum_test.go +++ b/api/api_ethereum_test.go @@ -12,9 +12,6 @@ import ( "time" "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/klaytn/klaytn/accounts" mock_accounts "github.com/klaytn/klaytn/accounts/mocks" mock_api "github.com/klaytn/klaytn/api/mocks" @@ -34,6 +31,8 @@ import ( "github.com/klaytn/klaytn/params" "github.com/klaytn/klaytn/rlp" "github.com/klaytn/klaytn/storage/database" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var dummyChainConfigForEthereumAPITest = ¶ms.ChainConfig{ From fb872b9c67eab72ebc8e10bdc4d0b0fa9beb7b17 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Thu, 18 Jan 2024 15:17:12 +0900 Subject: [PATCH 57/63] [RANDAO] Return a proper error type --- consensus/istanbul/backend/randao.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/consensus/istanbul/backend/randao.go b/consensus/istanbul/backend/randao.go index d6c2bd22a8..e38aef4c17 100644 --- a/consensus/istanbul/backend/randao.go +++ b/consensus/istanbul/backend/randao.go @@ -15,7 +15,6 @@ import ( "github.com/klaytn/klaytn/crypto" "github.com/klaytn/klaytn/crypto/bls" "github.com/klaytn/klaytn/params" - "github.com/klaytn/klaytn/storage/statedb" ) // For testing without KIP-113 contract setup @@ -78,16 +77,19 @@ func (p *ChainBlsPubkeyProvider) getAllCached(chain consensus.ChainReader, num * return nil, errors.New("KIP113 address not set in ChainConfig") } } else if chain.Config().IsRandaoForkEnabled(num) { - var err error + // If no state exist at block number `parentNum`, + // return the error `consensus.ErrPrunedAncestor` + pHeader := chain.GetHeaderByNumber(parentNum.Uint64()) + if pHeader == nil { + return nil, consensus.ErrUnknownAncestor + } + _, err := chain.StateAt(pHeader.Hash()) + if err != nil { + return nil, consensus.ErrPrunedAncestor + } kip113Addr, err = system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, parentNum) if err != nil { - if _, ok := err.(*statedb.MissingNodeError); ok { - parentNum = nil - kip113Addr, err = system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, parentNum) - if err != nil { - return nil, err - } - } + return nil, err } } else { return nil, errors.New("Cannot read KIP113 address from registry before Randao fork") From bea77174b89907e66f2a915368947967fe9eef07 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Thu, 18 Jan 2024 15:35:24 +0900 Subject: [PATCH 58/63] [RANDAO] Hash typo fixed --- consensus/istanbul/backend/randao.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/istanbul/backend/randao.go b/consensus/istanbul/backend/randao.go index e38aef4c17..354466d1de 100644 --- a/consensus/istanbul/backend/randao.go +++ b/consensus/istanbul/backend/randao.go @@ -83,7 +83,7 @@ func (p *ChainBlsPubkeyProvider) getAllCached(chain consensus.ChainReader, num * if pHeader == nil { return nil, consensus.ErrUnknownAncestor } - _, err := chain.StateAt(pHeader.Hash()) + _, err := chain.StateAt(pHeader.Root) if err != nil { return nil, consensus.ErrPrunedAncestor } From 844a013e90e9a2d43210dccde410790e23dbcee6 Mon Sep 17 00:00:00 2001 From: "ollie.j" Date: Fri, 19 Jan 2024 17:52:08 +0900 Subject: [PATCH 59/63] Set version v1.12.1 --- params/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/version.go b/params/version.go index cfb5b2bfb8..e8d39495de 100644 --- a/params/version.go +++ b/params/version.go @@ -26,7 +26,7 @@ const ( ReleaseNum = 0 VersionMajor = 1 // Major version component of the current release VersionMinor = 12 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release + VersionPatch = 1 // Patch version component of the current release ) // Version holds the textual version string. From dd67309bd20115cc5b34b81f60010921719d68d0 Mon Sep 17 00:00:00 2001 From: Hyunsoo Shin Date: Fri, 26 Jan 2024 09:34:27 +0900 Subject: [PATCH 60/63] [Downloader] Added error listener --- datasync/downloader/downloader.go | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/datasync/downloader/downloader.go b/datasync/downloader/downloader.go index 53357a55bb..075835996d 100644 --- a/datasync/downloader/downloader.go +++ b/datasync/downloader/downloader.go @@ -1286,22 +1286,22 @@ func (d *Downloader) fetchStakingInfos(from uint64) error { // various callbacks to handle the slight differences between processing them. // // The instrumentation parameters: -// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer) -// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers) -// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`) -// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed) -// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping) -// - pending: task callback for the number of requests still needing download (detect completion/non-completability) -// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish) -// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use) -// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions) -// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic) -// - fetch: network callback to actually send a particular download request to a physical remote peer -// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer) -// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping) -// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks -// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping) -// - kind: textual label of the type being downloaded to display in log mesages +// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer) +// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers) +// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`) +// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed) +// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping) +// - pending: task callback for the number of requests still needing download (detect completion/non-completability) +// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish) +// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use) +// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions) +// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic) +// - fetch: network callback to actually send a particular download request to a physical remote peer +// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer) +// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping) +// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks +// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping) +// - kind: textual label of the type being downloaded to display in log mesages func (d *Downloader) fetchParts(deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool, expire func() map[string]int, pending func() int, inFlight func() bool, reserve func(*peerConnection, int) (*fetchRequest, bool, bool), fetchHook func([]*types.Header), fetch func(*peerConnection, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peerConnection) int, @@ -1703,9 +1703,17 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error { for i, result := range results { blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions) } - if index, err := d.blockchain.InsertChain(blocks); err != nil { - logger.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err) - return fmt.Errorf("%w: %v", errInvalidChain, err) + + for _, block := range blocks { + select { + case <-d.cancelCh: + return errCanceled + default: + if _, err := d.blockchain.InsertChain(types.Blocks{block}); err != nil { + logger.Debug("Downloaded item processing failed", "number", block.Number(), "hash", block.Hash(), "err", err) + return fmt.Errorf("%w: %v", errInvalidChain, err) + } + } } return nil } From 5368f9a7fc8d0d18bd06895bfe3521b93083425d Mon Sep 17 00:00:00 2001 From: "ollie.j" Date: Fri, 26 Jan 2024 11:57:30 +0900 Subject: [PATCH 61/63] api: Print sigHash in getBlockWithConsensusInfo --- cmd/utils/nodecmd/util.go | 11 +++--- consensus/consensus.go | 8 +++-- consensus/istanbul/backend/api.go | 29 +++++---------- consensus/istanbul/backend/api_test.go | 50 ++++++++++++++++++++++++++ consensus/istanbul/backend/engine.go | 47 ++++++++++++------------ 5 files changed, 94 insertions(+), 51 deletions(-) create mode 100644 consensus/istanbul/backend/api_test.go diff --git a/cmd/utils/nodecmd/util.go b/cmd/utils/nodecmd/util.go index 482fe22aca..80e5d2f7db 100644 --- a/cmd/utils/nodecmd/util.go +++ b/cmd/utils/nodecmd/util.go @@ -182,7 +182,7 @@ func parseHeaderFile(headerFile string) (*types.Header, common.Hash, error) { } func decodeExtra(headerFile string) (map[string]interface{}, error) { - header, hash, err := parseHeaderFile(headerFile) + header, sigHash, err := parseHeaderFile(headerFile) if err != nil { return nil, err } @@ -194,21 +194,22 @@ func decodeExtra(headerFile string) (map[string]interface{}, error) { for idx, addr := range istanbulExtra.Validators { validators[idx] = addr.String() } - proposer, err := istanbul.GetSignatureAddress(hash.Bytes(), istanbulExtra.Seal) + proposer, err := istanbul.GetSignatureAddress(sigHash.Bytes(), istanbulExtra.Seal) if err != nil { return nil, err } - committers, cSealsBytes, err := backend.ParseCommitteedSeals(header) + committers, err := backend.RecoverCommittedSeals(istanbulExtra, header.Hash()) if err != nil { return nil, err } cSeals := make([]string, len(istanbulExtra.CommittedSeal)) for i := 0; i < len(cSeals); i++ { - cSeals[i] = hexutil.Encode(cSealsBytes[i]) + cSeals[i] = hexutil.Encode(istanbulExtra.CommittedSeal[i]) } m := make(map[string]interface{}) - m["hash"] = hash + m["hash"] = header.Hash().Hex() + m["sigHash"] = sigHash.Hex() m["validators"] = validators m["seal"] = hexutil.Encode(istanbulExtra.Seal) m["committedSeal"] = cSeals diff --git a/consensus/consensus.go b/consensus/consensus.go index 69c6c3732f..7e1b6a8987 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -65,8 +65,9 @@ type ChainReader interface { StateAt(root common.Hash) (*state.StateDB, error) } -//go:generate mockgen -destination=consensus/mocks/engine_mock.go -package=mocks github.com/klaytn/klaytn/consensus Engine // Engine is an algorithm agnostic consensus engine. +// +//go:generate mockgen -destination=consensus/mocks/engine_mock.go -package=mocks github.com/klaytn/klaytn/consensus Engine type Engine interface { // Author retrieves the Klaytn address of the account that minted the given // block. @@ -168,8 +169,11 @@ type Istanbul interface { } type ConsensusInfo struct { + // Proposer signs [sigHash] to make seal; Validators signs [block.Hash + msgCommit] to make committedSeal + SigHash common.Hash Proposer common.Address - OriginProposer common.Address // the proposal of 0 round at the same block number + OriginProposer common.Address // the proposer of 0th round at the same block number Committee []common.Address + Committers []common.Address Round byte } diff --git a/consensus/istanbul/backend/api.go b/consensus/istanbul/backend/api.go index 6ee9ada006..b988d21b90 100644 --- a/consensus/istanbul/backend/api.go +++ b/consensus/istanbul/backend/api.go @@ -327,15 +327,9 @@ func (api *APIExtension) makeRPCBlockOutput(b *types.Block, } } - committers, _, err := ParseCommitteedSeals(head) - if err != nil { - parseErr := make(map[string]interface{}) - parseErr["ERROR"] = err - return parseErr - } - r["committee"] = cInfo.Committee - r["committers"] = committers + r["committers"] = cInfo.Committers + r["sigHash"] = cInfo.SigHash r["proposer"] = cInfo.Proposer r["round"] = cInfo.Round r["originProposer"] = cInfo.OriginProposer @@ -343,23 +337,16 @@ func (api *APIExtension) makeRPCBlockOutput(b *types.Block, return r } -func ParseCommitteedSeals(header *types.Header) ([]common.Address, [][]byte, error) { - if header == nil { - return nil, nil, errors.New("Empty header") - } - istanbulExtra, err := types.ExtractIstanbulExtra(header) - if err != nil { - return nil, nil, err - } - committers := make([]common.Address, len(istanbulExtra.CommittedSeal)) - for idx, cs := range istanbulExtra.CommittedSeal { - committer, err := istanbul.GetSignatureAddress(istanbulCore.PrepareCommittedSeal(header.Hash()), cs) +func RecoverCommittedSeals(extra *types.IstanbulExtra, headerHash common.Hash) ([]common.Address, error) { + committers := make([]common.Address, len(extra.CommittedSeal)) + for idx, cs := range extra.CommittedSeal { + committer, err := istanbul.GetSignatureAddress(istanbulCore.PrepareCommittedSeal(headerHash), cs) if err != nil { - return nil, nil, err + return nil, err } committers[idx] = committer } - return committers, istanbulExtra.CommittedSeal, nil + return committers, nil } // TODO-Klaytn: This API functions should be managed with API functions with namespace "klay" diff --git a/consensus/istanbul/backend/api_test.go b/consensus/istanbul/backend/api_test.go new file mode 100644 index 0000000000..cb75b6ea58 --- /dev/null +++ b/consensus/istanbul/backend/api_test.go @@ -0,0 +1,50 @@ +package backend + +import ( + "testing" + + "github.com/klaytn/klaytn/blockchain/types" + "github.com/klaytn/klaytn/common" + "github.com/klaytn/klaytn/consensus/istanbul" + istanbulCore "github.com/klaytn/klaytn/consensus/istanbul/core" + "github.com/klaytn/klaytn/rlp" + "github.com/stretchr/testify/assert" +) + +func TestRecoverCommittedSeals(t *testing.T) { + // Baobab debug.getBlockRlp(144654443) + var ( + blockRlp = "f904f1f903c1a046f5775da65a7c9e9a12d2ee4d010285df656ebd64c97cad2669c42c4aa39d8b94f90675a56a03f836204d66c0f923e00500ddc90aa0cec442b90b1d21e465474f7e5c233c3461e950bf383b39227917ad8a608653dfa004de211f466c3543bb2e3db8c976e9d046c214bc33c982d664b7e494d0c2c88aa0390d7c4c79e1e594bcc3236742112d9a7358652e66b1c97dea1c490e69276617b90100000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000002000000000000000000800000000000000000000010000020000000000000010000000000000000000000000000000000000006000000000000000000000004000000000000001240004000000000000000000000000000000000000004800000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000002000000000000000000000000000000000000000400000a00000000000000000000000000000000000000000000184089f406b8302c7158465b315d945b90187d883010c00846b6c617988676f312e32302e36856c696e757800000000000000f90164f85494571e53df607be97431a5bbefca1dffe5aef56f4d945cb1a7dccbd0dc446e3640898ede8820368554c89499fb17d324fa0e07f23b49d09028ac0919414db694b74ff9dea397fe9e231df545eb53fe2adf776cb2b841dc7dca1dc15d06e83a0a39217052ad7915d3b0f0a68f49d4b53498130a1212327b9dd5514362eb3e5f0ebb277e7d38baecad9cd045d3627d3a1f322422eeee8a01f8c9b8410a3fb9227632261c8520f1ee859c503f017701fdb92c2e39f532e190779888a63cf112cf2e530f656153ab64103558c26fb9ca43d5cf31425e62569f891e1eca00b8414a32097b87c481f145ae75ded4e8ec2ea4230c7b2feb3f3afcd2d22ea0b7d4596f4354e185d4f1f93b4d758ddb97e9203e1d306413ad0db1f4a779156879baa300b841fde66a2abb916bf314c82a2dfcd82ed356af829a98573d7cdb4a74cccb6ceecc22358750332ad31dccd9a890dbde7e0d4e21f173515011656f6c222169ba94620080808505d21dba00b860a027d1591d784686d063234bc000a8b9f829da534c41b555c867329b2b4abc5ef8f36245546adcc35c31dbc7c16b084a0d48487817b0eb191ac5b9aa8f3c908d413f19a1e784c3920fc0c0ef9180582902dc30ee8b70859e493af467bc227403a047f8b901fe5f4a3387cb6ccd5c14a00f7649247d2d2d315a652c378f328c1970f9012a31f90126830a596a850ba43b740083061a8094a317038414a275365ed4a085b786e83e761d20a580944dd5fa43054709155d5e68fadd3aeb3f853f34b0b844202ee0ed00000000000000000000000000000000000000000000000000000000000a596a0000000000000000000000000000000000000000000000000000000000000377f847f8458207f5a085d89c94a1e010f90b8e825e5321b1fff91ea748f50424448e17d5f1658498bda053936941c152ce9721447711a80c39b07fb36d42c8a8b6a258d8b071ab3b6c96945e6b99bca5a21818d40d12c56194674989146fc8f847f8458207f5a0c40d29e71cdd4f07051398fa35d41a25cc2f5db165d3ad8aa8b5eff20cc81996a01ea154d91320586165b139870e688a0ab4837137c5208a2da60cbdc60d7c7d81" + expectedProposer = common.HexToAddress("0x5cb1a7dccbd0dc446e3640898ede8820368554c8") + expectedValidators = []common.Address{ // the composition may differ by consensus node. this particular node has received following 3 votes. + common.HexToAddress("0x5cb1a7dccbd0dc446e3640898ede8820368554c8"), + common.HexToAddress("0x571e53df607be97431a5bbefca1dffe5aef56f4d"), + common.HexToAddress("0x99fb17d324fa0e07f23b49d09028ac0919414db6"), + } + ) + + var block *types.Block + assert.Nil(t, rlp.DecodeBytes(common.FromHex(blockRlp), &block)) + extra, err := types.ExtractIstanbulExtra(block.Header()) + assert.Nil(t, err) + + // check block seal + seal := extra.Seal + sigHash := sigHash(block.Header()) + proposer, _ := istanbul.GetSignatureAddress(sigHash[:], seal) + assert.Equal(t, proposer, expectedProposer) + t.Logf("%x %x -> %x\n", sigHash, seal, proposer) + + // check committed seals manually + for i, seal := range extra.CommittedSeal { + sigData := istanbulCore.PrepareCommittedSeal(block.Hash()) + validator, _ := istanbul.GetSignatureAddress(sigData, seal) + assert.Equal(t, validator, expectedValidators[i]) + t.Logf("%x %x -> %x\n", sigData, seal, validator) + } + + // check committed seals using RecoverCommittedSeals + validators, err := RecoverCommittedSeals(extra, block.Hash()) + assert.Nil(t, err) + assert.Equal(t, validators, expectedValidators) +} diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 708fc734f4..3bdbc7549f 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -818,34 +818,35 @@ func (sb *backend) GetConsensusInfo(block *types.Block) (consensus.ConsensusInfo committeeAddrs[i] = v.Address() } - // verify the committee list of the block using istanbul - //proposalSeal := istanbulCore.PrepareCommittedSeal(block.Hash()) - //extra, err := types.ExtractIstanbulExtra(block.Header()) - //istanbulAddrs := make([]common.Address, len(committeeAddrs)) - //for i, seal := range extra.CommittedSeal { - // addr, err := istanbul.GetSignatureAddress(proposalSeal, seal) - // istanbulAddrs[i] = addr - // if err != nil { - // return proposer, []common.Address{}, err - // } - // - // var found bool = false - // for _, v := range committeeAddrs { - // if addr == v { - // found = true - // break - // } - // } - // if found == false { - // logger.Trace("validator is different!", "snap", committeeAddrs, "istanbul", istanbulAddrs) - // return proposer, committeeAddrs, errors.New("validator set is different from Istanbul engine!!") - // } - //} + // get the committers of this block from committed seals + extra, err := types.ExtractIstanbulExtra(block.Header()) + if err != nil { + return consensus.ConsensusInfo{}, err + } + committers, err := RecoverCommittedSeals(extra, block.Hash()) + if err != nil { + return consensus.ConsensusInfo{}, err + } + + // Uncomment to validate if committers are in the committee + // for _, recovered := range committers { + // found := false + // for _, calculated := range committeeAddrs { + // if recovered == calculated { + // found = true + // } + // } + // if !found { + // return consensus.ConsensusInfo{}, errInvalidCommittedSeals + // } + // } cInfo := consensus.ConsensusInfo{ + SigHash: sigHash(block.Header()), Proposer: proposer, OriginProposer: originProposer, Committee: committeeAddrs, + Committers: committers, Round: round, } From a688902457416f8f208b47f4a524b89beba5db9b Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Mon, 29 Jan 2024 12:13:02 +0900 Subject: [PATCH 62/63] Set cypress cancun/randao hardfork --- params/config.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/params/config.go b/params/config.go index 0a8235de79..676656c0fb 100644 --- a/params/config.go +++ b/params/config.go @@ -48,10 +48,17 @@ var ( MagmaCompatibleBlock: big.NewInt(99841497), KoreCompatibleBlock: big.NewInt(119750400), ShanghaiCompatibleBlock: big.NewInt(135456000), - CancunCompatibleBlock: nil, // TODO-Klaytn-Cancun: set Cypress CancunCompatibleBlock - Kip103CompatibleBlock: big.NewInt(119750400), - Kip103ContractAddress: common.HexToAddress("0xD5ad6D61Dd87EdabE2332607C328f5cc96aeCB95"), - DeriveShaImpl: 2, + CancunCompatibleBlock: big.NewInt(147534000), + RandaoCompatibleBlock: big.NewInt(147534000), + RandaoRegistry: &RegistryConfig{ + Records: map[string]common.Address{ + "KIP113": common.HexToAddress("0x3e80e75975bdb8e04B800485DD28BebeC6d97679"), + }, + Owner: common.HexToAddress("0x04992a2B7E7CE809d409adE32185D49A96AAa32d"), + }, + Kip103CompatibleBlock: big.NewInt(119750400), + Kip103ContractAddress: common.HexToAddress("0xD5ad6D61Dd87EdabE2332607C328f5cc96aeCB95"), + DeriveShaImpl: 2, Governance: &GovernanceConfig{ GoverningNode: common.HexToAddress("0x52d41ca72af615a1ac3301b0a93efa222ecc7541"), GovernanceMode: "single", From f950731fa02237c207bfa71a4944b94d112dcae0 Mon Sep 17 00:00:00 2001 From: "yumiel.ko" Date: Wed, 31 Jan 2024 14:19:24 +0800 Subject: [PATCH 63/63] api: enhance missing node error log at stateAtBlock --- node/cn/state_accessor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/cn/state_accessor.go b/node/cn/state_accessor.go index 1bb75bb582..694a367e97 100644 --- a/node/cn/state_accessor.go +++ b/node/cn/state_accessor.go @@ -101,7 +101,7 @@ func (cn *CN) stateAtBlock(block *types.Block, reexec uint64, base *state.StateD if err != nil { switch err.(type) { case *statedb2.MissingNodeError: - return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec) + return nil, fmt.Errorf("historical state unavailable. tried regeneration but not possible, possibly due to state migration/pruning or global state saving interval is bigger than reexec value (reexec=%d)", reexec) default: return nil, err }