-
Notifications
You must be signed in to change notification settings - Fork 816
EIP-7823: Set upper bounds for MODEXP #4070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
e37d995
dc806fc
b1eb790
e64c1c1
0ab29ab
9a3c8b8
a297da9
6b48d98
9bb33b8
8e2cbb9
2602782
982d369
df5c650
0c97ecf
bdebccd
3c71394
d942db5
f6ceb9b
f76ed56
c30ab2f
c61ac14
7d91fcf
aecc4b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,34 @@ | ||
import { Common, Mainnet } from '@ethereumjs/common' | ||
import { bytesToHex, hexToBytes } from '@ethereumjs/util' | ||
import { | ||
BIGINT_1, | ||
bytesToBigInt, | ||
bytesToHex, | ||
concatBytes, | ||
hexToBytes, | ||
randomBytes, | ||
setLengthLeft, | ||
setLengthRight, | ||
toBytes, | ||
} from '@ethereumjs/util' | ||
import { assert, beforeAll, describe, it } from 'vitest' | ||
|
||
import { createEVM, getActivePrecompiles } from '../../src/index.ts' | ||
import { gasLimitCheck } from '../../src/precompiles/util.ts' | ||
|
||
import { createEVM, getActivePrecompiles } from '../../src/index.ts' | ||
import { | ||
expMod, | ||
getAdjustedExponentLength, | ||
multiplicationComplexity, | ||
multiplicationComplexityEIP2565, | ||
} from '../../src/precompiles/05-modexp.ts' | ||
import { getPrecompileName } from '../../src/precompiles/index.ts' | ||
import { testData } from './modexp-testdata.ts' | ||
|
||
import type { PrefixedHexString } from '@ethereumjs/util' | ||
import type { EVM } from '../../src/index.ts' | ||
import type { PrecompileFunc } from '../../src/precompiles/types.ts' | ||
import type { PrecompileFunc, PrecompileInput } from '../../src/precompiles/types.ts' | ||
|
||
const BIGINT_200 = BigInt(200) | ||
|
||
const fuzzerTests = testData.data as PrefixedHexString[][] | ||
describe('Precompiles: MODEXP', () => { | ||
|
@@ -51,3 +71,81 @@ describe('Precompiles: MODEXP', () => { | |
assert.strictEqual(result.executionGasUsed, gas) | ||
}) | ||
}) | ||
|
||
function enoughGas(opts: PrecompileInput): boolean { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now the "utility methods" are exported to calculate the gas fee here, but I think it would be more clean to instead write a gas calculator method inside the precompile and export that to calculate the required gas. |
||
const pName = getPrecompileName('05') | ||
const data = opts.data.length < 96 ? setLengthRight(opts.data, 96) : opts.data | ||
|
||
let adjustedELen = getAdjustedExponentLength(data, opts) | ||
if (adjustedELen < BIGINT_1) { | ||
adjustedELen = BIGINT_1 | ||
} | ||
|
||
const bLen = bytesToBigInt(data.subarray(0, 32)) | ||
const mLen = bytesToBigInt(data.subarray(64, 96)) | ||
|
||
let maxLen = bLen | ||
if (maxLen < mLen) { | ||
maxLen = mLen | ||
} | ||
const Gquaddivisor = opts.common.param('modexpGquaddivisorGas') | ||
let gasUsed | ||
|
||
if (!(opts.common.isActivatedEIP(2565) === true)) { | ||
gasUsed = (adjustedELen * multiplicationComplexity(maxLen)) / Gquaddivisor | ||
} else { | ||
gasUsed = (adjustedELen * multiplicationComplexityEIP2565(maxLen)) / Gquaddivisor | ||
if (gasUsed < BIGINT_200) { | ||
gasUsed = BIGINT_200 | ||
} | ||
} | ||
if (!(gasLimitCheck(opts, gasUsed, pName) === true)) { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
describe('Precompiles: MODEXP with EIP-7823', async () => { | ||
const common = new Common({ chain: Mainnet, eips: [7823] }) | ||
const evm = await createEVM({ | ||
common, | ||
}) | ||
const addressStr = '0000000000000000000000000000000000000005' | ||
const MODEXP = getActivePrecompiles(common).get(addressStr)! | ||
|
||
for (let i = 1020; i < 1028; i++) { | ||
const bLen = i | ||
const eLen = i | ||
const mLen = i | ||
const maxInputLen = Math.max(bLen, eLen, mLen) | ||
const base = randomBytes(bLen) | ||
const exponent = randomBytes(eLen) | ||
const modulo = randomBytes(mLen) | ||
const expected = expMod(bytesToBigInt(base), bytesToBigInt(exponent), bytesToBigInt(modulo)) | ||
const input = concatBytes( | ||
setLengthLeft(toBytes(bLen), 32), | ||
setLengthLeft(toBytes(eLen), 32), | ||
setLengthLeft(toBytes(mLen), 32), | ||
concatBytes(base, exponent, modulo), | ||
) | ||
|
||
it(`MODEXP should reject and consume all gas for inputs over 8192 bits in length - case ${maxInputLen} bytes`, async () => { | ||
const opts = { | ||
data: input, | ||
gasLimit: BigInt(0xffffffff), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there should be a sanity check to confirm that this gasLimit is enough to pay for the precompile, but that the precompile now rejects it due to the incode length limit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have added |
||
common, | ||
_EVM: evm, | ||
} | ||
|
||
// sanity check to make sure there is enough gas in order to isolate error to cases causing OOG due to size limit of inputs | ||
while (!enoughGas(opts)) opts.gasLimit *= BigInt(256) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we then have the "calculate gas" method, we can thus "just set" this as the gasLimit. Even if the gas limit is enough, then it will still run out-of-gas because of the inputs being too large. |
||
|
||
const result = await MODEXP(opts) | ||
if (maxInputLen > 1024) { | ||
assert.strictEqual(result.exceptionError?.error, 'out of gas') | ||
} else { | ||
assert.deepEqual(bytesToBigInt(result.returnValue), expected) | ||
} | ||
}) | ||
} | ||
}) |
Uh oh!
There was an error while loading. Please reload this page.