diff --git a/.circleci/config.yml b/.circleci/config.yml index 912ab4218..36f9ac7f6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -130,6 +130,9 @@ commands: os-network: type: string default: "linux-amd64" + is-test: + type: boolean + default: false steps: - checkout - run: @@ -137,7 +140,9 @@ commands: command: | if [[ << parameters.os-network >> = "darwin-arm64" ]]; then # install awscli - brew install awscli + curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" + sudo softwareupdate --install-rosetta --agree-to-license + sudo installer -pkg AWSCLIV2.pkg -target / # install golang curl -O https://dl.google.com/go/go1.23.3.darwin-arm64.tar.gz mkdir $HOME/go1.23.3 @@ -168,28 +173,32 @@ commands: for item in kcn kpn ken; do ./build/package-<< parameters.package-type >>.sh -b $second_parameter $item done - - run: - name: "upload << parameters.package-type >>-<> packages to S3 repo" - command: | - source ~/.bashrc - KAIA_VERSION=$(go run build/rpm/main.go version) - PLATFORM_SUFFIX=$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m) - - for item in kcn kpn ken kcn-kairos kpn-kairos ken-kairos kgen kscn kbn kspn ksen homi; do - if [[ << parameters.package-type >> = "tar" ]]; then - aws s3 cp packages/${item}-v*.tar.gz s3://$FRONTEND_BUCKET/packages/kaia/$KAIA_VERSION/ - elif [[ << parameters.package-type >> = "rpm" ]]; then - BINARY=$item - KAIROS="" - if [[ $BINARY = *-kairos ]]; then - BINARY="${BINARY%-kairos}" - KAIROS="-kairos" - fi - TARGET_RPM=$(find $BINARY-$PLATFORM_SUFFIX/rpmbuild/RPMS/$(uname -m)/ | awk -v pat="$BINARY(d)?$KAIROS-v" '$0~pat') - aws s3 cp $TARGET_RPM s3://$FRONTEND_BUCKET/packages/rhel/9-stream/kaia/ - aws s3 cp $TARGET_RPM s3://$FRONTEND_BUCKET/packages/kaia/$KAIA_VERSION/ - fi - done + - when: + condition: + not: << parameters.is-test >> + steps: + - run: + name: "upload << parameters.package-type >>-<> packages to S3 repo" + command: | + source ~/.bashrc + KAIA_VERSION=$(go run build/rpm/main.go version) + PLATFORM_SUFFIX=$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m) + + for item in kcn kpn ken kcn-kairos kpn-kairos ken-kairos kgen kscn kbn kspn ksen homi; do + if [[ << parameters.package-type >> = "tar" ]]; then + aws s3 cp packages/${item}-v*.tar.gz s3://$FRONTEND_BUCKET/packages/kaia/$KAIA_VERSION/ + elif [[ << parameters.package-type >> = "rpm" ]]; then + BINARY=$item + KAIROS="" + if [[ $BINARY = *-kairos ]]; then + BINARY="${BINARY%-kairos}" + KAIROS="-kairos" + fi + TARGET_RPM=$(find $BINARY-$PLATFORM_SUFFIX/rpmbuild/RPMS/$(uname -m)/ | awk -v pat="$BINARY(d)?$KAIROS-v" '$0~pat') + aws s3 cp $TARGET_RPM s3://$FRONTEND_BUCKET/packages/rhel/9-stream/kaia/ + aws s3 cp $TARGET_RPM s3://$FRONTEND_BUCKET/packages/kaia/$KAIA_VERSION/ + fi + done createrepo-update: steps: - run: @@ -475,30 +484,56 @@ jobs: - packaging-and-upload: os-network: "linux-amd64" package-type: "rpm" + is-test: false +# - store_artifacts: +# path: /go/src/github.com/kaiachain/kaia/ken-linux-x86_64/rpmbuild/RPMS/x86_64/kend-v2.0.0-0.el9.x86_64.rpm +# destination: kend-v2.0.0-0.el9.x86_64.rpm +# - store_artifacts: +# path: /go/src/github.com/kaiachain/kaia/ken-linux-x86_64/rpmbuild/RPMS/x86_64/kend-kairos-v2.0.0-0.el9.x86_64.rpm +# destination: kend-kairos-v2.0.0-0.el9.x86_64.rpm tar-linux-amd64-packaging: executor: tar-linux-amd64-executor steps: - packaging-and-upload: os-network: "linux-amd64" package-type: "tar" + is-test: false +# - store_artifacts: +# path: packages +# destination: packages rpm-linux-arm64-packaging: executor: rpm-linux-arm64-executor steps: - packaging-and-upload: os-network: "linux-arm64" package-type: "rpm" + is-test: false +# - store_artifacts: +# path: /go/src/github.com/kaiachain/kaia/ken-linux-aarch64/rpmbuild/RPMS/aarch64/kend-v2.0.0-0.el9.aarch64.rpm +# destination: kend-v2.0.0-0.el9.aarch64.rpm +# - store_artifacts: +# path: /go/src/github.com/kaiachain/kaia/ken-linux-aarch64/rpmbuild/RPMS/aarch64/kend-kairos-v2.0.0-0.el9.aarch64.rpm +# destination: kend-kairos-v2.0.0-0.el9.aarch64.rpm tar-linux-arm64-packaging: executor: tar-linux-arm64-executor steps: - packaging-and-upload: os-network: "linux-arm64" package-type: "tar" + is-test: false +# - store_artifacts: +# path: packages +# destination: packages tar-darwin-arm64-packaging: executor: tar-darwin-arm64-executor steps: - packaging-and-upload: os-network: "darwin-arm64" package-type: "tar" + is-test: false +# - store_artifacts: +# path: packages +# destination: packages deploy-rpm-public: executor: rpm-linux-amd64-executor steps: diff --git a/accounts/abi/bind/backends/blockchain.go b/accounts/abi/bind/backends/blockchain.go index d8890ba5e..c39045bf8 100644 --- a/accounts/abi/bind/backends/blockchain.go +++ b/accounts/abi/bind/backends/blockchain.go @@ -156,13 +156,13 @@ func (b *BlockchainContractBackend) callContract(call kaia.CallMsg, block *types if call.AccessList != nil { accessList = *call.AccessList } - intrinsicGas, err := types.IntrinsicGas(call.Data, accessList, nil, call.To == nil, b.bc.Config().Rules(block.Number())) + intrinsicGas, dataTokens, err := types.IntrinsicGas(call.Data, accessList, nil, call.To == nil, b.bc.Config().Rules(block.Number())) if err != nil { return nil, err } msg := types.NewMessage(call.From, call.To, 0, call.Value, call.Gas, gasPrice, nil, nil, call.Data, - false, intrinsicGas, accessList, nil) + false, intrinsicGas, dataTokens, accessList, nil) txContext := blockchain.NewEVMTxContext(msg, block.Header(), b.bc.Config()) blockContext := blockchain.NewEVMBlockContext(block.Header(), b.bc, nil) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 58ab5b7e1..7181ed9a1 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -500,8 +500,8 @@ func (b *SimulatedBackend) callContract(_ context.Context, call kaia.CallMsg, bl if call.AccessList != nil { accessList = *call.AccessList } - intrinsicGas, _ := types.IntrinsicGas(call.Data, accessList, nil, call.To == nil, b.config.Rules(block.Number())) - msg := types.NewMessage(call.From, call.To, nonce, call.Value, call.Gas, gasPrice, nil, nil, call.Data, true, intrinsicGas, accessList, nil) + intrinsicGas, dataTokens, _ := types.IntrinsicGas(call.Data, accessList, nil, call.To == nil, b.config.Rules(block.Number())) + msg := types.NewMessage(call.From, call.To, nonce, call.Value, call.Gas, gasPrice, nil, nil, call.Data, true, intrinsicGas, dataTokens, accessList, nil) txContext := blockchain.NewEVMTxContext(msg, block.Header(), b.config) blockContext := blockchain.NewEVMBlockContext(block.Header(), b.blockchain, nil) diff --git a/api/api_ethereum.go b/api/api_ethereum.go index e6c87a3e5..a5ecca429 100644 --- a/api/api_ethereum.go +++ b/api/api_ethereum.go @@ -1413,11 +1413,11 @@ func EthDoCall(ctx context.Context, b Backend, args EthTransactionArgs, blockNrO } else { baseFee = new(big.Int).SetUint64(params.ZeroBaseFee) } - intrinsicGas, err := types.IntrinsicGas(args.data(), args.GetAccessList(), nil, args.To == nil, b.ChainConfig().Rules(header.Number)) + intrinsicGas, dataTokens, err := types.IntrinsicGas(args.data(), args.GetAccessList(), nil, args.To == nil, b.ChainConfig().Rules(header.Number)) if err != nil { return nil, err } - msg, err := args.ToMessage(globalGasCap, baseFee, intrinsicGas) + msg, err := args.ToMessage(globalGasCap, baseFee, intrinsicGas, dataTokens) if err != nil { return nil, err } @@ -1432,7 +1432,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{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}) + evm, vmError, err := b.GetEVM(ctx, msg, state, header, vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite, UseConsoleLog: b.IsConsoleLogEnabled()}) if err != nil { return nil, err } @@ -1570,11 +1570,11 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH precompiles := vm.ActivePrecompiles(rules) toMsg := func() (*types.Transaction, error) { - intrinsicGas, err := types.IntrinsicGas(args.data(), nil, nil, args.To == nil, rules) + intrinsicGas, dataTokens, err := types.IntrinsicGas(args.data(), nil, nil, args.To == nil, rules) if err != nil { return nil, err } - return args.ToMessage(gasCap, header.BaseFee, intrinsicGas) + return args.ToMessage(gasCap, header.BaseFee, intrinsicGas, dataTokens) } if args.Gas == nil { diff --git a/api/api_ethereum_test.go b/api/api_ethereum_test.go index a9265e8eb..2964a7d39 100644 --- a/api/api_ethereum_test.go +++ b/api/api_ethereum_test.go @@ -217,8 +217,6 @@ func testGetHeader(t *testing.T, testAPIName string, config *params.ChainConfig) // Author is called when calculates miner field of Header. dummyMiner := common.HexToAddress("0x9712f943b296758aaae79944ec975884188d3a96") mockEngine.EXPECT().Author(gomock.Any()).Return(dummyMiner, nil) - var dummyTotalDifficulty uint64 = 5 - mockBackend.EXPECT().GetTd(gomock.Any()).Return(new(big.Int).SetUint64(dummyTotalDifficulty)) // Create dummy header header := types.CopyHeader(&types.Header{ @@ -323,8 +321,6 @@ func testGetBlock(t *testing.T, testAPIName string, fullTxs bool) { // Author is called when calculates miner field of Header. dummyMiner := common.HexToAddress("0x9712f943b296758aaae79944ec975884188d3a96") mockEngine.EXPECT().Author(gomock.Any()).Return(dummyMiner, nil) - var dummyTotalDifficulty uint64 = 5 - mockBackend.EXPECT().GetTd(gomock.Any()).Return(new(big.Int).SetUint64(dummyTotalDifficulty)) // Create dummy header header := types.CopyHeader(&types.Header{ @@ -2492,6 +2488,7 @@ func testEstimateGas(t *testing.T, mockBackend *mock_api.MockBackend, fnEstimate 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() + mockBackend.EXPECT().IsConsoleLogEnabled().Return(false).AnyTimes() testcases := []struct { args EthTransactionArgs diff --git a/api/api_public_blockchain.go b/api/api_public_blockchain.go index 463c75cf0..a236714d0 100644 --- a/api/api_public_blockchain.go +++ b/api/api_public_blockchain.go @@ -273,6 +273,11 @@ func (s *PublicBlockChainAPI) IsSenderTxHashIndexingEnabled() bool { return s.b.IsSenderTxHashIndexingEnabled() } +// IsConsoleLogEnabled returns if console log is enabled or not. +func (s *PublicBlockChainAPI) IsConsoleLogEnabled() bool { + return s.b.IsConsoleLogEnabled() +} + // CallArgs represents the arguments for a call. type CallArgs struct { From common.Address `json:"from"` @@ -327,7 +332,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo // this makes sure resources are cleaned up. defer cancel() - intrinsicGas, err := types.IntrinsicGas(args.InputData(), args.GetAccessList(), nil, args.To == nil, b.ChainConfig().Rules(header.Number)) + intrinsicGas, dataTokens, err := types.IntrinsicGas(args.InputData(), args.GetAccessList(), nil, args.To == nil, b.ChainConfig().Rules(header.Number)) if err != nil { return nil, 0, err } @@ -339,7 +344,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo } else { baseFee = new(big.Int).SetUint64(params.ZeroBaseFee) } - msg, err := args.ToMessage(globalGasCap.Uint64(), baseFee, intrinsicGas) + msg, err := args.ToMessage(globalGasCap.Uint64(), baseFee, intrinsicGas, dataTokens) if err != nil { return nil, 0, err } @@ -387,7 +392,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{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite}, s.b.RPCEVMTimeout(), gasCap) + result, _, err := DoCall(ctx, s.b, args, blockNrOrHash, vm.Config{ComputationCostLimit: params.OpcodeComputationCostLimitInfinite, UseConsoleLog: s.b.IsConsoleLogEnabled()}, s.b.RPCEVMTimeout(), gasCap) if err != nil { return nil, err } @@ -595,7 +600,7 @@ func RpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, config *params.Cha } rules := config.Rules(b.Number()) - if rules.IsEthTxType { + if rules.IsMagma { if head.BaseFee == nil { fields["baseFeePerGas"] = (*hexutil.Big)(new(big.Int).SetUint64(params.ZeroBaseFee)) } else { @@ -689,7 +694,7 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash, config *pa return nil } -func (args *CallArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, intrinsicGas uint64) (*types.Transaction, error) { +func (args *CallArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, intrinsicGas uint64, dataTokens uint64) (*types.Transaction, error) { if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } else if args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil { @@ -737,5 +742,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, nil, nil, args.InputData(), false, intrinsicGas, accessList, nil), nil + return types.NewMessage(addr, args.To, 0, value, gas, gasPrice, nil, nil, args.InputData(), false, intrinsicGas, dataTokens, accessList, nil), nil } diff --git a/api/backend.go b/api/backend.go index 4b4d64d38..10d4b01ef 100644 --- a/api/backend.go +++ b/api/backend.go @@ -82,6 +82,7 @@ type Backend interface { IsParallelDBWrite() bool IsSenderTxHashIndexingEnabled() bool + IsConsoleLogEnabled() bool // TxPool API SendTx(ctx context.Context, signedTx *types.Transaction) error diff --git a/api/mocks/backend_mock.go b/api/mocks/backend_mock.go index aafe267ab..5a6c626ff 100644 --- a/api/mocks/backend_mock.go +++ b/api/mocks/backend_mock.go @@ -282,20 +282,6 @@ func (mr *MockBackendMockRecorder) GetPoolTransactions() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPoolTransactions", reflect.TypeOf((*MockBackend)(nil).GetPoolTransactions)) } -// GetTd mocks base method. -func (m *MockBackend) GetTd(arg0 common.Hash) *big.Int { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTd", arg0) - ret0, _ := ret[0].(*big.Int) - return ret0 -} - -// GetTd indicates an expected call of GetTd. -func (mr *MockBackendMockRecorder) GetTd(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTd", reflect.TypeOf((*MockBackend)(nil).GetTd), arg0) -} - // GetTxAndLookupInfo mocks base method. func (m *MockBackend) GetTxAndLookupInfo(arg0 common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { m.ctrl.T.Helper() @@ -411,6 +397,20 @@ func (mr *MockBackendMockRecorder) HeaderByNumberOrHash(arg0, arg1 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeaderByNumberOrHash", reflect.TypeOf((*MockBackend)(nil).HeaderByNumberOrHash), arg0, arg1) } +// IsConsoleLogEnabled mocks base method. +func (m *MockBackend) IsConsoleLogEnabled() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsConsoleLogEnabled") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsConsoleLogEnabled indicates an expected call of IsConsoleLogEnabled. +func (mr *MockBackendMockRecorder) IsConsoleLogEnabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsConsoleLogEnabled", reflect.TypeOf((*MockBackend)(nil).IsConsoleLogEnabled)) +} + // IsParallelDBWrite mocks base method. func (m *MockBackend) IsParallelDBWrite() bool { m.ctrl.T.Helper() diff --git a/api/tx_args.go b/api/tx_args.go index 915b23590..0af5dafbe 100644 --- a/api/tx_args.go +++ b/api/tx_args.go @@ -729,7 +729,7 @@ func (args *EthTransactionArgs) setDefaults(ctx context.Context, b Backend) erro } // ToMessage change EthTransactionArgs to types.Transaction in Kaia. -func (args *EthTransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, intrinsicGas uint64) (*types.Transaction, error) { +func (args *EthTransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, intrinsicGas uint64, dataTokens uint64) (*types.Transaction, error) { // Reject invalid combinations of pre- and post-1559 fee styles if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") @@ -780,7 +780,7 @@ func (args *EthTransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int, if args.AccessList != nil { accessList = *args.AccessList } - return types.NewMessage(addr, args.To, 0, value, gas, gasPrice, nil, nil, data, false, intrinsicGas, accessList, nil), nil + return types.NewMessage(addr, args.To, 0, value, gas, gasPrice, nil, nil, data, false, intrinsicGas, dataTokens, accessList, nil), nil } // toTransaction converts the arguments to a transaction. diff --git a/blockchain/bench_test.go b/blockchain/bench_test.go index a61b76f85..e26ed0bae 100644 --- a/blockchain/bench_test.go +++ b/blockchain/bench_test.go @@ -111,7 +111,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := types.IntrinsicGas(data, nil, nil, false, params.TestChainConfig.Rules(big.NewInt(0))) + gas, _, _ := types.IntrinsicGas(data, nil, nil, false, params.TestChainConfig.Rules(big.NewInt(0))) signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID) tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), signer, benchRootKey) gen.AddTx(tx) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 8975a8dbb..6b48f10d7 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -2150,18 +2150,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty return i, events, coalescedLogs, err } } - - // update governance CurrentSet if it is at an epoch block - if bc.engine.CreateSnapshot(bc, block.NumberU64(), block.Hash(), nil) != nil { - return i, events, coalescedLogs, err - } - - // update governance parameters - if istanbul, ok := bc.engine.(consensus.Istanbul); ok { - if err = istanbul.UpdateParam(block.NumberU64()); err != nil { - return i, events, coalescedLogs, err - } - } } // Append a single chain head event if we've progressed the chain if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() { diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go index 0d73bef51..1cd0f4064 100644 --- a/blockchain/blockchain_test.go +++ b/blockchain/blockchain_test.go @@ -1461,8 +1461,8 @@ func TestAccessListTx(t *testing.T) { b.SetRewardbase(common.Address{1}) // One transaction to 0xAAAA - intrinsicGas, _ := types.IntrinsicGas([]byte{}, list, nil, false, gspec.Config.Rules(block.Number())) - tx, _ := types.SignTx(types.NewMessage(senderAddr, &contractAddr, senderNonce, big.NewInt(0), 30000, big.NewInt(1), nil, nil, []byte{}, false, intrinsicGas, list, nil), signer, senderKey) + intrinsicGas, dataTokens, _ := types.IntrinsicGas([]byte{}, list, nil, false, gspec.Config.Rules(block.Number())) + tx, _ := types.SignTx(types.NewMessage(senderAddr, &contractAddr, senderNonce, big.NewInt(0), 30000, big.NewInt(1), nil, nil, []byte{}, false, intrinsicGas, dataTokens, list, nil), signer, senderKey) b.AddTx(tx) }) if n, err := chain.InsertChain(blocks); err != nil { @@ -2345,13 +2345,13 @@ func TestEIP7702(t *testing.T) { b.SetRewardbase(common.Address{1}) authorizationList := []types.Authorization{*auth1, *auth2} - intrinsicGas, err := types.IntrinsicGas(nil, nil, authorizationList, false, params.TestRules) + intrinsicGas, dataTokens, err := types.IntrinsicGas(nil, nil, authorizationList, false, params.TestRules) if err != nil { t.Fatalf("failed to run intrinsic gas: %v", err) } tx, err := types.SignTx(types.NewMessage(addr1, &addr1, uint64(0), nil, 500000, nil, newGkei(50), - big.NewInt(20), nil, false, intrinsicGas, nil, authorizationList), signer, key1) + big.NewInt(20), nil, false, intrinsicGas, dataTokens, nil, authorizationList), signer, key1) if err != nil { t.Fatalf("failed to sign tx: %v", err) } @@ -2426,13 +2426,13 @@ func TestEIP7702(t *testing.T) { }, key1) authorizationList := []types.Authorization{*authForEmpty} - intrinsicGas, err := types.IntrinsicGas(nil, nil, authorizationList, false, params.TestRules) + intrinsicGas, dataTokens, err := types.IntrinsicGas(nil, nil, authorizationList, false, params.TestRules) if err != nil { t.Fatalf("failed to run intrinsic gas: %v", err) } tx, err := types.SignTx(types.NewMessage(addr1, &addr1, state.GetNonce(addr1), nil, 500000, nil, newGkei(50), - big.NewInt(20), nil, false, intrinsicGas, nil, authorizationList), signer, key1) + big.NewInt(20), nil, false, intrinsicGas, dataTokens, nil, authorizationList), signer, key1) if err != nil { t.Fatalf("failed to sign tx: %v", err) } diff --git a/blockchain/error.go b/blockchain/error.go index 3a9dc2288..75a1a2d94 100644 --- a/blockchain/error.go +++ b/blockchain/error.go @@ -86,6 +86,10 @@ var ( // than required to start the invocation. ErrIntrinsicGas = errors.New("intrinsic gas too low") + // ErrDataFloorGas is returned if the transaction is specified to use less gas + // than required for the data floor cost. + ErrDataFloorGas = errors.New("insufficient gas for data floor cost") + // ErrGasLimit is returned if a transaction's requested gas limit exceeds the // maximum allowance of the current block. ErrGasLimit = errors.New("exceeds block gas limit") diff --git a/blockchain/state_processor.go b/blockchain/state_processor.go index f9ad52581..5ed7259d9 100644 --- a/blockchain/state_processor.go +++ b/blockchain/state_processor.go @@ -112,7 +112,7 @@ func ProcessParentBlockHash(header *types.Header, vmenv *vm.EVM, statedb vm.Stat gasLimit = uint64(30_000_000) ) - intrinsicGas, err := types.IntrinsicGas(data, nil, nil, false, rules) + intrinsicGas, dataTokens, err := types.IntrinsicGas(data, nil, nil, false, rules) if err != nil { return err } @@ -129,6 +129,7 @@ func ProcessParentBlockHash(header *types.Header, vmenv *vm.EVM, statedb vm.Stat data, false, intrinsicGas, + dataTokens, nil, nil, ) diff --git a/blockchain/state_transition.go b/blockchain/state_transition.go index b3eb82e38..286db0105 100644 --- a/blockchain/state_transition.go +++ b/blockchain/state_transition.go @@ -25,6 +25,7 @@ package blockchain import ( "errors" "fmt" + "math" "math/big" "github.com/kaiachain/kaia/blockchain/types" @@ -83,7 +84,7 @@ type Message interface { // ValidatedIntrinsicGas returns the intrinsic gas of the transaction. // The returned intrinsic gas should be derived by calling AsMessageAccountKeyPicker(). - ValidatedIntrinsicGas() uint64 + ValidatedIntrinsicGas() *types.ValidatedIntrinsicGas // FeeRatio returns a ratio of tx fee paid by the fee payer in percentage. // For example, if it is 30, 30% of tx fee will be paid by the fee payer. @@ -111,7 +112,7 @@ type Message interface { // IntrinsicGas returns `intrinsic gas` based on the tx type. // This value is used to differentiate tx fee based on the tx type. - IntrinsicGas(currentBlockNumber uint64) (uint64, error) + IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) // Type returns the transaction type of the message. Type() types.TxType @@ -345,19 +346,27 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } // Check clauses 4-5, subtract intrinsic gas if everything is correct - amount := msg.ValidatedIntrinsicGas() - if st.gas < amount { + validatedGas := msg.ValidatedIntrinsicGas() + if st.gas < validatedGas.Gas { return nil, ErrIntrinsicGas } - st.gas -= amount + rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber) + if rules.IsPrague { + floorGas, err := FloorDataGas(validatedGas.Tokens) + if err != nil { + return nil, err + } + if st.gas < floorGas { + return nil, fmt.Errorf("%w: have %d, want %d", ErrDataFloorGas, st.gas, floorGas) + } + } + st.gas -= validatedGas.Gas // Check clause 6 if msg.Value().Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.ValidatedSender(), msg.Value()) { return nil, vm.ErrInsufficientBalance } - rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber) - // Verify authorization list is not empty. if msg.AuthorizationList() != nil && len(msg.AuthorizationList()) == 0 { return nil, fmt.Errorf("%w: address %v", ErrEmptyAuthList, msg.ValidatedSender()) @@ -408,6 +417,15 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, vm.ErrTotalTimeLimitReached } + if rules.IsPrague { + // After EIP-7623: Data-heavy transactions pay the floor gas. + // Overflow error has already been checked and can be ignored here. + floorGas, _ := FloorDataGas(validatedGas.Tokens) + if st.gasUsed() < floorGas { + st.gas = st.initialGas - floorGas + } + } + if rules.IsKore { // After EIP-3529: refunds are capped to gasUsed / 5 st.refundGas(params.RefundQuotientEIP3529) @@ -582,3 +600,13 @@ func (st *StateTransition) processAuthorizationList(authList types.Authorization } } } + +// FloorDataGas calculates the minimum gas required for a transaction +// based on its data tokens (EIP-7623). +func FloorDataGas(tokens uint64) (uint64, error) { + // Check for overflow + if (math.MaxUint64-params.TxGas)/params.CostFloorPerToken7623 < tokens { + return 0, types.ErrGasUintOverflow + } + return params.TxGas + tokens*params.CostFloorPerToken7623, nil +} diff --git a/blockchain/system/multicall.go b/blockchain/system/multicall.go index 05c4a3ed2..adb61ccbc 100644 --- a/blockchain/system/multicall.go +++ b/blockchain/system/multicall.go @@ -49,6 +49,7 @@ func (caller *ContractCallerForMultiCall) CallContract(ctx context.Context, call gasPrice := big.NewInt(0) // execute call regardless of the balance of the sender gasLimit := uint64(1e8) // enough gas limit to execute multicall contract functions intrinsicGas := uint64(0) // read operation doesn't require intrinsicGas + dataTokens := uint64(0) // read operation doesn't require intrinsicGas // call.From: zero address will be assigned if nothing is specified // call.To: the target contract address will be assigned by `BoundContract` @@ -63,7 +64,7 @@ func (caller *ContractCallerForMultiCall) CallContract(ctx context.Context, call } msg := types.NewMessage(call.From, call.To, caller.state.GetNonce(call.From), - call.Value, gasLimit, gasPrice, nil, nil, call.Data, false, intrinsicGas, nil, nil) + call.Value, gasLimit, gasPrice, nil, nil, call.Data, false, intrinsicGas, dataTokens, nil, nil) blockContext := blockchain.NewEVMBlockContext(caller.header, caller.chain, nil) txContext := blockchain.NewEVMTxContext(msg, caller.header, caller.chain.Config()) diff --git a/blockchain/system/rebalance.go b/blockchain/system/rebalance.go index 0800bec07..abe43291b 100644 --- a/blockchain/system/rebalance.go +++ b/blockchain/system/rebalance.go @@ -108,6 +108,7 @@ func (caller *Kip103ContractCaller) CallContract(ctx context.Context, call kaia. gasPrice := big.NewInt(0) // execute call regardless of the balance of the sender gasLimit := uint64(1e8) // enough gas limit to execute kip103 contract functions intrinsicGas := uint64(0) // read operation doesn't require intrinsicGas + dataTokens := uint64(0) // read operation doesn't require intrinsicGas // call.From: zero address will be assigned if nothing is specified // call.To: the target contract address will be assigned by `BoundContract` @@ -122,7 +123,7 @@ func (caller *Kip103ContractCaller) CallContract(ctx context.Context, call kaia. // return nil, err //} msg := types.NewMessage(call.From, call.To, caller.state.GetNonce(call.From), - call.Value, gasLimit, gasPrice, nil, nil, call.Data, false, intrinsicGas, nil, nil) + call.Value, gasLimit, gasPrice, nil, nil, call.Data, false, intrinsicGas, dataTokens, nil, nil) blockContext := blockchain.NewEVMBlockContext(caller.header, caller.chain, nil) txContext := blockchain.NewEVMTxContext(msg, caller.header, caller.chain.Config()) diff --git a/blockchain/tx_pool.go b/blockchain/tx_pool.go index 2778d66ff..7c3d748fc 100644 --- a/blockchain/tx_pool.go +++ b/blockchain/tx_pool.go @@ -836,7 +836,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { } } - intrGas, err := tx.IntrinsicGas(pool.currentBlockNumber) + intrGas, dataTokens, err := tx.IntrinsicGas(pool.currentBlockNumber) intrGas += gasFrom + gasFeePayer if err != nil { return err @@ -844,6 +844,16 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { if tx.Gas() < intrGas { return ErrIntrinsicGas } + // Ensure the transaction can cover floor data gas. + if pool.rules.IsPrague { + floorGas, err := FloorDataGas(dataTokens) + if err != nil { + return err + } + if tx.Gas() < floorGas { + return fmt.Errorf("%w: gas %v, minimum needed %v", ErrDataFloorGas, tx.Gas(), floorGas) + } + } // "tx.Validate()" conducts additional validation for each new txType. // Validate humanReadable address when this tx has "true" in the humanReadable field. diff --git a/blockchain/types/transaction.go b/blockchain/types/transaction.go index c00e7b30e..9eb1b4bdd 100644 --- a/blockchain/types/transaction.go +++ b/blockchain/types/transaction.go @@ -72,6 +72,11 @@ func ErrFeePayer(err error) error { return fmt.Errorf("invalid fee payer: %s", err) } +type ValidatedIntrinsicGas struct { + Gas uint64 + Tokens uint64 +} + type Transaction struct { data TxInternalData time time.Time @@ -90,7 +95,7 @@ type Transaction struct { validatedFeePayer common.Address // validatedIntrinsicGas represents intrinsic gas of the transaction to be used for ApplyTransaction(). // This value is set in AsMessageWithAccountKeyPicker(). - validatedIntrinsicGas uint64 + validatedIntrinsicGas *ValidatedIntrinsicGas // The account's nonce is checked only if `checkNonce` is true. checkNonce bool // This value is set when the tx is invalidated in block tx validation, and is used to remove pending tx in txPool. @@ -385,7 +390,7 @@ func (tx *Transaction) ValidatedFeePayer() common.Address { return tx.validatedFeePayer } -func (tx *Transaction) ValidatedIntrinsicGas() uint64 { +func (tx *Transaction) ValidatedIntrinsicGas() *ValidatedIntrinsicGas { tx.mu.RLock() defer tx.mu.RUnlock() return tx.validatedIntrinsicGas @@ -393,7 +398,7 @@ func (tx *Transaction) ValidatedIntrinsicGas() uint64 { func (tx *Transaction) MakeRPCOutput() map[string]interface{} { return tx.data.MakeRPCOutput() } func (tx *Transaction) GetTxInternalData() TxInternalData { return tx.data } -func (tx *Transaction) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (tx *Transaction) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { return tx.data.IntrinsicGas(currentBlockNumber) } @@ -584,7 +589,7 @@ func (tx *Transaction) Execute(vm VM, stateDB StateDB, currentBlockNumber uint64 // XXX Rename message to something less arbitrary? // TODO-Kaia: Message is removed and this function will return *Transaction. func (tx *Transaction) AsMessageWithAccountKeyPicker(s Signer, picker AccountKeyPicker, currentBlockNumber uint64) (*Transaction, error) { - intrinsicGas, err := tx.IntrinsicGas(currentBlockNumber) + intrinsicGas, dataTokens, err := tx.IntrinsicGas(currentBlockNumber) if err != nil { return nil, err } @@ -607,7 +612,7 @@ func (tx *Transaction) AsMessageWithAccountKeyPicker(s Signer, picker AccountKey } tx.mu.Lock() - tx.validatedIntrinsicGas = intrinsicGas + gasFrom + gasFeePayer + tx.validatedIntrinsicGas = &ValidatedIntrinsicGas{Gas: intrinsicGas + gasFrom + gasFeePayer, Tokens: dataTokens} tx.mu.Unlock() return tx, err @@ -1079,9 +1084,9 @@ func (t *TransactionsByPriceAndNonce) Clear() { } // NewMessage returns a `*Transaction` object with the given arguments. -func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, checkNonce bool, intrinsicGas uint64, list AccessList, auth AuthorizationList) *Transaction { +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, checkNonce bool, intrinsicGas uint64, dataTokens uint64, list AccessList, auth AuthorizationList) *Transaction { transaction := &Transaction{ - validatedIntrinsicGas: intrinsicGas, + validatedIntrinsicGas: &ValidatedIntrinsicGas{Gas: intrinsicGas, Tokens: dataTokens}, validatedFeePayer: from, validatedSender: from, checkNonce: checkNonce, diff --git a/blockchain/types/transaction_test.go b/blockchain/types/transaction_test.go index b2403f6e3..fcb350c9b 100644 --- a/blockchain/types/transaction_test.go +++ b/blockchain/types/transaction_test.go @@ -627,27 +627,28 @@ func TestIntrinsicGas(t *testing.T) { data, err = hex.DecodeString(tc.inputString) // decode input string to hex data assert.Equal(t, nil, err) - gas, err = IntrinsicGas(data, nil, nil, false, params.Rules{IsIstanbul: false}) + // TODO-Kaia: Add test for EIP-7623 + gas, _, err = IntrinsicGas(data, nil, nil, false, params.Rules{IsIstanbul: false}) assert.Equal(t, tc.expectGas1, gas) assert.Equal(t, nil, err) - gas, err = IntrinsicGas(data, nil, nil, false, params.Rules{IsIstanbul: true}) + gas, _, err = IntrinsicGas(data, nil, nil, false, params.Rules{IsIstanbul: true}) assert.Equal(t, tc.expectGas2, gas) assert.Equal(t, nil, err) - gas, err = IntrinsicGas(data, nil, nil, false, params.Rules{IsIstanbul: true, IsShanghai: true, IsPrague: true}) + gas, _, err = IntrinsicGas(data, nil, nil, false, params.Rules{IsIstanbul: true, IsShanghai: true, IsPrague: true}) assert.Equal(t, tc.expectGas3, gas) assert.Equal(t, nil, err) - gas, err = IntrinsicGas(data, nil, nil, true, params.Rules{IsIstanbul: false}) + gas, _, err = IntrinsicGas(data, nil, nil, true, params.Rules{IsIstanbul: false}) assert.Equal(t, tc.expectGas4, gas) assert.Equal(t, nil, err) - gas, err = IntrinsicGas(data, nil, nil, true, params.Rules{IsIstanbul: true}) + gas, _, err = IntrinsicGas(data, nil, nil, true, params.Rules{IsIstanbul: true}) assert.Equal(t, tc.expectGas5, gas) assert.Equal(t, nil, err) - gas, err = IntrinsicGas(data, nil, nil, true, params.Rules{IsIstanbul: true, IsShanghai: true, IsPrague: true}) + gas, _, err = IntrinsicGas(data, nil, nil, true, params.Rules{IsIstanbul: true, IsShanghai: true, IsPrague: true}) assert.Equal(t, tc.expectGas6, gas) assert.Equal(t, nil, err) } diff --git a/blockchain/types/tx_internal_data.go b/blockchain/types/tx_internal_data.go index d81b5d5da..ff4963f8b 100644 --- a/blockchain/types/tx_internal_data.go +++ b/blockchain/types/tx_internal_data.go @@ -326,7 +326,7 @@ type TxInternalData interface { Equal(t TxInternalData) bool // IntrinsicGas computes additional 'intrinsic gas' based on tx types. - IntrinsicGas(currentBlockNumber uint64) (uint64, error) + IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) // SerializeForSign returns a slice containing attributes to make its tx signature. SerializeForSign() []interface{} @@ -572,7 +572,8 @@ func toWordSize(size uint64) uint64 { } // Klaytn-TxTypes since genesis, and EthTxTypes since istanbul use this. -func IntrinsicGasPayload(gas uint64, data []byte, isContractCreation bool, rules params.Rules) (uint64, error) { +func IntrinsicGasPayload(gas uint64, data []byte, isContractCreation bool, rules params.Rules) (uint64, uint64, error) { + var tokens uint64 // Bump the required gas by the amount of transactional data length := uint64(len(data)) if length > 0 { @@ -583,6 +584,10 @@ func IntrinsicGasPayload(gas uint64, data []byte, isContractCreation bool, rules nz++ } } + + z := length - nz + tokens = nz*params.TokenPerNonZeroByte7623 + z + // Since the genesis block, a flat 100 gas is paid // regardless of whether the value is zero or non-zero. nonZeroGas, zeroGas := params.TxDataGas, params.TxDataGas @@ -592,13 +597,12 @@ func IntrinsicGasPayload(gas uint64, data []byte, isContractCreation bool, rules } // Make sure we don't exceed uint64 for all data combinations if (math.MaxUint64-gas)/nonZeroGas < nz { - return 0, ErrGasUintOverflow + return 0, tokens, ErrGasUintOverflow } gas += nz * nonZeroGas - z := uint64(len(data)) - nz if (math.MaxUint64-gas)/zeroGas < z { - return 0, ErrGasUintOverflow + return 0, tokens, ErrGasUintOverflow } gas += z * zeroGas } @@ -606,17 +610,18 @@ func IntrinsicGasPayload(gas uint64, data []byte, isContractCreation bool, rules if isContractCreation && rules.IsShanghai { lenWords := toWordSize(length) if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { - return 0, ErrGasUintOverflow + return 0, tokens, ErrGasUintOverflow } gas += lenWords * params.InitCodeWordGas } - return gas, nil + return gas, tokens, nil } // Eth-TxTypes before istanbul use this. Only 0 tx type exists before Istanbul (No dynamic and access list types correspond to) // Calculate gas cost for type 0 transactions: // 68 gas for each non-zero byte and 16 gas for each zero byte in the data field. -func IntrinsicGasPayloadLegacy(gas uint64, data []byte) (uint64, error) { +func IntrinsicGasPayloadLegacy(gas uint64, data []byte) (uint64, uint64, error) { + var tokens uint64 length := uint64(len(data)) if length > 0 { // Zero and non-zero bytes are priced differently @@ -626,26 +631,33 @@ func IntrinsicGasPayloadLegacy(gas uint64, data []byte) (uint64, error) { nz++ } } + z := length - nz + tokens = nz*params.TokenPerNonZeroByte7623 + z + // Make sure we don't exceed uint64 for all data combinations if (math.MaxUint64-gas)/params.TxDataNonZeroGasFrontier < nz { - return 0, ErrGasUintOverflow + return 0, tokens, ErrGasUintOverflow } gas += nz * params.TxDataNonZeroGasFrontier - z := uint64(len(data)) - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { - return 0, ErrGasUintOverflow + return 0, tokens, ErrGasUintOverflow } gas += z * params.TxDataZeroGas } - return gas, nil + return gas, tokens, nil } -// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList AccessList, authorizationList AuthorizationList, contractCreation bool, r params.Rules) (uint64, error) { +// IntrinsicGas computes the 'intrinsic gas' and the number of tokens for EIP-7623 +// for a message with the given data. +func IntrinsicGas(data []byte, accessList AccessList, authorizationList AuthorizationList, contractCreation bool, r params.Rules) (uint64, uint64, error) { // Set the starting gas for the raw transaction - var gas uint64 + var ( + gas uint64 + tokens uint64 + ) + if contractCreation { gas = params.TxGasContractCreation } else { @@ -657,14 +669,14 @@ func IntrinsicGas(data []byte, accessList AccessList, authorizationList Authoriz if r.IsIstanbul { // tx types 1,2 only exist after istanbul; so they take this path. // tx types 8+ take this path as well. - gasPayloadWithGas, err = IntrinsicGasPayload(gas, data, contractCreation, r) + gasPayloadWithGas, tokens, err = IntrinsicGasPayload(gas, data, contractCreation, r) } else { // only for tx type 0 before istanbul. - gasPayloadWithGas, err = IntrinsicGasPayloadLegacy(gas, data) + gasPayloadWithGas, tokens, err = IntrinsicGasPayloadLegacy(gas, data) } if err != nil { - return 0, err + return 0, tokens, err } // We charge additional gas for the accessList: @@ -682,7 +694,7 @@ func IntrinsicGas(data []byte, accessList AccessList, authorizationList Authoriz gasPayloadWithGas += uint64(len(authorizationList)) * params.CallNewAccountGas } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } // CalcFeeWithRatio returns feePayer's fee and sender's fee based on feeRatio. diff --git a/blockchain/types/tx_internal_data_account_creation.go b/blockchain/types/tx_internal_data_account_creation.go index 919bbd56a..c7ca9efb0 100644 --- a/blockchain/types/tx_internal_data_account_creation.go +++ b/blockchain/types/tx_internal_data_account_creation.go @@ -364,10 +364,10 @@ func (t *TxInternalDataAccountCreation) SetSignature(s TxSignatures) { t.TxSignatures = s } -func (t *TxInternalDataAccountCreation) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataAccountCreation) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gasKey, err := t.Key.AccountCreationGas(currentBlockNumber) if err != nil { - return 0, err + return 0, 0, err } gas := params.TxGasAccountCreation + gasKey @@ -375,7 +375,7 @@ func (t *TxInternalDataAccountCreation) IntrinsicGas(currentBlockNumber uint64) gas += params.TxGasHumanReadable } - return gas, nil + return gas, 0, nil } func (t *TxInternalDataAccountCreation) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_account_update.go b/blockchain/types/tx_internal_data_account_update.go index 86821828a..f1ab0be7e 100644 --- a/blockchain/types/tx_internal_data_account_update.go +++ b/blockchain/types/tx_internal_data_account_update.go @@ -305,13 +305,13 @@ func (t *TxInternalDataAccountUpdate) SetSignature(s TxSignatures) { t.TxSignatures = s } -func (t *TxInternalDataAccountUpdate) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataAccountUpdate) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gasKey, err := t.Key.AccountCreationGas(currentBlockNumber) if err != nil { - return 0, err + return 0, 0, err } - return params.TxGasAccountUpdate + gasKey, nil + return params.TxGasAccountUpdate + gasKey, 0, nil } func (t *TxInternalDataAccountUpdate) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_cancel.go b/blockchain/types/tx_internal_data_cancel.go index 9cec98d90..deaed77c5 100644 --- a/blockchain/types/tx_internal_data_cancel.go +++ b/blockchain/types/tx_internal_data_cancel.go @@ -191,8 +191,8 @@ func (t *TxInternalDataCancel) SetSignature(s TxSignatures) { t.TxSignatures = s } -func (t *TxInternalDataCancel) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { - return params.TxGasCancel, nil +func (t *TxInternalDataCancel) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { + return params.TxGasCancel, 0, nil } func (t *TxInternalDataCancel) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_chain_data_anchoring.go b/blockchain/types/tx_internal_data_chain_data_anchoring.go index 9aafd1dea..dd2cd9407 100644 --- a/blockchain/types/tx_internal_data_chain_data_anchoring.go +++ b/blockchain/types/tx_internal_data_chain_data_anchoring.go @@ -260,17 +260,17 @@ func (t *TxInternalDataChainDataAnchoring) SetSignature(s TxSignatures) { t.TxSignatures = s } -func (t *TxInternalDataChainDataAnchoring) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataChainDataAnchoring) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxChainDataAnchoringGas // ChainDataAnchoring does not have Recipient, but it is not contract deployment transaction type. // So, isContractCreation is explicitly set as false. - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataChainDataAnchoring) Validate(stateDB StateDB, currentBlockNumber uint64) error { diff --git a/blockchain/types/tx_internal_data_ethereum_access_list.go b/blockchain/types/tx_internal_data_ethereum_access_list.go index 9539922d8..bdaaa4a58 100644 --- a/blockchain/types/tx_internal_data_ethereum_access_list.go +++ b/blockchain/types/tx_internal_data_ethereum_access_list.go @@ -293,7 +293,7 @@ func (t *TxInternalDataEthereumAccessList) RecoverPubkey(txhash common.Hash, hom return []*ecdsa.PublicKey{pk}, nil } -func (t *TxInternalDataEthereumAccessList) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataEthereumAccessList) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { return IntrinsicGas(t.Payload, t.AccessList, nil, t.Recipient == nil, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) } diff --git a/blockchain/types/tx_internal_data_ethereum_dynamic_fee.go b/blockchain/types/tx_internal_data_ethereum_dynamic_fee.go index 111234ed8..64230e2b4 100644 --- a/blockchain/types/tx_internal_data_ethereum_dynamic_fee.go +++ b/blockchain/types/tx_internal_data_ethereum_dynamic_fee.go @@ -288,7 +288,7 @@ func (t *TxInternalDataEthereumDynamicFee) RecoverPubkey(txhash common.Hash, hom return []*ecdsa.PublicKey{pk}, nil } -func (t *TxInternalDataEthereumDynamicFee) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataEthereumDynamicFee) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { return IntrinsicGas(t.Payload, t.AccessList, nil, t.Recipient == nil, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) } diff --git a/blockchain/types/tx_internal_data_ethereum_set_code.go b/blockchain/types/tx_internal_data_ethereum_set_code.go index 118716c58..2e5b9922f 100644 --- a/blockchain/types/tx_internal_data_ethereum_set_code.go +++ b/blockchain/types/tx_internal_data_ethereum_set_code.go @@ -347,7 +347,7 @@ func (t *TxInternalDataEthereumSetCode) Equal(a TxInternalData) bool { t.S.Cmp(ta.S) == 0 } -func (t *TxInternalDataEthereumSetCode) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataEthereumSetCode) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { return IntrinsicGas(t.Payload, t.AccessList, t.AuthorizationList, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) } diff --git a/blockchain/types/tx_internal_data_fee_delegated_account_update.go b/blockchain/types/tx_internal_data_fee_delegated_account_update.go index 29fe97a30..955cd24f0 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_account_update.go +++ b/blockchain/types/tx_internal_data_fee_delegated_account_update.go @@ -351,13 +351,13 @@ func (t *TxInternalDataFeeDelegatedAccountUpdate) RecoverFeePayerPubkey(txhash c return t.FeePayerSignatures.RecoverPubkey(txhash, homestead, vfunc) } -func (t *TxInternalDataFeeDelegatedAccountUpdate) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedAccountUpdate) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gasKey, err := t.Key.AccountCreationGas(currentBlockNumber) if err != nil { - return 0, err + return 0, 0, err } - return params.TxGasAccountUpdate + gasKey + params.TxGasFeeDelegated, nil + return params.TxGasAccountUpdate + gasKey + params.TxGasFeeDelegated, 0, nil } func (t *TxInternalDataFeeDelegatedAccountUpdate) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_account_update_with_ratio.go b/blockchain/types/tx_internal_data_fee_delegated_account_update_with_ratio.go index ef905a6b8..a3d64a78a 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_account_update_with_ratio.go +++ b/blockchain/types/tx_internal_data_fee_delegated_account_update_with_ratio.go @@ -376,13 +376,13 @@ func (t *TxInternalDataFeeDelegatedAccountUpdateWithRatio) RecoverFeePayerPubkey return t.FeePayerSignatures.RecoverPubkey(txhash, homestead, vfunc) } -func (t *TxInternalDataFeeDelegatedAccountUpdateWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedAccountUpdateWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gasKey, err := t.Key.AccountCreationGas(currentBlockNumber) if err != nil { - return 0, err + return 0, 0, err } - return params.TxGasAccountUpdate + gasKey + params.TxGasFeeDelegatedWithRatio, nil + return params.TxGasAccountUpdate + gasKey + params.TxGasFeeDelegatedWithRatio, 0, nil } func (t *TxInternalDataFeeDelegatedAccountUpdateWithRatio) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_cancel.go b/blockchain/types/tx_internal_data_fee_delegated_cancel.go index 9fd717f54..ada9770b9 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_cancel.go +++ b/blockchain/types/tx_internal_data_fee_delegated_cancel.go @@ -226,8 +226,8 @@ func (t *TxInternalDataFeeDelegatedCancel) SetSignature(s TxSignatures) { t.TxSignatures = s } -func (t *TxInternalDataFeeDelegatedCancel) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { - return params.TxGasCancel + params.TxGasFeeDelegated, nil +func (t *TxInternalDataFeeDelegatedCancel) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { + return params.TxGasCancel + params.TxGasFeeDelegated, 0, nil } func (t *TxInternalDataFeeDelegatedCancel) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_cancel_with_ratio.go b/blockchain/types/tx_internal_data_fee_delegated_cancel_with_ratio.go index 629998401..d22dbc5e8 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_cancel_with_ratio.go +++ b/blockchain/types/tx_internal_data_fee_delegated_cancel_with_ratio.go @@ -246,8 +246,8 @@ func (t *TxInternalDataFeeDelegatedCancelWithRatio) SetSignature(s TxSignatures) t.TxSignatures = s } -func (t *TxInternalDataFeeDelegatedCancelWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { - return params.TxGasCancel + params.TxGasFeeDelegatedWithRatio, nil +func (t *TxInternalDataFeeDelegatedCancelWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { + return params.TxGasCancel + params.TxGasFeeDelegatedWithRatio, 0, nil } func (t *TxInternalDataFeeDelegatedCancelWithRatio) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring.go b/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring.go index ba168b1d6..caa437c9a 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring.go +++ b/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring.go @@ -295,17 +295,17 @@ func (t *TxInternalDataFeeDelegatedChainDataAnchoring) RecoverFeePayerPubkey(txh return t.FeePayerSignatures.RecoverPubkey(txhash, homestead, vfunc) } -func (t *TxInternalDataFeeDelegatedChainDataAnchoring) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedChainDataAnchoring) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxChainDataAnchoringGas + params.TxGasFeeDelegated // ChainDataAnchoring does not have Recipient, but it is not contract deployment transaction type. // So, isContractCreation is explicitly set as false. - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedChainDataAnchoring) Validate(stateDB StateDB, currentBlockNumber uint64) error { diff --git a/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring_with_ratio.go b/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring_with_ratio.go index befc615d9..12d65c4fa 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring_with_ratio.go +++ b/blockchain/types/tx_internal_data_fee_delegated_chain_data_anchoring_with_ratio.go @@ -319,17 +319,17 @@ func (t *TxInternalDataFeeDelegatedChainDataAnchoringWithRatio) RecoverFeePayerP return t.FeePayerSignatures.RecoverPubkey(txhash, homestead, vfunc) } -func (t *TxInternalDataFeeDelegatedChainDataAnchoringWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedChainDataAnchoringWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxChainDataAnchoringGas + params.TxGasFeeDelegatedWithRatio // ChainDataAnchoring does not have Recipient, but it is not contract deployment transaction type. // So, isContractCreation is explicitly set as false. - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedChainDataAnchoringWithRatio) Validate(stateDB StateDB, currentBlockNumber uint64) error { diff --git a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy.go b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy.go index 0e1c8582e..3e16bbb0d 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy.go +++ b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy.go @@ -303,19 +303,19 @@ func (t *TxInternalDataFeeDelegatedSmartContractDeploy) String() string { enc) } -func (t *TxInternalDataFeeDelegatedSmartContractDeploy) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedSmartContractDeploy) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasContractCreation + params.TxGasFeeDelegated if t.HumanReadable { gas += params.TxGasHumanReadable } - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, true, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, true, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedSmartContractDeploy) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy_with_ratio.go b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy_with_ratio.go index f37090943..b5104fbe7 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy_with_ratio.go +++ b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_deploy_with_ratio.go @@ -323,19 +323,19 @@ func (t *TxInternalDataFeeDelegatedSmartContractDeployWithRatio) String() string enc) } -func (t *TxInternalDataFeeDelegatedSmartContractDeployWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedSmartContractDeployWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasContractCreation + params.TxGasFeeDelegatedWithRatio if t.HumanReadable { gas += params.TxGasHumanReadable } - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, true, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, true, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedSmartContractDeployWithRatio) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution.go b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution.go index d1b544271..d48eced42 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution.go +++ b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution.go @@ -273,15 +273,15 @@ func (t *TxInternalDataFeeDelegatedSmartContractExecution) String() string { enc) } -func (t *TxInternalDataFeeDelegatedSmartContractExecution) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedSmartContractExecution) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasContractExecution + params.TxGasFeeDelegated - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedSmartContractExecution) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution_with_ratio.go b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution_with_ratio.go index db4c4f468..49595d542 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution_with_ratio.go +++ b/blockchain/types/tx_internal_data_fee_delegated_smart_contract_execution_with_ratio.go @@ -293,15 +293,15 @@ func (t *TxInternalDataFeeDelegatedSmartContractExecutionWithRatio) String() str enc) } -func (t *TxInternalDataFeeDelegatedSmartContractExecutionWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedSmartContractExecutionWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasContractExecution + params.TxGasFeeDelegatedWithRatio - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedSmartContractExecutionWithRatio) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_value_transfer.go b/blockchain/types/tx_internal_data_fee_delegated_value_transfer.go index 4e24db28f..617e22437 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_value_transfer.go +++ b/blockchain/types/tx_internal_data_fee_delegated_value_transfer.go @@ -257,8 +257,8 @@ func (t *TxInternalDataFeeDelegatedValueTransfer) String() string { enc) } -func (t *TxInternalDataFeeDelegatedValueTransfer) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { - return params.TxGasValueTransfer + params.TxGasFeeDelegated, nil +func (t *TxInternalDataFeeDelegatedValueTransfer) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { + return params.TxGasValueTransfer + params.TxGasFeeDelegated, 0, nil } func (t *TxInternalDataFeeDelegatedValueTransfer) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo.go b/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo.go index 7f714cb60..0341c4696 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo.go +++ b/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo.go @@ -274,14 +274,14 @@ func (t *TxInternalDataFeeDelegatedValueTransferMemo) RecoverFeePayerPubkey(txha return t.FeePayerSignatures.RecoverPubkey(txhash, homestead, vfunc) } -func (t *TxInternalDataFeeDelegatedValueTransferMemo) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedValueTransferMemo) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasValueTransfer + params.TxGasFeeDelegated - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedValueTransferMemo) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo_with_ratio.go b/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo_with_ratio.go index 2e65e3517..98073dca7 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo_with_ratio.go +++ b/blockchain/types/tx_internal_data_fee_delegated_value_transfer_memo_with_ratio.go @@ -294,14 +294,14 @@ func (t *TxInternalDataFeeDelegatedValueTransferMemoWithRatio) RecoverFeePayerPu return t.FeePayerSignatures.RecoverPubkey(txhash, homestead, vfunc) } -func (t *TxInternalDataFeeDelegatedValueTransferMemoWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataFeeDelegatedValueTransferMemoWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasValueTransfer + params.TxGasFeeDelegatedWithRatio - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataFeeDelegatedValueTransferMemoWithRatio) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_fee_delegated_value_transfer_with_ratio.go b/blockchain/types/tx_internal_data_fee_delegated_value_transfer_with_ratio.go index 4b5f302b5..d5e342d95 100644 --- a/blockchain/types/tx_internal_data_fee_delegated_value_transfer_with_ratio.go +++ b/blockchain/types/tx_internal_data_fee_delegated_value_transfer_with_ratio.go @@ -275,8 +275,8 @@ func (t *TxInternalDataFeeDelegatedValueTransferWithRatio) String() string { enc) } -func (t *TxInternalDataFeeDelegatedValueTransferWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { - return params.TxGasValueTransfer + params.TxGasFeeDelegatedWithRatio, nil +func (t *TxInternalDataFeeDelegatedValueTransferWithRatio) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { + return params.TxGasValueTransfer + params.TxGasFeeDelegatedWithRatio, 0, nil } func (t *TxInternalDataFeeDelegatedValueTransferWithRatio) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_legacy.go b/blockchain/types/tx_internal_data_legacy.go index 84f0a4d00..0dd5d68a7 100644 --- a/blockchain/types/tx_internal_data_legacy.go +++ b/blockchain/types/tx_internal_data_legacy.go @@ -235,7 +235,7 @@ func (t *TxInternalDataLegacy) RecoverPubkey(txhash common.Hash, homestead bool, return []*ecdsa.PublicKey{pk}, nil } -func (t *TxInternalDataLegacy) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataLegacy) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { return IntrinsicGas(t.Payload, nil, nil, t.Recipient == nil, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) } diff --git a/blockchain/types/tx_internal_data_smart_contract_deploy.go b/blockchain/types/tx_internal_data_smart_contract_deploy.go index 73c7effde..ef8b79cd9 100644 --- a/blockchain/types/tx_internal_data_smart_contract_deploy.go +++ b/blockchain/types/tx_internal_data_smart_contract_deploy.go @@ -268,19 +268,19 @@ func (t *TxInternalDataSmartContractDeploy) String() string { enc) } -func (t *TxInternalDataSmartContractDeploy) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataSmartContractDeploy) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasContractCreation if t.HumanReadable { gas += params.TxGasHumanReadable } - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, true, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, true, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataSmartContractDeploy) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_smart_contract_execution.go b/blockchain/types/tx_internal_data_smart_contract_execution.go index 791ce9972..a5c0eaeb1 100644 --- a/blockchain/types/tx_internal_data_smart_contract_execution.go +++ b/blockchain/types/tx_internal_data_smart_contract_execution.go @@ -238,15 +238,15 @@ func (t *TxInternalDataSmartContractExecution) String() string { enc) } -func (t *TxInternalDataSmartContractExecution) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataSmartContractExecution) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasContractExecution - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataSmartContractExecution) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_value_transfer.go b/blockchain/types/tx_internal_data_value_transfer.go index 5b81efe59..60d44d514 100644 --- a/blockchain/types/tx_internal_data_value_transfer.go +++ b/blockchain/types/tx_internal_data_value_transfer.go @@ -222,11 +222,11 @@ func (t *TxInternalDataValueTransfer) SetSignature(s TxSignatures) { t.TxSignatures = s } -func (t *TxInternalDataValueTransfer) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataValueTransfer) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { // TxInternalDataValueTransfer does not have payload, and it // is not account creation. Hence, its intrinsic gas is determined by // params.TxGas. Refer to types.IntrinsicGas(). - return params.TxGasValueTransfer, nil + return params.TxGasValueTransfer, 0, nil } func (t *TxInternalDataValueTransfer) SerializeForSignToBytes() []byte { diff --git a/blockchain/types/tx_internal_data_value_transfer_memo.go b/blockchain/types/tx_internal_data_value_transfer_memo.go index 56193618c..71e6f3452 100644 --- a/blockchain/types/tx_internal_data_value_transfer_memo.go +++ b/blockchain/types/tx_internal_data_value_transfer_memo.go @@ -239,14 +239,14 @@ func (t *TxInternalDataValueTransferMemo) SetSignature(s TxSignatures) { t.TxSignatures = s } -func (t *TxInternalDataValueTransferMemo) IntrinsicGas(currentBlockNumber uint64) (uint64, error) { +func (t *TxInternalDataValueTransferMemo) IntrinsicGas(currentBlockNumber uint64) (uint64, uint64, error) { gas := params.TxGasValueTransfer - gasPayloadWithGas, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) + gasPayloadWithGas, tokens, err := IntrinsicGasPayload(gas, t.Payload, false, *fork.Rules(big.NewInt(int64(currentBlockNumber)))) if err != nil { - return 0, err + return 0, tokens, err } - return gasPayloadWithGas, nil + return gasPayloadWithGas, tokens, nil } func (t *TxInternalDataValueTransferMemo) SerializeForSignToBytes() []byte { diff --git a/blockchain/vm/contracts.go b/blockchain/vm/contracts.go index 1e19a3c9a..025f9c532 100644 --- a/blockchain/vm/contracts.go +++ b/blockchain/vm/contracts.go @@ -31,6 +31,7 @@ import ( "fmt" "math/big" "strconv" + "strings" "github.com/consensys/gnark-crypto/ecc" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" @@ -1382,3 +1383,110 @@ func (c *bls12381MapG2) Run(input []byte, contract *Contract, evm *EVM) ([]byte, // Encode the G2 point to 256 bytes return encodePointG2(&r), nil } + +// consoleLog implements solidity console.log for local networks. +type consoleLog struct{} + +func (c *consoleLog) GetRequiredGasAndComputationCost(input []byte) (uint64, uint64) { + return 0, 0 +} + +func (c *consoleLog) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { + decoded, _ := c.toLogString(input) + if decoded != "" { + logger.Debug(decoded) + } + return nil, nil +} + +// Hardhat console.log accepts format string, however we don't support it. +// Instead, we just join all the parameters with a space. +func (c *consoleLog) toLogString(input []byte) (string, error) { + if len(input) < 4 { + return "", errors.New("input too short") + } + selector := binary.BigEndian.Uint32(input[:4]) + params := input[4:] + + types, ok := common.ConsoleLogSignatures[selector] + if !ok { + return "", errors.New("unknown selector") + } + decoded, err := c.decode(params, types) + if err != nil { + return "", err + } + + return strings.Join(decoded, " "), nil +} + +var registerSize = 32 + +// decode decodes the input into a list of string according to the provided types +func (c *consoleLog) decode(params []byte, types []common.ConsoleLogType) ([]string, error) { + var res []string + for i, t := range types { + pos := i * registerSize + switch t { + case common.Uint256Ty: + if len(params) < pos+registerSize { + return nil, errors.New("input too short") + } + res = append(res, strconv.FormatUint(big.NewInt(0).SetBytes(params[pos:pos+registerSize]).Uint64(), 10)) + case common.Int256Ty: + if len(params) < pos+registerSize { + return nil, errors.New("input too short") + } + res = append(res, strconv.FormatInt(big.NewInt(0).SetBytes(params[pos:pos+registerSize]).Int64(), 10)) + case common.BoolTy: + if len(params) < pos+registerSize { + return nil, errors.New("input too short") + } + if params[pos+registerSize-1] != 0 { + res = append(res, "true") + } else { + res = append(res, "false") + } + case common.StringTy: + if len(params) < pos+registerSize { + return nil, errors.New("input too short") + } + start := int(big.NewInt(0).SetBytes(params[pos : pos+registerSize]).Int64()) + if len(params) < start+registerSize { + return nil, errors.New("input too short") + } + size := int(big.NewInt(0).SetBytes(params[start : start+registerSize]).Int64()) + if len(params) < start+size+registerSize { + return nil, errors.New("input too short") + } + res = append(res, string(params[start+registerSize:start+size+registerSize])) + case common.AddressTy: + if len(params) < pos+registerSize { + return nil, errors.New("input too short") + } + res = append(res, common.BytesToAddress(params[pos+12:pos+registerSize]).Hex()) + case common.BytesTy: + if len(params) < pos+registerSize { + return nil, errors.New("input too short") + } + start := int(big.NewInt(0).SetBytes(params[pos : pos+registerSize]).Int64()) + if len(params) < start+registerSize { + return nil, errors.New("input too short") + } + size := int(big.NewInt(0).SetBytes(params[start : start+registerSize]).Int64()) + if len(params) < start+size+registerSize { + return nil, errors.New("input too short") + } + res = append(res, common.Bytes2Hex(params[start+registerSize:start+size+registerSize])) + case common.Bytes1Ty, common.Bytes2Ty, common.Bytes3Ty, common.Bytes4Ty, common.Bytes5Ty, common.Bytes6Ty, common.Bytes7Ty, common.Bytes8Ty, common.Bytes9Ty, common.Bytes10Ty, common.Bytes11Ty, common.Bytes12Ty, common.Bytes13Ty, common.Bytes14Ty, common.Bytes15Ty, common.Bytes16Ty, common.Bytes17Ty, common.Bytes18Ty, common.Bytes19Ty, common.Bytes20Ty, common.Bytes21Ty, common.Bytes22Ty, common.Bytes23Ty, common.Bytes24Ty, common.Bytes25Ty, common.Bytes26Ty, common.Bytes27Ty, common.Bytes28Ty, common.Bytes29Ty, common.Bytes30Ty, common.Bytes31Ty, common.Bytes32Ty: + size, _ := strconv.Atoi(string(t[5:])) + if len(params) < pos+size { + return nil, errors.New("input too short") + } + res = append(res, common.Bytes2Hex(params[pos:pos+size])) + default: + return nil, errors.New("unknown type") + } + } + return res, nil +} diff --git a/blockchain/vm/contracts_test.go b/blockchain/vm/contracts_test.go index fb6eabc87..7879fdc35 100644 --- a/blockchain/vm/contracts_test.go +++ b/blockchain/vm/contracts_test.go @@ -24,18 +24,24 @@ package vm import ( "bytes" + "encoding/binary" "encoding/json" "fmt" "math/big" "os" + "reflect" + "strconv" + "strings" "testing" + "github.com/kaiachain/kaia/accounts/abi" "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/crypto" + "github.com/kaiachain/kaia/log" "github.com/kaiachain/kaia/params" "github.com/kaiachain/kaia/storage/database" "github.com/stretchr/testify/assert" @@ -468,3 +474,89 @@ func TestEVM_CVE_2021_39137(t *testing.T) { assert.Equal(t, tc.expectedResult, ret[12:32]) } } + +func TestConsoleLog(t *testing.T) { + // Test if the ConsoleLog.toLogString can convert the input correctly into log string + // Test all combinations of parameters from ConsoleLogSignatures + for selector, paramTypes := range common.ConsoleLogSignatures { + t.Run(fmt.Sprintf("selector_%x", selector), func(t *testing.T) { + // Generate test input values and expected output based on parameter types + var ( + inputs []interface{} + expectedStr []string + ) + + for _, paramType := range paramTypes { + switch paramType { + case common.Int256Ty: + inputs = append(inputs, big.NewInt(-123)) + expectedStr = append(expectedStr, "-123") + case common.Uint256Ty: + inputs = append(inputs, big.NewInt(123)) + expectedStr = append(expectedStr, "123") + case common.StringTy: + inputs = append(inputs, "test") + expectedStr = append(expectedStr, "test") + case common.BoolTy: + inputs = append(inputs, true) + expectedStr = append(expectedStr, "true") + case common.AddressTy: + addr := common.HexToAddress("0x1234567890123456789012345678901234567890") + inputs = append(inputs, addr) + expectedStr = append(expectedStr, addr.Hex()) + case common.BytesTy: + b := []byte{1, 2, 3} + inputs = append(inputs, b) + expectedStr = append(expectedStr, common.Bytes2Hex(b)) + default: + // Handle fixed byte types (Bytes1Ty through Bytes32Ty) + if len(paramType) > 5 && paramType[:5] == "Bytes" { + size, _ := strconv.Atoi(string(paramType[5:])) + b := make([]byte, size) + for i := 0; i < size; i++ { + b[i] = byte(i) + } + arrayType := reflect.ArrayOf(size, reflect.TypeOf(byte(0))) + array := reflect.New(arrayType).Elem() + for i := 0; i < size; i++ { + array.Index(i).Set(reflect.ValueOf(byte(i))) + } + inputs = append(inputs, array.Interface()) + expectedStr = append(expectedStr, common.Bytes2Hex(b)) + } + } + } + + expected := strings.Join(expectedStr, " ") + + // Encode the selector and parameters + sig := make([]byte, 4) + binary.BigEndian.PutUint32(sig, selector) + + // Pack parameters using abi encoding + // Parse the parameter types dynamically + arguments := abi.Arguments{} + for _, paramType := range paramTypes { + // when parsing paramType to abi.Type, convert to lowercase (e.g., "Uint256" -> "uint256") + typ, _ := abi.NewType(strings.ToLower(string(paramType)), "", nil) + arguments = append(arguments, abi.Argument{ + Type: typ, + }) + } + + // Encode the parameters + encodedParams, err := arguments.Pack(inputs...) + if err != nil { + log.Fatalf("Failed to encode parameters: %v", err) + } + + callData := append(sig, encodedParams...) + + // Decode and verify + c := &consoleLog{} + decoded, err := c.toLogString(callData) + assert.NoError(t, err) + assert.Equal(t, expected, decoded) + }) + } +} diff --git a/blockchain/vm/evm.go b/blockchain/vm/evm.go index 7e52a56c7..4891a9a53 100644 --- a/blockchain/vm/evm.go +++ b/blockchain/vm/evm.go @@ -35,9 +35,14 @@ import ( "github.com/kaiachain/kaia/params" ) -// emptyCodeHash is used by create to ensure deployment is disallowed to already -// deployed contract addresses (relevant after the account abstraction). -var emptyCodeHash = crypto.Keccak256Hash(nil) +var ( + // emptyCodeHash is used by create to ensure deployment is disallowed to already + // deployed contract addresses (relevant after the account abstraction). + emptyCodeHash = crypto.Keccak256Hash(nil) + + // consoleLog is a precompiled contract used for debugging purposes. + consoleLogContractAddress = common.HexToAddress("0x000000000000000000636F6E736F6C652E6C6F67") +) const ( CancelByCtxDone = 1 << iota @@ -256,7 +261,8 @@ func (evm *EVM) Call(caller types.ContractRef, addr common.Address, input []byte ) // Filter out invalid precompiled address calls, and create a precompiled contract object if it is not exist. - if IsPrecompiledContractAddress(addr, evm.chainRules) { + // Because IsPrecompiledContractAddress checks for 0..0x400 and ConsoleLog address is outside of the range, we add one more condition if UseConsoleLog. + if IsPrecompiledContractAddress(addr, evm.chainRules) || (addr == consoleLogContractAddress && evm.Config.UseConsoleLog) { precompiles := evm.GetPrecompiledContractMap(caller.Address()) if precompiles[addr] == nil || value.Sign() != 0 { // Return an error if an enabled precompiled address is called or a value is transferred to a precompiled address. @@ -636,6 +642,15 @@ func (evm *EVM) CreateWithAddress(caller types.ContractRef, code []byte, gas uin } func (evm *EVM) GetPrecompiledContractMap(addr common.Address) map[common.Address]PrecompiledContract { + precompiles := evm.getPrecompiledContractForVersion(addr) + // If console.log is enabled, add console.log precompile too. + if evm.Config.UseConsoleLog { + precompiles[consoleLogContractAddress] = &consoleLog{} + } + return precompiles +} + +func (evm *EVM) getPrecompiledContractForVersion(addr common.Address) map[common.Address]PrecompiledContract { // VmVersion means that the contract uses the precompiled contract map at the deployment time. // Also, it follows old map's gas price & computation cost. diff --git a/blockchain/vm/interpreter.go b/blockchain/vm/interpreter.go index 735b978ac..965cbe7ae 100644 --- a/blockchain/vm/interpreter.go +++ b/blockchain/vm/interpreter.go @@ -49,6 +49,9 @@ type Config struct { // ComputationCostLimit is the limit of the total computation cost of a transaction. Set infinite to disable the computation cost limit. ComputationCostLimit uint64 + // UseConsoleLog enables console.log() in solidity for local network + UseConsoleLog bool + // Enables collecting internal transaction data during processing a block EnableInternalTxTracing bool diff --git a/build/Dockerfile-go1.23.3-rpmbuild-gcc7 b/build/Dockerfile-go1.23.3-rpmbuild-gcc7 index a4db2ed72..48cba634d 100644 --- a/build/Dockerfile-go1.23.3-rpmbuild-gcc7 +++ b/build/Dockerfile-go1.23.3-rpmbuild-gcc7 @@ -1,7 +1,7 @@ FROM quay.io/centos/centos:stream9 RUN curl https://dl.google.com/go/go1.23.3.linux-amd64.tar.gz | tar xzvf - -C /usr/local -RUN yum install -y make rpm-build git createrepo python3 gcc unzip +RUN yum install -y make rpm-build git createrepo python3 gcc unzip gdb which RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && unzip awscliv2.zip && ./aws/install && rm awscliv2.zip ENV PATH=$PATH:/usr/local/go/bin CMD ["/bin/sh"] \ No newline at end of file diff --git a/build/Dockerfile-go1.23.3-rpmbuild-gcc7-arm b/build/Dockerfile-go1.23.3-rpmbuild-gcc7-arm index adbbe8dc8..386236616 100644 --- a/build/Dockerfile-go1.23.3-rpmbuild-gcc7-arm +++ b/build/Dockerfile-go1.23.3-rpmbuild-gcc7-arm @@ -1,7 +1,7 @@ FROM quay.io/centos/centos:stream9 RUN curl https://dl.google.com/go/go1.23.3.linux-arm64.tar.gz | tar xzvf - -C /usr/local -RUN yum install -y make rpm-build git createrepo python3 gcc unzip +RUN yum install -y make rpm-build git createrepo python3 gcc unzip gdb which RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" && unzip awscliv2.zip && ./aws/install && rm awscliv2.zip ENV PATH=$PATH:/usr/local/go/bin CMD ["/bin/sh"] \ No newline at end of file diff --git a/build/Dockerfile-go1.23.3-solc0.8.13-ubuntu-22.04-arm b/build/Dockerfile-go1.23.3-solc0.8.13-ubuntu-22.04-arm new file mode 100644 index 000000000..85ee3bb90 --- /dev/null +++ b/build/Dockerfile-go1.23.3-solc0.8.13-ubuntu-22.04-arm @@ -0,0 +1,40 @@ +FROM ubuntu:22.04 as solc_0.8.13_builder +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Seoul +RUN apt update +RUN apt install -yq tzdata && \ + ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata +RUN apt install -y git lsb-core sudo libboost-all-dev build-essential cmake z3 +RUN git clone --depth 1 --recursive -b v0.8.13 https://github.com/ethereum/solidity +RUN cd /solidity && cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSTATIC_LINKING=1 -DCMAKE_CXX_FLAGS="-w" +RUN cd /solidity && touch prerelease.txt +RUN cd /solidity && make solc +RUN cd /solidity && install -s solc/solc /usr/bin + +FROM ubuntu:22.04 as go_builder +RUN apt update && apt install -y software-properties-common +RUN add-apt-repository ppa:longsleep/golang-backports -y && \ + apt install -yq tzdata && \ + ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata +RUN apt install -y build-essential bash gcc musl-dev openssl wget golang golang-1.17 +RUN wget -O go.src.tar.gz https://dl.google.com/go/go1.23.3.src.tar.gz +RUN tar -C /usr/local -xzf go.src.tar.gz +RUN cd /usr/local/go/src/ && \ + ./make.bash + +FROM ubuntu:22.04 +RUN apt update +RUN apt install -yq tzdata && \ + ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata +RUN apt install -y ca-certificates libboost-all-dev git make gcc libc-dev curl bash python3 python3-dev python3-pip unzip +RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip" && unzip awscliv2.zip && ./aws/install && rm awscliv2.zip +COPY --from=solc_0.8.13_builder /usr/bin/solc /usr/bin/solc +COPY --from=go_builder /usr/local/go /usr/local + +ENV GOPATH /go +ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH +RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" +RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.60.1 \ No newline at end of file diff --git a/build/checksums.txt b/build/checksums.txt index 9caabe80a..b6420f9f6 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -1,7 +1,6 @@ # This file contains sha256 checksums of optional build dependencies. -# version:spec-tests 3.0.0 +# version:spec-tests pectra-devnet-5@v1.1.0 # https://github.com/ethereum/execution-spec-tests/releases -# https://github.com/ethereum/execution-spec-tests/releases/download/v3.0.0/ -4a750a0554158b4a47def19aa1409f5f75ccbebad223d41a2148790b06399528 fixtures_develop.tar.gz -9d12f331d21cdc580f658a9891133ac26da703351d2585077935e6abde72851d fixtures_pectra-devnet-4.tar.gz +# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-5@v1.1.0 +e39f265673b217b7438b4f6f8cf5c56e7e2dca5e5d51e5d3c69a5e4a6c79a1c0 fixtures_pectra-devnet-5.tar.gz diff --git a/build/ci.go b/build/ci.go index c9c5c3c82..15d61d5fc 100644 --- a/build/ci.go +++ b/build/ci.go @@ -275,7 +275,6 @@ func doTest(cmdline []string) { // Get test fixtures. csdb := build.MustLoadChecksums("build/checksums.txt") downloadSpecTestFixtures(csdb, *cachedir) - downloadPragueSpecTestFixtures(csdb, *cachedir) packages := []string{"./..."} if len(flag.CommandLine.Args()) > 0 { @@ -306,28 +305,7 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string { log.Fatal(err) } ext := ".tar.gz" - base := "fixtures_develop" // TODO(MariusVanDerWijden) rename once the version becomes part of the filename - url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/v%s/%s%s", executionSpecTestsVersion, base, ext) - archivePath := filepath.Join(cachedir, base+ext) - if err := csdb.DownloadFile(url, archivePath); err != nil { - log.Fatal(err) - } - if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil { - log.Fatal(err) - } - return filepath.Join(cachedir, base) -} - -// downloadPragueSpecTestFixtures downloads and extracts the execution-spec-tests fixtures for Prague. -func downloadPragueSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string { - // remove prague of v3 - os.RemoveAll(executionSpecTestsDir + "/fixtures/blockchain_tests/prague") - os.RemoveAll(executionSpecTestsDir + "/fixtures/blockchain_tests_engine/prague") - os.RemoveAll(executionSpecTestsDir + "/fixtures/state_tests/prague") - - executionSpecTestsVersion := "pectra-devnet-4@v1.0.1" - ext := ".tar.gz" - base := "fixtures_pectra-devnet-4" + base := "fixtures_pectra-devnet-5" // TODO(MariusVanDerWijden) rename once the version becomes part of the filename url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/%s/%s%s", executionSpecTestsVersion, base, ext) archivePath := filepath.Join(cachedir, base+ext) if err := csdb.DownloadFile(url, archivePath); err != nil { diff --git a/build/rpm/main.go b/build/rpm/main.go index 293d9f4f5..4ed92a9df 100644 --- a/build/rpm/main.go +++ b/build/rpm/main.go @@ -206,6 +206,8 @@ URL: https://kaia.io Source0: %{name}-%{version}.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) +%global debug_package %{nil} + %description {{ .Description }} diff --git a/cmd/utils/config.go b/cmd/utils/config.go index 488ada03f..d61fc2b76 100644 --- a/cmd/utils/config.go +++ b/cmd/utils/config.go @@ -698,6 +698,10 @@ func (kCfg *KaiaConfig) SetKaiaConfig(ctx *cli.Context, stack *node.Node) { if ctx.IsSet(OpcodeComputationCostLimitFlag.Name) { params.OpcodeComputationCostLimitOverride = ctx.Uint64(OpcodeComputationCostLimitFlag.Name) } + cfg.UseConsoleLog = ctx.Bool(UseConsoleLogFlag.Name) + if cfg.UseConsoleLog && !ctx.IsSet(NetworkIdFlag.Name) { + logger.Crit("Use of --use-console-log is only supported for private network, however --networkid is not set.") + } if ctx.IsSet(SnapshotFlag.Name) { cfg.SnapshotCacheSize = ctx.Int(SnapshotCacheSizeFlag.Name) diff --git a/cmd/utils/flaggroup.go b/cmd/utils/flaggroup.go index b74d20015..ac56554de 100644 --- a/cmd/utils/flaggroup.go +++ b/cmd/utils/flaggroup.go @@ -55,6 +55,7 @@ var FlagGroups = []FlagGroup{ BlockGenerationIntervalFlag, BlockGenerationTimeLimitFlag, OpcodeComputationCostLimitFlag, + UseConsoleLogFlag, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2967ea1d8..59ea178da 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2018,6 +2018,13 @@ var ( EnvVars: []string{"KLAYTN_OPCODE_COMPUTATION_COST_LIMIT", "KAIA_OPCODE_COMPUTATION_COST_LIMIT"}, Category: "KAIA", } + UseConsoleLogFlag = &cli.BoolFlag{ + Name: "use-console-log", + Usage: "", + Value: false, + EnvVars: []string{"KLAYTN_USE_CONSOLE_LOG", "KAIA_USE_CONSOLE_LOG"}, + Category: "KAIA", + } // Gas price oracle settings GpoBlocksFlag = &cli.IntFlag{ diff --git a/cmd/utils/nodeflags.go b/cmd/utils/nodeflags.go index 0a2ae6560..86a6fe79e 100644 --- a/cmd/utils/nodeflags.go +++ b/cmd/utils/nodeflags.go @@ -272,6 +272,7 @@ var CommonNodeFlags = []cli.Flag{ altsrc.NewIntFlag(APIFilterGetLogsMaxItemsFlag), altsrc.NewDurationFlag(APIFilterGetLogsDeadlineFlag), altsrc.NewUint64Flag(OpcodeComputationCostLimitFlag), + altsrc.NewBoolFlag(UseConsoleLogFlag), altsrc.NewBoolFlag(SnapshotFlag), altsrc.NewIntFlag(SnapshotCacheSizeFlag), altsrc.NewBoolFlag(SnapshotAsyncGen), diff --git a/common/console_log.go b/common/console_log.go new file mode 100644 index 000000000..d62feaa3d --- /dev/null +++ b/common/console_log.go @@ -0,0 +1,439 @@ +// Copyright 2024 The Kaia Authors +// This file is part of the Kaia library. +// +// The Kaia library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Kaia library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Kaia library. If not, see . + +package common + +type ConsoleLogType string + +const ( + Int256Ty ConsoleLogType = "Int256" + Uint256Ty = "Uint256" + StringTy = "String" + BoolTy = "Bool" + AddressTy = "Address" + BytesTy = "Bytes" + Bytes1Ty = "Bytes1" + Bytes2Ty = "Bytes2" + Bytes3Ty = "Bytes3" + Bytes4Ty = "Bytes4" + Bytes5Ty = "Bytes5" + Bytes6Ty = "Bytes6" + Bytes7Ty = "Bytes7" + Bytes8Ty = "Bytes8" + Bytes9Ty = "Bytes9" + Bytes10Ty = "Bytes10" + Bytes11Ty = "Bytes11" + Bytes12Ty = "Bytes12" + Bytes13Ty = "Bytes13" + Bytes14Ty = "Bytes14" + Bytes15Ty = "Bytes15" + Bytes16Ty = "Bytes16" + Bytes17Ty = "Bytes17" + Bytes18Ty = "Bytes18" + Bytes19Ty = "Bytes19" + Bytes20Ty = "Bytes20" + Bytes21Ty = "Bytes21" + Bytes22Ty = "Bytes22" + Bytes23Ty = "Bytes23" + Bytes24Ty = "Bytes24" + Bytes25Ty = "Bytes25" + Bytes26Ty = "Bytes26" + Bytes27Ty = "Bytes27" + Bytes28Ty = "Bytes28" + Bytes29Ty = "Bytes29" + Bytes30Ty = "Bytes30" + Bytes31Ty = "Bytes31" + Bytes32Ty = "Bytes32" +) + +/** Maps from a 4-byte function selector to a signature (argument types) */ +var ConsoleLogSignatures = map[uint32][]ConsoleLogType{ + 0x51973ec9: {}, + 0x2d5b6cb9: {Int256Ty}, + 0xf82c50f1: {Uint256Ty}, + 0x41304fac: {StringTy}, + 0x32458eed: {BoolTy}, + 0x2c2ecbc2: {AddressTy}, + 0x0be77f56: {BytesTy}, + 0x6e18a128: {Bytes1Ty}, + 0xe9b62296: {Bytes2Ty}, + 0x2d834926: {Bytes3Ty}, + 0xe05f48d1: {Bytes4Ty}, + 0xa684808d: {Bytes5Ty}, + 0xae84a591: {Bytes6Ty}, + 0x4ed57e28: {Bytes7Ty}, + 0x4f84252e: {Bytes8Ty}, + 0x90bd8cd0: {Bytes9Ty}, + 0x013d178b: {Bytes10Ty}, + 0x04004a2e: {Bytes11Ty}, + 0x86a06abd: {Bytes12Ty}, + 0x94529e34: {Bytes13Ty}, + 0x9266f07f: {Bytes14Ty}, + 0xda9574e0: {Bytes15Ty}, + 0x665c6104: {Bytes16Ty}, + 0x339f673a: {Bytes17Ty}, + 0xc4d23d9a: {Bytes18Ty}, + 0x5e6b5a33: {Bytes19Ty}, + 0x5188e3e9: {Bytes20Ty}, + 0xe9da3560: {Bytes21Ty}, + 0xd5fae89c: {Bytes22Ty}, + 0xaba1cf0d: {Bytes23Ty}, + 0xf1b35b34: {Bytes24Ty}, + 0x0b84bc58: {Bytes25Ty}, + 0xf8b149f1: {Bytes26Ty}, + 0x3a3757dd: {Bytes27Ty}, + 0xc82aeaee: {Bytes28Ty}, + 0x4b69c3d5: {Bytes29Ty}, + 0xee12c4ed: {Bytes30Ty}, + 0xc2854d92: {Bytes31Ty}, + 0x27b7cf85: {Bytes32Ty}, + 0xf666715a: {Uint256Ty, Uint256Ty}, + 0x643fd0df: {Uint256Ty, StringTy}, + 0x1c9d7eb3: {Uint256Ty, BoolTy}, + 0x69276c86: {Uint256Ty, AddressTy}, + 0xb60e72cc: {StringTy, Uint256Ty}, + 0x4b5c4277: {StringTy, StringTy}, + 0xc3b55635: {StringTy, BoolTy}, + 0x319af333: {StringTy, AddressTy}, + 0x399174d3: {BoolTy, Uint256Ty}, + 0x8feac525: {BoolTy, StringTy}, + 0x2a110e83: {BoolTy, BoolTy}, + 0x853c4849: {BoolTy, AddressTy}, + 0x8309e8a8: {AddressTy, Uint256Ty}, + 0x759f86bb: {AddressTy, StringTy}, + 0x75b605d3: {AddressTy, BoolTy}, + 0xdaf0d4aa: {AddressTy, AddressTy}, + 0xd1ed7a3c: {Uint256Ty, Uint256Ty, Uint256Ty}, + 0x71d04af2: {Uint256Ty, Uint256Ty, StringTy}, + 0x4766da72: {Uint256Ty, Uint256Ty, BoolTy}, + 0x5c96b331: {Uint256Ty, Uint256Ty, AddressTy}, + 0x37aa7d4c: {Uint256Ty, StringTy, Uint256Ty}, + 0xb115611f: {Uint256Ty, StringTy, StringTy}, + 0x4ceda75a: {Uint256Ty, StringTy, BoolTy}, + 0x7afac959: {Uint256Ty, StringTy, AddressTy}, + 0x20098014: {Uint256Ty, BoolTy, Uint256Ty}, + 0x85775021: {Uint256Ty, BoolTy, StringTy}, + 0x20718650: {Uint256Ty, BoolTy, BoolTy}, + 0x35085f7b: {Uint256Ty, BoolTy, AddressTy}, + 0x5a9b5ed5: {Uint256Ty, AddressTy, Uint256Ty}, + 0x63cb41f9: {Uint256Ty, AddressTy, StringTy}, + 0x9b6ec042: {Uint256Ty, AddressTy, BoolTy}, + 0xbcfd9be0: {Uint256Ty, AddressTy, AddressTy}, + 0xca47c4eb: {StringTy, Uint256Ty, Uint256Ty}, + 0x5970e089: {StringTy, Uint256Ty, StringTy}, + 0xca7733b1: {StringTy, Uint256Ty, BoolTy}, + 0x1c7ec448: {StringTy, Uint256Ty, AddressTy}, + 0x5821efa1: {StringTy, StringTy, Uint256Ty}, + 0x2ced7cef: {StringTy, StringTy, StringTy}, + 0xb0e0f9b5: {StringTy, StringTy, BoolTy}, + 0x95ed0195: {StringTy, StringTy, AddressTy}, + 0xc95958d6: {StringTy, BoolTy, Uint256Ty}, + 0xe298f47d: {StringTy, BoolTy, StringTy}, + 0x850b7ad6: {StringTy, BoolTy, BoolTy}, + 0x932bbb38: {StringTy, BoolTy, AddressTy}, + 0x0d26b925: {StringTy, AddressTy, Uint256Ty}, + 0xe0e9ad4f: {StringTy, AddressTy, StringTy}, + 0xc91d5ed4: {StringTy, AddressTy, BoolTy}, + 0xfcec75e0: {StringTy, AddressTy, AddressTy}, + 0x37103367: {BoolTy, Uint256Ty, Uint256Ty}, + 0xc3fc3970: {BoolTy, Uint256Ty, StringTy}, + 0xe8defba9: {BoolTy, Uint256Ty, BoolTy}, + 0x088ef9d2: {BoolTy, Uint256Ty, AddressTy}, + 0x1093ee11: {BoolTy, StringTy, Uint256Ty}, + 0xb076847f: {BoolTy, StringTy, StringTy}, + 0xdbb4c247: {BoolTy, StringTy, BoolTy}, + 0x9591b953: {BoolTy, StringTy, AddressTy}, + 0x12f21602: {BoolTy, BoolTy, Uint256Ty}, + 0x2555fa46: {BoolTy, BoolTy, StringTy}, + 0x50709698: {BoolTy, BoolTy, BoolTy}, + 0x1078f68d: {BoolTy, BoolTy, AddressTy}, + 0x5f7b9afb: {BoolTy, AddressTy, Uint256Ty}, + 0xde9a9270: {BoolTy, AddressTy, StringTy}, + 0x18c9c746: {BoolTy, AddressTy, BoolTy}, + 0xd2763667: {BoolTy, AddressTy, AddressTy}, + 0xb69bcaf6: {AddressTy, Uint256Ty, Uint256Ty}, + 0xa1f2e8aa: {AddressTy, Uint256Ty, StringTy}, + 0x678209a8: {AddressTy, Uint256Ty, BoolTy}, + 0x7bc0d848: {AddressTy, Uint256Ty, AddressTy}, + 0x67dd6ff1: {AddressTy, StringTy, Uint256Ty}, + 0xfb772265: {AddressTy, StringTy, StringTy}, + 0xcf020fb1: {AddressTy, StringTy, BoolTy}, + 0xf08744e8: {AddressTy, StringTy, AddressTy}, + 0x9c4f99fb: {AddressTy, BoolTy, Uint256Ty}, + 0x212255cc: {AddressTy, BoolTy, StringTy}, + 0xeb830c92: {AddressTy, BoolTy, BoolTy}, + 0xf11699ed: {AddressTy, BoolTy, AddressTy}, + 0x17fe6185: {AddressTy, AddressTy, Uint256Ty}, + 0x007150be: {AddressTy, AddressTy, StringTy}, + 0xf2a66286: {AddressTy, AddressTy, BoolTy}, + 0x018c84c2: {AddressTy, AddressTy, AddressTy}, + 0x193fb800: {Uint256Ty, Uint256Ty, Uint256Ty, Uint256Ty}, + 0x59cfcbe3: {Uint256Ty, Uint256Ty, Uint256Ty, StringTy}, + 0xc598d185: {Uint256Ty, Uint256Ty, Uint256Ty, BoolTy}, + 0xfa8185af: {Uint256Ty, Uint256Ty, Uint256Ty, AddressTy}, + 0x5da297eb: {Uint256Ty, Uint256Ty, StringTy, Uint256Ty}, + 0x27d8afd2: {Uint256Ty, Uint256Ty, StringTy, StringTy}, + 0x7af6ab25: {Uint256Ty, Uint256Ty, StringTy, BoolTy}, + 0x42d21db7: {Uint256Ty, Uint256Ty, StringTy, AddressTy}, + 0xeb7f6fd2: {Uint256Ty, Uint256Ty, BoolTy, Uint256Ty}, + 0xa5b4fc99: {Uint256Ty, Uint256Ty, BoolTy, StringTy}, + 0xab085ae6: {Uint256Ty, Uint256Ty, BoolTy, BoolTy}, + 0x9a816a83: {Uint256Ty, Uint256Ty, BoolTy, AddressTy}, + 0x88f6e4b2: {Uint256Ty, Uint256Ty, AddressTy, Uint256Ty}, + 0x6cde40b8: {Uint256Ty, Uint256Ty, AddressTy, StringTy}, + 0x15cac476: {Uint256Ty, Uint256Ty, AddressTy, BoolTy}, + 0x56a5d1b1: {Uint256Ty, Uint256Ty, AddressTy, AddressTy}, + 0x82c25b74: {Uint256Ty, StringTy, Uint256Ty, Uint256Ty}, + 0xb7b914ca: {Uint256Ty, StringTy, Uint256Ty, StringTy}, + 0x691a8f74: {Uint256Ty, StringTy, Uint256Ty, BoolTy}, + 0x3b2279b4: {Uint256Ty, StringTy, Uint256Ty, AddressTy}, + 0xb028c9bd: {Uint256Ty, StringTy, StringTy, Uint256Ty}, + 0x21ad0683: {Uint256Ty, StringTy, StringTy, StringTy}, + 0xb3a6b6bd: {Uint256Ty, StringTy, StringTy, BoolTy}, + 0xd583c602: {Uint256Ty, StringTy, StringTy, AddressTy}, + 0xcf009880: {Uint256Ty, StringTy, BoolTy, Uint256Ty}, + 0xd2d423cd: {Uint256Ty, StringTy, BoolTy, StringTy}, + 0xba535d9c: {Uint256Ty, StringTy, BoolTy, BoolTy}, + 0xae2ec581: {Uint256Ty, StringTy, BoolTy, AddressTy}, + 0xe8d3018d: {Uint256Ty, StringTy, AddressTy, Uint256Ty}, + 0x9c3adfa1: {Uint256Ty, StringTy, AddressTy, StringTy}, + 0x90c30a56: {Uint256Ty, StringTy, AddressTy, BoolTy}, + 0x6168ed61: {Uint256Ty, StringTy, AddressTy, AddressTy}, + 0xc6acc7a8: {Uint256Ty, BoolTy, Uint256Ty, Uint256Ty}, + 0xde03e774: {Uint256Ty, BoolTy, Uint256Ty, StringTy}, + 0x91a02e2a: {Uint256Ty, BoolTy, Uint256Ty, BoolTy}, + 0x88cb6041: {Uint256Ty, BoolTy, Uint256Ty, AddressTy}, + 0x2c1d0746: {Uint256Ty, BoolTy, StringTy, Uint256Ty}, + 0x68c8b8bd: {Uint256Ty, BoolTy, StringTy, StringTy}, + 0xeb928d7f: {Uint256Ty, BoolTy, StringTy, BoolTy}, + 0xef529018: {Uint256Ty, BoolTy, StringTy, AddressTy}, + 0x7464ce23: {Uint256Ty, BoolTy, BoolTy, Uint256Ty}, + 0xdddb9561: {Uint256Ty, BoolTy, BoolTy, StringTy}, + 0xb6f577a1: {Uint256Ty, BoolTy, BoolTy, BoolTy}, + 0x69640b59: {Uint256Ty, BoolTy, BoolTy, AddressTy}, + 0x078287f5: {Uint256Ty, BoolTy, AddressTy, Uint256Ty}, + 0xade052c7: {Uint256Ty, BoolTy, AddressTy, StringTy}, + 0x454d54a5: {Uint256Ty, BoolTy, AddressTy, BoolTy}, + 0xa1ef4cbb: {Uint256Ty, BoolTy, AddressTy, AddressTy}, + 0x0c9cd9c1: {Uint256Ty, AddressTy, Uint256Ty, Uint256Ty}, + 0xddb06521: {Uint256Ty, AddressTy, Uint256Ty, StringTy}, + 0x5f743a7c: {Uint256Ty, AddressTy, Uint256Ty, BoolTy}, + 0x15c127b5: {Uint256Ty, AddressTy, Uint256Ty, AddressTy}, + 0x46826b5d: {Uint256Ty, AddressTy, StringTy, Uint256Ty}, + 0x3e128ca3: {Uint256Ty, AddressTy, StringTy, StringTy}, + 0xcc32ab07: {Uint256Ty, AddressTy, StringTy, BoolTy}, + 0x9cba8fff: {Uint256Ty, AddressTy, StringTy, AddressTy}, + 0x5abd992a: {Uint256Ty, AddressTy, BoolTy, Uint256Ty}, + 0x90fb06aa: {Uint256Ty, AddressTy, BoolTy, StringTy}, + 0xe351140f: {Uint256Ty, AddressTy, BoolTy, BoolTy}, + 0xef72c513: {Uint256Ty, AddressTy, BoolTy, AddressTy}, + 0x736efbb6: {Uint256Ty, AddressTy, AddressTy, Uint256Ty}, + 0x031c6f73: {Uint256Ty, AddressTy, AddressTy, StringTy}, + 0x091ffaf5: {Uint256Ty, AddressTy, AddressTy, BoolTy}, + 0x2488b414: {Uint256Ty, AddressTy, AddressTy, AddressTy}, + 0xa7a87853: {StringTy, Uint256Ty, Uint256Ty, Uint256Ty}, + 0x854b3496: {StringTy, Uint256Ty, Uint256Ty, StringTy}, + 0x7626db92: {StringTy, Uint256Ty, Uint256Ty, BoolTy}, + 0xe21de278: {StringTy, Uint256Ty, Uint256Ty, AddressTy}, + 0xc67ea9d1: {StringTy, Uint256Ty, StringTy, Uint256Ty}, + 0x5ab84e1f: {StringTy, Uint256Ty, StringTy, StringTy}, + 0x7d24491d: {StringTy, Uint256Ty, StringTy, BoolTy}, + 0x7c4632a4: {StringTy, Uint256Ty, StringTy, AddressTy}, + 0xe41b6f6f: {StringTy, Uint256Ty, BoolTy, Uint256Ty}, + 0xabf73a98: {StringTy, Uint256Ty, BoolTy, StringTy}, + 0x354c36d6: {StringTy, Uint256Ty, BoolTy, BoolTy}, + 0xe0e95b98: {StringTy, Uint256Ty, BoolTy, AddressTy}, + 0x4f04fdc6: {StringTy, Uint256Ty, AddressTy, Uint256Ty}, + 0x9ffb2f93: {StringTy, Uint256Ty, AddressTy, StringTy}, + 0x82112a42: {StringTy, Uint256Ty, AddressTy, BoolTy}, + 0x5ea2b7ae: {StringTy, Uint256Ty, AddressTy, AddressTy}, + 0xf45d7d2c: {StringTy, StringTy, Uint256Ty, Uint256Ty}, + 0x5d1a971a: {StringTy, StringTy, Uint256Ty, StringTy}, + 0xc3a8a654: {StringTy, StringTy, Uint256Ty, BoolTy}, + 0x1023f7b2: {StringTy, StringTy, Uint256Ty, AddressTy}, + 0x8eafb02b: {StringTy, StringTy, StringTy, Uint256Ty}, + 0xde68f20a: {StringTy, StringTy, StringTy, StringTy}, + 0x2c1754ed: {StringTy, StringTy, StringTy, BoolTy}, + 0x6d572f44: {StringTy, StringTy, StringTy, AddressTy}, + 0xd6aefad2: {StringTy, StringTy, BoolTy, Uint256Ty}, + 0x5e84b0ea: {StringTy, StringTy, BoolTy, StringTy}, + 0x40785869: {StringTy, StringTy, BoolTy, BoolTy}, + 0xc371c7db: {StringTy, StringTy, BoolTy, AddressTy}, + 0x7cc3c607: {StringTy, StringTy, AddressTy, Uint256Ty}, + 0xeb1bff80: {StringTy, StringTy, AddressTy, StringTy}, + 0x5ccd4e37: {StringTy, StringTy, AddressTy, BoolTy}, + 0x439c7bef: {StringTy, StringTy, AddressTy, AddressTy}, + 0x64b5bb67: {StringTy, BoolTy, Uint256Ty, Uint256Ty}, + 0x742d6ee7: {StringTy, BoolTy, Uint256Ty, StringTy}, + 0x8af7cf8a: {StringTy, BoolTy, Uint256Ty, BoolTy}, + 0x935e09bf: {StringTy, BoolTy, Uint256Ty, AddressTy}, + 0x24f91465: {StringTy, BoolTy, StringTy, Uint256Ty}, + 0xa826caeb: {StringTy, BoolTy, StringTy, StringTy}, + 0x3f8a701d: {StringTy, BoolTy, StringTy, BoolTy}, + 0xe0625b29: {StringTy, BoolTy, StringTy, AddressTy}, + 0x8e3f78a9: {StringTy, BoolTy, BoolTy, Uint256Ty}, + 0x9d22d5dd: {StringTy, BoolTy, BoolTy, StringTy}, + 0x895af8c5: {StringTy, BoolTy, BoolTy, BoolTy}, + 0x7190a529: {StringTy, BoolTy, BoolTy, AddressTy}, + 0x5d08bb05: {StringTy, BoolTy, AddressTy, Uint256Ty}, + 0x2d8e33a4: {StringTy, BoolTy, AddressTy, StringTy}, + 0x958c28c6: {StringTy, BoolTy, AddressTy, BoolTy}, + 0x33e9dd1d: {StringTy, BoolTy, AddressTy, AddressTy}, + 0xf8f51b1e: {StringTy, AddressTy, Uint256Ty, Uint256Ty}, + 0x5a477632: {StringTy, AddressTy, Uint256Ty, StringTy}, + 0xfc4845f0: {StringTy, AddressTy, Uint256Ty, BoolTy}, + 0x63fb8bc5: {StringTy, AddressTy, Uint256Ty, AddressTy}, + 0x91d1112e: {StringTy, AddressTy, StringTy, Uint256Ty}, + 0x245986f2: {StringTy, AddressTy, StringTy, StringTy}, + 0x5f15d28c: {StringTy, AddressTy, StringTy, BoolTy}, + 0xaabc9a31: {StringTy, AddressTy, StringTy, AddressTy}, + 0x3e9f866a: {StringTy, AddressTy, BoolTy, Uint256Ty}, + 0x0454c079: {StringTy, AddressTy, BoolTy, StringTy}, + 0x79884c2b: {StringTy, AddressTy, BoolTy, BoolTy}, + 0x223603bd: {StringTy, AddressTy, BoolTy, AddressTy}, + 0x8ef3f399: {StringTy, AddressTy, AddressTy, Uint256Ty}, + 0x800a1c67: {StringTy, AddressTy, AddressTy, StringTy}, + 0xb59dbd60: {StringTy, AddressTy, AddressTy, BoolTy}, + 0xed8f28f6: {StringTy, AddressTy, AddressTy, AddressTy}, + 0x374bb4b2: {BoolTy, Uint256Ty, Uint256Ty, Uint256Ty}, + 0x8e69fb5d: {BoolTy, Uint256Ty, Uint256Ty, StringTy}, + 0xbe984353: {BoolTy, Uint256Ty, Uint256Ty, BoolTy}, + 0x00dd87b9: {BoolTy, Uint256Ty, Uint256Ty, AddressTy}, + 0x6a1199e2: {BoolTy, Uint256Ty, StringTy, Uint256Ty}, + 0xf5bc2249: {BoolTy, Uint256Ty, StringTy, StringTy}, + 0xe5e70b2b: {BoolTy, Uint256Ty, StringTy, BoolTy}, + 0xfedd1fff: {BoolTy, Uint256Ty, StringTy, AddressTy}, + 0x7f9bbca2: {BoolTy, Uint256Ty, BoolTy, Uint256Ty}, + 0x9143dbb1: {BoolTy, Uint256Ty, BoolTy, StringTy}, + 0xceb5f4d7: {BoolTy, Uint256Ty, BoolTy, BoolTy}, + 0x9acd3616: {BoolTy, Uint256Ty, BoolTy, AddressTy}, + 0x1537dc87: {BoolTy, Uint256Ty, AddressTy, Uint256Ty}, + 0x1bb3b09a: {BoolTy, Uint256Ty, AddressTy, StringTy}, + 0xb4c314ff: {BoolTy, Uint256Ty, AddressTy, BoolTy}, + 0x26f560a8: {BoolTy, Uint256Ty, AddressTy, AddressTy}, + 0x28863fcb: {BoolTy, StringTy, Uint256Ty, Uint256Ty}, + 0x1ad96de6: {BoolTy, StringTy, Uint256Ty, StringTy}, + 0x6b0e5d53: {BoolTy, StringTy, Uint256Ty, BoolTy}, + 0x1596a1ce: {BoolTy, StringTy, Uint256Ty, AddressTy}, + 0x7be0c3eb: {BoolTy, StringTy, StringTy, Uint256Ty}, + 0x1762e32a: {BoolTy, StringTy, StringTy, StringTy}, + 0x1e4b87e5: {BoolTy, StringTy, StringTy, BoolTy}, + 0x97d394d8: {BoolTy, StringTy, StringTy, AddressTy}, + 0x1606a393: {BoolTy, StringTy, BoolTy, Uint256Ty}, + 0x483d0416: {BoolTy, StringTy, BoolTy, StringTy}, + 0xdc5e935b: {BoolTy, StringTy, BoolTy, BoolTy}, + 0x538e06ab: {BoolTy, StringTy, BoolTy, AddressTy}, + 0xa5cada94: {BoolTy, StringTy, AddressTy, Uint256Ty}, + 0x12d6c788: {BoolTy, StringTy, AddressTy, StringTy}, + 0x6dd434ca: {BoolTy, StringTy, AddressTy, BoolTy}, + 0x2b2b18dc: {BoolTy, StringTy, AddressTy, AddressTy}, + 0x0bb00eab: {BoolTy, BoolTy, Uint256Ty, Uint256Ty}, + 0x7dd4d0e0: {BoolTy, BoolTy, Uint256Ty, StringTy}, + 0x619e4d0e: {BoolTy, BoolTy, Uint256Ty, BoolTy}, + 0x54a7a9a0: {BoolTy, BoolTy, Uint256Ty, AddressTy}, + 0xe3a9ca2f: {BoolTy, BoolTy, StringTy, Uint256Ty}, + 0x6d1e8751: {BoolTy, BoolTy, StringTy, StringTy}, + 0xb857163a: {BoolTy, BoolTy, StringTy, BoolTy}, + 0xf9ad2b89: {BoolTy, BoolTy, StringTy, AddressTy}, + 0x6d7045c1: {BoolTy, BoolTy, BoolTy, Uint256Ty}, + 0x2ae408d4: {BoolTy, BoolTy, BoolTy, StringTy}, + 0x3b2a5ce0: {BoolTy, BoolTy, BoolTy, BoolTy}, + 0x8c329b1a: {BoolTy, BoolTy, BoolTy, AddressTy}, + 0x4c123d57: {BoolTy, BoolTy, AddressTy, Uint256Ty}, + 0xa0a47963: {BoolTy, BoolTy, AddressTy, StringTy}, + 0xc0a302d8: {BoolTy, BoolTy, AddressTy, BoolTy}, + 0xf4880ea4: {BoolTy, BoolTy, AddressTy, AddressTy}, + 0x7bf181a1: {BoolTy, AddressTy, Uint256Ty, Uint256Ty}, + 0x51f09ff8: {BoolTy, AddressTy, Uint256Ty, StringTy}, + 0xd6019f1c: {BoolTy, AddressTy, Uint256Ty, BoolTy}, + 0x136b05dd: {BoolTy, AddressTy, Uint256Ty, AddressTy}, + 0xc21f64c7: {BoolTy, AddressTy, StringTy, Uint256Ty}, + 0xa73c1db6: {BoolTy, AddressTy, StringTy, StringTy}, + 0xe2bfd60b: {BoolTy, AddressTy, StringTy, BoolTy}, + 0x6f7c603e: {BoolTy, AddressTy, StringTy, AddressTy}, + 0x07831502: {BoolTy, AddressTy, BoolTy, Uint256Ty}, + 0x4a66cb34: {BoolTy, AddressTy, BoolTy, StringTy}, + 0x6a9c478b: {BoolTy, AddressTy, BoolTy, BoolTy}, + 0x1c41a336: {BoolTy, AddressTy, BoolTy, AddressTy}, + 0x0c66d1be: {BoolTy, AddressTy, AddressTy, Uint256Ty}, + 0xd812a167: {BoolTy, AddressTy, AddressTy, StringTy}, + 0x46600be0: {BoolTy, AddressTy, AddressTy, BoolTy}, + 0x1d14d001: {BoolTy, AddressTy, AddressTy, AddressTy}, + 0x34f0e636: {AddressTy, Uint256Ty, Uint256Ty, Uint256Ty}, + 0x4a28c017: {AddressTy, Uint256Ty, Uint256Ty, StringTy}, + 0x66f1bc67: {AddressTy, Uint256Ty, Uint256Ty, BoolTy}, + 0x20e3984d: {AddressTy, Uint256Ty, Uint256Ty, AddressTy}, + 0xbf01f891: {AddressTy, Uint256Ty, StringTy, Uint256Ty}, + 0x88a8c406: {AddressTy, Uint256Ty, StringTy, StringTy}, + 0xcf18105c: {AddressTy, Uint256Ty, StringTy, BoolTy}, + 0x5c430d47: {AddressTy, Uint256Ty, StringTy, AddressTy}, + 0x22f6b999: {AddressTy, Uint256Ty, BoolTy, Uint256Ty}, + 0xc5ad85f9: {AddressTy, Uint256Ty, BoolTy, StringTy}, + 0x3bf5e537: {AddressTy, Uint256Ty, BoolTy, BoolTy}, + 0xa31bfdcc: {AddressTy, Uint256Ty, BoolTy, AddressTy}, + 0x100f650e: {AddressTy, Uint256Ty, AddressTy, Uint256Ty}, + 0x1da986ea: {AddressTy, Uint256Ty, AddressTy, StringTy}, + 0xa1bcc9b3: {AddressTy, Uint256Ty, AddressTy, BoolTy}, + 0x478d1c62: {AddressTy, Uint256Ty, AddressTy, AddressTy}, + 0x1dc8e1b8: {AddressTy, StringTy, Uint256Ty, Uint256Ty}, + 0x448830a8: {AddressTy, StringTy, Uint256Ty, StringTy}, + 0x0ef7e050: {AddressTy, StringTy, Uint256Ty, BoolTy}, + 0x63183678: {AddressTy, StringTy, Uint256Ty, AddressTy}, + 0x159f8927: {AddressTy, StringTy, StringTy, Uint256Ty}, + 0x5d02c50b: {AddressTy, StringTy, StringTy, StringTy}, + 0x35a5071f: {AddressTy, StringTy, StringTy, BoolTy}, + 0xa04e2f87: {AddressTy, StringTy, StringTy, AddressTy}, + 0x515e38b6: {AddressTy, StringTy, BoolTy, Uint256Ty}, + 0xbc0b61fe: {AddressTy, StringTy, BoolTy, StringTy}, + 0x5f1d5c9f: {AddressTy, StringTy, BoolTy, BoolTy}, + 0x205871c2: {AddressTy, StringTy, BoolTy, AddressTy}, + 0x457fe3cf: {AddressTy, StringTy, AddressTy, Uint256Ty}, + 0xf7e36245: {AddressTy, StringTy, AddressTy, StringTy}, + 0x0df12b76: {AddressTy, StringTy, AddressTy, BoolTy}, + 0x0d36fa20: {AddressTy, StringTy, AddressTy, AddressTy}, + 0x386ff5f4: {AddressTy, BoolTy, Uint256Ty, Uint256Ty}, + 0x0aa6cfad: {AddressTy, BoolTy, Uint256Ty, StringTy}, + 0xc4643e20: {AddressTy, BoolTy, Uint256Ty, BoolTy}, + 0xccf790a1: {AddressTy, BoolTy, Uint256Ty, AddressTy}, + 0x80e6a20b: {AddressTy, BoolTy, StringTy, Uint256Ty}, + 0x475c5c33: {AddressTy, BoolTy, StringTy, StringTy}, + 0x50ad461d: {AddressTy, BoolTy, StringTy, BoolTy}, + 0x19fd4956: {AddressTy, BoolTy, StringTy, AddressTy}, + 0x8c4e5de6: {AddressTy, BoolTy, BoolTy, Uint256Ty}, + 0xdfc4a2e8: {AddressTy, BoolTy, BoolTy, StringTy}, + 0xcac43479: {AddressTy, BoolTy, BoolTy, BoolTy}, + 0xcf394485: {AddressTy, BoolTy, BoolTy, AddressTy}, + 0xa75c59de: {AddressTy, BoolTy, AddressTy, Uint256Ty}, + 0x2dd778e6: {AddressTy, BoolTy, AddressTy, StringTy}, + 0xa6f50b0f: {AddressTy, BoolTy, AddressTy, BoolTy}, + 0x660375dd: {AddressTy, BoolTy, AddressTy, AddressTy}, + 0xbe553481: {AddressTy, AddressTy, Uint256Ty, Uint256Ty}, + 0xfdb4f990: {AddressTy, AddressTy, Uint256Ty, StringTy}, + 0x9b4254e2: {AddressTy, AddressTy, Uint256Ty, BoolTy}, + 0x8da6def5: {AddressTy, AddressTy, Uint256Ty, AddressTy}, + 0xef1cefe7: {AddressTy, AddressTy, StringTy, Uint256Ty}, + 0x21bdaf25: {AddressTy, AddressTy, StringTy, StringTy}, + 0x6f1a594e: {AddressTy, AddressTy, StringTy, BoolTy}, + 0x8f736d16: {AddressTy, AddressTy, StringTy, AddressTy}, + 0x3971e78c: {AddressTy, AddressTy, BoolTy, Uint256Ty}, + 0xaa6540c8: {AddressTy, AddressTy, BoolTy, StringTy}, + 0x2cd4134a: {AddressTy, AddressTy, BoolTy, BoolTy}, + 0x9f1bc36e: {AddressTy, AddressTy, BoolTy, AddressTy}, + 0x94250d77: {AddressTy, AddressTy, AddressTy, Uint256Ty}, + 0xf808da20: {AddressTy, AddressTy, AddressTy, StringTy}, + 0x0e378994: {AddressTy, AddressTy, AddressTy, BoolTy}, + 0x665bf134: {AddressTy, AddressTy, AddressTy, AddressTy}, +} diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 5dafb2dd7..eec6fe527 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -375,17 +375,6 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type return c.verifySeal(chain, header, parents) } -// CreateSnapshot does not return a snapshot but creates a new snapshot if not exists at a given point in time -func (c *Clique) CreateSnapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) error { - _, err := c.snapshot(chain, number, hash, parents) - return err -} - -// GetKaiaHeadersForSnapshotApply is not used for Clique engine -func (c *Clique) GetKaiaHeadersForSnapshotApply(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) ([]*types.Header, error) { - return nil, nil -} - // snapshot retrieves the authorization snapshot at a given point in time. func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { // Search for a snapshot in memory or on disk for checkpoints @@ -593,7 +582,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro return nil } -func (c *Clique) InitSnapshot() {} +func (c *Clique) PurgeCache() {} func (c *Clique) Initialize(chain consensus.ChainReader, header *types.Header, state *state.StateDB) { } diff --git a/consensus/consensus.go b/consensus/consensus.go index 6a1eb6f5a..90b9e37e2 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -64,10 +64,10 @@ type ChainReader interface { // GetBlock retrieves a block from the database by hash and number. GetBlock(hash common.Hash, number uint64) *types.Block - // State() retrieves statedb + // State retrieves statedb State() (*state.StateDB, error) - // StateAt() retrieves statedb on a particular point in time + // StateAt retrieves statedb on a particular point in time StateAt(root common.Hash) (*state.StateDB, error) } @@ -128,18 +128,10 @@ type Engine interface { // Protocol returns the protocol for this consensus Protocol() Protocol - // TODO-kaiax-valset: delete CreateSnapshot which is called after post insert block - // e.g. insertChain, worker.wait, New() - // CreateSnapshot does not return a snapshot but creates a new snapshot if not exists at a given point in time. - CreateSnapshot(chain ChainReader, number uint64, hash common.Hash, parents []*types.Header) error - - // GetKaiaHeadersForSnapshotApply returns the headers need to be applied to calculate snapshot for the given block number. - GetKaiaHeadersForSnapshotApply(chain ChainReader, number uint64, hash common.Hash, parents []*types.Header) ([]*types.Header, error) - // GetConsensusInfo returns consensus information regarding the given block number. GetConsensusInfo(block *types.Block) (ConsensusInfo, error) - InitSnapshot() + PurgeCache() } // PoW is a consensus engine based on proof-of-work. @@ -178,9 +170,6 @@ type Istanbul interface { // SetChain sets chain of the Istanbul backend SetChain(chain ChainReader) - // UpdateParam updates the governance parameter - UpdateParam(num uint64) error - RegisterKaiaxModules(mGov gov.GovModule, mStaking staking.StakingModule, mValset valset.ValsetModule) kaiax.ConsensusModuleHost diff --git a/consensus/gxhash/consensus.go b/consensus/gxhash/consensus.go index 8223c2a2f..00f6e4ded 100644 --- a/consensus/gxhash/consensus.go +++ b/consensus/gxhash/consensus.go @@ -69,16 +69,6 @@ func (gxhash *Gxhash) PreprocessHeaderVerification(headers []*types.Header) (cha panic("this method is not used for PoW engine") } -// CreateSnapshot is not used for PoW engine. -func (gxhash *Gxhash) CreateSnapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) error { - return nil -} - -// GetKaiaHeadersForSnapshotApply is not used for PoW engine. -func (gxhash *Gxhash) GetKaiaHeadersForSnapshotApply(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) ([]*types.Header, error) { - return nil, nil -} - // GetConsensusInfo is not used for PoW engine. func (gxhash *Gxhash) GetConsensusInfo(block *types.Block) (consensus.ConsensusInfo, error) { return consensus.ConsensusInfo{}, nil @@ -350,7 +340,7 @@ func calcBlockScoreHomestead(time uint64, parent *types.Header) *big.Int { return x } -func (gxhash *Gxhash) InitSnapshot() {} +func (gxhash *Gxhash) PurgeCache() {} // VerifySeal implements consensus.Engine, checking whether the given block satisfies // the PoW blockscore requirements. diff --git a/consensus/istanbul/backend/api.go b/consensus/istanbul/backend/api.go index 887448d11..66f4c92a5 100644 --- a/consensus/istanbul/backend/api.go +++ b/consensus/istanbul/backend/api.go @@ -47,27 +47,6 @@ type API struct { istanbul *backend } -// GetSnapshot retrieves the state snapshot at a given block. -// TODO-kaia-valset: consider deprecation of GetSnapshot -func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { - // Retrieve the requested block number (or current if none requested) - header, err := headerByRpcNumber(api.chain, number) - if err != nil { - return nil, err - } - return checkStatesAndGetSnapshot(api.chain, api.istanbul, header.Number.Uint64(), header.Hash()) -} - -// GetSnapshotAtHash retrieves the state snapshot at a given block. -// TODO-kaia-valset: consider deprecation of GetSnapshotAtHash -func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { - header := api.chain.GetHeaderByHash(hash) - if header == nil { - return nil, errUnknownBlock - } - return checkStatesAndGetSnapshot(api.chain, api.istanbul, header.Number.Uint64(), header.Hash()) -} - // GetValidators retrieves the list of qualified validators with the given block number. func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) { header, err := headerByRpcNumber(api.chain, number) @@ -286,13 +265,6 @@ func (api *APIExtension) GetBlockWithConsensusInfoByNumber(number *rpc.BlockNumb } blockHash := block.Hash() - if blockNumber > 0 { - err := checkStatesForSnapshot(api.chain, api.istanbul, blockNumber-1, block.ParentHash()) - if err != nil { - return nil, err - } - } - cInfo, err := api.istanbul.GetConsensusInfo(block) if err != nil { logger.Error("Getting the proposer and validators failed.", "blockHash", blockHash, "err", err) @@ -369,13 +341,6 @@ func (api *APIExtension) GetBlockWithConsensusInfoByHash(blockHash common.Hash) return nil, fmt.Errorf("the block does not exist (block hash: %s)", blockHash.String()) } - if block.NumberU64() > 0 { - err := checkStatesForSnapshot(api.chain, api.istanbul, block.NumberU64()-1, block.ParentHash()) - if err != nil { - return nil, err - } - } - cInfo, err := api.istanbul.GetConsensusInfo(block) if err != nil { logger.Error("Getting the proposer and validators failed.", "blockHash", blockHash, "err", err) @@ -489,28 +454,3 @@ func headerByRpcNumber(chain consensus.ChainReader, number *rpc.BlockNumber) (*t } return header, nil } - -// Checks the all states for snapshot creation at the given block number -func checkStatesForSnapshot(chain consensus.ChainReader, istBackend *backend, number uint64, hash common.Hash) error { - headers, err := istBackend.GetKaiaHeadersForSnapshotApply(chain, number, hash, nil) - if err != nil { - return err - } - - for _, header := range headers { - if _, err := chain.StateAt(header.Root); err != nil { - return err - } - } - return nil -} - -// Checks the all states for snapshot creation at the given block number and returns the snapshot if all states exist -func checkStatesAndGetSnapshot(chain consensus.ChainReader, istBackend *backend, number uint64, hash common.Hash) (*Snapshot, error) { - err := checkStatesForSnapshot(chain, istBackend, number, hash) - if err != nil { - return nil, err - } - - return istBackend.snapshot(chain, number, hash, nil, false) -} diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index bcbb558e7..a7a8f75de 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -36,7 +36,6 @@ import ( "github.com/kaiachain/kaia/consensus" "github.com/kaiachain/kaia/consensus/istanbul" istanbulCore "github.com/kaiachain/kaia/consensus/istanbul/core" - "github.com/kaiachain/kaia/consensus/istanbul/validator" "github.com/kaiachain/kaia/crypto" "github.com/kaiachain/kaia/crypto/bls" "github.com/kaiachain/kaia/event" @@ -67,7 +66,6 @@ type BackendOpts struct { } func New(opts *BackendOpts) consensus.Istanbul { - recents, _ := lru.NewARC(inmemorySnapshots) recentMessages, _ := lru.NewARC(inmemoryPeers) knownMessages, _ := lru.NewARC(inmemoryMessages) backend := &backend{ @@ -79,7 +77,6 @@ func New(opts *BackendOpts) consensus.Istanbul { logger: logger.NewWith(), db: opts.DB, commitCh: make(chan *types.Result, 1), - recents: recents, candidates: make(map[common.Address]bool), coreStarted: false, recentMessages: recentMessages, @@ -127,8 +124,6 @@ type backend struct { candidates map[common.Address]bool // Protects the signer fields candidatesLock sync.RWMutex - // Snapshots for recent block to speed up reorgs - recents *lru.ARCCache // TODO-kaiax: Remove snapshot cache // event subscription for ChainHeadEvent event broadcaster consensus.Broadcaster @@ -383,32 +378,6 @@ func (sb *backend) HasPropsal(hash common.Hash, number *big.Int) bool { return sb.chain.GetHeader(hash, number.Uint64()) != nil } -// ParentValidators implements istanbul.Backend.GetParentValidators -func (sb *backend) ParentValidators(proposal istanbul.Proposal) istanbul.ValidatorSet { - if block, ok := proposal.(*types.Block); ok { - return sb.getValidators(block.Number().Uint64()-1, block.ParentHash()) - } - - // TODO-Kaia-Governance The following return case should not be called. Refactor it to error handling. - return validator.NewValidatorSet(nil, nil, - istanbul.ProposerPolicy(sb.chain.Config().Istanbul.ProposerPolicy), - sb.chain.Config().Istanbul.SubGroupSize, - sb.chain) -} - -func (sb *backend) getValidators(number uint64, hash common.Hash) istanbul.ValidatorSet { - snap, err := sb.snapshot(sb.chain, number, hash, nil, false) - if err != nil { - logger.Error("Snapshot not found.", "err", err) - // TODO-Kaia-Governance The following return case should not be called. Refactor it to error handling. - return validator.NewValidatorSet(nil, nil, - istanbul.ProposerPolicy(sb.chain.Config().Istanbul.ProposerPolicy), - sb.chain.Config().Istanbul.SubGroupSize, - sb.chain) - } - return snap.ValSet -} - func (sb *backend) LastProposal() (istanbul.Proposal, common.Address) { block := sb.currentBlock() @@ -432,3 +401,69 @@ func (sb *backend) HasBadProposal(hash common.Hash) bool { } return sb.hasBadBlock(hash) } + +func (sb *backend) GetValidatorSet(num uint64) (*istanbul.BlockValSet, error) { + council, err := sb.valsetModule.GetCouncil(num) + if err != nil { + return nil, err + } + + demoted, err := sb.valsetModule.GetDemotedValidators(num) + if err != nil { + return nil, err + } + + return istanbul.NewBlockValSet(council, demoted), nil +} + +func (sb *backend) GetCommitteeState(num uint64) (*istanbul.RoundCommitteeState, error) { + header := sb.chain.GetHeaderByNumber(num) + if header == nil { + return nil, errUnknownBlock + } + + return sb.GetCommitteeStateByRound(num, uint64(header.Round())) +} + +func (sb *backend) GetCommitteeStateByRound(num uint64, round uint64) (*istanbul.RoundCommitteeState, error) { + blockValSet, err := sb.GetValidatorSet(num) + if err != nil { + return nil, err + } + + committee, err := sb.valsetModule.GetCommittee(num, round) + if err != nil { + return nil, err + } + + proposer, err := sb.valsetModule.GetProposer(num, round) + if err != nil { + return nil, err + } + + committeeSize := sb.govModule.GetParamSet(num).CommitteeSize + return istanbul.NewRoundCommitteeState(blockValSet, committeeSize, committee, proposer), nil +} + +// GetProposer implements istanbul.Backend.GetProposer +func (sb *backend) GetProposer(number uint64) common.Address { + if h := sb.chain.GetHeaderByNumber(number); h != nil { + a, _ := sb.Author(h) + return a + } + return common.Address{} +} + +func (sb *backend) GetRewardAddress(num uint64, nodeId common.Address) common.Address { + sInfo, err := sb.stakingModule.GetStakingInfo(num) + if err != nil { + return common.Address{} + } + + for idx, id := range sInfo.NodeIds { + if id == nodeId { + return sInfo.RewardAddrs[idx] + } + } + return common.Address{} +} diff --git a/consensus/istanbul/backend/backend_test.go b/consensus/istanbul/backend/backend_test.go index 52971839b..912fcf4e0 100644 --- a/consensus/istanbul/backend/backend_test.go +++ b/consensus/istanbul/backend/backend_test.go @@ -26,7 +26,6 @@ import ( "bytes" "crypto/ecdsa" "math/big" - "strings" "testing" "time" @@ -41,25 +40,10 @@ import ( ) var ( - testCommitteeSize = uint64(21) - testSigningData = []byte("dummy data") - maxBlockNum = int64(100) + testSigningData = []byte("dummy data") + maxBlockNum = int64(100) ) -type keys []*ecdsa.PrivateKey - -func (slice keys) Len() int { - return len(slice) -} - -func (slice keys) Less(i, j int) bool { - return strings.Compare(crypto.PubkeyToAddress(slice[i].PublicKey).String(), crypto.PubkeyToAddress(slice[j].PublicKey).String()) < 0 -} - -func (slice keys) Swap(i, j int) { - slice[i], slice[j] = slice[j], slice[i] -} - func getTestConfig() *params.ChainConfig { config := params.TestChainConfig.Copy() config.Governance = params.GetDefaultGovernanceConfig() diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 0d698b5f1..446fcc961 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -39,7 +39,6 @@ import ( "github.com/kaiachain/kaia/consensus" "github.com/kaiachain/kaia/consensus/istanbul" istanbulCore "github.com/kaiachain/kaia/consensus/istanbul/core" - "github.com/kaiachain/kaia/consensus/istanbul/validator" "github.com/kaiachain/kaia/consensus/misc" "github.com/kaiachain/kaia/crypto/sha3" "github.com/kaiachain/kaia/kaiax" @@ -52,14 +51,8 @@ import ( ) const ( - // CheckpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database - // inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory - // inmemoryPeers = 40 - // inmemoryMessages = 1024 - - inmemorySnapshots = 496 // Number of recent vote snapshots to keep in memory - inmemoryPeers = 200 - inmemoryMessages = 4096 + inmemoryPeers = 200 + inmemoryMessages = 4096 allowedFutureBlockTime = 1 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks ) @@ -745,64 +738,6 @@ func (sb *backend) Stop() error { return nil } -// UpdateParam implements consensus.Istanbul.UpdateParam and it updates the governance parameters -func (sb *backend) UpdateParam(number uint64) error { - return nil -} - -// initSnapshot initializes and stores a new Snapshot. -func (sb *backend) initSnapshot(chain consensus.ChainReader) (*Snapshot, error) { - genesis := chain.GetHeaderByNumber(0) - if err := sb.VerifyHeader(chain, genesis, false); err != nil { - return nil, err - } - istanbulExtra, err := types.ExtractIstanbulExtra(genesis) - if err != nil { - return nil, err - } - - pset := sb.govModule.GetParamSet(0) - valSet := validator.NewValidatorSet(istanbulExtra.Validators, nil, - istanbul.ProposerPolicy(pset.ProposerPolicy), - pset.CommitteeSize, chain) - valSet.SetMixHash(genesis.MixHash) - snap := newSnapshot(sb.govModule, 0, genesis.Hash(), valSet, chain.Config()) - - if err := snap.store(sb.db); err != nil { - return nil, err - } - logger.Trace("Stored genesis voting snapshot to disk") - return snap, nil -} - -// getPrevHeaderAndUpdateParents returns previous header to find stored Snapshot object and drops the last element of the parents parameter. -func getPrevHeaderAndUpdateParents(chain consensus.ChainReader, number uint64, hash common.Hash, parents *[]*types.Header) *types.Header { - var header *types.Header - if len(*parents) > 0 { - // If we have explicit parents, pick from there (enforced) - header = (*parents)[len(*parents)-1] - if header.Hash() != hash || header.Number.Uint64() != number { - return nil - } - *parents = (*parents)[:len(*parents)-1] - } else { - // No explicit parents (or no more left), reach out to the database - header = chain.GetHeader(hash, number) - if header == nil { - return nil - } - } - return header -} - -// CreateSnapshot does not return a snapshot but creates a new snapshot if not exists at a given point in time -func (sb *backend) CreateSnapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) error { - if _, err := sb.snapshot(chain, number, hash, parents, true); err != nil { - return err - } - return nil -} - // GetConsensusInfo returns consensus information regarding the given block number. func (sb *backend) GetConsensusInfo(block *types.Block) (consensus.ConsensusInfo, error) { blockNumber := block.NumberU64() @@ -864,8 +799,7 @@ func (sb *backend) GetConsensusInfo(block *types.Block) (consensus.ConsensusInfo return cInfo, nil } -func (sb *backend) InitSnapshot() { - sb.recents.Purge() +func (sb *backend) PurgeCache() { sb.blsPubkeyProvider.ResetBlsCache() } diff --git a/consensus/istanbul/backend/engine_test.go b/consensus/istanbul/backend/engine_test.go index d42d0c2cc..7de71c462 100644 --- a/consensus/istanbul/backend/engine_test.go +++ b/consensus/istanbul/backend/engine_test.go @@ -1801,7 +1801,7 @@ func TestGovernance_Votes(t *testing.T) { } func TestGovernance_GovModule(t *testing.T) { - // Test that ReaderEngine (CurrentParams(), GetParamSet(), UpdateParams()) works. + // Test that ReaderEngine (CurrentParams(), GetParamSet()) works. type vote struct { name string value interface{} diff --git a/consensus/istanbul/backend/snapshot.go b/consensus/istanbul/backend/snapshot.go deleted file mode 100644 index 8515bb3ae..000000000 --- a/consensus/istanbul/backend/snapshot.go +++ /dev/null @@ -1,504 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Modifications Copyright 2018 The klaytn Authors -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . -// -// This file is derived from quorum/consensus/istanbul/backend/snapshot.go (2018/06/04). -// Modified and improved for the klaytn development. -// Modified and improved for the Kaia development. - -package backend - -import ( - "bytes" - "encoding/json" - "math/big" - "time" - - "github.com/kaiachain/kaia/blockchain/types" - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/consensus/istanbul/validator" - "github.com/kaiachain/kaia/kaiax/gov" - "github.com/kaiachain/kaia/kaiax/staking" - "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/storage/database" -) - -const ( - dbKeySnapshotPrefix = "istanbul-snapshot" -) - -// Snapshot is the state of the authorization voting at a given point in time. -type Snapshot struct { - Epoch uint64 // The number of blocks after which to checkpoint and reset the pending votes - Number uint64 // Block number where the snapshot was created - Hash common.Hash // Block hash where the snapshot was created - ValSet istanbul.ValidatorSet // Set of authorized validators at this moment - Policy uint64 - CommitteeSize uint64 -} - -func effectiveParams(g gov.GovModule, number uint64) (epoch uint64, policy uint64, committeeSize uint64) { - pset := g.GetParamSet(number) - epoch = pset.Epoch - policy = pset.ProposerPolicy - committeeSize = pset.CommitteeSize - - return -} - -// newSnapshot create a new snapshot with the specified startup parameters. This -// method does not initialize the set of recent validators, so only ever use if for -// the genesis block. -func newSnapshot(g gov.GovModule, number uint64, hash common.Hash, valSet istanbul.ValidatorSet, chainConfig *params.ChainConfig) *Snapshot { - epoch, policy, committeeSize := effectiveParams(g, number+1) - - snap := &Snapshot{ - Epoch: epoch, - Number: number, - Hash: hash, - ValSet: valSet, - Policy: policy, - CommitteeSize: committeeSize, - } - return snap -} - -// loadSnapshot loads an existing snapshot from the database. -func loadSnapshot(db database.DBManager, hash common.Hash) (*Snapshot, error) { - blob, err := db.ReadIstanbulSnapshot(hash) - if err != nil { - return nil, err - } - snap := new(Snapshot) - if err := json.Unmarshal(blob, snap); err != nil { - return nil, err - } - return snap, nil -} - -// store inserts the snapshot into the database. -func (s *Snapshot) store(db database.DBManager) error { - blob, err := json.Marshal(s) - if err != nil { - return err - } - - db.WriteIstanbulSnapshot(s.Hash, blob) - return nil -} - -// copy creates a deep copy of the snapshot, though not the individual votes. -func (s *Snapshot) copy() *Snapshot { - cpy := &Snapshot{ - Epoch: s.Epoch, - Number: s.Number, - Hash: s.Hash, - ValSet: s.ValSet.Copy(), - Policy: s.Policy, - CommitteeSize: s.CommitteeSize, - } - - return cpy -} - -// checkVote return whether it's a valid vote -func (s *Snapshot) checkVote(address common.Address, authorize bool) bool { - _, validator := s.ValSet.GetByAddress(address) - return (validator != nil && !authorize) || (validator == nil && authorize) -} - -// apply creates a new authorization snapshot by applying the given headers to -// the original one. -func (s *Snapshot) apply(headers []*types.Header, govModule gov.GovModule, addr common.Address, policy uint64, chain consensus.ChainReader, stakingModule staking.StakingModule, writable bool) (*Snapshot, error) { - // Allow passing in no headers for cleaner code - if len(headers) == 0 { - return s, nil - } - // Sanity check that the headers can be applied - for i := 0; i < len(headers)-1; i++ { - if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { - return nil, errInvalidVotingChain - } - } - if headers[0].Number.Uint64() != s.Number+1 { - return nil, errInvalidVotingChain - } - - // Iterate through the headers and create a new snapshot - snap := s.copy() - - // Copy values which might be changed by governance vote - snap.Epoch, snap.Policy, snap.CommitteeSize = effectiveParams(govModule, snap.Number+1) - - for _, header := range headers { - // Remove any votes on checkpoint blocks - number := header.Number.Uint64() - - // Resolve the authorization key and check against validators - validator, err := ecrecover(header) - if err != nil { - return nil, err - } - if _, v := snap.ValSet.GetByAddress(validator); v == nil { - return nil, errUnauthorized - } - - // Reload governance values - snap.Epoch, snap.Policy, snap.CommitteeSize = effectiveParams(govModule, number+1) - - if policy == uint64(params.WeightedRandom) { - // Snapshot of block N (Snapshot_N) should contain proposers for N+1 and following blocks. - // Validators for Block N+1 can be calculated based on the staking information from the previous stakingUpdateInterval block. - // If the governance mode is single, the governing node is added to validator all the time. - // - // Proposers for Block N+1 can be calculated from the nearest previous proposersUpdateInterval block. - // Refresh proposers in Snapshot_N using previous proposersUpdateInterval block for N+1, if not updated yet. - - // because snapshot(num)'s ValSet = validators for num+1 - pset := govModule.GetParamSet(number + 1) - - isSingle := (pset.GovernanceMode == "single") - govNode := pset.GoverningNode - minStaking := pset.MinimumStake.Uint64() - - if err := snap.ValSet.RefreshValSet(number+1, chain.Config(), isSingle, govNode, minStaking, stakingModule); err != nil { - logger.Trace("Skip refreshing validators while creating snapshot", "snap.Number", snap.Number, "err", err) - } - - // Do not refresh proposers from the kaia fork block. - // The proposer is calculated every block in `CalcProposer` function after the randao fork. - if !chain.Config().IsKaiaForkEnabled(big.NewInt(int64(number + 1))) { - pHeader := chain.GetHeaderByNumber(params.CalcProposerBlockNumber(number + 1)) - if pHeader != nil { - if err := snap.ValSet.RefreshProposers(pHeader.Hash(), pHeader.Number.Uint64(), chain.Config()); err != nil { - // There are three error cases and they just don't refresh proposers - // (1) no validator at all - // (2) invalid formatted hash - // (3) no staking info available - logger.Trace("Skip refreshing proposers while creating snapshot", "snap.Number", snap.Number, "pHeader.Number", pHeader.Number.Uint64(), "err", err) - } - } else { - logger.Trace("Can't refreshing proposers while creating snapshot due to lack of required header", "snap.Number", snap.Number) - } - } - } - } - snap.Number += uint64(len(headers)) - snap.Hash = headers[len(headers)-1].Hash() - - if snap.ValSet.Policy() == istanbul.WeightedRandom { - snap.ValSet.SetBlockNum(snap.Number) - - bigNum := new(big.Int).SetUint64(snap.Number) - if chain.Config().IsRandaoForkBlockParent(bigNum) { - // The ForkBlock must select proposers using MixHash but (ForkBlock - 1) has no MixHash. Using ZeroMixHash instead. - snap.ValSet.SetMixHash(params.ZeroMixHash) - } else if chain.Config().IsRandaoForkEnabled(bigNum) { - // Feed parent MixHash - snap.ValSet.SetMixHash(headers[len(headers)-1].MixHash) - } - } - snap.ValSet.SetSubGroupSize(snap.CommitteeSize) - - return snap, nil -} - -func (s *Snapshot) getMyVotingPower(addr common.Address) uint64 { - for _, a := range s.ValSet.List() { - if a.Address() == addr { - return a.VotingPower() - } - } - return 0 -} - -// validators retrieves the list of authorized validators in ascending order. -func (s *Snapshot) validators() []common.Address { - validators := make([]common.Address, 0, s.ValSet.Size()) - for _, validator := range s.ValSet.List() { - validators = append(validators, validator.Address()) - } - return sortValidatorArray(validators) -} - -// demotedValidators retrieves the list of authorized, but demoted validators in ascending order. -func (s *Snapshot) demotedValidators() []common.Address { - demotedValidators := make([]common.Address, 0, len(s.ValSet.DemotedList())) - for _, demotedValidator := range s.ValSet.DemotedList() { - demotedValidators = append(demotedValidators, demotedValidator.Address()) - } - return sortValidatorArray(demotedValidators) -} - -func (s *Snapshot) committee(prevHash common.Hash, view *istanbul.View) []common.Address { - committeeList := s.ValSet.SubList(prevHash, view) - - committee := make([]common.Address, 0, len(committeeList)) - for _, v := range committeeList { - committee = append(committee, v.Address()) - } - return committee -} - -func sortValidatorArray(validators []common.Address) []common.Address { - for i := 0; i < len(validators); i++ { - for j := i + 1; j < len(validators); j++ { - if bytes.Compare(validators[i][:], validators[j][:]) > 0 { - validators[i], validators[j] = validators[j], validators[i] - } - } - } - return validators -} - -type snapshotJSON struct { - Epoch uint64 `json:"epoch"` - Number uint64 `json:"number"` - Hash common.Hash `json:"hash"` - - // for validator set - Validators []common.Address `json:"validators"` - Policy istanbul.ProposerPolicy `json:"policy"` - SubGroupSize uint64 `json:"subgroupsize"` - - // for weighted validator - RewardAddrs []common.Address `json:"rewardAddrs"` - VotingPowers []uint64 `json:"votingPower"` - Weights []uint64 `json:"weight"` - Proposers []common.Address `json:"proposers"` - ProposersBlockNum uint64 `json:"proposersBlockNum"` - DemotedValidators []common.Address `json:"demotedValidators"` - MixHash []byte `json:"mixHash,omitempty"` -} - -func (s *Snapshot) toJSONStruct() *snapshotJSON { - var rewardAddrs []common.Address - var votingPowers []uint64 - var weights []uint64 - var proposers []common.Address - var proposersBlockNum uint64 - var validators []common.Address - var demotedValidators []common.Address - var mixHash []byte - - if s.ValSet.Policy() == istanbul.WeightedRandom { - validators, demotedValidators, rewardAddrs, votingPowers, weights, proposers, proposersBlockNum, mixHash = validator.GetWeightedCouncilData(s.ValSet) - } else { - validators = s.validators() - } - - return &snapshotJSON{ - Epoch: s.Epoch, - Number: s.Number, - Hash: s.Hash, - Validators: validators, - Policy: istanbul.ProposerPolicy(s.Policy), - SubGroupSize: s.CommitteeSize, - RewardAddrs: rewardAddrs, - VotingPowers: votingPowers, - Weights: weights, - Proposers: proposers, - ProposersBlockNum: proposersBlockNum, - DemotedValidators: demotedValidators, - MixHash: mixHash, - } -} - -// Unmarshal from a json byte array -func (s *Snapshot) UnmarshalJSON(b []byte) error { - var j snapshotJSON - if err := json.Unmarshal(b, &j); err != nil { - return err - } - - s.Epoch = j.Epoch - s.Number = j.Number - s.Hash = j.Hash - - if j.Policy == istanbul.WeightedRandom { - s.ValSet = validator.NewWeightedCouncil(j.Validators, j.DemotedValidators, j.RewardAddrs, j.VotingPowers, j.Weights, j.Policy, j.SubGroupSize, j.Number, j.ProposersBlockNum, nil) - validator.RecoverWeightedCouncilProposer(s.ValSet, j.Proposers) - s.ValSet.SetMixHash(j.MixHash) - } else { - s.ValSet = validator.NewSubSet(j.Validators, j.Policy, j.SubGroupSize) - } - return nil -} - -// Marshal to a json byte array -func (s *Snapshot) MarshalJSON() ([]byte, error) { - j := s.toJSONStruct() - return json.Marshal(j) -} - -// prepareSnapshotApply is a helper function to prepare snapshot and headers for the given block number and hash. -// It returns the snapshot, headers, and error if any. -func (sb *backend) prepareSnapshotApply(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, []*types.Header, error) { - // Search for a snapshot in memory or on disk for checkpoints - var ( - headers []*types.Header - snap *Snapshot - ) - - for snap == nil { - // If an in-memory snapshot was found, use that - if s, ok := sb.recents.Get(hash); ok { - snap = s.(*Snapshot) - break - } - // If an on-disk checkpoint snapshot can be found, use that - if params.IsCheckpointInterval(number) { - if s, err := loadSnapshot(sb.db, hash); err == nil { - logger.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash) - snap = s - break - } - } - // If we're at block zero, make a snapshot - if number == 0 { - var err error - if snap, err = sb.initSnapshot(chain); err != nil { - return nil, nil, err - } - break - } - // No snapshot for this header, gather the header and move backward - if header := getPrevHeaderAndUpdateParents(chain, number, hash, &parents); header == nil { - return nil, nil, consensus.ErrUnknownAncestor - } else { - headers = append(headers, header) - number, hash = number-1, header.ParentHash - } - } - // Previous snapshot found, apply any pending headers on top of it - for i := 0; i < len(headers)/2; i++ { - headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] - } - - return snap, headers, nil -} - -// GetKaiaHeadersForSnapshotApply returns the headers need to be applied to create snapshot for the given block number. -// Note that it only returns headers for kaia fork enabled blocks. -func (sb *backend) GetKaiaHeadersForSnapshotApply(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) ([]*types.Header, error) { - _, headers, err := sb.prepareSnapshotApply(chain, number, hash, parents) - if err != nil { - return nil, err - } - - kaiaHeaders := []*types.Header{} - for i := 0; i < len(headers); i++ { - if chain.Config().IsKaiaForkEnabled(new(big.Int).Add(headers[i].Number, big.NewInt(1))) { - kaiaHeaders = headers[i:] - break - } - } - - return kaiaHeaders, nil -} - -// snapshot retrieves the state of the authorization voting at a given point in time. -// There's in-memory snapshot and on-disk snapshot. On-disk snapshot is stored every checkpointInterval blocks. -// Moreover, if the block has no in-memory or on-disk snapshot, before generating snapshot, it gathers the header and apply the vote in it. -func (sb *backend) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header, writable bool) (*Snapshot, error) { - snap, headers, err := sb.prepareSnapshotApply(chain, number, hash, parents) - if err != nil { - return nil, err - } - - pset := sb.govModule.GetParamSet(snap.Number) - snap, err = snap.apply(headers, sb.govModule, sb.address, pset.ProposerPolicy, chain, sb.stakingModule, writable) - if err != nil { - return nil, err - } - - // If we've generated a new checkpoint snapshot, save to disk - if writable && params.IsCheckpointInterval(snap.Number) && len(headers) > 0 { - if err = snap.store(sb.db); err != nil { - return nil, err - } - 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 -// 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: - | ^ ^ ^ ^ ...| - 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) { - // Prevent nested call. Ignore header length one - // because it was handled before the `regen` called. - if !sb.isRestoringSnapshots.CompareAndSwap(false, true) || len(headers) <= 1 { - return - } - defer func() { - sb.isRestoringSnapshots.Store(false) - }() - - var ( - from = headers[0].Number.Uint64() - to = headers[len(headers)-1].Number.Uint64() - start = time.Now() - commitTried = false - ) - - // 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)) - } -} diff --git a/consensus/istanbul/backend/validator.go b/consensus/istanbul/backend/validator.go deleted file mode 100644 index d09543f3e..000000000 --- a/consensus/istanbul/backend/validator.go +++ /dev/null @@ -1,72 +0,0 @@ -package backend - -import ( - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus/istanbul" -) - -func (sb *backend) GetValidatorSet(num uint64) (*istanbul.BlockValSet, error) { - council, err := sb.valsetModule.GetCouncil(num) - if err != nil { - return nil, err - } - - demoted, err := sb.valsetModule.GetDemotedValidators(num) - if err != nil { - return nil, err - } - - return istanbul.NewBlockValSet(council, demoted), nil -} - -func (sb *backend) GetCommitteeState(num uint64) (*istanbul.RoundCommitteeState, error) { - header := sb.chain.GetHeaderByNumber(num) - if header == nil { - return nil, errUnknownBlock - } - - return sb.GetCommitteeStateByRound(num, uint64(header.Round())) -} - -func (sb *backend) GetCommitteeStateByRound(num uint64, round uint64) (*istanbul.RoundCommitteeState, error) { - blockValSet, err := sb.GetValidatorSet(num) - if err != nil { - return nil, err - } - - committee, err := sb.valsetModule.GetCommittee(num, round) - if err != nil { - return nil, err - } - - proposer, err := sb.valsetModule.GetProposer(num, round) - if err != nil { - return nil, err - } - - committeeSize := sb.govModule.GetParamSet(num).CommitteeSize - return istanbul.NewRoundCommitteeState(blockValSet, committeeSize, committee, proposer), nil -} - -// GetProposer implements istanbul.Backend.GetProposer -func (sb *backend) GetProposer(number uint64) common.Address { - if h := sb.chain.GetHeaderByNumber(number); h != nil { - a, _ := sb.Author(h) - return a - } - return common.Address{} -} - -func (sb *backend) GetRewardAddress(num uint64, nodeId common.Address) common.Address { - sInfo, err := sb.stakingModule.GetStakingInfo(num) - if err != nil { - return common.Address{} - } - - for idx, id := range sInfo.NodeIds { - if id == nodeId { - return sInfo.RewardAddrs[idx] - } - } - return common.Address{} -} diff --git a/consensus/istanbul/utils.go b/consensus/istanbul/utils.go index c6371c01d..a71043c57 100644 --- a/consensus/istanbul/utils.go +++ b/consensus/istanbul/utils.go @@ -23,8 +23,6 @@ package istanbul import ( - "math" - "github.com/kaiachain/kaia/common" "github.com/kaiachain/kaia/crypto" "github.com/kaiachain/kaia/crypto/sha3" @@ -52,29 +50,3 @@ func GetSignatureAddress(data []byte, sig []byte) (common.Address, error) { } return crypto.PubkeyToAddress(*pubkey), nil } - -// requiredMessageCount returns a minimum required number of consensus messages to proceed -func requiredMessageCount(qualifiedSize int, committeeSize uint64) int { - var size int - if qualifiedSize > int(committeeSize) { - size = int(committeeSize) - } else { - size = qualifiedSize - } - // For less than 4 validators, quorum size equals validator count. - if size < 4 { - return size - } - // Adopted QBFT quorum implementation - // https://github.com/Consensys/quorum/blob/master/consensus/istanbul/qbft/core/core.go#L312 - return int(math.Ceil(float64(2*size) / 3)) -} - -// f returns a maximum endurable number of byzantine fault nodes -func f(qualifiedSize int, committeeSize uint64) int { - if qualifiedSize > int(committeeSize) { - return int(math.Ceil(float64(committeeSize)/3)) - 1 - } else { - return int(math.Ceil(float64(qualifiedSize)/3)) - 1 - } -} diff --git a/consensus/istanbul/validator.go b/consensus/istanbul/validator.go index 096f442ac..98ca48370 100644 --- a/consensus/istanbul/validator.go +++ b/consensus/istanbul/validator.go @@ -23,114 +23,12 @@ package istanbul import ( - "strings" + "math" "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/kaiax/staking" "github.com/kaiachain/kaia/kaiax/valset" - "github.com/kaiachain/kaia/params" ) -type Validator interface { - // Address returns address - Address() common.Address - - // String representation of Validator - String() string - - RewardAddress() common.Address - VotingPower() uint64 - Weight() uint64 -} - -// ---------------------------------------------------------------------------- - -type Validators []Validator - -func (slice Validators) Len() int { - return len(slice) -} - -func (slice Validators) Less(i, j int) bool { - return strings.Compare(slice[i].String(), slice[j].String()) < 0 -} - -func (slice Validators) Swap(i, j int) { - slice[i], slice[j] = slice[j], slice[i] -} - -func (slice Validators) AddressStringList() []string { - var stringAddrs []string - for _, val := range slice { - stringAddrs = append(stringAddrs, val.Address().String()) - } - return stringAddrs -} - -// ---------------------------------------------------------------------------- - -type ValidatorSet interface { - // Calculate the proposer - CalcProposer(lastProposer common.Address, round uint64) - // Return the validator size - Size() uint64 - // Return the sub validator group size - SubGroupSize() uint64 - // Set the sub validator group size - SetSubGroupSize(size uint64) - // Return the validator array - List() []Validator - // Return the demoted validator array - DemotedList() []Validator - // SubList composes a committee after setting a proposer with a default value. - SubList(prevHash common.Hash, view *View) []Validator - // Return whether the given address is one of sub-list - CheckInSubList(prevHash common.Hash, view *View, addr common.Address) bool - // SubListWithProposer composes a committee with given parameters. - SubListWithProposer(prevHash common.Hash, proposer common.Address, view *View) []Validator - // Get validator by index - GetByIndex(i uint64) Validator - // Get validator by given address - GetByAddress(addr common.Address) (int, Validator) - // Get demoted validator by given address - GetDemotedByAddress(addr common.Address) (int, Validator) - // Get current proposer - GetProposer() Validator - // Check whether the validator with given address is a proposer - IsProposer(address common.Address) bool - // Add validator - AddValidator(address common.Address) bool - // Remove validator - RemoveValidator(address common.Address) bool - // Copy validator set - Copy() ValidatorSet - // Get the maximum number of faulty nodes - F() int - // Get proposer policy - Policy() ProposerPolicy - - IsSubSet() bool - - // Refreshes a list of validators at given blockNum - RefreshValSet(blockNum uint64, config *params.ChainConfig, isSingle bool, governingNode common.Address, minStaking uint64, stakingModule staking.StakingModule) error - - // Refreshes a list of candidate proposers with given hash and blockNum - RefreshProposers(hash common.Hash, blockNum uint64, config *params.ChainConfig) error - - SetBlockNum(blockNum uint64) - SetMixHash(mixHash []byte) - - Proposers() []Validator // TODO-Klaytn-Issue1166 For debugging - - TotalVotingPower() uint64 - - Selector(valSet ValidatorSet, lastProposer common.Address, round uint64) Validator -} - -// ---------------------------------------------------------------------------- - -type ProposalSelector func(ValidatorSet, common.Address, uint64) Validator - type BlockValSet struct { council *valset.AddressSet // council = demoted + qualified qualified *valset.AddressSet @@ -191,3 +89,29 @@ func (cs *RoundCommitteeState) IsProposer(addr common.Address) bool { return cs. func (cs *RoundCommitteeState) CommitteeSize() uint64 { return cs.committeeSize } func (cs *RoundCommitteeState) RequiredMessageCount() int { return cs.requiredMessageCount } func (cs *RoundCommitteeState) F() int { return cs.f } + +// requiredMessageCount returns a minimum required number of consensus messages to proceed +func requiredMessageCount(qualifiedSize int, committeeSize uint64) int { + var size int + if qualifiedSize > int(committeeSize) { + size = int(committeeSize) + } else { + size = qualifiedSize + } + // For less than 4 validators, quorum size equals validator count. + if size < 4 { + return size + } + // Adopted QBFT quorum implementation + // https://github.com/Consensys/quorum/blob/master/consensus/istanbul/qbft/core/core.go#L312 + return int(math.Ceil(float64(2*size) / 3)) +} + +// f returns a maximum endurable number of byzantine fault nodes +func f(qualifiedSize int, committeeSize uint64) int { + if qualifiedSize > int(committeeSize) { + return int(math.Ceil(float64(committeeSize)/3)) - 1 + } else { + return int(math.Ceil(float64(qualifiedSize)/3)) - 1 + } +} diff --git a/consensus/istanbul/validator/default.go b/consensus/istanbul/validator/default.go deleted file mode 100644 index ea85a771c..000000000 --- a/consensus/istanbul/validator/default.go +++ /dev/null @@ -1,404 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Modifications Copyright 2018 The klaytn Authors -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . -// -// This file is derived from quorum/consensus/istanbul/validator/default.go (2018/06/04). -// Modified and improved for the klaytn development. -// Modified and improved for the Kaia development. - -package validator - -import ( - "math" - "reflect" - "sort" - "sync" - "sync/atomic" - - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/fork" - "github.com/kaiachain/kaia/kaiax/staking" - "github.com/kaiachain/kaia/params" -) - -const ( - defaultSubSetLength = 21 -) - -type defaultValidator struct { - address common.Address -} - -func (val *defaultValidator) Address() common.Address { - return val.address -} - -func (val *defaultValidator) String() string { - return val.Address().String() -} - -func (val *defaultValidator) Equal(val2 *defaultValidator) bool { - return val.address == val2.address -} - -func (val *defaultValidator) Hash() int64 { - return val.address.Hash().Big().Int64() -} - -func (val *defaultValidator) RewardAddress() common.Address { return common.Address{} } -func (val *defaultValidator) VotingPower() uint64 { return 1000 } -func (val *defaultValidator) Weight() uint64 { return 0 } - -type defaultSet struct { - subSize uint64 - - validators istanbul.Validators - policy istanbul.ProposerPolicy - - proposer atomic.Value - validatorMu sync.RWMutex - selector istanbul.ProposalSelector -} - -func newDefaultSet(addrs []common.Address, policy istanbul.ProposerPolicy) *defaultSet { - valSet := &defaultSet{} - - valSet.subSize = defaultSubSetLength - valSet.policy = policy - // init validators - valSet.validators = make([]istanbul.Validator, len(addrs)) - for i, addr := range addrs { - valSet.validators[i] = New(addr) - } - // sort validator - sort.Sort(valSet.validators) - // init proposer - if valSet.Size() > 0 { - valSet.proposer.Store(valSet.GetByIndex(0)) - } - valSet.selector = roundRobinProposer - if policy == istanbul.Sticky { - valSet.selector = stickyProposer - } - - return valSet -} - -func newDefaultSubSet(addrs []common.Address, policy istanbul.ProposerPolicy, subSize uint64) *defaultSet { - valSet := &defaultSet{} - - valSet.subSize = subSize - valSet.policy = policy - // init validators - valSet.validators = make([]istanbul.Validator, len(addrs)) - for i, addr := range addrs { - valSet.validators[i] = New(addr) - } - // sort validator - sort.Sort(valSet.validators) - // init proposer - if valSet.Size() > 0 { - valSet.proposer.Store(valSet.GetByIndex(0)) - } - valSet.selector = roundRobinProposer - if policy == istanbul.Sticky { - valSet.selector = stickyProposer - } - - return valSet -} - -func (valSet *defaultSet) Size() uint64 { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - return uint64(len(valSet.validators)) -} - -func (valSet *defaultSet) SubGroupSize() uint64 { - return valSet.subSize -} - -// SetSubGroupSize sets committee size of the valSet. -func (valSet *defaultSet) SetSubGroupSize(size uint64) { - if size == 0 { - logger.Error("cannot assign committee size to 0") - return - } - valSet.subSize = size -} - -func (valSet *defaultSet) List() []istanbul.Validator { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - return valSet.validators -} - -func (valSet *defaultSet) DemotedList() []istanbul.Validator { - return nil -} - -// SubList composes a committee after setting a proposer with a default value. -// This functions returns whole validators if it failed to compose a committee. -func (valSet *defaultSet) SubList(prevHash common.Hash, view *istanbul.View) []istanbul.Validator { - // TODO-Kaia-Istanbul: investigate whether `valSet.GetProposer().Address()` is a proper value or the proposer should be calculated based on `view` - proposer := valSet.GetProposer() - if proposer == nil { - return valSet.List() - } - return valSet.SubListWithProposer(prevHash, proposer.Address(), view) -} - -// SubListWithProposer composes a committee with given parameters. -// The first member of the committee is set to the given proposer without calculating proposer with the given `view`. -// The second member of the committee is calculated with a round number of the given view and `valSet.blockNum`. -// The reset of the committee is selected with a random seed derived from `prevHash`. -// This functions returns whole validators if it failed to compose a committee. -func (valSet *defaultSet) SubListWithProposer(prevHash common.Hash, proposerAddr common.Address, view *istanbul.View) []istanbul.Validator { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - - validators := valSet.validators - validatorSize := uint64(len(validators)) - committeeSize := valSet.subSize - - // return early if the committee size is equal or larger than the validator size - if committeeSize >= validatorSize { - return validators - } - - // find the proposer - proposerIdx, proposer := valSet.GetByAddress(proposerAddr) - if proposerIdx < 0 { - logger.Error("invalid index of the proposer", - "addr", proposerAddr.String(), "index", proposerIdx) - return validators - } - - // return early if the committee size is 1 - if committeeSize == 1 { - return []istanbul.Validator{proposer} - } - - // find the next proposer - nextProposer := valSet.selector(valSet, proposer.Address(), view.Round.Uint64()) - nextProposerIdx, _ := valSet.GetByAddress(nextProposer.Address()) - if nextProposerIdx < 0 { - logger.Error("invalid index of the next proposer", - "addr", nextProposer.Address().String(), "index", nextProposerIdx) - return validators - } - - // seed will be used to select a random committee - seed, err := ConvertHashToSeed(prevHash) - if fork.Rules(view.Sequence).IsIstanbul { - seed += view.Round.Int64() - } - if err != nil { - logger.Error("failed to convert hash to seed", "prevHash", prevHash, "err", err) - return validators - } - - // select a random committee - committee := SelectRandomCommittee(validators, committeeSize, seed, proposerIdx, nextProposerIdx) - if committee == nil { - committee = validators - } - - logger.Trace("composed committee", "prevHash", prevHash.Hex(), "proposerAddr", proposerAddr, - "committee", committee, "committee size", len(committee), "valSet.subSize", committeeSize) - - return committee -} - -func (valSet *defaultSet) CheckInSubList(prevHash common.Hash, view *istanbul.View, addr common.Address) bool { - for _, val := range valSet.SubList(prevHash, view) { - if val.Address() == addr { - return true - } - } - return false -} - -func (valSet *defaultSet) IsSubSet() bool { - return valSet.Size() > valSet.subSize -} - -func (valSet *defaultSet) GetByIndex(i uint64) istanbul.Validator { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - if i < uint64(len(valSet.validators)) { - return valSet.validators[i] - } - return nil -} - -func (valSet *defaultSet) GetByAddress(addr common.Address) (int, istanbul.Validator) { - for i, val := range valSet.List() { - if addr == val.Address() { - return i, val - } - } - // TODO-Kaia-Istanbul: Enable this log when non-committee nodes don't call `core.startNewRound()` - // logger.Warn("failed to find an address in the validator list", - // "address", addr, "validatorAddrs", valSet.validators.AddressStringList()) - return -1, nil -} - -func (valSet *defaultSet) GetDemotedByAddress(addr common.Address) (int, istanbul.Validator) { - return -1, nil -} - -func (valSet *defaultSet) GetProposer() istanbul.Validator { - proposer := valSet.proposer.Load() - if proposer == nil { - logger.Error("Proposer is nil", "validators", valSet.validators) - return nil - } - return proposer.(istanbul.Validator) -} - -func (valSet *defaultSet) IsProposer(address common.Address) bool { - _, val := valSet.GetByAddress(address) - return reflect.DeepEqual(valSet.GetProposer(), val) -} - -func (valSet *defaultSet) CalcProposer(lastProposer common.Address, round uint64) { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - - if len(valSet.validators) == 0 { - logger.Error("List of validators is empty", "validators", len(valSet.validators)) - return - } - - valSet.proposer.Store(valSet.selector(valSet, lastProposer, round)) -} - -func calcSeed(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) uint64 { - offset := 0 - if idx, val := valSet.GetByAddress(proposer); val != nil { - offset = idx - } - return uint64(offset) + round -} - -func emptyAddress(addr common.Address) bool { - return addr == common.Address{} -} - -func roundRobinProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator { - seed := uint64(0) - if emptyAddress(proposer) { - seed = round - } else { - seed = calcSeed(valSet, proposer, round) + 1 - } - pick := seed % uint64(valSet.Size()) - return valSet.GetByIndex(pick) -} - -func stickyProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator { - seed := uint64(0) - if emptyAddress(proposer) { - seed = round - } else { - seed = calcSeed(valSet, proposer, round) - } - pick := seed % uint64(valSet.Size()) - return valSet.GetByIndex(pick) -} - -func (valSet *defaultSet) AddValidator(address common.Address) bool { - valSet.validatorMu.Lock() - defer valSet.validatorMu.Unlock() - for _, v := range valSet.validators { - if v.Address() == address { - return false - } - } - valSet.validators = append(valSet.validators, New(address)) - // TODO: we may not need to re-sort it again - // sort validator - sort.Sort(valSet.validators) - return true -} - -func (valSet *defaultSet) RemoveValidator(address common.Address) bool { - valSet.validatorMu.Lock() - defer valSet.validatorMu.Unlock() - - for i, v := range valSet.validators { - if v.Address() == address { - valSet.validators = append(valSet.validators[:i], valSet.validators[i+1:]...) - return true - } - } - return false -} - -func (valSet *defaultSet) Copy() istanbul.ValidatorSet { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - - addresses := make([]common.Address, 0, len(valSet.validators)) - for _, v := range valSet.validators { - addresses = append(addresses, v.Address()) - } - - newValSet := NewSubSet(addresses, valSet.policy, valSet.subSize).(*defaultSet) - if proposer := valSet.GetProposer(); proposer != nil { - // Copy the proposer if exist - if idx, p := newValSet.GetByAddress(proposer.Address()); idx != -1 { - newValSet.proposer.Store(p) - } - } - return newValSet -} - -func (valSet *defaultSet) F() int { - if valSet.Size() > valSet.subSize { - return int(math.Ceil(float64(valSet.subSize)/3)) - 1 - } else { - return int(math.Ceil(float64(valSet.Size())/3)) - 1 - } -} - -func (valSet *defaultSet) Policy() istanbul.ProposerPolicy { return valSet.policy } - -func (valSet *defaultSet) RefreshValSet(blockNum uint64, config *params.ChainConfig, isSingle bool, governingNode common.Address, minStaking uint64, stakingModule staking.StakingModule) error { - return nil -} - -func (valSet *defaultSet) RefreshProposers(hash common.Hash, blockNum uint64, config *params.ChainConfig) error { - return nil -} - -func (valSet *defaultSet) SetBlockNum(blockNum uint64) { /* Do nothing */ } -func (valSet *defaultSet) SetMixHash(mixHash []byte) { /* Do nothing */ } -func (valSet *defaultSet) Proposers() []istanbul.Validator { return nil } -func (valSet *defaultSet) TotalVotingPower() uint64 { - sum := uint64(0) - for _, v := range valSet.List() { - sum += v.VotingPower() - } - return sum -} - -func (valSet *defaultSet) Selector(valS istanbul.ValidatorSet, lastProposer common.Address, round uint64) istanbul.Validator { - return valSet.selector(valS, lastProposer, round) -} diff --git a/consensus/istanbul/validator/default_test.go b/consensus/istanbul/validator/default_test.go deleted file mode 100644 index 3b4539a51..000000000 --- a/consensus/istanbul/validator/default_test.go +++ /dev/null @@ -1,291 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Modifications Copyright 2018 The klaytn Authors -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . -// -// This file is derived from quorum/consensus/istanbul/validator/default_test.go (2018/06/04). -// Modified and improved for the klaytn development. -// Modified and improved for the Kaia development. - -package validator - -import ( - "fmt" - "math/big" - "reflect" - "strings" - "testing" - - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/crypto" - "github.com/kaiachain/kaia/fork" - "github.com/kaiachain/kaia/params" - "github.com/stretchr/testify/assert" -) - -var ( - testAddress = "136807B12327a8AfF9831F09617dA1B9D398cda2" - testAddress2 = "4dd324F9821485caE941640B32c3Bcf1fA6E93E6" - testAddress3 = "62E47d858bf8513fc401886B94E33e7DCec2Bfb7" - testAddress4 = "8aD8F547fa00f58A8c4fb3B671Ee5f1A75bA028a" - testAddress5 = "dd197E88fd97aF3877023cf20d69543fc72e6298" -) - -func TestNewValidatorSet(t *testing.T) { - var validators []istanbul.Validator - const ValCnt = 100 - - // Create 100 validators with random addresses - b := []byte{} - for i := 0; i < ValCnt; i++ { - key, _ := crypto.GenerateKey() - addr := crypto.PubkeyToAddress(key.PublicKey) - val := New(addr) - validators = append(validators, val) - b = append(b, val.Address().Bytes()...) - } - - // Create ValidatorSet - valSet := NewSet(ExtractValidators(b), istanbul.RoundRobin) - if valSet == nil { - t.Errorf("the validator byte array cannot be parsed") - t.FailNow() - } - - // Check validators sorting: should be in ascending order - for i := 0; i < ValCnt-1; i++ { - val := valSet.GetByIndex(uint64(i)) - nextVal := valSet.GetByIndex(uint64(i + 1)) - if strings.Compare(val.String(), nextVal.String()) >= 0 { - t.Errorf("validator set is not sorted in descending order") - } - } -} - -func TestNormalValSet(t *testing.T) { - b1 := common.Hex2Bytes(testAddress) - b2 := common.Hex2Bytes(testAddress2) - addr1 := common.BytesToAddress(b1) - addr2 := common.BytesToAddress(b2) - val1 := New(addr1) - val2 := New(addr2) - - valSet := newDefaultSet([]common.Address{addr1, addr2}, istanbul.RoundRobin) - if valSet == nil { - t.Errorf("the format of validator set is invalid") - t.FailNow() - } - - // check size - if size := valSet.Size(); size != 2 { - t.Errorf("the size of validator set is wrong: have %v, want 2", size) - } - // test get by index - if val := valSet.GetByIndex(uint64(0)); !reflect.DeepEqual(val, val1) { - t.Errorf("validator mismatch: have %v, want %v", val, val1) - } - // test get by invalid index - if val := valSet.GetByIndex(uint64(2)); val != nil { - t.Errorf("validator mismatch: have %v, want nil", val) - } - // test get by address - if _, val := valSet.GetByAddress(addr2); !reflect.DeepEqual(val, val2) { - t.Errorf("validator mismatch: have %v, want %v", val, val2) - } - // test get by invalid address - invalidAddr := common.HexToAddress("0x9535b2e7faaba5288511d89341d94a38063a349b") - if _, val := valSet.GetByAddress(invalidAddr); val != nil { - t.Errorf("validator mismatch: have %v, want nil", val) - } - // test get proposer - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) { - t.Errorf("proposer mismatch: have %v, want %v", val, val1) - } - // test calculate proposer - lastProposer := addr1 - valSet.CalcProposer(lastProposer, uint64(0)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) { - t.Errorf("proposer mismatch: have %v, want %v", val, val2) - } - valSet.CalcProposer(lastProposer, uint64(3)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) { - t.Errorf("proposer mismatch: have %v, want %v", val, val1) - } - // test empty last proposer - lastProposer = common.Address{} - valSet.CalcProposer(lastProposer, uint64(3)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) { - t.Errorf("proposer mismatch: have %v, want %v", val, val2) - } -} - -func TestEmptyValSet(t *testing.T) { - valSet := NewSet(ExtractValidators([]byte{}), istanbul.RoundRobin) - if valSet == nil { - t.Errorf("validator set should not be nil") - } -} - -func TestAddAndRemoveValidator(t *testing.T) { - valSet := NewSet(ExtractValidators([]byte{}), istanbul.RoundRobin) - if !valSet.AddValidator(common.StringToAddress(string(rune(2)))) { - t.Error("the validator should be added") - } - if valSet.AddValidator(common.StringToAddress(string(rune(2)))) { - t.Error("the existing validator should not be added") - } - valSet.AddValidator(common.StringToAddress(string(rune(1)))) - valSet.AddValidator(common.StringToAddress(string(rune(0)))) - if len(valSet.List()) != 3 { - t.Error("the size of validator set should be 3") - } - - for i, v := range valSet.List() { - expected := common.StringToAddress(string(rune(i))) - if v.Address() != expected { - t.Errorf("the order of validators is wrong: have %v, want %v", v.Address().Hex(), expected.Hex()) - } - } - - if !valSet.RemoveValidator(common.StringToAddress(string(rune(2)))) { - t.Error("the validator should be removed") - } - if valSet.RemoveValidator(common.StringToAddress(string(rune(2)))) { - t.Error("the non-existing validator should not be removed") - } - if len(valSet.List()) != 2 { - t.Error("the size of validator set should be 2") - } - valSet.RemoveValidator(common.StringToAddress(string(rune(1)))) - if len(valSet.List()) != 1 { - t.Error("the size of validator set should be 1") - } - valSet.RemoveValidator(common.StringToAddress(string(rune(0)))) - if len(valSet.List()) != 0 { - t.Error("the size of validator set should be 0") - } -} - -func TestStickyProposer(t *testing.T) { - b1 := common.Hex2Bytes(testAddress) - b2 := common.Hex2Bytes(testAddress2) - addr1 := common.BytesToAddress(b1) - addr2 := common.BytesToAddress(b2) - val1 := New(addr1) - val2 := New(addr2) - - valSet := newDefaultSet([]common.Address{addr1, addr2}, istanbul.Sticky) - - // test get proposer - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) { - t.Errorf("proposer mismatch: have %v, want %v", val, val1) - } - // test calculate proposer - lastProposer := addr1 - valSet.CalcProposer(lastProposer, uint64(0)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) { - t.Errorf("proposer mismatch: have %v, want %v", val, val1) - } - - valSet.CalcProposer(lastProposer, uint64(1)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) { - t.Errorf("proposer mismatch: have %v, want %v", val, val2) - } - // test empty last proposer - lastProposer = common.Address{} - valSet.CalcProposer(lastProposer, uint64(3)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) { - t.Errorf("proposer mismatch: have %v, want %v", val, val2) - } -} - -func TestDefaultSet_SubList(t *testing.T) { - fork.SetHardForkBlockNumberConfig(¶ms.ChainConfig{}) - defer fork.ClearHardForkBlockNumberConfig() - - b1 := common.Hex2Bytes(testAddress) - b2 := common.Hex2Bytes(testAddress2) - b3 := common.Hex2Bytes(testAddress3) - b4 := common.Hex2Bytes(testAddress4) - b5 := common.Hex2Bytes(testAddress5) - addr1 := common.BytesToAddress(b1) - addr2 := common.BytesToAddress(b2) - addr3 := common.BytesToAddress(b3) - addr4 := common.BytesToAddress(b4) - addr5 := common.BytesToAddress(b5) - testAddresses := []common.Address{addr1, addr2, addr3, addr4, addr5} - - valSet := NewSet(testAddresses, istanbul.RoundRobin) - if valSet == nil { - t.Errorf("the format of validator set is invalid") - t.FailNow() - } - valSet.SetSubGroupSize(3) - - hash := istanbul.RLPHash("This is a test hash") - view := &istanbul.View{ - Sequence: new(big.Int).SetInt64(1), - Round: new(big.Int).SetInt64(0), - } - - lenAddress := len(testAddresses) - for i := 0; i < lenAddress*2; i++ { - currentProposer := valSet.GetProposer() - assert.Equal(t, testAddresses[i%lenAddress], currentProposer.Address()) - - committee := valSet.SubList(hash, view) - - assert.Equal(t, testAddresses[i%lenAddress].String(), committee[0].String()) - assert.Equal(t, testAddresses[(i+1)%lenAddress].String(), committee[1].String()) - - valSet.CalcProposer(currentProposer.Address(), view.Round.Uint64()) - } -} - -func TestDefaultSet_Copy(t *testing.T) { - b1 := common.Hex2Bytes(testAddress) - b2 := common.Hex2Bytes(testAddress2) - b3 := common.Hex2Bytes(testAddress3) - b4 := common.Hex2Bytes(testAddress4) - b5 := common.Hex2Bytes(testAddress5) - addr1 := common.BytesToAddress(b1) - addr2 := common.BytesToAddress(b2) - addr3 := common.BytesToAddress(b3) - addr4 := common.BytesToAddress(b4) - addr5 := common.BytesToAddress(b5) - testAddresses := []common.Address{addr1, addr2, addr3, addr4, addr5} - - valSet := NewSet(testAddresses, istanbul.RoundRobin) - copiedValSet := valSet.Copy() - - assert.NotEqual(t, fmt.Sprintf("%p", &valSet), fmt.Sprintf("%p", &copiedValSet)) - - assert.Equal(t, valSet.List(), copiedValSet.List()) - assert.NotEqual(t, fmt.Sprintf("%p", valSet.List()), fmt.Sprintf("%p", copiedValSet.List())) - - for i := uint64(0); i < valSet.Size(); i++ { - assert.Equal(t, valSet.List()[i], copiedValSet.List()[i]) - assert.NotEqual(t, fmt.Sprintf("%p", valSet.List()[i]), fmt.Sprintf("%p", copiedValSet.List())[i]) - } - - assert.Equal(t, valSet.GetProposer(), copiedValSet.GetProposer()) - assert.NotEqual(t, fmt.Sprintf("%p", valSet.GetProposer()), fmt.Sprintf("%p", copiedValSet.GetProposer())) - - assert.Equal(t, valSet.Policy(), copiedValSet.Policy()) - assert.Equal(t, valSet.SubGroupSize(), copiedValSet.SubGroupSize()) - assert.Equal(t, valSet.TotalVotingPower(), copiedValSet.TotalVotingPower()) -} diff --git a/consensus/istanbul/validator/doc.go b/consensus/istanbul/validator/doc.go deleted file mode 100644 index 071c1392c..000000000 --- a/consensus/istanbul/validator/doc.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 The klaytn Authors -// This file is part of the klaytn library. -// -// The klaytn library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The klaytn library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the klaytn library. If not, see . - -/* -Package validator implements the types related to the validators participating in consensus. - -`Validator` and `ValidatorSet` interfaces are used to validate blocks and make a consensus. -Implementations of these interfaces are different, depending on proposer-choosing policy. - -# Validator - -`Validator` is a node which has 2 features to make a consensus: proposing and validating. - -Propose: A node can propose a block if it is a proposer. Only validators can be a proposer. - -Validate: A validator node can validate blocks from proposers. A block is valid only if more than 2/3 of validators approve the given block. - -# ValidatorSet - -`ValidatorSet` is a group of validators. It is also called as a council. -ValidatorSet calculates the block proposer of an upcoming block. -A validator selected as a block proposer will have a chance to make a block. - -# Implementation in Kaia - -Kaia implements `Validator` and `ValidatorSet` interface for Kaia consensus. -Kaia reflects the ratio of staking amounts to the probability of selecting a proposer. -This is called weightedRandom policy. -Detailed information can be found in https://docs.kaia.io/docs/learn/token-economy/#kaia-governance-council-reward-mechanism-. -Implementation structures are weightedValidator and weightedCouncil in weighted.go file. - -# Files - -- default.go : Validator and ValidatorSet for roundRobin policy is implemented. - -- weighted.go : Validator and ValidatorSet for weightedRandom policy is implemented. - -- validator.go : common functions for Validator and ValidatorSet are implemented. -*/ -package validator diff --git a/consensus/istanbul/validator/multi_staking_test.go b/consensus/istanbul/validator/multi_staking_test.go deleted file mode 100644 index b04f37f08..000000000 --- a/consensus/istanbul/validator/multi_staking_test.go +++ /dev/null @@ -1,350 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Copyright 2019 The klaytn Authors -// This file is part of the klaytn library. -// -// The klaytn library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The klaytn library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the klaytn library. If not, see . -// Modified and improved for the Kaia development. - -/* -Multiple Staking Contracts - -Validators can deploy multiple staking contracts. -If a validator wants to deploy additional staking contracts, those staking contracts should have same rewardAddress. -StakingAmounts of staking contracts with a same rewardAddress will be added and it is reflected to a probability of becoming a block proposer. - -# Testing - -StakingInfos are data from addressBook. -A StakingInfo has lists of addresses and stakingAmount. -They are matched by an index. Values of the lists with a same index are from a same staking contract. - -All addresses used in tests are made by 3 digits number. -NodeAddress : begin with 1 -rewardAddress : begin with 2 -NodeAddress of additional staking contract : begin with 9 -*/ -package validator - -import ( - "testing" - - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/reward" - "github.com/stretchr/testify/assert" -) - -func newTestWeightedCouncil(nodeAddrs []common.Address) *weightedCouncil { - return NewWeightedCouncil(nodeAddrs, nil, nil, make([]uint64, len(nodeAddrs)), nil, istanbul.WeightedRandom, 0, 0, 0, nil) -} - -// TestWeightedCouncil_getStakingAmountsOfValidators checks if validators and stakingAmounts from a stakingInfo are matched well. -// stakingAmounts of additional staking contracts will be added to stakingAmounts of validators which have the same reward address. -// input -// - validator and stakingInfo is matched by a nodeAddress. -// -// output -// - weightedValidators are sorted by nodeAddress -// - stakingAmounts should be same as expectedStakingAmounts -func TestWeightedCouncil_getStakingAmountsOfValidators(t *testing.T) { - testCases := []struct { - validators []common.Address - stakingInfo *reward.StakingInfo - expectedStakingAmounts []float64 - }{ - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, - CouncilStakingAmounts: []uint64{10000000, 5000000, 5000000}, - }, - []float64{10000000, 5000000, 5000000}, - }, - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, - CouncilStakingAmounts: []uint64{7000000, 5000000, 10000000}, - }, - []float64{7000000, 5000000, 10000000}, - }, - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, - CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, - }, - []float64{10000000, 5000000, 5000000, 5000000}, - }, - { - []common.Address{common.StringToAddress("104"), common.StringToAddress("103"), common.StringToAddress("102"), common.StringToAddress("101")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, - CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000, 5000000}, - }, - []float64{10000000, 10000000, 5000000, 5000000}, - }, - } - for _, testCase := range testCases { - council := newTestWeightedCouncil(testCase.validators) - candidates := append(council.validators, council.demotedValidators...) - weightedValidators, stakingAmounts, err := getStakingAmountsOfValidators(candidates, testCase.stakingInfo) - - assert.NoError(t, err) - assert.Equal(t, len(testCase.validators), len(weightedValidators)) - for _, validator := range weightedValidators { - assert.Contains(t, testCase.validators, validator.address) - } - assert.Equal(t, testCase.expectedStakingAmounts, stakingAmounts) - } -} - -func TestRewardAddressLookup(t *testing.T) { - testCases := []struct { - validators []common.Address - stakingInfo *reward.StakingInfo - expectedStakingAmounts []float64 - }{ - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, - CouncilStakingAmounts: []uint64{10000000, 5000000, 5000000}, - }, - []float64{10000000, 5000000, 5000000}, - }, - } - for _, testCase := range testCases { - council := newTestWeightedCouncil(testCase.validators) - weightedValidators, _, err := getStakingAmountsOfValidators(council.validators, testCase.stakingInfo) - - assert.NoError(t, err) - for _, val := range weightedValidators { - assert.NotEqual(t, val.RewardAddress(), common.Address{}) - } - } -} - -// TestCalcTotalAmount tests calcTotalAmount that calculates totalAmount of stakingAmounts and gini coefficient if UseGini is true. -// if UseGini is true, gini is calculated and reflected to stakingAmounts. -func TestCalcTotalAmount(t *testing.T) { - testCases := []struct { - weightedValidators []*weightedValidator // Produced by getStakingAmountsOfValidators() - stakingInfo *reward.StakingInfo // Produced by GetStakingInfo() - stakingAmounts []float64 // Produced by getStakingAmountsOfValidators() - expectedGini float64 // Gini among the []weightedValidators which is a subset of CouncilNodeAddrs - expectedTotalAmount float64 // Sum of Gini-adjusted amounts - expectedStakingAmounts []float64 // Gini-adjusted amounts - }{ - { // Gini disabled - []*weightedValidator{ - {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, - }, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - UseGini: false, - Gini: reward.DefaultGiniCoefficient, // To see if calcTotalAmount modifies Gini field, set it to -1. - }, - []float64{5000000, 5000000, 5000000}, - reward.DefaultGiniCoefficient, - 15000000, - []float64{5000000, 5000000, 5000000}, - }, - { // Gini enabled but equal amounts. - []*weightedValidator{ - {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, - }, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - UseGini: true, - Gini: reward.DefaultGiniCoefficient, - }, - []float64{5000000, 5000000, 5000000}, - 0, - 15000000, - []float64{5000000, 5000000, 5000000}, - }, - { // Gini enabled and unequal amounts. - []*weightedValidator{ - {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, {address: common.StringToAddress("104")}, {address: common.StringToAddress("105")}, - }, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, - UseGini: true, - Gini: reward.DefaultGiniCoefficient, - }, - []float64{10000000, 20000000, 30000000, 40000000, 50000000}, - 0.27, - 3779508, - []float64{324946, 560845, 771786, 967997, 1153934}, - }, - } - for _, testCase := range testCases { - stakingAmounts := testCase.stakingAmounts - totalAmount, giniUsed := calcTotalAmount(testCase.weightedValidators, testCase.stakingInfo, stakingAmounts) - - assert.Equal(t, testCase.expectedGini, giniUsed) // calcTotalAmount computes gini among validators on its own - assert.Equal(t, reward.DefaultGiniCoefficient, testCase.stakingInfo.Gini) // stakingInfo.Gini left untouched - assert.Equal(t, testCase.expectedTotalAmount, totalAmount) - assert.Equal(t, testCase.expectedStakingAmounts, stakingAmounts) - } -} - -// TestCalcWeight tests calcWeight that calculates weights and saves them to validators. -// weights are the ratio of each stakingAmount to totalStaking -func TestCalcWeight(t *testing.T) { - testCases := []struct { - weightedValidators []*weightedValidator - stakingAmounts []float64 - totalStaking float64 - expectedWeights []uint64 - }{ - { - []*weightedValidator{ - {}, {}, {}, - }, - []float64{0, 0, 0}, - 0, - []uint64{0, 0, 0}, - }, - { - []*weightedValidator{ - {}, {}, {}, - }, - []float64{5000000, 5000000, 5000000}, - 15000000, - []uint64{33, 33, 33}, - }, - { - []*weightedValidator{ - {}, {}, {}, {}, - }, - []float64{5000000, 10000000, 5000000, 5000000}, - 25000000, - []uint64{20, 40, 20, 20}, - }, - { - []*weightedValidator{ - {}, {}, {}, {}, {}, - }, - []float64{324946, 560845, 771786, 967997, 1153934}, - 3779508, - []uint64{9, 15, 20, 26, 31}, - }, - } - for _, testCase := range testCases { - calcWeight(testCase.weightedValidators, testCase.stakingAmounts, testCase.totalStaking) - for i, weight := range testCase.expectedWeights { - assert.Equal(t, weight, testCase.weightedValidators[i].Weight()) - } - } -} - -// TestWeightedCouncil_validatorWeightWithStakingInfo is union of above tests. -// Weight should be calculated exactly by a validator list and a stakingInfo given -func TestWeightedCouncil_validatorWeightWithStakingInfo(t *testing.T) { - testCases := []struct { - validators []common.Address - stakingInfo *reward.StakingInfo - expectedWeights []uint64 - }{ - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, - UseGini: false, - CouncilStakingAmounts: []uint64{0, 0, 0}, - }, - []uint64{0, 0, 0}, - }, - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204")}, - UseGini: true, - CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000}, - }, - []uint64{25, 25, 25, 25}, - }, - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("205")}, - UseGini: true, - CouncilStakingAmounts: []uint64{10000000, 20000000, 30000000, 40000000, 50000000}, - }, - []uint64{9, 15, 20, 26, 31}, - }, - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, - UseGini: false, - CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, - }, - []uint64{40, 20, 20, 20}, - }, - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, - UseGini: true, - CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, - }, - []uint64{38, 21, 21, 21}, - }, - { - []common.Address{common.StringToAddress("104"), common.StringToAddress("103"), common.StringToAddress("102"), common.StringToAddress("101")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, - UseGini: true, - CouncilStakingAmounts: []uint64{10000000, 5000000, 20000000, 5000000, 5000000, 5000000}, - }, - []uint64{29, 21, 37, 12}, - }, - { - []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, - &reward.StakingInfo{ - CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, - CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, - UseGini: true, - CouncilStakingAmounts: []uint64{10000000, 5000000, 20000000, 5000000, 5000000, 5000000}, - }, - []uint64{29, 21, 37, 12, 1}, - }, - } - for _, testCase := range testCases { - council := newTestWeightedCouncil(testCase.validators) - candidates := append(council.validators, council.demotedValidators...) - weightedValidators, stakingAmounts, err := getStakingAmountsOfValidators(candidates, testCase.stakingInfo) - assert.NoError(t, err) - totalStaking, _ := calcTotalAmount(weightedValidators, testCase.stakingInfo, stakingAmounts) - calcWeight(weightedValidators, stakingAmounts, totalStaking) - - for i, weight := range testCase.expectedWeights { - assert.Equal(t, weight, weightedValidators[i].Weight()) - } - } -} diff --git a/consensus/istanbul/validator/validator.go b/consensus/istanbul/validator/validator.go deleted file mode 100644 index 48fefbd95..000000000 --- a/consensus/istanbul/validator/validator.go +++ /dev/null @@ -1,178 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Modifications Copyright 2018 The klaytn Authors -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . -// -// This file is derived from quorum/consensus/istanbul/validator/validator.go (2018/06/04). -// Modified and improved for the klaytn development. -// Modified and improved for the Kaia development. - -package validator - -import ( - "encoding/binary" - "math/rand" - "strconv" - "strings" - - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/log" -) - -var logger = log.NewModuleLogger(log.ConsensusIstanbulValidator) - -func New(addr common.Address) istanbul.Validator { - return &defaultValidator{ - address: addr, - } -} - -func NewValidatorSet(addrs, demotedAddrs []common.Address, proposerPolicy istanbul.ProposerPolicy, subGroupSize uint64, chain consensus.ChainReader) istanbul.ValidatorSet { - var valSet istanbul.ValidatorSet - if proposerPolicy == istanbul.WeightedRandom { - valSet = NewWeightedCouncil(addrs, demotedAddrs, nil, nil, nil, proposerPolicy, subGroupSize, 0, 0, chain) - } else { - valSet = NewSubSet(addrs, proposerPolicy, subGroupSize) - } - - return valSet -} - -func NewSet(addrs []common.Address, policy istanbul.ProposerPolicy) istanbul.ValidatorSet { - return newDefaultSet(addrs, policy) -} - -func NewSubSet(addrs []common.Address, policy istanbul.ProposerPolicy, subSize uint64) istanbul.ValidatorSet { - return newDefaultSubSet(addrs, policy, subSize) -} - -func ExtractValidators(extraData []byte) []common.Address { - // get the validator addresses - addrs := make([]common.Address, (len(extraData) / common.AddressLength)) - for i := 0; i < len(addrs); i++ { - copy(addrs[i][:], extraData[i*common.AddressLength:]) - } - - return addrs -} - -// ConvertHashToSeed returns a random seed used to calculate proposer. -// It converts first 7.5 bytes of the given hash to int64. -func ConvertHashToSeed(hash common.Hash) (int64, error) { - // TODO-Kaia-Istanbul: convert hash.Hex() to int64 directly without string conversion - hashstring := strings.TrimPrefix(hash.Hex(), "0x") - if len(hashstring) > 15 { - hashstring = hashstring[:15] - } - - seed, err := strconv.ParseInt(hashstring, 16, 64) - if err != nil { - logger.Error("fail to make sub-list of validators", "hash", hash.Hex(), "seed", seed, "err", err) - return 0, err - } - return seed, nil -} - -// SelectRandomCommittee composes a committee selecting validators randomly based on the seed value. -// It returns nil if the given committeeSize is bigger than validatorSize or proposer indexes are invalid. -func SelectRandomCommittee(validators []istanbul.Validator, committeeSize uint64, seed int64, proposerIdx int, nextProposerIdx int) []istanbul.Validator { - // ensure validator indexes are valid - if proposerIdx < 0 || nextProposerIdx < 0 || proposerIdx == nextProposerIdx { - logger.Error("invalid indexes of validators", "proposerIdx", proposerIdx, "nextProposerIdx", nextProposerIdx) - return nil - } - - // ensure committeeSize and proposer indexes are valid - validatorSize := len(validators) - if validatorSize < int(committeeSize) || validatorSize <= proposerIdx || validatorSize <= nextProposerIdx { - logger.Error("invalid committee size or validator indexes", "validatorSize", validatorSize, - "committeeSize", committeeSize, "proposerIdx", proposerIdx, "nextProposerIdx", nextProposerIdx) - return nil - } - - // it cannot be happened. just to make sure - if committeeSize < 2 { - if committeeSize == 0 { - logger.Error("committee size has an invalid value", "committeeSize", committeeSize) - return nil - } - return []istanbul.Validator{validators[proposerIdx]} - } - - // first committee is the proposer and the second committee is the next proposer - committee := make([]istanbul.Validator, committeeSize) - committee[0] = validators[proposerIdx] - committee[1] = validators[nextProposerIdx] - - // select the reset of committee members randomly - picker := rand.New(rand.NewSource(seed)) - pickSize := validatorSize - 2 - indexs := make([]int, pickSize) - idx := 0 - for i := 0; i < validatorSize; i++ { - if i != proposerIdx && i != nextProposerIdx { - indexs[idx] = i - idx++ - } - } - - for i := 0; i < pickSize; i++ { - randIndex := picker.Intn(pickSize) - indexs[i], indexs[randIndex] = indexs[randIndex], indexs[i] - } - - for i := uint64(0); i < committeeSize-2; i++ { - committee[i+2] = validators[indexs[i]] - } - - return committee -} - -// SelectRandaoCommittee composes a committee selecting validators randomly based on the mixHash. -// It returns nil if the given committeeSize is bigger than validatorSize. -func SelectRandaoCommittee(validators []istanbul.Validator, committeeSize uint64, mixHash []byte) []istanbul.Validator { - // it cannot be happened. just to make sure - if committeeSize < 2 { - if committeeSize == 0 { - logger.Error("invalid committee size", "committeeSize", committeeSize) - return nil - } - return validators - } - - seed := int64(binary.BigEndian.Uint64(mixHash[:8])) - size := committeeSize - if committeeSize > uint64(len(validators)) { - size = uint64(len(validators)) - } - return shuffleValidators(validators, seed)[:size] -} - -func shuffleValidators(validators istanbul.Validators, seed int64) []istanbul.Validator { - ret := make([]istanbul.Validator, len(validators)) - copy(ret, validators) - swap := func(x, y int) { - ret[x], ret[y] = ret[y], ret[x] - } - - r := rand.New(rand.NewSource(seed)) - // The Fisher-Yates algorithm used in this shuffle is deterministic for a given seed. - r.Shuffle(len(ret), swap) - - return ret -} diff --git a/consensus/istanbul/validator/validator_test.go b/consensus/istanbul/validator/validator_test.go deleted file mode 100644 index 5c64e3c71..000000000 --- a/consensus/istanbul/validator/validator_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2024 The Kaia Authors -// This file is part of the Kaia library. -// -// The Kaia library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Kaia library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Kaia library. If not, see . -package validator - -import ( - "testing" - - "github.com/kaiachain/kaia/common" - "github.com/stretchr/testify/assert" -) - -func TestCalcSeed(t *testing.T) { - type testCase struct { - hash common.Hash - expected int64 - expectedErr error - } - testCases := []testCase{ - { - hash: common.HexToHash("0x1111"), - expected: 0, - expectedErr: nil, - }, - { - hash: common.HexToHash("0x1234000000000000000000000000000000000000000000000000000000000000"), - expected: 81979586966978560, - expectedErr: nil, - }, - { - hash: common.HexToHash("0x1234123412341230000000000000000000000000000000000000000000000000"), - expected: 81980837895291171, - expectedErr: nil, - }, - { - hash: common.HexToHash("0x1234123412341234000000000000000000000000000000000000000000000000"), - expected: 81980837895291171, - expectedErr: nil, - }, - { - hash: common.HexToHash("0x1234123412341234123412341234123412341234123412341234123412341234"), - expected: 81980837895291171, - expectedErr: nil, - }, - { - hash: common.HexToHash("0x0034123412341234123412341234123412341234123412341234123412341234"), - expected: 916044602622243, - expectedErr: nil, - }, - { - hash: common.HexToHash("0xabcdef3412341234123412341234123412341234123412341234123412341234"), - expected: 773738372352131363, - expectedErr: nil, - }, - } - - for _, tc := range testCases { - actual, err := ConvertHashToSeed(tc.hash) - assert.Equal(t, err, tc.expectedErr) - assert.Equal(t, actual, tc.expected) - } -} diff --git a/consensus/istanbul/validator/weighted.go b/consensus/istanbul/validator/weighted.go deleted file mode 100644 index c0399183f..000000000 --- a/consensus/istanbul/validator/weighted.go +++ /dev/null @@ -1,979 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Modifications Copyright 2019 The klaytn Authors -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . -// -// This file is derived from quorum/consensus/istanbul/validator/default.go (2018/06/04). -// Modified and improved for the klaytn development. -// Modified and improved for the Kaia development. - -package validator - -import ( - "errors" - "fmt" - "math" - "math/big" - "math/rand" - "reflect" - "sort" - "strconv" - "strings" - "sync" - "sync/atomic" - - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/fork" - "github.com/kaiachain/kaia/kaiax/staking" - "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" -) - -type weightedValidator struct { - address common.Address - - rewardAddress atomic.Value - votingPower uint64 // TODO-Klaytn-Issue1336 This should be updated for governance implementation - weight uint64 -} - -func (val *weightedValidator) Address() common.Address { - return val.address -} - -func (val *weightedValidator) String() string { - return val.Address().String() -} - -func (val *weightedValidator) Equal(val2 *weightedValidator) bool { - return val.address == val2.address -} - -func (val *weightedValidator) Hash() int64 { - return val.address.Hash().Big().Int64() -} - -func (val *weightedValidator) RewardAddress() common.Address { - rewardAddress := val.rewardAddress.Load() - if rewardAddress == nil { - return common.Address{} - } - return rewardAddress.(common.Address) -} - -func (val *weightedValidator) SetRewardAddress(rewardAddress common.Address) { - val.rewardAddress.Store(rewardAddress) -} - -func (val *weightedValidator) VotingPower() uint64 { - return val.votingPower -} - -func (val *weightedValidator) Weight() uint64 { - return atomic.LoadUint64(&val.weight) -} - -func newWeightedValidator(addr common.Address, reward common.Address, votingpower uint64, weight uint64) istanbul.Validator { - weightedValidator := &weightedValidator{ - address: addr, - votingPower: votingpower, - weight: weight, - } - weightedValidator.SetRewardAddress(reward) - return weightedValidator -} - -type weightedCouncil struct { - subSize uint64 - demotedValidators istanbul.Validators // validators staking KAIA less than minimum, and not in committee/proposers - validators istanbul.Validators // validators staking KAIA more than and equals to minimum, and in committee/proposers - policy istanbul.ProposerPolicy - - proposer atomic.Value // istanbul.Validator - validatorMu sync.RWMutex // this validator mutex protects concurrent usage of validators and demotedValidators - selector istanbul.ProposalSelector - - // TODO-Kaia-Governance proposers means that the proposers for next block, so refactor it. - // proposers are determined on a specific block, but it can be removed after votes. - proposers []istanbul.Validator - proposersBlockNum uint64 // block number when proposers is determined - - stakingInfo *reward.StakingInfo - - blockNum uint64 // block number when council is determined - mixHash []byte // mixHash at blockNum -} - -func RecoverWeightedCouncilProposer(valSet istanbul.ValidatorSet, proposerAddrs []common.Address) { - weightedCouncil, ok := valSet.(*weightedCouncil) - if !ok { - logger.Error("Not weightedCouncil type. Return without recovering.") - return - } - - proposers := []istanbul.Validator{} - - for i, proposerAddr := range proposerAddrs { - _, val := weightedCouncil.GetByAddress(proposerAddr) - if val == nil { - logger.Error("Proposer is not available now.", "proposer address", proposerAddr) - // The valSet.proposers hasn't been used since Randao HF. - continue - } - proposers = append(proposers, val) - - // TODO-Klaytn-Issue1166 Disable Trace log later - logger.Trace("RecoverWeightedCouncilProposer() proposers", "i", i, "address", val.Address().String()) - } - weightedCouncil.proposers = proposers -} - -func NewWeightedCouncil(addrs []common.Address, demotedAddrs []common.Address, rewards []common.Address, votingPowers []uint64, weights []uint64, policy istanbul.ProposerPolicy, committeeSize uint64, blockNum uint64, proposersBlockNum uint64, chain consensus.ChainReader) *weightedCouncil { - if policy != istanbul.WeightedRandom { - logger.Error("unsupported proposer policy for weighted council", "policy", policy) - return nil - } - - valSet := &weightedCouncil{} - valSet.policy = policy - - // prepare rewards if necessary - if rewards == nil { - rewards = make([]common.Address, len(addrs)) - for i := range addrs { - rewards[i] = common.Address{} - } - } - - // prepare weights if necessary - if weights == nil { - // initialize with 0 weight. - weights = make([]uint64, len(addrs)) - } - - // prepare votingPowers if necessary - if votingPowers == nil { - votingPowers = make([]uint64, len(addrs)) - if chain == nil { - logger.Crit("Requires chain to initialize voting powers.") - } - - //stateDB, err := chain.State() - //if err != nil { - // logger.Crit("Failed to get statedb from chain.") - //} - - for i := range addrs { - // TODO-Kaia-TokenEconomy: Use default value until the formula to calculate votingpower released - votingPowers[i] = 1000 - //staking := stateDB.GetBalance(addr) - //if staking.Cmp(common.Big0) == 0 { - // votingPowers[i] = 1 - //} else { - // votingPowers[i] = 2 - //} - } - } - - if len(addrs) != len(rewards) || - len(addrs) != len(votingPowers) || - len(addrs) != len(weights) { - logger.Error("incomplete information for weighted council", "num addrs", len(addrs), "num rewards", len(rewards), "num votingPowers", len(votingPowers), "num weights", len(weights)) - return nil - } - - // init validators - valSet.validators = make([]istanbul.Validator, len(addrs)) - for i, addr := range addrs { - valSet.validators[i] = newWeightedValidator(addr, rewards[i], votingPowers[i], weights[i]) - } - - // sort validators - sort.Sort(valSet.validators) - - // init demoted validators - valSet.demotedValidators = make([]istanbul.Validator, len(demotedAddrs)) - for i, addr := range demotedAddrs { - valSet.demotedValidators[i] = newWeightedValidator(addr, common.Address{}, 1000, 0) - } - - // sort demoted validators - sort.Sort(valSet.demotedValidators) - - // init proposer - if valSet.Size() > 0 { - valSet.proposer.Store(valSet.GetByIndex(0)) - } - valSet.SetSubGroupSize(committeeSize) - valSet.selector = weightedRandomProposer - - valSet.blockNum = blockNum - valSet.proposers = make([]istanbul.Validator, len(addrs)) - copy(valSet.proposers, valSet.validators) - valSet.proposersBlockNum = proposersBlockNum - - logger.Trace("Allocate new weightedCouncil", "weightedCouncil", valSet) - - return valSet -} - -func GetWeightedCouncilData(valSet istanbul.ValidatorSet) (validators []common.Address, demotedValidators []common.Address, rewardAddrs []common.Address, votingPowers []uint64, weights []uint64, proposers []common.Address, proposersBlockNum uint64, mixHash []byte) { - weightedCouncil, ok := valSet.(*weightedCouncil) - if !ok { - logger.Error("not weightedCouncil type.") - return - } - - if weightedCouncil.Policy() == istanbul.WeightedRandom { - numVals := len(weightedCouncil.validators) - validators = make([]common.Address, numVals) - rewardAddrs = make([]common.Address, numVals) - votingPowers = make([]uint64, numVals) - weights = make([]uint64, numVals) - for i, val := range weightedCouncil.List() { - weightedVal := val.(*weightedValidator) - validators[i] = weightedVal.address - rewardAddrs[i] = weightedVal.RewardAddress() - votingPowers[i] = weightedVal.votingPower - weights[i] = atomic.LoadUint64(&weightedVal.weight) - } - - numDemoted := len(weightedCouncil.demotedValidators) - demotedValidators = make([]common.Address, numDemoted) - for i, val := range weightedCouncil.demotedValidators { - demotedValidators[i] = val.Address() - } - - proposers = make([]common.Address, len(weightedCouncil.proposers)) - for i, proposer := range weightedCouncil.proposers { - proposers[i] = proposer.Address() - } - proposersBlockNum = weightedCouncil.proposersBlockNum - mixHash = weightedCouncil.mixHash - } else { - logger.Error("invalid proposer policy for weightedCouncil") - } - return -} - -func weightedRandomProposer(valSet istanbul.ValidatorSet, lastProposer common.Address, round uint64) istanbul.Validator { - weightedCouncil, ok := valSet.(*weightedCouncil) - if !ok { - logger.Error("weightedRandomProposer() Not weightedCouncil type.") - return nil - } - - rules := fork.Rules(new(big.Int).SetUint64(weightedCouncil.blockNum + 1)) - // After Randao: Select one from ValidatorSet using MixHash as a seed. - if rules.IsRandao { - if weightedCouncil.mixHash == nil { - logger.Error("no mixHash", "number", weightedCouncil.blockNum) - return nil - } - // def proposer_selector(validators, committee_size, round, seed): - // select_committee_KIP146(validators, committee_size, seed)[round % len(validators)] - committee := SelectRandaoCommittee(weightedCouncil.List(), weightedCouncil.subSize, weightedCouncil.mixHash) - return committee[round%uint64(len(committee))] - } - - // Before Randao: Select one from the pre-shuffled `proposers[]` with a round-robin algorithm. - numProposers := len(weightedCouncil.proposers) - if numProposers == 0 { - logger.Error("weightedRandomProposer() No available proposers.") - return nil - } - - // At RefreshProposers(), proposers is already randomly shuffled considering weights. - // So let's just round robin this array - blockNum := weightedCouncil.blockNum - picker := (blockNum + round - params.CalcProposerBlockNumber(blockNum+1)) % uint64(numProposers) - proposer := weightedCouncil.proposers[picker] - - // Enable below more detailed log when debugging - // logger.Trace("Select a proposer using weighted random", "proposer", proposer.String(), "picker", picker, "blockNum of council", blockNum, "round", round, "blockNum of proposers updated", weightedCouncil.proposersBlockNum, "number of proposers", numProposers, "all proposers", weightedCouncil.proposers) - - return proposer -} - -func (valSet *weightedCouncil) Size() uint64 { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - return uint64(len(valSet.validators)) -} - -func (valSet *weightedCouncil) SubGroupSize() uint64 { - return valSet.subSize -} - -// SetSubGroupSize sets committee size of the valSet. -func (valSet *weightedCouncil) SetSubGroupSize(size uint64) { - if size == 0 { - logger.Error("cannot assign committee size to 0") - return - } - valSet.subSize = size -} - -func (valSet *weightedCouncil) List() []istanbul.Validator { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - return valSet.validators -} - -func (valSet *weightedCouncil) DemotedList() []istanbul.Validator { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - return valSet.demotedValidators -} - -// SubList composes a committee after setting a proposer with a default value. -// This functions returns whole validators if it failed to compose a committee. -func (valSet *weightedCouncil) SubList(prevHash common.Hash, view *istanbul.View) []istanbul.Validator { - // TODO-Kaia-Istanbul: investigate whether `valSet.GetProposer().Address()` is a proper value - // TODO-Kaia-Istanbul: or the proposer should be calculated based on `view` - return valSet.SubListWithProposer(prevHash, valSet.GetProposer().Address(), view) -} - -// SubListWithProposer composes a committee with given parameters. -// The first member of the committee is set to the given proposer without calculating proposer with the given `view`. -// The second member of the committee is calculated with a round number of the given view and `valSet.blockNum`. -// The reset of the committee is selected with a random seed derived from `prevHash`. -// This functions returns whole validators if it failed to compose a committee. -func (valSet *weightedCouncil) SubListWithProposer(prevHash common.Hash, proposerAddr common.Address, view *istanbul.View) []istanbul.Validator { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - - validators := valSet.validators - validatorSize := uint64(len(validators)) - committeeSize := valSet.subSize - - // return early if the committee size is equal or larger than the validator size - if committeeSize >= validatorSize { - return validators - } - - // find the proposer - proposerIdx, proposer := valSet.getByAddress(proposerAddr) - if proposerIdx < 0 { - logger.Error("invalid index of the proposer", - "addr", proposerAddr.String(), "index", proposerIdx) - return validators - } - - // return early if the committee size is 1 - if committeeSize == 1 { - return []istanbul.Validator{proposer} - } - - // After Randao: SelectRandaoCommittee - if fork.Rules(view.Sequence).IsRandao { - // This committee must include proposers for all rounds because - // the proposer is picked from the this committee. See weightedRandomProposer(). - if valSet.mixHash == nil { - logger.Error("no mixHash", "number", valSet.blockNum) - return nil - } - // def select_committee_KIP146(validators, committee_size, seed): - // shuffled = shuffle_validators_KIP146(validators, seed) - // return shuffled[:min(committee_size, len(validators))] - return SelectRandaoCommittee(validators, committeeSize, valSet.mixHash) - } - - // Before Randao: SelectRandomCommittee, but the first two members are proposer and next proposer - // find the next proposer - var nextProposer istanbul.Validator - idx := uint64(1) - for { - // ensure finishing this loop - if idx > params.ProposerUpdateInterval() { - logger.Error("failed to find the next proposer", "validatorSize", validatorSize, - "proposer", proposer.Address().String(), "validatorAddrs", validators.AddressStringList()) - return validators - } - nextProposer = valSet.selector(valSet, proposerAddr, view.Round.Uint64()+idx) - if proposer.Address() != nextProposer.Address() { - break - } - idx++ - } - nextProposerIdx, _ := valSet.getByAddress(nextProposer.Address()) - if nextProposerIdx < 0 { - logger.Error("invalid index of the next proposer", - "addr", nextProposer.Address().String(), "index", nextProposerIdx) - return validators - } - - // seed will be used to select a random committee - seed, err := ConvertHashToSeed(prevHash) - if fork.Rules(view.Sequence).IsIstanbul { - seed += view.Round.Int64() - } - if err != nil { - logger.Error("failed to convert hash to seed", "prevHash", prevHash, "err", err) - return validators - } - - // select a random committee - committee := SelectRandomCommittee(validators, committeeSize, seed, proposerIdx, nextProposerIdx) - if committee == nil { - committee = validators - } - - logger.Trace("composed committee", "valSet.Number", valSet.blockNum, "prevHash", prevHash.Hex(), - "proposerAddr", proposerAddr, "committee", committee, "committee size", len(committee), "valSet.subSize", committeeSize) - - return committee -} - -func (valSet *weightedCouncil) CheckInSubList(prevHash common.Hash, view *istanbul.View, addr common.Address) bool { - for _, val := range valSet.SubList(prevHash, view) { - if val.Address() == addr { - return true - } - } - return false -} - -func (valSet *weightedCouncil) IsSubSet() bool { - // TODO-Kaia-RemoveLater We don't use this interface anymore. Eventually let's remove this function from ValidatorSet interface. - return valSet.Size() > valSet.subSize -} - -func (valSet *weightedCouncil) GetByIndex(i uint64) istanbul.Validator { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - if i < uint64(len(valSet.validators)) { - return valSet.validators[i] - } - return nil -} - -func (valSet *weightedCouncil) getByAddress(addr common.Address) (int, istanbul.Validator) { - for i, val := range valSet.validators { - if addr == val.Address() { - return i, val - } - } - // TODO-Kaia-Istanbul: Enable this log when non-committee nodes don't call `core.startNewRound()` - /*logger.Warn("failed to find an address in the validator list", - "address", addr, "validatorAddrs", valSet.validators.AddressStringList())*/ - return -1, nil -} - -func (valSet *weightedCouncil) getDemotedByAddress(addr common.Address) (int, istanbul.Validator) { - for i, val := range valSet.demotedValidators { - if addr == val.Address() { - return i, val - } - } - return -1, nil -} - -func (valSet *weightedCouncil) GetByAddress(addr common.Address) (int, istanbul.Validator) { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - return valSet.getByAddress(addr) -} - -func (valSet *weightedCouncil) GetDemotedByAddress(addr common.Address) (int, istanbul.Validator) { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - return valSet.getDemotedByAddress(addr) -} - -func (valSet *weightedCouncil) GetProposer() istanbul.Validator { - // TODO-Kaia-Istanbul: nil check for valSet.proposer is needed - // logger.Trace("GetProposer()", "proposer", valSet.proposer) - return valSet.proposer.Load().(istanbul.Validator) -} - -func (valSet *weightedCouncil) IsProposer(address common.Address) bool { - _, val := valSet.GetByAddress(address) - return reflect.DeepEqual(valSet.GetProposer(), val) -} - -func (valSet *weightedCouncil) chooseProposerByRoundRobin(lastProposer common.Address, round uint64) istanbul.Validator { - seed := uint64(0) - if emptyAddress(lastProposer) { - seed = round - } else { - offset := 0 - if idx, val := valSet.getByAddress(lastProposer); val != nil { - offset = idx - } - seed = uint64(offset) + round - } - pick := seed % uint64(len(valSet.validators)) - return valSet.validators[pick] -} - -func (valSet *weightedCouncil) CalcProposer(lastProposer common.Address, round uint64) { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - - newProposer := valSet.selector(valSet, lastProposer, round) - if newProposer == nil { - if len(valSet.validators) == 0 { - // TODO-Kaia We must make a policy about the mininum number of validators, which can prevent this case. - logger.Error("NO VALIDATOR! Use lastProposer as a workaround") - newProposer = newWeightedValidator(lastProposer, common.Address{}, 0, 0) - } else { - logger.Warn("Failed to select a new proposer, thus fall back to roundRobinProposer") - newProposer = valSet.chooseProposerByRoundRobin(lastProposer, round) - } - } - - logger.Debug("Update a proposer", "old", valSet.proposer, "new", newProposer, "last proposer", lastProposer.String(), "round", round, "blockNum of council", valSet.blockNum, "blockNum of proposers", valSet.proposersBlockNum) - valSet.proposer.Store(newProposer) -} - -func (valSet *weightedCouncil) AddValidator(address common.Address) bool { - valSet.validatorMu.Lock() - defer valSet.validatorMu.Unlock() - for _, v := range valSet.validators { - if v.Address() == address { - return false - } - } - for _, v := range valSet.demotedValidators { - if v.Address() == address { - return false - } - } - - // TODO-Kaia-Governance the new validator is added on validators only and demoted after `RefreshValSet` method. It is better to update here if it is demoted ones. - // TODO-Klaytn-Issue1336 Update for governance implementation. How to determine initial value for rewardAddress and votingPower ? - valSet.validators = append(valSet.validators, newWeightedValidator(address, common.Address{}, 1000, 0)) - - // sort validator - sort.Sort(valSet.validators) - return true -} - -// removeValidatorFromProposers makes new candidate proposers by removing a validator with given address from existing proposers. -func (valSet *weightedCouncil) removeValidatorFromProposers(address common.Address) { - newProposers := make([]istanbul.Validator, 0, len(valSet.proposers)) - - for _, v := range valSet.proposers { - if v.Address() != address { - newProposers = append(newProposers, v) - } - } - - valSet.proposers = newProposers -} - -func (valSet *weightedCouncil) RemoveValidator(address common.Address) bool { - valSet.validatorMu.Lock() - defer valSet.validatorMu.Unlock() - - for i, v := range valSet.validators { - if v.Address() == address { - valSet.validators = append(valSet.validators[:i], valSet.validators[i+1:]...) - valSet.removeValidatorFromProposers(address) - return true - } - } - for i, v := range valSet.demotedValidators { - if v.Address() == address { - valSet.demotedValidators = append(valSet.demotedValidators[:i], valSet.demotedValidators[i+1:]...) - return true - } - } - return false -} - -func (valSet *weightedCouncil) ReplaceValidators(vals []istanbul.Validator) bool { - valSet.validatorMu.Lock() - defer valSet.validatorMu.Unlock() - - valSet.validators = istanbul.Validators(make([]istanbul.Validator, len(vals))) - copy(valSet.validators, istanbul.Validators(vals)) - return true -} - -func (valSet *weightedCouncil) Copy() istanbul.ValidatorSet { - valSet.validatorMu.RLock() - defer valSet.validatorMu.RUnlock() - - newWeightedCouncil := weightedCouncil{ - subSize: valSet.subSize, - policy: valSet.policy, - proposer: valSet.proposer, - selector: valSet.selector, - stakingInfo: valSet.stakingInfo, - proposersBlockNum: valSet.proposersBlockNum, - blockNum: valSet.blockNum, - } - newWeightedCouncil.validators = make([]istanbul.Validator, len(valSet.validators)) - copy(newWeightedCouncil.validators, valSet.validators) - - newWeightedCouncil.demotedValidators = make([]istanbul.Validator, len(valSet.demotedValidators)) - copy(newWeightedCouncil.demotedValidators, valSet.demotedValidators) - - newWeightedCouncil.proposers = make([]istanbul.Validator, len(valSet.proposers)) - copy(newWeightedCouncil.proposers, valSet.proposers) - - newWeightedCouncil.mixHash = make([]byte, len(valSet.mixHash)) - copy(newWeightedCouncil.mixHash, valSet.mixHash) - - return &newWeightedCouncil -} - -func (valSet *weightedCouncil) F() int { - if valSet.Size() > valSet.subSize { - return int(math.Ceil(float64(valSet.subSize)/3)) - 1 - } else { - return int(math.Ceil(float64(valSet.Size())/3)) - 1 - } -} - -func (valSet *weightedCouncil) Policy() istanbul.ProposerPolicy { return valSet.policy } - -// RefreshValSet recalculates up-to-date validators at the staking block number. -// It returns an error if it can't make up-to-date validators -// - due to wrong parameters -// - due to lack of staking information -// It returns no error when weightedCouncil: -// - successfully calculated up-to-date validators -func (valSet *weightedCouncil) RefreshValSet(blockNum uint64, config *params.ChainConfig, isSingle bool, governingNode common.Address, minStaking uint64, stakingModule staking.StakingModule) error { - valSet.validatorMu.Lock() - defer valSet.validatorMu.Unlock() - - // Check errors - numValidators := len(valSet.validators) - if numValidators == 0 { - return errors.New("No validator") - } - - blockNumBig := new(big.Int).SetUint64(blockNum) - chainRules := config.Rules(blockNumBig) - - stakingBlockNum := blockNum - if !chainRules.IsKaia { - stakingBlockNum = params.CalcProposerBlockNumber(blockNum) + 1 - } - - // Temporary patch to use kaiax/staking + weightedCouncil - si, err := stakingModule.GetStakingInfo(stakingBlockNum) - if err != nil { - // Just return without refreshing validators - return errors.New("skip refreshing validators due to no staking info") - } - var useGini bool - if chainRules.IsKore { - useGini = false - } else { - useGini = config.Governance.Reward.UseGiniCoeff - } - newStakingInfo := reward.FromKaiaxWithGini(si, useGini, minStaking) - valSet.stakingInfo = newStakingInfo - - candidates := append(valSet.validators, valSet.demotedValidators...) - weightedValidators, stakingAmounts, err := getStakingAmountsOfValidators(candidates, newStakingInfo) - if err != nil { - return err - } - - if chainRules.IsIstanbul { - var demotedValidators []*weightedValidator - - weightedValidators, stakingAmounts, demotedValidators, _ = filterValidators(isSingle, governingNode, weightedValidators, stakingAmounts, minStaking) - valSet.setValidators(weightedValidators, demotedValidators) - } - - // Although this is for selecting proposer, update it because - // otherwise, all parameters should be re-calculated at `RefreshProposers` method. - if chainRules.IsKore { - setZeroWeight(weightedValidators) - } else { - totalStaking, _ := calcTotalAmount(weightedValidators, valSet.stakingInfo, stakingAmounts) - calcWeight(weightedValidators, stakingAmounts, totalStaking) - } - - logger.Debug("Refresh validators done.", "blockNum", blockNum, "valSet.blockNum", valSet.blockNum, "stakingInfo.BlockNum", valSet.stakingInfo.BlockNum) - - return nil -} - -// RefreshProposers recalculates up-to-date proposers only when blockNum is the proposer update interval. -// This is a legacy function and do not affect to proposer after the randao HF. -// After the randao HF, the proposer is selected at `CalcProposer` method. -// It returns an error if it can't make up-to-date proposers -// - due to wrong parameters -// - due to lack of staking information -// It returns no error when weightedCouncil: -// - already has up-to-date proposers -// - successfully calculated up-to-date proposers -func (valSet *weightedCouncil) RefreshProposers(hash common.Hash, blockNum uint64, config *params.ChainConfig) error { - valSet.validatorMu.Lock() - defer valSet.validatorMu.Unlock() - - // Check errors - numValidators := len(valSet.validators) - if numValidators == 0 { - return errors.New("No validator") - } - - hashString := strings.TrimPrefix(hash.Hex(), "0x") - if len(hashString) > 15 { - hashString = hashString[:15] - } - seed, err := strconv.ParseInt(hashString, 16, 64) - if err != nil { - return err - } - - if valSet.proposersBlockNum == blockNum { - // proposers are already refreshed - return nil - } - - // The weight of validators is already calculated at `RefreshValSet` method. - valSet.refreshProposers(seed, blockNum) - - logger.Debug("Refresh proposers done.", "blockNum", blockNum, "hash", hash, "valSet.blockNum", valSet.blockNum) - logger.Debug("New proposers calculated", "new proposers", valSet.proposers) - - return nil -} - -// setValidators converts weighted validator slice to istanbul.Validators and sets them to the council. -func (valSet *weightedCouncil) setValidators(validators []*weightedValidator, demoted []*weightedValidator) { - var ( - newValidators istanbul.Validators - newDemoted istanbul.Validators - ) - - for _, val := range validators { - newValidators = append(newValidators, val) - } - - for _, val := range demoted { - newDemoted = append(newDemoted, val) - } - - sort.Sort(newValidators) - sort.Sort(newDemoted) - - valSet.validators = newValidators - valSet.demotedValidators = newDemoted -} - -// filterValidators divided the given weightedValidators into two group filtered by the minimum amount of staking. -// If governance mode is single, the governing node will always be a validator. -// If no validator has enough KAIA, all become validators. -func filterValidators(isSingleMode bool, govNodeAddr common.Address, weightedValidators []*weightedValidator, stakingAmounts []float64, minStaking uint64) ([]*weightedValidator, []float64, []*weightedValidator, []float64) { - var ( - newWeightedValidators []*weightedValidator - newWeightedDemoted []*weightedValidator - govNode *weightedValidator - newValidatorsStaking []float64 - newDemotedStaking []float64 - govNodeStaking float64 - ) - for idx, val := range stakingAmounts { - if isSingleMode && govNodeAddr == weightedValidators[idx].Address() { - govNode = weightedValidators[idx] - govNodeStaking = val - } else if uint64(val) >= minStaking { - newWeightedValidators = append(newWeightedValidators, weightedValidators[idx]) - newValidatorsStaking = append(newValidatorsStaking, val) - } else { - newWeightedDemoted = append(newWeightedDemoted, weightedValidators[idx]) - newDemotedStaking = append(newDemotedStaking, val) - } - } - - // when no validator has more than minimum staking amount of KAIA, all members are in validators - if len(newWeightedValidators) <= 0 { - // 1. if governance mode is not single, - // 2. if governance mode is single and governing node does not have minimum staking amount of KAIA as well - if !isSingleMode || uint64(govNodeStaking) < minStaking { - newWeightedValidators, newValidatorsStaking = newWeightedDemoted, newDemotedStaking - newWeightedDemoted, newDemotedStaking = []*weightedValidator{}, []float64{} - logger.Debug("there is no council member staking more than the minimum, so all become validators", "numValidators", len(newWeightedValidators), "isSingleMode", isSingleMode, "govNodeAddr", govNodeAddr, "govNodeStaking", govNodeStaking, "minStaking", minStaking) - } - } - - // if the governance mode is single, governing node is added to validators all the time. - if isSingleMode { - newWeightedValidators = append(newWeightedValidators, govNode) - newValidatorsStaking = append(newValidatorsStaking, govNodeStaking) - } - return newWeightedValidators, newValidatorsStaking, newWeightedDemoted, newDemotedStaking -} - -// getStakingAmountsOfValidators calculates stakingAmounts of validators. -// If validators have multiple staking contracts, stakingAmounts will be a sum of stakingAmounts with the same rewardAddress. -// - []*weightedValidator : a list of validators which type is converted to weightedValidator -// - []float64 : a list of stakingAmounts. -func getStakingAmountsOfValidators(validators istanbul.Validators, stakingInfo *reward.StakingInfo) ([]*weightedValidator, []float64, error) { - nVals := len(validators) - weightedVals := make([]*weightedValidator, nVals) - stakingAmounts := make([]float64, nVals) - - for vIdx, val := range validators { - weightedVal, ok := val.(*weightedValidator) - if !ok { - return nil, nil, errors.New(fmt.Sprintf("not weightedValidator. val=%s", val.Address().String())) - } - weightedVals[vIdx] = weightedVal - - if cIdx, err := stakingInfo.GetIndexByNodeAddress(weightedVal.address); err == nil { - valRewardAddr := stakingInfo.CouncilRewardAddrs[cIdx] - weightedVal.SetRewardAddress(valRewardAddr) - for rIdx, rewardAddr := range stakingInfo.CouncilRewardAddrs { - if rewardAddr == valRewardAddr { - stakingAmounts[vIdx] += float64(stakingInfo.CouncilStakingAmounts[rIdx]) - } - } - } - } - - logger.Debug("stakingAmounts of validators", "validators", weightedVals, "stakingAmounts", stakingAmounts) - return weightedVals, stakingAmounts, nil -} - -// calcTotalAmount calculates totalAmount of stakingAmounts. -// If UseGini is true, gini is reflected to stakingAmounts. -func calcTotalAmount(weightedValidators []*weightedValidator, stakingInfo *reward.StakingInfo, stakingAmounts []float64) (float64, float64) { - totalStaking := float64(0) - // stakingInfo.Gini is calculated among all CNs (i.e. AddressBook.cnStakingContracts) - // But we want the gini calculated among the subset of CNs (i.e. validators) - gini := reward.DefaultGiniCoefficient - - if len(stakingInfo.CouncilNodeAddrs) == 0 { - return totalStaking, gini - } - - if stakingInfo.UseGini { - var tempStakingAmounts []float64 - for vIdx, val := range weightedValidators { - _, err := stakingInfo.GetIndexByNodeAddress(val.address) - if err == nil { - tempStakingAmounts = append(tempStakingAmounts, stakingAmounts[vIdx]) - } - } - gini = reward.CalcGiniCoefficient(tempStakingAmounts) - - for i := range stakingAmounts { - stakingAmounts[i] = math.Round(math.Pow(stakingAmounts[i], 1.0/(1+gini))) - totalStaking += stakingAmounts[i] - } - } else { - for _, stakingAmount := range stakingAmounts { - totalStaking += stakingAmount - } - } - - logger.Debug("calculate totalStaking", "UseGini", stakingInfo.UseGini, "Gini", gini, "totalStaking", totalStaking, "stakingAmounts", stakingAmounts) - return totalStaking, gini -} - -// setZeroWeight makes each validator's weight to zero -func setZeroWeight(weightedValidators []*weightedValidator) { - for _, weightedVal := range weightedValidators { - atomic.StoreUint64(&weightedVal.weight, 0) - } -} - -// calcWeight updates each validator's weight based on the ratio of its staking amount vs. the total staking amount. -func calcWeight(weightedValidators []*weightedValidator, stakingAmounts []float64, totalStaking float64) { - localLogger := logger.NewWith() - if totalStaking > 0 { - for i, weightedVal := range weightedValidators { - weight := uint64(math.Round(stakingAmounts[i] * 100 / totalStaking)) - if weight <= 0 { - // A validator, who holds zero or small stake, has minimum weight, 1. - weight = 1 - } - atomic.StoreUint64(&weightedVal.weight, weight) - localLogger = localLogger.NewWith(weightedVal.String(), weight) - } - } else { - for _, weightedVal := range weightedValidators { - atomic.StoreUint64(&weightedVal.weight, 0) - localLogger = localLogger.NewWith(weightedVal.String(), 0) - } - } - localLogger.Debug("calculation weight finished") -} - -func (valSet *weightedCouncil) refreshProposers(seed int64, blockNum uint64) { - var candidateValsIdx []int // This is a slice which stores index of validator. it is used for shuffling - - for index, val := range valSet.validators { - weight := val.Weight() - for i := uint64(0); i < weight; i++ { - candidateValsIdx = append(candidateValsIdx, index) - } - } - - if len(candidateValsIdx) == 0 { - // All validators has zero weight. Let's use all validators as candidate proposers. - for index := 0; index < len(valSet.validators); index++ { - candidateValsIdx = append(candidateValsIdx, index) - } - logger.Trace("Refresh uses all validators as candidate proposers, because all weight is zero.", "candidateValsIdx", candidateValsIdx) - } - - proposers := make([]istanbul.Validator, len(candidateValsIdx)) - - limit := len(candidateValsIdx) - picker := rand.New(rand.NewSource(seed)) - - // shuffle - for i := 0; i < limit; i++ { - randIndex := picker.Intn(limit) - candidateValsIdx[i], candidateValsIdx[randIndex] = candidateValsIdx[randIndex], candidateValsIdx[i] - } - - for i := 0; i < limit; i++ { - proposers[i] = valSet.validators[candidateValsIdx[i]] - // Below log is too verbose. Use is only when debugging. - // logger.Trace("Refresh calculates new proposers", "i", i, "proposers[i]", proposers[i].String()) - } - - valSet.proposers = proposers - valSet.proposersBlockNum = blockNum -} - -func (valSet *weightedCouncil) SetBlockNum(blockNum uint64) { - valSet.blockNum = blockNum -} - -func (valSet *weightedCouncil) SetMixHash(mixHash []byte) { - valSet.mixHash = mixHash -} - -func (valSet *weightedCouncil) Proposers() []istanbul.Validator { - return valSet.proposers -} - -func (valSet *weightedCouncil) TotalVotingPower() uint64 { - sum := uint64(0) - for _, v := range valSet.List() { - sum += v.VotingPower() - } - return sum -} - -func (valSet *weightedCouncil) Selector(valS istanbul.ValidatorSet, lastProposer common.Address, round uint64) istanbul.Validator { - return valSet.selector(valS, lastProposer, round) -} diff --git a/consensus/istanbul/validator/weighted_random_test.go b/consensus/istanbul/validator/weighted_random_test.go deleted file mode 100644 index 8861079d5..000000000 --- a/consensus/istanbul/validator/weighted_random_test.go +++ /dev/null @@ -1,662 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Copyright 2019 The klaytn Authors -// This file is part of the klaytn library. -// -// The klaytn library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The klaytn library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the klaytn library. If not, see . -// Modified and improved for the Kaia development. - -package validator - -import ( - "math/big" - "math/rand" - "reflect" - "sort" - "strconv" - "strings" - "testing" - "time" - - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/common/hexutil" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/crypto" - "github.com/kaiachain/kaia/fork" - "github.com/kaiachain/kaia/params" - "github.com/stretchr/testify/assert" -) - -var ( - testAddrs = []common.Address{ - common.HexToAddress("0x0adBC7b05Da383157200a9Fa192285898aB2CaAc"), - common.HexToAddress("0x371F315BeBe961776AC84B29e044b01074b93E69"), - common.HexToAddress("0x5845EAa7ac251542Dc96fBaD09E3CAd3ec105a7a"), - common.HexToAddress("0x63805D23fC86Aa16EFB157C036F226f3aa93099d"), - common.HexToAddress("0x68E0DEf1e6beb308eF5FdF2e19dB2884571c465c"), - common.HexToAddress("0x72E23aAe2Cc6eE54682bD67B6093F7b7971f3D2F"), - common.HexToAddress("0x78B898e37A45069518775972AB8155493e69A2F0"), - common.HexToAddress("0x8704Ffb473a16638ea42c7704995d6505102a4Ca"), - common.HexToAddress("0x93d3Ce8940c7907b0C1c3898dF7Aa797C457cD0f"), - common.HexToAddress("0x9a049EefC01aAE911F2B6F19d724dF9d3ca5cAe6"), - common.HexToAddress("0xC14124d61fc940c7aF29F62438D1B54fD7FFB65B"), - common.HexToAddress("0xc4cB0B3c2682C15D96739f9a13fE26f17c893f8f"), - common.HexToAddress("0xd4aB68fcEC8Fa23856188163B131F3E443e09EF8"), - } - testRewardAddrs = []common.Address{ - common.HexToAddress("0x0a6e50a28f10CD9dba36DD9D3B95BaA32F9fe77a"), - common.HexToAddress("0x23FB6C77E069BD6456181f48a9c77f3a3812E7e7"), - common.HexToAddress("0x43d5e084D8A6c7FbCd0EbA9a517533fF384f0577"), - common.HexToAddress("0x4d180C12FB3B061f44E91D30d574F78D1DeCAD90"), - common.HexToAddress("0x53094cE69ea701bfb9D06239087d4CF09F127B78"), - common.HexToAddress("0x5F2152bf0C97f1d2c3Ffec8A98FEEB1e50798090"), - common.HexToAddress("0x653f42fb1F3474de222F7DDa2109250218989B19"), - common.HexToAddress("0x93eaEAa38D534B52E7DB3AB939330022330cD427"), - common.HexToAddress("0x96a0d7f6A82B860313FF8668b858aD4930d7B2d6"), - common.HexToAddress("0xB89ff800C21b3334f0e355A73242bB4363cf6e10"), - common.HexToAddress("0xDEeeF6fAC16f095Fa944E481F8e6c3b42ae3Cefa"), - common.HexToAddress("0xbDE3Ee8c01484dDBD59a425457Ab138cf3aa0E11"), - common.HexToAddress("0xdD5572A7aC7AB7407e8e4082dB442668C02924E3"), - } - testStakingAddrs = []common.Address{ - common.HexToAddress("0x3776A66698babFA24F0316e4363B2E6C95B09ceF"), - common.HexToAddress("0x4d086A88329233E00158FEcbe7b38Dd8667Dd9f9"), - common.HexToAddress("0x5d7d13278AEF56263B7d25d51E1B2519Ac0D656B"), - common.HexToAddress("0x60fA2326f6C1A7a90Bd1B3c31Bd1A7f9Aed61443"), - common.HexToAddress("0x681C55B2CD831D262C785e213a70e277D0226c79"), - common.HexToAddress("0x6EeA09FF2bB16F1cD075c748E1684f1100085541"), - common.HexToAddress("0x817617C3f09d08a5d475bf72b4723A755CD9b8c7"), - common.HexToAddress("0x83F28D3512dC32701F375b112d0CB0810Cb736e4"), - common.HexToAddress("0x92D03E4998fB3F91A1E24496EDCf625136037f9e"), - common.HexToAddress("0xA0360cDC935A9f3bFe7Ad03D1C34989427ad239f"), - common.HexToAddress("0xE2946677DcEEDDF36F1f6EA00421635804872D49"), - common.HexToAddress("0xF246283a57A8018085AF39bdadFCC4aaC682e6dD"), - common.HexToAddress("0xF3c6f39e231C7363F9B5F4d71b5EE7Eb1fB265d7"), - } - testVotingPowers = []uint64{ - 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - 1, 1, 1, - } - testZeroWeights = []uint64{ - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 0, - } - testPrevHash = common.HexToHash("0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a") - testMixHash = hexutil.MustDecode("0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a") - - testExpectedProposers = []common.Address{ - common.HexToAddress("0x8704Ffb473a16638ea42c7704995d6505102a4Ca"), - common.HexToAddress("0xC14124d61fc940c7aF29F62438D1B54fD7FFB65B"), - common.HexToAddress("0x72E23aAe2Cc6eE54682bD67B6093F7b7971f3D2F"), - common.HexToAddress("0xc4cB0B3c2682C15D96739f9a13fE26f17c893f8f"), - common.HexToAddress("0x68E0DEf1e6beb308eF5FdF2e19dB2884571c465c"), - common.HexToAddress("0x371F315BeBe961776AC84B29e044b01074b93E69"), - common.HexToAddress("0x63805D23fC86Aa16EFB157C036F226f3aa93099d"), - common.HexToAddress("0x93d3Ce8940c7907b0C1c3898dF7Aa797C457cD0f"), - common.HexToAddress("0x5845EAa7ac251542Dc96fBaD09E3CAd3ec105a7a"), - common.HexToAddress("0x78B898e37A45069518775972AB8155493e69A2F0"), - common.HexToAddress("0x0adBC7b05Da383157200a9Fa192285898aB2CaAc"), - common.HexToAddress("0xd4aB68fcEC8Fa23856188163B131F3E443e09EF8"), - common.HexToAddress("0x9a049EefC01aAE911F2B6F19d724dF9d3ca5cAe6"), - } - - testNonZeroWeights = []uint64{ - 1, 1, 2, 1, 1, - 1, 0, 3, 2, 1, - 0, 1, 5, - } -) - -func makeTestValidators(weights []uint64) (validators istanbul.Validators) { - validators = make([]istanbul.Validator, len(testAddrs)) - for i := range testAddrs { - validators[i] = newWeightedValidator(testAddrs[i], testRewardAddrs[i], testVotingPowers[i], weights[i]) - } - sort.Sort(validators) - - return -} - -func makeTestWeightedCouncil(weights []uint64) (valSet *weightedCouncil) { - // prepare weighted council - valSet = NewWeightedCouncil(testAddrs, nil, testRewardAddrs, testVotingPowers, weights, istanbul.WeightedRandom, 21, 0, 0, nil) - return -} - -func TestWeightedCouncil_List(t *testing.T) { - validators := makeTestValidators(testZeroWeights) - - valSet := makeTestWeightedCouncil(testZeroWeights) - - validators_in_valset := valSet.List() - - if len(validators_in_valset) != len(validators) { - t.Errorf("len of validators in valSet is different from len of given test set %v, validators %v", len(validators_in_valset), len(validators)) - } - - for i := 0; i < len(validators); i++ { - if validators[i].String() != validators_in_valset[i].String() { - t.Errorf("The element in validators in valset is different from given test set%v, validators %v", validators_in_valset[i], validators[i]) - } - } -} - -func TestWeightedCouncil_GetByIndex(t *testing.T) { - validators := makeTestValidators(testZeroWeights) - valSet := makeTestWeightedCouncil(testZeroWeights) - - for i := 0; i < len(validators); i++ { - validatorToCheck := valSet.GetByIndex(uint64(i)) - - if validators[i].Address() != validatorToCheck.Address() { - t.Errorf("The validator with given index is different. index=%v, expected validator=%v, gotten validator %v", i, validators[i], valSet.GetByIndex(uint64(i))) - } - } - - for errorIndex := len(validators) + 1; errorIndex < 100; errorIndex++ { - validatorToCheck := valSet.GetByIndex(uint64(errorIndex)) - - if validatorToCheck != nil { - t.Errorf("The result should be nil with given index. index=%v", errorIndex) - } - } - - for errorIndex := -1; errorIndex > -100; errorIndex-- { - validatorToCheck := valSet.GetByIndex(uint64(errorIndex)) - - if validatorToCheck != nil { - t.Errorf("The result should be nil with given index. index=%v", errorIndex) - } - } -} - -func TestWeightedCouncil_GetByAddress(t *testing.T) { - validators := makeTestValidators(testZeroWeights) - valSet := makeTestWeightedCouncil(testZeroWeights) - - for i := 0; i < len(validators); i++ { - index, validatorToCheck := valSet.getByAddress(validators[i].Address()) - - if validators[index].Address() != validatorToCheck.Address() { - t.Errorf("The validator with given address is different index=%v, expected validator=%v, gotten validator %v", i, validators[i], valSet.GetByIndex(uint64(i))) - } - } - - _, errorValidator := valSet.getByAddress(common.Address{}) - if errorValidator != nil { - t.Errorf("The validator with given address should be nil.") - } -} - -func TestWeightedCouncil_GetProposer(t *testing.T) { - validators := makeTestValidators(testZeroWeights) - valSet := makeTestWeightedCouncil(testZeroWeights) - - // at the first, proposer is the first validator in the validator list - expectedProposer := validators[0] - proposerToCheck := valSet.GetProposer() - - if expectedProposer.Address() != proposerToCheck.Address() { - t.Errorf("proposer should be same. Expected proposer: %v, gotten proposer %v", expectedProposer, proposerToCheck) - } - - // random check. give random validator to valSet and check GetProposer() if it is same as the given validator - r := rand.New(rand.NewSource(time.Now().Unix())) - for i := 0; i < 100; i++ { - choosenIndex := r.Intn(len(validators)) - - valSet.proposer.Store(validators[choosenIndex]) - - expectedProposer := validators[choosenIndex] - proposerToCheck := valSet.GetProposer() - - if expectedProposer.Address() != proposerToCheck.Address() { - t.Errorf("proposer should be same. Expected proposer: %v, gotten proposer %v", expectedProposer, proposerToCheck) - } - } -} - -func TestDefaultSet_IsProposer(t *testing.T) { - validators := makeTestValidators(testZeroWeights) - valSet := makeTestWeightedCouncil(testZeroWeights) - - currentProposer := valSet.GetProposer() - - for i := 0; i < len(validators); i++ { - validatorToTest := validators[i] - - expectedResult := validatorToTest.Address() == currentProposer.Address() - result := valSet.IsProposer(validatorToTest.Address()) - - if result != expectedResult { - t.Errorf("The result is different from the expected result. Expected Result : %v, Gotten Result : %v, CurrentProposer Address : %v, TestValidator Address : %v", expectedResult, result, currentProposer.Address(), validatorToTest.Address()) - } - } -} - -func TestWeightedCouncil_RefreshWithZeroWeight(t *testing.T) { - fork.SetHardForkBlockNumberConfig(¶ms.ChainConfig{ - IstanbulCompatibleBlock: big.NewInt(5), - }) - defer fork.ClearHardForkBlockNumberConfig() - - validators := makeTestValidators(testZeroWeights) - - valSet := makeTestWeightedCouncil(testZeroWeights) - runRefreshForTest(valSet) - - // Run tests - - // 1. check all validators are chosen for proposers - var sortedProposers istanbul.Validators - sortedProposers = make([]istanbul.Validator, len(testAddrs)) - copy(sortedProposers, valSet.proposers) - sort.Sort(sortedProposers) - if !reflect.DeepEqual(sortedProposers, validators) { - t.Errorf("All validators are not in proposers: sorted proposers %v, validators %v", sortedProposers, validators) - } - - // 2. check proposers - for i, val := range valSet.proposers { - if !reflect.DeepEqual(val.Address(), testExpectedProposers[i]) { - t.Errorf("proposer mismatch: have %v, want %v", val.Address().String(), testExpectedProposers[i].String()) - } - } - - // 3. test calculate proposer different round - checkCalcProposerWithRound(t, valSet, testAddrs[0], 0) - checkCalcProposerWithRound(t, valSet, testAddrs[0], 1) - checkCalcProposerWithRound(t, valSet, testAddrs[0], 5) - checkCalcProposerWithRound(t, valSet, testAddrs[0], 13) - checkCalcProposerWithRound(t, valSet, testAddrs[0], 1000) - - // 4. test calculate proposer different block number - for i := 0; i < 100; i++ { - valSet.blockNum = uint64(i) - checkCalcProposerWithBlockNumber(t, valSet, testAddrs[0], 0) - } - - // 5. test calculate proposer different block number and round - for i := 0; i < 100; i++ { - valSet.blockNum = uint64(i) - for j := 0; j < 100; j++ { - round := uint64(j) - checkCalcProposerWithBlockNumberAndRound(t, valSet, testAddrs[0], round) - } - } -} - -func checkCalcProposerWithRound(t *testing.T, valSet *weightedCouncil, lastProposer common.Address, round uint64) { - valSet.CalcProposer(lastProposer, round) - _, expectedVal := valSet.GetByAddress(testExpectedProposers[round%uint64(len(valSet.proposers))]) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, expectedVal) { - t.Errorf("proposer mismatch: have %v, want %v", val.String(), expectedVal.Address().String()) - } -} - -func checkCalcProposerWithBlockNumber(t *testing.T, valSet *weightedCouncil, lastProposer common.Address, round uint64) { - valSet.CalcProposer(lastProposer, round) - _, expectedVal := valSet.GetByAddress(testExpectedProposers[valSet.blockNum%uint64(len(valSet.proposers))]) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, expectedVal) { - t.Errorf("proposer mismatch: have %v, want %v", val.String(), expectedVal.Address().String()) - } -} - -func checkCalcProposerWithBlockNumberAndRound(t *testing.T, valSet *weightedCouncil, lastProposer common.Address, round uint64) { - valSet.CalcProposer(lastProposer, round) - _, expectedVal := valSet.GetByAddress(testExpectedProposers[(valSet.blockNum+round)%uint64(len(valSet.proposers))]) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, expectedVal) { - t.Errorf("proposer mismatch: have %v, want %v", val.String(), expectedVal.Address().String()) - } -} - -func TestWeightedCouncil_RefreshWithNonZeroWeight(t *testing.T) { - validators := makeTestValidators(testNonZeroWeights) - - valSet := makeTestWeightedCouncil(testNonZeroWeights) - runRefreshForTest(valSet) - - // Run tests - - // 1. number of proposers - totalWeights := uint64(0) - for _, v := range validators { - totalWeights += v.Weight() - } - assert.Equal(t, totalWeights, uint64(len(valSet.proposers))) - - // 2. weight and appearance frequency - for _, v := range validators { - weight := v.Weight() - appearance := uint64(0) - for _, p := range valSet.proposers { - if v.Address() == p.Address() { - appearance++ - } - } - assert.Equal(t, weight, appearance) - } -} - -func TestWeightedCouncil_RemoveValidator(t *testing.T) { - validators := makeTestValidators(testNonZeroWeights) - valSet := makeTestWeightedCouncil(testNonZeroWeights) - runRefreshForTest(valSet) - - for _, val := range validators { - - _, removedVal := valSet.GetByAddress(val.Address()) - if removedVal == nil { - t.Errorf("Fail to find validator with address %v", removedVal.Address().String()) - } - - if !valSet.RemoveValidator(removedVal.Address()) { - t.Errorf("Fail to remove validator %v", removedVal.String()) - } - - // check whether removedVal is really removed from validators - for _, v := range valSet.validators { - if removedVal.Address() == v.Address() { - t.Errorf("Validator(%v) does not removed from validators", removedVal.Address().String()) - } - } - - // check whether removedVal is also removed from proposers immediately - for _, p := range valSet.proposers { - if removedVal.Address() == p.Address() { - t.Errorf("Validator(%v) does not removed from proposers", removedVal.Address().String()) - } - } - } - - assert.Equal(t, uint64(0), valSet.Size()) - assert.Equal(t, 0, len(valSet.Proposers())) -} - -func TestWeightedCouncil_RefreshAfterRemoveValidator(t *testing.T) { - validators := makeTestValidators(testNonZeroWeights) - valSet := makeTestWeightedCouncil(testNonZeroWeights) - runRefreshForTest(valSet) - - for _, val := range validators { - - _, removedVal := valSet.GetByAddress(val.Address()) - if removedVal == nil { - t.Errorf("Fail to find validator with address %v", removedVal.Address().String()) - } - - if !valSet.RemoveValidator(removedVal.Address()) { - t.Errorf("Fail to remove validator %v", removedVal.String()) - } - - // check whether removedVal is really removed from validators - for _, v := range valSet.validators { - if removedVal.Address() == v.Address() { - t.Errorf("Validator(%v) does not removed from validators", removedVal.Address().String()) - } - } - - runRefreshForTest(valSet) - - // check whether removedVal is excluded as expected when refreshing proposers - for _, p := range valSet.proposers { - if removedVal.Address() == p.Address() { - t.Errorf("Validator(%v) does not removed from proposers", removedVal.Address().String()) - } - } - } - - assert.Equal(t, uint64(0), valSet.Size()) - assert.Equal(t, 0, len(valSet.Proposers())) -} - -func runRefreshForTest(valSet *weightedCouncil) { - hashString := strings.TrimPrefix(testPrevHash.Hex(), "0x") - if len(hashString) > 15 { - hashString = hashString[:15] - } - seed, _ := strconv.ParseInt(hashString, 16, 64) - valSet.refreshProposers(seed, 0) -} - -func TestWeightedCouncil_SetSubGroupSize(t *testing.T) { - validators := makeTestValidators(testNonZeroWeights) - valSet := makeTestWeightedCouncil(testNonZeroWeights) - - validatorsLen := len(validators) - - for i := 1; i < validatorsLen; i++ { - valSet.SetSubGroupSize(uint64(i)) - - expectedSubGroupSize := uint64(i) - gottenSubGroupSize := valSet.SubGroupSize() - - if expectedSubGroupSize != gottenSubGroupSize { - t.Errorf("SubGroupSize should be %v but gotten SubGroupSize is %v", expectedSubGroupSize, gottenSubGroupSize) - } - } -} - -func TestWeightedCouncil_SubListWithProposer(t *testing.T) { - var ( - validators = makeTestValidators(testNonZeroWeights) - prevHash = crypto.Keccak256Hash([]byte("This is a test")) - valSet = makeTestWeightedCouncil(testNonZeroWeights) - - BlockBeforeHF = big.NewInt(4) - HFBlock = big.NewInt(5) - BlockAfterHF = big.NewInt(6) - - expectIndexOfSubsetLenTest = []int{1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10} - expectIndexOfRoundTestBeforeIstanbulCompatible = [][]int{ - {1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10}, - {2, 3, 7, 1, 11, 6, 9, 4, 0, 8, 12, 10}, - {3, 4, 7, 1, 11, 6, 9, 2, 0, 8, 12, 10}, - {4, 5, 7, 1, 11, 6, 9, 2, 0, 8, 12, 10}, - {5, 6, 7, 1, 11, 4, 9, 2, 0, 8, 12, 10}, - {6, 7, 5, 1, 11, 4, 9, 2, 0, 8, 12, 10}, - {7, 8, 5, 1, 11, 4, 9, 2, 0, 6, 12, 10}, - {8, 9, 5, 1, 11, 4, 7, 2, 0, 6, 12, 10}, - {9, 10, 5, 1, 11, 4, 7, 2, 0, 6, 12, 8}, - {10, 11, 5, 1, 9, 4, 7, 2, 0, 6, 12, 8}, - {11, 12, 5, 1, 9, 4, 7, 2, 0, 6, 10, 8}, - {12, 0, 6, 2, 10, 5, 8, 3, 1, 7, 11, 9}, - {0, 1, 7, 3, 11, 6, 9, 4, 2, 8, 12, 10}, - {1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10}, - {2, 3, 7, 1, 11, 6, 9, 4, 0, 8, 12, 10}, - } - expectIndexOfRoundTestAfterIstanbulCompatible = [][]int{ - {1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10}, - {2, 3, 6, 8, 10, 9, 1, 11, 5, 0, 4, 7}, - {3, 4, 10, 5, 8, 0, 7, 9, 12, 6, 1, 11}, - {4, 5, 6, 0, 7, 1, 3, 12, 2, 8, 9, 11}, - {5, 6, 4, 9, 12, 7, 0, 3, 8, 2, 1, 11}, - {6, 7, 2, 3, 8, 9, 11, 12, 5, 1, 4, 0}, - {7, 8, 3, 11, 5, 10, 0, 1, 2, 6, 9, 12}, - {8, 9, 5, 7, 11, 3, 1, 0, 10, 6, 4, 12}, - {9, 10, 3, 7, 5, 6, 2, 0, 12, 8, 11, 1}, - {10, 11, 7, 1, 0, 9, 8, 6, 12, 5, 2, 4}, - {11, 12, 4, 8, 1, 6, 0, 3, 9, 10, 2, 7}, - {12, 0, 7, 2, 4, 1, 6, 10, 9, 11, 8, 3}, - {0, 1, 9, 8, 2, 3, 10, 5, 7, 12, 4, 6}, - {1, 2, 11, 10, 6, 8, 7, 4, 9, 12, 0, 5}, - {2, 3, 4, 8, 6, 5, 11, 1, 12, 0, 9, 10}, - } - ) - - getExpectSubList := func(indices []int) []istanbul.Validator { - var expectSubList []istanbul.Validator - for _, index := range indices { - expectSubList = append(expectSubList, validators[index]) - } - return expectSubList - } - - fork.SetHardForkBlockNumberConfig(¶ms.ChainConfig{IstanbulCompatibleBlock: HFBlock}) - defer fork.ClearHardForkBlockNumberConfig() - - // SubsetLen test: various subset length test - valSet.SetBlockNum(1) - for testSubsetLen := 2; testSubsetLen < len(validators); testSubsetLen++ { - // set committee size and calculate proposer - valSet.SetSubGroupSize(uint64(testSubsetLen)) - valSet.CalcProposer(valSet.GetProposer().Address(), uint64(0)) - - // get committee list - expectSubList := getExpectSubList(expectIndexOfSubsetLenTest[0:testSubsetLen]) - - // compare the subList of valSet with expected committee list - viewBeforeHF := &istanbul.View{Sequence: BlockBeforeHF, Round: big.NewInt(int64(0))} - viewAfterHF := &istanbul.View{Sequence: BlockAfterHF, Round: big.NewInt(int64(0))} - assert.Equal(t, expectSubList, valSet.SubList(prevHash, viewBeforeHF), "test Subset length: %d(before istanbulCompatible)", testSubsetLen) - assert.Equal(t, expectSubList, valSet.SubList(prevHash, viewAfterHF), "test subset length: %d(after istanbulCompatible)", testSubsetLen) - } - - // Check: compare the size of the test data arrays - assert.Equal(t, len(expectIndexOfRoundTestBeforeIstanbulCompatible), len(expectIndexOfRoundTestAfterIstanbulCompatible)) - - // Round test: various round test - valSet.SetBlockNum(1) - valSet.SetSubGroupSize(uint64(len(validators) - 1)) - for round := 0; round < len(expectIndexOfRoundTestBeforeIstanbulCompatible); round++ { - // calculate proposer and set view with test round value - valSet.CalcProposer(valSet.GetProposer().Address(), uint64(round)) - - // get committee list - expectSubListBeforeHF := getExpectSubList(expectIndexOfRoundTestBeforeIstanbulCompatible[round]) - expectSubListAfterHF := getExpectSubList(expectIndexOfRoundTestAfterIstanbulCompatible[round]) - - // compare the subList of valSet with expected committee list - viewBeforeHF := &istanbul.View{Sequence: BlockBeforeHF, Round: big.NewInt(int64(round))} - viewAfterHF := &istanbul.View{Sequence: BlockAfterHF, Round: big.NewInt(int64(round))} - assert.Equal(t, expectSubListBeforeHF, valSet.SubList(prevHash, viewBeforeHF), "test round: %d(before istanbulCompatible)", round) - assert.Equal(t, expectSubListAfterHF, valSet.SubList(prevHash, viewAfterHF), "test round: %d(after istanbulCompatible)", round) - } -} - -func TestWeightedCouncil_Randao(t *testing.T) { - var ( - forkNum = uint64(5) - validators = makeTestWeightedCouncil(nil).List() - valSize = uint64(len(validators)) - ) - fork.SetHardForkBlockNumberConfig(¶ms.ChainConfig{ - RandaoCompatibleBlock: big.NewInt(int64(forkNum)), - }) - defer fork.ClearHardForkBlockNumberConfig() - valSet := makeTestWeightedCouncil(nil) - - testcases := []struct { - blockNum uint64 - round uint64 - committeeSize uint64 - mixHash []byte - expectedProposer istanbul.Validator - expectedCommittee []istanbul.Validator - }{ - { // Before fork - blockNum: forkNum - 2, - round: 0, - committeeSize: valSize + 1, - mixHash: testMixHash, - expectedProposer: validators[forkNum-2], - expectedCommittee: validators, - }, - { // After fork - blockNum: forkNum - 1, - round: 0, - committeeSize: valSize + 1, - mixHash: testMixHash, - expectedProposer: SelectRandaoCommittee(validators, valSize+1, testMixHash)[0], - expectedCommittee: validators, - }, - { // nil MixHash - blockNum: forkNum + 10, // expect log "no mixHash number=(forkNum+10)" - round: 0, - committeeSize: valSize - 1, - mixHash: nil, - expectedProposer: validators[0], // fall back to roundRobinProposer - expectedCommittee: nil, // SubList fails. - }, - { // IsSubset() == true - blockNum: forkNum, - round: 0, - committeeSize: valSize - 1, - mixHash: testMixHash, - expectedProposer: SelectRandaoCommittee(validators, valSize-1, testMixHash)[0], - expectedCommittee: SelectRandaoCommittee(validators, valSize-1, testMixHash), - }, - { // IsSubset() == false - blockNum: forkNum, - round: 0, - committeeSize: valSize + 1, - mixHash: testMixHash, - expectedProposer: SelectRandaoCommittee(validators, valSize+1, testMixHash)[0], - expectedCommittee: validators, - }, - { // Nonzero round - blockNum: forkNum, - round: 2, - committeeSize: valSize - 1, - mixHash: testMixHash, - expectedProposer: SelectRandaoCommittee(validators, valSize-1, testMixHash)[2], - expectedCommittee: SelectRandaoCommittee(validators, valSize-1, testMixHash), - }, - } - - for i, tc := range testcases { - valSet.SetBlockNum(tc.blockNum) - valSet.SetSubGroupSize(tc.committeeSize) - valSet.SetMixHash(tc.mixHash) - - view := &istanbul.View{ - Sequence: big.NewInt(int64(tc.blockNum)), - Round: big.NewInt(int64(tc.round)), - } - - // The lastProposer is ignored by weightedRandomProposer - // but it is used by roundRobinProposer. If lastProposer = 0x0, - // then roundRobinProposer returns validators[round]. - valSet.CalcProposer(common.Address{}, uint64(tc.round)) - proposer := valSet.GetProposer() - assert.Equal(t, tc.expectedProposer, proposer, "tc[%d]", i) - - committee := valSet.SubList(testPrevHash, view) - assert.Equal(t, tc.expectedCommittee, committee, "tc[%d]", i) - } -} - -func TestWeightedCouncil_Copy(t *testing.T) { - a := makeTestWeightedCouncil(testNonZeroWeights) - a.SetSubGroupSize(21) - a.SetBlockNum(1234) - a.SetMixHash(testMixHash) - - b := a.Copy().(*weightedCouncil) - - assert.Equal(t, a.subSize, b.subSize) - assert.Equal(t, a.demotedValidators, b.demotedValidators) - assert.Equal(t, a.validators, b.validators) - assert.Equal(t, a.policy, b.policy) - - assert.Equal(t, a.proposer, b.proposer) - assert.Equal(t, a.proposers, b.proposers) - assert.Equal(t, a.proposersBlockNum, b.proposersBlockNum) - assert.Equal(t, a.stakingInfo, b.stakingInfo) - assert.Equal(t, a.blockNum, b.blockNum) - assert.Equal(t, a.mixHash, b.mixHash) - - assert.Equal(t, a.GetProposer(), b.GetProposer()) -} diff --git a/consensus/istanbul/validator/weighted_test.go b/consensus/istanbul/validator/weighted_test.go deleted file mode 100644 index 7c35723d5..000000000 --- a/consensus/istanbul/validator/weighted_test.go +++ /dev/null @@ -1,231 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Copyright 2019 The klaytn Authors -// This file is part of the klaytn library. -// -// The klaytn library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The klaytn library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the klaytn library. If not, see . -// Modified and improved for the Kaia development. - -package validator - -import ( - "math/big" - "reflect" - "strings" - "testing" - - "github.com/kaiachain/kaia/blockchain" - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/consensus/istanbul" - "github.com/kaiachain/kaia/crypto" - "github.com/kaiachain/kaia/fork" - "github.com/kaiachain/kaia/params" - "github.com/stretchr/testify/assert" -) - -func TestNewWeightedCouncil(t *testing.T) { - const ValCnt = 100 - var validators []istanbul.Validator - var rewardAddrs []common.Address - var votingPowers []uint64 - - // Create 100 validators with random addresses - b := []byte{} - for i := 0; i < ValCnt; i++ { - key, _ := crypto.GenerateKey() - addr := crypto.PubkeyToAddress(key.PublicKey) - val := New(addr) - validators = append(validators, val) - b = append(b, val.Address().Bytes()...) - - rewardKey, _ := crypto.GenerateKey() - rewardAddr := crypto.PubkeyToAddress(rewardKey.PublicKey) - rewardAddrs = append(rewardAddrs, rewardAddr) - - votingPowers = append(votingPowers, uint64(1)) - } - - // Create ValidatorSet - valSet := NewWeightedCouncil(ExtractValidators(b), nil, rewardAddrs, votingPowers, nil, istanbul.WeightedRandom, 21, 0, 0, nil) - if valSet == nil { - t.Errorf("the validator byte array cannot be parsed") - t.FailNow() - } - - // Check validators sorting: should be in ascending order - for i := 0; i < ValCnt-1; i++ { - val := valSet.GetByIndex(uint64(i)) - nextVal := valSet.GetByIndex(uint64(i + 1)) - if strings.Compare(val.String(), nextVal.String()) >= 0 { - t.Errorf("validator set is not sorted in descending order") - } - } -} - -func TestNormalWeightedCouncil(t *testing.T) { - fork.SetHardForkBlockNumberConfig(¶ms.ChainConfig{ - IstanbulCompatibleBlock: big.NewInt(5), - }) - defer fork.ClearHardForkBlockNumberConfig() - - b1 := common.Hex2Bytes(testAddress) - b2 := common.Hex2Bytes(testAddress2) - addr1 := common.BytesToAddress(b1) - addr2 := common.BytesToAddress(b2) - - rewardKey1, _ := crypto.GenerateKey() - rewardAddr1 := crypto.PubkeyToAddress(rewardKey1.PublicKey) - - rewardKey2, _ := crypto.GenerateKey() - rewardAddr2 := crypto.PubkeyToAddress(rewardKey2.PublicKey) - - votingPower1 := uint64(1) - votingPower2 := uint64(2) - - weight1 := uint64(1) - weight2 := uint64(2) - - val1 := newWeightedValidator(addr1, rewardAddr1, votingPower1, weight1) - val2 := newWeightedValidator(addr2, rewardAddr2, votingPower2, weight2) - - valSet := NewWeightedCouncil([]common.Address{addr1, addr2}, nil, []common.Address{rewardAddr1, rewardAddr2}, []uint64{votingPower1, votingPower2}, []uint64{weight1, weight2}, istanbul.WeightedRandom, 21, 0, 0, nil) - if valSet == nil { - t.Errorf("the format of validator set is invalid") - t.FailNow() - } - - // check size - if size := valSet.Size(); size != 2 { - t.Errorf("the size of validator set is wrong: have %v, want 2", size) - } - - // test get by index - if val := valSet.GetByIndex(uint64(0)); !reflect.DeepEqual(val, val1) { - t.Errorf("validator mismatch:") - t.Errorf(" Address(): %v, %v", val.Address(), val1.Address()) - t.Errorf(" String(): %v, %v", val.String(), val1.String()) - t.Errorf(" RewardAddresS(): %v, %v", val.RewardAddress(), val1.RewardAddress()) - t.Errorf(" VotingPower(): %v, %v", val.VotingPower(), val1.VotingPower()) - t.Errorf(" Weight(): %v, %v", val.Weight(), val1.Weight()) - } - - // test get by invalid index - if val := valSet.GetByIndex(uint64(2)); val != nil { - t.Errorf("validator mismatch: have %v, want nil", val) - } - - // test get by address - if _, val := valSet.GetByAddress(addr2); !reflect.DeepEqual(val, val2) { - t.Errorf("validator mismatch: have %v, want %v", val, val2) - } - - // test get by invalid address - invalidAddr := common.HexToAddress("0x9535b2e7faaba5288511d89341d94a38063a349b") - if _, val := valSet.GetByAddress(invalidAddr); val != nil { - t.Errorf("validator mismatch: have %v, want nil", val) - } - - // test get proposer - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) { - t.Errorf("proposer mismatch: have %v, want %v", val, val1) - } - - // test calculate proposer - lastProposer := addr1 - valSet.CalcProposer(lastProposer, uint64(0)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) { - t.Errorf("proposer mismatch: have %v, want %v", val, val1) - } - - valSet.CalcProposer(lastProposer, uint64(1)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) { - t.Errorf("proposer mismatch: have %v, want %v", val, val2) - } - - valSet.CalcProposer(lastProposer, uint64(2)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) { - t.Errorf("proposer mismatch: have %v, want %v", val, val1) - } - - valSet.CalcProposer(lastProposer, uint64(5)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) { - t.Errorf("proposer mismatch: have %v, want %v", val, val2) - } - - // test empty last proposer - lastProposer = common.Address{} - valSet.CalcProposer(lastProposer, uint64(3)) - if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) { - t.Errorf("proposer mismatch: have %v, want %v", val, val2) - } -} - -func TestEmptyWeightedCouncil(t *testing.T) { - valSet := NewWeightedCouncil(ExtractValidators([]byte{}), nil, nil, nil, nil, istanbul.WeightedRandom, 0, 0, 0, &blockchain.BlockChain{}) - if valSet == nil { - t.Errorf("validator set should not be nil") - } -} - -func TestNewWeightedCouncil_InvalidPolicy(t *testing.T) { - // Invalid proposer policy - valSet := NewWeightedCouncil(ExtractValidators([]byte{}), nil, nil, nil, nil, istanbul.Sticky, 0, 0, 0, &blockchain.BlockChain{}) - assert.Equal(t, (*weightedCouncil)(nil), valSet) - - valSet = NewWeightedCouncil(ExtractValidators([]byte{}), nil, nil, nil, nil, istanbul.RoundRobin, 0, 0, 0, &blockchain.BlockChain{}) - assert.Equal(t, (*weightedCouncil)(nil), valSet) -} - -func TestNewWeightedCouncil_IncompleteParams(t *testing.T) { - const ValCnt = 3 - var validators []istanbul.Validator - var rewardAddrs []common.Address - var votingPowers []uint64 - var weights []uint64 - - // Create 3 validators with random addresses - b := []byte{} - for i := 0; i < ValCnt; i++ { - key, _ := crypto.GenerateKey() - addr := crypto.PubkeyToAddress(key.PublicKey) - val := New(addr) - validators = append(validators, val) - b = append(b, val.Address().Bytes()...) - - rewardKey, _ := crypto.GenerateKey() - rewardAddr := crypto.PubkeyToAddress(rewardKey.PublicKey) - rewardAddrs = append(rewardAddrs, rewardAddr) - - votingPowers = append(votingPowers, uint64(1)) - weights = append(weights, uint64(1)) - } - - // No validator address - valSet := NewWeightedCouncil(ExtractValidators([]byte{}), nil, rewardAddrs, votingPowers, weights, istanbul.WeightedRandom, 0, 0, 0, &blockchain.BlockChain{}) - assert.Equal(t, (*weightedCouncil)(nil), valSet) - - // Incomplete rewardAddrs - incompleteRewardAddrs := make([]common.Address, 1) - valSet = NewWeightedCouncil(ExtractValidators(b), nil, incompleteRewardAddrs, nil, nil, istanbul.WeightedRandom, 0, 0, 0, &blockchain.BlockChain{}) - assert.Equal(t, (*weightedCouncil)(nil), valSet) - - // Incomplete rewardAddrs - incompleteVotingPowers := make([]uint64, 1) - valSet = NewWeightedCouncil(ExtractValidators(b), nil, nil, incompleteVotingPowers, nil, istanbul.WeightedRandom, 0, 0, 0, &blockchain.BlockChain{}) - assert.Equal(t, (*weightedCouncil)(nil), valSet) - - // Incomplete rewardAddrs - incompleteWeights := make([]uint64, 1) - valSet = NewWeightedCouncil(ExtractValidators(b), nil, nil, nil, incompleteWeights, istanbul.WeightedRandom, 0, 0, 0, &blockchain.BlockChain{}) - assert.Equal(t, (*weightedCouncil)(nil), valSet) -} diff --git a/consensus/istanbul/utils_test.go b/consensus/istanbul/validator_test.go similarity index 100% rename from consensus/istanbul/utils_test.go rename to consensus/istanbul/validator_test.go diff --git a/consensus/mocks/engine_mock.go b/consensus/mocks/engine_mock.go index ee7ca50cb..412b8b36f 100644 --- a/consensus/mocks/engine_mock.go +++ b/consensus/mocks/engine_mock.go @@ -96,20 +96,6 @@ func (mr *MockEngineMockRecorder) CanVerifyHeadersConcurrently() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanVerifyHeadersConcurrently", reflect.TypeOf((*MockEngine)(nil).CanVerifyHeadersConcurrently)) } -// CreateSnapshot mocks base method. -func (m *MockEngine) CreateSnapshot(arg0 consensus.ChainReader, arg1 uint64, arg2 common.Hash, arg3 []*types.Header) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSnapshot", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// CreateSnapshot indicates an expected call of CreateSnapshot. -func (mr *MockEngineMockRecorder) CreateSnapshot(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshot", reflect.TypeOf((*MockEngine)(nil).CreateSnapshot), arg0, arg1, arg2, arg3) -} - // Finalize mocks base method. func (m *MockEngine) Finalize(arg0 consensus.ChainReader, arg1 *types.Header, arg2 *state.StateDB, arg3 []*types.Transaction, arg4 []*types.Receipt) (*types.Block, error) { m.ctrl.T.Helper() @@ -140,33 +126,6 @@ func (mr *MockEngineMockRecorder) GetConsensusInfo(arg0 interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConsensusInfo", reflect.TypeOf((*MockEngine)(nil).GetConsensusInfo), arg0) } -// GetKaiaHeadersForSnapshotApply mocks base method. -func (m *MockEngine) GetKaiaHeadersForSnapshotApply(arg0 consensus.ChainReader, arg1 uint64, arg2 common.Hash, arg3 []*types.Header) ([]*types.Header, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetKaiaHeadersForSnapshotApply", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*types.Header) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetKaiaHeadersForSnapshotApply indicates an expected call of GetKaiaHeadersForSnapshotApply. -func (mr *MockEngineMockRecorder) GetKaiaHeadersForSnapshotApply(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKaiaHeadersForSnapshotApply", reflect.TypeOf((*MockEngine)(nil).GetKaiaHeadersForSnapshotApply), arg0, arg1, arg2, arg3) -} - -// InitSnapshot mocks base method. -func (m *MockEngine) InitSnapshot() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "InitSnapshot") -} - -// InitSnapshot indicates an expected call of InitSnapshot. -func (mr *MockEngineMockRecorder) InitSnapshot() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitSnapshot", reflect.TypeOf((*MockEngine)(nil).InitSnapshot)) -} - // Initialize mocks base method. func (m *MockEngine) Initialize(arg0 consensus.ChainReader, arg1 *types.Header, arg2 *state.StateDB) { m.ctrl.T.Helper() @@ -222,6 +181,18 @@ func (mr *MockEngineMockRecorder) Protocol() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Protocol", reflect.TypeOf((*MockEngine)(nil).Protocol)) } +// PurgeCache mocks base method. +func (m *MockEngine) PurgeCache() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PurgeCache") +} + +// PurgeCache indicates an expected call of PurgeCache. +func (mr *MockEngineMockRecorder) PurgeCache() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurgeCache", reflect.TypeOf((*MockEngine)(nil).PurgeCache)) +} + // Seal mocks base method. func (m *MockEngine) Seal(arg0 consensus.ChainReader, arg1 *types.Block, arg2 <-chan struct{}) (*types.Block, error) { m.ctrl.T.Helper() diff --git a/datasync/chaindatafetcher/kafka/repository.go b/datasync/chaindatafetcher/kafka/repository.go index ce02c2519..25e56df06 100644 --- a/datasync/chaindatafetcher/kafka/repository.go +++ b/datasync/chaindatafetcher/kafka/repository.go @@ -24,7 +24,6 @@ import ( "github.com/kaiachain/kaia/blockchain" "github.com/kaiachain/kaia/blockchain/vm" - "github.com/kaiachain/kaia/common" "github.com/kaiachain/kaia/consensus" "github.com/kaiachain/kaia/datasync/chaindatafetcher/types" ) @@ -76,13 +75,6 @@ func (r *repository) SetComponent(component interface{}) { func (r *repository) HandleChainEvent(event blockchain.ChainEvent, dataType types.RequestType) error { switch dataType { case types.RequestTypeBlockGroup: - if event.Block.NumberU64() > 0 { - err := checkStatesForSnapshot(r.blockchain, r.engine, event.Block.NumberU64()-1, event.Block.ParentHash()) - if err != nil { - logger.Warn("skip fetching block", "number", event.Block.NumberU64(), "err", err) - return nil - } - } cInfo, err := r.engine.GetConsensusInfo(event.Block) if err != nil { return fmt.Errorf("failed to retrieve consensusinfo with the given block number: %v", event.Block.Number()) @@ -105,17 +97,3 @@ func (r *repository) HandleChainEvent(event blockchain.ChainEvent, dataType type return fmt.Errorf("not supported type. [blockNumber: %v, reqType: %v]", event.Block.NumberU64(), dataType) } } - -func checkStatesForSnapshot(chain consensus.ChainReader, engine consensus.Engine, number uint64, hash common.Hash) error { - headers, err := engine.GetKaiaHeadersForSnapshotApply(chain, number, hash, nil) - if err != nil { - return err - } - - for _, header := range headers { - if _, err := chain.StateAt(header.Root); err != nil { - return err - } - } - return nil -} diff --git a/datasync/downloader/downloader.go b/datasync/downloader/downloader.go index d36539505..4380eef83 100644 --- a/datasync/downloader/downloader.go +++ b/datasync/downloader/downloader.go @@ -38,7 +38,6 @@ import ( "github.com/kaiachain/kaia/log" "github.com/kaiachain/kaia/node/cn/snap" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" "github.com/kaiachain/kaia/snapshot" "github.com/kaiachain/kaia/storage/database" "github.com/kaiachain/kaia/storage/statedb" @@ -105,7 +104,7 @@ type Downloader struct { isStakingInfoRecovery bool stakingInfoRecoveryTotal int - stakingInfoRecoveryCh chan []*reward.StakingInfo + stakingInfoRecoveryCh chan []*staking.P2PStakingInfo stakingInfoRecoveryBlocks []uint64 queue *queue // Scheduler for selecting the hashes to download @@ -635,7 +634,7 @@ func (d *Downloader) SyncStakingInfo(id string, from, to uint64) error { d.stakingInfoRecoveryBlocks = blockNums d.stakingInfoRecoveryTotal = len(blockNums) - d.stakingInfoRecoveryCh = make(chan []*reward.StakingInfo, 1) + d.stakingInfoRecoveryCh = make(chan []*staking.P2PStakingInfo, 1) go func() { defer func() { @@ -671,7 +670,7 @@ func (d *Downloader) SyncStakingInfo(id string, from, to uint64) error { logger.Error("failed to receive expected block", "expected", d.stakingInfoRecoveryBlocks[0], "actual", stakingInfo.BlockNum) return } - d.stakingModule.PutStakingInfoToDB(stakingInfo.BlockNum, reward.ToKaiax(stakingInfo)) + d.stakingModule.PutStakingInfoToDB(stakingInfo.BlockNum, staking.ToStakingInfo(stakingInfo)) fixed++ d.stakingInfoRecoveryBlocks = d.stakingInfoRecoveryBlocks[1:] } @@ -1881,7 +1880,7 @@ func (d *Downloader) commitFastSyncData(results []*fetchResult, stateSync *state blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions) receipts[i] = result.Receipts if result.StakingInfo != nil { - d.stakingModule.PutStakingInfoToDB(result.StakingInfo.BlockNum, reward.ToKaiax(result.StakingInfo)) + d.stakingModule.PutStakingInfoToDB(result.StakingInfo.BlockNum, staking.ToStakingInfo(result.StakingInfo)) logger.Info("Imported new staking information", "number", result.StakingInfo.BlockNum) } } @@ -1896,7 +1895,7 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error { block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions) logger.Debug("Committing fast sync pivot as new head", "number", block.Number(), "hash", block.Hash()) if result.StakingInfo != nil { - d.stakingModule.PutStakingInfoToDB(result.StakingInfo.BlockNum, reward.ToKaiax(result.StakingInfo)) + d.stakingModule.PutStakingInfoToDB(result.StakingInfo.BlockNum, staking.ToStakingInfo(result.StakingInfo)) logger.Info("Imported new staking information on pivot block", "number", result.StakingInfo.BlockNum, "pivot", block.Number()) } if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}); err != nil { @@ -1935,7 +1934,7 @@ func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) (er } // DeliverStakingInfos injects a new batch of staking information received from a remote node. -func (d *Downloader) DeliverStakingInfos(id string, stakingInfos []*reward.StakingInfo) error { +func (d *Downloader) DeliverStakingInfos(id string, stakingInfos []*staking.P2PStakingInfo) error { if d.isStakingInfoRecovery { d.stakingInfoRecoveryCh <- stakingInfos } diff --git a/datasync/downloader/downloader_fake.go b/datasync/downloader/downloader_fake.go index 32f30ac45..b3570397a 100644 --- a/datasync/downloader/downloader_fake.go +++ b/datasync/downloader/downloader_fake.go @@ -24,9 +24,9 @@ import ( "github.com/kaiachain/kaia" "github.com/kaiachain/kaia/blockchain/types" "github.com/kaiachain/kaia/common" + "github.com/kaiachain/kaia/kaiax/staking" "github.com/kaiachain/kaia/node/cn/snap" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" ) // fakeDownloader do nothing @@ -46,7 +46,7 @@ func (*FakeDownloader) DeliverBodies(id string, transactions [][]*types.Transact func (*FakeDownloader) DeliverHeaders(id string, headers []*types.Header) error { return nil } func (*FakeDownloader) DeliverNodeData(id string, data [][]byte) error { return nil } func (*FakeDownloader) DeliverReceipts(id string, receipts [][]*types.Receipt) error { return nil } -func (*FakeDownloader) DeliverStakingInfos(id string, stakingInfos []*reward.StakingInfo) error { +func (*FakeDownloader) DeliverStakingInfos(id string, stakingInfos []*staking.P2PStakingInfo) error { return nil } diff --git a/datasync/downloader/downloader_test.go b/datasync/downloader/downloader_test.go index df4a0f15f..6309bef56 100644 --- a/datasync/downloader/downloader_test.go +++ b/datasync/downloader/downloader_test.go @@ -45,7 +45,6 @@ import ( "github.com/kaiachain/kaia/kaiax/staking/mock" "github.com/kaiachain/kaia/log" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" "github.com/kaiachain/kaia/snapshot" "github.com/kaiachain/kaia/storage/database" "github.com/kaiachain/kaia/storage/statedb" @@ -106,19 +105,19 @@ type downloadTester struct { stateDb database.DBManager // Database used by the tester for syncing from peers peerDb database.DBManager // Database of the peers containing all data - ownHashes []common.Hash // Hash chain belonging to the tester - ownHeaders map[common.Hash]*types.Header // Headers belonging to the tester - ownBlocks map[common.Hash]*types.Block // Blocks belonging to the tester - ownReceipts map[common.Hash]types.Receipts // Receipts belonging to the tester - ownStakingInfo map[common.Hash]*reward.StakingInfo // Staking info belonging to the tester - ownChainTd map[common.Hash]*big.Int // Total difficulties of the blocks in the local chain + ownHashes []common.Hash // Hash chain belonging to the tester + ownHeaders map[common.Hash]*types.Header // Headers belonging to the tester + ownBlocks map[common.Hash]*types.Block // Blocks belonging to the tester + ownReceipts map[common.Hash]types.Receipts // Receipts belonging to the tester + ownStakingInfo map[common.Hash]*staking.P2PStakingInfo // Staking info belonging to the tester + ownChainTd map[common.Hash]*big.Int // Total difficulties of the blocks in the local chain - peerHashes map[string][]common.Hash // Hash chain belonging to different test peers - peerHeaders map[string]map[common.Hash]*types.Header // Headers belonging to different test peers - peerBlocks map[string]map[common.Hash]*types.Block // Blocks belonging to different test peers - peerReceipts map[string]map[common.Hash]types.Receipts // Receipts belonging to different test peers - peerStakingInfos map[string]map[common.Hash]*reward.StakingInfo // StakingInfo belonging to different test peers - peerChainTds map[string]map[common.Hash]*big.Int // Total difficulties of the blocks in the peer chains + peerHashes map[string][]common.Hash // Hash chain belonging to different test peers + peerHeaders map[string]map[common.Hash]*types.Header // Headers belonging to different test peers + peerBlocks map[string]map[common.Hash]*types.Block // Blocks belonging to different test peers + peerReceipts map[string]map[common.Hash]types.Receipts // Receipts belonging to different test peers + peerStakingInfos map[string]map[common.Hash]*staking.P2PStakingInfo // StakingInfo belonging to different test peers + peerChainTds map[string]map[common.Hash]*big.Int // Total difficulties of the blocks in the peer chains peerMissingStates map[string]map[common.Hash]bool // State entries that fast sync should not return @@ -141,13 +140,13 @@ func newTester(t *testing.T) *downloadTester { ownHeaders: map[common.Hash]*types.Header{genesis.Hash(): genesis.Header()}, ownBlocks: map[common.Hash]*types.Block{genesis.Hash(): genesis}, ownReceipts: map[common.Hash]types.Receipts{genesis.Hash(): nil}, - ownStakingInfo: map[common.Hash]*reward.StakingInfo{}, + ownStakingInfo: map[common.Hash]*staking.P2PStakingInfo{}, ownChainTd: map[common.Hash]*big.Int{genesis.Hash(): genesis.BlockScore()}, peerHashes: make(map[string][]common.Hash), peerHeaders: make(map[string]map[common.Hash]*types.Header), peerBlocks: make(map[string]map[common.Hash]*types.Block), peerReceipts: make(map[string]map[common.Hash]types.Receipts), - peerStakingInfos: make(map[string]map[common.Hash]*reward.StakingInfo), + peerStakingInfos: make(map[string]map[common.Hash]*staking.P2PStakingInfo), peerChainTds: make(map[string]map[common.Hash]*big.Int), peerMissingStates: make(map[string]map[common.Hash]bool), } @@ -168,10 +167,10 @@ func newTester(t *testing.T) *downloadTester { return tester } -func (dl *downloadTester) makeStakingInfoData(blockNumber uint64) (*reward.StakingInfo, []byte) { +func (dl *downloadTester) makeStakingInfoData(blockNumber uint64) (*staking.P2PStakingInfo, []byte) { k, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(k.PublicKey) - si := &reward.StakingInfo{ + si := &staking.P2PStakingInfo{ BlockNum: blockNumber, KEFAddr: addr, // assign KEF in order to put unique staking information } @@ -184,8 +183,8 @@ func (dl *downloadTester) makeStakingInfoData(blockNumber uint64) (*reward.Staki // contains a transaction to allow testing correct block reassembly. // On every 4th block, staking information is updated to allow testing staking info // downloading as well. -func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts, map[common.Hash]*reward.StakingInfo) { - stakingUpdatedBlocks := make(map[uint64]*reward.StakingInfo) +func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts, map[common.Hash]*staking.P2PStakingInfo) { + stakingUpdatedBlocks := make(map[uint64]*staking.P2PStakingInfo) // Generate the block chain blocks, receipts := blockchain.GenerateChain(params.TestChainConfig, parent, gxhash.NewFaker(), dl.peerDb, n, func(i int, block *blockchain.BlockGen) { block.SetRewardbase(common.Address{seed}) @@ -207,7 +206,7 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren if blockNum%testStakingUpdateInterval == 0 { si, _ := dl.makeStakingInfoData(blockNum) stakingUpdatedBlocks[blockNum] = si - staking_impl.WriteStakingInfo(dl.peerDb.GetMiscDB(), blockNum, reward.ToKaiax(si)) + staking_impl.WriteStakingInfo(dl.peerDb.GetMiscDB(), blockNum, staking.ToStakingInfo(si)) } }) // Convert the block-chain into a hash-chain and header/block maps @@ -223,11 +222,11 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren receiptm := make(map[common.Hash]types.Receipts, n+1) receiptm[parent.Hash()] = parentReceipts - stakingInfom := make(map[common.Hash]*reward.StakingInfo) + stakingInfom := make(map[common.Hash]*staking.P2PStakingInfo) if parent.NumberU64()%testStakingUpdateInterval == 0 { si, _ := dl.makeStakingInfoData(parent.NumberU64()) stakingInfom[parent.Hash()] = si - staking_impl.WriteStakingInfo(dl.peerDb.GetMiscDB(), parent.NumberU64(), reward.ToKaiax(si)) + staking_impl.WriteStakingInfo(dl.peerDb.GetMiscDB(), parent.NumberU64(), staking.ToStakingInfo(si)) } for i, b := range blocks { @@ -244,7 +243,7 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren // makeChainFork creates two chains of length n, such that h1[:f] and // h2[:f] are different but have a common suffix of length n-f. -func (dl *downloadTester) makeChainFork(n, f int, parent *types.Block, parentReceipts types.Receipts, balanced bool) ([]common.Hash, []common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]*types.Block, map[common.Hash]types.Receipts, map[common.Hash]types.Receipts, map[common.Hash]*reward.StakingInfo, map[common.Hash]*reward.StakingInfo) { +func (dl *downloadTester) makeChainFork(n, f int, parent *types.Block, parentReceipts types.Receipts, balanced bool) ([]common.Hash, []common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]*types.Block, map[common.Hash]types.Receipts, map[common.Hash]types.Receipts, map[common.Hash]*staking.P2PStakingInfo, map[common.Hash]*staking.P2PStakingInfo) { // Create the common suffix hashes, headers, blocks, receipts, stakingInfos := dl.makeChain(n-f, 0, parent, parentReceipts, false) @@ -478,7 +477,7 @@ func (dl *downloadTester) InsertReceiptChain(blocks types.Blocks, receipts []typ si := staking_impl.ReadStakingInfo(dl.peerDb.GetMiscDB(), blocks[i].NumberU64()) if si != nil { - dl.ownStakingInfo[blocks[i].Hash()] = reward.FromKaiax(si) + dl.ownStakingInfo[blocks[i].Hash()] = staking.FromStakingInfo(si) } } return len(blocks), nil @@ -507,14 +506,14 @@ func (dl *downloadTester) Rollback(hashes []common.Hash) { } // newPeer registers a new block download source into the downloader. -func (dl *downloadTester) newPeer(id string, version int, hashes []common.Hash, headers map[common.Hash]*types.Header, blocks map[common.Hash]*types.Block, receipts map[common.Hash]types.Receipts, stakingInfos map[common.Hash]*reward.StakingInfo) error { +func (dl *downloadTester) newPeer(id string, version int, hashes []common.Hash, headers map[common.Hash]*types.Header, blocks map[common.Hash]*types.Block, receipts map[common.Hash]types.Receipts, stakingInfos map[common.Hash]*staking.P2PStakingInfo) error { return dl.newSlowPeer(id, version, hashes, headers, blocks, receipts, stakingInfos, 0) } // newSlowPeer registers a new block download source into the downloader, with a // specific delay time on processing the network packets sent to it, simulating // potentially slow network IO. -func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Hash, headers map[common.Hash]*types.Header, blocks map[common.Hash]*types.Block, receipts map[common.Hash]types.Receipts, stakingInfos map[common.Hash]*reward.StakingInfo, delay time.Duration) error { +func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Hash, headers map[common.Hash]*types.Header, blocks map[common.Hash]*types.Block, receipts map[common.Hash]types.Receipts, stakingInfos map[common.Hash]*staking.P2PStakingInfo, delay time.Duration) error { dl.lock.Lock() defer dl.lock.Unlock() @@ -527,7 +526,7 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha dl.peerHeaders[id] = make(map[common.Hash]*types.Header) dl.peerBlocks[id] = make(map[common.Hash]*types.Block) dl.peerReceipts[id] = make(map[common.Hash]types.Receipts) - dl.peerStakingInfos[id] = make(map[common.Hash]*reward.StakingInfo) + dl.peerStakingInfos[id] = make(map[common.Hash]*staking.P2PStakingInfo) dl.peerChainTds[id] = make(map[common.Hash]*big.Int) dl.peerMissingStates[id] = make(map[common.Hash]bool) @@ -726,7 +725,7 @@ func (dlp *downloadTesterPeer) RequestStakingInfo(hashes []common.Hash) error { stakingInfos := dlp.dl.peerStakingInfos[dlp.id] - results := []*reward.StakingInfo{} + results := []*staking.P2PStakingInfo{} for _, hash := range hashes { if stakingInfo, ok := stakingInfos[hash]; ok { @@ -1951,7 +1950,7 @@ func testStakingInfoSync(t *testing.T, protocol int) { time.Sleep(3 * time.Second) for _, stakingInfo := range stakingInfos { - expected, _ := json.Marshal(reward.ToKaiax(stakingInfo)) + expected, _ := json.Marshal(staking.ToStakingInfo(stakingInfo)) si := staking_impl.ReadStakingInfo(tester.stateDb.GetMiscDB(), stakingInfo.BlockNum) if si == nil { t.Errorf("failed to read stakingInfo") diff --git a/datasync/downloader/queue.go b/datasync/downloader/queue.go index 45ea2d25b..1d2d95014 100644 --- a/datasync/downloader/queue.go +++ b/datasync/downloader/queue.go @@ -34,9 +34,9 @@ import ( "github.com/kaiachain/kaia/common" "github.com/kaiachain/kaia/common/prque" "github.com/kaiachain/kaia/consensus/istanbul" + "github.com/kaiachain/kaia/kaiax/staking" kaiametrics "github.com/kaiachain/kaia/metrics" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" "github.com/rcrowley/go-metrics" ) @@ -74,7 +74,7 @@ type fetchResult struct { Header *types.Header Transactions types.Transactions Receipts types.Receipts - StakingInfo *reward.StakingInfo + StakingInfo *staking.P2PStakingInfo } func newFetchResult(header *types.Header, mode SyncMode, proposerPolicy uint64, isKaiaFork bool) *fetchResult { @@ -909,7 +909,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, // DeliverStakingInfos injects a stakinginfo retrieval response into the results queue. // The method returns the number of staking information accepted from the delivery // and also wakes any threads waiting for data delivery. -func (q *queue) DeliverStakingInfos(id string, stakingInfoList []*reward.StakingInfo) (int, error) { +func (q *queue) DeliverStakingInfos(id string, stakingInfoList []*staking.P2PStakingInfo) (int, error) { q.lock.Lock() defer q.lock.Unlock() validate := func(index int, header *types.Header) error { diff --git a/datasync/downloader/queue_test.go b/datasync/downloader/queue_test.go index 5a236a785..90d7e21ad 100644 --- a/datasync/downloader/queue_test.go +++ b/datasync/downloader/queue_test.go @@ -35,9 +35,9 @@ import ( "github.com/kaiachain/kaia/common" "github.com/kaiachain/kaia/consensus/gxhash" "github.com/kaiachain/kaia/consensus/istanbul" + "github.com/kaiachain/kaia/kaiax/staking" "github.com/kaiachain/kaia/log" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" "github.com/kaiachain/kaia/storage/database" ) @@ -67,7 +67,7 @@ func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Bloc type chainData struct { blocks []*types.Block - stakingInfos []*reward.StakingInfo + stakingInfos []*staking.P2PStakingInfo offset int } @@ -81,9 +81,9 @@ func init() { // Create a chain of blocks to import. 128 blocks are created and a transaction is contained on every 2nd block targetBlocks := 128 - var stakingInfos []*reward.StakingInfo + var stakingInfos []*staking.P2PStakingInfo for i := 4; i <= 128; i += 4 { - stakingInfos = append(stakingInfos, &reward.StakingInfo{BlockNum: uint64(i)}) + stakingInfos = append(stakingInfos, &staking.P2PStakingInfo{BlockNum: uint64(i)}) } blocks, _ := makeChain(targetBlocks, 0, genesis, false) diff --git a/datasync/downloader/types.go b/datasync/downloader/types.go index 2c2e5f692..afaf338d9 100644 --- a/datasync/downloader/types.go +++ b/datasync/downloader/types.go @@ -26,7 +26,7 @@ import ( "fmt" "github.com/kaiachain/kaia/blockchain/types" - "github.com/kaiachain/kaia/reward" + "github.com/kaiachain/kaia/kaiax/staking" ) // peerDropFn is a callback type for dropping a peer detected as malicious. @@ -83,7 +83,7 @@ func (p *statePack) Stats() string { return fmt.Sprintf("%d", len(p.states)) } type stakingInfoPack struct { peerId string - stakingInfos []*reward.StakingInfo + stakingInfos []*staking.P2PStakingInfo } func (p *stakingInfoPack) PeerId() string { return p.peerId } diff --git a/reward/staking_info.go b/kaiax/staking/p2p_staking_info.go similarity index 53% rename from reward/staking_info.go rename to kaiax/staking/p2p_staking_info.go index a3f57067e..fcb99a9a6 100644 --- a/reward/staking_info.go +++ b/kaiax/staking/p2p_staking_info.go @@ -16,32 +16,19 @@ // along with the klaytn library. If not, see . // Modified and improved for the Kaia development. -package reward +package staking import ( "encoding/json" - "errors" "io" "math" - "sort" "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/kaiax/staking" "github.com/kaiachain/kaia/rlp" ) -const ( - AddrNotFoundInCouncilNodes = -1 - maxStakingLimit = uint64(100000000000) - DefaultGiniCoefficient = -1.0 -) - -var ( - ErrAddrNotInStakingInfo = errors.New("Address is not in stakingInfo") -) - -// StakingInfo contains staking information. -type StakingInfo struct { +// P2PStakingInfo contains staking information which is a wrapped version of StakingInfo. +type P2PStakingInfo struct { BlockNum uint64 `json:"blockNum"` // Block number where staking information of Council is fetched // Information retrieved from AddressBook smart contract @@ -52,15 +39,15 @@ type StakingInfo struct { KEFAddr common.Address `json:"kefAddr"` // Address of KEF contract KIFAddr common.Address `json:"kifAddr"` // Address of KIF contract - UseGini bool `json:"useGini"` - Gini float64 `json:"gini"` // gini coefficient + UseGini bool `json:"useGini"` // configure whether Gini is used or not + Gini float64 `json:"gini"` // gini coefficient // Derived from CouncilStakingAddrs CouncilStakingAmounts []uint64 `json:"councilStakingAmounts"` // Staking amounts of Council } -func FromKaiax(si *staking.StakingInfo) *StakingInfo { - return &StakingInfo{ +func FromStakingInfo(si *StakingInfo) *P2PStakingInfo { + return &P2PStakingInfo{ BlockNum: si.SourceBlockNum, CouncilNodeAddrs: si.NodeIds, CouncilStakingAddrs: si.StakingContracts, @@ -71,8 +58,8 @@ func FromKaiax(si *staking.StakingInfo) *StakingInfo { } } -func FromKaiaxWithGini(si *staking.StakingInfo, useGini bool, minStake uint64) *StakingInfo { - return &StakingInfo{ +func FromStakingInfoWithGini(si *StakingInfo, useGini bool, minStake uint64) *P2PStakingInfo { + return &P2PStakingInfo{ BlockNum: si.SourceBlockNum, CouncilNodeAddrs: si.NodeIds, CouncilStakingAddrs: si.StakingContracts, @@ -85,8 +72,8 @@ func FromKaiaxWithGini(si *staking.StakingInfo, useGini bool, minStake uint64) * } } -func ToKaiax(si *StakingInfo) *staking.StakingInfo { - return &staking.StakingInfo{ +func ToStakingInfo(si *P2PStakingInfo) *StakingInfo { + return &StakingInfo{ SourceBlockNum: si.BlockNum, NodeIds: si.CouncilNodeAddrs, StakingContracts: si.CouncilStakingAddrs, @@ -97,9 +84,9 @@ func ToKaiax(si *StakingInfo) *staking.StakingInfo { } } -// MarshalJSON supports json marshalling for both oldStakingInfo and StakingInfo +// MarshalJSON supports json marshalling for both P2PStakingInfo and StakingInfo // TODO-Kaia-Mantle: remove this marshal function when backward-compatibility for KIR/PoC, KCF/KFF is not needed -func (st StakingInfo) MarshalJSON() ([]byte, error) { +func (st P2PStakingInfo) MarshalJSON() ([]byte, error) { type extendedSt struct { BlockNum uint64 `json:"blockNum"` CouncilNodeAddrs []common.Address `json:"councilNodeAddrs"` @@ -138,8 +125,8 @@ func (st StakingInfo) MarshalJSON() ([]byte, error) { return json.Marshal(&ext) } -// UnmarshalJSON supports json unmarshalling for both oldStakingInfo and StakingInfo -func (st *StakingInfo) UnmarshalJSON(input []byte) error { +// UnmarshalJSON supports json unmarshalling for both P2PStakingInfo and StakingInfo +func (st *P2PStakingInfo) UnmarshalJSON(input []byte) error { type extendedSt struct { BlockNum uint64 `json:"blockNum"` CouncilNodeAddrs []common.Address `json:"councilNodeAddrs"` @@ -191,35 +178,7 @@ func (st *StakingInfo) UnmarshalJSON(input []byte) error { return nil } -// Refined staking information suitable for proposer selection. -// Sometimes a node would register multiple NodeAddrs -// in which each entry has different StakingAddr and same RewardAddr. -// We treat those entries with common RewardAddr as one node. -// -// For example, -// -// NodeAddrs = [N1, N2, N3] -// StakingAddrs = [S1, S2, S3] -// RewardAddrs = [R1, R1, R3] -// StakingAmounts = [A1, A2, A3] -// -// can be consolidated into -// -// CN1 = {[N1,N2], [S1,S2], R1, A1+A2} -// CN3 = {[N3], [S3], R3, A3} -type consolidatedNode struct { - NodeAddrs []common.Address - StakingAddrs []common.Address - RewardAddr common.Address // common reward address - StakingAmount uint64 // sum of staking amounts -} - -type ConsolidatedStakingInfo struct { - nodes []consolidatedNode - nodeIndex map[common.Address]int // nodeAddr -> index in []nodes -} - -type stakingInfoRLP struct { +type p2pStakingInfoRLP struct { BlockNum uint64 CouncilNodeAddrs []common.Address CouncilStakingAddrs []common.Address @@ -231,39 +190,7 @@ type stakingInfoRLP struct { CouncilStakingAmounts []uint64 } -func newEmptyStakingInfo(blockNum uint64) *StakingInfo { - stakingInfo := &StakingInfo{ - BlockNum: blockNum, - CouncilNodeAddrs: make([]common.Address, 0, 0), - CouncilStakingAddrs: make([]common.Address, 0, 0), - CouncilRewardAddrs: make([]common.Address, 0, 0), - KEFAddr: common.Address{}, - KIFAddr: common.Address{}, - CouncilStakingAmounts: make([]uint64, 0, 0), - Gini: DefaultGiniCoefficient, - UseGini: false, - } - return stakingInfo -} - -func (s *StakingInfo) GetIndexByNodeAddress(nodeAddress common.Address) (int, error) { - for i, addr := range s.CouncilNodeAddrs { - if addr == nodeAddress { - return i, nil - } - } - return AddrNotFoundInCouncilNodes, ErrAddrNotInStakingInfo -} - -func (s *StakingInfo) GetStakingAmountByNodeId(nodeAddress common.Address) (uint64, error) { - i, err := s.GetIndexByNodeAddress(nodeAddress) - if err != nil { - return 0, err - } - return s.CouncilStakingAmounts[i], nil -} - -func (s *StakingInfo) String() string { +func (s *P2PStakingInfo) String() string { j, err := json.Marshal(s) if err != nil { return err.Error() @@ -271,13 +198,13 @@ func (s *StakingInfo) String() string { return string(j) } -func (s *StakingInfo) EncodeRLP(w io.Writer) error { +func (s *P2PStakingInfo) EncodeRLP(w io.Writer) error { // float64 is not rlp serializable, so it converts to bytes - return rlp.Encode(w, &stakingInfoRLP{s.BlockNum, s.CouncilNodeAddrs, s.CouncilStakingAddrs, s.CouncilRewardAddrs, s.KEFAddr, s.KIFAddr, s.UseGini, math.Float64bits(s.Gini), s.CouncilStakingAmounts}) + return rlp.Encode(w, &p2pStakingInfoRLP{s.BlockNum, s.CouncilNodeAddrs, s.CouncilStakingAddrs, s.CouncilRewardAddrs, s.KEFAddr, s.KIFAddr, s.UseGini, math.Float64bits(s.Gini), s.CouncilStakingAmounts}) } -func (s *StakingInfo) DecodeRLP(st *rlp.Stream) error { - var dec stakingInfoRLP +func (s *P2PStakingInfo) DecodeRLP(st *rlp.Stream) error { + var dec p2pStakingInfoRLP if err := st.Decode(&dec); err != nil { return err } @@ -287,98 +214,3 @@ func (s *StakingInfo) DecodeRLP(st *rlp.Stream) error { s.CouncilStakingAmounts = dec.CouncilStakingAmounts return nil } - -func (s *StakingInfo) GetConsolidatedStakingInfo() *ConsolidatedStakingInfo { - c := &ConsolidatedStakingInfo{ - nodes: make([]consolidatedNode, 0), - nodeIndex: make(map[common.Address]int), - } - - rewardIndex := make(map[common.Address]int) // temporarily map rewardAddr -> index in []nodes - - for j := 0; j < len(s.CouncilNodeAddrs); j++ { - var ( - nodeAddr = s.CouncilNodeAddrs[j] - stakingAddr = s.CouncilStakingAddrs[j] - rewardAddr = s.CouncilRewardAddrs[j] - stakingAmount = s.CouncilStakingAmounts[j] - ) - if idx, ok := rewardIndex[rewardAddr]; !ok { - c.nodes = append(c.nodes, consolidatedNode{ - NodeAddrs: []common.Address{nodeAddr}, - StakingAddrs: []common.Address{stakingAddr}, - RewardAddr: rewardAddr, - StakingAmount: stakingAmount, - }) - c.nodeIndex[nodeAddr] = len(c.nodes) - 1 // point to new element - rewardIndex[rewardAddr] = len(c.nodes) - 1 - } else { - c.nodes[idx].NodeAddrs = append(c.nodes[idx].NodeAddrs, nodeAddr) - c.nodes[idx].StakingAddrs = append(c.nodes[idx].StakingAddrs, stakingAddr) - c.nodes[idx].StakingAmount += stakingAmount - c.nodeIndex[nodeAddr] = idx // point to existing element - } - } - return c -} - -func (c *ConsolidatedStakingInfo) GetAllNodes() []consolidatedNode { - return c.nodes -} - -func (c *ConsolidatedStakingInfo) GetConsolidatedNode(nodeAddr common.Address) *consolidatedNode { - if idx, ok := c.nodeIndex[nodeAddr]; ok { - return &c.nodes[idx] - } - return nil -} - -// Calculate Gini coefficient of the StakingAmounts. -// Only amounts greater or equal to `minStake` are included in the calculation. -// Set `minStake` to 0 to calculate Gini coefficient of all amounts. -func (c *ConsolidatedStakingInfo) CalcGiniCoefficientMinStake(minStake uint64) float64 { - var amounts []float64 - for _, node := range c.nodes { - if node.StakingAmount >= minStake { - amounts = append(amounts, float64(node.StakingAmount)) - } - } - - if len(amounts) == 0 { - return DefaultGiniCoefficient - } - return CalcGiniCoefficient(amounts) -} - -func (c *ConsolidatedStakingInfo) String() string { - j, err := json.Marshal(c.nodes) - if err != nil { - return err.Error() - } - return string(j) -} - -type float64Slice []float64 - -func (p float64Slice) Len() int { return len(p) } -func (p float64Slice) Less(i, j int) bool { return p[i] < p[j] } -func (p float64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -func CalcGiniCoefficient(stakingAmount float64Slice) float64 { - sort.Sort(stakingAmount) - - // calculate gini coefficient - sumOfAbsoluteDifferences := float64(0) - subSum := float64(0) - - for i, x := range stakingAmount { - temp := x*float64(i) - subSum - sumOfAbsoluteDifferences = sumOfAbsoluteDifferences + temp - subSum = subSum + x - } - - result := sumOfAbsoluteDifferences / subSum / float64(len(stakingAmount)) - result = math.Round(result*100) / 100 - - return result -} diff --git a/kaiax/staking/staking_info.go b/kaiax/staking/staking_info.go index d7bd46793..c491414b8 100644 --- a/kaiax/staking/staking_info.go +++ b/kaiax/staking/staking_info.go @@ -29,6 +29,7 @@ import ( var EmptyGini float64 = -1.0 // StakingInfo is the staking info to be used for block processing. +// Token Economy - https://docs.kaia.io/docs/learn/token-economy/ type StakingInfo struct { // The source block number where the staking info is captured. SourceBlockNum uint64 `json:"blockNum"` diff --git a/kaiax/supply/impl/testutil_test.go b/kaiax/supply/impl/testutil_test.go index d34d76313..98a6f0e79 100644 --- a/kaiax/supply/impl/testutil_test.go +++ b/kaiax/supply/impl/testutil_test.go @@ -293,8 +293,6 @@ func setupMockEngine(engine *consensus_mock.MockEngine, chain *blockchain.BlockC ).AnyTimes() engine.EXPECT().VerifyHeader(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - engine.EXPECT().CreateSnapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - engine.EXPECT().Finalize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, receipts []*types.Receipt) (*types.Block, error) { header.BlockScore = common.Big1 diff --git a/kaiax/valset/impl/getter_context_test.go b/kaiax/valset/impl/getter_context_test.go index 1be2a322f..4aa0bc378 100644 --- a/kaiax/valset/impl/getter_context_test.go +++ b/kaiax/valset/impl/getter_context_test.go @@ -286,3 +286,57 @@ func TestWeightedRandomProposer_Select(t *testing.T) { assert.Equal(t, tc.expected, currProposer) } } + +// TestCollectStakingAmounts checks if validators and stakingAmounts from a stakingInfo are matched well. +// stakingAmounts of multiple staking contracts will be added to stakingAmounts of validators which have the same reward address. +// input +// - validator and stakingInfo is matched by a nodeAddress. +// +// output +// - weightedValidators are sorted by nodeAddress +// - stakingAmounts should be same as expectedStakingAmounts +func TestCollectStakingAmounts(t *testing.T) { + uintMS, floatMS := uint64(5000000), float64(5000000) + testCases := []struct { + validators []common.Address + stakingInfo *staking.StakingInfo + expectedStakingAmounts []float64 + }{ + { + numsToAddrs(1, 2, 3), + &staking.StakingInfo{ + NodeIds: numsToAddrs(1, 2, 3), + StakingContracts: numsToAddrs(1, 2, 3), + RewardAddrs: numsToAddrs(4, 5, 6), + StakingAmounts: []uint64{2 * uintMS, uintMS, uintMS}, + }, + []float64{2 * floatMS, floatMS, floatMS}, + }, + { + numsToAddrs(1, 2, 3, 4), + &staking.StakingInfo{ + NodeIds: numsToAddrs(1, 2, 3, 4, 5), + StakingContracts: numsToAddrs(1, 2, 3, 4, 5), + RewardAddrs: numsToAddrs(6, 7, 8, 9, 6), + StakingAmounts: []uint64{uintMS, uintMS, uintMS, uintMS, uintMS}, + }, + []float64{2 * floatMS, floatMS, floatMS, floatMS}, + }, + { + numsToAddrs(1, 2, 3, 4), + &staking.StakingInfo{ + NodeIds: numsToAddrs(1, 2, 3, 4, 5, 6), + StakingContracts: numsToAddrs(1, 2, 3, 4, 5, 6), + RewardAddrs: numsToAddrs(7, 8, 9, 10, 7, 8), + StakingAmounts: []uint64{uintMS, uintMS, uintMS, uintMS, uintMS, uintMS}, + }, + []float64{2 * floatMS, 2 * floatMS, floatMS, floatMS}, + }, + } + for _, tc := range testCases { + stakingAmounts := collectStakingAmounts(tc.validators, tc.stakingInfo) + for idx, nodeId := range tc.validators { + assert.Equal(t, stakingAmounts[nodeId], tc.expectedStakingAmounts[idx]) + } + } +} diff --git a/kaiax/valset/impl/getter_proposers_test.go b/kaiax/valset/impl/getter_proposers_test.go index dcd516fc1..f6ebe14d2 100644 --- a/kaiax/valset/impl/getter_proposers_test.go +++ b/kaiax/valset/impl/getter_proposers_test.go @@ -200,6 +200,27 @@ func TestWeightedRandomProposer_ListWeighted(t *testing.T) { useGini: false, expectedFreq: []int{1, 1, 1, 99}, // at least 1 slot is guaranteed for each validator. }, + { + desc: "identical stakingAmounts", + qualified: numsToAddrs(0, 1, 2), + amounts: []uint64{aM, aM, aM}, + useGini: true, + expectedFreq: []int{33, 33, 33}, + }, + { + desc: "multiple of minimum staking", + qualified: numsToAddrs(0, 1, 2, 3), + amounts: []uint64{aM, 2 * aM, aM, aM}, + useGini: true, + expectedFreq: []int{21, 38, 21, 21}, + }, + { + desc: "non-multiple of minimum staking", + qualified: numsToAddrs(0, 1, 2, 3, 4), + amounts: []uint64{324946, 560845, 771786, 967997, 1153934}, + useGini: true, + expectedFreq: []int{10, 16, 21, 25, 29}, + }, } for _, tc := range testcases { qualified := valset.NewAddressSet(tc.qualified) diff --git a/networks/p2p/discover/discover_storage_simple.go b/networks/p2p/discover/discover_storage_simple.go index 5b5cd76b5..cbc9d9a1d 100644 --- a/networks/p2p/discover/discover_storage_simple.go +++ b/networks/p2p/discover/discover_storage_simple.go @@ -222,8 +222,11 @@ func (s *simpleStorage) closest(target common.Hash, nresults int) *nodesByDistan // in the SimpleStorage. Change it cNodes := &nodesByDistance{target: target} nodes := s.shuffle(s.nodes) - if len(nodes) > s.max { - cNodes.entries = nodes[:s.max] + // TODO-Kaia We should cap the number of nodes with nresults, however we cannot estimate the side effect. + // As s.nodes can be longer than s.max, we cap the number of nodes returned with min(nresults, s.max). + cap := min(nresults, s.max) + if len(nodes) > cap { + cNodes.entries = nodes[:cap] } else { cNodes.entries = nodes } @@ -252,7 +255,10 @@ func (s *simpleStorage) bumpOrAdd(n *Node) bool { } s.localLogger.Trace("Add(New)", "StorageName", s.name(), "node", n) - s.nodes, _ = pushNode(s.nodes, n, math.MaxInt64) // TODO-Kaia-Node Change Max value for more reasonable one. + // TODO-Kaia-Node Change Max value for more reasonable one. + // As we cannot estimate the side effect, we leave s.nodes to be longer than s.max. + // s.max has been used to cap the number of nodes returned by `closest` function. + s.nodes, _ = pushNode(s.nodes, n, math.MaxInt64) n.addedAt = time.Now() if s.tab.nodeAddedHook != nil { s.tab.nodeAddedHook(n) diff --git a/networks/p2p/discover/discover_storage_simple_test.go b/networks/p2p/discover/discover_storage_simple_test.go index 158613397..691ba7af2 100644 --- a/networks/p2p/discover/discover_storage_simple_test.go +++ b/networks/p2p/discover/discover_storage_simple_test.go @@ -20,13 +20,13 @@ package discover import ( "crypto/rand" - rand2 "math/rand" "net" "testing" "github.com/kaiachain/kaia/common" "github.com/kaiachain/kaia/crypto" "github.com/pkg/errors" + "gotest.tools/assert" ) var ( @@ -293,31 +293,28 @@ func TestSimple_getNodes(t *testing.T) { } func TestSimple_closest(t *testing.T) { - typeList := []NodeType{NodeTypeUnknown, NodeTypeCN, NodeTypePN, NodeTypeEN} + typeList := []NodeType{NodeTypeUnknown, NodeTypeCN, NodeTypePN, NodeTypeEN, NodeTypeBN} + retrieveSizeList := []int{2, 16, 100, 120} + maxSizeList := []int{1, 2, 3, 5, 50, 100} for _, ntype := range typeList { var target NodeID rand.Read(target[:]) - hash := crypto.Keccak256Hash(target[:]) // randome target hash + hash := crypto.Keccak256Hash(target[:]) // random target hash storage := testStorages[ntype] storage.init() - storage.max = 5 - results := storage.closest(hash, rand2.Int()) // second parameter is not used - if len(results.entries) != 5 { - t.Errorf("the length of the results.entries is wrong. expected: %v, actual: %v", 5, len(results.entries)) - } - for _, node := range results.entries { - if !isIn(node, storage.nodes) { - t.Errorf("node does not exist in the storage. unknown node: %v", node) - } - } - storage.max = 50 - results = storage.closest(hash, rand2.Int()) - if len(results.entries) != 10 { - t.Errorf("the length of the results.entries is wrong. expected: %v, actual: %v", 10, len(results.entries)) - } - for _, node := range results.entries { - if !isIn(node, storage.nodes) { - t.Errorf("node does not exist in the storage. unknown node: %v", node) + + for _, maxSize := range maxSizeList { + storage.max = maxSize + for _, retrieveSize := range retrieveSizeList { + results := storage.closest(hash, retrieveSize) + // Result should be capped by minimum of: + // - retrieveSize + // - maxSize + // - length of nodes in storage + assert.Equal(t, len(results.entries), min(min(retrieveSize, maxSize), len(testStorages[ntype].nodes))) + for _, node := range results.entries { + assert.Assert(t, isIn(node, storage.nodes)) + } } } } diff --git a/networks/p2p/discover/udp.go b/networks/p2p/discover/udp.go index 9ce4a3d8a..9e5bef4a3 100644 --- a/networks/p2p/discover/udp.go +++ b/networks/p2p/discover/udp.go @@ -775,6 +775,10 @@ func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byt func (req *neighbors) name() string { return "NEIGHBORS/v4" } func findnodeRetrieveSize(nType NodeType) int { + // Returning too small value will make CNs unable to find each other. + if nType == NodeTypeCN { + return 100 + } // Return at most 2 PNs. // 1. Under current CN-PN-EN 3-tier operating practices, findnode(type=PN) packet originates only from EN. // CNs only connect to other CNs via CNBN. PNs are connected to PNs and CNs via static-nodes.json. diff --git a/node/cn/api_backend.go b/node/cn/api_backend.go index aa97dedf5..4beb8a1a6 100644 --- a/node/cn/api_backend.go +++ b/node/cn/api_backend.go @@ -84,8 +84,8 @@ func doSetHead(bc work.BlockChain, cn consensus.Engine, gpo *gasprice.Oracle, ta if err := bc.SetHead(targetBlkNum); err != nil { return err } - // Initialize snapshot cache, staking info cache, and governance cache - cn.InitSnapshot() + // Initialize staking info cache, and governance cache + cn.PurgeCache() gpo.PurgeCache() return nil } @@ -135,7 +135,7 @@ func (b *CNAPIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*typ if header := b.cn.blockchain.GetHeaderByHash(hash); header != nil { return header, nil } - return nil, fmt.Errorf("the header does not exist (hash: %d)", hash) + return nil, fmt.Errorf("the header does not exist (hash: %s)", hash.String()) } func (b *CNAPIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { @@ -365,6 +365,10 @@ func (b *CNAPIBackend) IsSenderTxHashIndexingEnabled() bool { return b.cn.BlockChain().IsSenderTxHashIndexingEnabled() } +func (b *CNAPIBackend) IsConsoleLogEnabled() bool { + return b.cn.config.UseConsoleLog +} + func (b *CNAPIBackend) RPCGasCap() *big.Int { return b.cn.config.RPCGasCap } diff --git a/node/cn/api_backend_test.go b/node/cn/api_backend_test.go index e11b32998..6fa15b3cf 100644 --- a/node/cn/api_backend_test.go +++ b/node/cn/api_backend_test.go @@ -19,7 +19,6 @@ package cn import ( - "encoding/json" "math/big" "testing" @@ -32,7 +31,6 @@ import ( "github.com/kaiachain/kaia/common/hexutil" "github.com/kaiachain/kaia/consensus" "github.com/kaiachain/kaia/consensus/gxhash" - "github.com/kaiachain/kaia/consensus/istanbul/backend" mocks3 "github.com/kaiachain/kaia/event/mocks" headergov_impl "github.com/kaiachain/kaia/kaiax/gov/headergov/impl" gov_impl "github.com/kaiachain/kaia/kaiax/gov/impl" @@ -789,14 +787,6 @@ func headerGovSetHeadTest(t *testing.T, tt *rewindTest) { t.Fatalf("Failed to import canonical chain start: %v", err) } - // Store snapshot - snap := backend.Snapshot{Number: params.CheckpointInterval, Hash: chain.GetHeaderByNumber(params.CheckpointInterval).Hash()} - blob, err := json.Marshal(snap) - assert.Nil(t, err) - db.WriteIstanbulSnapshot(snap.Hash, blob) - _, err = db.ReadIstanbulSnapshot(snap.Hash) - assert.Nil(t, err) - // Before setHead assert.Equal(t, newMintingAmount, govModule.GetParamSet(appliedGovBlockNum).MintingAmount.String()) @@ -817,10 +807,6 @@ func headerGovSetHeadTest(t *testing.T, tt *rewindTest) { // governance db and cachelookup assert.Equal(t, oldMintingAmount, govModule.GetParamSet(appliedGovBlockNum).MintingAmount.String()) - // snapshot db lookup - _, err = db.ReadIstanbulSnapshot(snap.Hash) - assert.Equal(t, err.Error(), "data is not found with the given key") - for _, b := range canonblocks[tt.expCanonicalBlocks:] { if _, err := chain.InsertChain(types.Blocks{b}); err != nil { t.Fatalf("Failed to import canonical chain start: %v", err) diff --git a/node/cn/backend.go b/node/cn/backend.go index f480cdbe0..2a0006ec6 100644 --- a/node/cn/backend.go +++ b/node/cn/backend.go @@ -59,7 +59,6 @@ import ( "github.com/kaiachain/kaia/node/cn/gasprice" "github.com/kaiachain/kaia/node/cn/tracers" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" "github.com/kaiachain/kaia/rlp" "github.com/kaiachain/kaia/storage/database" "github.com/kaiachain/kaia/work" @@ -369,15 +368,6 @@ func New(ctx *node.ServiceContext, config *Config) (*CN, error) { } } - // Governance states which are not yet applied to the db remains at in-memory storage - // It disappears during the node restart, so restoration is needed before the sync starts - // By calling CreateSnapshot, it restores the gov state snapshots and apply the votes in it - // Particularly, the gov.changeSet is also restored here. - // Temporarily set chain since snapshot needs state since kaia hardfork - logger.Info("Start creating istanbul snapshot") - cn.createSnapshot() - logger.Info("Finished creating istanbul snapshot") - // set worker if config.WorkerDisable { cn.miner = work.NewFakeWorker() @@ -453,48 +443,6 @@ func New(ctx *node.ServiceContext, config *Config) (*CN, error) { return cn, nil } -func (s *CN) createSnapshot() { - var ( - currBlock = s.blockchain.CurrentBlock() - headers []*types.Header - err error - ) - // Temporarily supply blockchain for `Finalize`, staking module for `snapshot` and reward module for `processor`. - s.blockchain.Engine().(consensus.Istanbul).SetChain(s.blockchain) - mStaking := staking_impl.NewStakingModule() - mReward := reward_impl.NewRewardModule() - mStaking.Init(&staking_impl.InitOpts{ - ChainKv: s.chainDB.GetMiscDB(), - ChainConfig: s.chainConfig, - Chain: s.blockchain, - }) - mReward.Init(&reward_impl.InitOpts{ - ChainConfig: s.chainConfig, - Chain: s.blockchain, - GovModule: s.govModule, - StakingModule: mStaking, - }) - s.blockchain.Engine().(consensus.Istanbul).RegisterStakingModule(mStaking) - s.blockchain.Engine().(consensus.Istanbul).RegisterConsensusModule(mReward) - if headers, err = s.Engine().GetKaiaHeadersForSnapshotApply(s.blockchain, currBlock.NumberU64(), currBlock.Hash(), nil); err != nil { - logger.Error("Failed to get headers to apply", "err", err) - } else { - preloadRef, err := reward.PreloadStakingInfo(s.blockchain, headers, mStaking) - if err != nil { - logger.Error("Preload staking info failed", "err", err) - } - defer func() { - mStaking.FreePreloadRef(preloadRef) - }() - } - if err := s.Engine().CreateSnapshot(s.blockchain, currBlock.NumberU64(), currBlock.Hash(), headers); err != nil { - logger.Error("CreateSnapshot failed", "err", err) - } - s.blockchain.Engine().(consensus.Istanbul).SetChain(nil) - s.blockchain.Engine().(consensus.Istanbul).RegisterStakingModule(nil) - s.blockchain.Engine().(consensus.Istanbul).UnregisterConsensusModule(mReward) -} - // setAcceptTxs sets AcceptTxs flag in 1CN case to receive tx propagation. func (s *CN) setAcceptTxs() error { if s.chainConfig.Istanbul != nil { diff --git a/node/cn/config.go b/node/cn/config.go index 079950ff8..8161f9c3b 100644 --- a/node/cn/config.go +++ b/node/cn/config.go @@ -198,6 +198,9 @@ type Config struct { // Disable option for unsafe debug APIs DisableUnsafeDebug bool `toml:",omitempty"` StateRegenerationTimeLimit time.Duration `toml:",omitempty"` + + // Use console.log in solidity for local network + UseConsoleLog bool } type configMarshaling struct { @@ -209,5 +212,6 @@ func (c *Config) getVMConfig() vm.Config { EnablePreimageRecording: c.EnablePreimageRecording, EnableInternalTxTracing: c.EnableInternalTxTracing, EnableOpDebug: c.EnableOpDebug, + UseConsoleLog: c.UseConsoleLog, } } diff --git a/node/cn/handler.go b/node/cn/handler.go index 7d53b4b5f..780acfe7b 100644 --- a/node/cn/handler.go +++ b/node/cn/handler.go @@ -48,7 +48,6 @@ import ( "github.com/kaiachain/kaia/networks/p2p/discover" "github.com/kaiachain/kaia/node/cn/snap" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" "github.com/kaiachain/kaia/rlp" "github.com/kaiachain/kaia/storage/database" "github.com/kaiachain/kaia/storage/statedb" @@ -1054,19 +1053,19 @@ func handleStakingInfoRequestMsg(pm *ProtocolManager, p Peer, msg p2p.Msg) error continue } number := header.Number.Uint64() - var result *reward.StakingInfo + var result *staking.P2PStakingInfo if pm.chainconfig.IsKaiaForkEnabled(header.Number) { st, err := pm.stakingModule.GetStakingInfo(number) if st == nil || err != nil { continue } - result = reward.FromKaiaxWithGini(st, false, pm.chainconfig.Governance.Reward.MinimumStake.Uint64()) + result = staking.FromStakingInfoWithGini(st, false, pm.chainconfig.Governance.Reward.MinimumStake.Uint64()) } else { st := pm.stakingModule.GetStakingInfoFromDB(number) if st == nil { continue } - result = reward.FromKaiaxWithGini(st, pm.chainconfig.Governance.Reward.UseGiniCoeff, pm.chainconfig.Governance.Reward.MinimumStake.Uint64()) + result = staking.FromStakingInfoWithGini(st, pm.chainconfig.Governance.Reward.UseGiniCoeff, pm.chainconfig.Governance.Reward.MinimumStake.Uint64()) } // If known, encode and queue for response packet @@ -1088,7 +1087,7 @@ func handleStakingInfoMsg(pm *ProtocolManager, p Peer, msg p2p.Msg) error { } // A batch of stakingInfos arrived to one of our previous requests - var stakingInfos []*reward.StakingInfo + var stakingInfos []*staking.P2PStakingInfo if err := msg.Decode(&stakingInfos); err != nil { return errResp(ErrDecode, "msg %v: %v", msg, err) } diff --git a/node/cn/handler_msg_test.go b/node/cn/handler_msg_test.go index bb088e324..c6fea1e9f 100644 --- a/node/cn/handler_msg_test.go +++ b/node/cn/handler_msg_test.go @@ -34,7 +34,6 @@ import ( "github.com/kaiachain/kaia/networks/p2p" mocks2 "github.com/kaiachain/kaia/node/cn/mocks" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" "github.com/kaiachain/kaia/rlp" "github.com/kaiachain/kaia/work/mocks" "github.com/stretchr/testify/assert" @@ -528,7 +527,7 @@ func TestHandleStakingInfoRequestMsg(t *testing.T) { mockBlockChain.EXPECT().GetHeaderByHash(gomock.Eq(hashes[1])).Return(&types.Header{Number: big.NewInt(5)}).Times(1) // not on staking interval useGini, minStake := testChainConfig.Governance.Reward.UseGiniCoeff, testChainConfig.Governance.Reward.MinimumStake.Uint64() - expectedResult := reward.FromKaiaxWithGini(si, useGini, minStake) + expectedResult := staking.FromStakingInfoWithGini(si, useGini, minStake) data, _ := rlp.EncodeToBytes(expectedResult) expectedRlpList := []rlp.RawValue{data} mockPeer.EXPECT().SendStakingInfoRLP(gomock.Eq(expectedRlpList)).Return(nil).Times(1) @@ -579,8 +578,8 @@ func TestHandleStakingInfoRequestMsgAfterKaia(t *testing.T) { mockBlockChain.EXPECT().GetHeaderByHash(gomock.Eq(hashes[1])).Return(&types.Header{Number: big.NewInt(6)}).Times(1) // should return StakingInfo(5) useGini, minStake := testChainConfig.Governance.Reward.UseGiniCoeff, testChainConfig.Governance.Reward.MinimumStake.Uint64() - dataBeforeKaia, _ := rlp.EncodeToBytes(reward.FromKaiaxWithGini(siBeforeKaia, useGini, minStake)) - dataAfterKaia, _ := rlp.EncodeToBytes(reward.FromKaiaxWithGini(siAfterKaia, useGini, minStake)) + dataBeforeKaia, _ := rlp.EncodeToBytes(staking.FromStakingInfoWithGini(siBeforeKaia, useGini, minStake)) + dataAfterKaia, _ := rlp.EncodeToBytes(staking.FromStakingInfoWithGini(siAfterKaia, useGini, minStake)) expectedRlpList := []rlp.RawValue{dataBeforeKaia, dataAfterKaia} mockPeer.EXPECT().SendStakingInfoRLP(gomock.Eq(expectedRlpList)).Return(nil).Times(1) @@ -643,8 +642,8 @@ func TestHandleStakingInfoMsg(t *testing.T) { RewardAddrs: []common.Address{{0x3}, {0x3}}, StakingAmounts: []uint64{2, 5, 6}, } - stakingInfos := []*reward.StakingInfo{ - reward.FromKaiaxWithGini(si, false, 5000000), + stakingInfos := []*staking.P2PStakingInfo{ + staking.FromStakingInfoWithGini(si, false, 5000000), } mockDownloader.EXPECT().DeliverStakingInfos(gomock.Eq(nodeids[0].String()), gomock.Eq(stakingInfos)).Times(1).Return(expectedErr) diff --git a/node/cn/handler_test.go b/node/cn/handler_test.go index b01e59408..01d525ba7 100644 --- a/node/cn/handler_test.go +++ b/node/cn/handler_test.go @@ -39,7 +39,6 @@ import ( "github.com/kaiachain/kaia/networks/p2p/discover" "github.com/kaiachain/kaia/node/cn/mocks" "github.com/kaiachain/kaia/params" - "github.com/kaiachain/kaia/reward" workmocks "github.com/kaiachain/kaia/work/mocks" "github.com/stretchr/testify/assert" ) @@ -66,8 +65,6 @@ var ( var hash1 common.Hash -var signer types.Signer - func init() { addrs = make([]common.Address, numVals) keys = make([]*ecdsa.PrivateKey, numVals) @@ -122,24 +119,6 @@ func newBlock(blockNum int) *types.Block { return block } -func newBlockWithParentHash(blockNum int, parentHash common.Hash) *types.Block { - header := &types.Header{ - Number: big.NewInt(int64(blockNum)), - BlockScore: big.NewInt(int64(1)), - Extra: addrs[0][:], - Governance: addrs[0][:], - Vote: addrs[0][:], - ParentHash: parentHash, - } - header.Hash() - block := types.NewBlockWithHeader(header) - block = block.WithBody(types.Transactions{}) - block.Hash() - block.Size() - block.BlockScore() - return block -} - func newReceipt(gasUsed int) *types.Receipt { rct := types.NewReceipt(uint(gasUsed), common.Hash{}, uint64(gasUsed)) rct.Logs = []*types.Log{} @@ -147,16 +126,6 @@ func newReceipt(gasUsed int) *types.Receipt { return rct } -func newStakingInfo(blockNumber uint64) *reward.StakingInfo { - return &reward.StakingInfo{ - BlockNum: blockNumber, - CouncilNodeAddrs: []common.Address{{0x1}, {0x1}}, - CouncilStakingAddrs: []common.Address{{0x2}, {0x2}}, - CouncilRewardAddrs: []common.Address{{0x3}, {0x3}}, - CouncilStakingAmounts: []uint64{2, 5, 6}, - } -} - func TestNewProtocolManager(t *testing.T) { // 1. If consensus.Engine returns an empty Protocol, NewProtocolManager throws an error. { diff --git a/node/cn/mocks/downloader_mock.go b/node/cn/mocks/downloader_mock.go index 2214e148d..203a8c610 100644 --- a/node/cn/mocks/downloader_mock.go +++ b/node/cn/mocks/downloader_mock.go @@ -13,8 +13,8 @@ import ( types "github.com/kaiachain/kaia/blockchain/types" common "github.com/kaiachain/kaia/common" downloader "github.com/kaiachain/kaia/datasync/downloader" + staking "github.com/kaiachain/kaia/kaiax/staking" snap "github.com/kaiachain/kaia/node/cn/snap" - reward "github.com/kaiachain/kaia/reward" ) // MockProtocolManagerDownloader is a mock of ProtocolManagerDownloader interface. @@ -123,7 +123,7 @@ func (mr *MockProtocolManagerDownloaderMockRecorder) DeliverSnapPacket(arg0, arg } // DeliverStakingInfos mocks base method. -func (m *MockProtocolManagerDownloader) DeliverStakingInfos(arg0 string, arg1 []*reward.StakingInfo) error { +func (m *MockProtocolManagerDownloader) DeliverStakingInfos(arg0 string, arg1 []*staking.P2PStakingInfo) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeliverStakingInfos", arg0, arg1) ret0, _ := ret[0].(error) diff --git a/node/cn/protocol.go b/node/cn/protocol.go index dc64a78ba..f9edc6288 100644 --- a/node/cn/protocol.go +++ b/node/cn/protocol.go @@ -33,8 +33,8 @@ import ( "github.com/kaiachain/kaia/common" "github.com/kaiachain/kaia/datasync/downloader" "github.com/kaiachain/kaia/datasync/fetcher" + "github.com/kaiachain/kaia/kaiax/staking" "github.com/kaiachain/kaia/node/cn/snap" - "github.com/kaiachain/kaia/reward" "github.com/kaiachain/kaia/rlp" ) @@ -131,7 +131,7 @@ type ProtocolManagerDownloader interface { DeliverHeaders(id string, headers []*types.Header) error DeliverNodeData(id string, data [][]byte) error DeliverReceipts(id string, receipts [][]*types.Receipt) error - DeliverStakingInfos(id string, stakingInfos []*reward.StakingInfo) error + DeliverStakingInfos(id string, stakingInfos []*staking.P2PStakingInfo) error DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) error Terminate() diff --git a/node/cn/tracers/api.go b/node/cn/tracers/api.go index dd3678f45..54e2abdd1 100644 --- a/node/cn/tracers/api.go +++ b/node/cn/tracers/api.go @@ -895,7 +895,7 @@ func (api *CommonAPI) TraceCall(ctx context.Context, args kaiaapi.CallArgs, bloc defer release() // Execute the trace - intrinsicGas, err := types.IntrinsicGas(args.InputData(), args.GetAccessList(), nil, args.To == nil, api.backend.ChainConfig().Rules(block.Number())) + intrinsicGas, dataTokens, err := types.IntrinsicGas(args.InputData(), args.GetAccessList(), nil, args.To == nil, api.backend.ChainConfig().Rules(block.Number())) if err != nil { return nil, err } @@ -907,7 +907,7 @@ func (api *CommonAPI) TraceCall(ctx context.Context, args kaiaapi.CallArgs, bloc if rpcGasCap := api.backend.RPCGasCap(); rpcGasCap != nil { gasCap = rpcGasCap.Uint64() } - msg, err := args.ToMessage(gasCap, basefee, intrinsicGas) + msg, err := args.ToMessage(gasCap, basefee, intrinsicGas, dataTokens) if err != nil { return nil, err } diff --git a/params/protocol_params.go b/params/protocol_params.go index 67223b917..aeb13ea37 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -35,21 +35,23 @@ var TargetGasLimit = GenesisGasLimit // The artificial target const ( // Fee schedule parameters - CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. // G_callvalue - CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. // G_newaccount - TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. // G_transaction - TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. // G_transaction + G_create - TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. // G_txdatazero - QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. - SstoreSetGas uint64 = 20000 // Once per SLOAD operation. // G_sset - LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. // G_logdata - CallStipend uint64 = 2300 // Free gas given at beginning of call. // G_callstipend - Sha3Gas uint64 = 30 // Once per SHA3 operation. // G_sha3 - Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. // G_sha3word - InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract. // G_InitCodeWord - SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. // G_sreset - SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. // G_sreset - SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. // R_sclear + CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. // G_callvalue + CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. // G_newaccount + TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. // G_transaction + TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. // G_transaction + G_create + TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. // G_txdatazero + TokenPerNonZeroByte7623 uint64 = 4 // Token cost per non-zero byte as specified by EIP-7623. + CostFloorPerToken7623 uint64 = 10 // Cost floor per byte of data as specified by EIP-7623. + QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. + SstoreSetGas uint64 = 20000 // Once per SLOAD operation. // G_sset + LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. // G_logdata + CallStipend uint64 = 2300 // Free gas given at beginning of call. // G_callstipend + Sha3Gas uint64 = 30 // Once per SHA3 operation. // G_sha3 + Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. // G_sha3word + InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract. // G_InitCodeWord + SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. // G_sreset + SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. // G_sreset + SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. // R_sclear // gasSStoreEIP2200 SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed diff --git a/reward/doc.go b/reward/doc.go deleted file mode 100644 index fba2b65ad..000000000 --- a/reward/doc.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019 The klaytn Authors -// This file is part of the klaytn library. -// -// The klaytn library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The klaytn library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the klaytn library. If not, see . - -/* -Package reward implements the Kaia Reward System. -The Kaia reward system manages stakingInfo and distributes block rewards. - -# Managing stakingInfo - -Kaia uses WeightedRandom policy to choose a block proposer. -It means, the percentage of becoming a block proposer depends on how much KAIA a node has staked. -Therefore, a node with stakes more than others will have more opportunities than other nodes. - -StakingInfo is a data including stakingAmount and addresses (node, staking, reward, KIF and KEF). -StakingAmount is a balance how much KAIA each node has staked. Only CCO can stake KAIA. -Each CCO stakes KAIA by a smart contract called staking contract. -StakingAmount is derived by checking balance of staking contract. -StakingInfo has 5 types of addresses. All addresses are obtained by the addressBookContract which is pre-deployed in the genesis block. -stakingAddress are addresses of staking contracts of CCO. reward, KIF and KEF addresses are the addresses which get a block reward when a block has been created. -StakingInfo is made every 86400 blocks (stakingInterval) and used in a next interval. - - type StakingInfo struct { - BlockNum uint64 - CouncilNodeAddrs []common.Address // Address of Council - CouncilStakingAddrs []common.Address // Address of Staking contract which holds staking balance - CouncilRewardAddrs []common.Address // Address of Council account which will get a block reward - KEFAddr common.Address // Address of KEF contract - KIFAddr common.Address // Address of KIF contract - UseGini bool // configure whether Gini is used or not - Gini float64 // Gini coefficient - CouncilStakingAmounts []uint64 // StakingAmounts of Council. They are derived from Staking addresses of council - } - -StakingInfo is managed by a StakingManager which has a cache for saving StakingInfos. -The StakingManager calculates block number with interval to find a stakingInfo for current block -and returns correct stakingInfo to use. - - related struct - - RewardDistributor - - StakingManager - - addressBookConnector - - stakingInfoCache - - stakingInfo - -# Distributing Reward - -Kaia distributes the reward of a block to proposer, KIF and KEF. -The detail information of KIF and KEF is available on Kaia docs. - -Token Economy - https://docs.kaia.io/docs/learn/token-economy/ - -Configurations related to the reward system such as mintingAmount, ratio and unitPrice are determined by the Kaia governance. -All configurations are saved as rewardConfig on every epoch block (default 604,800 blocks) and managed by a rewardConfigCache. - -A proposer which has made a current block will get the reward of the block. -A block reward is calculated by following steps. -First, calculate totalReward by adding mintingAmount and totalTxFee (unitPrice * gasUsed). -Second, divide totalReward by ratio (default 34/54/12 - proposer/KIF/KEF). -Last, distribute reward to each address (proposer, KIF, KEF). - - related struct - - RewardDistributor - - rewardConfigCache -*/ -package reward diff --git a/reward/staking_manager.go b/reward/staking_manager.go deleted file mode 100644 index 46df92415..000000000 --- a/reward/staking_manager.go +++ /dev/null @@ -1,142 +0,0 @@ -// Modifications Copyright 2024 The Kaia Authors -// Copyright 2019 The klaytn Authors -// This file is part of the klaytn library. -// -// The klaytn library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The klaytn library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the klaytn library. If not, see . -// Modified and improved for the Kaia development. - -package reward - -import ( - "fmt" - - "github.com/kaiachain/kaia/blockchain" - "github.com/kaiachain/kaia/blockchain/state" - "github.com/kaiachain/kaia/blockchain/types" - "github.com/kaiachain/kaia/blockchain/vm" - "github.com/kaiachain/kaia/common" - "github.com/kaiachain/kaia/event" - "github.com/kaiachain/kaia/kaiax/staking" - "github.com/kaiachain/kaia/log" - "github.com/kaiachain/kaia/params" -) - -var ( - logger = log.NewModuleLogger(log.Reward) -) - -// blockChain is an interface for blockchain.Blockchain used in reward package. -type blockChain interface { - SubscribeChainHeadEvent(ch chan<- blockchain.ChainHeadEvent) event.Subscription - GetBlockByNumber(number uint64) *types.Block - GetReceiptsByBlockHash(hash common.Hash) types.Receipts - StateAt(root common.Hash) (*state.StateDB, error) - Config() *params.ChainConfig - CurrentHeader() *types.Header - GetBlock(hash common.Hash, number uint64) *types.Block - GetHeaderByNumber(number uint64) *types.Header - State() (*state.StateDB, error) - CurrentBlock() *types.Block - - StateCache() state.Database - Processor() blockchain.Processor - - blockchain.ChainContext -} - -// PreloadStakingInfo preloads staking info for the given headers. -// It first finds the first block that does not have state, and then -// it regenerates the state from the nearest block that has state to the target block to preload staking info. -// Note that the state is saved every 128 blocks to disk in full node. -func PreloadStakingInfo(bc blockChain, headers []*types.Header, stakingModule staking.StakingModule) (uint64, error) { - // If no headers to preload, do nothing - if len(headers) == 0 { - return 0, nil - } - - var ( - current *types.Block - database state.Database - target = headers[len(headers)-1].Number.Uint64() - ) - - database = state.NewDatabaseWithExistingCache(bc.StateCache().TrieDB().DiskDB(), bc.StateCache().TrieDB().TrieNodeCache()) - - // Find the first block that does not have state - i := 0 - for i < len(headers) { - if _, err := state.New(headers[i].Root, database, nil, nil); err != nil { - break - } - i++ - } - // Early return if all blocks have state - if i == len(headers) { - return 0, nil - } - - // Find the nearest block that has state - origin := headers[i].Number.Uint64() - headers[i].Number.Uint64()%128 - current = bc.GetBlockByNumber(origin) - if current == nil { - return 0, fmt.Errorf("block %d not found", origin) - } - statedb, err := state.New(current.Header().Root, database, nil, nil) - if err != nil { - return 0, err - } - - var ( - parent common.Hash - preloadRef = stakingModule.AllocPreloadRef() - ) - - // Include target since we want staking info at `target`, not for `target`. - for current.NumberU64() <= target { - stakingModule.PreloadFromState(preloadRef, current.Header(), statedb) - if current.NumberU64() == target { - break - } - // Retrieve the next block to regenerate and process it - next := current.NumberU64() + 1 - if current = bc.GetBlockByNumber(next); current == nil { - return preloadRef, fmt.Errorf("block #%d not found", next) - } - _, _, _, _, _, err := bc.Processor().Process(current, statedb, vm.Config{}) - if err != nil { - return preloadRef, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) - } - // Finalize the state so any modifications are written to the trie - root, err := statedb.Commit(true) - if err != nil { - return preloadRef, err - } - if err := statedb.Reset(root); err != nil { - return preloadRef, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err) - } - database.TrieDB().ReferenceRoot(root) - if !common.EmptyHash(parent) { - database.TrieDB().Dereference(parent) - } - if current.Root() != root { - err = fmt.Errorf("mistmatching state root block expected %x reexecuted %x", current.Root(), root) - // Logging here because something went wrong when the state roots disagree even if the execution was successful. - logger.Error("incorrectly regenerated historical state", "block", current.NumberU64(), "err", err) - return preloadRef, fmt.Errorf("incorrectly regenerated historical state for block %d: %v", current.NumberU64(), err) - } - parent = root - } - - return preloadRef, nil -} diff --git a/tests/kaia_test_account_map_test.go b/tests/kaia_test_account_map_test.go index b59bee2ca..9be775fdb 100644 --- a/tests/kaia_test_account_map_test.go +++ b/tests/kaia_test_account_map_test.go @@ -137,7 +137,7 @@ func (a *AccountMap) Update(txs types.Transactions, signer types.Signer, picker // TODO-Kaia: This gas fee calculation is correct only if the transaction is a value transfer transaction. // Calculate the correct transaction fee by checking the corresponding receipt. - intrinsicGas, err := tx.IntrinsicGas(currentBlockNumber) + intrinsicGas, _, err := tx.IntrinsicGas(currentBlockNumber) if err != nil { return err } diff --git a/tests/state_test.go b/tests/state_test.go index ebe1e0ea9..0cfc380f2 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -92,6 +92,12 @@ func (suite *ExecutionSpecStateTestSuite) TestExecutionSpecState() { st.skipLoad(`^prague\/eip7702_set_code_tx\/set_code_txs\/invalid_tx_invalid_auth_signature.json`) st.skipLoad(`^prague\/eip7702_set_code_tx\/set_code_txs\/tx_validity_chain_id.json`) st.skipLoad(`^prague\/eip7702_set_code_tx\/set_code_txs\/tx_validity_nonce.json`) + // not yet supported EIPs + st.skipLoad(`^prague\/eip2537_bls_12_381_precompiles\/`) + st.skipLoad(`^prague\/eip7702_set_code_tx\/`) + // temporary skip failing frontier tests + st.skipLoad(`^frontier\/opcodes\/all_opcodes\/all_opcodes.json`) + st.skipLoad(`^frontier\/precompiles\/precompile_absence\/precompile_absence.json`) // tests to skip // unsupported EIPs @@ -99,6 +105,8 @@ func (suite *ExecutionSpecStateTestSuite) TestExecutionSpecState() { st.skipLoad(`^cancun\/eip4844_blobs\/`) // calculate the different consumed gas because 0x0a and 0x0b contract is set to access list by ActivePrecompiles in Cancun st.skipLoad(`^prague\/eip2537_bls_12_381_precompiles\/bls12_precompiles_before_fork\/precompile_before_fork.json\/tests\/prague\/eip2537_bls_12_381_precompiles\/test_bls12_precompiles_before_fork.py::test_precompile_before_fork`) + // type 3 tx (EIP-4844) is not supported + st.skipLoad(`^prague\/eip7623_increase_calldata_cost\/.*type_3.*`) st.walk(t, executionSpecStateTestDir, func(t *testing.T, name string, test *StateTest) { execStateTest(t, st, test, name, []string{ diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 10fcd5ac6..460076e13 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -434,17 +434,18 @@ func (tx *stTransaction) toMessage(ps stPostState, r params.Rules, isTestExecuti } var intrinsicGas uint64 + var dataTokens uint64 if isTestExecutionSpecState { - intrinsicGas, err = useEthIntrinsicGas(data, accessList, authorizationList, to == nil, r) + intrinsicGas, dataTokens, err = useEthIntrinsicGas(data, accessList, authorizationList, to == nil, r) } else { - intrinsicGas, err = types.IntrinsicGas(data, nil, nil, to == nil, r) + intrinsicGas, dataTokens, err = types.IntrinsicGas(data, nil, nil, to == nil, r) } if err != nil { return nil, err } - msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, true, intrinsicGas, accessList, authorizationList) + msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, true, intrinsicGas, dataTokens, accessList, authorizationList) return msg, nil } @@ -496,7 +497,7 @@ func useEthOpCodeGas(r params.Rules, evm *vm.EVM) { } } -func useEthIntrinsicGas(data []byte, accessList types.AccessList, authorizationList types.AuthorizationList, contractCreation bool, r params.Rules) (uint64, error) { +func useEthIntrinsicGas(data []byte, accessList types.AccessList, authorizationList types.AuthorizationList, contractCreation bool, r params.Rules) (uint64, uint64, error) { if r.IsIstanbul { r.IsPrague = true } diff --git a/tests/tx_gas_calculation_test.go b/tests/tx_gas_calculation_test.go index 35371c310..5b14681dc 100644 --- a/tests/tx_gas_calculation_test.go +++ b/tests/tx_gas_calculation_test.go @@ -846,7 +846,8 @@ func genMapForDeploy(from TestAccount, to TestAccount, gasPrice *big.Int, txType intrinsicGas := getIntrinsicGas(txType) intrinsicGas += uint64(0x175fd) - gasPayloadWithGas, err := types.IntrinsicGasPayload(intrinsicGas, common.FromHex(code), true, params.Rules{IsIstanbul: true, IsShanghai: true}) + // TODO-Kaia: Add test for EIP-7623 + gasPayloadWithGas, _, err := types.IntrinsicGasPayload(intrinsicGas, common.FromHex(code), true, params.Rules{IsIstanbul: true, IsShanghai: true}) if err != nil { return nil, 0 } @@ -881,7 +882,8 @@ func genMapForExecution(from TestAccount, to TestAccount, gasPrice *big.Int, txT intrinsicGas := getIntrinsicGas(txType) intrinsicGas += uint64(0x9ec4) - gasPayloadWithGas, err := types.IntrinsicGasPayload(intrinsicGas, data, false, params.Rules{IsShanghai: false}) + // TODO-Kaia: Add test for EIP-7623 + gasPayloadWithGas, _, err := types.IntrinsicGasPayload(intrinsicGas, data, false, params.Rules{IsShanghai: false}) if err != nil { return nil, 0 } diff --git a/tests/tx_validation_test.go b/tests/tx_validation_test.go index c1ae1d810..b6e151ecc 100644 --- a/tests/tx_validation_test.go +++ b/tests/tx_validation_test.go @@ -452,7 +452,7 @@ func TestValidationPoolInsertPrague(t *testing.T) { authorizationList := types.AuthorizationList{*auth} tx := types.NewMessage(reservoir.Addr, &eoaWithCode.Addr, reservoir.GetNonce(), nil, gasLimit, - nil, big.NewInt(25*params.Gkei), big.NewInt(25*params.Gkei), nil, false, uint64(0), nil, authorizationList) + nil, big.NewInt(25*params.Gkei), big.NewInt(25*params.Gkei), nil, false, uint64(0), uint64(0), nil, authorizationList) tx.ChainId().Set(bcdata.bc.Config().ChainID) err = tx.SignWithKeys(signer, reservoir.Keys) assert.Equal(t, nil, err) diff --git a/work/worker.go b/work/worker.go index 030aadf7b..02f2b02ee 100644 --- a/work/worker.go +++ b/work/worker.go @@ -450,18 +450,6 @@ func (self *worker) wait(TxResendUseLegacy bool) { } } - // update governance CurrentSet if it is at an epoch block - if err := self.engine.CreateSnapshot(self.chain, block.NumberU64(), block.Hash(), nil); err != nil { - logger.Error("Failed to call snapshot", "err", err) - } - - // update governance parameters - if istanbul, ok := self.engine.(consensus.Istanbul); ok { - if err := istanbul.UpdateParam(block.NumberU64()); err != nil { - logger.Error("Failed to update governance parameters", "err", err) - } - } - logger.Info("Successfully wrote mined block", "num", block.NumberU64(), "hash", block.Hash(), "txs", len(block.Transactions()), "elapsed", blockWriteTime) self.chain.PostChainEvents(events, logs)