From 0ce02381cc2d7523031a253b14709328892f78e0 Mon Sep 17 00:00:00 2001 From: Edgar Aroutiounian Date: Tue, 4 Feb 2020 13:46:43 -0800 Subject: [PATCH 1/5] [rpc] Remove IsBeaconChainExplorerNode, let any node on beaconchain calculate median stake (#2177) --- hmy/api_backend.go | 5 ----- hmy/backend.go | 1 - internal/hmyapi/apiv1/backend.go | 1 - internal/hmyapi/apiv1/blockchain.go | 6 +++--- internal/hmyapi/apiv2/backend.go | 1 - internal/hmyapi/apiv2/blockchain.go | 6 +++--- internal/hmyapi/backend.go | 1 - node/rpc.go | 7 ------- 8 files changed, 6 insertions(+), 22 deletions(-) diff --git a/hmy/api_backend.go b/hmy/api_backend.go index 21232f3e1..9bfa1abfc 100644 --- a/hmy/api_backend.go +++ b/hmy/api_backend.go @@ -427,11 +427,6 @@ func (b *APIBackend) GetCurrentTransactionErrorSink() []types.RPCTransactionErro return b.hmy.nodeAPI.ErroredTransactionSink() } -// IsBeaconChainExplorerNode .. -func (b *APIBackend) IsBeaconChainExplorerNode() bool { - return b.hmy.nodeAPI.IsBeaconChainExplorerNode() -} - // GetPendingCXReceipts .. func (b *APIBackend) GetPendingCXReceipts() []*types.CXReceiptsProof { return b.hmy.nodeAPI.PendingCXReceipts() diff --git a/hmy/backend.go b/hmy/backend.go index f92548fac..73df1ab47 100644 --- a/hmy/backend.go +++ b/hmy/backend.go @@ -50,7 +50,6 @@ type NodeAPI interface { IsCurrentlyLeader() bool ErroredStakingTransactionSink() []staking.RPCTransactionError ErroredTransactionSink() []types.RPCTransactionError - IsBeaconChainExplorerNode() bool PendingCXReceipts() []*types.CXReceiptsProof } diff --git a/internal/hmyapi/apiv1/backend.go b/internal/hmyapi/apiv1/backend.go index 4042dbc36..844eee4fd 100644 --- a/internal/hmyapi/apiv1/backend.go +++ b/internal/hmyapi/apiv1/backend.go @@ -79,7 +79,6 @@ type Backend interface { GetShardState() (*shard.State, error) GetCurrentStakingErrorSink() []staking.RPCTransactionError GetCurrentTransactionErrorSink() []types.RPCTransactionError - IsBeaconChainExplorerNode() bool GetMedianRawStakeSnapshot() *big.Int GetPendingCXReceipts() []*types.CXReceiptsProof } diff --git a/internal/hmyapi/apiv1/blockchain.go b/internal/hmyapi/apiv1/blockchain.go index 37b3cae62..a30cb03b0 100644 --- a/internal/hmyapi/apiv1/blockchain.go +++ b/internal/hmyapi/apiv1/blockchain.go @@ -493,16 +493,16 @@ func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformati } var ( - errNotExplorerNode = errors.New("cannot call this rpc on non beaconchain explorer node") + errNotBeaconChainShard = errors.New("cannot call this rpc on non beaconchain node") ) // GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain // explorer node func (s *PublicBlockChainAPI) GetMedianRawStakeSnapshot() (*big.Int, error) { - if s.b.IsBeaconChainExplorerNode() { + if s.b.GetShardID() == shard.BeaconChainShardID { return s.b.GetMedianRawStakeSnapshot(), nil } - return nil, errNotExplorerNode + return nil, errNotBeaconChainShard } // GetAllValidatorAddresses returns all validator addresses. diff --git a/internal/hmyapi/apiv2/backend.go b/internal/hmyapi/apiv2/backend.go index 7f5a322f4..4a94b9dd8 100644 --- a/internal/hmyapi/apiv2/backend.go +++ b/internal/hmyapi/apiv2/backend.go @@ -79,7 +79,6 @@ type Backend interface { GetShardState() (*shard.State, error) GetCurrentStakingErrorSink() []staking.RPCTransactionError GetCurrentTransactionErrorSink() []types.RPCTransactionError - IsBeaconChainExplorerNode() bool GetMedianRawStakeSnapshot() *big.Int GetPendingCXReceipts() []*types.CXReceiptsProof } diff --git a/internal/hmyapi/apiv2/blockchain.go b/internal/hmyapi/apiv2/blockchain.go index 1709e4d62..63d93a343 100644 --- a/internal/hmyapi/apiv2/blockchain.go +++ b/internal/hmyapi/apiv2/blockchain.go @@ -455,16 +455,16 @@ func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformati } var ( - errNotExplorerNode = errors.New("cannot call this rpc on non beaconchain explorer node") + errNotBeaconChainShard = errors.New("cannot call this rpc on non beaconchain node") ) // GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain // explorer node func (s *PublicBlockChainAPI) GetMedianRawStakeSnapshot() (*big.Int, error) { - if s.b.IsBeaconChainExplorerNode() { + if s.b.GetShardID() == shard.BeaconChainShardID { return s.b.GetMedianRawStakeSnapshot(), nil } - return nil, errNotExplorerNode + return nil, errNotBeaconChainShard } // GetAllValidatorAddresses returns all validator addresses. diff --git a/internal/hmyapi/backend.go b/internal/hmyapi/backend.go index 13fa46229..c6f14263b 100644 --- a/internal/hmyapi/backend.go +++ b/internal/hmyapi/backend.go @@ -81,7 +81,6 @@ type Backend interface { GetShardState() (*shard.State, error) GetCurrentStakingErrorSink() []staking.RPCTransactionError GetCurrentTransactionErrorSink() []types.RPCTransactionError - IsBeaconChainExplorerNode() bool GetMedianRawStakeSnapshot() *big.Int GetPendingCXReceipts() []*types.CXReceiptsProof } diff --git a/node/rpc.go b/node/rpc.go index f48c59856..b0b18c917 100644 --- a/node/rpc.go +++ b/node/rpc.go @@ -16,7 +16,6 @@ import ( "github.com/harmony-one/harmony/internal/hmyapi/apiv2" "github.com/harmony-one/harmony/internal/hmyapi/filters" "github.com/harmony-one/harmony/internal/utils" - "github.com/harmony-one/harmony/shard" staking "github.com/harmony-one/harmony/staking/types" ) @@ -85,12 +84,6 @@ func (node *Node) ErroredTransactionSink() []types.RPCTransactionError { return result } -// IsBeaconChainExplorerNode .. -func (node *Node) IsBeaconChainExplorerNode() bool { - return node.NodeConfig.Role() == nodeconfig.ExplorerNode && - node.Consensus.ShardID == shard.BeaconChainShardID -} - // StartRPC start RPC service func (node *Node) StartRPC(nodePort string) error { // Gather all the possible APIs to surface From 721611a94980f44989894b7a2abda0f88a484d38 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Tue, 4 Feb 2020 15:33:31 -0800 Subject: [PATCH 2/5] Report duplicate bls keys on the same validator as error (#2179) * Update description fields individually; Cleanup staking txn fields * fix build * Update max total edit logic * fix lint * Change edit description to object * fix lint * 0 means no-op for edit validator * Report duplicate bls keys on the same validator as error --- staking/types/validator.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/staking/types/validator.go b/staking/types/validator.go index c7cd2bb95..709d1ed74 100644 --- a/staking/types/validator.go +++ b/staking/types/validator.go @@ -39,6 +39,9 @@ var ( errBLSKeysNotMatchSigs = errors.New("bls keys and corresponding signatures could not be verified") errNilMinSelfDelegation = errors.New("MinSelfDelegation can not be nil") errNilMaxTotalDelegation = errors.New("MaxTotalDelegation can not be nil") + errSlotKeyToRemoveNotFound = errors.New("slot key to remove not found") + errSlotKeyToAddExists = errors.New("slot key to add already exists") + errDuplicateSlotKeys = errors.New("slot keys can not have duplicates") ) // ValidatorWrapper contains validator and its delegation information @@ -259,6 +262,14 @@ func (w *ValidatorWrapper) SanityCheck() error { ) } + allKeys := map[shard.BlsPublicKey]struct{}{} + for i := range w.Validator.SlotPubKeys { + if _, ok := allKeys[w.Validator.SlotPubKeys[i]]; !ok { + allKeys[w.Validator.SlotPubKeys[i]] = struct{}{} + } else { + return errDuplicateSlotKeys + } + } return nil } @@ -433,6 +444,8 @@ func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator) error // we found key to be removed if index >= 0 { validator.SlotPubKeys = append(validator.SlotPubKeys[:index], validator.SlotPubKeys[index+1:]...) + } else { + return errSlotKeyToRemoveNotFound } } @@ -449,6 +462,8 @@ func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator) error return err } validator.SlotPubKeys = append(validator.SlotPubKeys, *edit.SlotKeyToAdd) + } else { + return errSlotKeyToAddExists } } From d98267c113045340657e47a83a96436ecef06a37 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Tue, 4 Feb 2020 16:15:32 -0800 Subject: [PATCH 3/5] Add one-off block verification rule for beacon chain bad block (#2180) * Add one-off block verification rule for beacon chain bad block * Add more comment --- core/block_validator.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/block_validator.go b/core/block_validator.go index 441e8a843..0b44af191 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -91,7 +91,9 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. rbloom := types.CreateBloom(receipts) - if rbloom != header.Bloom() { + // Beacon chain block 1213181 is a one-off block with empty bloom which is expected to be non-empty. + // Skip the validation for it to avoid failure. + if rbloom != header.Bloom() && (block.NumberU64() != 1213181 || block.ShardID() != 0) { return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom(), rbloom) } // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) From 1241ece48ca698787d55f4302be793914a674ef9 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Tue, 4 Feb 2020 17:04:07 -0800 Subject: [PATCH 4/5] Fix testnet config for staking (#2182) * Add one-off block verification rule for beacon chain bad block * Add more comment * Fix testnet config --- internal/configs/sharding/testnet.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/configs/sharding/testnet.go b/internal/configs/sharding/testnet.go index 635f05a57..7c5b0492c 100644 --- a/internal/configs/sharding/testnet.go +++ b/internal/configs/sharding/testnet.go @@ -27,7 +27,7 @@ const ( func (testnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { switch { - case epoch.Cmp(params.PangaeaChainConfig.StakingEpoch) >= 0: + case epoch.Cmp(params.TestnetChainConfig.StakingEpoch) >= 0: return testnetV1 default: // genesis return testnetV0 @@ -77,8 +77,8 @@ func (ts testnetSchedule) GetShardingStructure(numShard, shardID int) []map[stri var testnetReshardingEpoch = []*big.Int{ big.NewInt(0), - params.PangaeaChainConfig.StakingEpoch, + params.TestnetChainConfig.StakingEpoch, } var testnetV0 = MustNewInstance(3, 10, 7, genesis.TNHarmonyAccounts, genesis.TNFoundationalAccounts, testnetReshardingEpoch, TestnetSchedule.BlocksPerEpoch()) -var testnetV1 = MustNewInstance(3, 10, 3, genesis.TNHarmonyAccounts, genesis.TNFoundationalAccounts, testnetReshardingEpoch, TestnetSchedule.BlocksPerEpoch()) +var testnetV1 = MustNewInstance(3, 20, 7, genesis.TNHarmonyAccounts, genesis.TNFoundationalAccounts, testnetReshardingEpoch, TestnetSchedule.BlocksPerEpoch()) From cae94683a476df288bff4986999ffb530c19334d Mon Sep 17 00:00:00 2001 From: Edgar Aroutiounian Date: Tue, 4 Feb 2020 20:23:50 -0800 Subject: [PATCH 5/5] [state][staking][availability] Handle availability only in finalize (#2185) --- core/block_validator.go | 8 +++-- core/blockchain.go | 9 +++--- core/state/dump.go | 22 ++++++++++--- core/types.go | 2 +- internal/chain/engine.go | 7 +++++ node/node_newblock.go | 49 ----------------------------- staking/availability/apply.go | 56 +++++++++++++++++++++++++++++++++ staking/availability/measure.go | 12 +++++-- 8 files changed, 102 insertions(+), 63 deletions(-) create mode 100644 staking/availability/apply.go diff --git a/core/block_validator.go b/core/block_validator.go index 0b44af191..0df9e2dc6 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -19,10 +19,12 @@ package core import ( "bytes" "encoding/binary" + "encoding/hex" "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/internal/ctxerror" @@ -83,7 +85,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. -func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.DB, receipts types.Receipts, cxReceipts types.CXReceipts, usedGas uint64) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.DB, receipts types.Receipts, cxReceipts types.CXReceipts, usedGas uint64) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) @@ -115,7 +117,9 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat // Validate the state root against the received state root and throw // an error if they don't match. if root := statedb.IntermediateRoot(v.config.IsS3(header.Epoch())); header.Root() != root { - return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root(), root) + dump, _ := rlp.EncodeToBytes(header) + const msg = "invalid merkle root (remote: %x local: %x, rlp dump %s)" + return fmt.Errorf(msg, header.Root(), root, hex.EncodeToString(dump)) } return nil } diff --git a/core/blockchain.go b/core/blockchain.go index a600b167e..efce8d19b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -246,6 +246,7 @@ func (bc *BlockChain) ValidateNewBlock(block *types.Block) error { return err } + // NOTE Order of mutating state here matters. // Process block using the parent state as reference point. receipts, cxReceipts, _, usedGas, _, err := bc.processor.Process(block, state, bc.vmConfig) if err != nil { @@ -253,8 +254,7 @@ func (bc *BlockChain) ValidateNewBlock(block *types.Block) error { return err } - err = bc.Validator().ValidateState(block, bc.CurrentBlock(), state, receipts, cxReceipts, usedGas) - if err != nil { + if err := bc.Validator().ValidateState(block, state, receipts, cxReceipts, usedGas); err != nil { bc.reportBlock(block, receipts, err) return err } @@ -1476,8 +1476,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifyHeaders bool) (int, } // Validate the state using the default validator - err = bc.Validator().ValidateState(block, parent, state, receipts, cxReceipts, usedGas) - if err != nil { + if err := bc.Validator().ValidateState( + block, state, receipts, cxReceipts, usedGas, + ); err != nil { bc.reportBlock(block, receipts, err) return i, events, coalescedLogs, err } diff --git a/core/state/dump.go b/core/state/dump.go index 986dfaf37..8b4395407 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -23,6 +23,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" + common2 "github.com/harmony-one/harmony/internal/common" + staking "github.com/harmony-one/harmony/staking/types" ) // DumpAccount ... @@ -55,31 +57,41 @@ func (db *DB) RawDump() Dump { if err := rlp.DecodeBytes(it.Value, &data); err != nil { panic(err) } - obj := newObject(nil, common.BytesToAddress(addr), data) + var wrapper staking.ValidatorWrapper + wrap := "" + if err := rlp.DecodeBytes(obj.Code(db.db), &wrapper); err != nil { + // + } else { + marsh, err := json.Marshal(wrapper) + if err == nil { + wrap = string(marsh) + } + } + account := DumpAccount{ Balance: data.Balance.String(), Nonce: data.Nonce, Root: common.Bytes2Hex(data.Root[:]), CodeHash: common.Bytes2Hex(data.CodeHash), - Code: common.Bytes2Hex(obj.Code(db.db)), + Code: wrap, Storage: make(map[string]string), } storageIt := trie.NewIterator(obj.getTrie(db.db).NodeIterator(nil)) for storageIt.Next() { account.Storage[common.Bytes2Hex(db.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) } - dump.Accounts[common.Bytes2Hex(addr)] = account + dump.Accounts[common2.MustAddressToBech32(common.BytesToAddress(addr))] = account } return dump } // Dump ... -func (db *DB) Dump() []byte { +func (db *DB) Dump() string { json, err := json.MarshalIndent(db.RawDump(), "", " ") if err != nil { fmt.Println("dump err", err) } - return json + return string(json) } diff --git a/core/types.go b/core/types.go index f716c4dc6..8c6a0c5f5 100644 --- a/core/types.go +++ b/core/types.go @@ -34,7 +34,7 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block, parent *types.Block, state *state.DB, receipts types.Receipts, cxs types.CXReceipts, usedGas uint64) error + ValidateState(block *types.Block, state *state.DB, receipts types.Receipts, cxs types.CXReceipts, usedGas uint64) error // ValidateHeader checks whether a header conforms to the consensus rules of a // given engine. Verifying the seal may be done optionally here, or explicitly diff --git a/internal/chain/engine.go b/internal/chain/engine.go index ad526797e..b2d3c5ae3 100644 --- a/internal/chain/engine.go +++ b/internal/chain/engine.go @@ -20,6 +20,7 @@ import ( "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/shard" "github.com/harmony-one/harmony/shard/committee" + "github.com/harmony-one/harmony/staking/availability" "github.com/harmony-one/harmony/staking/slash" staking "github.com/harmony-one/harmony/staking/types" "github.com/pkg/errors" @@ -329,6 +330,12 @@ func (e *engineImpl) Finalize( } } + if isBeaconChain && isNewEpoch && inStakingEra { + if err := availability.Apply(chain, state); err != nil { + return nil, nil, err + } + } + header.SetRoot(state.IntermediateRoot(chain.Config().IsS3(header.Epoch()))) return types.NewBlock(header, txs, receipts, outcxs, incxs, stks), payout, nil } diff --git a/node/node_newblock.go b/node/node_newblock.go index a03a93e51..410cd0616 100644 --- a/node/node_newblock.go +++ b/node/node_newblock.go @@ -167,55 +167,6 @@ func (node *Node) proposeNewBlock() (*types.Block, error) { } } - // Bump up signers counts - _, header := node.Worker.GetCurrentState(), node.Blockchain().CurrentHeader() - if epoch := header.Epoch(); node.Blockchain().Config().IsStaking(epoch) { - - if header.ShardID() == shard.BeaconChainShardID { - superCommittee, err := node.Blockchain().ReadShardState(header.Epoch()) - processed := make(map[common.Address]struct{}) - - if err != nil { - return nil, err - } - - for j := range superCommittee.Shards { - shard := superCommittee.Shards[j] - for j := range shard.Slots { - slot := shard.Slots[j] - if slot.EffectiveStake != nil { // For external validator - _, ok := processed[slot.EcdsaAddress] - if !ok { - processed[slot.EcdsaAddress] = struct{}{} - } - } - } - } - - // TODO(Edgar) Need to uncomment and fix, unknown why enabling - // this code causes merkle root verification issues - - // if err := availability.IncrementValidatorSigningCounts( - // node.Blockchain(), header, header.ShardID(), state, processed, - // ); err != nil { - // return nil, err - // } - - // // kick out the inactive validators so they won't come up in the auction as possible - // // candidates in the following call to SuperCommitteeForNextEpoch - // if shard.Schedule.IsLastBlock(header.Number().Uint64()) { - // fmt.Println("hit the last block condition") - // if err := availability.SetInactiveUnavailableValidators( - // node.Blockchain(), state, processed, - // ); err != nil { - // return nil, err - // } - // } - } else { - // TODO Handle shard chain - } - } - // Prepare shard state shardState := new(shard.State) if shardState, err = node.Blockchain().SuperCommitteeForNextEpoch( diff --git a/staking/availability/apply.go b/staking/availability/apply.go new file mode 100644 index 000000000..a2ed6b584 --- /dev/null +++ b/staking/availability/apply.go @@ -0,0 +1,56 @@ +package availability + +import ( + "github.com/ethereum/go-ethereum/common" + engine "github.com/harmony-one/harmony/consensus/engine" + "github.com/harmony-one/harmony/core/state" + "github.com/harmony-one/harmony/shard" +) + +// Apply .. +func Apply(bc engine.ChainReader, state *state.DB) error { + header := bc.CurrentHeader() + if epoch := header.Epoch(); bc.Config().IsStaking(epoch) { + if header.ShardID() == shard.BeaconChainShardID { + superCommittee, err := bc.ReadShardState(header.Epoch()) + processed := make(map[common.Address]struct{}) + + if err != nil { + return err + } + + for j := range superCommittee.Shards { + shard := superCommittee.Shards[j] + for j := range shard.Slots { + slot := shard.Slots[j] + if slot.EffectiveStake != nil { // For external validator + _, ok := processed[slot.EcdsaAddress] + if !ok { + processed[slot.EcdsaAddress] = struct{}{} + } + } + } + } + + if err := IncrementValidatorSigningCounts( + bc, header, header.ShardID(), state, processed, + ); err != nil { + return err + } + + // // kick out the inactive validators so they won't come up in the auction as possible + // // candidates in the following call to SuperCommitteeForNextEpoch + if shard.Schedule.IsLastBlock(header.Number().Uint64()) { + if err := SetInactiveUnavailableValidators( + bc, state, processed, + ); err != nil { + return err + } + } + } else { + // TODO Handle shard chain + } + } + + return nil +} diff --git a/staking/availability/measure.go b/staking/availability/measure.go index a92a7c92b..81d32336b 100644 --- a/staking/availability/measure.go +++ b/staking/availability/measure.go @@ -16,8 +16,12 @@ import ( var ( measure = new(big.Int).Div(big.NewInt(2), big.NewInt(3)) - errValidatorEpochDeviation = errors.New("validator snapshot epoch not exactly one epoch behind") - errNegativeSign = errors.New("impossible period of signing") + errValidatorEpochDeviation = errors.New( + "validator snapshot epoch not exactly one epoch behind", + ) + errNegativeSign = errors.New("impossible period of signing") + // ErrDivByZero .. + ErrDivByZero = errors.New("toSign of availability cannot be 0, mistake in protocol") ) // BlockSigners .. @@ -216,6 +220,10 @@ func SetInactiveUnavailableValidators( ) } + if toSign.Cmp(common.Big0) == 0 { + return ErrDivByZero + } + if r := new(big.Int).Div(signed, toSign); r.Cmp(measure) == -1 { wrapper.Active = false if err := state.UpdateStakingInfo(addrs[i], wrapper); err != nil {