do not return bech32 in receipt, fix 7% fee (#4588)

* Fix: max rate issue (#4580)

* fix: max-rate bellow the era min-rate

* fix comments

* add localnet epoch config

* update config

* update config

* update config

* update config

* add log

* remove hip30 from localnet

* disable localnet config

* engine: actually fix the 7% fee implementation

* rpc: fix transaction receipt format for eth

use the same receipt as `hmyv2_`. using a boolean variable, decide if
the addresses need to be converted to bech32. do not return a contract
address unless a contract was actually deployed in the transaction by
using a pointer address type.

* rpc: add comment indicating function is unused

with the switch to `v2.NewReceipt` for even `eth_` queries, the
`eth.NewReceipt` function is no longer used

* build: fix delegation tests

* update comment

blocks was referring to `blocks of code` and not blocks in a chain.
removed the confusing word

* rpc: remove ConvertToEth in GetBlockReceipts

* internal: max rate hard fork schedule

* internal: testnet max rate schedule

---------

Co-authored-by: Diego Nava <8563843+diego1q2w@users.noreply.github.com>
pull/4590/head v2023.4.0
Max 12 months ago committed by GitHub
parent 2bba333bf2
commit 3e7ff3839f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      core/state/statedb.go
  2. 10
      core/tx_pool.go
  3. 8
      hmy/staking.go
  4. 30
      internal/chain/engine.go
  5. 20
      internal/params/config.go
  6. 4
      rpc/blockchain.go
  7. 2
      rpc/eth/types.go
  8. 14
      rpc/transaction.go
  9. 55
      rpc/v2/types.go
  10. 24
      staking/availability/measure.go
  11. 16
      staking/types/delegation.go
  12. 14
      staking/types/delegation_test.go
  13. 4
      test/build-localnet-validator.sh

@ -894,7 +894,9 @@ func (db *DB) Finalise(deleteEmptyObjects bool) {
// Commit validator changes in cache to stateObjects // Commit validator changes in cache to stateObjects
// TODO: remove validator cache after commit // TODO: remove validator cache after commit
for addr, wrapper := range db.stateValidators { 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)) addressesToPrefetch := make([][]byte, 0, len(db.journal.dirties))
for addr := range db.journal.dirties { for addr := range db.journal.dirties {

@ -850,15 +850,11 @@ func (pool *TxPool) validateStakingTx(tx *staking.StakingTransaction) error {
} }
currentBlockNumber := pool.chain.CurrentBlock().Number() currentBlockNumber := pool.chain.CurrentBlock().Number()
pendingBlockNumber := new(big.Int).Add(currentBlockNumber, big.NewInt(1)) 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) chainContext, ok := pool.chain.(ChainContext)
if !ok { if !ok {
chainContext = nil // might use testing blockchain, set to nil for verifier to handle. 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 return err
case staking.DirectiveEditValidator: case staking.DirectiveEditValidator:
msg, err := staking.RLPDecodeStakeMsg(tx.Data(), 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 { if from != stkMsg.DelegatorAddress {
return errors.WithMessagef(ErrInvalidSender, "staking transaction sender is %s", b32) return errors.WithMessagef(ErrInvalidSender, "staking transaction sender is %s", b32)
} }
_, err = VerifyAndUndelegateFromMsg(pool.currentState, pool.pendingEpoch(), stkMsg) _, err = VerifyAndUndelegateFromMsg(pool.currentState, pool.pendingEpoch(), stkMsg)
return err return err
case staking.DirectiveCollectRewards: 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 { func (pool *TxPool) pendingEpoch() *big.Int {
currentBlock := pool.chain.CurrentBlock() currentBlock := pool.chain.CurrentBlock()
pendingEpoch := currentBlock.Epoch() pendingEpoch := currentBlock.Epoch()
if shard.Schedule.IsLastBlock(currentBlock.Number().Uint64()) { if shard.Schedule.IsLastBlock(currentBlock.Number().Uint64()) {
pendingEpoch.Add(pendingEpoch, big.NewInt(1)) pendingEpoch = new(big.Int).Add(pendingEpoch, common.Big1)
} }
return pendingEpoch return pendingEpoch
} }

@ -143,6 +143,11 @@ func (hmy *Harmony) IsNoEarlyUnlockEpoch(epoch *big.Int) bool {
return hmy.BlockChain.Config().IsNoEarlyUnlock(epoch) 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 // IsCommitteeSelectionBlock checks if the given block is the committee selection block
func (hmy *Harmony) IsCommitteeSelectionBlock(header *block.Header) bool { func (hmy *Harmony) IsCommitteeSelectionBlock(header *block.Header) bool {
return chain.IsCommitteeSelectionBlock(hmy.BlockChain, header) return chain.IsCommitteeSelectionBlock(hmy.BlockChain, header)
@ -592,6 +597,7 @@ func (hmy *Harmony) GetUndelegationPayouts(
return undelegationPayouts, nil return undelegationPayouts, nil
} }
isMaxRate := hmy.IsMaxRate(epoch)
lockingPeriod := hmy.GetDelegationLockingPeriodInEpoch(undelegationPayoutBlock.Epoch()) lockingPeriod := hmy.GetDelegationLockingPeriodInEpoch(undelegationPayoutBlock.Epoch())
for _, validator := range hmy.GetAllValidatorAddresses() { for _, validator := range hmy.GetAllValidatorAddresses() {
wrapper, err := hmy.BlockChain.ReadValidatorInformationAtRoot(validator, undelegationPayoutBlock.Root()) wrapper, err := hmy.BlockChain.ReadValidatorInformationAtRoot(validator, undelegationPayoutBlock.Root())
@ -600,7 +606,7 @@ func (hmy *Harmony) GetUndelegationPayouts(
} }
noEarlyUnlock := hmy.IsNoEarlyUnlockEpoch(epoch) noEarlyUnlock := hmy.IsNoEarlyUnlockEpoch(epoch)
for _, delegation := range wrapper.Delegations { 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 { if withdraw.Cmp(bigZero) == 1 {
undelegationPayouts.SetPayoutByDelegatorAddrAndValidatorAddr(validator, delegation.DelegatorAddress, withdraw) undelegationPayouts.SetPayoutByDelegatorAddrAndValidatorAddr(validator, delegation.DelegatorAddress, withdraw)
} }

@ -370,9 +370,15 @@ func payoutUndelegations(
const msg = "[Finalize] failed to read all validators" const msg = "[Finalize] failed to read all validators"
return errors.New(msg) return errors.New(msg)
} }
// Payout undelegated/unlocked tokens // Payout undelegated/unlocked tokens at the end of each epoch
lockPeriod := GetLockPeriodInEpoch(chain, header.Epoch()) lockPeriod := GetLockPeriodInEpoch(chain, header.Epoch())
noEarlyUnlock := chain.Config().IsNoEarlyUnlock(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 { for _, validator := range validators {
wrapper, err := state.ValidatorWrapper(validator, true, false) wrapper, err := state.ValidatorWrapper(validator, true, false)
if err != nil { if err != nil {
@ -383,7 +389,7 @@ func payoutUndelegations(
for i := range wrapper.Delegations { for i := range wrapper.Delegations {
delegation := &wrapper.Delegations[i] delegation := &wrapper.Delegations[i]
totalWithdraw := delegation.RemoveUnlockedUndelegations( totalWithdraw := delegation.RemoveUnlockedUndelegations(
header.Epoch(), wrapper.LastEpochInCommittee, lockPeriod, noEarlyUnlock, header.Epoch(), wrapper.LastEpochInCommittee, lockPeriod, noEarlyUnlock, isMaxRate,
) )
if totalWithdraw.Sign() != 0 { if totalWithdraw.Sign() != 0 {
state.AddBalance(delegation.DelegatorAddress, totalWithdraw) state.AddBalance(delegation.DelegatorAddress, totalWithdraw)
@ -426,6 +432,7 @@ func setElectionEpochAndMinFee(chain engine.ChainReader, header *block.Header, s
map[common.Address]struct{}, map[common.Address]struct{},
len(newShardState.StakedValidators().Addrs), len(newShardState.StakedValidators().Addrs),
) )
// this loop is for elected validators only
for _, addr := range newShardState.StakedValidators().Addrs { for _, addr := range newShardState.StakedValidators().Addrs {
wrapper, err := state.ValidatorWrapper(addr, true, false) wrapper, err := state.ValidatorWrapper(addr, true, false)
if err != nil { if err != nil {
@ -448,11 +455,13 @@ func setElectionEpochAndMinFee(chain engine.ChainReader, header *block.Header, s
} }
isElected[addr] = struct{}{} isElected[addr] = struct{}{}
} }
// due to a bug in the old implementation of the minimum fee, // due to a bug in the old implementation of the minimum fee,
// unelected validators did not have their fee updated even // unelected validators did not have their fee updated even
// when the protocol required them to do so. here we fix it, // when the protocol required them to do so. here we fix it,
// but only after the HIP-30 hard fork is effective. // but only after the HIP-30 hard fork is effective
if config.IsHIP30(newShardState.Epoch) { // this loop applies to all validators, but excludes the ones in isElected
if config.IsHIP30(newShardState.Epoch) && minRateNotZero {
for _, addr := range chain.ValidatorCandidates() { for _, addr := range chain.ValidatorCandidates() {
// skip elected validator // skip elected validator
if _, ok := isElected[addr]; ok { 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 return nil
} }

@ -75,6 +75,7 @@ var (
ValidatorCodeFixEpoch: big.NewInt(1535), // 2023-07-20 05:51:07+00:00 ValidatorCodeFixEpoch: big.NewInt(1535), // 2023-07-20 05:51:07+00:00
HIP30Epoch: big.NewInt(1673), // 2023-11-02 17:30:00+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 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. // 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 ValidatorCodeFixEpoch: big.NewInt(1296), // 2023-04-28 07:14:20+00:00
HIP30Epoch: big.NewInt(2176), // 2023-10-12 10:00:00+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 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. // PangaeaChainConfig contains the chain parameters for the Pangaea network.
// All features except for CrossLink are enabled at launch. // All features except for CrossLink are enabled at launch.
@ -161,6 +163,7 @@ var (
ValidatorCodeFixEpoch: EpochTBD, ValidatorCodeFixEpoch: EpochTBD,
HIP30Epoch: EpochTBD, HIP30Epoch: EpochTBD,
BlockGas30MEpoch: big.NewInt(0), BlockGas30MEpoch: big.NewInt(0),
MaxRateEpoch: EpochTBD,
} }
// PartnerChainConfig contains the chain parameters for the Partner network. // PartnerChainConfig contains the chain parameters for the Partner network.
@ -205,6 +208,7 @@ var (
ValidatorCodeFixEpoch: big.NewInt(5), ValidatorCodeFixEpoch: big.NewInt(5),
HIP30Epoch: big.NewInt(7), HIP30Epoch: big.NewInt(7),
BlockGas30MEpoch: big.NewInt(7), BlockGas30MEpoch: big.NewInt(7),
MaxRateEpoch: EpochTBD,
} }
// StressnetChainConfig contains the chain parameters for the Stress test network. // StressnetChainConfig contains the chain parameters for the Stress test network.
@ -249,6 +253,7 @@ var (
ValidatorCodeFixEpoch: EpochTBD, ValidatorCodeFixEpoch: EpochTBD,
HIP30Epoch: EpochTBD, HIP30Epoch: EpochTBD,
BlockGas30MEpoch: big.NewInt(0), BlockGas30MEpoch: big.NewInt(0),
MaxRateEpoch: EpochTBD,
} }
// LocalnetChainConfig contains the chain parameters to run for local development. // LocalnetChainConfig contains the chain parameters to run for local development.
@ -292,6 +297,7 @@ var (
ValidatorCodeFixEpoch: big.NewInt(2), ValidatorCodeFixEpoch: big.NewInt(2),
HIP30Epoch: EpochTBD, HIP30Epoch: EpochTBD,
BlockGas30MEpoch: big.NewInt(0), BlockGas30MEpoch: big.NewInt(0),
MaxRateEpoch: EpochTBD,
} }
// AllProtocolChanges ... // AllProtocolChanges ...
@ -336,7 +342,8 @@ var (
big.NewInt(0), // FeeCollectEpoch big.NewInt(0), // FeeCollectEpoch
big.NewInt(0), // ValidatorCodeFixEpoch big.NewInt(0), // ValidatorCodeFixEpoch
big.NewInt(0), // BlockGas30M big.NewInt(0), // BlockGas30M
big.NewInt(0), // HIP30Epoch big.NewInt(0), // BlockGas30M
big.NewInt(0), // MaxRateEpoch
} }
// TestChainConfig ... // TestChainConfig ...
@ -382,6 +389,7 @@ var (
big.NewInt(0), // ValidatorCodeFixEpoch big.NewInt(0), // ValidatorCodeFixEpoch
big.NewInt(0), // HIP30Epoch big.NewInt(0), // HIP30Epoch
big.NewInt(0), // BlockGas30M big.NewInt(0), // BlockGas30M
big.NewInt(0), // MaxRateEpoch
} }
// TestRules ... // TestRules ...
@ -547,6 +555,9 @@ type ChainConfig struct {
HIP30Epoch *big.Int `json:"hip30-epoch,omitempty"` HIP30Epoch *big.Int `json:"hip30-epoch,omitempty"`
BlockGas30MEpoch *big.Int `json:"block-gas-30m-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. // String implements the fmt.Stringer interface.
@ -612,6 +623,9 @@ func (c *ChainConfig) mustValid() {
// capabilities required to transfer balance across shards // capabilities required to transfer balance across shards
require(c.HIP30Epoch.Cmp(c.CrossTxEpoch) > 0, require(c.HIP30Epoch.Cmp(c.CrossTxEpoch) > 0,
"must satisfy: HIP30Epoch > CrossTxEpoch") "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. // 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) 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 // During this epoch, shards 2 and 3 will start sending
// their balances over to shard 0 or 1. // their balances over to shard 0 or 1.
func (c *ChainConfig) IsOneEpochBeforeHIP30(epoch *big.Int) bool { func (c *ChainConfig) IsOneEpochBeforeHIP30(epoch *big.Int) bool {

@ -461,10 +461,10 @@ func (s *PublicBlockchainService) GetBlockReceipts(
case V1: case V1:
r, err = v1.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()]) r, err = v1.NewReceipt(tx, blockHash, block.NumberU64(), index, rmap[tx.Hash()])
case V2: 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: case Eth:
if tx, ok := tx.(*types.Transaction); ok { 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: default:
return nil, ErrUnknownRPCVersion return nil, ErrUnknownRPCVersion

@ -111,7 +111,7 @@ func NewTransaction(
return result, nil 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) { func NewReceipt(tx *types.EthTransaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt) (map[string]interface{}, error) {
senderAddr, err := tx.SenderAddress() senderAddr, err := tx.SenderAddress()
if err != nil { if err != nil {

@ -751,19 +751,11 @@ func (s *PublicTransactionService) GetTransactionReceipt(
return nil, err return nil, err
} }
return NewStructuredResponse(RPCReceipt) return NewStructuredResponse(RPCReceipt)
case V2: case V2, Eth:
if tx == nil { if tx == nil {
RPCReceipt, err = v2.NewReceipt(stx, blockHash, blockNumber, index, receipt) RPCReceipt, err = v2.NewReceipt(stx, blockHash, blockNumber, index, receipt, false)
} else { } else {
RPCReceipt, err = v2.NewReceipt(tx, blockHash, blockNumber, index, receipt) RPCReceipt, err = v2.NewReceipt(tx, blockHash, blockNumber, index, receipt, s.version == Eth)
}
if err != nil {
return nil, err
}
return NewStructuredResponse(RPCReceipt)
case Eth:
if tx != nil {
RPCReceipt, err = eth.NewReceipt(tx.ConvertToEth(), blockHash, blockNumber, index, receipt)
} }
if err != nil { if err != nil {
return nil, err return nil, err

@ -204,20 +204,20 @@ type UndelegateMsg struct {
// TxReceipt represents a transaction receipt that will serialize to the RPC representation. // TxReceipt represents a transaction receipt that will serialize to the RPC representation.
type TxReceipt struct { type TxReceipt struct {
BlockHash common.Hash `json:"blockHash"` BlockHash common.Hash `json:"blockHash"`
TransactionHash common.Hash `json:"transactionHash"` TransactionHash common.Hash `json:"transactionHash"`
BlockNumber uint64 `json:"blockNumber"` BlockNumber uint64 `json:"blockNumber"`
TransactionIndex uint64 `json:"transactionIndex"` TransactionIndex uint64 `json:"transactionIndex"`
GasUsed uint64 `json:"gasUsed"` GasUsed uint64 `json:"gasUsed"`
CumulativeGasUsed uint64 `json:"cumulativeGasUsed"` CumulativeGasUsed uint64 `json:"cumulativeGasUsed"`
ContractAddress common.Address `json:"contractAddress"` ContractAddress *common.Address `json:"contractAddress"`
Logs []*types.Log `json:"logs"` Logs []*types.Log `json:"logs"`
LogsBloom ethtypes.Bloom `json:"logsBloom"` LogsBloom ethtypes.Bloom `json:"logsBloom"`
ShardID uint32 `json:"shardID"` ShardID uint32 `json:"shardID"`
From string `json:"from"` From string `json:"from"`
To string `json:"to"` To string `json:"to"`
Root hexutil.Bytes `json:"root"` Root hexutil.Bytes `json:"root"`
Status uint `json:"status"` Status uint `json:"status"`
} }
// StakingTxReceipt represents a staking transaction receipt that will serialize to the RPC representation. // 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 // NewReceipt returns a transaction OR staking transaction that will serialize to the RPC representation
func NewReceipt( 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) { ) (interface{}, error) {
plainTx, ok := tx.(*types.Transaction) plainTx, ok := tx.(*types.Transaction)
if ok { if ok {
return NewTxReceipt(plainTx, blockHash, blockNumber, blockIndex, receipt) return NewTxReceipt(plainTx, blockHash, blockNumber, blockIndex, receipt, eth)
} }
stakingTx, ok := tx.(*staking.StakingTransaction) stakingTx, ok := tx.(*staking.StakingTransaction)
if ok { if ok {
@ -349,7 +349,7 @@ func NewReceipt(
// NewTxReceipt returns a plain transaction receipt that will serialize to the RPC representation // NewTxReceipt returns a plain transaction receipt that will serialize to the RPC representation
func NewTxReceipt( 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) { ) (*TxReceipt, error) {
// Set correct to & from address // Set correct to & from address
senderAddr, err := tx.SenderAddress() senderAddr, err := tx.SenderAddress()
@ -363,13 +363,18 @@ func NewTxReceipt(
receiver = "" receiver = ""
} else { } else {
// Handle response type for regular transaction // Handle response type for regular transaction
sender, err = internal_common.AddressToBech32(senderAddr) if eth {
if err != nil { sender = senderAddr.String()
return nil, err receiver = tx.To().String()
} } else {
receiver, err = internal_common.AddressToBech32(*tx.To()) sender, err = internal_common.AddressToBech32(senderAddr)
if err != nil { if err != nil {
return nil, err 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 the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if receipt.ContractAddress != (common.Address{}) { if receipt.ContractAddress != (common.Address{}) {
txReceipt.ContractAddress = receipt.ContractAddress txReceipt.ContractAddress = &receipt.ContractAddress
} }
return txReceipt, nil return txReceipt, nil
} }

@ -267,3 +267,27 @@ func UpdateMinimumCommissionFee(
} }
return false, nil 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
}

@ -178,15 +178,21 @@ func (d *Delegation) DeleteEntry(epoch *big.Int) {
// RemoveUnlockedUndelegations removes all fully unlocked // RemoveUnlockedUndelegations removes all fully unlocked
// undelegations and returns the total sum // undelegations and returns the total sum
func (d *Delegation) RemoveUnlockedUndelegations( func (d *Delegation) RemoveUnlockedUndelegations(
curEpoch, lastEpochInCommittee *big.Int, lockPeriod int, noEarlyUnlock bool, curEpoch, lastEpochInCommittee *big.Int, lockPeriod int, noEarlyUnlock bool, isMaxRate bool,
) *big.Int { ) *big.Int {
totalWithdraw := big.NewInt(0) totalWithdraw := big.NewInt(0)
count := 0 count := 0
for j := range d.Undelegations { for j := range d.Undelegations {
if big.NewInt(0).Sub(curEpoch, d.Undelegations[j].Epoch).Int64() >= int64(lockPeriod) || epochsSinceUndelegation := big.NewInt(0).Sub(curEpoch, d.Undelegations[j].Epoch).Int64()
(!noEarlyUnlock && big.NewInt(0).Sub(curEpoch, lastEpochInCommittee).Int64() >= int64(lockPeriod)) { // >=7 epochs have passed since undelegation, or
// need to wait at least 7 epochs to withdraw; or the validator has been out of committee for 7 epochs lockPeriodApplies := epochsSinceUndelegation >= int64(lockPeriod)
totalWithdraw.Add(totalWithdraw, d.Undelegations[j].Amount) // >=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++ count++
} else { } else {
break break

@ -75,7 +75,7 @@ func TestUnlockedLastEpochInCommittee(t *testing.T) {
amount4 := big.NewInt(4000) amount4 := big.NewInt(4000)
delegation.Undelegate(epoch4, amount4) 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 { if result.Cmp(big.NewInt(8000)) != 0 {
t.Errorf("removing an unlocked undelegation fails") t.Errorf("removing an unlocked undelegation fails")
} }
@ -90,7 +90,7 @@ func TestUnlockedLastEpochInCommitteeFail(t *testing.T) {
amount4 := big.NewInt(4000) amount4 := big.NewInt(4000)
delegation.Undelegate(epoch4, amount4) 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 { if result.Cmp(big.NewInt(0)) != 0 {
t.Errorf("premature delegation shouldn't be unlocked") t.Errorf("premature delegation shouldn't be unlocked")
} }
@ -104,7 +104,7 @@ func TestUnlockedFullPeriod(t *testing.T) {
amount5 := big.NewInt(4000) amount5 := big.NewInt(4000)
delegation.Undelegate(epoch5, amount5) 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 { if result.Cmp(big.NewInt(4000)) != 0 {
t.Errorf("removing an unlocked undelegation fails") t.Errorf("removing an unlocked undelegation fails")
} }
@ -118,7 +118,7 @@ func TestQuickUnlock(t *testing.T) {
amount7 := big.NewInt(4000) amount7 := big.NewInt(4000)
delegation.Undelegate(epoch7, amount7) 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 { if result.Cmp(big.NewInt(4000)) != 0 {
t.Errorf("removing an unlocked undelegation fails") t.Errorf("removing an unlocked undelegation fails")
} }
@ -133,7 +133,7 @@ func TestUnlockedFullPeriodFail(t *testing.T) {
amount5 := big.NewInt(4000) amount5 := big.NewInt(4000)
delegation.Undelegate(epoch5, amount5) 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 { if result.Cmp(big.NewInt(0)) != 0 {
t.Errorf("premature delegation shouldn't be unlocked") t.Errorf("premature delegation shouldn't be unlocked")
} }
@ -147,7 +147,7 @@ func TestUnlockedPremature(t *testing.T) {
amount6 := big.NewInt(4000) amount6 := big.NewInt(4000)
delegation.Undelegate(epoch6, amount6) 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 { if result.Cmp(big.NewInt(0)) != 0 {
t.Errorf("premature delegation shouldn't be unlocked") t.Errorf("premature delegation shouldn't be unlocked")
} }
@ -161,7 +161,7 @@ func TestNoEarlyUnlock(t *testing.T) {
amount4 := big.NewInt(4000) amount4 := big.NewInt(4000)
delegation.Undelegate(epoch4, amount4) 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 { if result.Cmp(big.NewInt(0)) != 0 {
t.Errorf("should not allow early unlock") t.Errorf("should not allow early unlock")
} }

@ -32,7 +32,7 @@ hmy --node="http://localhost:9500" staking create-validator \
--bls-pubkeys 4f41a37a3a8d0695dd6edcc58142c6b7d98e74da5c90e79b587b3b960b6a4f5e048e6d8b8a000d77a478d44cd640270c,7dcc035a943e29e17959dabe636efad7303d2c6f273ace457ba9dcc2fd19d3f37e70ba1cd8d082cf8ff7be2f861db48c \ --bls-pubkeys 4f41a37a3a8d0695dd6edcc58142c6b7d98e74da5c90e79b587b3b960b6a4f5e048e6d8b8a000d77a478d44cd640270c,7dcc035a943e29e17959dabe636efad7303d2c6f273ace457ba9dcc2fd19d3f37e70ba1cd8d082cf8ff7be2f861db48c \
--name "s0-localnet-validator1" --identity "validator1" --details "validator1" \ --name "s0-localnet-validator1" --identity "validator1" --details "validator1" \
--security-contact "localnet" --website "localnet.one" \ --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/ --max-total-delegation 100000000 --min-self-delegation 10000 --bls-pubkeys-dir .hmy/extbls/
hmy --node="http://localhost:9500" staking create-validator \ hmy --node="http://localhost:9500" staking create-validator \
@ -40,7 +40,7 @@ hmy --node="http://localhost:9500" staking create-validator \
--bls-pubkeys b0917378b179a519a5055259c4f8980cce37d58af300b00dd98b07076d3d9a3b16c4a55f84522f553872225a7b1efc0c \ --bls-pubkeys b0917378b179a519a5055259c4f8980cce37d58af300b00dd98b07076d3d9a3b16c4a55f84522f553872225a7b1efc0c \
--name "s0-localnet-validator2" --identity "validator2" --details "validator2" \ --name "s0-localnet-validator2" --identity "validator2" --details "validator2" \
--security-contact "localnet" --website "localnet.one" \ --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/ --max-total-delegation 100000000 --min-self-delegation 10000 --bls-pubkeys-dir .hmy/extbls/
hmy --node="http://localhost:9500" staking create-validator \ hmy --node="http://localhost:9500" staking create-validator \

Loading…
Cancel
Save