Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ recover:

.PHONY: ioctl
ioctl:
$(GOBUILD) -ldflags "$(PackageFlags)" -o ./bin/$(BUILD_TARGET_IOCTL) -v ./tools/ioctl
$(GOBUILD) -tags $(BUILD_TAGS) -ldflags "$(PackageFlags)" -o ./bin/$(BUILD_TARGET_IOCTL) -v ./tools/ioctl

.PHONY: newioctl
newioctl:
Expand Down
14 changes: 7 additions & 7 deletions action/candidate_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,18 +373,18 @@ func PackCandidateRegisteredEvent(
blsPubKey []byte,
) (Topics, []byte, error) {
data, err := _candidateRegisteredEvent.Inputs.NonIndexed().Pack(
operatorAddress.Bytes(),
common.BytesToAddress(operatorAddress.Bytes()),
name,
rewardAddress.Bytes(),
common.BytesToAddress(rewardAddress.Bytes()),
blsPubKey,
)
if err != nil {
return nil, nil, errors.Wrap(err, "failed to pack CandidateRegisterWithBLS event")
}
topics := make(Topics, 3)
topics[0] = hash.Hash256(_candidateRegisteredEvent.ID)
topics[1] = hash.Hash256(candidate.Bytes())
topics[2] = hash.Hash256(ownerAddress.Bytes())
topics[1] = hash.BytesToHash256(candidate.Bytes())
topics[2] = hash.BytesToHash256(ownerAddress.Bytes())
return topics, data, nil
}

Expand All @@ -406,8 +406,8 @@ func PackStakedEvent(
}
topics := make(Topics, 3)
topics[0] = hash.Hash256(_stakedEvent.ID)
topics[1] = hash.Hash256(voter.Bytes())
topics[2] = hash.Hash256(candidate.Bytes())
topics[1] = hash.BytesToHash256(voter.Bytes())
topics[2] = hash.BytesToHash256(candidate.Bytes())
return topics, data, nil
}

Expand All @@ -422,7 +422,7 @@ func PackCandidateActivatedEvent(
}
topics := make(Topics, 2)
topics[0] = hash.Hash256(_candidateActivatedEvent.ID)
topics[1] = hash.Hash256(candidate.Bytes())
topics[1] = hash.BytesToHash256(candidate.Bytes())
return topics, data, nil
}

Expand Down
6 changes: 3 additions & 3 deletions action/candidate_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func PackCandidateUpdatedEvent(
blsPubKey []byte,
) (Topics, []byte, error) {
data, err := _candidateUpdateWithBLSEvent.Inputs.NonIndexed().Pack(
rewardAddress.Bytes(),
common.BytesToAddress(rewardAddress.Bytes()),
name,
common.BytesToAddress(operatorAddress.Bytes()),
blsPubKey,
Expand All @@ -302,7 +302,7 @@ func PackCandidateUpdatedEvent(
}
topics := make(Topics, 3)
topics[0] = hash.Hash256(_candidateUpdateWithBLSEvent.ID)
topics[1] = hash.Hash256(candidate.Bytes())
topics[2] = hash.Hash256(ownerAddress.Bytes())
topics[1] = hash.BytesToHash256(candidate.Bytes())
topics[2] = hash.BytesToHash256(ownerAddress.Bytes())
return topics, data, nil
}
102 changes: 102 additions & 0 deletions action/candidateregister_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"

"github.com/iotexproject/go-pkgs/crypto"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/iotex-address/address"

"github.com/iotexproject/iotex-core/v2/test/identityset"
)
Expand Down Expand Up @@ -239,3 +242,102 @@ func TestIsValidCandidateName(t *testing.T) {
require.Equal(tt.output, output)
}
}

