go-ethereum/core/vm/contracts_with_ctx.go
2024-01-25 02:48:19 -08:00

134 lines
4 KiB
Go

package vm
import (
"fmt"
"github.com/holiman/uint256"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
// Wrapper type which allows PrecompiledContract to be used as PrecompiledContractWithCtx
type precompileWrapper struct {
PrecompiledContract
}
func (pw precompileWrapper) Run(input []byte, ctx *precompileContext) ([]byte, error) {
return pw.PrecompiledContract.Run(input)
}
// Interface for precompiled contract with ctx object allowing for writes to state.
type PrecompiledContractWithCtx interface {
RequiredGas(input []byte) uint64
Run(input []byte, ctx *precompileContext) ([]byte, error)
}
type precompileContext struct {
*BlockContext
*params.Rules
caller common.Address
evm *EVM
}
func NewContext(caller common.Address, evm *EVM) *precompileContext {
return &precompileContext{
BlockContext: &evm.Context,
Rules: &evm.chainRules,
caller: caller,
evm: evm,
}
}
var vmBlockCtx = BlockContext{
CanTransfer: func(db StateDB, addr common.Address, amount *uint256.Int) bool {
return db.GetBalance(addr).Cmp(amount) >= 0
},
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {
panic("transfer: not implemented")
},
GetHash: func(u uint64) common.Hash {
panic("getHash: not implemented")
},
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(10),
Time: uint64(time.Now().Unix()),
}
var vmTxCtx = TxContext{
GasPrice: common.Big1,
Origin: common.HexToAddress("a11ce"),
}
// Create a global mock EVM for use in the following tests.
var mockEVM = &EVM{
Context: vmBlockCtx,
TxContext: vmTxCtx,
}
// Native token mint precompile to make bridging to native token possible.
type mint struct{}
func (c *mint) RequiredGas(input []byte) uint64 {
// TODO: determine appropriate gas cost
return 100
}
// Predetermined create2 address of whitelist contract with exclusive mint/burn privileges.
// This address assumes deployer is 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266.
const whitelistCreate2Addr = "0x5D1415C0973034d162F5FEcF19B50dA057057e29"
func (c *mint) Run(input []byte, ctx *precompileContext) ([]byte, error) {
if ctx.caller != common.HexToAddress(whitelistCreate2Addr) {
log.Error("Error parsing transfer: caller not whitelisted")
return nil, fmt.Errorf("Error parsing transfer: caller not whitelisted")
}
mintTo := common.BytesToAddress(input[0:32])
value, err := uint256.FromHex(hexutil.Encode(input[32:64]))
if err != nil {
log.Error("Error parsing transfer: unable to parse value from " + hexutil.Encode(input[32:64]))
return nil, fmt.Errorf("Error parsing transfer: unable to parse value from " + hexutil.Encode(input[32:64]))
}
// Create native token out of thin air
ctx.evm.StateDB.AddBalance(mintTo, value)
return input, nil
}
// Native token burn precompile to make bridging back to L1 possible.
type burn struct{}
func (c *burn) RequiredGas(input []byte) uint64 {
// TODO: determine appropriate gas cost
return 100
}
// Note ctx.CanTransfer method obtains an incorrect balance w.r.t "burnFrom" address,
// specifically during estimateGas. The CanTransfer check was therefore removed,
// and the calling contract is responsible for checking balance.
func (c *burn) Run(input []byte, ctx *precompileContext) ([]byte, error) {
if ctx.caller != common.HexToAddress(whitelistCreate2Addr) {
log.Error("Error parsing transfer: caller not whitelisted")
return nil, fmt.Errorf("Error parsing transfer: caller not whitelisted")
}
burnFrom := common.BytesToAddress(input[0:32])
value, err := uint256.FromHex(hexutil.Encode(input[32:64]))
if err != nil {
log.Error("Error parsing transfer: unable to parse value from " + hexutil.Encode(input[32:64]))
return nil, fmt.Errorf("Error parsing transfer: unable to parse value from " + hexutil.Encode(input[32:64]))
}
ctx.evm.StateDB.SubBalance(burnFrom, value)
return input, nil
}