github.com/aergoio/aergo@v1.3.1/contract/system/execute.go (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  package system
     6  
     7  import (
     8  	"encoding/json"
     9  	"math/big"
    10  
    11  	"github.com/aergoio/aergo/state"
    12  	"github.com/aergoio/aergo/types"
    13  )
    14  
    15  type SystemContext struct {
    16  	BlockNo  uint64
    17  	Call     *types.CallInfo
    18  	Args     []string
    19  	Staked   *types.Staking
    20  	Vote     *types.Vote
    21  	Sender   *state.V
    22  	Receiver *state.V
    23  }
    24  
    25  func ExecuteSystemTx(scs *state.ContractState, txBody *types.TxBody,
    26  	sender, receiver *state.V, blockNo types.BlockNo) ([]*types.Event, error) {
    27  
    28  	context, err := ValidateSystemTx(sender.ID(), txBody, sender, scs, blockNo)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	context.Receiver = receiver
    33  
    34  	var event *types.Event
    35  	switch context.Call.Name {
    36  	case types.Stake:
    37  		event, err = staking(txBody, sender, receiver, scs, blockNo, context)
    38  	case types.VoteBP:
    39  		event, err = voting(txBody, sender, receiver, scs, blockNo, context)
    40  	case types.Unstake:
    41  		event, err = unstaking(txBody, sender, receiver, scs, blockNo, context)
    42  	default:
    43  		err = types.ErrTxInvalidPayload
    44  	}
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	var events []*types.Event
    49  	events = append(events, event)
    50  	return events, nil
    51  }
    52  
    53  func GetNamePrice(scs *state.ContractState) *big.Int {
    54  	votelist, err := getVoteResult(scs, []byte(types.VoteNamePrice[2:]), 1)
    55  	if err != nil {
    56  		panic("could not get vote result for min staking")
    57  	}
    58  	if len(votelist.Votes) == 0 {
    59  		return types.NamePrice
    60  	}
    61  	return new(big.Int).SetBytes(votelist.Votes[0].GetCandidate())
    62  }
    63  
    64  func GetMinimumStaking(scs *state.ContractState) *big.Int {
    65  	votelist, err := getVoteResult(scs, []byte(types.VoteMinStaking[2:]), 1)
    66  	if err != nil {
    67  		panic("could not get vote result for min staking")
    68  	}
    69  	if len(votelist.Votes) == 0 {
    70  		return types.StakingMinimum
    71  	}
    72  	minimumStaking, ok := new(big.Int).SetString(string(votelist.Votes[0].GetCandidate()), 10)
    73  	if !ok {
    74  		panic("could not get vote result for min staking")
    75  	}
    76  	return minimumStaking
    77  }
    78  
    79  func ValidateSystemTx(account []byte, txBody *types.TxBody, sender *state.V,
    80  	scs *state.ContractState, blockNo uint64) (*SystemContext, error) {
    81  	var ci types.CallInfo
    82  	context := &SystemContext{Call: &ci, Sender: sender, BlockNo: blockNo}
    83  
    84  	if err := json.Unmarshal(txBody.Payload, &ci); err != nil {
    85  		return nil, types.ErrTxInvalidPayload
    86  	}
    87  	switch ci.Name {
    88  	case types.Stake:
    89  		if sender != nil && sender.Balance().Cmp(txBody.GetAmountBigInt()) < 0 {
    90  			return nil, types.ErrInsufficientBalance
    91  		}
    92  		staked, err := validateForStaking(account, txBody, scs, blockNo)
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  		context.Staked = staked
    97  	case types.VoteBP:
    98  		staked, err := getStaking(scs, account)
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  		if staked.GetAmountBigInt().Cmp(new(big.Int).SetUint64(0)) == 0 {
   103  			return nil, types.ErrMustStakeBeforeVote
   104  		}
   105  		oldvote, err := GetVote(scs, account, []byte(ci.Name[2:]))
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		if oldvote.Amount != nil && staked.GetWhen()+VotingDelay > blockNo {
   110  			return nil, types.ErrLessTimeHasPassed
   111  		}
   112  		context.Staked = staked
   113  		context.Vote = oldvote
   114  	case types.Unstake:
   115  		staked, err := validateForUnstaking(account, txBody, scs, blockNo)
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  		context.Staked = staked
   120  	default:
   121  		return nil, types.ErrTxInvalidPayload
   122  	}
   123  	return context, nil
   124  }
   125  
   126  func validateForStaking(account []byte, txBody *types.TxBody, scs *state.ContractState, blockNo uint64) (*types.Staking, error) {
   127  	staked, err := getStaking(scs, account)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	if staked.GetAmount() != nil && staked.GetWhen()+StakingDelay > blockNo {
   132  		return nil, types.ErrLessTimeHasPassed
   133  	}
   134  	toBe := new(big.Int).Add(staked.GetAmountBigInt(), txBody.GetAmountBigInt())
   135  	if GetMinimumStaking(scs).Cmp(toBe) > 0 {
   136  		return nil, types.ErrTooSmallAmount
   137  	}
   138  	return staked, nil
   139  }
   140  
   141  func validateForUnstaking(account []byte, txBody *types.TxBody, scs *state.ContractState, blockNo uint64) (*types.Staking, error) {
   142  	staked, err := getStaking(scs, account)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	if staked.GetAmountBigInt().Cmp(big.NewInt(0)) == 0 {
   147  		return nil, types.ErrMustStakeBeforeUnstake
   148  	}
   149  	if staked.GetAmountBigInt().Cmp(txBody.GetAmountBigInt()) < 0 {
   150  		return nil, types.ErrExceedAmount
   151  	}
   152  	if staked.GetWhen()+StakingDelay > blockNo {
   153  		return nil, types.ErrLessTimeHasPassed
   154  	}
   155  	toBe := new(big.Int).Sub(staked.GetAmountBigInt(), txBody.GetAmountBigInt())
   156  	if toBe.Cmp(big.NewInt(0)) != 0 && GetMinimumStaking(scs).Cmp(toBe) > 0 {
   157  		return nil, types.ErrTooSmallAmount
   158  	}
   159  	return staked, nil
   160  }