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

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  package system
     6  
     7  import (
     8  	"encoding/binary"
     9  	"errors"
    10  	"fmt"
    11  	"math/big"
    12  
    13  	"github.com/aergoio/aergo/state"
    14  	"github.com/aergoio/aergo/types"
    15  )
    16  
    17  var consensusType string
    18  
    19  var stakingKey = []byte("staking")
    20  var stakingTotalKey = []byte("stakingtotal")
    21  
    22  const StakingDelay = 60 * 60 * 24 //block interval
    23  //const StakingDelay = 5
    24  
    25  func InitGovernance(consensus string) {
    26  	consensusType = consensus
    27  }
    28  
    29  func staking(txBody *types.TxBody, sender, receiver *state.V,
    30  	scs *state.ContractState, blockNo types.BlockNo, context *SystemContext) (*types.Event, error) {
    31  	if consensusType != "dpos" {
    32  		return nil, fmt.Errorf("unsupported staking for the consensus: %s", consensusType)
    33  	}
    34  
    35  	staked := context.Staked
    36  	beforeStaked := staked.GetAmountBigInt()
    37  	amount := txBody.GetAmountBigInt()
    38  	staked.Amount = new(big.Int).Add(beforeStaked, amount).Bytes()
    39  	staked.When = blockNo
    40  	if err := setStaking(scs, sender.ID(), staked); err != nil {
    41  		return nil, err
    42  	}
    43  	if err := addTotal(scs, amount); err != nil {
    44  		return nil, err
    45  	}
    46  	sender.SubBalance(amount)
    47  	receiver.AddBalance(amount)
    48  	return &types.Event{
    49  		ContractAddress: receiver.ID(),
    50  		EventIdx:        0,
    51  		EventName:       "stake",
    52  		JsonArgs: `{"who":"` +
    53  			types.EncodeAddress(sender.ID()) +
    54  			`", "amount":"` + txBody.GetAmountBigInt().String() + `"}`,
    55  	}, nil
    56  }
    57  
    58  func unstaking(txBody *types.TxBody, sender, receiver *state.V, scs *state.ContractState,
    59  	blockNo types.BlockNo, context *SystemContext) (*types.Event, error) {
    60  	staked := context.Staked
    61  	amount := txBody.GetAmountBigInt()
    62  	var backToBalance *big.Int
    63  	if staked.GetAmountBigInt().Cmp(amount) < 0 {
    64  		amount = new(big.Int).SetUint64(0)
    65  		backToBalance = staked.GetAmountBigInt()
    66  	} else {
    67  		amount = new(big.Int).Sub(staked.GetAmountBigInt(), txBody.GetAmountBigInt())
    68  		backToBalance = txBody.GetAmountBigInt()
    69  	}
    70  	staked.Amount = amount.Bytes()
    71  	//blockNo will be updated in voting
    72  	staked.When = blockNo
    73  
    74  	if err := setStaking(scs, sender.ID(), staked); err != nil {
    75  		return nil, err
    76  	}
    77  	if err := refreshAllVote(txBody, scs, context); err != nil {
    78  		return nil, err
    79  	}
    80  	if err := subTotal(scs, backToBalance); err != nil {
    81  		return nil, err
    82  	}
    83  	sender.AddBalance(backToBalance)
    84  	receiver.SubBalance(backToBalance)
    85  	return &types.Event{
    86  		ContractAddress: receiver.ID(),
    87  		EventIdx:        0,
    88  		EventName:       "unstake",
    89  		JsonArgs: `{"who":"` +
    90  			types.EncodeAddress(sender.ID()) +
    91  			`", "amount":"` + txBody.GetAmountBigInt().String() + `"}`,
    92  	}, nil
    93  }
    94  
    95  func setStaking(scs *state.ContractState, who []byte, staking *types.Staking) error {
    96  	key := append(stakingKey, who...)
    97  	return scs.SetData(key, serializeStaking(staking))
    98  }
    99  
   100  func getStaking(scs *state.ContractState, who []byte) (*types.Staking, error) {
   101  	key := append(stakingKey, who...)
   102  	data, err := scs.GetData(key)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	var staking types.Staking
   107  	if len(data) != 0 {
   108  		return deserializeStaking(data), nil
   109  	}
   110  	return &staking, nil
   111  }
   112  
   113  func GetStaking(scs *state.ContractState, address []byte) (*types.Staking, error) {
   114  	if address != nil {
   115  		return getStaking(scs, address)
   116  	}
   117  	return nil, errors.New("invalid argument: address should not be nil")
   118  }
   119  
   120  func GetStakingTotal(ar AccountStateReader) (*big.Int, error) {
   121  	scs, err := ar.GetSystemAccountState()
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	return getStakingTotal(scs)
   126  }
   127  
   128  func getStakingTotal(scs *state.ContractState) (*big.Int, error) {
   129  	data, err := scs.GetData(stakingTotalKey)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return new(big.Int).SetBytes(data), nil
   134  }
   135  
   136  func addTotal(scs *state.ContractState, amount *big.Int) error {
   137  	data, err := scs.GetData(stakingTotalKey)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	total := new(big.Int).SetBytes(data)
   142  	return scs.SetData(stakingTotalKey, new(big.Int).Add(total, amount).Bytes())
   143  }
   144  
   145  func subTotal(scs *state.ContractState, amount *big.Int) error {
   146  	data, err := scs.GetData(stakingTotalKey)
   147  	if err != nil {
   148  		return err
   149  	}
   150  	total := new(big.Int).SetBytes(data)
   151  	return scs.SetData(stakingTotalKey, new(big.Int).Sub(total, amount).Bytes())
   152  }
   153  
   154  func serializeStaking(v *types.Staking) []byte {
   155  	var ret []byte
   156  	if v != nil {
   157  		when := make([]byte, 8)
   158  		binary.LittleEndian.PutUint64(when, v.GetWhen())
   159  		ret = append(ret, when...)
   160  		ret = append(ret, v.GetAmount()...)
   161  	}
   162  	return ret
   163  }
   164  
   165  func deserializeStaking(data []byte) *types.Staking {
   166  	when := binary.LittleEndian.Uint64(data[:8])
   167  	amount := data[8:]
   168  	return &types.Staking{Amount: amount, When: when}
   169  }