forked from ava-labs/subnet-evm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
contract.go
118 lines (96 loc) · 4.42 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
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package nativeminter
import (
"errors"
"fmt"
"math/big"
"github.com/bilgin-kocak/subnet-evm/precompile/allowlist"
"github.com/bilgin-kocak/subnet-evm/precompile/contract"
"github.com/bilgin-kocak/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
)
const (
mintInputAddressSlot = iota
mintInputAmountSlot
mintInputLen = common.HashLength + common.HashLength
MintGasCost = 30_000
)
var (
// Singleton StatefulPrecompiledContract for minting native assets by permissioned callers.
ContractNativeMinterPrecompile contract.StatefulPrecompiledContract = createNativeMinterPrecompile()
mintSignature = contract.CalculateFunctionSelector("mintNativeCoin(address,uint256)") // address, amount
ErrCannotMint = errors.New("non-enabled cannot mint")
)
// GetContractNativeMinterStatus returns the role of [address] for the minter list.
func GetContractNativeMinterStatus(stateDB contract.StateDB, address common.Address) allowlist.Role {
return allowlist.GetAllowListStatus(stateDB, ContractAddress, address)
}
// SetContractNativeMinterStatus sets the permissions of [address] to [role] for the
// minter list. assumes [role] has already been verified as valid.
func SetContractNativeMinterStatus(stateDB contract.StateDB, address common.Address, role allowlist.Role) {
allowlist.SetAllowListRole(stateDB, ContractAddress, address, role)
}
// PackMintInput packs [address] and [amount] into the appropriate arguments for minting operation.
// Assumes that [amount] can be represented by 32 bytes.
func PackMintInput(address common.Address, amount *big.Int) ([]byte, error) {
// function selector (4 bytes) + input(hash for address + hash for amount)
res := make([]byte, contract.SelectorLen+mintInputLen)
err := contract.PackOrderedHashesWithSelector(res, mintSignature, []common.Hash{
address.Hash(),
common.BigToHash(amount),
})
return res, err
}
// UnpackMintInput attempts to unpack [input] into the arguments to the mint precompile
// assumes that [input] does not include selector (omits first 4 bytes in PackMintInput)
func UnpackMintInput(input []byte) (common.Address, *big.Int, error) {
if len(input) != mintInputLen {
return common.Address{}, nil, fmt.Errorf("invalid input length for minting: %d", len(input))
}
to := common.BytesToAddress(contract.PackedHash(input, mintInputAddressSlot))
assetAmount := new(big.Int).SetBytes(contract.PackedHash(input, mintInputAmountSlot))
return to, assetAmount, nil
}
// mintNativeCoin checks if the caller is permissioned for minting operation.
// The execution function parses the [input] into native coin amount and receiver address.
func mintNativeCoin(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, MintGasCost); err != nil {
return nil, 0, err
}
if readOnly {
return nil, remainingGas, vmerrs.ErrWriteProtection
}
to, amount, err := UnpackMintInput(input)
if err != nil {
return nil, remainingGas, err
}
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", ErrCannotMint, caller)
}
// if there is no address in the state, create one.
if !stateDB.Exist(to) {
stateDB.CreateAccount(to)
}
stateDB.AddBalance(to, amount)
// Return an empty output and the remaining gas
return []byte{}, remainingGas, nil
}
// createNativeMinterPrecompile returns a StatefulPrecompiledContract for native coin minting. The precompile
// is accessed controlled by an allow list at [precompileAddr].
func createNativeMinterPrecompile() contract.StatefulPrecompiledContract {
enabledFuncs := allowlist.CreateAllowListFunctions(ContractAddress)
mintFunc := contract.NewStatefulPrecompileFunction(mintSignature, mintNativeCoin)
enabledFuncs = append(enabledFuncs, mintFunc)
// Construct the contract with no fallback function.
contract, err := contract.NewStatefulPrecompileContract(nil, enabledFuncs)
// TODO: Change this to be returned as an error after refactoring this precompile
// to use the new precompile template.
if err != nil {
panic(err)
}
return contract
}