diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 2ee4ebc77e..b5224dff55 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -343,6 +343,91 @@ func TestPrecompileBlsInputSize(t *testing.T) { func TestPrecompiledEcrecover(t *testing.T) { testJson("ecRecover", "01", t) } +func TestPrecompileJovianInputSizeLimits(t *testing.T) { + const ( + maxTxGas = 16_000_000 // Target limit (actual params.MaxTxGas is 16,777,216) + txBaseGas = 21_000 // params.TxGas + preimageOracleGas = 100_000 // PreimageOracle.PRECOMPILE_CALL_RESERVED_GAS + calldataOverhead = 164 // Function selector + ABI params + calldataGasPerByte = 16 // params.TxDataNonZeroGasEIP2028 + ) + + tests := []struct { + name string + precompileAddr string + maxInputSize uint64 + inputElementSize int + expectedError string + }{ + { + name: "bn256Pairing", + precompileAddr: "2f08", + maxInputSize: params.Bn256PairingMaxInputSizeJovian, + inputElementSize: 192, + expectedError: "bad elliptic curve pairing input size", + }, + { + name: "BLS G1 MSM", + precompileAddr: "2f0b", + maxInputSize: params.Bls12381G1MulMaxInputSizeJovian, + inputElementSize: 160, + expectedError: "g1 msm input size exceeds maximum", + }, + { + name: "BLS G2 MSM", + precompileAddr: "2f0d", + maxInputSize: params.Bls12381G2MulMaxInputSizeJovian, + inputElementSize: 288, + expectedError: "g2 msm input size exceeds maximum", + }, + { + name: "BLS Pairing", + precompileAddr: "2f0e", + maxInputSize: params.Bls12381PairingMaxInputSizeJovian, + inputElementSize: 384, + expectedError: "pairing input size exceeds maximum", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + addr := common.HexToAddress(tt.precompileAddr) + precompile, ok := allPrecompiles[addr] + if !ok { + t.Fatalf("precompile %s not found in allPrecompiles", tt.precompileAddr) + } + + t.Run("GasAtLimit", func(t *testing.T) { + inputSize := (int(tt.maxInputSize) / tt.inputElementSize) * tt.inputElementSize + input := make([]byte, inputSize) + + precompileGas := precompile.RequiredGas(input) + + calldataGas := uint64(calldataOverhead+inputSize) * calldataGasPerByte + + totalGas := txBaseGas + preimageOracleGas + calldataGas + precompileGas + + if totalGas >= maxTxGas { + t.Errorf("%s at Jovian limit (%d bytes) exceeds 16M gas: %d gas (over by %d)", + tt.name, inputSize, totalGas, totalGas-maxTxGas) + } + + margin := maxTxGas - totalGas + t.Logf("✓ %s: %d bytes → %d gas (margin: %d gas)", tt.name, inputSize, totalGas, margin) + }) + + t.Run("AboveLimit", func(t *testing.T) { + big := make([]byte, tt.maxInputSize+1) + testPrecompiledFailure(tt.precompileAddr, precompiledFailureTest{ + Input: common.Bytes2Hex(big), + ExpectedError: tt.expectedError, + Name: tt.name + "_jovian_input_too_big", + }, t) + }) + }) + } +} + func testJson(name, addr string, t *testing.T) { tests, err := loadJson(name) if err != nil { diff --git a/params/protocol_params.go b/params/protocol_params.go index cd49a521e0..e8faf7c4e3 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -185,6 +185,10 @@ const ( Bls12381G2MulMaxInputSizeIsthmus uint64 = 488448 // Maximum input size for BLS12-381 G2 multiple-scalar-multiply operation Bls12381PairingMaxInputSizeIsthmus uint64 = 235008 // Maximum input size for BLS12-381 pairing check + // Jovian precompile limits ensure accelerated precompiles for fault proofs stay under the 16M gas + // transaction limit introduced in Fusaka (EIP-7825). Since accelerated precompiles execute on L1 + // via PreimageOracle.loadPrecompilePreimagePart, the entire L1 transaction must stay under 16M gas: + // Total Gas = TxBaseGas (21K) + PreimageOracleGas (100K) + CalldataGas + PrecompileGas < 16M Bn256PairingMaxInputSizeJovian uint64 = 81984 // bn256Pairing limit (427 pairs) Bls12381G1MulMaxInputSizeJovian uint64 = 288960 // BLS12-381 G1 MSM limit (1,806 pairs) Bls12381G2MulMaxInputSizeJovian uint64 = 278784 // BLS12-381 G2 MSM limit (968 pairs)