func TestStakingEvent(t *testing.T) {
require := require.New(t)
cand := identityset.Address(0)
owner := identityset.Address(1)
operator := identityset.Address(2)
reward := identityset.Address(3)
voter := identityset.Address(4)
blsPrivKey, err := crypto.GenerateBLS12381PrivateKey(identityset.PrivateKey(0).Bytes())
require.NoError(err)
blsPubKey := blsPrivKey.PublicKey().Bytes()
name := "test"

t.Run("register", func(t *testing.T) {
topics, data, err := PackCandidateRegisteredEvent(cand, operator, owner, name, reward, blsPubKey)
require.NoError(err)
checkCandidateRegisterEvent(require, topics, data, owner, cand, operator, reward, name, blsPubKey)
})
t.Run("staked", func(t *testing.T) {
bktIdx := uint64(1)
amount := big.NewInt(100)
duration := uint32(3600)
autoStake := true
topics, data, err := PackStakedEvent(voter, cand, bktIdx, amount, duration, autoStake)
require.NoError(err)
checkStakedEvent(require, topics, data, voter, cand, bktIdx, amount, duration, autoStake)
})
t.Run("activate", func(t *testing.T) {
bktIdx := uint64(1)
topics, data, err := PackCandidateActivatedEvent(cand, bktIdx)
require.NoError(err)
checkActivatedEvent(require, topics, data, cand, bktIdx)
})
t.Run("update", func(t *testing.T) {
topics, data, err := PackCandidateUpdatedEvent(cand, operator, owner, name, reward, blsPubKey)
require.NoError(err)
checkCandidateUpdateEvent(require, topics, data, owner, cand, operator, reward, name, blsPubKey)
})
}

func checkCandidateRegisterEvent(require *require.Assertions, topics Topics, data []byte,
owner, cand, operator, reward address.Address, name string, blsPubKey []byte,
) {
paramsNonIndexed, err := _candidateRegisteredEvent.Inputs.Unpack(data)
require.NoError(err)
require.Equal(4, len(paramsNonIndexed))
require.Equal(operator.Bytes(), paramsNonIndexed[0].(common.Address).Bytes())
require.Equal(name, paramsNonIndexed[1].(string))
require.Equal(reward.Bytes(), paramsNonIndexed[2].(common.Address).Bytes())
require.Equal(blsPubKey, paramsNonIndexed[3].([]byte))
require.Equal(3, len(topics))
require.Equal(hash.Hash256(_candidateRegisteredEvent.ID), topics[0])
require.Equal(hash.BytesToHash256(cand.Bytes()), topics[1])
require.Equal(hash.BytesToHash256(owner.Bytes()), topics[2])
}

func checkStakedEvent(require *require.Assertions, topics Topics, data []byte,
voter, cand address.Address, bktIdx uint64, amount *big.Int, duration uint32, autoStake bool,
) {
paramsNonIndexed, err := _stakedEvent.Inputs.Unpack(data)
require.NoError(err)
require.Equal(4, len(paramsNonIndexed))
require.Equal(bktIdx, paramsNonIndexed[0].(uint64))
require.Equal(amount, paramsNonIndexed[1].(*big.Int))
require.Equal(duration, paramsNonIndexed[2].(uint32))
require.Equal(autoStake, paramsNonIndexed[3].(bool))
require.Equal(3, len(topics))
require.Equal(hash.Hash256(_stakedEvent.ID), topics[0])
require.Equal(hash.BytesToHash256(voter.Bytes()), topics[1])
require.Equal(hash.BytesToHash256(cand.Bytes()), topics[2])
}

func checkActivatedEvent(require *require.Assertions, topics Topics, data []byte,
cand address.Address, bktIdx uint64,
) {
paramsNonIndexed, err := _candidateActivatedEvent.Inputs.Unpack(data)
require.NoError(err)
require.Equal(1, len(paramsNonIndexed))
require.Equal(bktIdx, paramsNonIndexed[0].(uint64))
require.Equal(2, len(topics))
require.Equal(hash.Hash256(_candidateActivatedEvent.ID), topics[0])
require.Equal(hash.BytesToHash256(cand.Bytes()), topics[1])
}

func checkCandidateUpdateEvent(require *require.Assertions, topics Topics, data []byte,
owner, cand, operator, reward address.Address, name string, blsPubKey []byte,
) {
paramsNonIndexed, err := _candidateUpdateWithBLSEvent.Inputs.Unpack(data)
require.NoError(err)
require.Equal(4, len(paramsNonIndexed))
require.Equal(reward.Bytes(), paramsNonIndexed[0].(common.Address).Bytes())
require.Equal(name, paramsNonIndexed[1].(string))
require.Equal(operator.Bytes(), paramsNonIndexed[2].(common.Address).Bytes())
require.Equal(blsPubKey, paramsNonIndexed[3].([]byte))
require.Equal(3, len(topics))
require.Equal(hash.Hash256(_candidateUpdateWithBLSEvent.ID), topics[0])
require.Equal(hash.BytesToHash256(cand.Bytes()), topics[1])
require.Equal(hash.BytesToHash256(owner.Bytes()), topics[2])
}
12 changes: 7 additions & 5 deletions action/protocol/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ type (
CandidateBLSPublicKey bool
NotUseMinSelfStakeToBeActive bool
StoreVoteOfNFTBucketIntoView bool
CandidateSlashByOwner bool
}

