Skip to content

Commit

Permalink
🧪 Test: more tests (#1469)
Browse files Browse the repository at this point in the history
## Description

_Concise description of proposed changes_

## Testing

Explain the quality checks that have been done on the code changes

## Additional Information

- [ ] I read the [contributing docs](../docs/contributing.md) (if this
is your first contribution)

Your ENS/address:

---------

Co-authored-by: William Cory <[email protected]>
  • Loading branch information
roninjin10 and William Cory authored Oct 1, 2024
1 parent 57e2ed3 commit 3f8119e
Show file tree
Hide file tree
Showing 15 changed files with 688 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-islands-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tevm/actions": patch
---

Fixed bug where loadState would not validate params correctly and then fail with a confusing error if state was wrong
5 changes: 5 additions & 0 deletions .changeset/wild-bats-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tevm/actions": patch
---

Fixed bug where client status would stay mining if an error gets thrown while emitting events after mining
3 changes: 0 additions & 3 deletions packages/actions/src/Call/handleEvmError.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,6 @@ export const handleRunTxError = (e) => {
if (e.message.includes("sender doesn't have enough funds to send tx.")) {
return new InsufficientBalanceError(e.message, { cause: /** @type {any}*/ (e) })
}
if (e.message.includes("sender doesn't have enough funds to send tx. The upfront cost is")) {
return new InsufficientBalanceError(e.message, { cause: /** @type {any}*/ (e) })
}
return new InternalEvmError(e.message, { cause: /** @type {any}*/ (e) })
}
if (!(e instanceof EvmError)) {
Expand Down
70 changes: 68 additions & 2 deletions packages/actions/src/LoadState/loadStateHandler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createTevmNode } from '@tevm/node'
import { createStateManager } from '@tevm/state'
import { EthjsAddress } from '@tevm/utils'
import { bytesToHex, hexToBytes } from '@tevm/utils'
import { expect, test } from 'vitest'
import { describe, expect, test } from 'vitest'
import { dumpStateHandler } from '../DumpState/dumpStateHandler.js'
import { loadStateHandler } from './loadStateHandler.js'

Expand Down Expand Up @@ -36,7 +37,7 @@ test('should load state into the state manager', async () => {

const client = { getVm: () => ({ stateManager }) } as any

await loadStateHandler(client)({ state })
await loadStateHandler(client)({ state, throwOnFail: false })

accountData = await stateManager.getAccount(address)

Expand All @@ -62,3 +63,68 @@ test('should load state into the state manager', async () => {
},
})
})

describe('loadStateHandler', () => {
test('should return errors for invalid params', async () => {
const client = createTevmNode()
const handler = loadStateHandler(client)

const result = await handler({
state: 5 as any,
throwOnFail: false,
} as any)

expect(result.errors).toBeDefined()
expect(result.errors?.length).toBeGreaterThan(0)
expect(result.errors?.[0]?.message).toMatchInlineSnapshot(`
"Invalid state: Expected object, received number
Docs: https://tevm.sh/reference/tevm/errors/classes/invalidrequesterror/
Version: 1.1.0.next-73"
`)
})

test('should throw error for unsupported state manager', async () => {
const client = createTevmNode()
const vm = await client.getVm()
// @ts-ignore - Intentionally removing the method for testing
delete vm.stateManager.generateCanonicalGenesis

const handler = loadStateHandler({ getVm: () => Promise.resolve(vm) } as any)

const result = await handler({ state: {}, throwOnFail: false })
expect(result.errors?.[0]?.message).toMatchInlineSnapshot(`
"UnexpectedError
Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/
Details: Unsupported state manager. Must use a Tevm state manager from \`@tevm/state\` package. This may indicate a bug in tevm internal code.
Version: 1.1.0.next-73"
`)
})

test('should handle error when generating genesis fails', async () => {
const client = createTevmNode()
const vm = await client.getVm()
vm.stateManager.generateCanonicalGenesis = () => {
throw new Error('Genesis generation failed')
}

const handler = loadStateHandler({ getVm: () => Promise.resolve(vm) } as any)

const result = await handler({
state: {},
throwOnFail: false,
})

expect(result.errors).toBeDefined()
expect(result.errors?.length).toBe(1)
expect(result.errors?.[0]?.message).toMatchInlineSnapshot(`
"UnexpectedError
Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/
Details: Genesis generation failed
Version: 1.1.0.next-73"
`)
expect(result.errors?.[0]?.cause?.message).toMatchInlineSnapshot(`"Genesis generation failed"`)
})
})
6 changes: 6 additions & 0 deletions packages/actions/src/LoadState/validateLoadStateParams.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export const validateLoadStateParams = (action) => {
errors.push(new InvalidRequestError(error))
})
}

