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

Add a feature to the DPOS contract to toggle migration mode #1693

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
84 changes: 84 additions & 0 deletions builtin/plugins/dposv3/dpos.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ var (
errDistributionNotFound = errors.New("Distribution record not found.")
errOnlyOracle = errors.New("Function can only be called with oracle address.")
errDelegationLocked = errors.New("Delegation currently locked.")
errMigrationModeEnabled = errors.New("Migration mode enabled.")
)

type (
Expand Down Expand Up @@ -129,6 +130,7 @@ type (
SetMaxDowntimePercentageRequest = dtypes.SetMaxDowntimePercentageRequest
EnableValidatorJailingRequest = dtypes.EnableValidatorJailingRequest
IgnoreUnbondLocktimeRequest = dtypes.IgnoreUnbondLocktimeRequest
ToggleMigrationModeRequest = dtypes.ToggleMigrationModeRequest
Candidate = dtypes.Candidate
CandidateStatistic = dtypes.CandidateStatistic
Delegation = dtypes.Delegation
Expand Down Expand Up @@ -258,6 +260,16 @@ func (c *DPOS) Delegate(ctx contract.Context, req *DelegateRequest) error {
delegator := ctx.Message().Sender
ctx.Logger().Info("DPOSv3 Delegate", "delegator", delegator, "request", req)

if ctx.FeatureEnabled(features.DPOSVersion3_11, false) {
state, err := LoadState(ctx)
if err != nil {
return err
}
if state.Params.MigrationModeEnabled {
return errMigrationModeEnabled
}
}

if req.ValidatorAddress == nil {
return logDposError(ctx, errors.New("Delegate called with req.ValidatorAddress == nil"), req.String())
}
Expand Down Expand Up @@ -341,6 +353,16 @@ func (c *DPOS) Delegate(ctx contract.Context, req *DelegateRequest) error {
}

func (c *DPOS) Redelegate(ctx contract.Context, req *RedelegateRequest) error {
if ctx.FeatureEnabled(features.DPOSVersion3_11, false) {
state, err := LoadState(ctx)
if err != nil {
return err
}
if state.Params.MigrationModeEnabled {
return errMigrationModeEnabled
}
}

delegator := ctx.Message().Sender

if req.ValidatorAddress == nil {
Expand Down Expand Up @@ -480,6 +502,16 @@ func (c *DPOS) ConsolidateDelegations(ctx contract.Context, req *ConsolidateDele
delegator := ctx.Message().Sender
ctx.Logger().Info("DPOSv3 ConsolidateDelegations", "delegator", delegator, "request", req)

if ctx.FeatureEnabled(features.DPOSVersion3_11, false) {
state, err := LoadState(ctx)
if err != nil {
return err
}
if state.Params.MigrationModeEnabled {
return errMigrationModeEnabled
}
}

// Unless considation is for the limbo validator, check that the new
// validator address corresponds to one of the registered candidates
if loom.UnmarshalAddressPB(req.ValidatorAddress).Compare(LimboValidatorAddress(ctx)) != 0 {
Expand Down Expand Up @@ -591,6 +623,16 @@ func (c *DPOS) CheckRewardsFromAllValidators(ctx contract.StaticContext, req *Ch
/// a delegator has delegated to, and returns the total amount which will be transferred to the
/// delegator's account after the next election.
func (c *DPOS) ClaimRewardsFromAllValidators(ctx contract.Context, req *ClaimDelegatorRewardsRequest) (*ClaimDelegatorRewardsResponse, error) {
if ctx.FeatureEnabled(features.DPOSVersion3_11, false) {
state, err := LoadState(ctx)
if err != nil {
return nil, err
}
if state.Params.MigrationModeEnabled {
return nil, errMigrationModeEnabled
}
}

if ctx.FeatureEnabled(features.DPOSVersion3_6, false) {
return c.claimRewardsFromAllValidators2(ctx, req)
}
Expand Down Expand Up @@ -717,6 +759,16 @@ func (c *DPOS) Unbond(ctx contract.Context, req *UnbondRequest) error {
delegator := ctx.Message().Sender
ctx.Logger().Info("DPOSv3 Unbond", "delegator", delegator, "request", req)

if ctx.FeatureEnabled(features.DPOSVersion3_11, false) {
state, err := LoadState(ctx)
if err != nil {
return err
}
if state.Params.MigrationModeEnabled {
return errMigrationModeEnabled
}
}

if req.ValidatorAddress == nil {
return logDposError(ctx, errors.New("Unbond called with req.ValidatorAddress == nil"), req.String())
} else if req.Amount == nil {
Expand Down Expand Up @@ -1027,6 +1079,10 @@ func (c *DPOS) RegisterCandidate(ctx contract.Context, req *RegisterCandidateReq
return err
}

if ctx.FeatureEnabled(features.DPOSVersion3_11, false) && state.Params.MigrationModeEnabled {
return errMigrationModeEnabled
}

if (statistic == nil || common.IsZero(statistic.WhitelistAmount.Value)) && common.IsPositive(state.Params.RegistrationRequirement.Value) {
// A currently unregistered candidate must make a loom token deposit
// = 'registrationRequirement' in order to run for validator.
Expand Down Expand Up @@ -1195,6 +1251,16 @@ func (c *DPOS) UpdateCandidateInfo(ctx contract.Context, req *UpdateCandidateInf
// Leaving the validator set mid-election period results in a loss of rewards
// but it should not result in slashing due to downtime.
func (c *DPOS) UnregisterCandidate(ctx contract.Context, req *UnregisterCandidateRequest) error {
if ctx.FeatureEnabled(features.DPOSVersion3_11, false) {
state, err := LoadState(ctx)
if err != nil {
return err
}
if state.Params.MigrationModeEnabled {
return errMigrationModeEnabled
}
}

candidateAddress := ctx.Message().Sender

// Allow oracle to specify the candidate
Expand Down Expand Up @@ -1621,6 +1687,24 @@ func (c *DPOS) IgnoreUnbondLocktime(ctx contract.Context, req *IgnoreUnbondLockt
return saveState(ctx, state)
}

func (c *DPOS) ToggleMigrationMode(ctx contract.Context, req *ToggleMigrationModeRequest) error {
if !ctx.FeatureEnabled(features.DPOSVersion3_11, false) {
return errors.New("DPOS v3.11 is not enabled")
}

state, err := LoadState(ctx)
if err != nil {
return err
}
sender := ctx.Message().Sender
if state.Params.OracleAddress == nil || sender.Compare(loom.UnmarshalAddressPB(state.Params.OracleAddress)) != 0 {
return errOnlyOracle
}

state.Params.MigrationModeEnabled = !state.Params.MigrationModeEnabled
return saveState(ctx, state)
}

// ***************************
// REWARDS & SLASHING
// ***************************
Expand Down
23 changes: 23 additions & 0 deletions cmd/loom/dposV3_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,28 @@ func IgnoreUnbondLocktimeCmd() *cobra.Command {
return cmd
}

const toggleMigrationModeCmdExample = `
loom dpos3 toggle-migration-mode -k path/to/private_key
`

func ToggleMigrationModeCmd() *cobra.Command {
var flags cli.ContractCallFlags
cmd := &cobra.Command{
Use: "toggle-migration-mode",
Example: toggleMigrationModeCmdExample,
RunE: func(cmd *cobra.Command, args []string) error {
if err := cli.CallContractWithFlags(
&flags, DPOSV3ContractName, "ToggleMigrationMode", &dposv3.ToggleMigrationModeRequest{}, nil,
); err != nil {
return err
}
return nil
},
}
cli.AddContractCallFlags(cmd.Flags(), &flags)
return cmd
}

func NewDPOSV3Command() *cobra.Command {
cmd := &cobra.Command{
Use: "dpos3 <command>",
Expand Down Expand Up @@ -1684,6 +1706,7 @@ func NewDPOSV3Command() *cobra.Command {
UnjailValidatorCmdV3(),
EnableValidatorJailingCmd(),
IgnoreUnbondLocktimeCmd(),
ToggleMigrationModeCmd(),
)
return cmd
}
3 changes: 3 additions & 0 deletions features/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ const (
DPOSVersion3_9 = "dpos:v3.9"
// Makes it possible for the oracle to call Redelegate & UnregisterCandidate
DPOSVersion3_10 = "dpos:v3.10"
// Make it possible to put the DPOS contract into migration mode, where
// delegation/redelegation/consolidation and validator registration is disabled
DPOSVersion3_11 = "dpos:v3.11"

// Enables rewards to be distributed even when a delegator owns less than 0.01% of the validator's stake
// Also makes whitelists give bonuses correctly if whitelist locktime tier is set to be 0-3 (else defaults to 5%)
Expand Down