wip, l1call/l1delegatecall

This commit is contained in:
Jeffery Walsh 2023-10-26 15:43:27 -07:00
parent e0d981f6f3
commit 1e74d11f5d
11 changed files with 249 additions and 38 deletions

View file

@ -162,6 +162,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
if ctx.IsSet(utils.EthStatsURLFlag.Name) { if ctx.IsSet(utils.EthStatsURLFlag.Name) {
cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name) cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name)
} }
applyMetricConfig(ctx, &cfg) applyMetricConfig(ctx, &cfg)
return stack, cfg return stack, cfg
@ -183,6 +184,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
// CHANGE(TAIKO): register Taiko RPC APIs. // CHANGE(TAIKO): register Taiko RPC APIs.
utils.RegisterTaikoAPIs(stack, &cfg.Eth, eth) utils.RegisterTaikoAPIs(stack, &cfg.Eth, eth)
// CHANGE(TAIKO): register L1 node
registerL1Node(ctx, &cfg)
// Create gauge with geth system and build information // Create gauge with geth system and build information
if eth != nil { // The 'eth' backend may be nil in light mode if eth != nil { // The 'eth' backend may be nil in light mode
var protos []string var protos []string
@ -378,3 +382,11 @@ func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir s
return nil 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
}
}

View file

@ -194,6 +194,11 @@ var (
utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBBucketFlag,
utils.MetricsInfluxDBOrganizationFlag, utils.MetricsInfluxDBOrganizationFlag,
} }
taikoFlags = []cli.Flag{
utils.TaikoFlag,
utils.L1RPCUrlFlag,
}
) )
var app = flags.NewApp("the go-ethereum command line interface") var app = flags.NewApp("the go-ethereum command line interface")
@ -244,7 +249,7 @@ func init() {
metricsFlags, metricsFlags,
) )
// CHANGE(taiko): append Taiko flags into the original GETH flags // 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") flags.AutoEnvVars(app.Flags, "GETH")

View file

@ -1792,9 +1792,13 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
} }
// Override any default configs for hard coded networks. // Override any default configs for hard coded networks.
switch { 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): case ctx.IsSet(TaikoFlag.Name):
cfg.Genesis = core.TaikoGenesisBlock(cfg.NetworkId) cfg.Genesis = core.TaikoGenesisBlock(cfg.NetworkId)
if ctx.IsSet(L1RPCUrlFlag.Name) {
cfg.L1RPCUrl = ctx.String(L1RPCUrlFlag.Name)
}
case ctx.Bool(MainnetFlag.Name): case ctx.Bool(MainnetFlag.Name):
if !ctx.IsSet(NetworkIdFlag.Name) { if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 1 cfg.NetworkId = 1

View file

@ -12,10 +12,15 @@ import (
) )
var ( var (
TaikoFlag = cli.BoolFlag{ TaikoFlag = &cli.BoolFlag{
Name: "taiko", Name: "taiko",
Usage: "Taiko network", 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. // RegisterTaikoAPIs initializes and registers the Taiko RPC APIs.

View file

@ -39,7 +39,17 @@ import (
// contract. // contract.
type PrecompiledContract interface { type PrecompiledContract interface {
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte) ([]byte, error) // Run runs the precompiled contract 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 // 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{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{}, common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, 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 // PrecompiledContractsBLS contains the set of pre-compiled Ethereum
@ -168,13 +181,13 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// - the returned bytes, // - the returned bytes,
// - the _remaining_ gas, // - the _remaining_ gas,
// - any error that occurred // - 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) gasCost := p.RequiredGas(input)
if suppliedGas < gasCost { if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas return nil, 0, ErrOutOfGas
} }
suppliedGas -= gasCost suppliedGas -= gasCost
output, err := p.Run(input) output, err := p.Run(opts, input)
return output, suppliedGas, err return output, suppliedGas, err
} }
@ -185,7 +198,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas return params.EcrecoverGas
} }
func (c *ecrecover) Run(input []byte) ([]byte, error) { func (c *ecrecover) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) {
const ecRecoverInputLength = 128 const ecRecoverInputLength = 128
input = common.RightPadBytes(input, ecRecoverInputLength) input = common.RightPadBytes(input, ecRecoverInputLength)
@ -226,7 +239,7 @@ type sha256hash struct{}
func (c *sha256hash) RequiredGas(input []byte) uint64 { func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas 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) h := sha256.Sum256(input)
return h[:], nil return h[:], nil
} }
@ -241,7 +254,7 @@ type ripemd160hash struct{}
func (c *ripemd160hash) RequiredGas(input []byte) uint64 { func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas 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 := ripemd160.New()
ripemd.Write(input) ripemd.Write(input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
@ -257,7 +270,7 @@ type dataCopy struct{}
func (c *dataCopy) RequiredGas(input []byte) uint64 { func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas 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 return common.CopyBytes(in), nil
} }
@ -383,7 +396,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
return gas.Uint64() return gas.Uint64()
} }
func (c *bigModExp) Run(input []byte) ([]byte, error) { func (c *bigModExp) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) {
var ( var (
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(input, 32, 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 return params.Bn256AddGasIstanbul
} }
func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { func (c *bn256AddIstanbul) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) {
return runBn256Add(input) return runBn256Add(input)
} }
@ -476,7 +489,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasByzantium return params.Bn256AddGasByzantium
} }
func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { func (c *bn256AddByzantium) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) {
return runBn256Add(input) return runBn256Add(input)
} }
@ -501,7 +514,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasIstanbul return params.Bn256ScalarMulGasIstanbul
} }
func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { func (c *bn256ScalarMulIstanbul) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) {
return runBn256ScalarMul(input) return runBn256ScalarMul(input)
} }
@ -514,7 +527,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasByzantium return params.Bn256ScalarMulGasByzantium
} }
func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { func (c *bn256ScalarMulByzantium) Run(opts *TaikoRunOpts, input []byte) ([]byte, error) {
return runBn256ScalarMul(input) return runBn256ScalarMul(input)
} }
@ -569,7 +582,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul 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) return runBn256Pairing(input)
} }
@ -582,7 +595,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium 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) return runBn256Pairing(input)
} }
@ -608,7 +621,7 @@ var (
errBlake2FInvalidFinalFlag = errors.New("invalid final flag") 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) // Make sure the input is valid (correct length and final flag)
if len(input) != blake2FInputLength { if len(input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength return nil, errBlake2FInvalidInputLength
@ -662,7 +675,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G1AddGas 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. // 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). // > 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). // > 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 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. // 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). // > 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). // > 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 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. // 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). // 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). // 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 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. // 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). // > 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). // > 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 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. // 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). // > 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). // > 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 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 // 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). // > 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). // > 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 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. // 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: // > 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 // > - `128` bytes of G1 point encoding
@ -1003,7 +1016,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG1Gas 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. // 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. // > 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. // > 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 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. // 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. // > 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. // > 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. // 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 { if len(input) != blobVerifyInputLength {
return nil, errBlobVerifyInvalidInputLength return nil, errBlobVerifyInvalidInputLength
} }

