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 }