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 }