diff --git a/core/state/statedb.go b/core/state/statedb.go index 96bd4d26e..fce6f750b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -894,7 +894,9 @@ func (db *DB) Finalise(deleteEmptyObjects bool) { // Commit validator changes in cache to stateObjects // TODO: remove validator cache after commit for addr, wrapper := range db.stateValidators { - db.UpdateValidatorWrapper(addr, wrapper) + if err := db.UpdateValidatorWrapper(addr, wrapper); err != nil { + utils.Logger().Warn().Err(err).Msg("Unable to update the validator wrapper on the finalize") + } } addressesToPrefetch := make([][]byte, 0, len(db.journal.dirties)) for addr := range db.journal.dirties { diff --git a/core/tx_pool.go b/core/tx_pool.go index d91849022..2457da385 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -850,15 +850,11 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error { } currentBlockNumber := pool.chain.CurrentBlock().Number() pendingBlockNumber := new(big.Int).Add(currentBlockNumber, big.NewInt(1)) - pendingEpoch := pool.chain.CurrentBlock().Epoch() - if shard.Schedule.IsLastBlock(currentBlockNumber.Uint64()) { - pendingEpoch = new(big.Int).Add(pendingEpoch, big.NewInt(1)) - } chainContext, ok := pool.chain.(ChainContext) if !ok { chainContext = nil // might use testing blockchain, set to nil for verifier to handle. } - _, err = VerifyAndCreateValidatorFromMsg(pool.currentState, chainContext, pendingEpoch, pendingBlockNumber, stkMsg) + _, err = VerifyAndCreateValidatorFromMsg(pool.currentState, chainContext, pool.pendingEpoch(), pendingBlockNumber, stkMsg) return err case staking.DirectiveEditValidator: msg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveEditValidator) @@ -932,7 +928,6 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error { if from != stkMsg.DelegatorAddress { return errors.WithMessagef(ErrInvalidSender, "staking transaction sender is %s", b32) } - _, err = VerifyAndUndelegateFromMsg(pool.currentState, pool.pendingEpoch(), stkMsg) return err case staking.DirectiveCollectRewards: @@ -964,11 +959,12 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error { } } +// pendingEpoch refers to the epoch of the pending block func (pool *TxPool) pendingEpoch() *big.Int { currentBlock := pool.chain.CurrentBlock() pendingEpoch := currentBlock.Epoch() if shard.Schedule.IsLastBlock(currentBlock.Number().Uint64()) { - pendingEpoch.Add(pendingEpoch, big.NewInt(1)) + pendingEpoch = new(big.Int).Add(pendingEpoch, common.Big1) } return pendingEpoch } diff --git a/hmy/staking.go b/hmy/staking.go index 9d99b7908..83e800544 100644 --- a/hmy/staking.go +++ b/hmy/staking.go @@ -143,6 +143,11 @@ func (hmy *Harmony) IsNoEarlyUnlockEpoch(epoch *big.Int) bool { return hmy.BlockChain.Config().IsNoEarlyUnlock(epoch) } +// IsMaxRate ... +func (hmy *Harmony) IsMaxRate(epoch *big.Int) bool { + return hmy.BlockChain.Config().IsMaxRate(epoch) +} + // IsCommitteeSelectionBlock checks if the given block is the committee selection block func (hmy *Harmony) IsCommitteeSelectionBlock(header *block.Header) bool { return chain.IsCommitteeSelectionBlock(hmy.BlockChain, header) @@ -592,6 +597,7 @@ func (hmy *Harmony) GetUndelegationPayouts( return undelegationPayouts, nil } + isMaxRate := hmy.IsMaxRate(epoch) lockingPeriod := hmy.GetDelegationLockingPeriodInEpoch(undelegationPayoutBlock.Epoch()) for _, validator := range hmy.GetAllValidatorAddresses() { wrapper, err := hmy.BlockChain.ReadValidatorInformationAtRoot(validator, undelegationPayoutBlock.Root()) @@ -600,7 +606,7 @@ func (hmy *Harmony) GetUndelegationPayouts( } noEarlyUnlock := hmy.IsNoEarlyUnlockEpoch(epoch) for _, delegation := range wrapper.Delegations { - withdraw := delegation.RemoveUnlockedUndelegations(epoch, wrapper.LastEpochInCommittee, lockingPeriod, noEarlyUnlock) + withdraw := delegation.RemoveUnlockedUndelegations(epoch, wrapper.LastEpochInCommittee, lockingPeriod, noEarlyUnlock, isMaxRate) if withdraw.Cmp(bigZero) == 1 { undelegationPayouts.SetPayoutByDelegatorAddrAndValidatorAddr(validator, delegation.DelegatorAddress, withdraw) } diff --git a/internal/chain/engine.go b/internal/chain/engine.go index 4f3aac9ff..8e07f7a1e 100644 --- a/internal/chain/engine.go +++ b/internal/chain/engine.go @@ -370,9 +370,15 @@ func payoutUndelegations( const msg = "[Finalize] failed to read all validators" return errors.New(msg) } - // Payout undelegated/unlocked tokens + // Payout undelegated/unlocked tokens at the end of each epoch lockPeriod := GetLockPeriodInEpoch(chain, header.Epoch()) noEarlyUnlock := chain.Config().IsNoEarlyUnlock(header.Epoch()) + newShardState, err := header.GetShardState() + if err != nil { + const msg = "[Finalize] failed to read shard state" + return errors.New(msg) + } + isMaxRate := chain.Config().IsMaxRate(newShardState.Epoch) for _, validator := range validators { wrapper, err := state.ValidatorWrapper(validator, true, false) if err != nil { @@ -383,7 +389,7 @@ func payoutUndelegations( for i := range wrapper.Delegations { delegation := &wrapper.Delegations[i] totalWithdraw := delegation.RemoveUnlockedUndelegations( - header.Epoch(), wrapper.LastEpochInCommittee, lockPeriod, noEarlyUnlock, + header.Epoch(), wrapper.LastEpochInCommittee, lockPeriod, noEarlyUnlock, isMaxRate, ) if totalWithdraw.Sign() != 0 { state.AddBalance(delegation.DelegatorAddress, totalWithdraw) @@ -426,6 +432,7 @@ func setElectionEpochAndMinFee(chain engine.ChainReader, header *block.Header, s map[common.Address]struct{}, len(newShardState.StakedValidators().Addrs), ) + // this loop is for elected validators only for _, addr := range newShardState.StakedValidators().Addrs { wrapper, err := state.ValidatorWrapper(addr, true, false) if err != nil { @@ -448,11 +455,13 @@ func setElectionEpochAndMinFee(chain engine.ChainReader, header *block.Header, s } isElected[addr] = struct{}{} } + // due to a bug in the old implementation of the minimum fee, // unelected validators did not have their fee updated even // when the protocol required them to do so. here we fix it, - // but only after the HIP-30 hard fork is effective. - if config.IsHIP30(newShardState.Epoch) { + // but only after the HIP-30 hard fork is effective + // this loop applies to all validators, but excludes the ones in isElected + if config.IsHIP30(newShardState.Epoch) && minRateNotZero { for _, addr := range chain.ValidatorCandidates() { // skip elected validator if _, ok := isElected[addr]; ok { @@ -466,6 +475,19 @@ func setElectionEpochAndMinFee(chain engine.ChainReader, header *block.Header, s } } } + + // for all validators which have MaxRate < minRate + maxChangeRate + // set their MaxRate equal to the minRate + MaxChangeRate + // this will allow the wrapper.SanityCheck to pass if Rate is set to a value + // higher than the the MaxRate by UpdateMinimumCommissionFee above + if config.IsMaxRate(newShardState.Epoch) && minRateNotZero { + for _, addr := range chain.ValidatorCandidates() { + if _, err := availability.UpdateMaxCommissionFee(state, addr, minRate); err != nil { + return err + } + } + } + return nil } diff --git a/internal/params/config.go b/internal/params/config.go index 86695ba40..c96bf8ab9 100644 --- a/internal/params/config.go +++ b/internal/params/config.go @@ -75,6 +75,7 @@ var ( ValidatorCodeFixEpoch: big.NewInt(1535), // 2023-07-20 05:51:07+00:00 HIP30Epoch: big.NewInt(1673), // 2023-11-02 17:30:00+00:00 BlockGas30MEpoch: big.NewInt(1673), // 2023-11-02 17:30:00+00:00 + MaxRateEpoch: big.NewInt(1733), // 2023-12-17 12:20:15+00:00 } // TestnetChainConfig contains the chain parameters to run a node on the harmony test network. @@ -118,6 +119,7 @@ var ( ValidatorCodeFixEpoch: big.NewInt(1296), // 2023-04-28 07:14:20+00:00 HIP30Epoch: big.NewInt(2176), // 2023-10-12 10:00:00+00:00 BlockGas30MEpoch: big.NewInt(2176), // 2023-10-12 10:00:00+00:00 + MaxRateEpoch: big.NewInt(2520), // 2023-12-16 12:17:14+00:00 } // PangaeaChainConfig contains the chain parameters for the Pangaea network. // All features except for CrossLink are enabled at launch. @@ -161,6 +163,7 @@ var ( ValidatorCodeFixEpoch: EpochTBD, HIP30Epoch: EpochTBD, BlockGas30MEpoch: big.NewInt(0), + MaxRateEpoch: EpochTBD, } // PartnerChainConfig contains the chain parameters for the Partner network. @@ -205,6 +208,7 @@ var ( ValidatorCodeFixEpoch: big.NewInt(5), HIP30Epoch: big.NewInt(7), BlockGas30MEpoch: big.NewInt(7), + MaxRateEpoch: EpochTBD, } // StressnetChainConfig contains the chain parameters for the Stress test network. @@ -249,6 +253,7 @@ var ( ValidatorCodeFixEpoch: EpochTBD, HIP30Epoch: EpochTBD, BlockGas30MEpoch: big.NewInt(0), + MaxRateEpoch: EpochTBD, } // LocalnetChainConfig contains the chain parameters to run for local development. @@ -292,6 +297,7 @@ var ( ValidatorCodeFixEpoch: big.NewInt(2), HIP30Epoch: EpochTBD, BlockGas30MEpoch: big.NewInt(0), + MaxRateEpoch: EpochTBD, } // AllProtocolChanges ... @@ -336,7 +342,8 @@ var ( big.NewInt(0), // FeeCollectEpoch big.NewInt(0), // ValidatorCodeFixEpoch big.NewInt(0), // BlockGas30M - big.NewInt(0), // HIP30Epoch + big.NewInt(0), // BlockGas30M + big.NewInt(0), // MaxRateEpoch } // TestChainConfig ... @@ -382,6 +389,7 @@ var ( big.NewInt(0), // ValidatorCodeFixEpoch big.NewInt(0), // HIP30Epoch big.NewInt(0), // BlockGas30M + big.NewInt(0), // MaxRateEpoch } // TestRules ... @@ -547,6 +555,9 @@ type ChainConfig struct { HIP30Epoch *big.Int `json:"hip30-epoch,omitempty"` BlockGas30MEpoch *big.Int `json:"block-gas-30m-epoch,omitempty"` + + // MaxRateEpoch will make sure the validator max-rate is at least equal to the minRate + the validator max-rate-increase + MaxRateEpoch *big.Int `json:"max-rate-epoch,omitempty"` } // String implements the fmt.Stringer interface. @@ -612,6 +623,9 @@ func (c *ChainConfig) mustValid() { // capabilities required to transfer balance across shards require(c.HIP30Epoch.Cmp(c.CrossTxEpoch) > 0, "must satisfy: HIP30Epoch > CrossTxEpoch") + // max rate (7%) fix is applied on or after hip30 + require(c.MaxRateEpoch.Cmp(c.HIP30Epoch) >= 0, + "must satisfy: MaxRateEpoch >= HIP30Epoch") } // IsEIP155 returns whether epoch is either equal to the EIP155 fork epoch or greater. @@ -803,6 +817,10 @@ func (c *ChainConfig) IsHIP30(epoch *big.Int) bool { return isForked(c.HIP30Epoch, epoch) } +func (c *ChainConfig) IsMaxRate(epoch *big.Int) bool { + return isForked(c.MaxRateEpoch, epoch) +} + // During this epoch, shards 2 and 3 will start sending // their balances over to shard 0 or 1. func (c *ChainConfig) IsOneEpochBeforeHIP30(epoch *big.Int) bool { diff --git a/rpc/blockchain.go b/rpc/blockchain.go index ae588e750..e830b30f0 100644 --- a/rpc/blockchain.go +++ b/rpc/blockchain.go @@ -461,10 +461,10 @@ func (s *PublicBlockchainService) GetBlockReceipts( case V1: r, err = v1.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()]) case V2: - r, err = v2.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()]) + r, err = v2.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()], false) case Eth: if tx, ok := tx.(*types.Transaction); ok { - r, err = eth.NewReceipt(tx.ConvertToEth(), blockHash, block.NumberU64(), index, rmap[tx.Hash()]) + r, err = v2.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()], true) } default: return nil, ErrUnknownRPCVersion diff --git a/rpc/eth/types.go b/rpc/eth/types.go index f76aa4442..e3a64eb7f 100644 --- a/rpc/eth/types.go +++ b/rpc/eth/types.go @@ -111,7 +111,7 @@ func NewTransaction( return result, nil } -// NewReceipt returns the RPC data for a new receipt +// NewReceipt returns the RPC data for a new receipt. It is unused at the moment. func NewReceipt(tx *types.EthTransaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt) (map[string]interface{}, error) { senderAddr, err := tx.SenderAddress() if err != nil { diff --git a/rpc/transaction.go b/rpc/transaction.go index 8ea211d6a..ef30092b2 100644 --- a/rpc/transaction.go +++ b/rpc/transaction.go @@ -751,19 +751,11 @@ func (s *PublicTransactionService) GetTransactionReceipt( return nil, err } return NewStructuredResponse(RPCReceipt) - case V2: + case V2, Eth: if tx == nil { - RPCReceipt, err = v2.NewReceipt(stx, blockHash, blockNumber, index, receipt) + RPCReceipt, err = v2.NewReceipt(stx, blockHash, blockNumber, index, receipt, false) } else { - RPCReceipt, err = v2.NewReceipt(tx, blockHash, blockNumber, index, receipt) - } - if err != nil { - return nil, err - } - return NewStructuredResponse(RPCReceipt) - case Eth: - if tx != nil { - RPCReceipt, err = eth.NewReceipt(tx.ConvertToEth(), blockHash, blockNumber, index, receipt) + RPCReceipt, err = v2.NewReceipt(tx, blockHash, blockNumber, index, receipt, s.version == Eth) } if err != nil { return nil, err diff --git a/rpc/v2/types.go b/rpc/v2/types.go index c9abbead0..d2b21627c 100644 --- a/rpc/v2/types.go +++ b/rpc/v2/types.go @@ -204,20 +204,20 @@ type UndelegateMsg struct { // TxReceipt represents a transaction receipt that will serialize to the RPC representation. type TxReceipt struct { - BlockHash common.Hash `json:"blockHash"` - TransactionHash common.Hash `json:"transactionHash"` - BlockNumber uint64 `json:"blockNumber"` - TransactionIndex uint64 `json:"transactionIndex"` - GasUsed uint64 `json:"gasUsed"` - CumulativeGasUsed uint64 `json:"cumulativeGasUsed"` - ContractAddress common.Address `json:"contractAddress"` - Logs []*types.Log `json:"logs"` - LogsBloom ethtypes.Bloom `json:"logsBloom"` - ShardID uint32 `json:"shardID"` - From string `json:"from"` - To string `json:"to"` - Root hexutil.Bytes `json:"root"` - Status uint `json:"status"` + BlockHash common.Hash `json:"blockHash"` + TransactionHash common.Hash `json:"transactionHash"` + BlockNumber uint64 `json:"blockNumber"` + TransactionIndex uint64 `json:"transactionIndex"` + GasUsed uint64 `json:"gasUsed"` + CumulativeGasUsed uint64 `json:"cumulativeGasUsed"` + ContractAddress *common.Address `json:"contractAddress"` + Logs []*types.Log `json:"logs"` + LogsBloom ethtypes.Bloom `json:"logsBloom"` + ShardID uint32 `json:"shardID"` + From string `json:"from"` + To string `json:"to"` + Root hexutil.Bytes `json:"root"` + Status uint `json:"status"` } // StakingTxReceipt represents a staking transaction receipt that will serialize to the RPC representation. @@ -334,11 +334,11 @@ func NewTransaction( // NewReceipt returns a transaction OR staking transaction that will serialize to the RPC representation func NewReceipt( - tx interface{}, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt, + tx interface{}, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt, eth bool, ) (interface{}, error) { plainTx, ok := tx.(*types.Transaction) if ok { - return NewTxReceipt(plainTx, blockHash, blockNumber, blockIndex, receipt) + return NewTxReceipt(plainTx, blockHash, blockNumber, blockIndex, receipt, eth) } stakingTx, ok := tx.(*staking.StakingTransaction) if ok { @@ -349,7 +349,7 @@ func NewReceipt( // NewTxReceipt returns a plain transaction receipt that will serialize to the RPC representation func NewTxReceipt( - tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt, + tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt, eth bool, ) (*TxReceipt, error) { // Set correct to & from address senderAddr, err := tx.SenderAddress() @@ -363,13 +363,18 @@ func NewTxReceipt( receiver = "" } else { // Handle response type for regular transaction - sender, err = internal_common.AddressToBech32(senderAddr) - if err != nil { - return nil, err - } - receiver, err = internal_common.AddressToBech32(*tx.To()) - if err != nil { - return nil, err + if eth { + sender = senderAddr.String() + receiver = tx.To().String() + } else { + sender, err = internal_common.AddressToBech32(senderAddr) + if err != nil { + return nil, err + } + receiver, err = internal_common.AddressToBech32(*tx.To()) + if err != nil { + return nil, err + } } } @@ -404,7 +409,7 @@ func NewTxReceipt( // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation if receipt.ContractAddress != (common.Address{}) { - txReceipt.ContractAddress = receipt.ContractAddress + txReceipt.ContractAddress = &receipt.ContractAddress } return txReceipt, nil } diff --git a/staking/availability/measure.go b/staking/availability/measure.go index 881baa855..6bf36bfb0 100644 --- a/staking/availability/measure.go +++ b/staking/availability/measure.go @@ -267,3 +267,27 @@ func UpdateMinimumCommissionFee( } return false, nil } + +// UpdateMaxCommissionFee makes sure the max-rate is at least higher than the rate + max-rate-change. +func UpdateMaxCommissionFee(state *state.DB, addr common.Address, minRate numeric.Dec) (bool, error) { + utils.Logger().Info().Msg("begin update max commission fee") + + wrapper, err := state.ValidatorWrapper(addr, true, false) + if err != nil { + return false, err + } + + minMaxRate := minRate.Add(wrapper.MaxChangeRate) + + if wrapper.MaxRate.LT(minMaxRate) { + utils.Logger().Info(). + Str("addr", addr.Hex()). + Str("old max-rate", wrapper.MaxRate.String()). + Str("new max-rate", minMaxRate.String()). + Msg("updating max commission rate") + wrapper.MaxRate.SetBytes(minMaxRate.Bytes()) + return true, nil + } + + return false, nil +} diff --git a/staking/types/delegation.go b/staking/types/delegation.go index 9f0a0c622..c222048e4 100644 --- a/staking/types/delegation.go +++ b/staking/types/delegation.go @@ -178,15 +178,21 @@ func (d *Delegation) DeleteEntry(epoch *big.Int) { // RemoveUnlockedUndelegations removes all fully unlocked // undelegations and returns the total sum func (d *Delegation) RemoveUnlockedUndelegations( - curEpoch, lastEpochInCommittee *big.Int, lockPeriod int, noEarlyUnlock bool, + curEpoch, lastEpochInCommittee *big.Int, lockPeriod int, noEarlyUnlock bool, isMaxRate bool, ) *big.Int { totalWithdraw := big.NewInt(0) count := 0 for j := range d.Undelegations { - if big.NewInt(0).Sub(curEpoch, d.Undelegations[j].Epoch).Int64() >= int64(lockPeriod) || - (!noEarlyUnlock && big.NewInt(0).Sub(curEpoch, lastEpochInCommittee).Int64() >= int64(lockPeriod)) { - // need to wait at least 7 epochs to withdraw; or the validator has been out of committee for 7 epochs - totalWithdraw.Add(totalWithdraw, d.Undelegations[j].Amount) + epochsSinceUndelegation := big.NewInt(0).Sub(curEpoch, d.Undelegations[j].Epoch).Int64() + // >=7 epochs have passed since undelegation, or + lockPeriodApplies := epochsSinceUndelegation >= int64(lockPeriod) + // >=7 epochs have passed since unelection during the noEarlyUnlock configuration + earlyUnlockPeriodApplies := big.NewInt(0).Sub(curEpoch, lastEpochInCommittee).Int64() >= int64(lockPeriod) && !noEarlyUnlock + maxRateApplies := isMaxRate && epochsSinceUndelegation > int64(lockPeriod) + if lockPeriodApplies || earlyUnlockPeriodApplies { + if !maxRateApplies { + totalWithdraw.Add(totalWithdraw, d.Undelegations[j].Amount) + } count++ } else { break diff --git a/staking/types/delegation_test.go b/staking/types/delegation_test.go index c40750e4c..feadda3cc 100644 --- a/staking/types/delegation_test.go +++ b/staking/types/delegation_test.go @@ -75,7 +75,7 @@ func TestUnlockedLastEpochInCommittee(t *testing.T) { amount4 := big.NewInt(4000) delegation.Undelegate(epoch4, amount4) - result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false) + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false, false) if result.Cmp(big.NewInt(8000)) != 0 { t.Errorf("removing an unlocked undelegation fails") } @@ -90,7 +90,7 @@ func TestUnlockedLastEpochInCommitteeFail(t *testing.T) { amount4 := big.NewInt(4000) delegation.Undelegate(epoch4, amount4) - result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false) + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false, false) if result.Cmp(big.NewInt(0)) != 0 { t.Errorf("premature delegation shouldn't be unlocked") } @@ -104,7 +104,7 @@ func TestUnlockedFullPeriod(t *testing.T) { amount5 := big.NewInt(4000) delegation.Undelegate(epoch5, amount5) - result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false) + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false, false) if result.Cmp(big.NewInt(4000)) != 0 { t.Errorf("removing an unlocked undelegation fails") } @@ -118,7 +118,7 @@ func TestQuickUnlock(t *testing.T) { amount7 := big.NewInt(4000) delegation.Undelegate(epoch7, amount7) - result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 0, false) + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 0, false, false) if result.Cmp(big.NewInt(4000)) != 0 { t.Errorf("removing an unlocked undelegation fails") } @@ -133,7 +133,7 @@ func TestUnlockedFullPeriodFail(t *testing.T) { amount5 := big.NewInt(4000) delegation.Undelegate(epoch5, amount5) - result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false) + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false, false) if result.Cmp(big.NewInt(0)) != 0 { t.Errorf("premature delegation shouldn't be unlocked") } @@ -147,7 +147,7 @@ func TestUnlockedPremature(t *testing.T) { amount6 := big.NewInt(4000) delegation.Undelegate(epoch6, amount6) - result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false) + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, false, false) if result.Cmp(big.NewInt(0)) != 0 { t.Errorf("premature delegation shouldn't be unlocked") } @@ -161,7 +161,7 @@ func TestNoEarlyUnlock(t *testing.T) { amount4 := big.NewInt(4000) delegation.Undelegate(epoch4, amount4) - result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true) + result := delegation.RemoveUnlockedUndelegations(curEpoch, lastEpochInCommittee, 7, true, false) if result.Cmp(big.NewInt(0)) != 0 { t.Errorf("should not allow early unlock") } diff --git a/test/build-localnet-validator.sh b/test/build-localnet-validator.sh index 08d987777..70501c8d6 100644 --- a/test/build-localnet-validator.sh +++ b/test/build-localnet-validator.sh @@ -32,7 +32,7 @@ hmy --node="http://localhost:9500" staking create-validator \ --bls-pubkeys 4f41a37a3a8d0695dd6edcc58142c6b7d98e74da5c90e79b587b3b960b6a4f5e048e6d8b8a000d77a478d44cd640270c,7dcc035a943e29e17959dabe636efad7303d2c6f273ace457ba9dcc2fd19d3f37e70ba1cd8d082cf8ff7be2f861db48c \ --name "s0-localnet-validator1" --identity "validator1" --details "validator1" \ --security-contact "localnet" --website "localnet.one" \ - --max-change-rate 0.1 --max-rate 0.1 --rate 0.1 \ + --max-change-rate 0.01 --max-rate 0.01 --rate 0.01 \ --max-total-delegation 100000000 --min-self-delegation 10000 --bls-pubkeys-dir .hmy/extbls/ hmy --node="http://localhost:9500" staking create-validator \ @@ -40,7 +40,7 @@ hmy --node="http://localhost:9500" staking create-validator \ --bls-pubkeys b0917378b179a519a5055259c4f8980cce37d58af300b00dd98b07076d3d9a3b16c4a55f84522f553872225a7b1efc0c \ --name "s0-localnet-validator2" --identity "validator2" --details "validator2" \ --security-contact "localnet" --website "localnet.one" \ - --max-change-rate 0.1 --max-rate 0.1 --rate 0.1 \ + --max-change-rate 0.1 --max-rate 0.1 --rate 0.05 \ --max-total-delegation 100000000 --min-self-delegation 10000 --bls-pubkeys-dir .hmy/extbls/ hmy --node="http://localhost:9500" staking create-validator \