Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 9 commits into from
Dec 15, 2023
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)
ONECasey marked this conversation as resolved.
Show resolved Hide resolved
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")
MaxMustermann2 marked this conversation as resolved.
Show resolved Hide resolved
}

// 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)
MaxMustermann2 marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading