Skip to content

Commit b88cf95

Browse files
authored
Merge pull request #1026 from ethereumjs/eip-2565
Add EIP 2565, ModExp precompile gas cost
2 parents 86fb51d + 1256bcf commit b88cf95

File tree

6 files changed

+199
-13
lines changed

6 files changed

+199
-13
lines changed

packages/common/src/eips/2565.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "EIP-2565",
3+
"number": 2565,
4+
"comment": "ModExp gas cost",
5+
"url": "https://eips.ethereum.org/EIPS/eip-2565",
6+
"status": "Last call",
7+
"minimumHardfork": "byzantium",
8+
"gasConfig": {},
9+
"gasPrices": {
10+
"modexpGquaddivisor": {
11+
"v": 3,
12+
"d": "Gquaddivisor from modexp precompile for gas calculation"
13+
}
14+
},
15+
"vm": {},
16+
"pow": {}
17+
}

packages/common/src/eips/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ import { eipsType } from './../types'
33
export const EIPs: eipsType = {
44
2315: require('./2315.json'),
55
2537: require('./2537.json'),
6+
2565: require('./2565.json'),
67
2929: require('./2929.json'),
78
}

packages/vm/lib/evm/precompiles/05-modexp.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ function multComplexity(x: BN): BN {
2222
}
2323
}
2424

25+
function multComplexityEIP2565(x: BN): BN {
26+
const words = x.addn(7).divn(8)
27+
return words.mul(words)
28+
}
29+
2530
function getAdjustedExponentLength(data: Buffer): BN {
2631
let expBytesStart
2732
try {
@@ -86,7 +91,23 @@ export default function (opts: PrecompileInput): ExecResult {
8691
maxLen = mLen
8792
}
8893
const Gquaddivisor = opts._common.param('gasPrices', 'modexpGquaddivisor')
89-
const gasUsed = adjustedELen.mul(multComplexity(maxLen)).divn(Gquaddivisor)
94+
let gasUsed
95+
96+
const bStart = new BN(96)
97+
const bEnd = bStart.add(bLen)
98+
const eStart = bEnd
99+
const eEnd = eStart.add(eLen)
100+
const mStart = eEnd
101+
const mEnd = mStart.add(mLen)
102+
103+
if (!opts._common.eips().includes(2565)) {
104+
gasUsed = adjustedELen.mul(multComplexity(maxLen)).divn(Gquaddivisor)
105+
} else {
106+
gasUsed = adjustedELen.mul(multComplexityEIP2565(maxLen)).divn(Gquaddivisor)
107+
if (gasUsed.ltn(200)) {
108+
gasUsed = new BN(200)
109+
}
110+
}
90111

91112
if (opts.gasLimit.lt(gasUsed)) {
92113
return OOGResult(opts.gasLimit)
@@ -95,7 +116,7 @@ export default function (opts: PrecompileInput): ExecResult {
95116
if (bLen.isZero()) {
96117
return {
97118
gasUsed,
98-
returnValue: new BN(0).toArrayLike(Buffer, 'be', 1),
119+
returnValue: new BN(0).toArrayLike(Buffer, 'be', mLen.toNumber()),
99120
}
100121
}
101122

@@ -113,21 +134,14 @@ export default function (opts: PrecompileInput): ExecResult {
113134
return OOGResult(opts.gasLimit)
114135
}
115136

116-
const bStart = new BN(96)
117-
const bEnd = bStart.add(bLen)
118-
const eStart = bEnd
119-
const eEnd = eStart.add(eLen)
120-
const mStart = eEnd
121-
const mEnd = mStart.add(mLen)
137+
const B = new BN(setLengthRight(data.slice(bStart.toNumber(), bEnd.toNumber()), bLen.toNumber()))
138+
const E = new BN(setLengthRight(data.slice(eStart.toNumber(), eEnd.toNumber()), eLen.toNumber()))
139+
const M = new BN(setLengthRight(data.slice(mStart.toNumber(), mEnd.toNumber()), mLen.toNumber()))
122140

123141
if (mEnd.gt(maxInt)) {
124142
return OOGResult(opts.gasLimit)
125143
}
126144

127-
const B = new BN(setLengthRight(data.slice(bStart.toNumber(), bEnd.toNumber()), bLen.toNumber()))
128-
const E = new BN(setLengthRight(data.slice(eStart.toNumber(), eEnd.toNumber()), eLen.toNumber()))
129-
const M = new BN(setLengthRight(data.slice(mStart.toNumber(), mEnd.toNumber()), mLen.toNumber()))
130-
131145
let R
132146
if (M.isZero()) {
133147
R = new BN(0)

packages/vm/lib/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export default class VM extends AsyncEventEmitter {
163163

164164
if (opts.common) {
165165
//EIPs
166-
const supportedEIPs = [2537, 2929]
166+
const supportedEIPs = [2537, 2565, 2929]
167167
for (const eip of opts.common.eips()) {
168168
if (!supportedEIPs.includes(eip)) {
169169
throw new Error(`${eip} is not supported by the VM`)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import tape from 'tape'
2+
import { Address, BN } from 'ethereumjs-util'
3+
import Common from '@ethereumjs/common'
4+
import VM from '../../../lib'
5+
6+
// See https://github.com/holiman/go-ethereum/blob/2c99023b68c573ba24a5b01db13e000bd9b82417/core/vm/testdata/precompiles/modexp_eip2565.json
7+
const testData = require('./eip-2565-testdata.json')
8+
9+
tape('EIP-2565 ModExp gas cost tests', (t) => {
10+
t.test('Test return data, gas cost and execution status against testdata', async (st) => {
11+
const common = new Common({ chain: 'mainnet', hardfork: 'byzantium', eips: [2565] })
12+
const vm = new VM({ common: common })
13+
14+
for (const test of testData) {
15+
const testName = test.Name
16+
const to = new Address(Buffer.from('0000000000000000000000000000000000000005', 'hex'))
17+
const result = await vm.runCall({
18+
caller: Address.zero(),
19+
gasLimit: new BN(0xffffffffff),
20+
to,
21+
value: new BN(0),
22+
data: Buffer.from(test.Input, 'hex'),
23+
})
24+
25+
if (!result.execResult.gasUsed.eq(new BN(test.Gas))) {
26+
st.fail(
27+
`[${testName}]: Gas usage incorrect, expected ${
28+
test.Gas
29+
}, got ${result.execResult.gasUsed.toNumber()}`
30+
)
31+
continue
32+
}
33+
34+
if (result.execResult.exceptionError) {
35+
st.fail(`[${testName}]: Call should not fail`)
36+
continue
37+
}
38+
39+
if (!result.execResult.returnValue.equals(Buffer.from(test.Expected, 'hex'))) {
40+
console.log(result.execResult.returnValue.toString('hex'))
41+
st.fail(`[${testName}]: Return value not the expected value`)
42+
continue
43+
}
44+
45+
st.pass(`[${testName}]: Call produced the expected results`)
46+
}
47+
48+
st.end()
49+
})
50+
})

0 commit comments

Comments
 (0)