diff --git a/boba_examples/turing-hello-world/test/local-webserver.ts b/boba_examples/turing-hello-world/test/local-webserver.ts index f73615d0dc..8c47c72ef9 100644 --- a/boba_examples/turing-hello-world/test/local-webserver.ts +++ b/boba_examples/turing-hello-world/test/local-webserver.ts @@ -1,5 +1,4 @@ -import { BigNumber, Contract, ContractFactory, providers, Wallet, utils } from 'ethers' -import { ethers } from 'hardhat' +import { Contract, ContractFactory, providers, Wallet, utils } from 'ethers' import chai, { expect } from 'chai' import { solidity } from 'ethereum-waffle' chai.use(solidity) @@ -280,14 +279,26 @@ if (hre.network.name === "boba_local") { } }) - it("should charge extra gas for L1 calldata storage", async() => { - const g1 = (await hello.estimateGas.multArray(urlStr2, 1, 10, gasOverride)).toNumber() - const g2 = (await hello.estimateGas.multArray(urlStr2, 101, 10, gasOverride)).toNumber() + it("should charge extra gas for L1 calldata storage", async() => { + const eg1 = (await hello.estimateGas.multArray(urlStr2, 1, 10, gasOverride)).toNumber() + let tx1 = await hello.multArray(urlStr2, 1, 10, gasOverride) + const res1 = await tx1.wait() + expect(res1).to.be.ok + const ag1 = res1.gasUsed.toNumber() + + const eg2 = (await hello.estimateGas.multArray(urlStr2, 101, 10, gasOverride)).toNumber() + let tx2 = await hello.multArray(urlStr2, 101, 10, gasOverride) + const res2 = await tx2.wait() + expect(res2).to.be.ok + const ag2 = res2.gasUsed.toNumber() + // Larger calldata costs more gas inside the contract itself. We need to test for // additional usage on top of this from the L1 calldata calculation. The exact value - // depends on the L1 gas price so this test doesn't look for a specific number - expect (g2 - g1).to.be.above(110000) - }) + // depends on the L1 gas price so this test doesn't look for a specific number. + // Actual tx is a different code path than estimateGas so both are checked. + expect (eg2 - eg1).to.be.above(110000) + expect (ag2 - ag1).to.be.above(110000) + }) it("should support a large response", async() => { const nElem = 2038 @@ -353,7 +364,7 @@ if (hre.network.name === "boba_local") { helper.address ) // Change expected value if tests are added or skipped above - expect(postBalance).to.equal( utils.parseEther('0.9')) + expect(postBalance).to.equal(utils.parseEther('0.7')) }) }) } else { diff --git a/integration-tests/test/eth-l2/turing.spec.ts b/integration-tests/test/eth-l2/turing.spec.ts index d6936e115f..0eca6b2363 100644 --- a/integration-tests/test/eth-l2/turing.spec.ts +++ b/integration-tests/test/eth-l2/turing.spec.ts @@ -73,7 +73,7 @@ describe('Turing 256 Bit Random Number Test', async () => { ).attach(L1StandardBridgeAddress) /* eslint-disable */ const http = require('http') - const ip = require('ip') + const ip = require('ip') // start local server const server = (module.exports = http .createServer(async function (req, res) { @@ -114,7 +114,7 @@ describe('Turing 256 Bit Random Number Test', async () => { res.end('Expected content-type: application/json') } }) - .listen(apiPort)) + .listen(apiPort)) URL = `http://${ip.address()}:${apiPort}/echo` /* eslint-enable */ }) @@ -267,16 +267,26 @@ describe('Turing 256 Bit Random Number Test', async () => { } catch (e) { expect(e.error.toString()).to.contain('SERVER_ERROR') } + try { + await random.MixedInput(URL, 123, 999, { gasLimit: 11_000_000 }) + } catch (e) { + expect(e.error.toString()).to.contain('SERVER_ERROR') + } }) // Should reject a 2nd call from a different EVM depth. it('should disallow nested Turing calls', async () => { try { - const tr = await random.NestedRandom(1) + await random.estimateGas.NestedRandom(1) expect(1).to.equal(0) } catch (e) { expect(e.error.toString()).to.contain('SERVER_ERROR') } + try { + const tr = await random.NestedRandom(1, { gasLimit: 11_000_000 }) + } catch (e) { + expect(e.error.toString()).to.contain('SERVER_ERROR') + } }) it('should allow repeated Random calls (legacy support)', async () => { diff --git a/l2geth/core/state_processor.go b/l2geth/core/state_processor.go index e62fb363eb..45f366451e 100644 --- a/l2geth/core/state_processor.go +++ b/l2geth/core/state_processor.go @@ -124,6 +124,11 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo } } + p2 := msg.GasPrice() + if vmenv.ChainConfig().IsTuringCharge2Fork(vmenv.BlockNumber) && p2.BitLen() > 0 { + vmenv.Context.TuringGasMul = float64(l1GasPrice.Uint64()) / float64(p2.Uint64()) + } + // Determine the L2 Boba fee if users chose BOBA as the fee token feeTokenSelection := statedb.GetFeeTokenSelection(msg.From()) diff --git a/l2geth/core/vm/evm.go b/l2geth/core/vm/evm.go index cb82164b8a..9f1fdb71ad 100644 --- a/l2geth/core/vm/evm.go +++ b/l2geth/core/vm/evm.go @@ -757,6 +757,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas log.Debug("TURING ERROR: evm.Context.Turing already set") return nil, gas, ErrTuringDepth } + + // For compatibility, only apply a charge beyond the legacy size limit + if isTuring2 { + if len(evm.Context.Turing) > 160 { + feePerByte := evm.Context.TuringGasMul * 500.0 / 32.0 + turingGas = uint64(float64(len(evm.Context.Turing)) * feePerByte) + } + + if contract.Gas <= turingGas { + log.Debug("TURING ERROR: Insufficient gas for calldata", "have", contract.Gas, "need", turingGas) + return nil, 0, ErrTuringTooLong + } else { + log.Debug("TURING Deducting calldata gas", "had", contract.Gas, "len", len(evm.Context.Turing), "Mul", evm.Context.TuringGasMul, "deducting", turingGas) + contract.UseGas(turingGas) + } + } + ret, err = run(evm, contract, evm.Context.Turing, false) log.Trace("TURING REPLAY", "evm.Context.Turing", evm.Context.Turing) } diff --git a/l2geth/params/config.go b/l2geth/params/config.go index 2524d13640..b54c7c0e37 100644 --- a/l2geth/params/config.go +++ b/l2geth/params/config.go @@ -305,6 +305,36 @@ var ( // Enable the conditional logic to prevent Turing balances from reaching zero BobaOperaTestnetTuringChargeForkNum = big.NewInt(3000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaMainnetTuringCharge2ForkNum = big.NewInt(1064000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaGoerliTuringCharge2ForkNum = big.NewInt(114000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaMoonbeamTuringCharge2ForkNum = big.NewInt(1580000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaMoonbaseTuringCharge2ForkNum = big.NewInt(350000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaAvaxTuringCharge2ForkNum = big.NewInt(101200) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaFujiTuringCharge2ForkNum = big.NewInt(4000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaBnbTuringCharge2ForkNum = big.NewInt(25740000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaBnbTestnetTuringCharge2ForkNum = big.NewInt(428000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaOperaTuringCharge2ForkNum = big.NewInt(80000) + + // Enable the conditional logic to fix bug in charging for L1 Turing calldata + BobaOperaTestnetTuringCharge2ForkNum = big.NewInt(3000) ) // TrustedCheckpoint represents a set of post-processed trie roots (CHT and @@ -567,6 +597,43 @@ func (c *ChainConfig) IsTuringChargeFork(num *big.Int) bool { return true } +func (c *ChainConfig) IsTuringCharge2Fork(num *big.Int) bool { + if c.ChainID == nil { + return true + } + if c.ChainID.Cmp(OpMainnetChainID) == 0 { + return isForked(BobaMainnetTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpGoerliChainID) == 0 { + return isForked(BobaGoerliTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpMoonbeamChainID) == 0 { + return isForked(BobaMoonbeamTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpMoonbaseChainID) == 0 { + return isForked(BobaMoonbaseTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpBnbChainID) == 0 { + return isForked(BobaBnbTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpBnbTestnetChainID) == 0 { + return isForked(BobaBnbTestnetTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpAvaxChainID) == 0 { + return isForked(BobaAvaxTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpFujiChainID) == 0 { + return isForked(BobaFujiTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpOperaChainID) == 0 { + return isForked(BobaOperaTuringCharge2ForkNum, num) + } + if c.ChainID.Cmp(OpOperaTestnetChainID) == 0 { + return isForked(BobaOperaTestnetTuringCharge2ForkNum, num) + } + return true +} + func (c *ChainConfig) IsEthereumL2() bool { if os.Getenv("IS_ETHEREUM_L2") == "true" { return true