// FeatureWithHeightCtx provides feature check functions.
Expand Down Expand Up @@ -325,10 +326,11 @@ func WithFeatureCtx(ctx context.Context) context.Context {
TimestampedStakingContract: g.IsWake(height),
PreStateSystemAction: !g.IsWake(height),
CreatePostActionStates: g.IsWake(height),
NotSlashUnproductiveDelegates: !g.IsToBeEnabled(height),
CandidateBLSPublicKey: g.IsToBeEnabled(height),
NotUseMinSelfStakeToBeActive: !g.IsToBeEnabled(height),
StoreVoteOfNFTBucketIntoView: !g.IsToBeEnabled(height),
NotSlashUnproductiveDelegates: !g.IsXingu(height),
CandidateBLSPublicKey: g.IsXingu(height),
NotUseMinSelfStakeToBeActive: !g.IsXingu(height),
StoreVoteOfNFTBucketIntoView: !g.IsXingu(height),
CandidateSlashByOwner: !g.IsToBeEnabled(height),
},
)
}
Expand All @@ -351,7 +353,7 @@ func GetFeatureCtx(ctx context.Context) (FeatureCtx, bool) {
func MustGetFeatureCtx(ctx context.Context) FeatureCtx {
fc, ok := ctx.Value(featureContextKey{}).(FeatureCtx)
if !ok {
log.S().Panic("Miss feature context")
log.L().Panic("Miss feature context")
}
return fc
}
Expand Down
2 changes: 1 addition & 1 deletion action/protocol/execution/evm/evmstatedbadapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ func (stateDB *StateDBAdapter) CommitContracts() error {
sort.Slice(contractAddrs, func(i, j int) bool { return bytes.Compare(contractAddrs[i][:], contractAddrs[j][:]) < 0 })

for _, addr := range contractAddrs {
_, err := stateDB.sm.DelState(protocol.KeyOption(addr[:]))
_, err := stateDB.sm.DelState(protocol.KeyOption(addr[:]), protocol.ObjectOption(&state.Account{}))
if stateDB.assertError(err, "failed to delete SelfDestruct account/contract", zap.Error(err), zap.String("address", addr.Hex())) {
return errors.Wrapf(err, "failed to delete SelfDestruct account/contract %x", addr[:])
}
Expand Down
17 changes: 13 additions & 4 deletions action/protocol/managers.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,22 @@ func ObjectOption(obj any) StateOption {
}
}

// ErigonStoreOnlyOption sets the option to only read/write from/to erigon store
func ErigonStoreOnlyOption() StateOption {
return func(cfg *StateConfig) error {
cfg.ErigonStoreOnly = true
return nil
}
}

type (
// StateConfig is the config for accessing stateDB
StateConfig struct {
Namespace string // namespace used by state's storage
Key []byte
Keys [][]byte
Object any // object used by state's storage
Namespace string // namespace used by state's storage
Key []byte
Keys [][]byte
Object any // object used by state's storage
ErigonStoreOnly bool // whether only read/write from/to erigon store
}

// StateOption sets parameter for access state
Expand Down
2 changes: 1 addition & 1 deletion action/protocol/rewarding/protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func testProtocol(t *testing.T, test func(*testing.T, context.Context, protocol.
g.Rewarding.NumDelegatesForFoundationBonus = 5
g.Rewarding.FoundationBonusLastEpoch = 365
g.Rewarding.ProductivityThreshold = 50
g.ToBeEnabledBlockHeight = slashHeight
g.XinguBlockHeight = slashHeight
// Initialize the protocol
if withExempt {
g.Rewarding.ExemptAddrStrsFromEpochReward = []string{
Expand Down
28 changes: 22 additions & 6 deletions action/protocol/rewarding/reward.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ func (p *Protocol) GrantEpochReward(
actionCtx := protocol.MustGetActionCtx(ctx)
blkCtx := protocol.MustGetBlockCtx(ctx)
featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
featureCtx := protocol.MustGetFeatureCtx(ctx)
rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx))
pp := poll.MustGetProtocol(protocol.MustGetRegistry(ctx))
epochNum := rp.GetEpochNum(blkCtx.BlockHeight)
Expand Down Expand Up @@ -213,7 +214,7 @@ func (p *Protocol) GrantEpochReward(
var err error
uqdMap := make(map[string]uint64)
epochStartHeight := rp.GetEpochHeight(epochNum)
if featureWithHeightCtx.GetUnproductiveDelegates(epochStartHeight) {
if featureWithHeightCtx.GetUnproductiveDelegates(epochStartHeight) || !featureCtx.NotSlashUnproductiveDelegates {
// Get unqualified delegate list
uqdMap, err = pp.CalculateUnproductiveDelegates(ctx, sm)
if err != nil {
Expand All @@ -227,7 +228,7 @@ func (p *Protocol) GrantEpochReward(
actualTotalReward := big.NewInt(0)
transactionLogs := make([]*action.TransactionLog, 0)
rewardLogs := make([]*action.Log, 0)
if !protocol.MustGetFeatureCtx(ctx).NotSlashUnproductiveDelegates {
if !featureCtx.NotSlashUnproductiveDelegates {
slashAmount, slashLogs, err := p.slashUqd(ctx, sm, blkCtx.BlockHeight, actionCtx.ActionHash, candidates, a.blockReward, uqdMap)
if err != nil {
return nil, nil, err
Expand All @@ -243,7 +244,11 @@ func (p *Protocol) GrantEpochReward(
rewardLogs = append(rewardLogs, slashLogs...)
actualTotalReward = big.NewInt(0).Sub(actualTotalReward, slashAmount)
}
addrs, amounts, err := p.splitEpochReward(candidates, a.epochReward, a.numDelegatesForEpochReward, exemptAddrs, uqdMap)
epochRewardSplitUqdMap := make(map[string]uint64)
if featureWithHeightCtx.GetUnproductiveDelegates(epochStartHeight) {
epochRewardSplitUqdMap = uqdMap
}
addrs, amounts, err := p.splitEpochReward(candidates, a.epochReward, a.numDelegatesForEpochReward, exemptAddrs, epochRewardSplitUqdMap)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -541,6 +546,7 @@ func (p *Protocol) slashUqd(
}
slashLogs := make([]*action.Log, 0)
snapshot := view.Snapshot()
fCtx := protocol.MustGetFeatureCtx(ctx)
for _, candidate := range candidates {
if missed, ok := uqdMap[candidate.Address]; ok {
if missed == 0 {
Expand All @@ -549,14 +555,24 @@ func (p *Protocol) slashUqd(
}
amount := big.NewInt(0).Mul(slashRate, big.NewInt(0).SetUint64(missed))
actLog, err := p.slashDelegate(ctx, sm, stakingProtocol, blockHeight, actionHash, candidate, amount)
if err != nil {
switch errors.Cause(err) {
case nil:
slashLogs = append(slashLogs, actLog)
totalSlashAmount.Add(totalSlashAmount, amount)
case staking.ErrNoSelfStakeBucket:
log.S().Errorf("Candidate %s doesn't have self-stake bucket, no slash", candidate.Address)
case staking.ErrCandidateNotExist:
if !fCtx.CandidateSlashByOwner {
log.S().Errorf("Candidate %s doesn't exist, ignore slash", candidate.Address)
continue
}
fallthrough
default:
if err := view.Revert(snapshot); err != nil {
return nil, nil, errors.Wrap(err, "failed to revert view")
}
return nil, nil, err
}
slashLogs = append(slashLogs, actLog)
totalSlashAmount.Add(totalSlashAmount, amount)
}
}
return totalSlashAmount, slashLogs, nil
Expand Down
29 changes: 29 additions & 0 deletions action/protocol/staking/candidate_center.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,22 @@ func (m *CandidateCenter) GetBySelfStakingIndex(index uint64) *Candidate {
return nil
}

// GetByOperator returns the candidate by operator
func (m *CandidateCenter) GetByOperator(operator address.Address) *Candidate {
if operator == nil {
return nil
}

if d := m.change.getByOperator(operator); d != nil {
return d
}

if d, hit := m.base.getByOperator(operator.String()); hit {
return d.Clone()
}
return nil
}

// Upsert adds a candidate into map, overwrites if already exist
func (m *CandidateCenter) Upsert(d *Candidate) error {
if err := d.Validate(); err != nil {
Expand Down Expand Up @@ -461,6 +477,19 @@ func (cc *candChange) getBySelfStakingIndex(index uint64) *Candidate {
return nil
}

func (cc *candChange) getByOperator(operator address.Address) *Candidate {
if operator == nil {
return nil
}

for _, d := range cc.dirty {
if address.Equal(operator, d.Operator) {
return d.Clone()
}
}
return nil
}

func (cc *candChange) upsert(d *Candidate) error {
if err := d.Validate(); err != nil {
return err
Expand Down
Loading
Loading