From 6bc602194c0479d72646799ade249d55342a42e1 Mon Sep 17 00:00:00 2001 From: Ganesha Upadhyaya Date: Tue, 28 Apr 2020 18:48:42 -0700 Subject: [PATCH] refactor check duplicates to staking_verifier (#2898) --- core/staking_verifier.go | 65 ++++++++++- core/staking_verifier_test.go | 213 ++++++++++++++++++++++++---------- core/state_transition.go | 58 +-------- core/tx_pool.go | 7 +- core/tx_pool_test.go | 24 ++++ test/chain/reward/main.go | 23 +++- 6 files changed, 264 insertions(+), 126 deletions(-) diff --git a/core/staking_verifier.go b/core/staking_verifier.go index 63012322e..b21169fb1 100644 --- a/core/staking_verifier.go +++ b/core/staking_verifier.go @@ -23,6 +23,46 @@ var ( errBlockNumMissing = errors.New("no block number was provided") ) +func checkDuplicateFields( + bc ChainContext, state vm.StateDB, + validator common.Address, identity string, blsKeys []shard.BLSPublicKey, +) error { + addrs, err := bc.ReadValidatorList() + if err != nil { + return err + } + + checkIdentity := identity != "" + checkBlsKeys := len(blsKeys) != 0 + + blsKeyMap := map[shard.BLSPublicKey]struct{}{} + for _, key := range blsKeys { + blsKeyMap[key] = struct{}{} + } + + for _, addr := range addrs { + if !bytes.Equal(validator.Bytes(), addr.Bytes()) { + wrapper, err := state.ValidatorWrapperCopy(addr) + + if err != nil { + return err + } + + if checkIdentity && wrapper.Identity == identity { + return errors.Wrapf(errDupIdentity, "duplicate identity %s", identity) + } + if checkBlsKeys { + for _, existingKey := range wrapper.SlotPubKeys { + if _, ok := blsKeyMap[existingKey]; ok { + return errors.Wrapf(errDupBlsKey, "duplicate bls key %x", existingKey) + } + } + } + } + } + return nil +} + // TODO: add unit tests to check staking msg verification // VerifyAndCreateValidatorFromMsg verifies the create validator message using @@ -31,12 +71,14 @@ var ( // // Note that this function never updates the stateDB, it only reads from stateDB. func VerifyAndCreateValidatorFromMsg( - stateDB vm.StateDB, epoch *big.Int, blockNum *big.Int, msg *staking.CreateValidator, + stateDB vm.StateDB, chainContext ChainContext, epoch *big.Int, blockNum *big.Int, msg *staking.CreateValidator, ) (*staking.ValidatorWrapper, error) { - if stateDB == nil { return nil, errStateDBIsMissing } + if chainContext == nil { + return nil, errChainContextMissing + } if epoch == nil { return nil, errEpochMissing } @@ -51,6 +93,13 @@ func VerifyAndCreateValidatorFromMsg( errValidatorExist, common2.MustAddressToBech32(msg.ValidatorAddress), ) } + if err := checkDuplicateFields( + chainContext, stateDB, + msg.ValidatorAddress, + msg.Identity, + msg.SlotPubKeys); err != nil { + return nil, err + } if !CanTransfer(stateDB, msg.ValidatorAddress, msg.Amount) { return nil, errInsufficientBalanceForStake } @@ -82,7 +131,6 @@ func VerifyAndEditValidatorFromMsg( stateDB vm.StateDB, chainContext ChainContext, epoch, blockNum *big.Int, msg *staking.EditValidator, ) (*staking.ValidatorWrapper, error) { - if stateDB == nil { return nil, errStateDBIsMissing } @@ -95,6 +143,17 @@ func VerifyAndEditValidatorFromMsg( if !stateDB.IsValidator(msg.ValidatorAddress) { return nil, errValidatorNotExist } + newBlsKeys := []shard.BLSPublicKey{} + if msg.SlotKeyToAdd != nil { + newBlsKeys = append(newBlsKeys, *msg.SlotKeyToAdd) + } + if err := checkDuplicateFields( + chainContext, stateDB, + msg.ValidatorAddress, + msg.Identity, + newBlsKeys); err != nil { + return nil, err + } wrapper, err := stateDB.ValidatorWrapperCopy(msg.ValidatorAddress) if err != nil { return nil, err diff --git a/core/staking_verifier_test.go b/core/staking_verifier_test.go index 9039c0db3..5fb7bbf86 100644 --- a/core/staking_verifier_test.go +++ b/core/staking_verifier_test.go @@ -6,11 +6,17 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/harmony-one/bls/ffi/go/bls" + blockfactory "github.com/harmony-one/harmony/block/factory" "github.com/harmony-one/harmony/core/state" + "github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/crypto/hash" + chain2 "github.com/harmony-one/harmony/internal/chain" common2 "github.com/harmony-one/harmony/internal/common" + "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/numeric" "github.com/harmony-one/harmony/shard" staking "github.com/harmony-one/harmony/staking/types" @@ -23,6 +29,25 @@ var ( twelveK = new(big.Int).Mul(big.NewInt(12000), big.NewInt(1e18)) ) +func createChain(database *ethdb.MemDatabase) *BlockChain { + key, _ := crypto.GenerateKey() + gspec := Genesis{ + Config: params.TestChainConfig, + Factory: blockfactory.ForTest, + Alloc: GenesisAlloc{ + crypto.PubkeyToAddress(key.PublicKey): { + Balance: big.NewInt(8e18), + }, + }, + GasLimit: 1e18, + ShardID: 0, + } + genesis := gspec.MustCommit(database) + _ = genesis + chain, _ := NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil) + return chain +} + func generateBLSKeySigPair() (shard.BLSPublicKey, shard.BLSSignature) { p := &bls.PublicKey{} p.DeserializeHexStr(testBLSPubKey) @@ -75,11 +100,13 @@ func createValidator() *staking.CreateValidator { // Test CV1: create validator func TestCV1(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -87,18 +114,20 @@ func TestCV1(t *testing.T) { // Test CV3: validator already exists func TestCV3(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } statedb.SetValidatorFlag(msg.ValidatorAddress) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); !strings.Contains(err.Error(), errValidatorExist.Error()) { t.Error("expected", errValidatorExist, "got", err) } @@ -106,13 +135,15 @@ func TestCV3(t *testing.T) { // Test CV9: name == 140 characters func TestCV9(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() // name length: 140 characters msg.Name = "Helloiwfhwifbwfbcerghveugbviuscbhwiefbcusidbcifwefhgciwefherhbfiwuehfciwiuebfcuyiewfhwieufwiweifhcwefhwefhwidsffevjnononwondqmeofniowfndjowe" statedb.AddBalance(msg.ValidatorAddress, tenK) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -120,13 +151,15 @@ func TestCV9(t *testing.T) { // Test CV10: identity == 140 characters func TestCV10(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() // identity length: 140 characters msg.Identity = "Helloiwfhwifbwfbcerghveugbviuscbhwiefbcusidbcifwefhgciwefherhbfiwuehfciwiuebfcuyiewfhwieufwiweifhcwefhwefhwidsffevjnononwondqmeofniowfndjowe" statedb.AddBalance(msg.ValidatorAddress, tenK) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -134,13 +167,15 @@ func TestCV10(t *testing.T) { // Test CV11: website == 140 characters func TestCV11(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() // website length: 140 characters msg.Website = "Helloiwfhwifbwfbcerghveugbviuscbhwiefbcusidbcifwefhgciwefherhbfiwuehfciwiuebfcuyiewfhwieufwiweifhcwefhwefhwidsffevjnononwondqmeofniowfndjowe" statedb.AddBalance(msg.ValidatorAddress, tenK) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -148,13 +183,15 @@ func TestCV11(t *testing.T) { // Test CV12: security == 140 characters func TestCV12(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() // security contact length: 140 characters msg.SecurityContact = "Helloiwfhwifbwfbcerghveugbviuscbhwiefbcusidbcifwefhgciwefherhbfiwuehfciwiuebfcuyiewfhwieufwiweifhcwefhwefhwidsffevjnononwondqmeofniowfndjowe" statedb.AddBalance(msg.ValidatorAddress, tenK) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -162,13 +199,15 @@ func TestCV12(t *testing.T) { // Test CV13: details == 280 characters func TestCV13(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() // details length: 280 characters msg.Details = "HelloiwfhwifbwfbcerghveugbviuscbhwiefbcusidbcifwefhgciwefherhbfiwuehfciwiuebfcuyiewfhwieufwiweifhcwefhwefhwidsffevjnononwondqmeofniowfndjoweHlloiwfhwifbwfbcerghveugbviuscbhwiefbcusidbcifwefhgciwefherhbfiwuehfciwiuedbfcuyiewfhwieufwiweifhcwefhwefhwidsffevjnononwondqmeofniowfndjowe" statedb.AddBalance(msg.ValidatorAddress, tenK) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -176,14 +215,16 @@ func TestCV13(t *testing.T) { // Test CV14: commission rate <= max rate & max change rate <= max rate func TestCV14(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // commission rate == max rate && max change rate == max rate msg.CommissionRates.Rate, _ = numeric.NewDecFromStr("0.5") msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("0.5") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -191,13 +232,15 @@ func TestCV14(t *testing.T) { // Test CV15: commission rate > max rate func TestCV15(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // commission rate: 0.6 > max rate: 0.5 msg.CommissionRates.Rate, _ = numeric.NewDecFromStr("0.6") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "commission rate and change rate can not be larger than max commission rate", "got", nil) } @@ -205,13 +248,15 @@ func TestCV15(t *testing.T) { // Test CV16: max change rate > max rate func TestCV16(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max change rate: 0.6 > max rate: 0.5 msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("0.6") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "commission rate and change rate can not be larger than max commission rate", "got", nil) } @@ -219,13 +264,15 @@ func TestCV16(t *testing.T) { // Test CV17: max rate == 1 func TestCV17(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max rate == 1 msg.CommissionRates.MaxRate, _ = numeric.NewDecFromStr("1") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -233,7 +280,9 @@ func TestCV17(t *testing.T) { // Test CV18: max rate == 1 && max change rate == 1 && commission rate == 0 func TestCV18(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max rate == 1 && max change rate == 1 && commission rate == 0 @@ -241,7 +290,7 @@ func TestCV18(t *testing.T) { msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("1") msg.CommissionRates.Rate, _ = numeric.NewDecFromStr("1") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -249,13 +298,15 @@ func TestCV18(t *testing.T) { // Test CV19: commission rate == 0 func TestCV19(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // commission rate == 0 msg.CommissionRates.Rate, _ = numeric.NewDecFromStr("0") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -263,13 +314,15 @@ func TestCV19(t *testing.T) { // Test CV20: max change rate == 0 func TestCV20(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // commission rate == 0 msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("0") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -277,7 +330,9 @@ func TestCV20(t *testing.T) { // Test CV21: max change rate == 0 & max rate == 0 & commission rate == 0 func TestCV21(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max change rate == 0 & max rate == 0 & commission rate == 0 @@ -285,7 +340,7 @@ func TestCV21(t *testing.T) { msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("0") msg.CommissionRates.Rate, _ = numeric.NewDecFromStr("0") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -293,14 +348,16 @@ func TestCV21(t *testing.T) { // Test CV22: max change rate == 1 & max rate == 1 func TestCV22(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max change rate == 1 & max rate == 1 msg.CommissionRates.MaxRate, _ = numeric.NewDecFromStr("1") msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("1") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -308,13 +365,15 @@ func TestCV22(t *testing.T) { // Test CV23: commission rate < 0 func TestCV23(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // commission rate < 0 msg.CommissionRates.Rate, _ = numeric.NewDecFromStr("-0.1") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "rate:-0.100000000000000000: commission rate, change rate and max rate should be within 0-100 percent", "got", nil) } @@ -322,13 +381,15 @@ func TestCV23(t *testing.T) { // Test CV24: max rate < 0 func TestCV24(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max rate < 0 msg.CommissionRates.MaxRate, _ = numeric.NewDecFromStr("-0.001") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "rate:-0.001000000000000000: commission rate, change rate and max rate should be within 0-100 percent", "got", nil) } @@ -336,13 +397,15 @@ func TestCV24(t *testing.T) { // Test CV25: max change rate < 0 func TestCV25(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max rate < 0 msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("-0.001") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "rate:-0.001000000000000000: commission rate, change rate and max rate should be within 0-100 percent", "got", nil) } @@ -350,13 +413,15 @@ func TestCV25(t *testing.T) { // Test CV26: commission rate > 1 func TestCV26(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // commission rate > 1 msg.CommissionRates.Rate, _ = numeric.NewDecFromStr("1.01") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "rate:1.01000000000000000: commission rate, change rate and max rate should be within 0-100 percent", "got", nil) } @@ -364,13 +429,15 @@ func TestCV26(t *testing.T) { // Test CV27: max rate > 1 func TestCV27(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max rate > 1 msg.CommissionRates.MaxRate, _ = numeric.NewDecFromStr("1.01") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "rate:1.01000000000000000: commission rate, change rate and max rate should be within 0-100 percent", "got", nil) } @@ -378,13 +445,15 @@ func TestCV27(t *testing.T) { // Test CV28: max change rate > 1 func TestCV28(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // max change rate > 1 msg.CommissionRates.MaxChangeRate, _ = numeric.NewDecFromStr("1.01") if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "rate:1.01000000000000000: commission rate, change rate and max rate should be within 0-100 percent", "got", nil) } @@ -392,14 +461,16 @@ func TestCV28(t *testing.T) { // Test CV29: amount > MinSelfDelegation func TestCV29(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, twelveK) // amount > MinSelfDelegation msg.Amount = twelveK msg.MinSelfDelegation = tenK if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -407,14 +478,16 @@ func TestCV29(t *testing.T) { // Test CV30: amount == MinSelfDelegation func TestCV30(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // amount > MinSelfDelegation msg.Amount = tenK msg.MinSelfDelegation = tenK if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err != nil { t.Error("expected", nil, "got", err) } @@ -422,14 +495,16 @@ func TestCV30(t *testing.T) { // Test CV31: amount < MinSelfDelegation func TestCV31(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // amount > MinSelfDelegation msg.Amount = twelveK msg.MinSelfDelegation = tenK if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "min_self_delegation 5000000000000000000, after delegation amount 4000000000000000000: self delegation can not be less than min_self_delegation", "got", nil) } @@ -437,14 +512,16 @@ func TestCV31(t *testing.T) { // Test CV32: MaxTotalDelegation < MinSelfDelegation func TestCV32(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // MaxTotalDelegation < MinSelfDelegation msg.MaxTotalDelegation = tenK msg.MinSelfDelegation = twelveK if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "max_total_delegation can not be less than min_self_delegation", "got", nil) } @@ -452,13 +529,15 @@ func TestCV32(t *testing.T) { // Test CV33: MinSelfDelegation < 1 ONE func TestCV33(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // MinSelfDelegation < 10,000 ONE msg.MinSelfDelegation = big.NewInt(1e18) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "delegation-given 1000000000000000000: min_self_delegation has to be greater than 10,000 ONE", "got", nil) } @@ -466,13 +545,15 @@ func TestCV33(t *testing.T) { // Test CV34: MinSelfDelegation not specified func TestCV34(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // MinSelfDelegation not specified msg.MinSelfDelegation = nil if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "MinSelfDelegation can not be nil", "got", nil) } @@ -480,13 +561,15 @@ func TestCV34(t *testing.T) { // Test CV35: MinSelfDelegation < 0 func TestCV35(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // MinSelfDelegation < 0 msg.MinSelfDelegation = big.NewInt(-1) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "delegation-given -1: min_self_delegation has to be greater than 1 ONE", "got", nil) } @@ -494,14 +577,16 @@ func TestCV35(t *testing.T) { // Test CV36: amount > MaxTotalDelegation func TestCV36(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // amount > MaxTotalDelegation msg.Amount = big.NewInt(4e18) msg.MaxTotalDelegation = big.NewInt(3e18) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "total delegation can not be bigger than max_total_delegation", "got", nil) } @@ -509,13 +594,15 @@ func TestCV36(t *testing.T) { // Test CV39: MaxTotalDelegation < 0 func TestCV39(t *testing.T) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + database := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, state.NewDatabase(database)) + chain := createChain(database) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, tenK) // MaxTotalDelegation < 0 msg.MaxTotalDelegation = big.NewInt(-1) if _, err := VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, chain, postStakingEpoch, big.NewInt(0), msg, ); err == nil { t.Error("expected", "max_total_delegation can not be less than min_self_delegation", "got", nil) } diff --git a/core/state_transition.go b/core/state_transition.go index 90716b08d..f47c3b95f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -17,12 +17,9 @@ package core import ( - "bytes" "math" "math/big" - "github.com/harmony-one/harmony/shard" - staking2 "github.com/harmony-one/harmony/staking" "github.com/harmony-one/harmony/staking/network" @@ -389,54 +386,11 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) { return st.gasUsed(), err } -func (st *StateTransition) checkDuplicateFields(validator common.Address, identity string, blsKeys []shard.BLSPublicKey) error { - addrs, err := st.bc.ReadValidatorList() - if err != nil { - return err - } - - checkIdentity := identity != "" - checkBlsKeys := len(blsKeys) != 0 - - blsKeyMap := map[shard.BLSPublicKey]struct{}{} - for _, key := range blsKeys { - blsKeyMap[key] = struct{}{} - } - - for _, addr := range addrs { - if !bytes.Equal(validator.Bytes(), addr.Bytes()) { - wrapper, err := st.state.ValidatorWrapperCopy(addr) - - if err != nil { - return err - } - - if checkIdentity && wrapper.Identity == identity { - return errors.Wrapf(errDupIdentity, "duplicate identity %s", identity) - } - if checkBlsKeys { - for _, existingKey := range wrapper.SlotPubKeys { - if _, ok := blsKeyMap[existingKey]; ok { - return errors.Wrapf(errDupBlsKey, "duplicate bls key %x", existingKey) - } - } - } - } - } - return nil -} - func (st *StateTransition) verifyAndApplyCreateValidatorTx( createValidator *staking.CreateValidator, blockNum *big.Int, ) error { - if err := st.checkDuplicateFields( - createValidator.ValidatorAddress, - createValidator.Identity, - createValidator.SlotPubKeys); err != nil { - return err - } wrapper, err := VerifyAndCreateValidatorFromMsg( - st.state, st.evm.EpochNumber, blockNum, createValidator, + st.state, st.bc, st.evm.EpochNumber, blockNum, createValidator, ) if err != nil { return err @@ -452,16 +406,6 @@ func (st *StateTransition) verifyAndApplyCreateValidatorTx( func (st *StateTransition) verifyAndApplyEditValidatorTx( editValidator *staking.EditValidator, blockNum *big.Int, ) error { - newBlsKeys := []shard.BLSPublicKey{} - if editValidator.SlotKeyToAdd != nil { - newBlsKeys = append(newBlsKeys, *editValidator.SlotKeyToAdd) - } - if err := st.checkDuplicateFields( - editValidator.ValidatorAddress, - editValidator.Identity, - newBlsKeys); err != nil { - return err - } wrapper, err := VerifyAndEditValidatorFromMsg( st.state, st.bc, st.evm.EpochNumber, blockNum, editValidator, ) diff --git a/core/tx_pool.go b/core/tx_pool.go index 9c86eabf7..9adaff2fd 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -762,8 +762,11 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error { if shard.Schedule.IsLastBlock(currentBlockNumber.Uint64()) { pendingEpoch = new(big.Int).Add(pendingEpoch, big.NewInt(1)) } - - _, err = VerifyAndCreateValidatorFromMsg(pool.currentState, pendingEpoch, pendingBlockNumber, stkMsg) + chainContext, ok := pool.chain.(ChainContext) + if !ok { + chainContext = nil // might use testing blockchain, set to nil for verifier to handle. + } + _, err = VerifyAndCreateValidatorFromMsg(pool.currentState, chainContext, pendingEpoch, pendingBlockNumber, stkMsg) return err case staking.DirectiveEditValidator: msg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveEditValidator) diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 4bd202032..f23690e06 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -35,7 +35,9 @@ import ( "github.com/harmony-one/harmony/common/denominations" "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/crypto/hash" + chain2 "github.com/harmony-one/harmony/internal/chain" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/numeric" "github.com/harmony-one/harmony/shard" @@ -136,6 +138,26 @@ func pricedTransaction(shardID uint32, nonce uint64, gaslimit uint64, gasprice * return signedTx } +func createBlockChain() *BlockChain { + key, _ := crypto.GenerateKey() + gspec := Genesis{ + Config: params.TestChainConfig, + Factory: blockfactory.ForTest, + Alloc: GenesisAlloc{ + crypto.PubkeyToAddress(key.PublicKey): { + Balance: big.NewInt(8e18), + }, + }, + GasLimit: 1e18, + ShardID: 0, + } + database := ethdb.NewMemDatabase() + genesis := gspec.MustCommit(database) + _ = genesis + blockchain, _ := NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil) + return blockchain +} + func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) blockchain := &testBlockChain{statedb, 1e18, new(event.Feed)} @@ -301,6 +323,7 @@ func TestCreateValidatorTransaction(t *testing.T) { t.Parallel() pool, _ := setupTxPool() + pool.chain = createBlockChain() defer pool.Stop() fromKey, _ := crypto.GenerateKey() @@ -328,6 +351,7 @@ func TestMixedTransactions(t *testing.T) { t.Parallel() pool, _ := setupTxPool() + pool.chain = createBlockChain() defer pool.Stop() fromKey, _ := crypto.GenerateKey() diff --git a/test/chain/reward/main.go b/test/chain/reward/main.go index 986c33417..c5006f44e 100644 --- a/test/chain/reward/main.go +++ b/test/chain/reward/main.go @@ -6,14 +6,19 @@ import ( "math/rand" "time" + blockfactory "github.com/harmony-one/harmony/block/factory" + "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" common2 "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/state" + "github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/crypto/hash" + "github.com/harmony-one/harmony/internal/chain" "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/numeric" "github.com/harmony-one/harmony/shard" @@ -83,11 +88,27 @@ func createValidator() *staking.CreateValidator { } func main() { + key, _ := crypto.GenerateKey() + gspec := core.Genesis{ + Config: params.TestChainConfig, + Factory: blockfactory.ForTest, + Alloc: core.GenesisAlloc{ + crypto.PubkeyToAddress(key.PublicKey): { + Balance: big.NewInt(8000000000000000000), + }, + }, + GasLimit: 1e18, + ShardID: 0, + } + database := ethdb.NewMemDatabase() + genesis := gspec.MustCommit(database) + _ = genesis + bc, _ := core.NewBlockChain(database, nil, gspec.Config, chain.Engine, vm.Config{}, nil) statedb, _ := state.New(common2.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) msg := createValidator() statedb.AddBalance(msg.ValidatorAddress, new(big.Int).Mul(big.NewInt(5e18), big.NewInt(2000))) validator, err := core.VerifyAndCreateValidatorFromMsg( - statedb, postStakingEpoch, big.NewInt(0), msg, + statedb, bc, postStakingEpoch, big.NewInt(0), msg, ) if err != nil { fmt.Print(err)