-
Notifications
You must be signed in to change notification settings - Fork 217
/
contract.go
299 lines (253 loc) · 13 KB
/
contract.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Code generated
// This file is a generated precompile contract with stubbed abstract functions.
package rewardmanager
import (
_ "embed"
"errors"
"fmt"
"github.com/ava-labs/subnet-evm/accounts/abi"
"github.com/ava-labs/subnet-evm/constants"
"github.com/ava-labs/subnet-evm/precompile/allowlist"
"github.com/ava-labs/subnet-evm/precompile/contract"
"github.com/ava-labs/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
)
const (
AllowFeeRecipientsGasCost uint64 = (contract.WriteGasCostPerSlot) + allowlist.ReadAllowListGasCost // write 1 slot + read allow list
AreFeeRecipientsAllowedGasCost uint64 = allowlist.ReadAllowListGasCost
CurrentRewardAddressGasCost uint64 = allowlist.ReadAllowListGasCost
DisableRewardsGasCost uint64 = (contract.WriteGasCostPerSlot) + allowlist.ReadAllowListGasCost // write 1 slot + read allow list
SetRewardAddressGasCost uint64 = (contract.WriteGasCostPerSlot) + allowlist.ReadAllowListGasCost // write 1 slot + read allow list
)
// Singleton StatefulPrecompiledContract and signatures.
var (
ErrCannotAllowFeeRecipients = errors.New("non-enabled cannot call allowFeeRecipients")
ErrCannotAreFeeRecipientsAllowed = errors.New("non-enabled cannot call areFeeRecipientsAllowed")
ErrCannotCurrentRewardAddress = errors.New("non-enabled cannot call currentRewardAddress")
ErrCannotDisableRewards = errors.New("non-enabled cannot call disableRewards")
ErrCannotSetRewardAddress = errors.New("non-enabled cannot call setRewardAddress")
ErrCannotEnableBothRewards = errors.New("cannot enable both fee recipients and reward address at the same time")
ErrEmptyRewardAddress = errors.New("reward address cannot be empty")
// RewardManagerRawABI contains the raw ABI of RewardManager contract.
//go:embed contract.abi
RewardManagerRawABI string
RewardManagerABI = contract.ParseABI(RewardManagerRawABI)
RewardManagerPrecompile = createRewardManagerPrecompile() // will be initialized by init function
rewardAddressStorageKey = common.Hash{'r', 'a', 's', 'k'}
allowFeeRecipientsAddressValue = common.Hash{'a', 'f', 'r', 'a', 'v'}
)
// GetRewardManagerAllowListStatus returns the role of [address] for the RewardManager list.
func GetRewardManagerAllowListStatus(stateDB contract.StateDB, address common.Address) allowlist.Role {
return allowlist.GetAllowListStatus(stateDB, ContractAddress, address)
}
// SetRewardManagerAllowListStatus sets the permissions of [address] to [role] for the
// RewardManager list. Assumes [role] has already been verified as valid.
func SetRewardManagerAllowListStatus(stateDB contract.StateDB, address common.Address, role allowlist.Role) {
allowlist.SetAllowListRole(stateDB, ContractAddress, address, role)
}
// PackAllowFeeRecipients packs the function selector (first 4 func signature bytes).
// This function is mostly used for tests.
func PackAllowFeeRecipients() ([]byte, error) {
return RewardManagerABI.Pack("allowFeeRecipients")
}
// EnableAllowFeeRecipients enables fee recipients.
func EnableAllowFeeRecipients(stateDB contract.StateDB) {
stateDB.SetState(ContractAddress, rewardAddressStorageKey, allowFeeRecipientsAddressValue)
}
// DisableRewardAddress disables rewards and burns them by sending to Blackhole Address.
func DisableFeeRewards(stateDB contract.StateDB) {
stateDB.SetState(ContractAddress, rewardAddressStorageKey, constants.BlackholeAddr.Hash())
}
func allowFeeRecipients(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
if remainingGas, err = contract.DeductGas(suppliedGas, AllowFeeRecipientsGasCost); err != nil {
return nil, 0, err
}
if readOnly {
return nil, remainingGas, vmerrs.ErrWriteProtection
}
// no input provided for this function
// Allow list is enabled and AllowFeeRecipients is a state-changer function.
// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
// You can modify/delete this code if you don't want this function to be restricted by the allow list.
stateDB := accessibleState.GetStateDB()
// Verify that the caller is in the allow list and therefore has the right to call this function.
callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
if !callerStatus.IsEnabled() {
return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotAllowFeeRecipients, caller)
}
// allow list code ends here.
// this function does not return an output, leave this one as is
EnableAllowFeeRecipients(stateDB)
packedOutput := []byte{}
// Return the packed output and the remaining gas
return packedOutput, remainingGas, nil
}
// PackAreFeeRecipientsAllowed packs the include selector (first 4 func signature bytes).
// This function is mostly used for tests.
func PackAreFeeRecipientsAllowed() ([]byte, error) {
return RewardManagerABI.Pack("areFeeRecipientsAllowed")
}
// PackAreFeeRecipientsAllowedOutput attempts to pack given isAllowed of type bool
// to conform the ABI outputs.
func PackAreFeeRecipientsAllowedOutput(isAllowed bool) ([]byte, error) {
return RewardManagerABI.PackOutput("areFeeRecipientsAllowed", isAllowed)
}
func areFeeRecipientsAllowed(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
if remainingGas, err = contract.DeductGas(suppliedGas, AreFeeRecipientsAllowedGasCost); err != nil {
return nil, 0, err
}
// no input provided for this function
stateDB := accessibleState.GetStateDB()
var output bool
_, output = GetStoredRewardAddress(stateDB)
packedOutput, err := PackAreFeeRecipientsAllowedOutput(output)
if err != nil {
return nil, remainingGas, err
}
// Return the packed output and the remaining gas
return packedOutput, remainingGas, nil
}
// PackCurrentRewardAddress packs the include selector (first 4 func signature bytes).
// This function is mostly used for tests.
func PackCurrentRewardAddress() ([]byte, error) {
return RewardManagerABI.Pack("currentRewardAddress")
}
// PackCurrentRewardAddressOutput attempts to pack given rewardAddress of type common.Address
// to conform the ABI outputs.
func PackCurrentRewardAddressOutput(rewardAddress common.Address) ([]byte, error) {
return RewardManagerABI.PackOutput("currentRewardAddress", rewardAddress)
}
// GetStoredRewardAddress returns the current value of the address stored under rewardAddressStorageKey.
// Returns an empty address and true if allow fee recipients is enabled, otherwise returns current reward address and false.
func GetStoredRewardAddress(stateDB contract.StateDB) (common.Address, bool) {
val := stateDB.GetState(ContractAddress, rewardAddressStorageKey)
return common.BytesToAddress(val.Bytes()), val == allowFeeRecipientsAddressValue
}
// StoredRewardAddress stores the given [val] under rewardAddressStorageKey.
func StoreRewardAddress(stateDB contract.StateDB, val common.Address) error {
// if input is empty, return an error
if val == (common.Address{}) {
return ErrEmptyRewardAddress
}
stateDB.SetState(ContractAddress, rewardAddressStorageKey, val.Hash())
return nil
}
// PackSetRewardAddress packs [addr] of type common.Address into the appropriate arguments for setRewardAddress.
// the packed bytes include selector (first 4 func signature bytes).
// This function is mostly used for tests.
func PackSetRewardAddress(addr common.Address) ([]byte, error) {
return RewardManagerABI.Pack("setRewardAddress", addr)
}
// UnpackSetRewardAddressInput attempts to unpack [input] into the common.Address type argument
// assumes that [input] does not include selector (omits first 4 func signature bytes)
func UnpackSetRewardAddressInput(input []byte) (common.Address, error) {
res, err := RewardManagerABI.UnpackInput("setRewardAddress", input)
if err != nil {
return common.Address{}, err
}
unpacked := *abi.ConvertType(res[0], new(common.Address)).(*common.Address)
return unpacked, nil
}
func setRewardAddress(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
if remainingGas, err = contract.DeductGas(suppliedGas, SetRewardAddressGasCost); err != nil {
return nil, 0, err
}
if readOnly {
return nil, remainingGas, vmerrs.ErrWriteProtection
}
// attempts to unpack [input] into the arguments to the SetRewardAddressInput.
// Assumes that [input] does not include selector
// You can use unpacked [inputStruct] variable in your code
inputStruct, err := UnpackSetRewardAddressInput(input)
if err != nil {
return nil, remainingGas, err
}
// Allow list is enabled and SetRewardAddress is a state-changer function.
// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
// You can modify/delete this code if you don't want this function to be restricted by the allow list.
stateDB := accessibleState.GetStateDB()
// Verify that the caller is in the allow list and therefore has the right to call this function.
callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
if !callerStatus.IsEnabled() {
return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotSetRewardAddress, caller)
}
// allow list code ends here.
if err := StoreRewardAddress(stateDB, inputStruct); err != nil {
return nil, remainingGas, err
}
// this function does not return an output, leave this one as is
packedOutput := []byte{}
// Return the packed output and the remaining gas
return packedOutput, remainingGas, nil
}
func currentRewardAddress(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
if remainingGas, err = contract.DeductGas(suppliedGas, CurrentRewardAddressGasCost); err != nil {
return nil, 0, err
}
// no input provided for this function
stateDB := accessibleState.GetStateDB()
output, _ := GetStoredRewardAddress(stateDB)
packedOutput, err := PackCurrentRewardAddressOutput(output)
if err != nil {
return nil, remainingGas, err
}
// Return the packed output and the remaining gas
return packedOutput, remainingGas, nil
}
// PackDisableRewards packs the include selector (first 4 func signature bytes).
// This function is mostly used for tests.
func PackDisableRewards() ([]byte, error) {
return RewardManagerABI.Pack("disableRewards")
}
func disableRewards(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
if remainingGas, err = contract.DeductGas(suppliedGas, DisableRewardsGasCost); err != nil {
return nil, 0, err
}
if readOnly {
return nil, remainingGas, vmerrs.ErrWriteProtection
}
// no input provided for this function
// Allow list is enabled and DisableRewards is a state-changer function.
// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
// You can modify/delete this code if you don't want this function to be restricted by the allow list.
stateDB := accessibleState.GetStateDB()
// Verify that the caller is in the allow list and therefore has the right to call this function.
callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
if !callerStatus.IsEnabled() {
return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotDisableRewards, caller)
}
// allow list code ends here.
DisableFeeRewards(stateDB)
// this function does not return an output, leave this one as is
packedOutput := []byte{}
// Return the packed output and the remaining gas
return packedOutput, remainingGas, nil
}
// createRewardManagerPrecompile returns a StatefulPrecompiledContract with getters and setters for the precompile.
// Access to the getters/setters is controlled by an allow list for [precompileAddr].
func createRewardManagerPrecompile() contract.StatefulPrecompiledContract {
var functions []*contract.StatefulPrecompileFunction
functions = append(functions, allowlist.CreateAllowListFunctions(ContractAddress)...)
abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{
"allowFeeRecipients": allowFeeRecipients,
"areFeeRecipientsAllowed": areFeeRecipientsAllowed,
"currentRewardAddress": currentRewardAddress,
"disableRewards": disableRewards,
"setRewardAddress": setRewardAddress,
}
for name, function := range abiFunctionMap {
method, ok := RewardManagerABI.Methods[name]
if !ok {
panic(fmt.Errorf("given method (%s) does not exist in the ABI", name))
}
functions = append(functions, contract.NewStatefulPrecompileFunction(method.ID, function))
}
// Construct the contract with no fallback function.
statefulContract, err := contract.NewStatefulPrecompileContract(nil, functions)
if err != nil {
panic(err)
}
return statefulContract
}