github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/rewarding/admin.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package rewarding
     7  
     8  import (
     9  	"context"
    10  	"math/big"
    11  
    12  	"github.com/pkg/errors"
    13  	"google.golang.org/protobuf/proto"
    14  
    15  	"github.com/iotexproject/iotex-address/address"
    16  	"github.com/iotexproject/iotex-core/action/protocol"
    17  	"github.com/iotexproject/iotex-core/action/protocol/rewarding/rewardingpb"
    18  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    19  )
    20  
    21  // admin stores the admin data of the rewarding protocol
    22  type admin struct {
    23  	blockReward                    *big.Int
    24  	epochReward                    *big.Int
    25  	numDelegatesForEpochReward     uint64
    26  	foundationBonus                *big.Int
    27  	numDelegatesForFoundationBonus uint64
    28  	foundationBonusLastEpoch       uint64
    29  	productivityThreshold          uint64
    30  }
    31  
    32  // Serialize serializes admin state into bytes
    33  func (a admin) Serialize() ([]byte, error) {
    34  	gen := rewardingpb.Admin{
    35  		BlockReward:                    a.blockReward.String(),
    36  		EpochReward:                    a.epochReward.String(),
    37  		NumDelegatesForEpochReward:     a.numDelegatesForEpochReward,
    38  		FoundationBonus:                a.foundationBonus.String(),
    39  		NumDelegatesForFoundationBonus: a.numDelegatesForFoundationBonus,
    40  		FoundationBonusLastEpoch:       a.foundationBonusLastEpoch,
    41  		ProductivityThreshold:          a.productivityThreshold,
    42  	}
    43  	return proto.Marshal(&gen)
    44  }
    45  
    46  // Deserialize deserializes bytes into admin state
    47  func (a *admin) Deserialize(data []byte) error {
    48  	gen := rewardingpb.Admin{}
    49  	if err := proto.Unmarshal(data, &gen); err != nil {
    50  		return err
    51  	}
    52  	blockReward, ok := new(big.Int).SetString(gen.BlockReward, 10)
    53  	if !ok {
    54  		return errors.New("failed to set block reward")
    55  	}
    56  	epochReward, ok := new(big.Int).SetString(gen.EpochReward, 10)
    57  	if !ok {
    58  		return errors.New("failed to set epoch reward")
    59  	}
    60  	foundationBonus, ok := new(big.Int).SetString(gen.FoundationBonus, 10)
    61  	if !ok {
    62  		return errors.New("failed to set bootstrap bonus")
    63  	}
    64  	a.blockReward = blockReward
    65  	a.epochReward = epochReward
    66  	a.numDelegatesForEpochReward = gen.NumDelegatesForEpochReward
    67  	a.foundationBonus = foundationBonus
    68  	a.numDelegatesForFoundationBonus = gen.NumDelegatesForFoundationBonus
    69  	a.foundationBonusLastEpoch = gen.FoundationBonusLastEpoch
    70  	a.productivityThreshold = gen.ProductivityThreshold
    71  	return nil
    72  }
    73  
    74  func (a *admin) grantFoundationBonus(epoch uint64) bool {
    75  	return epoch <= a.foundationBonusLastEpoch
    76  }
    77  
    78  // exempt stores the addresses that exempt from epoch reward
    79  type exempt struct {
    80  	addrs []address.Address
    81  }
    82  
    83  // Serialize serializes exempt state into bytes
    84  func (e *exempt) Serialize() ([]byte, error) {
    85  	epb := rewardingpb.Exempt{}
    86  	for _, addr := range e.addrs {
    87  		epb.Addrs = append(epb.Addrs, addr.Bytes())
    88  	}
    89  	return proto.Marshal(&epb)
    90  }
    91  
    92  // Deserialize deserializes bytes into exempt state
    93  func (e *exempt) Deserialize(data []byte) error {
    94  	epb := rewardingpb.Exempt{}
    95  	if err := proto.Unmarshal(data, &epb); err != nil {
    96  		return err
    97  	}
    98  	e.addrs = nil
    99  	for _, addrBytes := range epb.Addrs {
   100  		addr, err := address.FromBytes(addrBytes)
   101  		if err != nil {
   102  			return err
   103  		}
   104  		e.addrs = append(e.addrs, addr)
   105  	}
   106  	return nil
   107  }
   108  
   109  // CreateGenesisStates initializes the rewarding protocol by setting the original admin, block and epoch reward
   110  func (p *Protocol) CreateGenesisStates(
   111  	ctx context.Context,
   112  	sm protocol.StateManager,
   113  ) error {
   114  	blkCtx := protocol.MustGetBlockCtx(ctx)
   115  	g := genesis.MustExtractGenesisContext(ctx)
   116  	if err := p.assertZeroBlockHeight(blkCtx.BlockHeight); err != nil {
   117  		return err
   118  	}
   119  
   120  	blockReward := g.BlockReward()
   121  	if err := p.assertAmount(blockReward); err != nil {
   122  		return err
   123  	}
   124  
   125  	epochReward := g.EpochReward()
   126  	if err := p.assertAmount(epochReward); err != nil {
   127  		return err
   128  	}
   129  
   130  	if err := p.putState(
   131  		ctx,
   132  		sm,
   133  		_adminKey,
   134  		&admin{
   135  			blockReward:                    blockReward,
   136  			epochReward:                    epochReward,
   137  			numDelegatesForEpochReward:     g.NumDelegatesForEpochReward,
   138  			foundationBonus:                g.FoundationBonus(),
   139  			numDelegatesForFoundationBonus: g.NumDelegatesForFoundationBonus,
   140  			foundationBonusLastEpoch:       g.FoundationBonusLastEpoch,
   141  			productivityThreshold:          g.ProductivityThreshold,
   142  		},
   143  	); err != nil {
   144  		return err
   145  	}
   146  
   147  	initBalance := g.InitBalance()
   148  	if err := p.putState(
   149  		ctx,
   150  		sm,
   151  		_fundKey,
   152  		&fund{
   153  			totalBalance:     initBalance,
   154  			unclaimedBalance: initBalance,
   155  		},
   156  	); err != nil {
   157  		return err
   158  	}
   159  	return p.putState(
   160  		ctx,
   161  		sm,
   162  		_exemptKey,
   163  		&exempt{
   164  			addrs: g.ExemptAddrsFromEpochReward(),
   165  		},
   166  	)
   167  }
   168  
   169  // BlockReward returns the block reward amount
   170  func (p *Protocol) BlockReward(
   171  	ctx context.Context,
   172  	sm protocol.StateReader,
   173  ) (*big.Int, error) {
   174  	a := admin{}
   175  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   176  		return nil, err
   177  	}
   178  	return a.blockReward, nil
   179  }
   180  
   181  // EpochReward returns the epoch reward amount
   182  func (p *Protocol) EpochReward(
   183  	ctx context.Context,
   184  	sm protocol.StateReader,
   185  ) (*big.Int, error) {
   186  	a := admin{}
   187  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   188  		return nil, err
   189  	}
   190  	return a.epochReward, nil
   191  }
   192  
   193  // NumDelegatesForEpochReward returns the number of candidates sharing an epoch reward
   194  func (p *Protocol) NumDelegatesForEpochReward(
   195  	ctx context.Context,
   196  	sm protocol.StateManager,
   197  ) (uint64, error) {
   198  	a := admin{}
   199  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   200  		return 0, err
   201  	}
   202  	return a.numDelegatesForEpochReward, nil
   203  }
   204  
   205  // FoundationBonus returns the foundation bonus amount
   206  func (p *Protocol) FoundationBonus(ctx context.Context, sm protocol.StateReader) (*big.Int, error) {
   207  	a := admin{}
   208  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   209  		return nil, err
   210  	}
   211  	return a.foundationBonus, nil
   212  }
   213  
   214  // FoundationBonusLastEpoch returns the last epoch when the foundation bonus will still be granted
   215  func (p *Protocol) FoundationBonusLastEpoch(ctx context.Context, sm protocol.StateReader) (uint64, error) {
   216  	a := admin{}
   217  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   218  		return 0, err
   219  	}
   220  	return a.foundationBonusLastEpoch, nil
   221  }
   222  
   223  // NumDelegatesForFoundationBonus returns the number of delegates that will get foundation bonus
   224  func (p *Protocol) NumDelegatesForFoundationBonus(ctx context.Context, sm protocol.StateReader) (uint64, error) {
   225  	a := admin{}
   226  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   227  		return 0, err
   228  	}
   229  	return a.numDelegatesForFoundationBonus, nil
   230  }
   231  
   232  // ProductivityThreshold returns the productivity threshold
   233  func (p *Protocol) ProductivityThreshold(ctx context.Context, sm protocol.StateManager) (uint64, error) {
   234  	a := admin{}
   235  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   236  		return 0, err
   237  	}
   238  	return a.productivityThreshold, nil
   239  }
   240  
   241  // SetReward updates block or epoch reward amount
   242  func (p *Protocol) SetReward(
   243  	ctx context.Context,
   244  	sm protocol.StateManager,
   245  	amount *big.Int,
   246  	blockLevel bool,
   247  ) error {
   248  	if err := p.assertAmount(amount); err != nil {
   249  		return err
   250  	}
   251  	a := admin{}
   252  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   253  		return err
   254  	}
   255  	if blockLevel {
   256  		a.blockReward = amount
   257  	} else {
   258  		a.epochReward = amount
   259  	}
   260  	return p.putState(ctx, sm, _adminKey, &a)
   261  }
   262  
   263  func (p *Protocol) assertAmount(amount *big.Int) error {
   264  	if amount.Cmp(big.NewInt(0)) >= 0 {
   265  		return nil
   266  	}
   267  	return errors.Errorf("amount %s shouldn't be negative", amount.String())
   268  }
   269  
   270  func (p *Protocol) assertZeroBlockHeight(height uint64) error {
   271  	if height != 0 {
   272  		return errors.Errorf("current block height %d is not zero", height)
   273  	}
   274  	return nil
   275  }