Skip to content

Commit

Permalink
Merge pull request #207 from icon-project/goldworm-imp-set-bond-requi…
Browse files Browse the repository at this point in the history
…rement-rate

Implement setBondRequirementRate API in ChainScore
  • Loading branch information
goldworm-icon committed Jul 24, 2024
2 parents 93612e4 + d0dee81 commit 1ff4c7b
Show file tree
Hide file tree
Showing 17 changed files with 442 additions and 82 deletions.
147 changes: 106 additions & 41 deletions doc/icon_chainscore_api.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions icon/chainscore.go
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,14 @@ var chainMethods = []*chainMethod{
scoreapi.Dict,
},
}, icmodule.RevisionIISS4R0, 0},
{scoreapi.Method{
scoreapi.Function, "setBondRequirementRate",
scoreapi.FlagExternal, 1,
[]scoreapi.Parameter{
{"rate", scoreapi.Integer, nil, nil},
},
nil,
}, icmodule.RevisionSetBondRequirementRate, 0},
}

func applyStepLimits(fee *FeeConfig, as state.AccountState) error {
Expand Down
11 changes: 11 additions & 0 deletions icon/chainscore_iiss.go
Original file line number Diff line number Diff line change
Expand Up @@ -1048,3 +1048,14 @@ func (s *chainScore) Ex_getPRepCountConfig() (map[string]interface{}, error) {
}
return es.GetPRepCountConfig()
}

func (s *chainScore) Ex_setBondRequirementRate(rate int64) error {
if err := s.checkGovernance(true); err != nil {
return err
}
es, err := s.getExtensionState()
if err != nil {
return err
}
return es.SetBondRequirementRate(s.newCallContext(s.cc), icmodule.Rate(rate))
}
3 changes: 3 additions & 0 deletions icon/icmodule/revision.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const (
Revision25
Revision26
Revision27
Revision28
RevisionReserved
)

Expand Down Expand Up @@ -126,6 +127,8 @@ const (
RevisionFixIssueRegulator = Revision26

RevisionRecoverUnderIssuance = Revision27

RevisionSetBondRequirementRate = Revision28
)

var revisionFlags []module.Revision
Expand Down
14 changes: 10 additions & 4 deletions icon/icsim/revision.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var revHandlerTable = []revHandlerItem{
//{icmodule.RevisionBlockAccounts2, onRevBlockAccounts2},
{icmodule.RevisionIISS4R0, onRevIISS4R0},
{icmodule.RevisionIISS4R1, onRevIISS4R1},
{icmodule.RevisionSetBondRequirementRate, onRevSetBondRequirementRate},
}

