Skip to content

Commit

Permalink
Verkle checkpoint db tests (#3407)
Browse files Browse the repository at this point in the history
* Update db write stats on commits

* Add unit tests for verkle checkpoint db implemenation

* Fix linting issues

* Update write stats in batch function of CheckpointDB implementations

* Update trie and verkle CheckpointDB tests

* Remove redundant stats update

---------

Co-authored-by: Gabriel Rocheleau <contact@rockwaterweb.com>
  • Loading branch information
scorbajio and gabrocheleau committed May 9, 2024
1 parent 3dd3bec commit ded3c6b
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/trie/src/db/checkpoint.ts
Expand Up @@ -248,6 +248,7 @@ export class CheckpointDB implements DB {
type: op.type,
opts: { ...op.opts, ...{ valueEncoding: this.valueEncoding } },
}
this._stats.db.writes += 1
if (op.type === 'put' && this.valueEncoding === ValueEncoding.String) {
convertedOp.value = bytesToUnprefixedHex(<Uint8Array>convertedOp.value)
}
Expand Down
92 changes: 86 additions & 6 deletions packages/trie/test/db/checkpoint.spec.ts
@@ -1,18 +1,102 @@
import { MapDB, hexToBytes, utf8ToBytes } from '@ethereumjs/util'
import { assert, describe, it } from 'vitest'
import { assert, beforeEach, describe, it } from 'vitest'

import { CheckpointDB } from '../../src/index.js'

import type { BatchDBOp } from '@ethereumjs/util'

describe('DB tests', () => {
let db: CheckpointDB
const k = utf8ToBytes('k1')
const v = utf8ToBytes('v1')
const v2 = utf8ToBytes('v2')
const v3 = utf8ToBytes('v3')

beforeEach(() => {
// Set up a new CheckpointDB instance for each test
db = new CheckpointDB({ db: new MapDB() })
})

it('should initialize with empty checkpoints', () => {
assert(db.checkpoints.length === 0)
})

it('should add a checkpoint', () => {
db.checkpoint(new Uint8Array())
assert(db.checkpoints.length === 1)
})

it('should commit the latest checkpoint', async () => {
db.checkpoint(new Uint8Array())

// Add some data to the latest checkpoint
await db.put(hexToBytes('0x123'), hexToBytes('0x456'))

await db.commit()

// Ensure that the checkpoint is removed and the data is committed
assert(db.checkpoints.length === 0)
assert(db._stats.db.writes === 1)
})

it('should revert the latest checkpoint', async () => {
const expectedRoot = new Uint8Array()

// Add a checkpoint
db.checkpoint(expectedRoot)

// Revert the checkpoint
const actualRoot = await db.revert()

// Ensure that the latest checkpoint is removed and the root is returned
assert(db.checkpoints.length === 0)
assert.equal(actualRoot, expectedRoot, 'roots should match')
})

it('should get a value', async () => {
const key1 = hexToBytes('0x123')
const val1 = hexToBytes('0x456')

// Add some data
await db.put(key1, val1)

// Get the value
const actualValue = await db.get(key1)

// Ensure that the value is correct
assert.deepEqual(actualValue, val1)
})

it('should put a value', async () => {
const key1 = hexToBytes('0x123')
const val1 = hexToBytes('0x456')

// Put a value
await db.put(key1, val1)

// Get the value
const actualValue = await db.get(key1)

// Ensure that the value is correct
assert.deepEqual(actualValue, val1)
})

it('should delete a value', async () => {
const key1 = hexToBytes('0x123')
const val1 = hexToBytes('0x456')

// Add some data
await db.put(key1, val1)

// Delete the value
await db.del(key1)

// Ensure that the value is deleted
const actualValue = await db.get(key1)
assert(actualValue === undefined, 'deleted value should be undefined')
})

it('Checkpointing: revert -> put (add)', async () => {
const db = new CheckpointDB({ db: new MapDB() })
db.checkpoint(hexToBytes('0x01'))
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before revert: v1')
Expand All @@ -21,7 +105,6 @@ describe('DB tests', () => {
})

it('Checkpointing: revert -> put (update)', async () => {
const db = new CheckpointDB({ db: new MapDB() })
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before CP: v1')
db.checkpoint(hexToBytes('0x01'))
Expand All @@ -32,7 +115,6 @@ describe('DB tests', () => {
})

it('Checkpointing: revert -> put (update) batched', async () => {
const db = new CheckpointDB({ db: new MapDB() })
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before CP: v1')
db.checkpoint(hexToBytes('0x01'))
Expand All @@ -46,7 +128,6 @@ describe('DB tests', () => {
})

it('Checkpointing: revert -> del', async () => {
const db = new CheckpointDB({ db: new MapDB() })
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before CP: v1')
db.checkpoint(hexToBytes('0x01'))
Expand All @@ -57,7 +138,6 @@ describe('DB tests', () => {
})

it('Checkpointing: nested checkpoints -> commit -> revert', async () => {
const db = new CheckpointDB({ db: new MapDB() })
await db.put(k, v)

assert.deepEqual(await db.get(k), v, 'before CP: v1')
Expand Down
1 change: 1 addition & 0 deletions packages/verkle/src/db/checkpoint.ts
Expand Up @@ -231,6 +231,7 @@ export class CheckpointDB implements DB {
type: op.type,
opts: op.opts,
}
this._stats.db.writes += 1
if (op.type === 'put') return convertedOp as PutBatch<Uint8Array, Uint8Array>
else return convertedOp as DelBatch<Uint8Array>
})
Expand Down
152 changes: 152 additions & 0 deletions packages/verkle/test/db/checkpoint.spec.ts
@@ -0,0 +1,152 @@
import { MapDB, hexToBytes, utf8ToBytes } from '@ethereumjs/util'
import { assert, beforeEach, describe, it } from 'vitest'

import { CheckpointDB } from '../../src/index.js'

import type { BatchDBOp } from '@ethereumjs/util'

describe('DB tests', () => {
let db: CheckpointDB
const k = utf8ToBytes('k1')
const v = utf8ToBytes('v1')
const v2 = utf8ToBytes('v2')
const v3 = utf8ToBytes('v3')

beforeEach(() => {
// Set up a new CheckpointDB instance for each test
db = new CheckpointDB({ db: new MapDB() })
})

it('should initialize with empty checkpoints', () => {
assert(db.checkpoints.length === 0)
})

it('should add a checkpoint', () => {
db.checkpoint(new Uint8Array())
assert(db.checkpoints.length === 1)
})

it('should commit the latest checkpoint', async () => {
// Add some data to the latest checkpoint
db.checkpoint(new Uint8Array())
await db.put(hexToBytes('0x123'), hexToBytes('0x456'))

await db.commit()

// Ensure that the checkpoint is removed and the data is committed
assert(db.checkpoints.length === 0)
assert(db._stats.db.writes === 1)
})

it('should revert the latest checkpoint', async () => {
const expectedRoot = new Uint8Array()

// Add a checkpoint
db.checkpoint(expectedRoot)

// Revert the checkpoint
const actualRoot = await db.revert()

// Ensure that the latest checkpoint is removed and the root is returned
assert(db.checkpoints.length === 0)
assert.equal(actualRoot, expectedRoot, 'roots should match')
})

it('should get a value', async () => {
const key1 = hexToBytes('0x123')
const val1 = hexToBytes('0x456')

// Add some data
await db.put(key1, val1)

// Get the value
const actualValue = await db.get(key1)

// Ensure that the value is correct
assert.deepEqual(actualValue, val1)
})

it('should put a value', async () => {
const key1 = hexToBytes('0x123')
const val1 = hexToBytes('0x456')

// Put a value
await db.put(key1, val1)

// Get the value
const actualValue = await db.get(key1)

// Ensure that the value is correct
assert.deepEqual(actualValue, val1)
})

it('should delete a value', async () => {
const key1 = hexToBytes('0x123')
const val1 = hexToBytes('0x456')

// Add some data
await db.put(key1, val1)

// Delete the value
await db.del(key1)

// Ensure that the value is deleted
const actualValue = await db.get(key1)
assert(actualValue === undefined, 'deleted value should be undefined')
})

it('Checkpointing: revert -> put (add)', async () => {
db.checkpoint(hexToBytes('0x01'))
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before revert: v1')
await db.revert()
assert.deepEqual(await db.get(k), undefined, 'after revert: null')
})

it('Checkpointing: revert -> put (update)', async () => {
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before CP: v1')
db.checkpoint(hexToBytes('0x01'))
await db.put(k, v2)
await db.put(k, v3)
await db.revert()
assert.deepEqual(await db.get(k), v, 'after revert: v1')
})

it('Checkpointing: revert -> put (update) batched', async () => {
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before CP: v1')
db.checkpoint(hexToBytes('0x01'))
const ops = [
{ type: 'put', key: k, value: v2 },
{ type: 'put', key: k, value: v3 },
] as BatchDBOp[]
await db.batch(ops)
await db.revert()
assert.deepEqual(await db.get(k), v, 'after revert: v1')
})

it('Checkpointing: revert -> del', async () => {
await db.put(k, v)
assert.deepEqual(await db.get(k), v, 'before CP: v1')
db.checkpoint(hexToBytes('0x01'))
await db.del(k)
assert.deepEqual(await db.get(k), undefined, 'before revert: undefined')
await db.revert()
assert.deepEqual(await db.get(k), v, 'after revert: v1')
})

it('Checkpointing: nested checkpoints -> commit -> revert', async () => {
await db.put(k, v)

assert.deepEqual(await db.get(k), v, 'before CP: v1')
db.checkpoint(hexToBytes('0x01'))
await db.put(k, v2)
db.checkpoint(hexToBytes('0x02'))
await db.put(k, v3)
await db.commit()
assert.deepEqual(await db.get(k), v3, 'after commit (second CP): v3')
await db.revert()
assert.deepEqual(await db.get(k), v, 'after revert (first CP): v1')
})
})

0 comments on commit ded3c6b

Please sign in to comment.