Skip to content

Commit

Permalink
do not return bech32 in receipt, fix 7% fee (#4588)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
MaxMustermann2 and diego1q2w committed Dec 15, 2023
1 parent 2bba333 commit 3e7ff38
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 67 deletions.
4 changes: 3 additions & 1 deletion core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 3 additions & 7 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
}
Expand Down
8 changes: 7 additions & 1 deletion hmy/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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())
Expand All @@ -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)
}
Expand Down
30 changes: 26 additions & 4 deletions internal/chain/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
}

Expand Down
20 changes: 19 additions & 1 deletion internal/params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -161,6 +163,7 @@ var (
ValidatorCodeFixEpoch: EpochTBD,
HIP30Epoch: EpochTBD,
BlockGas30MEpoch: big.NewInt(0),
MaxRateEpoch: EpochTBD,
}

// PartnerChainConfig contains the chain parameters for the Partner network.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -292,6 +297,7 @@ var (
ValidatorCodeFixEpoch: big.NewInt(2),
HIP30Epoch: EpochTBD,
BlockGas30MEpoch: big.NewInt(0),
MaxRateEpoch: EpochTBD,
}

// AllProtocolChanges ...
Expand Down Expand Up @@ -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 ...
Expand Down Expand Up @@ -382,6 +389,7 @@ var (
big.NewInt(0), // ValidatorCodeFixEpoch
big.NewInt(0), // HIP30Epoch
big.NewInt(0), // BlockGas30M
big.NewInt(0), // MaxRateEpoch
}

// TestRules ...
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions rpc/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rpc/eth/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
14 changes: 3 additions & 11 deletions rpc/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
55 changes: 30 additions & 25 deletions rpc/v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand All @@ -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()
Expand All @@ -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
}
}
}

Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit 3e7ff38

Please sign in to comment.