Skip to content

Commit

Permalink
✅ Test: MOre stateManager test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
William Cory authored and William Cory committed Jul 10, 2024
1 parent 372d408 commit ef08bab
Show file tree
Hide file tree
Showing 18 changed files with 713 additions and 21 deletions.
32 changes: 32 additions & 0 deletions packages/state/src/actions/__snapshots__/commit.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP

exports[`commit should clear all storage entries for the account corresponding to `address` 1`] = `
[
Map {
"0000000000000000000000000000000004277dc9" => undefined,
}
,
Map {},
]
`;

exports[`commit should clear all storage entries for the account corresponding to `address` 2`] = `
[
Map {
"0000000000000000000000000000000004277dc9" => undefined,
}
,
Map {},
]
`;

exports[`commit should clear all storage entries for the account corresponding to `address` 3`] = `
[
Map {},
Map {},
]
`;

exports[`commit should clear all storage entries for the account corresponding to `address` 4`] = `"0x886f43e0144bf4f5748e999d0178ed7e4edea8ad708e0bf26a61341e8ae91d1e"`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP

exports[`deepCopy should throw if uncommitted state 1`] = `[InternalError: Attempted to deepCopy state with uncommitted checkpoints
Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/
Version: 1.1.0.next-73]`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP

exports[`generateCanonicalGenesis should successfully generate canonical genesis 1`] = `{}`;

exports[`generateCanonicalGenesis should throw if there are uncommitted checkpoints 1`] = `[InternalError: Attempted to generateCanonicalGenesis state with uncommitted checkpoints
Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/
Version: 1.1.0.next-73]`;
159 changes: 159 additions & 0 deletions packages/state/src/actions/__snapshots__/getAccount.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP

exports[`getAccount forking Should fetch account from remote provider if not in cache and fork transport is provided 1`] = `
Account {
"balance": 121136012589291788664n,
"codeHash": Uint8Array [
197,
210,
70,
1,
134,
247,
35,
60,
146,
126,
125,
178,
220,
199,
3,
192,
229,
0,
182,
83,
202,
130,
39,
59,
123,
250,
216,
4,
93,
133,
164,
112,
],
"nonce": 654430n,
"storageRoot": Uint8Array [
86,
232,
31,
23,
27,
204,
85,
166,
255,
131,
69,
230,
146,
192,
248,
110,
91,
72,
224,
27,
153,
108,
173,
192,
1,
98,
47,
181,
227,
99,
180,
33,
],
}
`;

exports[`getAccount forking Should fetch account from remote provider if not in cache and fork transport is provided 2`] = `
Account {
"balance": 121136012589291788664n,
"codeHash": Uint8Array [
197,
210,
70,
1,
134,
247,
35,
60,
146,
126,
125,
178,
220,
199,
3,
192,
229,
0,
182,
83,
202,
130,
39,
59,
123,
250,
216,
4,
93,
133,
164,
112,
],
"nonce": 654430n,
"storageRoot": Uint8Array [
86,
232,
31,
23,
27,
204,
85,
166,
255,
131,
69,
230,
146,
192,
248,
110,
91,
72,
224,
27,
153,
108,
173,
192,
1,
98,
47,
181,
227,
99,
180,
33,
],
}
`;

exports[`getAccount forking Should return undefined and cache as undefined for empty remote account 1`] = `
{
"accountRLP": undefined,
}
`;

exports[`getAccount forking Should return undefined and cache as undefined for empty remote account 2`] = `{}`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP

exports[`getContractStorage should throw an error if the key is not 32 bytes long 1`] = `
"Storage key must be 32 bytes long. Received 2. If using numberToHex make the length 32.
Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/
Version: 1.1.0.next-73"
`;

