From 90704214620021992dfb185ed62848336f7bbe3f Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 23 Nov 2019 17:22:18 -0800 Subject: [PATCH] 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