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

274 lines
10 KiB
Go

// (c) 2021-2024, Lux Partners Limited. All rights reserved.
// See the file LICENSE for licensing terms.
package allowlist
import (
_ "embed"
"errors"
"fmt"
"math/big"
<<<<<<< HEAD
<<<<<<< HEAD
"github.com/luxdefi/evm/precompile/contract"
"github.com/luxdefi/evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
=======
"github.com/ethereum/go-ethereum/common"
<<<<<<< HEAD
"github.com/luxdefi/subnet-evm/precompile/contract"
"github.com/luxdefi/subnet-evm/vmerrs"
>>>>>>> 9e0b39d (Update deps)
=======
"github.com/luxdefi/evm/precompile/contract"
"github.com/luxdefi/evm/vmerrs"
>>>>>>> fd08c47 (Update import path)
=======
"github.com/luxdefi/evm/precompile/contract"
"github.com/luxdefi/evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
>>>>>>> d5328b4 (Sync upstream)
)
// AllowList is an abstraction that allows other precompiles to manage
// which addresses can call the precompile by maintaining an allowlist
// in the storage trie.
const (
ModifyAllowListGasCost = contract.WriteGasCostPerSlot
ReadAllowListGasCost = contract.ReadGasCostPerSlot
allowListInputLen = common.HashLength
)
var (
// Error returned when an invalid write is attempted
ErrCannotModifyAllowList = errors.New("cannot modify allow list")
// AllowListRawABI contains the raw ABI of AllowList library interface.
//go:embed allowlist.abi
AllowListRawABI string
AllowListABI = contract.ParseABI(AllowListRawABI)
)
// GetAllowListStatus returns the allow list role of [address] for the precompile
// at [precompileAddr]
func GetAllowListStatus(state contract.StateDB, precompileAddr common.Address, address common.Address) Role {
// Generate the state key for [address]
addressKey := address.Hash()
return Role(state.GetState(precompileAddr, addressKey))
}
// SetAllowListRole sets the permissions of [address] to [role] for the precompile
// at [precompileAddr].
// assumes [role] has already been verified as valid.
func SetAllowListRole(stateDB contract.StateDB, precompileAddr, address common.Address, role Role) {
// Generate the state key for [address]
addressKey := address.Hash()
// Assign [role] to the address
// This stores the [role] in the contract storage with address [precompileAddr]
// and [addressKey] hash. It means that any reusage of the [addressKey] for different value
// conflicts with the same slot [role] is stored.
// Precompile implementations must use a different key than [addressKey]
stateDB.SetState(precompileAddr, addressKey, role.Hash())
}
func PackModifyAllowList(address common.Address, role Role) ([]byte, error) {
<<<<<<< HEAD
funcName, err := role.GetSetterFunctionName()
if err != nil {
return nil, err
}
=======
funcName := role.GetSetterFunctionName()
>>>>>>> d5328b4 (Sync upstream)
return AllowListABI.Pack(funcName, address)
}
func UnpackModifyAllowListInput(input []byte, r Role, useStrictMode bool) (common.Address, error) {
if useStrictMode && len(input) != allowListInputLen {
return common.Address{}, fmt.Errorf("invalid input length for modifying allow list: %d", len(input))
}
<<<<<<< HEAD
<<<<<<< HEAD
funcName, err := r.GetSetterFunctionName()
if err != nil {
return common.Address{}, err
}
var modifyAddress common.Address
err = AllowListABI.UnpackInputIntoInterface(&modifyAddress, funcName, input, useStrictMode)
return modifyAddress, err
=======
addressKey := common.BytesToHash(address.Bytes())
input = append(input, addressKey.Bytes()...)
return input, nil
}
// PackReadAllowList packs [address] into the input data to the read allow list function
func PackReadAllowList(address common.Address) []byte {
input := make([]byte, 0, contract.SelectorLen+common.HashLength)
input = append(input, readAllowListSignature...)
addressKey := common.BytesToHash(address.Bytes())
input = append(input, addressKey.Bytes()...)
return input
>>>>>>> 9e0b39d (Update deps)
=======
funcName := r.GetSetterFunctionName()
var modifyAddress common.Address
err := AllowListABI.UnpackInputIntoInterface(&modifyAddress, funcName, input, useStrictMode)
return modifyAddress, err
>>>>>>> d5328b4 (Sync upstream)
}
// createAllowListRoleSetter returns an execution function for setting the allow list status of the input address argument to [role].
// This execution function is speciifc to [precompileAddr].
func createAllowListRoleSetter(precompileAddr common.Address, role Role) contract.RunStatefulPrecompileFunc {
return func(evm contract.AccessibleState, callerAddr, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
if remainingGas, err = contract.DeductGas(suppliedGas, ModifyAllowListGasCost); err != nil {
return nil, 0, err
}
// do not use strict mode after DUpgrade
useStrictMode := !contract.IsDUpgradeActivated(evm)
modifyAddress, err := UnpackModifyAllowListInput(input, role, useStrictMode)
if err != nil {
return nil, remainingGas, err
}
if readOnly {
return nil, remainingGas, vmerrs.ErrWriteProtection
}
stateDB := evm.GetStateDB()
// Verify that the caller is an admin with permission to modify the allow list
callerStatus := GetAllowListStatus(stateDB, precompileAddr, callerAddr)
// Verify that the address we are trying to modify has a status that allows it to be modified
modifyStatus := GetAllowListStatus(stateDB, precompileAddr, modifyAddress)
if !callerStatus.CanModify(modifyStatus, role) {
return nil, remainingGas, fmt.Errorf("%w: modify address: %s, from role: %s, to role: %s", ErrCannotModifyAllowList, callerAddr, modifyStatus, role)
}
if contract.IsDUpgradeActivated(evm) {
if remainingGas, err = contract.DeductGas(remainingGas, AllowListEventGasCost); err != nil {
return nil, 0, err
}
topics, data, err := PackRoleSetEvent(role, modifyAddress, callerAddr, modifyStatus)
if err != nil {
return nil, remainingGas, err
}
stateDB.AddLog(
precompileAddr,
topics,
data,
evm.GetBlockContext().Number().Uint64(),
)
}
SetAllowListRole(stateDB, precompileAddr, modifyAddress, role)
return []byte{}, remainingGas, nil
}
}
// PackReadAllowList packs [address] into the input data to the read allow list function
func PackReadAllowList(address common.Address) ([]byte, error) {
return AllowListABI.Pack("readAllowList", address)
}
func UnpackReadAllowListInput(input []byte, useStrictMode bool) (common.Address, error) {
if useStrictMode && len(input) != allowListInputLen {
return common.Address{}, fmt.Errorf("invalid input length for read allow list: %d", len(input))
}
var modifyAddress common.Address
err := AllowListABI.UnpackInputIntoInterface(&modifyAddress, "readAllowList", input, useStrictMode)
return modifyAddress, err
}
func PackReadAllowListOutput(roleNumber *big.Int) ([]byte, error) {
return AllowListABI.PackOutput("readAllowList", roleNumber)
}
// createReadAllowList returns an execution function that reads the allow list for the given [precompileAddr].
// The execution function parses the input into a single address and returns the 32 byte hash that specifies the
// designated role of that address
func createReadAllowList(precompileAddr common.Address) contract.RunStatefulPrecompileFunc {
return func(evm contract.AccessibleState, callerAddr common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
if remainingGas, err = contract.DeductGas(suppliedGas, ReadAllowListGasCost); err != nil {
return nil, 0, err
}
// We skip the fixed length check with DUpgrade
useStrictMode := !contract.IsDUpgradeActivated(evm)
readAddress, err := UnpackReadAllowListInput(input, useStrictMode)
if err != nil {
return nil, remainingGas, err
}
role := GetAllowListStatus(evm.GetStateDB(), precompileAddr, readAddress)
packedOutput, err := PackReadAllowListOutput(role.Big())
if err != nil {
return nil, remainingGas, err
}
return packedOutput, remainingGas, nil
}
}
// CreateAllowListPrecompile returns a StatefulPrecompiledContract with R/W control of an allow list at [precompileAddr]
func CreateAllowListPrecompile(precompileAddr common.Address) contract.StatefulPrecompiledContract {
// Construct the contract with no fallback function.
allowListFuncs := CreateAllowListFunctions(precompileAddr)
contract, err := contract.NewStatefulPrecompileContract(nil, allowListFuncs)
if err != nil {
panic(err)
}
return contract
}
func CreateAllowListFunctions(precompileAddr common.Address) []*contract.StatefulPrecompileFunction {
var functions []*contract.StatefulPrecompileFunction
for name, method := range AllowListABI.Methods {
var fn *contract.StatefulPrecompileFunction
<<<<<<< HEAD
if name == "readAllowList" {
fn = contract.NewStatefulPrecompileFunction(method.ID, createReadAllowList(precompileAddr))
} else if adminFnName, _ := AdminRole.GetSetterFunctionName(); name == adminFnName {
fn = contract.NewStatefulPrecompileFunction(method.ID, createAllowListRoleSetter(precompileAddr, AdminRole))
} else if enabledFnName, _ := EnabledRole.GetSetterFunctionName(); name == enabledFnName {
fn = contract.NewStatefulPrecompileFunction(method.ID, createAllowListRoleSetter(precompileAddr, EnabledRole))
} else if noRoleFnName, _ := NoRole.GetSetterFunctionName(); name == noRoleFnName {
fn = contract.NewStatefulPrecompileFunction(method.ID, createAllowListRoleSetter(precompileAddr, NoRole))
} else if managerFnName, _ := ManagerRole.GetSetterFunctionName(); name == managerFnName {
fn = contract.NewStatefulPrecompileFunctionWithActivator(method.ID, createAllowListRoleSetter(precompileAddr, ManagerRole), contract.IsDUpgradeActivated)
} else {
=======
switch name {
case AdminRole.GetSetterFunctionName():
fn = contract.NewStatefulPrecompileFunction(method.ID, createAllowListRoleSetter(precompileAddr, AdminRole))
case EnabledRole.GetSetterFunctionName():
fn = contract.NewStatefulPrecompileFunction(method.ID, createAllowListRoleSetter(precompileAddr, EnabledRole))
case NoRole.GetSetterFunctionName():
fn = contract.NewStatefulPrecompileFunction(method.ID, createAllowListRoleSetter(precompileAddr, NoRole))
case "readAllowList":
fn = contract.NewStatefulPrecompileFunction(method.ID, createReadAllowList(precompileAddr))
case ManagerRole.GetSetterFunctionName():
fn = contract.NewStatefulPrecompileFunctionWithActivator(method.ID, createAllowListRoleSetter(precompileAddr, ManagerRole), contract.IsDUpgradeActivated)
default:
>>>>>>> d5328b4 (Sync upstream)
panic(fmt.Sprintf("unexpected method name: %s", name))
}
functions = append(functions, fn)
}
<<<<<<< HEAD
=======
>>>>>>> d5328b4 (Sync upstream)
return functions
}