exports[`getContractStorage forking should fetch storage from remote provider if not in cache and fork transport is provided 1`] = `
Uint8Array [
0,
]
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP

exports[`putContractStorage should throw an error if the key is not 32 bytes long 1`] = `[InternalError: Storage key must be 32 bytes long. Received 18,52
Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/
Version: 1.1.0.next-73]`;

exports[`putContractStorage should throw an error if the account does not exist 1`] = `[InternalError: cannot putContractStorage on non existing acccount! Consider checking if account exists first
Docs: https://tevm.sh/reference/tevm/errors/classes/internalerror/
Version: 1.1.0.next-73]`;
2 changes: 0 additions & 2 deletions packages/state/src/actions/commit.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { keccak256, numberToHex, toHex } from 'viem'
import { dumpCanonicalGenesis } from './dumpCannonicalGenesis.js'

// TODO we might want to sometimes prune state roots

/**
* Commits the current change-set to the instance since the
* last call to checkpoint.
Expand Down
92 changes: 85 additions & 7 deletions packages/state/src/actions/commit.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,105 @@
import { describe, expect, it } from 'bun:test'
import { beforeEach, describe, expect, it, jest } from 'bun:test'
import { createAddress } from '@tevm/address'
import { EthjsAccount } from '@tevm/utils'
import { createBaseState } from '../createBaseState.js'
import { checkpoint } from './checkpoint.js'
import { commit } from './commit.js'
import { putAccount } from './putAccount.js'

describe(commit.name, () => {
it('should clear all storage entries for the account corresponding to `address`', async () => {
// No mocks
const baseState = createBaseState({
loggingLevel: 'warn',
})
await putAccount(baseState)(createAddress(69696969), EthjsAccount.fromAccountData({ balance: 20n }))
expect(baseState.caches.storage._checkpoints).toBe(0)
expect(baseState.caches.accounts._checkpoints).toBe(0)
expect(baseState.caches.contracts._checkpoints).toBe(0)
await checkpoint(baseState)()
expect(baseState.caches.storage._checkpoints).toBe(1)
expect(baseState.caches.accounts._checkpoints).toBe(1)
expect(baseState.caches.contracts._checkpoints).toBe(1)
expect(baseState.caches.accounts._diffCache).toEqual([new Map(), new Map()])
expect(baseState.caches.accounts._diffCache).toEqual([
new Map(
Object.entries({
'0000000000000000000000000000000004277dc9': undefined,
}),
),
new Map(),
])
expect(baseState.caches.storage._diffCache).toEqual([new Map(), new Map()])
await commit(baseState)(true)
expect(baseState.caches.storage._checkpoints).toBe(1)
expect(baseState.caches.accounts._checkpoints).toBe(1)
expect(baseState.caches.contracts._checkpoints).toBe(1)
expect(baseState.caches.accounts._diffCache).toEqual([
new Map(
Object.entries({
'0000000000000000000000000000000004277dc9': undefined,
}),
),
new Map(),
])
expect(baseState.caches.storage._diffCache).toEqual([new Map(), new Map()])
expect(baseState.getCurrentStateRoot()).toEqual(
'0x886f43e0144bf4f5748e999d0178ed7e4edea8ad708e0bf26a61341e8ae91d1e',
)
})

let baseState: ReturnType<typeof createBaseState>

beforeEach(() => {
baseState = createBaseState({
loggingLevel: 'warn',
})
baseState.getCurrentStateRoot = jest.fn(() => 'existingStateRoot') as any
baseState.setCurrentStateRoot = jest.fn()
baseState.logger.debug = jest.fn()
baseState.options.onCommit = jest.fn()
baseState.stateRoots.set = jest.fn()
baseState.caches.accounts.commit = jest.fn()
baseState.caches.contracts.commit = jest.fn()
baseState.caches.storage.commit = jest.fn()
})

it('should commit to existing state root', async () => {
await checkpoint(baseState)()
await commit(baseState)()
expect(baseState.caches.storage._checkpoints).toBe(0)
expect(baseState.caches.accounts._checkpoints).toBe(0)
expect(baseState.caches.contracts._checkpoints).toBe(0)
expect(baseState.caches.accounts._diffCache).toEqual([new Map()])
expect(baseState.caches.storage._diffCache).toEqual([new Map()])

expect(baseState.getCurrentStateRoot).toHaveBeenCalled()
expect(baseState.setCurrentStateRoot).toHaveBeenCalledWith('existingStateRoot')
expect(baseState.logger.debug).toHaveBeenCalledWith('Comitting to existing state root...')
expect(baseState.stateRoots.set).toHaveBeenCalledWith('existingStateRoot', expect.any(Object))
expect(baseState.caches.accounts.commit).toHaveBeenCalled()
expect(baseState.caches.contracts.commit).toHaveBeenCalled()
expect(baseState.caches.storage.commit).toHaveBeenCalled()
expect(baseState.options.onCommit).toHaveBeenCalledWith(baseState)
})

it('should commit to a new state root', async () => {
await checkpoint(baseState)()
await commit(baseState)(true)

expect(baseState.setCurrentStateRoot).toHaveBeenCalledWith(expect.any(String))
expect(baseState.logger.debug).toHaveBeenCalledWith(
expect.objectContaining({ root: expect.any(String) }),
'Committing to new state root...',
)
expect(baseState.stateRoots.set).toHaveBeenCalledWith(expect.any(String), expect.any(Object))
expect(baseState.caches.accounts.commit).toHaveBeenCalled()
expect(baseState.caches.contracts.commit).toHaveBeenCalled()
expect(baseState.caches.storage.commit).toHaveBeenCalled()
expect(baseState.options.onCommit).toHaveBeenCalledWith(baseState)
})

it('should handle onCommit callback correctly', async () => {
const onCommitMock = jest.fn()
baseState.options.onCommit = onCommitMock

await checkpoint(baseState)()
await commit(baseState)()

expect(onCommitMock).toHaveBeenCalledWith(baseState)
})
})
24 changes: 24 additions & 0 deletions packages/state/src/actions/deepCopy.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, it } from 'bun:test'
import { InternalError } from '@tevm/errors'
import { EthjsAccount, EthjsAddress, hexToBytes, keccak256 } from '@tevm/utils'
import { createBaseState } from '../createBaseState.js'
import { checkpoint } from './checkpoint.js'
Expand Down Expand Up @@ -41,4 +42,27 @@ describe(deepCopy.name, () => {
expect(await getAccount(newState)(address)).toEqual(account)
expect(await getContractCode(newState)(address)).toEqual(hexToBytes(deployedBytecode))
})

it('should throw if uncommitted state', async () => {
const baseState = createBaseState({
loggingLevel: 'warn',
})

const address = EthjsAddress.fromString(`0x${'01'.repeat(20)}`)

const nonce = 2n
const balance = 420n
const account = new EthjsAccount(nonce, balance, undefined, hexToBytes(keccak256(deployedBytecode)))

await putAccount(baseState)(address, account)

await putContractCode(baseState)(address, hexToBytes(deployedBytecode))

await checkpoint(baseState)()

const error = await deepCopy(baseState)().catch((e) => e)

expect(error).toBeInstanceOf(InternalError)
expect(error).toMatchSnapshot()
})
})
Loading

0 comments on commit ef08bab

Please sign in to comment.