Skip to content

Commit 9cbf19b

Browse files
Handle balance overflow as evm exception (ethereumjs#720)
* Handle balance overflow as evm exception error * vm: Lowercase exception messages * vm: renaming error message variable Co-authored-by: Everton Fraga <ev@ethereum.org>
1 parent b24a74a commit 9cbf19b

File tree

3 files changed

+55
-13
lines changed

3 files changed

+55
-13
lines changed

packages/vm/lib/evm/evm.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,24 @@ export default class EVM {
151151
// Load `to` account
152152
const toAccount = await this._state.getAccount(message.to)
153153
// Add tx value to the `to` account
154+
let errorMessage
154155
if (!message.delegatecall) {
155-
await this._addToBalance(toAccount, message)
156+
try {
157+
await this._addToBalance(toAccount, message)
158+
} catch (e) {
159+
errorMessage = e
160+
}
156161
}
157162

158163
// Load code
159164
await this._loadCode(message)
160-
if (!message.code || message.code.length === 0) {
165+
// Exit early if there's no code or value transfer overflowed
166+
if (!message.code || message.code.length === 0 || errorMessage) {
161167
return {
162168
gasUsed: new BN(0),
163169
execResult: {
164170
gasUsed: new BN(0),
171+
exceptionError: errorMessage, // Only defined if addToBalance failed
165172
returnValue: Buffer.alloc(0),
166173
},
167174
}
@@ -218,14 +225,21 @@ export default class EVM {
218225
toAccount.nonce = new BN(toAccount.nonce).addn(1).toArrayLike(Buffer)
219226

220227
// Add tx value to the `to` account
221-
await this._addToBalance(toAccount, message)
228+
let errorMessage
229+
try {
230+
await this._addToBalance(toAccount, message)
231+
} catch (e) {
232+
errorMessage = e
233+
}
222234

223-
if (!message.code || message.code.length === 0) {
235+
// Exit early if there's no contract code or value transfer overflowed
236+
if (!message.code || message.code.length === 0 || errorMessage) {
224237
return {
225238
gasUsed: new BN(0),
226239
createdAddress: message.to,
227240
execResult: {
228241
gasUsed: new BN(0),
242+
exceptionError: errorMessage, // only defined if addToBalance failed
229243
returnValue: Buffer.alloc(0),
230244
},
231245
}
@@ -383,7 +397,7 @@ export default class EVM {
383397
async _addToBalance(toAccount: Account, message: Message): Promise<void> {
384398
const newBalance = new BN(toAccount.balance).add(message.value)
385399
if (newBalance.gt(MAX_INTEGER)) {
386-
throw new Error('Value overflow')
400+
throw new VmError(ERROR.VALUE_OVERFLOW)
387401
}
388402
toAccount.balance = toBuffer(newBalance)
389403
// putAccount as the nonce may have changed for contract creation

packages/vm/lib/exceptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export enum ERROR {
1111
CREATE_COLLISION = 'create collision',
1212
STOP = 'stop',
1313
REFUND_EXHAUSTED = 'refund exhausted',
14+
VALUE_OVERFLOW = 'value overflow',
1415
}
1516

1617
export class VmError {

packages/vm/tests/api/runTx.js

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ tape('should run simple tx without errors', async (t) => {
7474
t.end()
7575
})
7676

77-
tape('should fail when account balance overflows', async t => {
77+
tape('should fail when account balance overflows (call)', async t => {
7878
const vm = new VM()
7979
const suite = setup(vm)
8080

@@ -84,11 +84,28 @@ tape('should fail when account balance overflows', async t => {
8484
await suite.putAccount(tx.from.toString('hex'), from)
8585
await suite.putAccount(tx.to, to)
8686

87-
shouldFail(t,
88-
suite.runTx({ tx }),
89-
(e) => t.equal(e.message, 'Value overflow')
90-
)
87+
const res = await suite.runTx({ tx })
88+
89+
t.equal(res.execResult.exceptionError.error, 'value overflow')
90+
t.equal(vm.stateManager._checkpointCount, 0)
91+
t.end()
92+
})
93+
94+
tape('should fail when account balance overflows (create)', async t => {
95+
const vm = new VM()
96+
const suite = setup(vm)
9197

98+
const contractAddress = Buffer.from('37d6c3cdbc9304cad74eef8e18a85ed54263b4e7', 'hex')
99+
const tx = getTransaction(true, true, '0x01', true)
100+
const from = createAccount()
101+
const to = createAccount('0x00', ethUtil.MAX_INTEGER)
102+
await suite.putAccount(tx.from.toString('hex'), from)
103+
await suite.putAccount(contractAddress, to)
104+
105+
const res = await suite.runTx({ tx })
106+
107+
t.equal(res.execResult.exceptionError.error, 'value overflow')
108+
t.equal(vm.stateManager._checkpointCount, 0)
92109
t.end()
93110
})
94111

@@ -117,15 +134,25 @@ function shouldFail (st, p, onErr) {
117134
p.then(() => st.fail('runTx didnt return any errors')).catch(onErr)
118135
}
119136

120-
function getTransaction (sign = false, calculageGas = false, value = '0x00') {
137+
function getTransaction (sign = false, calculageGas = false, value = '0x00', createContract = false) {
138+
let to = '0x0000000000000000000000000000000000000000'
139+
let data = '0x7f7465737432000000000000000000000000000000000000000000000000000000600057'
140+
141+
if (createContract){
142+
to = undefined
143+
data = '0x6080604052348015600f57600080fd5b50603e80601d6000396000f3fe6080604052600080fdfea' +
144+
'265627a7a723158204aed884a44fd1747efccba1447a2aa2d9a4b06dd6021c4a3bbb993021e0a909e' +
145+
'64736f6c634300050f0032'
146+
}
147+
121148
const privateKey = Buffer.from('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', 'hex')
122149
const txParams = {
123150
nonce: '0x00',
124151
gasPrice: 100,
125152
gasLimit: 1000,
126-
to: '0x0000000000000000000000000000000000000000',
153+
to: to,
127154
value: value,
128-
data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
155+
data: data,
129156
chainId: 3
130157
}
131158

0 commit comments

Comments
 (0)