github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/rewarding/protocol.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  	"go.uber.org/zap"
    14  
    15  	"github.com/iotexproject/go-pkgs/hash"
    16  	"github.com/iotexproject/iotex-address/address"
    17  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    18  
    19  	"github.com/iotexproject/iotex-core/action"
    20  	"github.com/iotexproject/iotex-core/action/protocol"
    21  	accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
    22  	"github.com/iotexproject/iotex-core/action/protocol/rolldpos"
    23  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    24  	"github.com/iotexproject/iotex-core/pkg/log"
    25  	"github.com/iotexproject/iotex-core/state"
    26  )
    27  
    28  const (
    29  	// TODO: it works only for one instance per protocol definition now
    30  	_protocolID           = "rewarding"
    31  	_v2RewardingNamespace = "Rewarding"
    32  )
    33  
    34  var (
    35  	_adminKey                    = []byte("adm")
    36  	_fundKey                     = []byte("fnd")
    37  	_blockRewardHistoryKeyPrefix = []byte("brh")
    38  	_epochRewardHistoryKeyPrefix = []byte("erh")
    39  	_accountKeyPrefix            = []byte("acc")
    40  	_exemptKey                   = []byte("xpt")
    41  	errInvalidEpoch              = errors.New("invalid start/end epoch number")
    42  )
    43  
    44  // Protocol defines the protocol of the rewarding fund and the rewarding process. It allows the admin to config the
    45  // reward amount, users to donate tokens to the fund, block producers to grant them block and epoch reward and,
    46  // beneficiaries to claim the balance into their personal account.
    47  type Protocol struct {
    48  	keyPrefix []byte
    49  	addr      address.Address
    50  	cfg       genesis.Rewarding
    51  }
    52  
    53  // NewProtocol instantiates a rewarding protocol instance.
    54  func NewProtocol(cfg genesis.Rewarding) *Protocol {
    55  	h := hash.Hash160b([]byte(_protocolID))
    56  	addr, err := address.FromBytes(h[:])
    57  	if err != nil {
    58  		log.L().Panic("Error when constructing the address of rewarding protocol", zap.Error(err))
    59  	}
    60  	if err = validateFoundationBonusExtension(cfg); err != nil {
    61  		log.L().Panic("failed to validate foundation bonus extension", zap.Error(err))
    62  	}
    63  	return &Protocol{
    64  		keyPrefix: h[:],
    65  		addr:      addr,
    66  		cfg:       cfg,
    67  	}
    68  }
    69  
    70  // ProtocolAddr returns the address generated from protocol id
    71  func ProtocolAddr() address.Address {
    72  	return protocol.HashStringToAddress(_protocolID)
    73  }
    74  
    75  // verify that foundation bonus extension epochs are in increasing order
    76  func validateFoundationBonusExtension(cfg genesis.Rewarding) error {
    77  	if cfg.FoundationBonusP2StartEpoch > 0 || cfg.FoundationBonusP2EndEpoch > 0 {
    78  		if cfg.FoundationBonusP2StartEpoch < cfg.FoundationBonusLastEpoch || cfg.FoundationBonusP2EndEpoch < cfg.FoundationBonusP2StartEpoch {
    79  			return errInvalidEpoch
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  // FindProtocol finds the registered protocol from registry
    86  func FindProtocol(registry *protocol.Registry) *Protocol {
    87  	if registry == nil {
    88  		return nil
    89  	}
    90  	p, ok := registry.Find(_protocolID)
    91  	if !ok {
    92  		return nil
    93  	}
    94  	rp, ok := p.(*Protocol)
    95  	if !ok {
    96  		log.S().Panic("fail to cast reward protocol")
    97  	}
    98  	return rp
    99  }
   100  
   101  // CreatePreStates updates state manager
   102  func (p *Protocol) CreatePreStates(ctx context.Context, sm protocol.StateManager) error {
   103  	g := genesis.MustExtractGenesisContext(ctx)
   104  	blkCtx := protocol.MustGetBlockCtx(ctx)
   105  	switch blkCtx.BlockHeight {
   106  	case g.AleutianBlockHeight:
   107  		return p.SetReward(ctx, sm, g.AleutianEpochReward(), false)
   108  	case g.DardanellesBlockHeight:
   109  		return p.SetReward(ctx, sm, g.DardanellesBlockReward(), true)
   110  	case g.GreenlandBlockHeight:
   111  		return p.migrateValueGreenland(ctx, sm)
   112  	case g.KamchatkaBlockHeight:
   113  		return p.setFoundationBonusExtension(ctx, sm)
   114  	}
   115  	return nil
   116  }
   117  
   118  func (p *Protocol) migrateValueGreenland(_ context.Context, sm protocol.StateManager) error {
   119  	if err := p.migrateValue(sm, _adminKey, &admin{}); err != nil {
   120  		return err
   121  	}
   122  	if err := p.migrateValue(sm, _fundKey, &fund{}); err != nil {
   123  		return err
   124  	}
   125  	return p.migrateValue(sm, _exemptKey, &exempt{})
   126  }
   127  
   128  func (p *Protocol) migrateValue(sm protocol.StateManager, key []byte, value interface{}) error {
   129  	if _, err := p.stateV1(sm, key, value); err != nil {
   130  		if errors.Cause(err) == state.ErrStateNotExist {
   131  			// doesn't exist now just skip migration
   132  			return nil
   133  		}
   134  		return err
   135  	}
   136  	if err := p.putStateV2(sm, key, value); err != nil {
   137  		return err
   138  	}
   139  	return p.deleteStateV1(sm, key)
   140  }
   141  
   142  func (p *Protocol) setFoundationBonusExtension(ctx context.Context, sm protocol.StateManager) error {
   143  	a := admin{}
   144  	if _, err := p.state(ctx, sm, _adminKey, &a); err != nil {
   145  		return err
   146  	}
   147  
   148  	rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx))
   149  	blkCtx := protocol.MustGetBlockCtx(ctx)
   150  	newLastEpoch := rp.GetEpochNum(blkCtx.BlockHeight) + 8760
   151  
   152  	if a.foundationBonusLastEpoch < p.cfg.FoundationBonusP2EndEpoch {
   153  		a.foundationBonusLastEpoch = p.cfg.FoundationBonusP2EndEpoch
   154  	}
   155  	if a.foundationBonusLastEpoch < newLastEpoch {
   156  		a.foundationBonusLastEpoch = newLastEpoch
   157  	}
   158  	return p.putState(ctx, sm, _adminKey, &a)
   159  }
   160  
   161  // CreatePostSystemActions creates a list of system actions to be appended to block actions
   162  func (p *Protocol) CreatePostSystemActions(ctx context.Context, _ protocol.StateReader) ([]action.Envelope, error) {
   163  	blkCtx := protocol.MustGetBlockCtx(ctx)
   164  	grants := []action.Envelope{createGrantRewardAction(action.BlockReward, blkCtx.BlockHeight)}
   165  	rp := rolldpos.FindProtocol(protocol.MustGetRegistry(ctx))
   166  	if rp != nil && blkCtx.BlockHeight == rp.GetEpochLastBlockHeight(rp.GetEpochNum(blkCtx.BlockHeight)) {
   167  		grants = append(grants, createGrantRewardAction(action.EpochReward, blkCtx.BlockHeight))
   168  	}
   169  
   170  	return grants, nil
   171  }
   172  
   173  func createGrantRewardAction(rewardType int, height uint64) action.Envelope {
   174  	builder := action.EnvelopeBuilder{}
   175  	gb := action.GrantRewardBuilder{}
   176  	grant := gb.SetRewardType(rewardType).SetHeight(height).Build()
   177  
   178  	return builder.SetNonce(0).
   179  		SetGasPrice(big.NewInt(0)).
   180  		SetGasLimit(grant.GasLimit()).
   181  		SetAction(&grant).
   182  		Build()
   183  }
   184  
   185  // Validate validates a reward action
   186  func (p *Protocol) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error {
   187  	switch act.(type) {
   188  	case *action.GrantReward:
   189  		actionCtx := protocol.MustGetActionCtx(ctx)
   190  		if !address.Equal(protocol.MustGetBlockCtx(ctx).Producer, actionCtx.Caller) {
   191  			return errors.New("Only producer could create reward")
   192  		}
   193  		if actionCtx.GasPrice != nil && actionCtx.GasPrice.Cmp(big.NewInt(0)) != 0 || actionCtx.IntrinsicGas != 0 {
   194  			return errors.New("invalid gas price or intrinsic gas for reward action")
   195  		}
   196  	}
   197  	return nil
   198  }
   199  
   200  // Handle handles the actions on the rewarding protocol
   201  func (p *Protocol) Handle(
   202  	ctx context.Context,
   203  	act action.Action,
   204  	sm protocol.StateManager,
   205  ) (*action.Receipt, error) {
   206  	// TODO: simplify the boilerplate
   207  	switch act := act.(type) {
   208  	case *action.DepositToRewardingFund:
   209  		si := sm.Snapshot()
   210  		rlog, err := p.Deposit(ctx, sm, act.Amount(), iotextypes.TransactionLogType_DEPOSIT_TO_REWARDING_FUND)
   211  		if err != nil {
   212  			log.L().Debug("Error when handling rewarding action", zap.Error(err))
   213  			return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil)
   214  		}
   215  		return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog)
   216  	case *action.ClaimFromRewardingFund:
   217  		si := sm.Snapshot()
   218  		rlog, err := p.Claim(ctx, sm, act.Amount())
   219  		if err != nil {
   220  			log.L().Debug("Error when handling rewarding action", zap.Error(err))
   221  			return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil)
   222  		}
   223  		return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog)
   224  	case *action.GrantReward:
   225  		switch act.RewardType() {
   226  		case action.BlockReward:
   227  			si := sm.Snapshot()
   228  			rewardLog, err := p.GrantBlockReward(ctx, sm)
   229  			if err != nil {
   230  				log.L().Debug("Error when handling rewarding action", zap.Error(err))
   231  				return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil)
   232  			}
   233  			if rewardLog == nil {
   234  				return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil)
   235  			}
   236  			return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, []*action.Log{rewardLog})
   237  		case action.EpochReward:
   238  			si := sm.Snapshot()
   239  			rewardLogs, err := p.GrantEpochReward(ctx, sm)
   240  			if err != nil {
   241  				log.L().Debug("Error when handling rewarding action", zap.Error(err))
   242  				return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil)
   243  			}
   244  			return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, rewardLogs)
   245  		}
   246  	}
   247  	return nil, nil
   248  }
   249  
   250  // ReadState read the state on blockchain via protocol
   251  func (p *Protocol) ReadState(
   252  	ctx context.Context,
   253  	sr protocol.StateReader,
   254  	method []byte,
   255  	args ...[]byte,
   256  ) ([]byte, uint64, error) {
   257  	switch string(method) {
   258  	case "AvailableBalance":
   259  		balance, height, err := p.AvailableBalance(ctx, sr)
   260  		if err != nil {
   261  			return nil, uint64(0), err
   262  		}
   263  		return []byte(balance.String()), height, nil
   264  	case "TotalBalance":
   265  		balance, height, err := p.TotalBalance(ctx, sr)
   266  		if err != nil {
   267  			return nil, uint64(0), err
   268  		}
   269  		return []byte(balance.String()), height, nil
   270  	case "UnclaimedBalance":
   271  		if len(args) != 1 {
   272  			return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args))
   273  		}
   274  		addr, err := address.FromString(string(args[0]))
   275  		if err != nil {
   276  			return nil, uint64(0), err
   277  		}
   278  		balance, height, err := p.UnclaimedBalance(ctx, sr, addr)
   279  		if err != nil {
   280  			return nil, uint64(0), err
   281  		}
   282  		return []byte(balance.String()), height, nil
   283  	default:
   284  		return nil, uint64(0), errors.New("corresponding method isn't found")
   285  	}
   286  }
   287  
   288  // Register registers the protocol with a unique ID
   289  func (p *Protocol) Register(r *protocol.Registry) error {
   290  	return r.Register(_protocolID, p)
   291  }
   292  
   293  // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists
   294  func (p *Protocol) ForceRegister(r *protocol.Registry) error {
   295  	return r.ForceRegister(_protocolID, p)
   296  }
   297  
   298  // Name returns the name of protocol
   299  func (p *Protocol) Name() string {
   300  	return _protocolID
   301  }
   302  
   303  // useV2Storage return true after greenland when we start using v2 storage.
   304  func useV2Storage(ctx context.Context) bool {
   305  	return protocol.MustGetFeatureCtx(ctx).UseV2Storage
   306  }
   307  
   308  func (p *Protocol) state(ctx context.Context, sm protocol.StateReader, key []byte, value interface{}) (uint64, error) {
   309  	h, _, err := p.stateCheckLegacy(ctx, sm, key, value)
   310  	return h, err
   311  }
   312  
   313  func (p *Protocol) stateCheckLegacy(ctx context.Context, sm protocol.StateReader, key []byte, value interface{}) (uint64, bool, error) {
   314  	if useV2Storage(ctx) {
   315  		h, err := p.stateV2(sm, key, value)
   316  		if errors.Cause(err) != state.ErrStateNotExist {
   317  			return h, false, err
   318  		}
   319  	}
   320  	h, err := p.stateV1(sm, key, value)
   321  	return h, true, err
   322  }
   323  
   324  func (p *Protocol) stateV1(sm protocol.StateReader, key []byte, value interface{}) (uint64, error) {
   325  	keyHash := hash.Hash160b(append(p.keyPrefix, key...))
   326  	return sm.State(value, protocol.LegacyKeyOption(keyHash))
   327  }
   328  
   329  func (p *Protocol) stateV2(sm protocol.StateReader, key []byte, value interface{}) (uint64, error) {
   330  	k := append(p.keyPrefix, key...)
   331  	return sm.State(value, protocol.KeyOption(k), protocol.NamespaceOption(_v2RewardingNamespace))
   332  }
   333  
   334  func (p *Protocol) putState(ctx context.Context, sm protocol.StateManager, key []byte, value interface{}) error {
   335  	if useV2Storage(ctx) {
   336  		return p.putStateV2(sm, key, value)
   337  	}
   338  	return p.putStateV1(sm, key, value)
   339  }
   340  
   341  func (p *Protocol) putStateV1(sm protocol.StateManager, key []byte, value interface{}) error {
   342  	keyHash := hash.Hash160b(append(p.keyPrefix, key...))
   343  	_, err := sm.PutState(value, protocol.LegacyKeyOption(keyHash))
   344  	return err
   345  }
   346  
   347  func (p *Protocol) putStateV2(sm protocol.StateManager, key []byte, value interface{}) error {
   348  	k := append(p.keyPrefix, key...)
   349  	_, err := sm.PutState(value, protocol.KeyOption(k), protocol.NamespaceOption(_v2RewardingNamespace))
   350  	return err
   351  }
   352  
   353  func (p *Protocol) deleteState(ctx context.Context, sm protocol.StateManager, key []byte) error {
   354  	if useV2Storage(ctx) {
   355  		return p.deleteStateV2(sm, key)
   356  	}
   357  	return p.deleteStateV1(sm, key)
   358  }
   359  
   360  func (p *Protocol) deleteStateV1(sm protocol.StateManager, key []byte) error {
   361  	keyHash := hash.Hash160b(append(p.keyPrefix, key...))
   362  	_, err := sm.DelState(protocol.LegacyKeyOption(keyHash))
   363  	if errors.Cause(err) == state.ErrStateNotExist {
   364  		// don't care if not exist
   365  		return nil
   366  	}
   367  	return err
   368  }
   369  
   370  func (p *Protocol) deleteStateV2(sm protocol.StateManager, key []byte) error {
   371  	k := append(p.keyPrefix, key...)
   372  	_, err := sm.DelState(protocol.KeyOption(k), protocol.NamespaceOption(_v2RewardingNamespace))
   373  	if errors.Cause(err) == state.ErrStateNotExist {
   374  		// don't care if not exist
   375  		return nil
   376  	}
   377  	return err
   378  }
   379  
   380  func (p *Protocol) settleSystemAction(
   381  	ctx context.Context,
   382  	sm protocol.StateManager,
   383  	status uint64,
   384  	si int,
   385  	logs []*action.Log,
   386  	tLogs ...*action.TransactionLog,
   387  ) (*action.Receipt, error) {
   388  	return p.settleAction(ctx, sm, status, si, true, logs, tLogs...)
   389  }
   390  
   391  func (p *Protocol) settleUserAction(
   392  	ctx context.Context,
   393  	sm protocol.StateManager,
   394  	status uint64,
   395  	si int,
   396  	logs []*action.Log,
   397  	tLogs ...*action.TransactionLog,
   398  ) (*action.Receipt, error) {
   399  	return p.settleAction(ctx, sm, status, si, false, logs, tLogs...)
   400  }
   401  
   402  func (p *Protocol) settleAction(
   403  	ctx context.Context,
   404  	sm protocol.StateManager,
   405  	status uint64,
   406  	si int,
   407  	isSystemAction bool,
   408  	logs []*action.Log,
   409  	tLogs ...*action.TransactionLog,
   410  ) (*action.Receipt, error) {
   411  	actionCtx := protocol.MustGetActionCtx(ctx)
   412  	blkCtx := protocol.MustGetBlockCtx(ctx)
   413  	if status == uint64(iotextypes.ReceiptStatus_Failure) {
   414  		if err := sm.Revert(si); err != nil {
   415  			return nil, err
   416  		}
   417  	}
   418  	skipUpdateForSystemAction := protocol.MustGetFeatureCtx(ctx).FixGasAndNonceUpdate
   419  	if !isSystemAction || !skipUpdateForSystemAction {
   420  		gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas))
   421  		depositLog, err := DepositGas(ctx, sm, gasFee)
   422  		if err != nil {
   423  			return nil, err
   424  		}
   425  		if depositLog != nil {
   426  			tLogs = append(tLogs, depositLog)
   427  		}
   428  		if err := p.increaseNonce(
   429  			ctx,
   430  			sm,
   431  			actionCtx.Caller,
   432  			actionCtx.Nonce,
   433  			!skipUpdateForSystemAction && actionCtx.Nonce == 0,
   434  		); err != nil {
   435  			return nil, err
   436  		}
   437  	}
   438  
   439  	return p.createReceipt(status, blkCtx.BlockHeight, actionCtx.ActionHash, actionCtx.IntrinsicGas, logs, tLogs...), nil
   440  }
   441  
   442  func (p *Protocol) increaseNonce(ctx context.Context, sm protocol.StateManager, addr address.Address, nonce uint64, skipSetNonce bool) error {
   443  	accountCreationOpts := []state.AccountCreationOption{}
   444  	if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount {
   445  		accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption())
   446  	}
   447  	acc, err := accountutil.LoadOrCreateAccount(sm, addr, accountCreationOpts...)
   448  	if err != nil {
   449  		return err
   450  	}
   451  	if !skipSetNonce {
   452  		if err := acc.SetPendingNonce(nonce + 1); err != nil {
   453  			return errors.Wrapf(err, "invalid nonce %d", nonce)
   454  		}
   455  	}
   456  	return accountutil.StoreAccount(sm, addr, acc)
   457  }
   458  
   459  func (p *Protocol) createReceipt(
   460  	status uint64,
   461  	blkHeight uint64,
   462  	actHash hash.Hash256,
   463  	gasConsumed uint64,
   464  	logs []*action.Log,
   465  	tLogs ...*action.TransactionLog,
   466  ) *action.Receipt {
   467  	// TODO: need to review the fields
   468  	return (&action.Receipt{
   469  		Status:          status,
   470  		BlockHeight:     blkHeight,
   471  		ActionHash:      actHash,
   472  		GasConsumed:     gasConsumed,
   473  		ContractAddress: p.addr.String(),
   474  	}).AddLogs(logs...).AddTransactionLogs(tLogs...)
   475  }