View file

@ -98,7 +98,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
in := common.Hex2Bytes(test.Input) in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in) gas := p.RequiredGas(in)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { 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) t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected { } else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) 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 gas := p.RequiredGas(in) - 1
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { 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" { if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err) 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) in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in) gas := p.RequiredGas(in)
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas) _, _, err := RunPrecompiledContract(p, in, gas, nil)
if err.Error() != test.ExpectedError { if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) 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() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
copy(data, in) copy(data, in)
res, _, err = RunPrecompiledContract(p, data, reqGas) res, _, err = RunPrecompiledContract(p, data, reqGas, nil)
} }
bench.StopTimer() bench.StopTimer()
elapsed := uint64(time.Since(start)) elapsed := uint64(time.Since(start))

View file

@ -220,7 +220,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
} }
if isPrecompile { 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 { } else {
// Initialise a new contract and set the code that is to be used by the EVM. // 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. // 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 // It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile { 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 { } else {
addrCopy := addr addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM. // 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 // It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile { 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 { } else {
addrCopy := addr addrCopy := addr
// Initialise a new contract and make initialise the delegate values // 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 { 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 { } else {
// At this point, we use a copy of address. If we don't, the go compiler will // 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' // leak the 'contract' to the outer scope, and make allocation for 'contract'

137
core/vm/taiko.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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
}

View file

@ -169,6 +169,9 @@ type Config struct {
// OverrideVerkle (TODO: remove after the fork) // OverrideVerkle (TODO: remove after the fork)
OverrideVerkle *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"`
//CHANGE(taiko):
L1RPCUrl string
} }
// CreateConsensusEngine creates a consensus engine for the given chain config. // CreateConsensusEngine creates a consensus engine for the given chain config.

View file

@ -342,6 +342,7 @@ type ChainConfig struct {
// CHANGE(taiko): Taiko network flag. // 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. // EthashConfig is the consensus engine configs for proof-of-work based sealing.

View file

@ -140,6 +140,9 @@ const (
Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
IdentityBaseGas uint64 = 15 // Base price for a data copy operation IdentityBaseGas uint64 = 15 // Base price for a data copy operation
IdentityPerWordGas uint64 = 3 // Per-work 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 Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition
Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition