From 1e74d11f5d59ef45d46fbba088fea92449682f74 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 26 Oct 2023 15:43:27 -0700 Subject: [PATCH] wip, l1call/l1delegatecall --- cmd/geth/config.go | 12 ++++ cmd/geth/main.go | 7 +- cmd/utils/flags.go | 6 +- cmd/utils/taiko_flags.go | 7 +- core/vm/contracts.go | 65 ++++++++++-------- core/vm/contracts_test.go | 8 +-- core/vm/evm.go | 36 ++++++++-- core/vm/taiko.go | 137 ++++++++++++++++++++++++++++++++++++++ eth/ethconfig/config.go | 3 + params/config.go | 3 +- params/protocol_params.go | 3 + 11 files changed, 249 insertions(+), 38 deletions(-) create mode 100644 core/vm/taiko.go diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 64c569ca63..b8f488cdae 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -162,6 +162,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { if ctx.IsSet(utils.EthStatsURLFlag.Name) { cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name) } + applyMetricConfig(ctx, &cfg) return stack, cfg @@ -183,6 +184,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { // CHANGE(TAIKO): register Taiko RPC APIs. utils.RegisterTaikoAPIs(stack, &cfg.Eth, eth) + // CHANGE(TAIKO): register L1 node + registerL1Node(ctx, &cfg) + // Create gauge with geth system and build information if eth != nil { // The 'eth' backend may be nil in light mode var protos []string @@ -378,3 +382,11 @@ func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir s return nil } + +// CHANGE(TAIKO) +func registerL1Node(ctx *cli.Context, cfg *gethConfig) { + if ctx.IsSet(utils.L1RPCUrlFlag.Name) { + v := ctx.String(utils.L1RPCUrlFlag.Name) + cfg.Eth.L1RPCUrl = v + } +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index fa6221f528..4317545d5d 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -194,6 +194,11 @@ var ( utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, } + + taikoFlags = []cli.Flag{ + utils.TaikoFlag, + utils.L1RPCUrlFlag, + } ) var app = flags.NewApp("the go-ethereum command line interface") @@ -244,7 +249,7 @@ func init() { metricsFlags, ) // CHANGE(taiko): append Taiko flags into the original GETH flags - app.Flags = append(app.Flags, &utils.TaikoFlag) + app.Flags = append(app.Flags, taikoFlags...) flags.AutoEnvVars(app.Flags, "GETH") diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2bdbc8d06d..37a4301814 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1792,9 +1792,13 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } // Override any default configs for hard coded networks. switch { - // CHANGE(taiko): when --taiko flag is set, use the Taiko genesis. + // CHANGE(taiko): when --taiko flag is set, use the Taiko genesis, and set L1 + // rpc url. case ctx.IsSet(TaikoFlag.Name): cfg.Genesis = core.TaikoGenesisBlock(cfg.NetworkId) + if ctx.IsSet(L1RPCUrlFlag.Name) { + cfg.L1RPCUrl = ctx.String(L1RPCUrlFlag.Name) + } case ctx.Bool(MainnetFlag.Name): if !ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = 1 diff --git a/cmd/utils/taiko_flags.go b/cmd/utils/taiko_flags.go index d6d23c96cf..29b6eb52be 100644 --- a/cmd/utils/taiko_flags.go +++ b/cmd/utils/taiko_flags.go @@ -12,10 +12,15 @@ import ( ) var ( - TaikoFlag = cli.BoolFlag{ + TaikoFlag = &cli.BoolFlag{ Name: "taiko", Usage: "Taiko network", } + L1RPCUrlFlag = &cli.StringFlag{ + Name: "l1RpcUrl", + Usage: "Rpc url of an L1 node (must be archive)", + Required: true, + } ) // RegisterTaikoAPIs initializes and registers the Taiko RPC APIs. diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 574bb9bef6..d2cec163ae 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -38,8 +38,18 @@ import ( // requires a deterministic gas count based on the input size of the Run method of the // contract. type PrecompiledContract interface { - RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use - Run(input []byte) ([]byte, error) // Run runs the precompiled contract + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(opt *TaikoRunOpts, input []byte) ([]byte, error) // Run runs the precompiled contract +} + +// CHANGE(TAIKO): TaikoL1RunOpts is an opt added to the PrecompiledContract Run method +// to allow the taiko-specific precompiles to have access to additional necessary +// params. +type TaikoRunOpts struct { + L1RPCUrl string + StateDB StateDB + Interpreter *EVMInterpreter + Caller ContractRef } // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum @@ -105,6 +115,9 @@ var PrecompiledContractsCancun = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{9}): &blake2F{}, common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, + // CHANGE(taik): l1call and l1delegatecall precompiles + common.BytesToAddress([]byte{41}): &l1Call{}, + common.BytesToAddress([]byte{42}): &l1DelegateCall{}, } // PrecompiledContractsBLS contains the set of pre-compiled Ethereum @@ -168,13 +181,13 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, opts *TaikoRunOpts) (ret []byte, remainingGas uint64, err error) { gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas } suppliedGas -= gasCost - output, err := p.Run(input) + output, err := p.Run(opts, input) return output, suppliedGas, err } @@ -185,7 +198,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(input []byte) ([]byte, error) { +func (c *ecrecover) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { const ecRecoverInputLength = 128 input = common.RightPadBytes(input, ecRecoverInputLength) @@ -226,7 +239,7 @@ type sha256hash struct{} func (c *sha256hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas } -func (c *sha256hash) Run(input []byte) ([]byte, error) { +func (c *sha256hash) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { h := sha256.Sum256(input) return h[:], nil } @@ -241,7 +254,7 @@ type ripemd160hash struct{} func (c *ripemd160hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas } -func (c *ripemd160hash) Run(input []byte) ([]byte, error) { +func (c *ripemd160hash) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { ripemd := ripemd160.New() ripemd.Write(input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil @@ -257,7 +270,7 @@ type dataCopy struct{} func (c *dataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(in []byte) ([]byte, error) { +func (c *dataCopy) Run(opts *TaikoRunOpts, in []byte) ([]byte, error) { return common.CopyBytes(in), nil } @@ -383,7 +396,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(input []byte) ([]byte, error) { +func (c *bigModExp) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { var ( baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() @@ -463,7 +476,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256AddIstanbul) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { return runBn256Add(input) } @@ -476,7 +489,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256AddByzantium) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { return runBn256Add(input) } @@ -501,7 +514,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulIstanbul) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { return runBn256ScalarMul(input) } @@ -514,7 +527,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulByzantium) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { return runBn256ScalarMul(input) } @@ -569,7 +582,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256PairingIstanbul) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { return runBn256Pairing(input) } @@ -582,7 +595,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256PairingByzantium) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { return runBn256Pairing(input) } @@ -608,7 +621,7 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(input []byte) ([]byte, error) { +func (c *blake2F) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Make sure the input is valid (correct length and final flag) if len(input) != blake2FInputLength { return nil, errBlake2FInvalidInputLength @@ -662,7 +675,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G1Add) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 G1Add precompile. // > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). // > Output is an encoding of addition operation result - single G1 point (`128` bytes). @@ -700,7 +713,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G1MulGas } -func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) { +func (c *bls12381G1Mul) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 G1Mul precompile. // > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiplication operation result - single G1 point (`128` bytes). @@ -750,7 +763,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 } -func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) { +func (c *bls12381G1MultiExp) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 G1MultiExp precompile. // G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). @@ -793,7 +806,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G2Add) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 G2Add precompile. // > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). // > Output is an encoding of addition operation result - single G2 point (`256` bytes). @@ -831,7 +844,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G2MulGas } -func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) { +func (c *bls12381G2Mul) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 G2MUL precompile logic. // > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiplication operation result - single G2 point (`256` bytes). @@ -881,7 +894,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 } -func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) { +func (c *bls12381G2MultiExp) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 G2MultiExp precompile logic // > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). @@ -924,7 +937,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas } -func (c *bls12381Pairing) Run(input []byte) ([]byte, error) { +func (c *bls12381Pairing) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 Pairing precompile logic. // > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: // > - `128` bytes of G1 point encoding @@ -1003,7 +1016,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG1) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 Map_To_G1 precompile. // > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field. // > Output of this call is `128` bytes and is G1 point following respective encoding rules. @@ -1038,7 +1051,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG2) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { // Implements EIP-2537 Map_FP2_TO_G2 precompile logic. // > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field. // > Output of this call is `256` bytes and is G2 point following respective encoding rules. @@ -1093,7 +1106,7 @@ var ( ) // Run executes the point evaluation precompile. -func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) { +func (b *kzgPointEvaluation) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { if len(input) != blobVerifyInputLength { return nil, errBlobVerifyInvalidInputLength } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index f40e2c8f9e..04aa8a116d 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -98,7 +98,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - if res, _, err := RunPrecompiledContract(p, in, gas); err != nil { + if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) @@ -120,7 +120,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { gas := p.RequiredGas(in) - 1 t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := RunPrecompiledContract(p, in, gas, nil) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -137,7 +137,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := RunPrecompiledContract(p, in, gas, nil) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -169,7 +169,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = RunPrecompiledContract(p, data, reqGas) + res, _, err = RunPrecompiledContract(p, data, reqGas, nil) } bench.StopTimer() elapsed := uint64(time.Since(start)) diff --git a/core/vm/evm.go b/core/vm/evm.go index 2c6cc7d484..287482fb45 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -220,7 +220,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } if isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + // CHANGE(taiko): create taikorunopts + opts := &TaikoRunOpts{ + L1RPCUrl: evm.chainConfig.L1RPCUrl, + StateDB: evm.StateDB, + Interpreter: evm.interpreter, + Caller: caller, + } + ret, gas, err = RunPrecompiledContract(p, input, gas, opts) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -283,7 +290,14 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + // CHANGE(taiko): create taikorunopts + opts := &TaikoRunOpts{ + L1RPCUrl: evm.chainConfig.L1RPCUrl, + StateDB: evm.StateDB, + Interpreter: evm.interpreter, + Caller: caller, + } + ret, gas, err = RunPrecompiledContract(p, input, gas, opts) } else { addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. @@ -328,7 +342,14 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + // CHANGE(taiko): create taikorunopts + opts := &TaikoRunOpts{ + L1RPCUrl: evm.chainConfig.L1RPCUrl, + StateDB: evm.StateDB, + Interpreter: evm.interpreter, + Caller: caller, + } + ret, gas, err = RunPrecompiledContract(p, input, gas, opts) } else { addrCopy := addr // Initialise a new contract and make initialise the delegate values @@ -377,7 +398,14 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte } if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + // CHANGE(taiko): create taikorunopts + opts := &TaikoRunOpts{ + L1RPCUrl: evm.chainConfig.L1RPCUrl, + StateDB: evm.StateDB, + Interpreter: evm.interpreter, + Caller: caller, + } + ret, gas, err = RunPrecompiledContract(p, input, gas, opts) } else { // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' diff --git a/core/vm/taiko.go b/core/vm/taiko.go new file mode 100644 index 0000000000..b7f30e9660 --- /dev/null +++ b/core/vm/taiko.go @@ -0,0 +1,137 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "context" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + + "github.com/ethereum/go-ethereum/params" +) + +// l1Call implemented as a native contract. it executes read-only code from +// an L1 Contract in the context of the L1 contract. +type l1Call struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +func (c *l1Call) RequiredGas(input []byte) uint64 { + return params.L1Call +} + +// Run implements +// solidity call such as: +// l1call(abi.encodePacked(address(contractAddress), abiEncodedData)) +func (c *l1Call) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { + r, err := rpc.Dial(opts.L1RPCUrl) + if err != nil { + return nil, err + } + + client := ethclient.NewClient(r) + + // get first 32 bytes which should be the address of the contract + addr := common.BytesToAddress(input[:32]) + + // rest of bytes should be msg data + msgData := input[64:] + + // use latest blockNumber for now? + // call contract, get the response, but dont execute it on L1. + contractResponse, err := client.CallContract( + context.Background(), + ethereum.CallMsg{ + To: &addr, + Data: msgData, + }, + nil, + ) + + if err != nil { + return nil, err + } + + return contractResponse, nil +} + +// l1DelegateCall implemented as a native contract. +// it executes read-only code from an L1 contract in the context of the L2 +// calling contract. +type l1DelegateCall struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +func (c *l1DelegateCall) RequiredGas(input []byte) uint64 { + return params.L1DelegateCall +} + +// Run implements +// solidity call such as: +// l1delegetecall(abi.encodePacked(address(contractAddress), abiEncodedData)) +// defaults to latest block. TODO: get latest block synced from +// TaikoL2 contract, dont allow usage of L1 blocks that havent been +// synced to L2. +func (c *l1DelegateCall) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) { + snapshot := opts.StateDB.Snapshot() + + defer func() { + opts.StateDB.RevertToSnapshot(snapshot) + }() + + // add the L1 bytecode gotten below to this stateDB, then execute contract, then REVERT. + + r, err := rpc.Dial(opts.L1RPCUrl) + if err != nil { + return nil, err + } + + client := ethclient.NewClient(r) + + // get first 32 bytes which should be the address of the contract + addr := common.BytesToAddress(input[:32]) + + // rest of bytes should be msg data + msgData := input[32:] + + // use latest blockNumber for now? + // call contract, get the response, but dont execute it on L1. + l1ContractBytecode, err := client.CodeAt( + context.Background(), + addr, + nil, + ) + + // load this bytecode in at an address, and execute the rest of this msg.data + // at that address. + + if err != nil { + return nil, err + } + + // overwrite L2 contract with the L1 contract, at the same address. + // it will be reverted after + contract := NewContract(opts.Caller, AccountRef(addr), nil, 0) + contract.SetCallCode(&addr, opts.StateDB.GetCodeHash(addr), l1ContractBytecode) + ret, err := opts.Interpreter.Run(contract, msgData, false) + if err != nil { + return nil, err + } + + return ret, nil +} diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index ab59112b34..cc888893c7 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -169,6 +169,9 @@ type Config struct { // OverrideVerkle (TODO: remove after the fork) OverrideVerkle *uint64 `toml:",omitempty"` + + //CHANGE(taiko): + L1RPCUrl string } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/params/config.go b/params/config.go index 5b768fb550..59201552f1 100644 --- a/params/config.go +++ b/params/config.go @@ -341,7 +341,8 @@ type ChainConfig struct { IsDevMode bool `json:"isDev,omitempty"` // CHANGE(taiko): Taiko network flag. - Taiko bool `json:"taiko"` + Taiko bool `json:"taiko"` + L1RPCUrl string `json:"l1RpcUrl"` } // EthashConfig is the consensus engine configs for proof-of-work based sealing. diff --git a/params/protocol_params.go b/params/protocol_params.go index 8a5c011849..a85d69ac20 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -140,6 +140,9 @@ const ( Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation IdentityBaseGas uint64 = 15 // Base price for a data copy operation IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation + // CHANGE(taiko): Gas cost for l1call and l1delegatecall + L1Call uint64 = 1000 + L1DelegateCall uint64 = 1500 Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition