diff --git a/action/protocol/account/transfer_test.go b/action/protocol/account/transfer_test.go index 34bcefad26..c0dbe11ad8 100644 --- a/action/protocol/account/transfer_test.go +++ b/action/protocol/account/transfer_test.go @@ -51,7 +51,7 @@ func TestProtocol_HandleTransfer(t *testing.T) { // set-up protocol and genesis states p := NewProtocol(rewarding.DepositGas) - reward := rewarding.NewProtocol(0, 0) + reward := rewarding.NewProtocol(config.Default.Genesis.Rewarding) registry := protocol.NewRegistry() require.NoError(reward.Register(registry)) chainCtx := genesis.WithGenesisContext( diff --git a/action/protocol/execution/protocol_test.go b/action/protocol/execution/protocol_test.go index b70f359012..d92875db9d 100644 --- a/action/protocol/execution/protocol_test.go +++ b/action/protocol/execution/protocol_test.go @@ -409,7 +409,7 @@ func (sct *SmartContractTest) prepareBlockchain( protocol.NewGenericValidator(sf, accountutil.AccountState), )), ) - reward := rewarding.NewProtocol(0, 0) + reward := rewarding.NewProtocol(cfg.Genesis.Rewarding) r.NoError(reward.Register(registry)) r.NotNil(bc) diff --git a/action/protocol/rewarding/admin.go b/action/protocol/rewarding/admin.go index 814e2a97c5..3b355bf1f0 100644 --- a/action/protocol/rewarding/admin.go +++ b/action/protocol/rewarding/admin.go @@ -28,6 +28,7 @@ type admin struct { numDelegatesForFoundationBonus uint64 foundationBonusLastEpoch uint64 productivityThreshold uint64 + foundationBonusExtension []genesis.Period } // Serialize serializes admin state into bytes @@ -40,10 +41,26 @@ func (a admin) Serialize() ([]byte, error) { NumDelegatesForFoundationBonus: a.numDelegatesForFoundationBonus, FoundationBonusLastEpoch: a.foundationBonusLastEpoch, ProductivityThreshold: a.productivityThreshold, + FoundationBonusExtension: a.periodProto(), } return proto.Marshal(&gen) } +func (a *admin) periodProto() []*rewardingpb.Period { + if len(a.foundationBonusExtension) == 0 { + return nil + } + + pb := make([]*rewardingpb.Period, len(a.foundationBonusExtension)) + for i, ext := range a.foundationBonusExtension { + pb[i] = &rewardingpb.Period{ + Start: ext.Start, + End: ext.End, + } + } + return pb +} + // Deserialize deserializes bytes into admin state func (a *admin) Deserialize(data []byte) error { gen := rewardingpb.Admin{} @@ -69,9 +86,37 @@ func (a *admin) Deserialize(data []byte) error { a.numDelegatesForFoundationBonus = gen.NumDelegatesForFoundationBonus a.foundationBonusLastEpoch = gen.FoundationBonusLastEpoch a.productivityThreshold = gen.ProductivityThreshold + + if len(gen.FoundationBonusExtension) == 0 { + return nil + } + a.foundationBonusExtension = make([]genesis.Period, len(gen.FoundationBonusExtension)) + for i, v := range gen.FoundationBonusExtension { + a.foundationBonusExtension[i] = genesis.Period{ + Start: v.Start, + End: v.End, + } + } return nil } +func (a *admin) hasFoundationBonusExtension() bool { + // starting Kamchatka height, we add the foundation bonus extension epoch into admin{} struct + return len(a.foundationBonusExtension) > 0 +} + +func (a *admin) grantFoundationBonus(epoch uint64) bool { + if epoch <= a.foundationBonusLastEpoch { + return true + } + for _, ext := range a.foundationBonusExtension { + if epoch >= ext.Start && epoch <= ext.End { + return true + } + } + return false +} + // exempt stores the addresses that exempt from epoch reward type exempt struct { addrs []address.Address @@ -124,14 +169,6 @@ func (p *Protocol) CreateGenesisStates( return err } - initBalance := g.InitBalance() - numDelegatesForEpochReward := g.NumDelegatesForEpochReward - exemptAddrs := g.ExemptAddrsFromEpochReward() - foundationBonus := g.FoundationBonus() - numDelegatesForFoundationBonus := g.NumDelegatesForFoundationBonus - foundationBonusLastEpoch := g.FoundationBonusLastEpoch - productivityThreshold := g.ProductivityThreshold - if err := p.putState( ctx, sm, @@ -139,15 +176,17 @@ func (p *Protocol) CreateGenesisStates( &admin{ blockReward: blockReward, epochReward: epochReward, - numDelegatesForEpochReward: numDelegatesForEpochReward, - foundationBonus: foundationBonus, - numDelegatesForFoundationBonus: numDelegatesForFoundationBonus, - foundationBonusLastEpoch: foundationBonusLastEpoch, - productivityThreshold: productivityThreshold, + numDelegatesForEpochReward: g.NumDelegatesForEpochReward, + foundationBonus: g.FoundationBonus(), + numDelegatesForFoundationBonus: g.NumDelegatesForFoundationBonus, + foundationBonusLastEpoch: g.FoundationBonusLastEpoch, + productivityThreshold: g.ProductivityThreshold, }, ); err != nil { return err } + + initBalance := g.InitBalance() if err := p.putState( ctx, sm, @@ -164,7 +203,7 @@ func (p *Protocol) CreateGenesisStates( sm, exemptKey, &exempt{ - addrs: exemptAddrs, + addrs: g.ExemptAddrsFromEpochReward(), }, ) } diff --git a/action/protocol/rewarding/admin_test.go b/action/protocol/rewarding/admin_test.go index ac5176f06f..edfdff8726 100644 --- a/action/protocol/rewarding/admin_test.go +++ b/action/protocol/rewarding/admin_test.go @@ -8,6 +8,7 @@ package rewarding import ( "context" + "encoding/hex" "math/big" "testing" @@ -15,8 +16,40 @@ import ( "github.com/stretchr/testify/require" "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/blockchain/genesis" + "github.com/iotexproject/iotex-core/config" ) +func TestAdminPb(t *testing.T) { + r := require.New(t) + + // actual data of admin.v1 on mainnet + b, err := hex.DecodeString("0a133830303030303030303030303030303030303012173138373530303030303030303030303030303030303030186422143830303030303030303030303030303030303030282430b8443855") + r.NoError(err) + a := admin{} + r.NoError(a.Deserialize(b)) + + g := genesis.Default + r.Equal(a.blockReward.String(), g.DardanellesBlockRewardStr) + r.Equal(a.epochReward.String(), g.AleutianEpochRewardStr) + r.Equal(a.numDelegatesForEpochReward, g.NumDelegatesForEpochReward) + r.Equal(a.foundationBonus.String(), g.FoundationBonusStr) + r.Equal(a.numDelegatesForFoundationBonus, g.NumDelegatesForFoundationBonus) + r.Equal(a.foundationBonusLastEpoch, g.FoundationBonusLastEpoch) + r.EqualValues(85, a.productivityThreshold) + r.False(a.hasFoundationBonusExtension()) + + // add foundation bonus extension + a.foundationBonusExtension = config.Default.Genesis.Rewarding.FoundationBonusExtension + b1, err := a.Serialize() + r.NoError(err) + r.Equal(b, b1[:len(b)]) + a1 := admin{} + r.NoError(a1.Deserialize(b1)) + r.True(a1.hasFoundationBonusExtension()) + r.Equal(a, a1) +} + func TestProtocol_SetEpochReward(t *testing.T) { testProtocol(t, func(t *testing.T, ctx context.Context, sm protocol.StateManager, p *Protocol) { amount, err := p.EpochReward(ctx, sm) diff --git a/action/protocol/rewarding/protocol.go b/action/protocol/rewarding/protocol.go index 41b9a4c099..0497165563 100644 --- a/action/protocol/rewarding/protocol.go +++ b/action/protocol/rewarding/protocol.go @@ -39,36 +39,55 @@ var ( epochRewardHistoryKeyPrefix = []byte("erh") accountKeyPrefix = []byte("acc") exemptKey = []byte("xpt") + errInvalidEpoch = errors.New("invalid start/end epoch number") ) // Protocol defines the protocol of the rewarding fund and the rewarding process. It allows the admin to config the // reward amount, users to donate tokens to the fund, block producers to grant them block and epoch reward and, // beneficiaries to claim the balance into their personal account. type Protocol struct { - keyPrefix []byte - addr address.Address - foundationBonusP2StartEpoch uint64 - foundationBonusP2EndEpoch uint64 + keyPrefix []byte + addr address.Address + cfg genesis.Rewarding } // NewProtocol instantiates a rewarding protocol instance. -func NewProtocol( - foundationBonusP2Start uint64, - foundationBonusP2End uint64, -) *Protocol { +func NewProtocol(cfg genesis.Rewarding) *Protocol { h := hash.Hash160b([]byte(protocolID)) addr, err := address.FromBytes(h[:]) if err != nil { log.L().Panic("Error when constructing the address of rewarding protocol", zap.Error(err)) } + if err = validateFoundationBonusExtension(cfg); err != nil { + log.L().Panic("failed to validate foundation bonus extension", zap.Error(err)) + } return &Protocol{ - keyPrefix: h[:], - addr: addr, - foundationBonusP2StartEpoch: foundationBonusP2Start, - foundationBonusP2EndEpoch: foundationBonusP2End, + keyPrefix: h[:], + addr: addr, + cfg: cfg, } } +// verify that foundation bonus extension epochs are in increasing order +func validateFoundationBonusExtension(cfg genesis.Rewarding) error { + end := cfg.FoundationBonusLastEpoch + hasP2Extension := (cfg.FoundationBonusP2StartEpoch > 0 || cfg.FoundationBonusP2EndEpoch > 0) + if hasP2Extension { + if cfg.FoundationBonusP2StartEpoch < end || cfg.FoundationBonusP2EndEpoch < cfg.FoundationBonusP2StartEpoch { + return errInvalidEpoch + } + end = cfg.FoundationBonusP2EndEpoch + } + + for _, v := range cfg.FoundationBonusExtension { + if v.Start < end || v.End < v.Start { + return errInvalidEpoch + } + end = v.End + } + return nil +} + // FindProtocol finds the registered protocol from registry func FindProtocol(registry *protocol.Registry) *Protocol { if registry == nil { @@ -96,6 +115,8 @@ func (p *Protocol) CreatePreStates(ctx context.Context, sm protocol.StateManager return p.SetReward(ctx, sm, g.DardanellesBlockReward(), true) case g.GreenlandBlockHeight: return p.migrateValueGreenland(ctx, sm) + case g.KamchatkaBlockHeight: + return p.setFoundationBonusExtension(ctx, sm) } return nil } @@ -124,6 +145,44 @@ func (p *Protocol) migrateValue(sm protocol.StateManager, key []byte, value inte return p.deleteStateV1(sm, key) } +func (p *Protocol) setFoundationBonusExtension(ctx context.Context, sm protocol.StateManager) error { + a := admin{} + if _, err := p.state(ctx, sm, adminKey, &a); err != nil { + return err + } + + hasP2Extension := (p.cfg.FoundationBonusP2StartEpoch > 0 || p.cfg.FoundationBonusP2EndEpoch > 0) + if !a.hasFoundationBonusExtension() { + if hasP2Extension { + a.foundationBonusExtension = append(a.foundationBonusExtension, + genesis.Period{p.cfg.FoundationBonusP2StartEpoch, p.cfg.FoundationBonusP2EndEpoch}) + } + for _, v := range p.cfg.FoundationBonusExtension { + a.foundationBonusExtension = append(a.foundationBonusExtension, + genesis.Period{v.Start, v.End}) + } + return p.putState(ctx, sm, adminKey, &a) + } + + // add the last/new foundation bonus extension period to admin + adminExtension := len(a.foundationBonusExtension) + configExtension := len(p.cfg.FoundationBonusExtension) + if hasP2Extension { + configExtension++ + } + if configExtension != adminExtension+1 { + return errInvalidEpoch + } + + last := len(p.cfg.FoundationBonusExtension) - 1 + if p.cfg.FoundationBonusExtension[last].Start < a.foundationBonusExtension[adminExtension-1].End { + return errInvalidEpoch + } + a.foundationBonusExtension = append(a.foundationBonusExtension, + genesis.Period{p.cfg.FoundationBonusExtension[last].Start, p.cfg.FoundationBonusExtension[last].End}) + return p.putState(ctx, sm, adminKey, &a) +} + // CreatePostSystemActions creates a list of system actions to be appended to block actions func (p *Protocol) CreatePostSystemActions(ctx context.Context, _ protocol.StateReader) ([]action.Envelope, error) { blkCtx := protocol.MustGetBlockCtx(ctx) diff --git a/action/protocol/rewarding/protocol_test.go b/action/protocol/rewarding/protocol_test.go index fd598a6140..f12686da2f 100644 --- a/action/protocol/rewarding/protocol_test.go +++ b/action/protocol/rewarding/protocol_test.go @@ -35,50 +35,64 @@ import ( "github.com/iotexproject/iotex-core/testutil/testdb" ) +func TestValidateExtension(t *testing.T) { + r := require.New(t) + + g := config.Default.Genesis.Rewarding + r.NoError(validateFoundationBonusExtension(g)) + + last := g.FoundationBonusP2StartEpoch + g.FoundationBonusP2StartEpoch = g.FoundationBonusLastEpoch - 1 + r.Equal(errInvalidEpoch, validateFoundationBonusExtension(g)) + g.FoundationBonusP2StartEpoch = last + + last = g.FoundationBonusP2EndEpoch + g.FoundationBonusP2EndEpoch = g.FoundationBonusP2StartEpoch - 1 + r.Equal(errInvalidEpoch, validateFoundationBonusExtension(g)) + g.FoundationBonusP2EndEpoch = last + + for i, v := range g.FoundationBonusExtension { + g.FoundationBonusExtension[i].Start = last - 1 + r.Equal(errInvalidEpoch, validateFoundationBonusExtension(g)) + g.FoundationBonusExtension[i].Start = v.Start + g.FoundationBonusExtension[i].End = v.Start - 1 + r.Equal(errInvalidEpoch, validateFoundationBonusExtension(g)) + g.FoundationBonusExtension[i].End = v.End + last = v.End + } +} + func testProtocol(t *testing.T, test func(*testing.T, context.Context, protocol.StateManager, *Protocol), withExempt bool) { ctrl := gomock.NewController(t) registry := protocol.NewRegistry() - sm := mock_chainmanager.NewMockStateManager(ctrl) - cb := batch.NewCachedBatch() - - sm.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn( - func(account interface{}, opts ...protocol.StateOption) (uint64, error) { - cfg, err := protocol.CreateStateConfig(opts...) - if err != nil { - return 0, err - } - val, err := cb.Get("state", cfg.Key) - if err != nil { - return 0, state.ErrStateNotExist - } - return 0, state.Deserialize(account, val) - }).AnyTimes() - sm.EXPECT().PutState(gomock.Any(), gomock.Any()).DoAndReturn( - func(account interface{}, opts ...protocol.StateOption) (uint64, error) { - cfg, err := protocol.CreateStateConfig(opts...) - if err != nil { - return 0, err - } - ss, err := state.Serialize(account) - if err != nil { - return 0, err - } - cb.Put("state", cfg.Key, ss, "failed to put state") - return 0, nil - }).AnyTimes() - - sm.EXPECT().Height().Return(uint64(1), nil).AnyTimes() + sm := testdb.NewMockStateManager(ctrl) + g := config.Default.Genesis + // Create a test account with 1000 token + g.InitBalanceMap[identityset.Address(28).String()] = "1000" + g.Rewarding.InitBalanceStr = "0" + g.Rewarding.ExemptAddrStrsFromEpochReward = []string{} + g.Rewarding.BlockRewardStr = "10" + g.Rewarding.EpochRewardStr = "100" + g.Rewarding.NumDelegatesForEpochReward = 4 + g.Rewarding.FoundationBonusStr = "5" + g.Rewarding.NumDelegatesForFoundationBonus = 5 + g.Rewarding.FoundationBonusLastEpoch = 365 + g.Rewarding.ProductivityThreshold = 50 + // Initialize the protocol + if withExempt { + g.Rewarding.ExemptAddrStrsFromEpochReward = []string{ + identityset.Address(31).String(), + } + g.Rewarding.NumDelegatesForEpochReward = 10 + } rp := rolldpos.NewProtocol( genesis.Default.NumCandidateDelegates, genesis.Default.NumDelegates, genesis.Default.NumSubEpochs, ) - p := NewProtocol( - genesis.Default.FoundationBonusP2StartEpoch, - genesis.Default.FoundationBonusP2EndEpoch, - ) + p := NewProtocol(g.Rewarding) candidates := []*state.Candidate{ { Address: identityset.Address(27).String(), @@ -154,32 +168,13 @@ func testProtocol(t *testing.T, test func(*testing.T, context.Context, protocol. require.NoError(t, pp.Register(registry)) require.NoError(t, p.Register(registry)) - ge := config.Default.Genesis - // Create a test account with 1000 token - ge.InitBalanceMap[identityset.Address(28).String()] = "1000" - ge.Rewarding.InitBalanceStr = "0" - ge.Rewarding.ExemptAddrStrsFromEpochReward = []string{} - ge.Rewarding.BlockRewardStr = "10" - ge.Rewarding.EpochRewardStr = "100" - ge.Rewarding.NumDelegatesForEpochReward = 4 - ge.Rewarding.FoundationBonusStr = "5" - ge.Rewarding.NumDelegatesForFoundationBonus = 5 - ge.Rewarding.FoundationBonusLastEpoch = 365 - ge.Rewarding.ProductivityThreshold = 50 - // Initialize the protocol - if withExempt { - ge.Rewarding.ExemptAddrStrsFromEpochReward = []string{ - identityset.Address(31).String(), - } - ge.Rewarding.NumDelegatesForEpochReward = 10 - } ctx := protocol.WithBlockCtx( context.Background(), protocol.BlockCtx{ BlockHeight: 0, }, ) - ctx = genesis.WithGenesisContext(ctx, ge) + ctx = genesis.WithGenesisContext(ctx, g) ctx = protocol.WithFeatureCtx(ctx) ap := account.NewProtocol(DepositGas) require.NoError(t, ap.Register(registry)) @@ -187,28 +182,22 @@ func testProtocol(t *testing.T, test func(*testing.T, context.Context, protocol. require.NoError(t, p.CreateGenesisStates(ctx, sm)) ctx = protocol.WithBlockCtx( - ctx, - protocol.BlockCtx{ + ctx, protocol.BlockCtx{ Producer: identityset.Address(27), BlockHeight: genesis.Default.NumDelegates * genesis.Default.NumSubEpochs, }, ) ctx = protocol.WithActionCtx( - ctx, - protocol.ActionCtx{ + ctx, protocol.ActionCtx{ Caller: identityset.Address(28), }, ) - ctx = genesis.WithGenesisContext( - protocol.WithBlockchainCtx( - protocol.WithRegistry(ctx, registry), - protocol.BlockchainCtx{ - Tip: protocol.TipInfo{ - Height: 20, - }, + ctx = protocol.WithBlockchainCtx( + protocol.WithRegistry(ctx, registry), protocol.BlockchainCtx{ + Tip: protocol.TipInfo{ + Height: 20, }, - ), - ge, + }, ) blockReward, err := p.BlockReward(ctx, sm) require.NoError(t, err) @@ -242,7 +231,7 @@ func testProtocol(t *testing.T, test func(*testing.T, context.Context, protocol. func TestProtocol_Handle(t *testing.T) { ctrl := gomock.NewController(t) - cfg := config.Default + g := config.Default.Genesis registry := protocol.NewRegistry() sm := mock_chainmanager.NewMockStateManager(ctrl) cb := batch.NewCachedBatch() @@ -274,20 +263,31 @@ func TestProtocol_Handle(t *testing.T) { sm.EXPECT().Snapshot().Return(1).AnyTimes() sm.EXPECT().Revert(gomock.Any()).Return(nil).AnyTimes() - cfg.Genesis.NumSubEpochs = 15 + g.Rewarding.InitBalanceStr = "1000000" + g.Rewarding.BlockRewardStr = "10" + g.Rewarding.EpochRewardStr = "100" + g.Rewarding.NumDelegatesForEpochReward = 10 + g.Rewarding.ExemptAddrStrsFromEpochReward = []string{} + g.Rewarding.FoundationBonusStr = "5" + g.Rewarding.NumDelegatesForFoundationBonus = 5 + g.Rewarding.FoundationBonusLastEpoch = 0 + g.Rewarding.ProductivityThreshold = 50 + // Create a test account with 1000000 token + g.InitBalanceMap[identityset.Address(0).String()] = "1000000" + g.NumSubEpochs = 15 rp := rolldpos.NewProtocol( - cfg.Genesis.NumCandidateDelegates, - cfg.Genesis.NumDelegates, - cfg.Genesis.NumSubEpochs, - rolldpos.EnableDardanellesSubEpoch(cfg.Genesis.DardanellesBlockHeight, cfg.Genesis.DardanellesNumSubEpochs), + g.NumCandidateDelegates, + g.NumDelegates, + g.NumSubEpochs, + rolldpos.EnableDardanellesSubEpoch(g.DardanellesBlockHeight, g.DardanellesNumSubEpochs), ) - require.Equal(t, cfg.Genesis.FairbankBlockHeight, rp.GetEpochHeight(cfg.Genesis.FoundationBonusP2StartEpoch)) - require.Equal(t, cfg.Genesis.FoundationBonusP2StartEpoch, rp.GetEpochNum(cfg.Genesis.FairbankBlockHeight)) - require.Equal(t, cfg.Genesis.FoundationBonusP2EndEpoch, cfg.Genesis.FoundationBonusP2StartEpoch+24*365) + require.Equal(t, g.FairbankBlockHeight, rp.GetEpochHeight(g.FoundationBonusP2StartEpoch)) + require.Equal(t, g.FoundationBonusP2StartEpoch, rp.GetEpochNum(g.FairbankBlockHeight)) + require.Equal(t, g.FoundationBonusP2EndEpoch, g.FoundationBonusP2StartEpoch+24*365) require.NoError(t, rp.Register(registry)) - pp := poll.NewLifeLongDelegatesProtocol(cfg.Genesis.Delegates) + pp := poll.NewLifeLongDelegatesProtocol(g.Delegates) require.NoError(t, pp.Register(registry)) - p := NewProtocol(0, 0) + p := NewProtocol(g.Rewarding) require.NoError(t, p.Register(registry)) // Test for ForceRegister require.NoError(t, p.ForceRegister(registry)) @@ -295,18 +295,6 @@ func TestProtocol_Handle(t *testing.T) { // address package also defined protocol address, make sure they match require.Equal(t, p.addr.Bytes(), address.RewardingProtocolAddrHash[:]) - cfg.Genesis.Rewarding.InitBalanceStr = "1000000" - cfg.Genesis.Rewarding.BlockRewardStr = "10" - cfg.Genesis.Rewarding.EpochRewardStr = "100" - cfg.Genesis.Rewarding.NumDelegatesForEpochReward = 10 - cfg.Genesis.Rewarding.ExemptAddrStrsFromEpochReward = []string{} - cfg.Genesis.Rewarding.FoundationBonusStr = "5" - cfg.Genesis.Rewarding.NumDelegatesForFoundationBonus = 5 - cfg.Genesis.Rewarding.FoundationBonusLastEpoch = 0 - cfg.Genesis.Rewarding.ProductivityThreshold = 50 - // Create a test account with 1000000 token - cfg.Genesis.InitBalanceMap[identityset.Address(0).String()] = "1000000" - ctx := protocol.WithBlockCtx( context.Background(), protocol.BlockCtx{ @@ -314,7 +302,7 @@ func TestProtocol_Handle(t *testing.T) { }, ) - ctx = genesis.WithGenesisContext(protocol.WithRegistry(ctx, registry), cfg.Genesis) + ctx = genesis.WithGenesisContext(protocol.WithRegistry(ctx, registry), g) ctx = protocol.WithFeatureCtx(ctx) ap := account.NewProtocol(DepositGas) require.NoError(t, ap.Register(registry)) @@ -470,10 +458,7 @@ func TestStateCheckLegacy(t *testing.T) { ctrl := gomock.NewController(t) sm := testdb.NewMockStateManager(ctrl) - p := NewProtocol( - genesis.Default.FoundationBonusP2StartEpoch, - genesis.Default.FoundationBonusP2EndEpoch, - ) + p := NewProtocol(genesis.Default.Rewarding) chainCtx := genesis.WithGenesisContext( context.Background(), genesis.Genesis{ @@ -543,56 +528,118 @@ func TestStateCheckLegacy(t *testing.T) { } func TestMigrateValue(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - sm := testdb.NewMockStateManager(ctrl) - p := NewProtocol( - genesis.Default.FoundationBonusP2StartEpoch, - genesis.Default.FoundationBonusP2EndEpoch, - ) - // put old - require.NoError(p.putStateV1(sm, adminKey, &admin{ - blockReward: big.NewInt(811), - epochReward: big.NewInt(922), - foundationBonus: big.NewInt(700), - numDelegatesForEpochReward: 118, - })) - require.NoError(p.putStateV1(sm, fundKey, &fund{ - totalBalance: big.NewInt(811), - unclaimedBalance: big.NewInt(922), - })) - require.NoError(p.putStateV1(sm, exemptKey, &exempt{ - addrs: []address.Address{identityset.Address(0)}, - })) - - // migrate - require.NoError(p.migrateValueGreenland(context.Background(), sm)) - - // assert old (not exist) - _, err := p.stateV1(sm, adminKey, &admin{}) - require.Equal(state.ErrStateNotExist, err) - _, err = p.stateV1(sm, fundKey, &fund{}) - require.Equal(state.ErrStateNotExist, err) - _, err = p.stateV1(sm, exemptKey, &exempt{}) - require.Equal(state.ErrStateNotExist, err) - - // assert new (with correct value) - a := admin{} - _, err = p.stateV2(sm, adminKey, &a) - require.NoError(err) - require.Equal(uint64(118), a.numDelegatesForEpochReward) - require.Equal("811", a.blockReward.String()) - - f := fund{} - _, err = p.stateV2(sm, fundKey, &f) - require.NoError(err) - require.Equal("811", f.totalBalance.String()) - - e := exempt{} - _, err = p.stateV2(sm, exemptKey, &e) - require.NoError(err) - require.Equal(identityset.Address(0).String(), e.addrs[0].String()) + r := require.New(t) + + a1 := admin{ + blockReward: big.NewInt(10), + epochReward: big.NewInt(100), + numDelegatesForEpochReward: 10, + foundationBonus: big.NewInt(5), + numDelegatesForFoundationBonus: 5, + foundationBonusLastEpoch: 365, + productivityThreshold: 50, + } + f1 := fund{ + totalBalance: new(big.Int), + unclaimedBalance: new(big.Int), + } + e1 := exempt{ + []address.Address{identityset.Address(31)}, + } + g := genesis.Default + + testProtocol(t, func(t *testing.T, ctx context.Context, sm protocol.StateManager, p *Protocol) { + // verify v1 state + a := admin{} + _, err := p.stateV1(sm, adminKey, &a) + r.NoError(err) + r.Equal(a1, a) + r.False(a.hasFoundationBonusExtension()) + + f := fund{} + _, err = p.stateV1(sm, fundKey, &f) + r.NoError(err) + r.Equal(f1, f) + + e := exempt{} + _, err = p.stateV1(sm, exemptKey, &e) + r.NoError(err) + r.Equal(e1, e) + + for i, v := range []struct { + height uint64 + err error + }{ + {g.GreenlandBlockHeight, nil}, + {g.KamchatkaBlockHeight, nil}, + {g.KamchatkaBlockHeight, errInvalidEpoch}, + {g.KamchatkaBlockHeight, errInvalidEpoch}, + } { + if i == 3 { + // test wrong vale (start < end) + p.cfg.FoundationBonusExtension = append(p.cfg.FoundationBonusExtension, + genesis.Period{18440, 20000}) + } + blkCtx := protocol.MustGetBlockCtx(ctx) + blkCtx.BlockHeight = v.height + fCtx := protocol.WithFeatureCtx(protocol.WithBlockCtx(ctx, blkCtx)) + r.Equal(v.err, p.CreatePreStates(fCtx, sm)) + + // verify v1 is deleted + _, err = p.stateV1(sm, adminKey, &a) + r.Equal(state.ErrStateNotExist, err) + _, err = p.stateV1(sm, fundKey, &f) + r.Equal(state.ErrStateNotExist, err) + _, err = p.stateV1(sm, exemptKey, &e) + r.Equal(state.ErrStateNotExist, err) + + // verify v2 exist + _, err = p.stateV2(sm, adminKey, &a) + r.NoError(err) + _, err = p.stateV2(sm, fundKey, &f) + r.NoError(err) + r.Equal(f1, f) + _, err = p.stateV2(sm, exemptKey, &e) + r.NoError(err) + r.Equal(e1, e) + + switch v.height { + case g.GreenlandBlockHeight: + r.False(a.hasFoundationBonusExtension()) + r.Equal(a1, a) + // test migrate with no data + r.NoError(p.migrateValueGreenland(ctx, sm)) + case g.KamchatkaBlockHeight: + r.True(a.hasFoundationBonusExtension()) + r.Equal(a.foundationBonusExtension[0].Start, g.FoundationBonusP2StartEpoch) + r.Equal(a.foundationBonusExtension[0].End, g.FoundationBonusP2EndEpoch) + r.Equal(a.foundationBonusExtension[1:], g.FoundationBonusExtension) + a.foundationBonusExtension = nil + r.Equal(a1, a) + // test migrate with no data + r.NoError(p.migrateValueGreenland(ctx, sm)) + } + } - // test migrate with no data - require.NoError(p.migrateValueGreenland(context.Background(), sm)) + // verify grant foundation bonus epoch + _, err = p.stateV2(sm, adminKey, &a) + r.NoError(err) + for _, v := range []struct { + epoch uint64 + grant bool + }{ + {365, true}, + {366, false}, + {g.Rewarding.FoundationBonusP2StartEpoch - 1, false}, + {g.Rewarding.FoundationBonusP2StartEpoch, true}, + {g.Rewarding.FoundationBonusP2EndEpoch, true}, + {g.Rewarding.FoundationBonusP2EndEpoch + 1, false}, + {g.Rewarding.FoundationBonusExtension[0].Start - 1, false}, + {g.Rewarding.FoundationBonusExtension[0].Start, true}, + {g.Rewarding.FoundationBonusExtension[0].End, true}, + {g.Rewarding.FoundationBonusExtension[0].End + 1, false}, + } { + r.Equal(v.grant, a.grantFoundationBonus(v.epoch)) + } + }, true) } diff --git a/action/protocol/rewarding/reward.go b/action/protocol/rewarding/reward.go index c7a27184d6..4626e89877 100644 --- a/action/protocol/rewarding/reward.go +++ b/action/protocol/rewarding/reward.go @@ -221,7 +221,13 @@ func (p *Protocol) GrantEpochReward( } // Reward additional bootstrap bonus - if epochNum <= a.foundationBonusLastEpoch || (epochNum >= p.foundationBonusP2StartEpoch && epochNum <= p.foundationBonusP2EndEpoch) { + var grantFB bool + if a.hasFoundationBonusExtension() { + grantFB = a.grantFoundationBonus(epochNum) + } else { + grantFB = epochNum <= a.foundationBonusLastEpoch || (epochNum >= p.cfg.FoundationBonusP2StartEpoch && epochNum <= p.cfg.FoundationBonusP2EndEpoch) + } + if grantFB { for i, count := 0, uint64(0); i < len(candidates) && count < a.numDelegatesForFoundationBonus; i++ { if _, ok := exemptAddrs[candidates[i].Address]; ok { continue diff --git a/action/protocol/rewarding/reward_test.go b/action/protocol/rewarding/reward_test.go index 306e4ff39c..148fcceb00 100644 --- a/action/protocol/rewarding/reward_test.go +++ b/action/protocol/rewarding/reward_test.go @@ -331,7 +331,23 @@ func TestProtocol_NoRewardAddr(t *testing.T) { }).AnyTimes() sm.EXPECT().Height().Return(uint64(1), nil).AnyTimes() - p := NewProtocol(0, 0) + ge := config.Default.Genesis + ge.Rewarding.InitBalanceStr = "0" + ge.Rewarding.BlockRewardStr = "10" + ge.Rewarding.EpochRewardStr = "100" + ge.Rewarding.NumDelegatesForEpochReward = 10 + ge.Rewarding.ExemptAddrStrsFromEpochReward = []string{} + ge.Rewarding.FoundationBonusStr = "5" + ge.Rewarding.NumDelegatesForFoundationBonus = 5 + ge.Rewarding.FoundationBonusLastEpoch = 365 + ge.Rewarding.ProductivityThreshold = 50 + ge.Rewarding.FoundationBonusP2StartEpoch = 365 + ge.Rewarding.FoundationBonusP2EndEpoch = 365 + + // Create a test account with 1000 token + ge.InitBalanceMap[identityset.Address(0).String()] = "1000" + + p := NewProtocol(ge.Rewarding) rp := rolldpos.NewProtocol( genesis.Default.NumCandidateDelegates, genesis.Default.NumDelegates, @@ -385,20 +401,6 @@ func TestProtocol_NoRewardAddr(t *testing.T) { require.NoError(t, pp.Register(registry)) require.NoError(t, p.Register(registry)) - ge := config.Default.Genesis - ge.Rewarding.InitBalanceStr = "0" - ge.Rewarding.BlockRewardStr = "10" - ge.Rewarding.EpochRewardStr = "100" - ge.Rewarding.NumDelegatesForEpochReward = 10 - ge.Rewarding.ExemptAddrStrsFromEpochReward = []string{} - ge.Rewarding.FoundationBonusStr = "5" - ge.Rewarding.NumDelegatesForFoundationBonus = 5 - ge.Rewarding.FoundationBonusLastEpoch = 365 - ge.Rewarding.ProductivityThreshold = 50 - - // Create a test account with 1000 token - ge.InitBalanceMap[identityset.Address(0).String()] = "1000" - // Initialize the protocol ctx := protocol.WithBlockCtx( genesis.WithGenesisContext( diff --git a/action/protocol/rewarding/rewardingpb/rewarding.pb.go b/action/protocol/rewarding/rewardingpb/rewarding.pb.go index b92ac2bc37..ecd3b5c59f 100644 --- a/action/protocol/rewarding/rewardingpb/rewarding.pb.go +++ b/action/protocol/rewarding/rewardingpb/rewarding.pb.go @@ -9,13 +9,14 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.23.0 -// protoc v3.12.4 +// protoc-gen-go v1.25.0 +// protoc v3.14.0 // source: rewarding.proto package rewardingpb import ( + proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -29,6 +30,10 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + type RewardLog_RewardType int32 const ( @@ -83,13 +88,14 @@ type Admin struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BlockReward string `protobuf:"bytes,1,opt,name=blockReward,proto3" json:"blockReward,omitempty"` - EpochReward string `protobuf:"bytes,2,opt,name=epochReward,proto3" json:"epochReward,omitempty"` - NumDelegatesForEpochReward uint64 `protobuf:"varint,3,opt,name=numDelegatesForEpochReward,proto3" json:"numDelegatesForEpochReward,omitempty"` - FoundationBonus string `protobuf:"bytes,4,opt,name=foundationBonus,proto3" json:"foundationBonus,omitempty"` - NumDelegatesForFoundationBonus uint64 `protobuf:"varint,5,opt,name=numDelegatesForFoundationBonus,proto3" json:"numDelegatesForFoundationBonus,omitempty"` - FoundationBonusLastEpoch uint64 `protobuf:"varint,6,opt,name=foundationBonusLastEpoch,proto3" json:"foundationBonusLastEpoch,omitempty"` - ProductivityThreshold uint64 `protobuf:"varint,7,opt,name=productivityThreshold,proto3" json:"productivityThreshold,omitempty"` + BlockReward string `protobuf:"bytes,1,opt,name=blockReward,proto3" json:"blockReward,omitempty"` + EpochReward string `protobuf:"bytes,2,opt,name=epochReward,proto3" json:"epochReward,omitempty"` + NumDelegatesForEpochReward uint64 `protobuf:"varint,3,opt,name=numDelegatesForEpochReward,proto3" json:"numDelegatesForEpochReward,omitempty"` + FoundationBonus string `protobuf:"bytes,4,opt,name=foundationBonus,proto3" json:"foundationBonus,omitempty"` + NumDelegatesForFoundationBonus uint64 `protobuf:"varint,5,opt,name=numDelegatesForFoundationBonus,proto3" json:"numDelegatesForFoundationBonus,omitempty"` + FoundationBonusLastEpoch uint64 `protobuf:"varint,6,opt,name=foundationBonusLastEpoch,proto3" json:"foundationBonusLastEpoch,omitempty"` + ProductivityThreshold uint64 `protobuf:"varint,7,opt,name=productivityThreshold,proto3" json:"productivityThreshold,omitempty"` + FoundationBonusExtension []*Period `protobuf:"bytes,8,rep,name=foundationBonusExtension,proto3" json:"foundationBonusExtension,omitempty"` } func (x *Admin) Reset() { @@ -173,6 +179,13 @@ func (x *Admin) GetProductivityThreshold() uint64 { return 0 } +func (x *Admin) GetFoundationBonusExtension() []*Period { + if x != nil { + return x.FoundationBonusExtension + } + return nil +} + type Fund struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -423,12 +436,67 @@ func (x *RewardLog) GetAmount() string { return "" } +type Period struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Start uint64 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` + End uint64 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *Period) Reset() { + *x = Period{} + if protoimpl.UnsafeEnabled { + mi := &file_rewarding_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Period) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Period) ProtoMessage() {} + +func (x *Period) ProtoReflect() protoreflect.Message { + mi := &file_rewarding_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Period.ProtoReflect.Descriptor instead. +func (*Period) Descriptor() ([]byte, []int) { + return file_rewarding_proto_rawDescGZIP(), []int{6} +} + +func (x *Period) GetStart() uint64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *Period) GetEnd() uint64 { + if x != nil { + return x.End + } + return 0 +} + var File_rewarding_proto protoreflect.FileDescriptor var file_rewarding_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x0b, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x62, 0x22, 0xef, - 0x02, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, + 0x6f, 0x12, 0x0b, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x62, 0x22, 0xc0, + 0x03, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, @@ -451,29 +519,42 @@ var file_rewarding_proto_rawDesc = []byte{ 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, - 0x22, 0x56, 0x0a, 0x04, 0x46, 0x75, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2a, 0x0a, 0x10, - 0x75, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x75, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, - 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x52, 0x65, 0x77, 0x61, - 0x72, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x23, 0x0a, 0x07, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x1e, - 0x0a, 0x06, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x22, 0xb6, - 0x01, 0x0a, 0x09, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x12, 0x35, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x72, 0x65, 0x77, - 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x4c, - 0x6f, 0x67, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0x46, 0x0a, 0x0a, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, - 0x0c, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x10, 0x00, 0x12, - 0x10, 0x0a, 0x0c, 0x45, 0x50, 0x4f, 0x43, 0x48, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x10, - 0x01, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x42, 0x4f, 0x4e, 0x55, 0x53, 0x10, 0x02, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x4f, 0x0a, 0x18, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, + 0x6e, 0x75, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x62, + 0x2e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x52, 0x18, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x6e, 0x75, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0x56, 0x0a, 0x04, 0x46, 0x75, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2a, 0x0a, + 0x10, 0x75, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x75, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, + 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x52, 0x65, 0x77, + 0x61, 0x72, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x23, 0x0a, 0x07, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, + 0x1e, 0x0a, 0x06, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, + 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x22, + 0xb6, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x12, 0x35, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x72, 0x65, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, + 0x4c, 0x6f, 0x67, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x46, 0x0a, 0x0a, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, + 0x0a, 0x0c, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x10, 0x00, + 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x50, 0x4f, 0x43, 0x48, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, + 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x42, 0x4f, 0x4e, 0x55, 0x53, 0x10, 0x02, 0x22, 0x30, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x69, + 0x6f, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x42, 0x4a, 0x5a, 0x48, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x6f, 0x74, 0x65, 0x78, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x69, 0x6f, 0x74, 0x65, 0x78, 0x2d, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2f, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -489,7 +570,7 @@ func file_rewarding_proto_rawDescGZIP() []byte { } var file_rewarding_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_rewarding_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_rewarding_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_rewarding_proto_goTypes = []interface{}{ (RewardLog_RewardType)(0), // 0: rewardingpb.RewardLog.RewardType (*Admin)(nil), // 1: rewardingpb.Admin @@ -498,14 +579,16 @@ var file_rewarding_proto_goTypes = []interface{}{ (*Account)(nil), // 4: rewardingpb.Account (*Exempt)(nil), // 5: rewardingpb.Exempt (*RewardLog)(nil), // 6: rewardingpb.RewardLog + (*Period)(nil), // 7: rewardingpb.Period } var file_rewarding_proto_depIdxs = []int32{ - 0, // 0: rewardingpb.RewardLog.type:type_name -> rewardingpb.RewardLog.RewardType - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 7, // 0: rewardingpb.Admin.foundationBonusExtension:type_name -> rewardingpb.Period + 0, // 1: rewardingpb.RewardLog.type:type_name -> rewardingpb.RewardLog.RewardType + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_rewarding_proto_init() } @@ -586,6 +669,18 @@ func file_rewarding_proto_init() { return nil } } + file_rewarding_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Period); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -593,7 +688,7 @@ func file_rewarding_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rewarding_proto_rawDesc, NumEnums: 1, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/action/protocol/rewarding/rewardingpb/rewarding.proto b/action/protocol/rewarding/rewardingpb/rewarding.proto index 889f2d7065..3b9a695f26 100644 --- a/action/protocol/rewarding/rewardingpb/rewarding.proto +++ b/action/protocol/rewarding/rewardingpb/rewarding.proto @@ -8,6 +8,7 @@ // protoc --go_out=plugins=grpc:. *.proto syntax = "proto3"; package rewardingpb; +option go_package = "github.com/iotexproject/iotex-core/action/protocol/rewarding/rewardingpb"; message Admin { string blockReward = 1; @@ -17,6 +18,7 @@ message Admin { uint64 numDelegatesForFoundationBonus = 5; uint64 foundationBonusLastEpoch = 6; uint64 productivityThreshold = 7; + repeated Period foundationBonusExtension = 8; } message Fund { @@ -45,3 +47,8 @@ message RewardLog { string addr = 2; string amount = 3; } + +message Period { + uint64 start = 1; + uint64 end = 2; +} diff --git a/api/api_test.go b/api/api_test.go index 34194c061a..dd02329cb1 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -2605,7 +2605,7 @@ func setupChain(cfg config.Config) (blockchain.Blockchain, blockdao.BlockDAO, bl genesis.Default.NumSubEpochs, rolldpos.EnableDardanellesSubEpoch(cfg.Genesis.DardanellesBlockHeight, cfg.Genesis.DardanellesNumSubEpochs), ) - r := rewarding.NewProtocol(0, 0) + r := rewarding.NewProtocol(cfg.Genesis.Rewarding) if err := rolldposProtocol.Register(registry); err != nil { return nil, nil, nil, nil, nil, nil, nil, "", err diff --git a/blockchain/genesis/genesis.go b/blockchain/genesis/genesis.go index 5244442498..4cb41f0fbe 100644 --- a/blockchain/genesis/genesis.go +++ b/blockchain/genesis/genesis.go @@ -90,6 +90,9 @@ func defaultConfig() Genesis { FoundationBonusLastEpoch: 8760, FoundationBonusP2StartEpoch: 9698, FoundationBonusP2EndEpoch: 18458, + FoundationBonusExtension: []Period{ + {28440, 37200}, + }, }, Staking: Staking{ VoteWeightCalConsts: VoteWeightCalConsts{ @@ -243,6 +246,11 @@ type ( // VotesStr is the score for the operator to rank and weight for rewardee to split epoch reward VotesStr string `yaml:"votes"` } + // Period consists of the start/end epoch + Period struct { + Start uint64 `yaml:"start"` + End uint64 `yaml:"end"` + } // Rewarding contains the configs for rewarding protocol Rewarding struct { // InitBalanceStr is the initial balance of the rewarding protocol in decimal string format @@ -271,6 +279,8 @@ type ( FoundationBonusP2EndEpoch uint64 `yaml:"foundationBonusP2EndEpoch"` // ProductivityThreshold is the percentage number that a delegate's productivity needs to reach not to get probation ProductivityThreshold uint64 `yaml:"productivityThreshold"` + // FoundationBonusExtension is the epoch number of extension foundation bonus after part 2 + FoundationBonusExtension []Period `yaml:"foundationBonusExtension"` } // Staking contains the configs for staking protocol Staking struct { diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go index 1a6c96b3a3..c3996e6285 100644 --- a/blockchain/integrity/integrity_test.go +++ b/blockchain/integrity/integrity_test.go @@ -551,7 +551,7 @@ func TestCreateBlockchain(t *testing.T) { ) ep := execution.NewProtocol(dao.GetBlockHash, rewarding.DepositGas) require.NoError(ep.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(rewardingProtocol.Register(registry)) require.NoError(bc.Start(ctx)) require.NotNil(bc) @@ -602,7 +602,7 @@ func TestGetBlockHash(t *testing.T) { ) ep := execution.NewProtocol(dao.GetBlockHash, rewarding.DepositGas) require.NoError(ep.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(rewardingProtocol.Register(registry)) require.NoError(bc.Start(ctx)) require.NotNil(bc) @@ -758,7 +758,7 @@ func TestBlockchain_MintNewBlock(t *testing.T) { ) ep := execution.NewProtocol(dao.GetBlockHash, rewarding.DepositGas) require.NoError(t, ep.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(t, rewardingProtocol.Register(registry)) require.NoError(t, bc.Start(ctx)) defer func() { @@ -832,7 +832,7 @@ func TestBlockchain_MintNewBlock_PopAccount(t *testing.T) { require.NoError(t, rp.Register(registry)) ep := execution.NewProtocol(dao.GetBlockHash, rewarding.DepositGas) require.NoError(t, ep.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(t, rewardingProtocol.Register(registry)) require.NoError(t, bc.Start(ctx)) defer func() { @@ -932,7 +932,7 @@ func TestConstantinople(t *testing.T) { ) ep := execution.NewProtocol(dao.GetBlockHash, rewarding.DepositGas) require.NoError(ep.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(rewardingProtocol.Register(registry)) require.NoError(bc.Start(ctx)) defer func() { @@ -1418,7 +1418,7 @@ func TestBlockchainInitialCandidate(t *testing.T) { genesis.Default.NumSubEpochs, ) require.NoError(rolldposProtocol.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(rewardingProtocol.Register(registry)) pollProtocol := poll.NewLifeLongDelegatesProtocol(cfg.Genesis.Delegates) require.NoError(pollProtocol.Register(registry)) diff --git a/chainservice/chainservice.go b/chainservice/chainservice.go index 1ccea1b1dc..3446bacb77 100644 --- a/chainservice/chainservice.go +++ b/chainservice/chainservice.go @@ -326,10 +326,7 @@ func New( } } // TODO: rewarding protocol for standalone mode is weird, rDPoSProtocol could be passed via context - rewardingProtocol := rewarding.NewProtocol( - cfg.Genesis.FoundationBonusP2StartEpoch, - cfg.Genesis.FoundationBonusP2EndEpoch, - ) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) // TODO: explorer dependency deleted at #1085, need to revive by migrating to api consensus, err := consensus.NewConsensus(cfg, chain, sf, copts...) if err != nil { diff --git a/consensus/scheme/rolldpos/roundcalculator_test.go b/consensus/scheme/rolldpos/roundcalculator_test.go index f631c4a186..f61b22446c 100644 --- a/consensus/scheme/rolldpos/roundcalculator_test.go +++ b/consensus/scheme/rolldpos/roundcalculator_test.go @@ -199,7 +199,7 @@ func makeChain(t *testing.T) (blockchain.Blockchain, factory.Factory, actpool.Ac ) require.NoError(rolldposProtocol.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(rewardingProtocol.Register(registry)) acc := account.NewProtocol(rewarding.DepositGas) require.NoError(acc.Register(registry)) diff --git a/e2etest/bigint_test.go b/e2etest/bigint_test.go index b7591f7ea1..8397a3a688 100644 --- a/e2etest/bigint_test.go +++ b/e2etest/bigint_test.go @@ -95,7 +95,7 @@ func prepareBlockchain(ctx context.Context, executor string, r *require.Assertio )), ) r.NotNil(bc) - reward := rewarding.NewProtocol(0, 0) + reward := rewarding.NewProtocol(cfg.Genesis.Rewarding) r.NoError(reward.Register(registry)) ep := execution.NewProtocol(dao.GetBlockHash, rewarding.DepositGas) diff --git a/e2etest/local_test.go b/e2etest/local_test.go index c58701acfb..e5721e7b04 100644 --- a/e2etest/local_test.go +++ b/e2etest/local_test.go @@ -206,7 +206,7 @@ func TestLocalCommit(t *testing.T) { cfg.Genesis.NumSubEpochs, ) require.NoError(rolldposProtocol.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(rewardingProtocol.Register(registry)) acc := account.NewProtocol(rewarding.DepositGas) require.NoError(acc.Register(registry)) diff --git a/gasstation/gasstattion_test.go b/gasstation/gasstattion_test.go index b59a4083a6..f975091894 100644 --- a/gasstation/gasstattion_test.go +++ b/gasstation/gasstattion_test.go @@ -67,7 +67,7 @@ func TestSuggestGasPriceForUserAction(t *testing.T) { ) ep := execution.NewProtocol(blkMemDao.GetBlockHash, rewarding.DepositGas) require.NoError(t, ep.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(t, rewardingProtocol.Register(registry)) require.NoError(t, bc.Start(ctx)) defer func() { @@ -143,7 +143,7 @@ func TestSuggestGasPriceForSystemAction(t *testing.T) { ) ep := execution.NewProtocol(blkMemDao.GetBlockHash, rewarding.DepositGas) require.NoError(t, ep.Register(registry)) - rewardingProtocol := rewarding.NewProtocol(0, 0) + rewardingProtocol := rewarding.NewProtocol(cfg.Genesis.Rewarding) require.NoError(t, rewardingProtocol.Register(registry)) require.NoError(t, bc.Start(ctx)) defer func() {