if (formattedErrors.state?._errors) {
formattedErrors.state._errors.forEach((error) => {
errors.push(new InvalidRequestError(`Invalid state: ${error}`))
})
}
}
return errors
}
77 changes: 29 additions & 48 deletions packages/actions/src/Mine/mineHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,33 @@ import { validateMineParams } from './validateMineParams.js'
export const mineHandler =
(client, options = {}) =>
async ({ throwOnFail = options.throwOnFail ?? true, ...params } = {}) => {
switch (client.status) {
case 'MINING': {
const err = new MisconfiguredClientError('Mining is already in progress')
return maybeThrowOnFail(throwOnFail, { errors: [err] })
}
case 'INITIALIZING': {
await client.ready()
client.status = 'MINING'
break
}
case 'SYNCING': {
const err = new MisconfiguredClientError('Syncing not currently implemented')
return maybeThrowOnFail(throwOnFail, { errors: [err] })
}
case 'STOPPED': {
const err = new MisconfiguredClientError('Client is stopped')
return maybeThrowOnFail(throwOnFail, { errors: [err] })
}
case 'READY': {
client.status = 'MINING'
break
}
default: {
const err = new UnreachableCodeError(client.status)
return maybeThrowOnFail(throwOnFail, { errors: [err] })
}
}
try {
client.logger.debug({ throwOnFail, ...params }, 'mineHandler called with params')
const errors = validateMineParams(params)
Expand All @@ -36,43 +63,6 @@ export const mineHandler =
const pool = await client.getTxPool()
const originalVm = await client.getVm()

switch (client.status) {
case 'MINING': {
// wait for the previous mine to finish
await new Promise((resolve) => {
client.on('newBlock', async () => {
if (client.status === 'MINING') {
return
}
client.status = 'MINING'
resolve(client)
})
})
break
}
case 'INITIALIZING': {
await client.ready()
client.status = 'MINING'
break
}
case 'SYNCING': {
const err = new MisconfiguredClientError('Syncing not currently implemented')
return maybeThrowOnFail(throwOnFail, { errors: [err] })
}
case 'STOPPED': {
const err = new MisconfiguredClientError('Client is stopped')
return maybeThrowOnFail(throwOnFail, { errors: [err] })
}
case 'READY': {
client.status = 'MINING'
break
}
default: {
const err = new UnreachableCodeError(client.status)
return maybeThrowOnFail(throwOnFail, { errors: [err] })
}
}

const vm = await originalVm.deepCopy()
const receiptsManager = await client.getReceiptsManager()

Expand Down Expand Up @@ -121,15 +111,6 @@ export const mineHandler =
const txResult = await blockBuilder.addTransaction(nextTx, {
skipHardForkValidation: true,
})
if (txResult.execResult.exceptionError) {
if (txResult.execResult.exceptionError.error === 'out of gas') {
client.logger.debug(txResult.execResult.executionGasUsed, 'out of gas')
}
client.logger.debug(
txResult.execResult.exceptionError,
`There was an exception when building block for tx ${bytesToHex(nextTx.hash())}`,
)
}
receipts.push(txResult.receipt)
index++
}
Expand Down Expand Up @@ -161,14 +142,14 @@ export const mineHandler =
originalVm.evm.blockchain = vm.evm.blockchain
await originalVm.stateManager.setStateRoot(hexToBytes(vm.stateManager._baseState.getCurrentStateRoot()))

client.status = 'READY'

emitEvents(client, newBlocks, newReceipts)

return { blockHashes: newBlocks.map((b) => bytesToHex(b.hash())) }
} catch (e) {
return maybeThrowOnFail(throwOnFail, {
errors: [new InternalError(/** @type {Error} */ (e).message, { cause: e })],
})
} finally {
client.status = 'READY'
}
}
Loading

0 comments on commit 3f8119e

Please sign in to comment.