From 8c572167532bf5adbc0541656305b2f790f35fee Mon Sep 17 00:00:00 2001 From: Shogo Hyodo Date: Mon, 3 Feb 2025 14:44:10 +0900 Subject: [PATCH 1/7] Fix ToBlock --- blockchain/genesis.go | 16 ++++- blockchain/genesis_test.go | 135 +++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/blockchain/genesis.go b/blockchain/genesis.go index 9cb579ce6..a45cf92e2 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -33,6 +33,7 @@ import ( "github.com/kaiachain/kaia/blockchain/state" "github.com/kaiachain/kaia/blockchain/types" + "github.com/kaiachain/kaia/blockchain/types/accountkey" "github.com/kaiachain/kaia/common" "github.com/kaiachain/kaia/common/hexutil" "github.com/kaiachain/kaia/common/math" @@ -307,10 +308,21 @@ func (g *Genesis) ToBlock(baseStateRoot common.Hash, db database.DBManager) *typ db = database.NewMemoryDBManager() } stateDB, _ := state.New(baseStateRoot, state.NewDatabase(db), nil, nil) + rules := params.Rules{} + if g.Config != nil { + rules = g.Config.Rules(new(big.Int).SetUint64(g.Number)) + } for addr, account := range g.Alloc { - if len(account.Code) != 0 { - originalCode := stateDB.GetCode(addr) + originalCode := stateDB.GetCode(addr) + if _, ok := types.ParseDelegation(account.Code); ok && rules.IsPrague { + stateDB.SetCodeToEOA(addr, account.Code, rules) + } else if len(account.Code) == 0 && len(account.Storage) != 0 && rules.IsPrague { + stateDB.CreateEOA(addr, false, accountkey.NewAccountKeyLegacy()) + } else if len(account.Code) != 0 { + stateDB.CreateSmartContractAccount(addr, params.CodeFormatEVM, rules) stateDB.SetCode(addr, account.Code) + } + if len(account.Code) != 0 { // If originalCode is not nil, // just update the code and don't change the other states if originalCode != nil { diff --git a/blockchain/genesis_test.go b/blockchain/genesis_test.go index dad830940..3abe21bcf 100644 --- a/blockchain/genesis_test.go +++ b/blockchain/genesis_test.go @@ -26,16 +26,21 @@ import ( "fmt" "math/big" "reflect" + "strings" "testing" "github.com/davecgh/go-spew/spew" + "github.com/kaiachain/kaia/blockchain/state" "github.com/kaiachain/kaia/blockchain/types" + "github.com/kaiachain/kaia/blockchain/types/account" "github.com/kaiachain/kaia/blockchain/vm" "github.com/kaiachain/kaia/common" + "github.com/kaiachain/kaia/common/hexutil" "github.com/kaiachain/kaia/consensus/gxhash" "github.com/kaiachain/kaia/params" "github.com/kaiachain/kaia/storage/database" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TestDefaultGenesisBlock tests the genesis block generation functions: DefaultGenesisBlock, DefaultKairosGenesisBlock @@ -371,6 +376,136 @@ func TestGenesisRestoreState(t *testing.T) { assert.True(t, ok) } +// TestCodeInfo tests the genesis accounts +func TestGenesisAccount(t *testing.T) { + // test account address + addr := common.HexToAddress("0x0100000000000000000000000000000000000000") + + // accumulate forks from Kairos config + v := reflect.ValueOf(*params.KairosChainConfig.Copy()) + forks := map[string]*big.Int{"EmptyForkCompatibleBlock": big.NewInt(0)} + for i := 0; i < v.NumField(); i++ { + if strings.HasSuffix(v.Type().Field(i).Name, "CompatibleBlock") { + forks[v.Type().Field(i).Name] = v.Field(i).Interface().(*big.Int) + } + } + + tcs := map[string]struct { + account GenesisAccount + test func(*testing.T, params.Rules, params.VmVersion, bool, account.Account) + }{ + "simple EOA": { + account: GenesisAccount{ + Balance: big.NewInt(0), + }, + test: func(t *testing.T, r params.Rules, vmv params.VmVersion, ok bool, acc account.Account) { + require.False(t, ok) + require.Equal(t, params.VmVersion0, vmv) + require.Equal(t, account.ExternallyOwnedAccountType, acc.Type()) + }, + }, + "simple SCA": { + account: GenesisAccount{ + Balance: big.NewInt(0), + Code: hexutil.MustDecode("0x00"), + }, + test: func(t *testing.T, r params.Rules, vmv params.VmVersion, ok bool, acc account.Account) { + if !r.IsIstanbul { + require.True(t, ok) + require.Equal(t, params.VmVersion0, vmv) + require.Equal(t, account.SmartContractAccountType, acc.Type()) + } else if r.IsIstanbul { + require.True(t, ok) + require.Equal(t, params.VmVersion1, vmv) + require.Equal(t, account.SmartContractAccountType, acc.Type()) + } + }, + }, + "account with delegation code": { + account: GenesisAccount{ + Balance: big.NewInt(0), + Code: types.AddressToDelegation(common.HexToAddress("0x000000000000000000000000000000000000000")), + }, + test: func(t *testing.T, r params.Rules, vmv params.VmVersion, ok bool, acc account.Account) { + if !r.IsIstanbul { + require.True(t, ok) + require.Equal(t, params.VmVersion0, vmv) + require.Equal(t, account.SmartContractAccountType, acc.Type()) + } else if r.IsIstanbul && !r.IsPrague { + require.True(t, ok) + require.Equal(t, params.VmVersion1, vmv) + require.Equal(t, account.SmartContractAccountType, acc.Type()) + } else { + require.True(t, ok) + require.Equal(t, params.VmVersion1, vmv) + require.Equal(t, account.ExternallyOwnedAccountType, acc.Type()) + } + }, + }, + "account with empty code but non-empty storage": { + account: GenesisAccount{ + Balance: big.NewInt(0), + Storage: map[common.Hash]common.Hash{{1}: {1}}, + }, + test: func(t *testing.T, r params.Rules, vmv params.VmVersion, ok bool, acc account.Account) { + if !r.IsPrague { + require.True(t, ok) + require.Equal(t, params.VmVersion0, vmv) + require.Equal(t, account.SmartContractAccountType, acc.Type()) + } else { + require.False(t, ok) + require.Equal(t, params.VmVersion0, vmv) + require.Equal(t, account.ExternallyOwnedAccountType, acc.Type()) + } + }, + }, + } + + // test all cases for each fork + for tcn, tc := range tcs { + for fork, n := range forks { + t.Run(fmt.Sprintf("%s fork %s", tcn, fork), func(t *testing.T) { + genesis := &Genesis{ + Config: ¶ms.ChainConfig{ + ChainID: new(big.Int).SetUint64(uint64(1))}, + Alloc: GenesisAlloc{ + addr: tc.account, + }, + } + + // make config for fork + for f2, n2 := range forks { + var n3 *big.Int + if n == nil { + n3 = big.NewInt(0) + } else if n2 != nil && n.Cmp(n2) >= 0 { + n3 = big.NewInt(0) + } + r := reflect.ValueOf(genesis.Config) + f := reflect.Indirect(r).FieldByName(f2) + if f.Kind() != reflect.Invalid { + f.Set(reflect.ValueOf(n3)) + } + } + + genesis.Config.SetDefaultsForGenesis() + genesis.Governance = SetGenesisGovernance(genesis) + InitDeriveSha(genesis.Config) + + db := database.NewMemoryDBManager() + gblock := genesis.ToBlock(common.Hash{}, db) + stateDB, _ := state.New(gblock.Root(), state.NewDatabase(db), nil, nil) + + rules := genesis.Config.Rules(new(big.Int).SetUint64(genesis.Number)) + vmVersion, ok := stateDB.GetVmVersion(addr) + account := stateDB.GetAccount(addr) + + tc.test(t, rules, vmVersion, ok, account) + }) + } + } +} + func genMainnetGenesisBlock() *Genesis { genesis := DefaultGenesisBlock() genesis.Config = params.MainnetChainConfig.Copy() From df865daff990f3ee01fd5afd5b6909a333082a4a Mon Sep 17 00:00:00 2001 From: Shogo Hyodo Date: Tue, 4 Feb 2025 16:12:44 +0900 Subject: [PATCH 2/7] Fix for lint --- blockchain/genesis_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blockchain/genesis_test.go b/blockchain/genesis_test.go index 3abe21bcf..10ef464da 100644 --- a/blockchain/genesis_test.go +++ b/blockchain/genesis_test.go @@ -467,7 +467,8 @@ func TestGenesisAccount(t *testing.T) { t.Run(fmt.Sprintf("%s fork %s", tcn, fork), func(t *testing.T) { genesis := &Genesis{ Config: ¶ms.ChainConfig{ - ChainID: new(big.Int).SetUint64(uint64(1))}, + ChainID: new(big.Int).SetUint64(1), + }, Alloc: GenesisAlloc{ addr: tc.account, }, From f259a847d9530582dfdccd09122b72e56dfcae19 Mon Sep 17 00:00:00 2001 From: Shogo Hyodo Date: Wed, 5 Feb 2025 13:11:10 +0900 Subject: [PATCH 3/7] Remove nested branch --- blockchain/genesis.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/blockchain/genesis.go b/blockchain/genesis.go index a45cf92e2..f854d483d 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -322,13 +322,11 @@ func (g *Genesis) ToBlock(baseStateRoot common.Hash, db database.DBManager) *typ stateDB.CreateSmartContractAccount(addr, params.CodeFormatEVM, rules) stateDB.SetCode(addr, account.Code) } - if len(account.Code) != 0 { - // If originalCode is not nil, - // just update the code and don't change the other states - if originalCode != nil { - logger.Warn("this address already has a not nil code, now the code of this address has been changed", "addr", addr.String()) - continue - } + // If account.Code is nil and originalCode is not nil, + // just update the code and don't change the other states + if len(account.Code) != 0 && originalCode != nil { + logger.Warn("this address already has a not nil code, now the code of this address has been changed", "addr", addr.String()) + continue } for key, value := range account.Storage { stateDB.SetState(addr, key, value) From d6ffd9845d77ae14e5d13d96cdd9febc6fcef52e Mon Sep 17 00:00:00 2001 From: Shogo Hyodo Date: Thu, 6 Feb 2025 16:32:59 +0900 Subject: [PATCH 4/7] Update blockchain/genesis.go Co-authored-by: Yunjong Jeong (ollie) <5933330+blukat29@users.noreply.github.com> --- blockchain/genesis.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockchain/genesis.go b/blockchain/genesis.go index f854d483d..3b81550a4 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -315,10 +315,13 @@ func (g *Genesis) ToBlock(baseStateRoot common.Hash, db database.DBManager) *typ for addr, account := range g.Alloc { originalCode := stateDB.GetCode(addr) if _, ok := types.ParseDelegation(account.Code); ok && rules.IsPrague { + // EOA with code. Usually SetCodeTx creates it. Unit tests may create it here in the genesis. stateDB.SetCodeToEOA(addr, account.Code, rules) } else if len(account.Code) == 0 && len(account.Storage) != 0 && rules.IsPrague { + // Represents an EOA that had code then nullified with another SetCodeTx. Unit tests may create it here in the genesis. stateDB.CreateEOA(addr, false, accountkey.NewAccountKeyLegacy()) } else if len(account.Code) != 0 { + // Regular genesis smart contract account. stateDB.CreateSmartContractAccount(addr, params.CodeFormatEVM, rules) stateDB.SetCode(addr, account.Code) } From 7ac816785e2548fdd43878f1fc82fd1834772a7a Mon Sep 17 00:00:00 2001 From: Shogo Hyodo Date: Thu, 6 Feb 2025 16:51:10 +0900 Subject: [PATCH 5/7] Use switch statement --- blockchain/genesis.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/blockchain/genesis.go b/blockchain/genesis.go index 3b81550a4..766662379 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -314,13 +314,14 @@ func (g *Genesis) ToBlock(baseStateRoot common.Hash, db database.DBManager) *typ } for addr, account := range g.Alloc { originalCode := stateDB.GetCode(addr) - if _, ok := types.ParseDelegation(account.Code); ok && rules.IsPrague { + switch _, ok := types.ParseDelegation(account.Code); { + case ok && rules.IsPrague: // EOA with code. Usually SetCodeTx creates it. Unit tests may create it here in the genesis. stateDB.SetCodeToEOA(addr, account.Code, rules) - } else if len(account.Code) == 0 && len(account.Storage) != 0 && rules.IsPrague { + case len(account.Code) == 0 && len(account.Storage) != 0 && rules.IsPrague: // Represents an EOA that had code then nullified with another SetCodeTx. Unit tests may create it here in the genesis. stateDB.CreateEOA(addr, false, accountkey.NewAccountKeyLegacy()) - } else if len(account.Code) != 0 { + case len(account.Code) != 0: // Regular genesis smart contract account. stateDB.CreateSmartContractAccount(addr, params.CodeFormatEVM, rules) stateDB.SetCode(addr, account.Code) From a2597fce98718f3a7e6c6e908030c81fbeccf66b Mon Sep 17 00:00:00 2001 From: Shogo Hyodo Date: Thu, 6 Feb 2025 18:12:30 +0900 Subject: [PATCH 6/7] Update blockchain/genesis.go Co-authored-by: murogari <55307968+Mdaiki0730@users.noreply.github.com> --- blockchain/genesis.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/blockchain/genesis.go b/blockchain/genesis.go index 766662379..5e556dbe1 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -314,14 +314,18 @@ func (g *Genesis) ToBlock(baseStateRoot common.Hash, db database.DBManager) *typ } for addr, account := range g.Alloc { originalCode := stateDB.GetCode(addr) - switch _, ok := types.ParseDelegation(account.Code); { - case ok && rules.IsPrague: + _, ok := types.ParseDelegation(account.Code) + isEOAWithCode := ok && rules.IsPrague + isEOAWithoutCode := len(account.Code) == 0 && len(account.Storage) != 0 && rules.IsPrague + isAccountWithCode := len(account.Code) != 0 + switch { + case isEOAWithCode: // EOA with code. Usually SetCodeTx creates it. Unit tests may create it here in the genesis. stateDB.SetCodeToEOA(addr, account.Code, rules) - case len(account.Code) == 0 && len(account.Storage) != 0 && rules.IsPrague: + case isEOAWithoutCode: // Represents an EOA that had code then nullified with another SetCodeTx. Unit tests may create it here in the genesis. stateDB.CreateEOA(addr, false, accountkey.NewAccountKeyLegacy()) - case len(account.Code) != 0: + case isAccountWithCode: // Regular genesis smart contract account. stateDB.CreateSmartContractAccount(addr, params.CodeFormatEVM, rules) stateDB.SetCode(addr, account.Code) From 2d8564f409d21a087e6f51349495f1f20f59ef8c Mon Sep 17 00:00:00 2001 From: Shogo Hyodo Date: Thu, 6 Feb 2025 18:14:10 +0900 Subject: [PATCH 7/7] Fix a bit --- blockchain/genesis.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockchain/genesis.go b/blockchain/genesis.go index 5e556dbe1..7fde1de92 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -317,7 +317,7 @@ func (g *Genesis) ToBlock(baseStateRoot common.Hash, db database.DBManager) *typ _, ok := types.ParseDelegation(account.Code) isEOAWithCode := ok && rules.IsPrague isEOAWithoutCode := len(account.Code) == 0 && len(account.Storage) != 0 && rules.IsPrague - isAccountWithCode := len(account.Code) != 0 + isSCAWithCode := !isEOAWithCode && len(account.Code) != 0 switch { case isEOAWithCode: // EOA with code. Usually SetCodeTx creates it. Unit tests may create it here in the genesis. @@ -325,7 +325,7 @@ func (g *Genesis) ToBlock(baseStateRoot common.Hash, db database.DBManager) *typ case isEOAWithoutCode: // Represents an EOA that had code then nullified with another SetCodeTx. Unit tests may create it here in the genesis. stateDB.CreateEOA(addr, false, accountkey.NewAccountKeyLegacy()) - case isAccountWithCode: + case isSCAWithCode: // Regular genesis smart contract account. stateDB.CreateSmartContractAccount(addr, params.CodeFormatEVM, rules) stateDB.SetCode(addr, account.Code)