// DO NOT update revHandlerMap manually
Expand Down Expand Up @@ -99,7 +100,7 @@ func onRevIISS(sim *simulatorImpl, wc WorldContext, rev, _ int) error {
if err := es.State.SetSubPRepCount(cfg.SubPRepCount); err != nil {
return err
}
if err := es.State.SetBondRequirement(cfg.BondRequirement); err != nil {
if err := es.State.SetBondRequirement(rev, cfg.BondRequirement); err != nil {
return err
}
if err := es.State.SetLockVariables(big.NewInt(cfg.LockMinMultiplier), big.NewInt(cfg.LockMaxMultiplier)); err != nil {
Expand Down Expand Up @@ -194,7 +195,7 @@ func onRevIISS2(_ *simulatorImpl, wc WorldContext, _, _ int) error {
return nil
}

func onRevICON2R1(_ *simulatorImpl, wc WorldContext, _, _ int) error {
func onRevICON2R1(_ *simulatorImpl, wc WorldContext, rev, _ int) error {
es := getExtensionState(wc)
as := wc.GetAccountState(state.SystemID)

Expand All @@ -209,8 +210,8 @@ func onRevICON2R1(_ *simulatorImpl, wc WorldContext, _, _ int) error {
if err := scoredb.NewVarDB(as, state.VarNextBlockVersion).Set(module.BlockVersion2); err != nil {
return err
}
if es.State.GetBondRequirement() == icmodule.ToRate(icmodule.IISS2BondRequirement) {
if err := es.State.SetBondRequirement(icmodule.ToRate(icmodule.DefaultBondRequirement)); err != nil {
if es.State.GetBondRequirement(rev) == icmodule.ToRate(icmodule.IISS2BondRequirement) {
if err := es.State.SetBondRequirement(rev, icmodule.ToRate(icmodule.DefaultBondRequirement)); err != nil {
return err
}
}
Expand Down Expand Up @@ -267,3 +268,8 @@ func onRevIISS4R1(_ *simulatorImpl, wc WorldContext, _, _ int) error {
es := getExtensionState(wc)
return es.State.SetIISSVersion(icstate.IISSVersion4)
}

func onRevSetBondRequirementRate(sim *simulatorImpl, wc WorldContext, rev, _ int) error {
es := getExtensionState(wc)
return es.State.MigrateBondRequirement(rev)
}
147 changes: 147 additions & 0 deletions icon/icsim/setbondrequirementrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright 2024 ICON Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package icsim

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/icon-project/goloop/icon/icmodule"
"github.com/icon-project/goloop/icon/iiss"
"github.com/icon-project/goloop/module"
"github.com/icon-project/goloop/service/state"
"github.com/icon-project/goloop/service/txresult"
)

func assertBondRequirement(t *testing.T, sim Simulator, br, nextBr icmodule.Rate) {
term := sim.GetPRepTermInJSON()
networkInfo := sim.GetNetworkInfoInJSON()

if term["revision"].(int) < icmodule.RevisionSetBondRequirementRate {
assert.Equal(t, br.Percent(), term["bondRequirement"])
assert.NotContains(t, term, "bondRequirementRate")
} else {
assert.NotContains(t, term, "bondRequirement")
assert.Equal(t, br.NumInt64(), term["bondRequirementRate"])
}

if sim.Revision().Value() < icmodule.RevisionSetBondRequirementRate {
assert.Equal(t, nextBr.Percent(), networkInfo["bondRequirement"])
assert.NotContains(t, networkInfo, "bondRequirementRate")
} else {
assert.NotContains(t, networkInfo, "bondRequirement")
assert.Equal(t, nextBr.NumInt64(), networkInfo["bondRequirementRate"])
}
}

func assertEventSetBondRequirementRate(t *testing.T, ev *txresult.TestEventLog, rate icmodule.Rate) {
err := ev.Assert(state.SystemAddress, iiss.EventBondRequirementRateSet, nil, []any{rate.NumInt64()})
assert.NoError(t, err)
}

func TestSimulatorImpl_SetBondRequirementRate(t *testing.T) {
const (
termPeriod = int64(10)
)
var err error
var csi module.ConsensusInfo
var receipts []Receipt
var nextBr icmodule.Rate
br := icmodule.ToRate(5)
rev := icmodule.ValueToRevision(icmodule.RevisionSetBondRequirementRate - 1)

cfg := NewSimConfigWithParams(map[SimConfigOption]interface{}{
SCOTermPeriod: termPeriod,
SCOBondRequirement: br,
})
env, err := NewEnv(cfg, rev)
sim := env.Simulator()
assert.NoError(t, err)
assert.NotNil(t, sim)
assert.Equal(t, sim.Revision().Value(), rev.Value())

// T(0)
assertBondRequirement(t, sim, br, br)
assert.NoError(t, sim.Go(csi, 1))
assertBondRequirement(t, sim, br, br)
assert.NoError(t, sim.GoToTermEnd(csi))

// T(1)
// SetBondRequirementRate is forbidden before icmodule.RevisionSetBondRequirementRate
receipts, err = sim.GoBySetBondRequirementRate(csi, env.Governance(), icmodule.ToRate(3))
assert.NoError(t, err)
assert.Equal(t, Failure, receipts[1].Status())
assertBondRequirement(t, sim, br, br)

// Revision update to RevisionSetBondRequirementRate
rev = icmodule.ValueToRevision(icmodule.RevisionSetBondRequirementRate)
receipts, err = sim.GoBySetRevision(csi, env.Governance(), rev)
assert.NoError(t, err)
assert.Equal(t, 2, len(receipts))
assert.Equal(t, Success, receipts[1].Status())
assert.Equal(t, sim.Revision().Value(), rev.Value())

// GetBondRequirementRate() works after RevisionSetBondRequirementRate
assertBondRequirement(t, sim, br, br)

// Ensure that calling setBondRequirementRate() more than once during the same term works well
for i := 0; i < 2; i++ {
nextBr = icmodule.ToRate(int64(i))
receipts, err = sim.GoBySetBondRequirementRate(csi, env.Governance(), nextBr)
rcpt := receipts[1]
assert.NoError(t, err)
assert.Equal(t, 2, len(receipts))
assert.Equal(t, Success, rcpt.Status())
assert.Equal(t, 1, len(rcpt.Events()))
assertEventSetBondRequirementRate(t, rcpt.Events()[0], nextBr)
assertBondRequirement(t, sim, br, nextBr)
}

// When calling setBondRequirementRate() with the new rate that is the same as the existing one,
// the transaction succeeds without any event logs
receipts, err = sim.GoBySetBondRequirementRate(csi, env.Governance(), nextBr)
assert.NoError(t, err)
assert.Equal(t, 2, len(receipts))
rcpt := receipts[1]
assert.Equal(t, Success, rcpt.Status())
assert.Zero(t, len(rcpt.Events()))
assertBondRequirement(t, sim, br, nextBr)

assert.NoError(t, sim.GoToTermEnd(csi))

// T(2)
assertBondRequirement(t, sim, nextBr, nextBr)
assert.NoError(t, sim.Go(csi, 1))
assertBondRequirement(t, sim, nextBr, nextBr)
assert.NoError(t, sim.GoToTermEnd(csi))

// T(3)
assertBondRequirement(t, sim, nextBr, nextBr)
assert.NoError(t, sim.Go(csi, 1))
assertBondRequirement(t, sim, nextBr, nextBr)

// Ensure that only valid BondRequirementRates are allowed
for _, rate := range []icmodule.Rate{-1, icmodule.ToRate(101)} {
receipts, err = sim.GoBySetBondRequirementRate(csi, env.Governance(), rate)
assert.NoError(t, err)
assert.Equal(t, 2, len(receipts))
assert.Equal(t, Failure, receipts[1].Status())
assert.Zero(t, len(receipts[1].Events()))
assertBondRequirement(t, sim, nextBr, nextBr)
}
}
6 changes: 6 additions & 0 deletions icon/icsim/simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
)

type TxType int

const (
TypeTransfer TxType = iota
TypeSetStake
Expand All @@ -46,6 +47,7 @@ const (
TypeHandleDoubleSignReport
TypeSetPRepCountConfig
TypeSetRewardFundAllocation2
TypeSetBondRequirementRate
)

type Transaction interface {
Expand Down Expand Up @@ -182,4 +184,8 @@ type Simulator interface {
SetRewardFundAllocation2(from module.Address, values map[icstate.RFundKey]icmodule.Rate) Transaction
GoBySetRewardFundAllocation2(
csi module.ConsensusInfo, from module.Address, values map[icstate.RFundKey]icmodule.Rate) ([]Receipt, error)

// After RevisionSetBondRequirementRate
SetBondRequirementRate(from module.Address, rate icmodule.Rate) Transaction
GoBySetBondRequirementRate(csi module.ConsensusInfo, from module.Address, rate icmodule.Rate) ([]Receipt, error)
}
17 changes: 17 additions & 0 deletions icon/icsim/simulatorimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ func (sim *simulatorImpl) executeTx(cc *callContext, tx Transaction) error {
err = sim.setPRepCountConfig(es, cc, tx)
case TypeSetRewardFundAllocation2:
err = sim.setRewardFundAllocation2(es, cc, tx)
case TypeSetBondRequirementRate:
err = sim.setBondRequirementRate(es, cc, tx)
default:
return errors.Errorf("Unexpected transaction: %v", tx.Type())
}
Expand Down Expand Up @@ -877,6 +879,21 @@ func (sim *simulatorImpl) setRewardFundAllocation2(
return es.State.SetRewardFund(rf)
}

func (sim *simulatorImpl) SetBondRequirementRate(from module.Address, rate icmodule.Rate) Transaction {
return NewTransaction(TypeSetBondRequirementRate, from, rate)
}

func (sim *simulatorImpl) GoBySetBondRequirementRate(
csi module.ConsensusInfo, from module.Address, rate icmodule.Rate) ([]Receipt, error) {
return sim.goByOneTransaction(csi, TypeSetBondRequirementRate, from, rate)
}

func (sim *simulatorImpl) setBondRequirementRate(es *iiss.ExtensionStateImpl, cc *callContext, tx Transaction) error {
args := tx.Args()
rate := args[0].(icmodule.Rate)
return es.SetBondRequirementRate(cc, rate)
}

func NewSimulator(
revision module.Revision, initValidators []module.Validator, initBalances map[string]*big.Int, config *SimConfig,
) (Simulator, error) {
Expand Down
8 changes: 8 additions & 0 deletions icon/iiss/eventlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const (
EventRewardFundSet = "RewardFundSet(int)"
EventRewardFundAllocationSet = "RewardFundAllocationSet(str,int)"
EventNetworkScoreSet = "NetworkScoreSet(str,Address)"
EventBondRequirementRateSet = "BondRequirementRateSet(int)"
)

func EmitSlashingRateSetEvent(cc icmodule.CallContext, penaltyType icmodule.PenaltyType, rate icmodule.Rate) {
Expand Down Expand Up @@ -393,3 +394,10 @@ func EmitNetworkScoreSetEvent(cc icmodule.CallContext, role string, address modu
[][]byte{[]byte(role), addrBytes},
)
}

func EmitBondRequirementRateSetEvent(cc icmodule.CallContext, rate icmodule.Rate) {
cc.OnEvent(state.SystemAddress,
[][]byte{[]byte(EventBondRequirementRateSet)},
[][]byte{intconv.Int64ToBytes(rate.NumInt64())},
)
}
19 changes: 19 additions & 0 deletions icon/iiss/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -2073,3 +2073,22 @@ func (es *ExtensionStateImpl) SetMinimumBond(cc icmodule.CallContext, nBond *big
EmitMinimumBondSetEvent(cc, nBond)
return nil
}

func (es *ExtensionStateImpl) SetBondRequirementRate(cc icmodule.CallContext, rate icmodule.Rate) error {
revision := cc.Revision().Value()
if revision < icmodule.RevisionSetBondRequirementRate {
return scoreresult.AccessDeniedError.Errorf("SetBondRequirementRateNotAllowed(rev=%d)", revision)
}
if !rate.IsValid() {
return scoreresult.InvalidParameterError.Errorf("InvalidBondRequirementRate(%d)", rate)
}

var err error
oldRate := es.State.GetBondRequirement(revision)
if rate != oldRate {
if err = es.State.SetBondRequirement(revision, rate); err == nil {
EmitBondRequirementRateSetEvent(cc, rate)
}
}
return err
}
34 changes: 28 additions & 6 deletions icon/iiss/icstate/networkvalue.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,16 +242,34 @@ func (s *State) SetTotalStake(value *big.Int) error {
return setValue(s.store, VarTotalStake, value)
}

func (s *State) GetBondRequirement() icmodule.Rate {
func (s *State) GetBondRequirement(revision int) icmodule.Rate {
v := getValue(s.store, VarBondRequirement).Int64()
return icmodule.ToRate(v)
if revision < icmodule.RevisionSetBondRequirementRate {
return icmodule.ToRate(v)
} else {
return icmodule.Rate(v)
}
}

func (s *State) SetBondRequirement(br icmodule.Rate) error {
func (s *State) SetBondRequirement(revision int, br icmodule.Rate) error {
if !br.IsValid() {
return errors.IllegalArgumentError.New("Bond Requirement should range from 0% to 100%")
}
return setValue(s.store, VarBondRequirement, br.Percent())

if revision < icmodule.RevisionSetBondRequirementRate {
return setValue(s.store, VarBondRequirement, br.Percent())
} else {
return setValue(s.store, VarBondRequirement, br.NumInt64())
}
}

func (s *State) MigrateBondRequirement(revision int) error {
if revision != icmodule.RevisionSetBondRequirementRate {
return scoreresult.AccessDeniedError.Errorf("MigrateBondRequirementNotAllowed(rev=%d)", revision)
}
// Change BondRequirementRate unit from 1% to 0.01%
rate := s.GetBondRequirement(revision - 1)
return s.SetBondRequirement(revision, rate)
}

func (s *State) SetUnbondingPeriodMultiplier(value int64) error {
Expand Down Expand Up @@ -482,15 +500,19 @@ func (s *State) SetMinimumBond(bond *big.Int) error {
}

func (s *State) GetNetworkInfoInJSON(revision int) (map[string]interface{}, error) {
br := s.GetBondRequirement()
br := s.GetBondRequirement(revision)
jso := make(map[string]interface{})
jso["mainPRepCount"] = s.GetMainPRepCount()
jso["extraMainPRepCount"] = s.GetExtraMainPRepCount()
jso["subPRepCount"] = s.GetSubPRepCount()
jso["totalStake"] = s.GetTotalStake()
jso["iissVersion"] = int64(s.GetIISSVersion())
jso["termPeriod"] = s.GetTermPeriod()
jso["bondRequirement"] = br.Percent()
if revision < icmodule.RevisionSetBondRequirementRate {
jso["bondRequirement"] = br.Percent()
} else {
jso["bondRequirementRate"] = br.NumInt64()
}
jso["lockMinMultiplier"] = s.GetLockMinMultiplier()
jso["lockMaxMultiplier"] = s.GetLockMaxMultiplier()
jso["rewardFund"] = s.GetRewardFund(revision).ToJSON()
Expand Down
Loading

0 comments on commit 1ff4c7b

Please sign in to comment.