From 90704214620021992dfb185ed62848336f7bbe3f Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 23 Nov 2019 17:22:18 -0800 Subject: [PATCH 1/5] Refactor shard compute logic and make local test friendly for staking --- cmd/staking/root.go | 12 ++-- consensus/consensus_service.go | 73 +++++++++++++++++++++++- consensus/quorum/one-node-staked-vote.go | 12 ++-- core/blockchain.go | 5 +- internal/chain/engine.go | 4 +- node/node.go | 5 +- node/node_genesis.go | 2 +- node/node_handler.go | 47 --------------- node/worker/worker.go | 3 +- shard/committee/assignment.go | 30 ++++++---- shard/shard_state.go | 10 +++- test/configs/local-resharding.txt | 29 +++++----- 12 files changed, 135 insertions(+), 97 deletions(-) diff --git a/cmd/staking/root.go b/cmd/staking/root.go index 053fc106a..511e712ac 100644 --- a/cmd/staking/root.go +++ b/cmd/staking/root.go @@ -56,11 +56,15 @@ var ( rate = "0.15" testAccounts = []string{ - "one1pdv9lrdwl0rg5vglh4xtyrv3wjk3wsqket7zxy", - "one12fuf7x9rgtdgqg7vgq0962c556m3p7afsxgvll"} + "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7", + "one1uyshu2jgv8w465yc8kkny36thlt2wvel89tcmg", + "one1r4zyyjqrulf935a479sgqlpa78kz7zlcg2jfen", + "one1p7ht2d4kl8ve7a8jxw746yfnx4wnfxtp8jqxwe"} testBLSPubKeys = []string{ - "65f55eb3052f9e9f632b2923be594ba77c55543f5c58ee1454b9cfd658d25e06373b0f7d42a19c84768139ea294f6204", - "02c8ff0b88f313717bc3a627d2f8bb172ba3ad3bb9ba3ecb8eed4b7c878653d3d4faf769876c528b73f343967f74a917"} + "678ec9670899bf6af85b877058bea4fc1301a5a3a376987e826e3ca150b80e3eaadffedad0fedfa111576fa76ded980c", + "a547a9bf6fdde4f4934cde21473748861a3cc0fe8bbb5e57225a29f483b05b72531f002f8187675743d819c955a86100", + "fc4b9c535ee91f015efff3f32fbb9d32cdd9bfc8a837bb3eee89b8fff653c7af2050a4e147ebe5c7233dc2d5df06ee0a", + "ca86e551ee42adaaa6477322d7db869d3e203c00d7b86c82ebee629ad79cb6d57b8f3db28336778ec2180e56a8e07296"} ) func (s *staker) run(cmd *cobra.Command, args []string) error { diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index 624492934..ffe8a2fd9 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -453,12 +453,69 @@ func (consensus *Consensus) getLeaderPubKeyFromCoinbase(header *block.Header) (* // (b) node in committed but has any err during processing: Syncing mode // (c) node in committed and everything looks good: Normal mode func (consensus *Consensus) UpdateConsensusInformation() Mode { + curHeader := consensus.ChainReader.CurrentHeader() + + next := new(big.Int).Add(curHeader.Epoch(), common.Big1) + if consensus.ChainReader.Config().IsStaking(next) && + consensus.Decider.Policy() != quorum.SuperMajorityStake { + + prevSubCommitteeDump := consensus.Decider.JSON() + + consensus.Decider = quorum.NewDecider(quorum.SuperMajorityStake) + consensus.Decider.SetShardIDProvider(func() (uint32, error) { + return consensus.ShardID, nil + }) + s, err := committee.WithStakingEnabled.Compute( + next, consensus.ChainReader, + ) + + if err != nil { + utils.Logger().Error(). + Err(err). + Uint32("shard", consensus.ShardID). + Msg("Error when computing committee with staking") + return Syncing + } + + utils.Logger().Print("XXXXXXXX") + utils.Logger().Print(s.FindCommitteeByID(consensus.ShardID).Slots) + if _, err := consensus.Decider.SetVoters( + s.FindCommitteeByID(consensus.ShardID).Slots, + ); err != nil { + utils.Logger().Error(). + Err(err). + Uint32("shard", consensus.ShardID). + Msg("Error when updating voting power") + return Syncing + } + + utils.Logger().Info(). + Uint64("block-number", curHeader.Number().Uint64()). + Uint64("epoch", curHeader.Epoch().Uint64()). + Uint32("shard-id", consensus.ShardID). + RawJSON("prev-subcommittee", []byte(prevSubCommitteeDump)). + RawJSON("current-subcommittee", []byte(consensus.Decider.JSON())). + Msg("changing committee") + } + pubKeys := []*bls.PublicKey{} hasError := false header := consensus.ChainReader.CurrentHeader() epoch := header.Epoch() - curPubKeys := committee.WithStakingEnabled.ComputePublicKeys( + + // TODO: change GetCommitteePublicKeys to read from DB + curShardState, err := committee.WithStakingEnabled.Compute( epoch, consensus.ChainReader, + ) + if err != nil { + utils.Logger().Error(). + Err(err). + Uint32("shard", consensus.ShardID). + Msg("Error retrieving current shard state") + return Syncing + } + curPubKeys := committee.WithStakingEnabled.GetCommitteePublicKeys( + curShardState, )[int(header.ShardID())] consensus.numPrevPubKeys = len(curPubKeys) consensus.getLogger().Info().Msg("[UpdateConsensusInformation] Updating.....") @@ -467,8 +524,20 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { consensus.SetEpochNum(epoch.Uint64() + 1) consensus.getLogger().Info().Uint64("headerNum", header.Number().Uint64()). Msg("[UpdateConsensusInformation] Epoch updated for next epoch") - pubKeys = committee.WithStakingEnabled.ComputePublicKeys( + + nextShardState, err := committee.WithStakingEnabled.Compute( new(big.Int).Add(epoch, common.Big1), consensus.ChainReader, + ) + if err != nil { + utils.Logger().Error(). + Err(err). + Uint32("shard", consensus.ShardID). + Msg("Error retrieving next shard state") + return Syncing + } + + pubKeys = committee.WithStakingEnabled.GetCommitteePublicKeys( + nextShardState, )[int(header.ShardID())] } else { consensus.SetEpochNum(epoch.Uint64()) diff --git a/consensus/quorum/one-node-staked-vote.go b/consensus/quorum/one-node-staked-vote.go index d8569d44f..eb71303a5 100644 --- a/consensus/quorum/one-node-staked-vote.go +++ b/consensus/quorum/one-node-staked-vote.go @@ -16,8 +16,8 @@ import ( var ( twoThird = numeric.NewDec(2).Quo(numeric.NewDec(3)) ninetyPercent = numeric.MustNewDecFromStr("0.90") - harmonysShare = numeric.MustNewDecFromStr("0.68") - stakersShare = numeric.MustNewDecFromStr("0.32") + harmonysShare = numeric.MustNewDecFromStr("0.90") // Change back to 0.68 + stakersShare = numeric.MustNewDecFromStr("0.10") // Change back to 0.32 totalShare = numeric.MustNewDecFromStr("1.00") ) @@ -148,10 +148,10 @@ func (v *stakedVoteWeight) SetVoters( v.stakedTotal = numeric.ZeroDec() for i := range staked { - if staked[i].StakeWithDelegationApplied == nil { + if staked[i].TotalStake == nil { v.hmySlotCount++ } else { - v.stakedTotal = v.stakedTotal.Add(*staked[i].StakeWithDelegationApplied) + v.stakedTotal = v.stakedTotal.Add(*staked[i].TotalStake) } } @@ -169,9 +169,9 @@ func (v *stakedVoteWeight) SetVoters( } // Real Staker - if staked[i].StakeWithDelegationApplied != nil { + if staked[i].TotalStake != nil { member.isHarmonyNode = false - member.effectivePercent = staked[i].StakeWithDelegationApplied. + member.effectivePercent = staked[i].TotalStake. Quo(v.stakedTotal). Mul(stakersShare) theirPercentage = theirPercentage.Add(member.effectivePercent) diff --git a/core/blockchain.go b/core/blockchain.go index 07f5abd8e..2a166f6c7 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1156,7 +1156,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. shard := (*shardState)[i] for j := range shard.Slots { slot := shard.Slots[j] - if slot.StakeWithDelegationApplied != nil { // For external validator + if slot.TotalStake != nil { // For external validator _, ok := processed[slot.EcdsaAddress] if !ok { processed[slot.EcdsaAddress] = struct{}{} @@ -1963,6 +1963,7 @@ func (bc *BlockChain) GetVrfByNumber(number uint64) []byte { // GetShardState returns the shard state for the given epoch, // creating one if needed. +// TODO: [STAKING] func (bc *BlockChain) GetShardState(epoch *big.Int) (shard.State, error) { shardState, err := bc.ReadShardState(epoch) if err == nil { // TODO ek – distinguish ErrNotFound @@ -1971,7 +1972,7 @@ func (bc *BlockChain) GetShardState(epoch *big.Int) (shard.State, error) { if epoch.Cmp(big.NewInt(GenesisEpoch)) == 0 { shardState, err = committee.WithStakingEnabled.Compute( - big.NewInt(GenesisEpoch), bc.Config(), nil, + big.NewInt(GenesisEpoch), nil, ) } else { prevEpoch := new(big.Int).Sub(epoch, common.Big1) diff --git a/internal/chain/engine.go b/internal/chain/engine.go index 3a543750e..12ef426fe 100644 --- a/internal/chain/engine.go +++ b/internal/chain/engine.go @@ -225,7 +225,7 @@ func QuorumForBlock( ) (quorum int, err error) { var ss shard.State if reCalculate { - ss, _ = committee.WithStakingEnabled.Compute(h.Epoch(), chain.Config(), chain) + ss, _ = committee.WithStakingEnabled.Compute(h.Epoch(), chain) } else { ss, err = chain.ReadShardState(h.Epoch()) if err != nil { @@ -284,7 +284,7 @@ func GetPublicKeys(chain engine.ChainReader, header *block.Header, reCalculate b var shardState shard.State var err error if reCalculate { - shardState, _ = committee.WithStakingEnabled.Compute(header.Epoch(), chain.Config(), chain) + shardState, _ = committee.WithStakingEnabled.Compute(header.Epoch(), chain) } else { shardState, err = chain.ReadShardState(header.Epoch()) if err != nil { diff --git a/node/node.go b/node/node.go index 7401e3daf..17915e2f5 100644 --- a/node/node.go +++ b/node/node.go @@ -486,8 +486,11 @@ func (node *Node) InitConsensusWithValidators() (err error) { Uint32("shardID", shardID). Uint64("epoch", epoch.Uint64()). Msg("[InitConsensusWithValidators] Try To Get PublicKeys") - pubKeys := committee.WithStakingEnabled.ComputePublicKeys( + shardState, err := committee.WithStakingEnabled.Compute( epoch, node.Consensus.ChainReader, + ) + pubKeys := committee.WithStakingEnabled.GetCommitteePublicKeys( + shardState, )[int(shardID)] if len(pubKeys) == 0 { utils.Logger().Error(). diff --git a/node/node_genesis.go b/node/node_genesis.go index 4ce9bd1ff..5a01caaac 100644 --- a/node/node_genesis.go +++ b/node/node_genesis.go @@ -41,7 +41,7 @@ type genesisInitializer struct { // InitChainDB sets up a new genesis block in the database for the given shard. func (gi *genesisInitializer) InitChainDB(db ethdb.Database, shardID uint32) error { shardState, _ := committee.WithStakingEnabled.Compute( - big.NewInt(core.GenesisEpoch), &gi.node.chainConfig, nil, + big.NewInt(core.GenesisEpoch), nil, ) if shardID != shard.BeaconChainShardID { // store only the local shard for shard chains diff --git a/node/node_handler.go b/node/node_handler.go index e5954c008..637eb7b53 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -8,7 +8,6 @@ import ( "sync/atomic" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/harmony-one/bls/ffi/go/bls" @@ -16,7 +15,6 @@ import ( proto_discovery "github.com/harmony-one/harmony/api/proto/discovery" proto_node "github.com/harmony-one/harmony/api/proto/node" "github.com/harmony-one/harmony/block" - "github.com/harmony-one/harmony/consensus/quorum" "github.com/harmony-one/harmony/core/types" bls2 "github.com/harmony-one/harmony/crypto/bls" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" @@ -26,7 +24,6 @@ import ( "github.com/harmony-one/harmony/p2p" "github.com/harmony-one/harmony/p2p/host" "github.com/harmony-one/harmony/shard" - "github.com/harmony-one/harmony/shard/committee" staking "github.com/harmony-one/harmony/staking/types" libp2p_peer "github.com/libp2p/go-libp2p-core/peer" ) @@ -377,51 +374,7 @@ func (node *Node) PostConsensusProcessing(newBlock *types.Block, commitSigAndBit // Update consensus keys at last so the change of leader status doesn't mess up normal flow if len(newBlock.Header().ShardState()) > 0 { - next := new(big.Int).Add(newBlock.Epoch(), common.Big1) - if node.chainConfig.StakingEpoch.Cmp(next) == 0 && - node.Consensus.Decider.Policy() != quorum.SuperMajorityStake { - node.Consensus.Decider = quorum.NewDecider(quorum.SuperMajorityStake) - node.Consensus.Decider.SetShardIDProvider(func() (uint32, error) { - return node.Consensus.ShardID, nil - }) - s, _ := committee.WithStakingEnabled.Compute( - next, &node.chainConfig, node.Consensus.ChainReader, - ) - - prevSubCommitteeDump := node.Consensus.Decider.JSON() - - if _, err := node.Consensus.Decider.SetVoters( - s.FindCommitteeByID(node.Consensus.ShardID).Slots, - ); err != nil { - utils.Logger().Error(). - Err(err). - Uint32("shard", node.Consensus.ShardID). - Msg("Error when updating voting power") - return - } - - utils.Logger().Info(). - Uint64("block-number", newBlock.Number().Uint64()). - Uint64("epoch", newBlock.Epoch().Uint64()). - Uint32("shard-id", node.Consensus.ShardID). - RawJSON("prev-subcommittee", []byte(prevSubCommitteeDump)). - RawJSON("current-subcommittee", []byte(node.Consensus.Decider.JSON())). - Msg("changing committee") - } - // TODO Need to refactor UpdateConsensusInformation so can fold the following logic - // into UCI - todo because UCI mutates state & called in overloaded contexts node.Consensus.UpdateConsensusInformation() - - if shard.Schedule.IsLastBlock(newBlock.Number().Uint64()) { - if node.chainConfig.StakingEpoch.Cmp(next) == 0 { - // Hit this case again, need after UpdateConsensus - curPubKeys := committee.WithStakingEnabled.ComputePublicKeys( - next, node.Consensus.ChainReader, - )[int(node.Consensus.ShardID)] - node.Consensus.Decider.UpdateParticipants(curPubKeys) - } - } - } // TODO chao: uncomment this after beacon syncing is stable diff --git a/node/worker/worker.go b/node/worker/worker.go index 8163f2c0c..255e1428c 100644 --- a/node/worker/worker.go +++ b/node/worker/worker.go @@ -295,13 +295,13 @@ func (w *Worker) SuperCommitteeForNextEpoch( if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) { nextCommittee, oops = committee.WithStakingEnabled.Compute( new(big.Int).Add(w.current.header.Epoch(), common.Big1), - w.config, beacon, ) } default: // WARN When we first enable staking, this condition may not be robust by itself. + // TODO: needs to make sure beacon chain sync works. if w.config.IsStaking(w.current.header.Epoch()) { switch beacon.CurrentHeader().Epoch().Cmp(w.current.header.Epoch()) { case 1: @@ -313,7 +313,6 @@ func (w *Worker) SuperCommitteeForNextEpoch( if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) { nextCommittee, oops = committee.WithStakingEnabled.Compute( new(big.Int).Add(w.current.header.Epoch(), common.Big1), - w.config, beacon, ) } diff --git a/shard/committee/assignment.go b/shard/committee/assignment.go index 32e9a4de3..9a5e1825a 100644 --- a/shard/committee/assignment.go +++ b/shard/committee/assignment.go @@ -19,14 +19,14 @@ import ( // ValidatorListProvider .. type ValidatorListProvider interface { Compute( - epoch *big.Int, config *params.ChainConfig, reader DataProvider, + epoch *big.Int, reader DataProvider, ) (shard.State, error) ReadFromDB(epoch *big.Int, reader DataProvider) (shard.State, error) } // PublicKeysProvider per epoch type PublicKeysProvider interface { - ComputePublicKeys(epoch *big.Int, reader DataProvider) [][]*bls.PublicKey + GetCommitteePublicKeys(superComm shard.State) [][]*bls.PublicKey ReadPublicKeysFromDB( hash common.Hash, reader DataProvider, ) ([]*bls.PublicKey, error) @@ -123,12 +123,15 @@ func eposStakedCommittee( // TODO benchmark difference if went with data structure that sorts on insert for i := range candidates { - // TODO Should be using .ValidatorStakingWithDelegation, not implemented yet validator, err := stakerReader.ReadValidatorData(candidates[i]) validatorStake := big.NewInt(0) + utils.Logger().Print("TEST-VALIDATOR") + utils.Logger().Print(validator) for _, delegation := range validator.Delegations { validatorStake.Add(validatorStake, delegation.Amount) } + utils.Logger().Print("TEST-VALIDATOR2") + utils.Logger().Print(validatorStake) if err != nil { return nil, err } @@ -184,13 +187,8 @@ func eposStakedCommittee( return superComm, nil } -// ComputePublicKeys produces publicKeys of entire supercommittee per epoch -func (def partialStakingEnabled) ComputePublicKeys( - epoch *big.Int, d DataProvider, -) [][]*bls.PublicKey { - config := d.Config() - superComm, _ := def.Compute(epoch, config, d) - +// GetCommitteePublicKeys produces publicKeys of entire supercommittee per epoch +func (def partialStakingEnabled) GetCommitteePublicKeys(superComm shard.State) [][]*bls.PublicKey { allIdentities := make([][]*bls.PublicKey, len(superComm)) for i := range superComm { @@ -245,10 +243,18 @@ func (def partialStakingEnabled) ReadFromDB( // ReadFromComputation is single entry point for reading the State of the network func (def partialStakingEnabled) Compute( - epoch *big.Int, config *params.ChainConfig, stakerReader DataProvider, + epoch *big.Int, stakerReader DataProvider, ) (newSuperComm shard.State, err error) { + preStaking := true + if stakerReader != nil { + config := stakerReader.Config() + if config.IsStaking(epoch) { + preStaking = false + } + } + instance := shard.Schedule.InstanceForEpoch(epoch) - if !config.IsStaking(epoch) { + if preStaking { return preStakingEnabledCommittee(instance), nil } stakedSlots := diff --git a/shard/shard_state.go b/shard/shard_state.go index 2295e53bc..cbc07a48d 100644 --- a/shard/shard_state.go +++ b/shard/shard_state.go @@ -39,7 +39,7 @@ type Slot struct { EcdsaAddress common.Address `json:"ecdsa-address"` BlsPublicKey BlsPublicKey `json:"bls-pubkey"` // nil means our node, 0 means not active, > 0 means staked node - StakeWithDelegationApplied *numeric.Dec `json:"staked-validator" rlp:"nil"` + TotalStake *numeric.Dec `json:"total-stake" rlp:"nil"` } // SlotList is a list of SlotList. @@ -71,7 +71,7 @@ func (ss State) JSON() string { for j := range ss[i].Slots { n := ss[i].Slots[j] dump[i].NodeList[j].BlsPublicKey = n.BlsPublicKey - dump[i].NodeList[j].StakeWithDelegationApplied = n.StakeWithDelegationApplied + dump[i].NodeList[j].TotalStake = n.TotalStake dump[i].NodeList[j].EcdsaAddress = common2.MustAddressToBech32(n.EcdsaAddress) } } @@ -266,5 +266,9 @@ func (n Slot) Serialize() []byte { } func (n Slot) String() string { - return "ECDSA: " + common2.MustAddressToBech32(n.EcdsaAddress) + ", BLS: " + hex.EncodeToString(n.BlsPublicKey[:]) + total := "nil" + if n.TotalStake != nil { + total = n.TotalStake.String() + } + return "ECDSA: " + common2.MustAddressToBech32(n.EcdsaAddress) + ", BLS: " + hex.EncodeToString(n.BlsPublicKey[:]) + ", TotalStake: " + total } diff --git a/test/configs/local-resharding.txt b/test/configs/local-resharding.txt index 42ac0fce0..39cab2b2b 100644 --- a/test/configs/local-resharding.txt +++ b/test/configs/local-resharding.txt @@ -8,19 +8,18 @@ 127.0.0.1 9007 validator one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9 c4e4708b6cf2a2ceeb59981677e9821eebafc5cf483fb5364a28fa604cc0ce69beeed40f3f03815c9e196fdaec5f1097 127.0.0.1 9008 validator one1d2rngmem4x2c6zxsjjz29dlah0jzkr0k2n88wc 86dc2fdc2ceec18f6923b99fd86a68405c132e1005cf1df72dca75db0adfaeb53d201d66af37916d61f079f34f21fb96 127.0.0.1 9009 validator one1658znfwf40epvy7e46cqrmzyy54h4n0qa73nep 49d15743b36334399f9985feb0753430a2b287b2d68b84495bbb15381854cbf01bca9d1d9f4c9c8f18509b2bfa6bd40f -127.0.0.1 9010 validator one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe 52ecce5f64db21cbe374c9268188f5d2cdd5bec1a3112276a350349860e35fb81f8cfe447a311e0550d961cf25cb988d -127.0.0.1 9011 validator one1uyshu2jgv8w465yc8kkny36thlt2wvel89tcmg a547a9bf6fdde4f4934cde21473748861a3cc0fe8bbb5e57225a29f483b05b72531f002f8187675743d819c955a86100 -127.0.0.1 9012 validator one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7 678ec9670899bf6af85b877058bea4fc1301a5a3a376987e826e3ca150b80e3eaadffedad0fedfa111576fa76ded980c -127.0.0.1 9013 validator one129r9pj3sk0re76f7zs3qz92rggmdgjhtwge62k 63f479f249c59f0486fda8caa2ffb247209489dae009dfde6144ff38c370230963d360dffd318cfb26c213320e89a512 -127.0.0.1 9099 explorer +127.0.0.1 9010 validator one1z05g55zamqzfw9qs432n33gycdmyvs38xjemyl 95117937cd8c09acd2dfae847d74041a67834ea88662a7cbed1e170350bc329e53db151e5a0ef3e712e35287ae954818 +127.0.0.1 9011 validator one1ljznytjyn269azvszjlcqvpcj6hjm822yrcp2e 68ae289d73332872ec8d04ac256ca0f5453c88ad392730c5741b6055bc3ec3d086ab03637713a29f459177aaa8340615 -127.0.0.1 9100 validator one1ghkz3frhske7emk79p7v2afmj4a5t0kmjyt4s5 eca09c1808b729ca56f1b5a6a287c6e1c3ae09e29ccf7efa35453471fcab07d9f73cee249e2b91f5ee44eb9618be3904 -127.0.0.1 9101 validator one1d7jfnr6yraxnrycgaemyktkmhmajhp8kl0yahv f47238daef97d60deedbde5302d05dea5de67608f11f406576e363661f7dcbc4a1385948549b31a6c70f6fde8a391486 -127.0.0.1 9102 validator one1r4zyyjqrulf935a479sgqlpa78kz7zlcg2jfen fc4b9c535ee91f015efff3f32fbb9d32cdd9bfc8a837bb3eee89b8fff653c7af2050a4e147ebe5c7233dc2d5df06ee0a -127.0.0.1 9103 validator one1p7ht2d4kl8ve7a8jxw746yfnx4wnfxtp8jqxwe ca86e551ee42adaaa6477322d7db869d3e203c00d7b86c82ebee629ad79cb6d57b8f3db28336778ec2180e56a8e07296 -127.0.0.1 9104 validator one1z05g55zamqzfw9qs432n33gycdmyvs38xjemyl 95117937cd8c09acd2dfae847d74041a67834ea88662a7cbed1e170350bc329e53db151e5a0ef3e712e35287ae954818 -127.0.0.1 9105 validator one1ljznytjyn269azvszjlcqvpcj6hjm822yrcp2e 68ae289d73332872ec8d04ac256ca0f5453c88ad392730c5741b6055bc3ec3d086ab03637713a29f459177aaa8340615 -127.0.0.1 9107 validator one1uyshu2jgv8w465yc8kkny36thlt2wvel89tcmg a547a9bf6fdde4f4934cde21473748861a3cc0fe8bbb5e57225a29f483b05b72531f002f8187675743d819c955a86100 -127.0.0.1 9108 validator one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7 678ec9670899bf6af85b877058bea4fc1301a5a3a376987e826e3ca150b80e3eaadffedad0fedfa111576fa76ded980c -127.0.0.1 9109 validator one1658znfwf40epvy7e46cqrmzyy54h4n0qa73nep 576d3c48294e00d6be4a22b07b66a870ddee03052fe48a5abbd180222e5d5a1f8946a78d55b025de21635fd743bbad90 -127.0.0.1 9110 validator one1d2rngmem4x2c6zxsjjz29dlah0jzkr0k2n88wc 16513c487a6bb76f37219f3c2927a4f281f9dd3fd6ed2e3a64e500de6545cf391dd973cc228d24f9bd01efe94912e714 + +127.0.0.1 9100 validator one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe 52ecce5f64db21cbe374c9268188f5d2cdd5bec1a3112276a350349860e35fb81f8cfe447a311e0550d961cf25cb988d +127.0.0.1 9101 validator one1uyshu2jgv8w465yc8kkny36thlt2wvel89tcmg a547a9bf6fdde4f4934cde21473748861a3cc0fe8bbb5e57225a29f483b05b72531f002f8187675743d819c955a86100 +127.0.0.1 9102 validator one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7 678ec9670899bf6af85b877058bea4fc1301a5a3a376987e826e3ca150b80e3eaadffedad0fedfa111576fa76ded980c +127.0.0.1 9103 validator one129r9pj3sk0re76f7zs3qz92rggmdgjhtwge62k 63f479f249c59f0486fda8caa2ffb247209489dae009dfde6144ff38c370230963d360dffd318cfb26c213320e89a512 +127.0.0.1 9104 validator one1d2rngmem4x2c6zxsjjz29dlah0jzkr0k2n88wc 16513c487a6bb76f37219f3c2927a4f281f9dd3fd6ed2e3a64e500de6545cf391dd973cc228d24f9bd01efe94912e714 +127.0.0.1 9105 validator one1658znfwf40epvy7e46cqrmzyy54h4n0qa73nep 576d3c48294e00d6be4a22b07b66a870ddee03052fe48a5abbd180222e5d5a1f8946a78d55b025de21635fd743bbad90 +127.0.0.1 9106 validator one1ghkz3frhske7emk79p7v2afmj4a5t0kmjyt4s5 eca09c1808b729ca56f1b5a6a287c6e1c3ae09e29ccf7efa35453471fcab07d9f73cee249e2b91f5ee44eb9618be3904 +127.0.0.1 9107 validator one1d7jfnr6yraxnrycgaemyktkmhmajhp8kl0yahv f47238daef97d60deedbde5302d05dea5de67608f11f406576e363661f7dcbc4a1385948549b31a6c70f6fde8a391486 +127.0.0.1 9108 validator one1r4zyyjqrulf935a479sgqlpa78kz7zlcg2jfen fc4b9c535ee91f015efff3f32fbb9d32cdd9bfc8a837bb3eee89b8fff653c7af2050a4e147ebe5c7233dc2d5df06ee0a +127.0.0.1 9109 validator one1p7ht2d4kl8ve7a8jxw746yfnx4wnfxtp8jqxwe ca86e551ee42adaaa6477322d7db869d3e203c00d7b86c82ebee629ad79cb6d57b8f3db28336778ec2180e56a8e07296 +127.0.0.1 9099 explorer \ No newline at end of file From e79ba5fe880b56973a50a80d918b9145fce07cf0 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 23 Nov 2019 22:20:52 -0800 Subject: [PATCH 2/5] Make shard chain follow beacon chain's epoch after stakingEpoch --- block/factory/factory.go | 6 ++-- consensus/consensus_service.go | 21 ++++++------ consensus/consensus_v2.go | 1 + core/blockchain.go | 34 +------------------ internal/params/config.go | 51 ++++++++++++++++++----------- node/node.go | 25 +++++++------- node/node_handler.go | 7 +++- node/node_newblock.go | 18 ++++++---- node/worker/worker.go | 51 ++++++++++++++++++++++------- shard/committee/assignment.go | 60 +++++----------------------------- 10 files changed, 126 insertions(+), 148 deletions(-) diff --git a/block/factory/factory.go b/block/factory/factory.go index dcef0c142..6c3e3aac9 100644 --- a/block/factory/factory.go +++ b/block/factory/factory.go @@ -30,11 +30,11 @@ func NewFactory(chainConfig *params.ChainConfig) Factory { func (f *factory) NewHeader(epoch *big.Int) *block.Header { var impl blockif.Header switch { - case epoch.Cmp(f.chainConfig.StakingEpoch) >= 0: + case f.chainConfig.IsPreStaking(epoch): impl = v3.NewHeader() - case epoch.Cmp(f.chainConfig.CrossLinkEpoch) >= 0: + case f.chainConfig.IsCrossLink(epoch): impl = v2.NewHeader() - case epoch.Cmp(f.chainConfig.CrossTxEpoch) >= 0: + case f.chainConfig.IsCrossTx(epoch): impl = v1.NewHeader() default: impl = v0.NewHeader() diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index ffe8a2fd9..fed3145e5 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -465,7 +465,7 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { consensus.Decider.SetShardIDProvider(func() (uint32, error) { return consensus.ShardID, nil }) - s, err := committee.WithStakingEnabled.Compute( + s, err := committee.WithStakingEnabled.ReadFromDB( next, consensus.ChainReader, ) @@ -473,12 +473,10 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { utils.Logger().Error(). Err(err). Uint32("shard", consensus.ShardID). - Msg("Error when computing committee with staking") + Msg("Error when reading staking committee") return Syncing } - utils.Logger().Print("XXXXXXXX") - utils.Logger().Print(s.FindCommitteeByID(consensus.ShardID).Slots) if _, err := consensus.Decider.SetVoters( s.FindCommitteeByID(consensus.ShardID).Slots, ); err != nil { @@ -504,7 +502,7 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { epoch := header.Epoch() // TODO: change GetCommitteePublicKeys to read from DB - curShardState, err := committee.WithStakingEnabled.Compute( + curShardState, err := committee.WithStakingEnabled.ReadFromDB( epoch, consensus.ChainReader, ) if err != nil { @@ -514,9 +512,11 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { Msg("Error retrieving current shard state") return Syncing } + + curCommittee := curShardState.FindCommitteeByID(header.ShardID()) curPubKeys := committee.WithStakingEnabled.GetCommitteePublicKeys( - curShardState, - )[int(header.ShardID())] + curCommittee, + ) consensus.numPrevPubKeys = len(curPubKeys) consensus.getLogger().Info().Msg("[UpdateConsensusInformation] Updating.....") if shard.Schedule.IsLastBlock(header.Number().Uint64()) { @@ -525,7 +525,7 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { consensus.getLogger().Info().Uint64("headerNum", header.Number().Uint64()). Msg("[UpdateConsensusInformation] Epoch updated for next epoch") - nextShardState, err := committee.WithStakingEnabled.Compute( + nextShardState, err := committee.WithStakingEnabled.ReadFromDB( new(big.Int).Add(epoch, common.Big1), consensus.ChainReader, ) if err != nil { @@ -536,9 +536,10 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { return Syncing } + nextCommittee := nextShardState.FindCommitteeByID(header.ShardID()) pubKeys = committee.WithStakingEnabled.GetCommitteePublicKeys( - nextShardState, - )[int(header.ShardID())] + nextCommittee, + ) } else { consensus.SetEpochNum(epoch.Uint64()) pubKeys = curPubKeys diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index f731635e0..a97025d42 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -830,6 +830,7 @@ func (consensus *Consensus) finalizeCommits() { utils.Logger().Info(). Uint64("blockNum", block.NumberU64()). + Uint64("epochNum", block.Epoch().Uint64()). Uint64("ViewId", block.Header().ViewID().Uint64()). Str("blockHash", block.Hash().String()). Int("index", consensus.Decider.IndexOf(consensus.PubKey)). diff --git a/core/blockchain.go b/core/blockchain.go index 2a166f6c7..8d1e684ff 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -28,6 +28,7 @@ import ( "time" "github.com/harmony-one/harmony/crypto/bls" + lru "github.com/hashicorp/golang-lru" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" @@ -48,9 +49,7 @@ import ( "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/shard" - "github.com/harmony-one/harmony/shard/committee" staking "github.com/harmony-one/harmony/staking/types" - lru "github.com/hashicorp/golang-lru" ) var ( @@ -1961,37 +1960,6 @@ func (bc *BlockChain) GetVrfByNumber(number uint64) []byte { return header.Vrf() } -// GetShardState returns the shard state for the given epoch, -// creating one if needed. -// TODO: [STAKING] -func (bc *BlockChain) GetShardState(epoch *big.Int) (shard.State, error) { - shardState, err := bc.ReadShardState(epoch) - if err == nil { // TODO ek – distinguish ErrNotFound - return shardState, err - } - - if epoch.Cmp(big.NewInt(GenesisEpoch)) == 0 { - shardState, err = committee.WithStakingEnabled.Compute( - big.NewInt(GenesisEpoch), nil, - ) - } else { - prevEpoch := new(big.Int).Sub(epoch, common.Big1) - shardState, err = committee.WithStakingEnabled.ReadFromDB( - prevEpoch, bc, - ) - } - - if err != nil { - return nil, err - } - err = bc.WriteShardState(epoch, shardState) - if err != nil { - return nil, err - } - utils.Logger().Debug().Str("epoch", epoch.String()).Msg("saved new shard state") - return shardState, nil -} - // ChainDb returns the database func (bc *BlockChain) ChainDb() ethdb.Database { return bc.db } diff --git a/internal/params/config.go b/internal/params/config.go index 5892b2258..6c5f403e0 100644 --- a/internal/params/config.go +++ b/internal/params/config.go @@ -23,33 +23,36 @@ var EpochTBD = big.NewInt(10000000) var ( // MainnetChainConfig is the chain parameters to run a node on the main network. MainnetChainConfig = &ChainConfig{ - ChainID: MainnetChainID, - CrossTxEpoch: big.NewInt(28), - CrossLinkEpoch: EpochTBD, - StakingEpoch: EpochTBD, - EIP155Epoch: big.NewInt(28), - S3Epoch: big.NewInt(28), + ChainID: MainnetChainID, + CrossTxEpoch: big.NewInt(28), + CrossLinkEpoch: EpochTBD, + StakingEpoch: EpochTBD, + PreStakingEpoch: EpochTBD, + EIP155Epoch: big.NewInt(28), + S3Epoch: big.NewInt(28), } // TestnetChainConfig contains the chain parameters to run a node on the harmony test network. TestnetChainConfig = &ChainConfig{ - ChainID: TestnetChainID, - CrossTxEpoch: big.NewInt(0), - CrossLinkEpoch: big.NewInt(0), - StakingEpoch: big.NewInt(3), - EIP155Epoch: big.NewInt(0), - S3Epoch: big.NewInt(0), + ChainID: TestnetChainID, + CrossTxEpoch: big.NewInt(0), + CrossLinkEpoch: big.NewInt(2), + StakingEpoch: big.NewInt(3), + PreStakingEpoch: big.NewInt(0), + EIP155Epoch: big.NewInt(0), + S3Epoch: big.NewInt(0), } // PangaeaChainConfig contains the chain parameters for the Pangaea network. // All features except for CrossLink are enabled at launch. PangaeaChainConfig = &ChainConfig{ - ChainID: PangaeaChainID, - CrossTxEpoch: big.NewInt(0), - CrossLinkEpoch: EpochTBD, - StakingEpoch: EpochTBD, - EIP155Epoch: big.NewInt(0), - S3Epoch: big.NewInt(0), + ChainID: PangaeaChainID, + CrossTxEpoch: big.NewInt(0), + CrossLinkEpoch: EpochTBD, + StakingEpoch: EpochTBD, + PreStakingEpoch: EpochTBD, + EIP155Epoch: big.NewInt(0), + S3Epoch: big.NewInt(0), } // AllProtocolChanges ... @@ -60,6 +63,7 @@ var ( big.NewInt(0), // CrossTxEpoch big.NewInt(0), // CrossLinkEpoch big.NewInt(0), // StakingEpoch + big.NewInt(0), // PreStakingEpoch big.NewInt(0), // EIP155Epoch big.NewInt(0), // S3Epoch } @@ -72,6 +76,7 @@ var ( big.NewInt(0), // CrossTxEpoch big.NewInt(0), // CrossLinkEpoch big.NewInt(0), // StakingEpoch + big.NewInt(0), // PreStakingEpoch big.NewInt(0), // EIP155Epoch big.NewInt(0), // S3Epoch } @@ -108,9 +113,12 @@ type ChainConfig struct { // cross-shard links. CrossLinkEpoch *big.Int `json:"crossLinkEpoch,omitempty"` - // StakingEpoch is the epoch we allow staking transactions + // StakingEpoch is the epoch when shard assign takes staking into account StakingEpoch *big.Int `json:"stakingEpoch,omitempty"` + // PreStakingEpoch is the epoch we allow staking transactions + PreStakingEpoch *big.Int `json:"preStakingEpoch,omitempty"` + EIP155Epoch *big.Int `json:"eip155Epoch,omitempty"` // EIP155 hard fork epoch (include EIP158 too) S3Epoch *big.Int `json:"s3Epoch,omitempty"` // S3 epoch is the first epoch containing S3 mainnet and all ethereum update up to Constantinople } @@ -151,6 +159,11 @@ func (c *ChainConfig) IsStaking(epoch *big.Int) bool { return isForked(c.StakingEpoch, epoch) } +// IsPreStaking determines whether staking transactions are allowed +func (c *ChainConfig) IsPreStaking(epoch *big.Int) bool { + return isForked(c.PreStakingEpoch, epoch) +} + // IsCrossLink returns whether epoch is either equal to the CrossLink fork epoch or greater. func (c *ChainConfig) IsCrossLink(epoch *big.Int) bool { return isForked(c.CrossLinkEpoch, epoch) diff --git a/node/node.go b/node/node.go index 17915e2f5..3647568ac 100644 --- a/node/node.go +++ b/node/node.go @@ -278,17 +278,20 @@ func (node *Node) addPendingTransactions(newTxs types.Transactions) { // Add new staking transactions to the pending staking transaction list. func (node *Node) addPendingStakingTransactions(newStakingTxs staking.StakingTransactions) { txPoolLimit := 1000 // TODO: incorporate staking txn into TxPool - node.pendingStakingTxMutex.Lock() - for _, tx := range newStakingTxs { - if _, ok := node.pendingStakingTransactions[tx.Hash()]; !ok { - node.pendingStakingTransactions[tx.Hash()] = tx - } - if len(node.pendingStakingTransactions) > txPoolLimit { - break + + if node.Blockchain().Config().IsPreStaking(node.Worker.GetNewEpoch()) { + node.pendingStakingTxMutex.Lock() + for _, tx := range newStakingTxs { + if _, ok := node.pendingStakingTransactions[tx.Hash()]; !ok { + node.pendingStakingTransactions[tx.Hash()] = tx + } + if len(node.pendingStakingTransactions) > txPoolLimit { + break + } } + utils.Logger().Info().Int("length of newStakingTxs", len(newStakingTxs)).Int("totalPending", len(node.pendingStakingTransactions)).Msg("Got more staking transactions") + node.pendingStakingTxMutex.Unlock() } - utils.Logger().Info().Int("length of newStakingTxs", len(newStakingTxs)).Int("totalPending", len(node.pendingStakingTransactions)).Msg("Got more staking transactions") - node.pendingStakingTxMutex.Unlock() } // AddPendingStakingTransaction staking transactions @@ -490,8 +493,8 @@ func (node *Node) InitConsensusWithValidators() (err error) { epoch, node.Consensus.ChainReader, ) pubKeys := committee.WithStakingEnabled.GetCommitteePublicKeys( - shardState, - )[int(shardID)] + shardState.FindCommitteeByID(shardID), + ) if len(pubKeys) == 0 { utils.Logger().Error(). Uint32("shardID", shardID). diff --git a/node/node_handler.go b/node/node_handler.go index 637eb7b53..1b1c40195 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -342,7 +342,12 @@ func (node *Node) PostConsensusProcessing(newBlock *types.Block, commitSigAndBit node.BroadcastCXReceipts(newBlock, commitSigAndBitmap) } else { utils.Logger().Info(). - Uint64("BlockNum", newBlock.NumberU64()). + Uint64("blockNum", newBlock.NumberU64()). + Uint64("epochNum", newBlock.Epoch().Uint64()). + Uint64("ViewId", newBlock.Header().ViewID().Uint64()). + Str("blockHash", newBlock.Hash().String()). + Int("numTxns", len(newBlock.Transactions())). + Int("numStakingTxns", len(newBlock.StakingTransactions())). Msg("BINGO !!! Reached Consensus") // 15% of the validator also need to do broadcasting rand.Seed(time.Now().UTC().UnixNano()) diff --git a/node/node_newblock.go b/node/node_newblock.go index b8dec7191..3f5c3d809 100644 --- a/node/node_newblock.go +++ b/node/node_newblock.go @@ -78,6 +78,7 @@ func (node *Node) WaitForConsensusReadyV2(readySignal chan struct{}, stopChan ch func (node *Node) proposeNewBlock() (*types.Block, error) { // Update worker's current header and state data in preparation to propose/process new transactions coinbase := node.Consensus.SelfAddress + node.Worker.UpdateCurrent(coinbase) // Prepare transactions including staking transactions pending, err := node.TxPool.Pending() @@ -88,14 +89,16 @@ func (node *Node) proposeNewBlock() (*types.Block, error) { // TODO: integrate staking transaction into tx pool pendingStakingTransactions := types2.StakingTransactions{} - node.pendingStakingTxMutex.Lock() - for _, tx := range node.pendingStakingTransactions { - pendingStakingTransactions = append(pendingStakingTransactions, tx) + // Only process staking transactions after pre-staking epoch happened. + if node.Blockchain().Config().IsPreStaking(node.Worker.GetNewEpoch()) { + node.pendingStakingTxMutex.Lock() + for _, tx := range node.pendingStakingTransactions { + pendingStakingTransactions = append(pendingStakingTransactions, tx) + } + node.pendingStakingTransactions = make(map[common.Hash]*types2.StakingTransaction) + node.pendingStakingTxMutex.Unlock() } - node.pendingStakingTransactions = make(map[common.Hash]*types2.StakingTransaction) - node.pendingStakingTxMutex.Unlock() - node.Worker.UpdateCurrent(coinbase) if err := node.Worker.CommitTransactions(pending, pendingStakingTransactions, coinbase); err != nil { utils.Logger().Error().Err(err).Msg("cannot commit transactions") return nil, err @@ -119,8 +122,9 @@ func (node *Node) proposeNewBlock() (*types.Block, error) { } // Prepare shard state + // NOTE: this will potentially override shard chain's epoch to beacon chain's epoch during staking migration period. shardState, err := node.Worker.SuperCommitteeForNextEpoch( - node.Consensus.ShardID, node.Blockchain(), + node.Consensus.ShardID, node.Beaconchain(), ) if err != nil { diff --git a/node/worker/worker.go b/node/worker/worker.go index 255e1428c..f728a6c8d 100644 --- a/node/worker/worker.go +++ b/node/worker/worker.go @@ -288,38 +288,65 @@ func (w *Worker) SuperCommitteeForNextEpoch( ) (shard.State, error) { var ( nextCommittee shard.State - oops error + err error ) switch shardID { case shard.BeaconChainShardID: if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) { - nextCommittee, oops = committee.WithStakingEnabled.Compute( + nextCommittee, err = committee.WithStakingEnabled.Compute( new(big.Int).Add(w.current.header.Epoch(), common.Big1), beacon, ) } default: - // WARN When we first enable staking, this condition may not be robust by itself. - // TODO: needs to make sure beacon chain sync works. + beaconEpoch := beacon.CurrentHeader().Epoch() if w.config.IsStaking(w.current.header.Epoch()) { - switch beacon.CurrentHeader().Epoch().Cmp(w.current.header.Epoch()) { + switch beaconEpoch.Cmp(w.current.header.Epoch()) { case 1: - nextCommittee, oops = committee.WithStakingEnabled.ReadFromDB( - beacon.CurrentHeader().Epoch(), beacon, + // If beacon chain is bigger than shard chain in epoch, it means I should catch up with beacon chain now + nextCommittee, err = committee.WithStakingEnabled.ReadFromDB( + beaconEpoch, beacon, ) + blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) // Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch + utils.Logger().Debug(). + Uint64("blockNum", w.current.header.Number().Uint64()). + Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). + Uint64("myNewEpoch", blockEpoch.Uint64()). + Msg("Propose new epoch as beacon chain's epoch") + w.current.header.SetEpoch(blockEpoch) + + case 0: + // If it's same epoch, no need to propose new shard state (new epoch change) + case -1: + // If beacon chain is behind, shard chain should wait for the beacon chain by not changing epochs. } } else { - if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) { - nextCommittee, oops = committee.WithStakingEnabled.Compute( - new(big.Int).Add(w.current.header.Epoch(), common.Big1), - beacon, + if w.config.IsStaking(beaconEpoch) { + // If beacon is already in staking, but I am not, I should just catch up with beacon chain's epoch + nextCommittee, err = committee.WithStakingEnabled.ReadFromDB( + beaconEpoch, beacon, ) + blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) // Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch + utils.Logger().Debug(). + Uint64("blockNum", w.current.header.Number().Uint64()). + Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). + Uint64("myNewEpoch", blockEpoch.Uint64()). + Msg("Propose one-time catch up with beacon chain's epoch") + w.current.header.SetEpoch(blockEpoch) + } else { + // If both beacon and I are not in staking, do pre-staking committee calculation + if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) { + nextCommittee, err = committee.WithStakingEnabled.Compute( + new(big.Int).Add(w.current.header.Epoch(), common.Big1), + w.chain, + ) + } } } } - return nextCommittee, oops + return nextCommittee, err } // FinalizeNewBlock generate a new block for the next consensus round. diff --git a/shard/committee/assignment.go b/shard/committee/assignment.go index 9a5e1825a..6c61c5b76 100644 --- a/shard/committee/assignment.go +++ b/shard/committee/assignment.go @@ -8,7 +8,6 @@ import ( "github.com/harmony-one/harmony/block" common2 "github.com/harmony-one/harmony/internal/common" shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" - "github.com/harmony-one/harmony/internal/ctxerror" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/shard" @@ -22,19 +21,11 @@ type ValidatorListProvider interface { epoch *big.Int, reader DataProvider, ) (shard.State, error) ReadFromDB(epoch *big.Int, reader DataProvider) (shard.State, error) -} - -// PublicKeysProvider per epoch -type PublicKeysProvider interface { - GetCommitteePublicKeys(superComm shard.State) [][]*bls.PublicKey - ReadPublicKeysFromDB( - hash common.Hash, reader DataProvider, - ) ([]*bls.PublicKey, error) + GetCommitteePublicKeys(committee *shard.Committee) []*bls.PublicKey } // Reader is committee.Reader and it is the API that committee membership assignment needs type Reader interface { - PublicKeysProvider ValidatorListProvider } @@ -187,54 +178,19 @@ func eposStakedCommittee( return superComm, nil } -// GetCommitteePublicKeys produces publicKeys of entire supercommittee per epoch -func (def partialStakingEnabled) GetCommitteePublicKeys(superComm shard.State) [][]*bls.PublicKey { - allIdentities := make([][]*bls.PublicKey, len(superComm)) +// GetCommitteePublicKeys returns the public keys of a shard +func (def partialStakingEnabled) GetCommitteePublicKeys(committee *shard.Committee) []*bls.PublicKey { + allIdentities := make([]*bls.PublicKey, len(committee.Slots)) - for i := range superComm { - allIdentities[i] = make([]*bls.PublicKey, len(superComm[i].Slots)) - for j := range superComm[i].Slots { - identity := &bls.PublicKey{} - superComm[i].Slots[j].BlsPublicKey.ToLibBLSPublicKey(identity) - allIdentities[i][j] = identity - } + for i := range committee.Slots { + identity := &bls.PublicKey{} + committee.Slots[i].BlsPublicKey.ToLibBLSPublicKey(identity) + allIdentities[i] = identity } return allIdentities } -func (def partialStakingEnabled) ReadPublicKeysFromDB( - h common.Hash, reader DataProvider, -) ([]*bls.PublicKey, error) { - header := reader.GetHeaderByHash(h) - shardID := header.ShardID() - superCommittee, err := reader.ReadShardState(header.Epoch()) - if err != nil { - return nil, err - } - subCommittee := superCommittee.FindCommitteeByID(shardID) - if subCommittee == nil { - return nil, ctxerror.New("cannot find shard in the shard state", - "blockNumber", header.Number(), - "shardID", header.ShardID(), - ) - } - committerKeys := []*bls.PublicKey{} - - for i := range subCommittee.Slots { - committerKey := new(bls.PublicKey) - err := subCommittee.Slots[i].BlsPublicKey.ToLibBLSPublicKey(committerKey) - if err != nil { - return nil, ctxerror.New("cannot convert BLS public key", - "blsPublicKey", subCommittee.Slots[i].BlsPublicKey).WithCause(err) - } - committerKeys = append(committerKeys, committerKey) - } - return committerKeys, nil - - return nil, nil -} - func (def partialStakingEnabled) ReadFromDB( epoch *big.Int, reader DataProvider, ) (newSuperComm shard.State, err error) { From 9353092ad450fa5ff15fea3b87b29771cda0e70a Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 23 Nov 2019 22:23:15 -0800 Subject: [PATCH 3/5] Remove local code --- consensus/quorum/one-node-staked-vote.go | 4 ++-- shard/committee/assignment.go | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/consensus/quorum/one-node-staked-vote.go b/consensus/quorum/one-node-staked-vote.go index eb71303a5..a3d3bf220 100644 --- a/consensus/quorum/one-node-staked-vote.go +++ b/consensus/quorum/one-node-staked-vote.go @@ -16,8 +16,8 @@ import ( var ( twoThird = numeric.NewDec(2).Quo(numeric.NewDec(3)) ninetyPercent = numeric.MustNewDecFromStr("0.90") - harmonysShare = numeric.MustNewDecFromStr("0.90") // Change back to 0.68 - stakersShare = numeric.MustNewDecFromStr("0.10") // Change back to 0.32 + harmonysShare = numeric.MustNewDecFromStr("0.68") + stakersShare = numeric.MustNewDecFromStr("0.32") totalShare = numeric.MustNewDecFromStr("1.00") ) diff --git a/shard/committee/assignment.go b/shard/committee/assignment.go index 6c61c5b76..b53d1d108 100644 --- a/shard/committee/assignment.go +++ b/shard/committee/assignment.go @@ -116,13 +116,9 @@ func eposStakedCommittee( for i := range candidates { validator, err := stakerReader.ReadValidatorData(candidates[i]) validatorStake := big.NewInt(0) - utils.Logger().Print("TEST-VALIDATOR") - utils.Logger().Print(validator) for _, delegation := range validator.Delegations { validatorStake.Add(validatorStake, delegation.Amount) } - utils.Logger().Print("TEST-VALIDATOR2") - utils.Logger().Print(validatorStake) if err != nil { return nil, err } From c258ab167fa16f7bcbc192df015b65ed4d61e5e7 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sun, 24 Nov 2019 00:13:09 -0800 Subject: [PATCH 4/5] Fix staking one-time catch up logic --- node/worker/worker.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/node/worker/worker.go b/node/worker/worker.go index f728a6c8d..c2432e593 100644 --- a/node/worker/worker.go +++ b/node/worker/worker.go @@ -308,7 +308,10 @@ func (w *Worker) SuperCommitteeForNextEpoch( nextCommittee, err = committee.WithStakingEnabled.ReadFromDB( beaconEpoch, beacon, ) - blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) // Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch + + // Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch + // This shouldn't be exactly beaconEpoch because the next block will have beaconEpoch + 1 + blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) utils.Logger().Debug(). Uint64("blockNum", w.current.header.Number().Uint64()). Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). @@ -322,20 +325,24 @@ func (w *Worker) SuperCommitteeForNextEpoch( // If beacon chain is behind, shard chain should wait for the beacon chain by not changing epochs. } } else { - if w.config.IsStaking(beaconEpoch) { - // If beacon is already in staking, but I am not, I should just catch up with beacon chain's epoch + beaconEpochSubOne := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) + if w.config.IsStaking(beaconEpochSubOne) { + // If I am not in staking epoch yet and beacon chain already proposed a staking-based shard state, + // which means beacon chain's epoch is greater than stakingEpoch, I should just catch up with beacon chain's epoch nextCommittee, err = committee.WithStakingEnabled.ReadFromDB( beaconEpoch, beacon, ) - blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) // Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch + utils.Logger().Debug(). Uint64("blockNum", w.current.header.Number().Uint64()). Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). - Uint64("myNewEpoch", blockEpoch.Uint64()). + Uint64("myNewEpoch", beaconEpochSubOne.Uint64()). Msg("Propose one-time catch up with beacon chain's epoch") - w.current.header.SetEpoch(blockEpoch) + // Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch + w.current.header.SetEpoch(beaconEpochSubOne) } else { - // If both beacon and I are not in staking, do pre-staking committee calculation + // If I are not in staking nor has beacon chain proposed a staking-based shard state, + // do pre-staking committee calculation if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) { nextCommittee, err = committee.WithStakingEnabled.Compute( new(big.Int).Add(w.current.header.Epoch(), common.Big1), From d9900b44df068d7021c729c09cfc9507f544e430 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sun, 24 Nov 2019 02:45:53 -0800 Subject: [PATCH 5/5] Fix staking epoch logic --- cmd/client/txgen/main.go | 2 +- cmd/harmony/main.go | 11 ++++-- consensus/consensus_service.go | 71 ++++++++++++++++++++-------------- core/state_transition.go | 1 + internal/utils/utils.go | 12 ++++++ node/node_newblock.go | 11 +++++- node/worker/worker.go | 23 +++++------ 7 files changed, 85 insertions(+), 46 deletions(-) diff --git a/cmd/client/txgen/main.go b/cmd/client/txgen/main.go index 2725c055b..5f7e9d54c 100644 --- a/cmd/client/txgen/main.go +++ b/cmd/client/txgen/main.go @@ -219,7 +219,7 @@ syncLoop: Msg("Error when adding new block") } stateMutex.Lock() - if err := txGen.Worker.UpdateCurrent(block.Coinbase()); err != nil { + if err := txGen.Worker.UpdateCurrent(); err != nil { utils.Logger().Warn().Err(err).Msg("(*Worker).UpdateCurrent failed") } stateMutex.Unlock() diff --git a/cmd/harmony/main.go b/cmd/harmony/main.go index 7d62c5c05..fd5dd7e04 100644 --- a/cmd/harmony/main.go +++ b/cmd/harmony/main.go @@ -195,6 +195,7 @@ func setupInitialAccount() (isLeader bool) { pubKey := setupConsensusKey(nodeconfig.GetDefaultConfig()) reshardingEpoch := genesisShardingConfig.ReshardingEpoch() + // TODO: after staking, what if the FN validator uses the old bls pub keys? if reshardingEpoch != nil && len(reshardingEpoch) > 0 { for _, epoch := range reshardingEpoch { config := shard.Schedule.InstanceForEpoch(epoch) @@ -208,12 +209,14 @@ func setupInitialAccount() (isLeader bool) { } if initialAccount == nil { - fmt.Fprintf(os.Stderr, "ERROR cannot find your BLS key in the genesis/FN tables: %s\n", pubKey.SerializeToHexStr()) - os.Exit(100) + initialAccount.ShardID = uint32(*shardID) + initialAccount.BlsPublicKey = pubKey.SerializeToHexStr() + blsAddressBytes := pubKey.GetAddress() + initialAccount.Address = hex.EncodeToString(blsAddressBytes[:]) + } else { + fmt.Printf("My Genesis Account: %v\n", *initialAccount) } - fmt.Printf("My Genesis Account: %v\n", *initialAccount) - return isLeader } diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index fed3145e5..ad5cba684 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -428,15 +428,29 @@ func (consensus *Consensus) getLeaderPubKeyFromCoinbase(header *block.Header) (* ) } committerKey := new(bls.PublicKey) + isStaking := consensus.ChainReader.Config().IsStaking(header.Epoch()) for _, member := range committee.Slots { - if member.EcdsaAddress == header.Coinbase() { - err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) - if err != nil { - return nil, ctxerror.New("cannot convert BLS public key", - "blsPublicKey", member.BlsPublicKey, - "coinbaseAddr", header.Coinbase()).WithCause(err) + if isStaking { + // After staking the coinbase address will be the address of bls public key + if utils.GetAddressFromBlsPubKeyBytes(member.BlsPublicKey[:]) == header.Coinbase() { + err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) + if err != nil { + return nil, ctxerror.New("cannot convert BLS public key", + "blsPublicKey", member.BlsPublicKey, + "coinbaseAddr", header.Coinbase()).WithCause(err) + } + return committerKey, nil + } + } else { + if member.EcdsaAddress == header.Coinbase() { + err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) + if err != nil { + return nil, ctxerror.New("cannot convert BLS public key", + "blsPublicKey", member.BlsPublicKey, + "coinbaseAddr", header.Coinbase()).WithCause(err) + } + return committerKey, nil } - return committerKey, nil } } return nil, ctxerror.New("cannot find corresponding BLS Public Key", "coinbaseAddr", header.Coinbase()) @@ -455,9 +469,10 @@ func (consensus *Consensus) getLeaderPubKeyFromCoinbase(header *block.Header) (* func (consensus *Consensus) UpdateConsensusInformation() Mode { curHeader := consensus.ChainReader.CurrentHeader() - next := new(big.Int).Add(curHeader.Epoch(), common.Big1) - if consensus.ChainReader.Config().IsStaking(next) && - consensus.Decider.Policy() != quorum.SuperMajorityStake { + curEpoch := curHeader.Epoch() + nextEpoch := new(big.Int).Add(curHeader.Epoch(), common.Big1) + if (consensus.ChainReader.Config().IsStaking(nextEpoch) && len(curHeader.ShardState()) > 0 && !consensus.ChainReader.Config().IsStaking(curEpoch)) || + (consensus.ChainReader.Config().IsStaking(curEpoch) && consensus.Decider.Policy() != quorum.SuperMajorityStake) { prevSubCommitteeDump := consensus.Decider.JSON() @@ -466,7 +481,7 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { return consensus.ShardID, nil }) s, err := committee.WithStakingEnabled.ReadFromDB( - next, consensus.ChainReader, + nextEpoch, consensus.ChainReader, ) if err != nil { @@ -489,7 +504,7 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { utils.Logger().Info(). Uint64("block-number", curHeader.Number().Uint64()). - Uint64("epoch", curHeader.Epoch().Uint64()). + Uint64("curEpoch", curHeader.Epoch().Uint64()). Uint32("shard-id", consensus.ShardID). RawJSON("prev-subcommittee", []byte(prevSubCommitteeDump)). RawJSON("current-subcommittee", []byte(consensus.Decider.JSON())). @@ -498,12 +513,10 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { pubKeys := []*bls.PublicKey{} hasError := false - header := consensus.ChainReader.CurrentHeader() - epoch := header.Epoch() // TODO: change GetCommitteePublicKeys to read from DB curShardState, err := committee.WithStakingEnabled.ReadFromDB( - epoch, consensus.ChainReader, + curEpoch, consensus.ChainReader, ) if err != nil { utils.Logger().Error(). @@ -513,35 +526,35 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { return Syncing } - curCommittee := curShardState.FindCommitteeByID(header.ShardID()) + curCommittee := curShardState.FindCommitteeByID(curHeader.ShardID()) curPubKeys := committee.WithStakingEnabled.GetCommitteePublicKeys( curCommittee, ) consensus.numPrevPubKeys = len(curPubKeys) consensus.getLogger().Info().Msg("[UpdateConsensusInformation] Updating.....") - if shard.Schedule.IsLastBlock(header.Number().Uint64()) { - // increase epoch by one if it's the last block - consensus.SetEpochNum(epoch.Uint64() + 1) - consensus.getLogger().Info().Uint64("headerNum", header.Number().Uint64()). - Msg("[UpdateConsensusInformation] Epoch updated for next epoch") + if len(curHeader.ShardState()) > 0 { + // increase curEpoch by one if it's the last block + consensus.SetEpochNum(curEpoch.Uint64() + 1) + consensus.getLogger().Info().Uint64("headerNum", curHeader.Number().Uint64()). + Msg("[UpdateConsensusInformation] Epoch updated for nextEpoch curEpoch") nextShardState, err := committee.WithStakingEnabled.ReadFromDB( - new(big.Int).Add(epoch, common.Big1), consensus.ChainReader, + nextEpoch, consensus.ChainReader, ) if err != nil { utils.Logger().Error(). Err(err). Uint32("shard", consensus.ShardID). - Msg("Error retrieving next shard state") + Msg("Error retrieving nextEpoch shard state") return Syncing } - nextCommittee := nextShardState.FindCommitteeByID(header.ShardID()) + nextCommittee := nextShardState.FindCommitteeByID(curHeader.ShardID()) pubKeys = committee.WithStakingEnabled.GetCommitteePublicKeys( nextCommittee, ) } else { - consensus.SetEpochNum(epoch.Uint64()) + consensus.SetEpochNum(curEpoch.Uint64()) pubKeys = curPubKeys } @@ -558,10 +571,10 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { Msg("[UpdateConsensusInformation] Successfully updated public keys") consensus.UpdatePublicKeys(pubKeys) - // take care of possible leader change during the epoch - if !shard.Schedule.IsLastBlock(header.Number().Uint64()) && - header.Number().Uint64() != 0 { - leaderPubKey, err := consensus.getLeaderPubKeyFromCoinbase(header) + // take care of possible leader change during the curEpoch + if !shard.Schedule.IsLastBlock(curHeader.Number().Uint64()) && + curHeader.Number().Uint64() != 0 { + leaderPubKey, err := consensus.getLeaderPubKeyFromCoinbase(curHeader) if err != nil || leaderPubKey == nil { consensus.getLogger().Debug().Err(err). Msg("[SYNC] Unable to get leaderPubKey from coinbase") diff --git a/core/state_transition.go b/core/state_transition.go index 204007d7c..5304d2814 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -243,6 +243,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo } } st.refundGas() + // TODO: need to move the gas fee to the general block rewards st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) return ret, st.gasUsed(), vmerr != nil, err diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 5222e62e5..e08446242 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -86,6 +86,18 @@ func GetAddressFromBlsPubKey(pubKey *bls.PublicKey) common.Address { return addr } +// GetAddressFromBlsPubKeyBytes return the address object from bls pub key. +func GetAddressFromBlsPubKeyBytes(pubKeyBytes []byte) common.Address { + pubKey := &bls.PublicKey{} + err := pubKey.Deserialize(pubKeyBytes[:]) + addr := common.Address{} + if err != nil { + addrBytes := pubKey.GetAddress() + addr.SetBytes(addrBytes[:]) + } + return addr +} + // GenKeyP2P generates a pair of RSA keys used in libp2p host func GenKeyP2P(ip, port string) (p2p_crypto.PrivKey, p2p_crypto.PubKey, error) { r := mrand.New(mrand.NewSource(int64(GetUniqueIDFromIPPort(ip, port)))) diff --git a/node/node_newblock.go b/node/node_newblock.go index 3f5c3d809..bb33838a3 100644 --- a/node/node_newblock.go +++ b/node/node_newblock.go @@ -78,7 +78,16 @@ func (node *Node) WaitForConsensusReadyV2(readySignal chan struct{}, stopChan ch func (node *Node) proposeNewBlock() (*types.Block, error) { // Update worker's current header and state data in preparation to propose/process new transactions coinbase := node.Consensus.SelfAddress - node.Worker.UpdateCurrent(coinbase) + + // After staking, all coinbase will be the address of bls pub key + if node.Blockchain().Config().IsStaking(node.Worker.GetNewEpoch()) { + addr := common.Address{} + blsPubKeyBytes := node.Consensus.PubKey.GetAddress() + addr.SetBytes(blsPubKeyBytes[:]) + coinbase = addr + } + + node.Worker.UpdateCurrent() // Prepare transactions including staking transactions pending, err := node.TxPool.Pending() diff --git a/node/worker/worker.go b/node/worker/worker.go index c2432e593..ba76278eb 100644 --- a/node/worker/worker.go +++ b/node/worker/worker.go @@ -215,7 +215,7 @@ func (w *Worker) CommitReceipts(receiptsList []*types.CXReceiptsProof) error { } // UpdateCurrent updates the current environment with the current state and header. -func (w *Worker) UpdateCurrent(coinbase common.Address) error { +func (w *Worker) UpdateCurrent() error { parent := w.chain.CurrentBlock() num := parent.Number() timestamp := time.Now().Unix() @@ -227,7 +227,6 @@ func (w *Worker) UpdateCurrent(coinbase common.Address) error { GasLimit(core.CalcGasLimit(parent, w.gasFloor, w.gasCeil)). Time(big.NewInt(timestamp)). ShardID(w.chain.ShardID()). - Coinbase(coinbase). Header() return w.makeCurrent(parent, header) } @@ -301,7 +300,9 @@ func (w *Worker) SuperCommitteeForNextEpoch( default: // TODO: needs to make sure beacon chain sync works. beaconEpoch := beacon.CurrentHeader().Epoch() - if w.config.IsStaking(w.current.header.Epoch()) { + nextEpoch := new(big.Int).Add(w.current.header.Epoch(), common.Big1) + if w.config.IsStaking(nextEpoch) { + // If next epoch is staking epoch, I should wait and listen for beacon chain for epoch changes switch beaconEpoch.Cmp(w.current.header.Epoch()) { case 1: // If beacon chain is bigger than shard chain in epoch, it means I should catch up with beacon chain now @@ -315,31 +316,30 @@ func (w *Worker) SuperCommitteeForNextEpoch( utils.Logger().Debug(). Uint64("blockNum", w.current.header.Number().Uint64()). Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). - Uint64("myNewEpoch", blockEpoch.Uint64()). + Uint64("myCurEpoch", blockEpoch.Uint64()). Msg("Propose new epoch as beacon chain's epoch") w.current.header.SetEpoch(blockEpoch) - case 0: // If it's same epoch, no need to propose new shard state (new epoch change) case -1: // If beacon chain is behind, shard chain should wait for the beacon chain by not changing epochs. } } else { - beaconEpochSubOne := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) - if w.config.IsStaking(beaconEpochSubOne) { - // If I am not in staking epoch yet and beacon chain already proposed a staking-based shard state, - // which means beacon chain's epoch is greater than stakingEpoch, I should just catch up with beacon chain's epoch + if w.config.IsStaking(beaconEpoch) { + // If I am not even in the last epoch before staking epoch and beacon chain is already in staking epoch, + // I should just catch up with beacon chain's epoch nextCommittee, err = committee.WithStakingEnabled.ReadFromDB( beaconEpoch, beacon, ) + blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1)) utils.Logger().Debug(). Uint64("blockNum", w.current.header.Number().Uint64()). Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). - Uint64("myNewEpoch", beaconEpochSubOne.Uint64()). + Uint64("myCurEpoch", blockEpoch.Uint64()). Msg("Propose one-time catch up with beacon chain's epoch") // Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch - w.current.header.SetEpoch(beaconEpochSubOne) + w.current.header.SetEpoch(blockEpoch) } else { // If I are not in staking nor has beacon chain proposed a staking-based shard state, // do pre-staking committee calculation @@ -397,6 +397,7 @@ func (w *Worker) FinalizeNewBlock(sig []byte, signers []byte, viewID uint64, coi s := w.current.state.Copy() copyHeader := types.CopyHeader(w.current.header) + // TODO: feed coinbase into here so the proposer gets extra rewards. block, err := w.engine.Finalize(w.chain, copyHeader, s, w.current.txs, w.current.receipts, w.current.outcxs, w.current.incxs, w.current.stakingTxs) if err != nil { return nil, ctxerror.New("cannot finalize block").WithCause(err)