Skip to content

Commit

Permalink
Merge pull request #2095 from klaytn/release/v1.12.1
Browse files Browse the repository at this point in the history
[Master] release/v1.12.1 QA Sign-off
  • Loading branch information
blukat29 committed Feb 1, 2024
2 parents 6f2b639 + c4e3de2 commit 4831f49
Show file tree
Hide file tree
Showing 46 changed files with 691 additions and 544 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
122 changes: 106 additions & 16 deletions api/api_ethereum.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package api
import (
"context"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"math/big"
Expand All @@ -27,6 +28,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"
Expand Down Expand Up @@ -392,37 +394,125 @@ 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)
}

// 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
}
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.
Expand Down Expand Up @@ -1346,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
}
Expand Down Expand Up @@ -1401,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
Expand Down
12 changes: 6 additions & 6 deletions api/api_ethereum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@ import (
"math/big"
"reflect"
"testing"

"github.com/stretchr/testify/require"

"github.com/klaytn/klaytn/consensus"
"github.com/klaytn/klaytn/crypto"
"github.com/klaytn/klaytn/governance"
"time"

"github.com/golang/mock/gomock"
"github.com/klaytn/klaytn/accounts"
Expand All @@ -27,13 +22,17 @@ 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"
"github.com/stretchr/testify/require"
)

var dummyChainConfigForEthereumAPITest = &params.ChainConfig{
Expand Down Expand Up @@ -2489,6 +2488,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()
Expand Down
49 changes: 17 additions & 32 deletions api/api_private_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ 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
Expand All @@ -45,42 +43,29 @@ 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) {
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 {
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 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
}
logger.Info("Compacting database completed", "range", fmt.Sprintf("%#X-%#X", start, end), "elapsed", common.PrettyDuration(time.Since(cstart)))
}
return nil
}
Expand Down

0 comments on commit 4831f49

Please sign in to comment.