Files
2024-01-03 09:52:28 +01:00

180 lines
6.6 KiB
Go

// (c) 2021-2024, Lux Partners Limited. All rights reserved.
// See the file LICENSE for licensing terms.
package nativeminter
import (
_ "embed"
"errors"
"fmt"
"math/big"
<<<<<<< HEAD
<<<<<<< HEAD
"github.com/luxdefi/evm/precompile/allowlist"
"github.com/luxdefi/evm/precompile/contract"
"github.com/luxdefi/evm/vmerrs"
=======
>>>>>>> fd08c47 (Update import path)
"github.com/ethereum/go-ethereum/common"
=======
>>>>>>> d5328b4 (Sync upstream)
"github.com/luxdefi/evm/precompile/allowlist"
"github.com/luxdefi/evm/precompile/contract"
"github.com/luxdefi/evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
)
const (
mintInputLen = common.HashLength + common.HashLength
MintGasCost = 30_000
)
type MintNativeCoinInput struct {
Addr common.Address
Amount *big.Int
}
var (
// Singleton StatefulPrecompiledContract for minting native assets by permissioned callers.
ContractNativeMinterPrecompile contract.StatefulPrecompiledContract = createNativeMinterPrecompile()
ErrCannotMint = errors.New("non-enabled cannot mint")
ErrInvalidLen = errors.New("invalid input length for minting")
// NativeMinterRawABI contains the raw ABI of NativeMinter contract.
//go:embed contract.abi
NativeMinterRawABI string
NativeMinterABI = contract.ParseABI(NativeMinterRawABI)
)
// 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)
}
<<<<<<< HEAD
<<<<<<< HEAD
// PackMintNativeCoin packs [address] and [amount] into the appropriate arguments for mintNativeCoin.
func PackMintNativeCoin(address common.Address, amount *big.Int) ([]byte, error) {
return NativeMinterABI.Pack("mintNativeCoin", address, amount)
=======
// 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{
common.BytesToHash(address.Bytes()),
common.BigToHash(amount),
})
return res, err
>>>>>>> 9e0b39d (Update deps)
=======
// PackMintNativeCoin packs [address] and [amount] into the appropriate arguments for mintNativeCoin.
func PackMintNativeCoin(address common.Address, amount *big.Int) ([]byte, error) {
return NativeMinterABI.Pack("mintNativeCoin", address, amount)
>>>>>>> d5328b4 (Sync upstream)
}
// UnpackMintNativeCoinInput attempts to unpack [input] as address and amount.
// assumes that [input] does not include selector (omits first 4 func signature bytes)
// if [useStrictMode] is true, it will return an error if the length of [input] is not [mintInputLen]
func UnpackMintNativeCoinInput(input []byte, useStrictMode bool) (common.Address, *big.Int, error) {
// Initially we had this check to ensure that the input was the correct length.
// However solidity does not always pack the input to the correct length, and allows
// for extra padding bytes to be added to the end of the input. Therefore, we have removed
// this check with the DUpgrade. We still need to keep this check for backwards compatibility.
if useStrictMode && len(input) != mintInputLen {
return common.Address{}, nil, fmt.Errorf("%w: %d", ErrInvalidLen, len(input))
}
inputStruct := MintNativeCoinInput{}
err := NativeMinterABI.UnpackInputIntoInterface(&inputStruct, "mintNativeCoin", input, useStrictMode)
return inputStruct.Addr, inputStruct.Amount, err
}
// 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
}
useStrictMode := !contract.IsDUpgradeActivated(accessibleState)
to, amount, err := UnpackMintNativeCoinInput(input, useStrictMode)
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 contract.IsDUpgradeActivated(accessibleState) {
if remainingGas, err = contract.DeductGas(remainingGas, NativeCoinMintedEventGasCost); err != nil {
return nil, 0, err
}
topics, data, err := PackNativeCoinMintedEvent(caller, to, amount)
if err != nil {
return nil, remainingGas, err
}
stateDB.AddLog(
ContractAddress,
topics,
data,
accessibleState.GetBlockContext().Number().Uint64(),
)
}
// 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 with getters and setters for the precompile.
// Access to the getters/setters is controlled by an allow list for ContractAddress.
func createNativeMinterPrecompile() contract.StatefulPrecompiledContract {
var functions []*contract.StatefulPrecompileFunction
functions = append(functions, allowlist.CreateAllowListFunctions(ContractAddress)...)
abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{
"mintNativeCoin": mintNativeCoin,
}
for name, function := range abiFunctionMap {
method, ok := NativeMinterABI.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
}