Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
166 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import { | ||
bytesToHex, | ||
bytesToUnprefixedHex, | ||
hexToBytes, | ||
randomBytes, | ||
unprefixedHexToBytes, | ||
} from '@ethereumjs/util' | ||
import { assert, describe, it } from 'vitest' | ||
|
||
import { Trie } from '../../src/index.js' | ||
import { _batch, orderBatch } from '../../src/node/batch.js' | ||
|
||
import type { BatchDBOp } from '@ethereumjs/util' | ||
|
||
describe('trie._updateNode', async () => { | ||
const keyvals = Array.from({ length: 200 }, () => { | ||
return [randomBytes(20), randomBytes(32)] | ||
}) | ||
const testKeyVal = [randomBytes(20), randomBytes(32)] | ||
const trie_1 = new Trie() | ||
const trie_2 = new Trie() | ||
for (const [key, val] of keyvals) { | ||
await trie_1.put(key, val) | ||
await trie_2.put(key, val) | ||
} | ||
const root_1A = bytesToHex(trie_1.root()) | ||
const root_2A = bytesToHex(trie_2.root()) | ||
it('should create identical tries', () => { | ||
assert.equal(root_1A, root_2A, 'roots should match') | ||
}) | ||
await trie_1.put(testKeyVal[0], testKeyVal[1]) | ||
const root_1B = bytesToHex(trie_1.root()) | ||
it('root should change after put', () => { | ||
assert.notEqual(root_1A, root_1B, 'root should change') | ||
}) | ||
const { remaining, stack, node } = await trie_1.findPath(testKeyVal[0]) | ||
const _stack = [...stack] | ||
it('should find new node after put', () => { | ||
assert.deepEqual(node?.value(), testKeyVal[1], 'node should match') | ||
}) | ||
await trie_2['_updateNode'](testKeyVal[0], testKeyVal[1], remaining, stack) | ||
it('_updateNode should change stack', () => { | ||
assert.notEqual(_stack, stack, 'stacks should change') | ||
}) | ||
const root2B = bytesToHex(trie_2.root()) | ||
it('should update trie', () => { | ||
assert.equal(root_1B, root2B, 'tries should match') | ||
}) | ||
}) | ||
|
||
describe('Nibble Order', async () => { | ||
const testNibbles = [ | ||
[0, 1, 2, 3, 4, 10], | ||
[0, 1, 2, 3, 3, 10], | ||
[0, 1, 2, 3, 2, 10], | ||
[0, 1, 2, 2, 4, 10], | ||
[0, 1, 2, 2, 3, 10], | ||
[1, 1, 0, 3, 4, 10], | ||
[2, 1, 0, 3, 4, 10], | ||
[0, 1, 0, 3, 4, 10], | ||
] | ||
const nibbleStrings = testNibbles.map((t) => { | ||
return t.map((n) => n.toString(16)).join('') | ||
}) | ||
const keys = nibbleStrings.map((str) => unprefixedHexToBytes(str)) | ||
for (const [i, k] of keys.entries()) { | ||
it('should be matching', () => { | ||
assert.equal(bytesToUnprefixedHex(k), nibbleStrings[i], 'nibble string should match') | ||
}) | ||
} | ||
const batchOP: BatchDBOp[] = keys.map((k) => { | ||
return { | ||
type: 'put', | ||
key: k, | ||
value: randomBytes(32), | ||
} | ||
}) | ||
|
||
const sortedTestNibbles = [ | ||
[0, 1, 0, 3, 4, 10], | ||
[0, 1, 2, 2, 3, 10], | ||
[0, 1, 2, 2, 4, 10], | ||
[0, 1, 2, 3, 2, 10], | ||
[0, 1, 2, 3, 3, 10], | ||
[0, 1, 2, 3, 4, 10], | ||
[1, 1, 0, 3, 4, 10], | ||
[2, 1, 0, 3, 4, 10], | ||
] | ||
const sortedNibleStrings = sortedTestNibbles.map((t) => { | ||
return t.map((n) => n.toString(16)).join('') | ||
}) | ||
const sortedKeys = sortedNibleStrings.map((str) => { | ||
return unprefixedHexToBytes(str) | ||
}) | ||
for (const [i, k] of sortedKeys.entries()) { | ||
it('should match', () => { | ||
assert.equal(bytesToUnprefixedHex(k), sortedNibleStrings[i], 'nibble string should match') | ||
}) | ||
} | ||
const inNibbleOrder = orderBatch(batchOP) | ||
|
||
const keysNibbleOrder = inNibbleOrder.map((o) => bytesToUnprefixedHex(o.key)) | ||
|
||
const smalltest = [hexToBytes('0x65'), hexToBytes('0x55')] | ||
const smallbatch: BatchDBOp[] = smalltest.map((k) => { | ||
return { | ||
type: 'put', | ||
key: k, | ||
value: randomBytes(32), | ||
} | ||
}) | ||
const sortbatch = orderBatch(smallbatch) | ||
assert.notDeepEqual(smallbatch, sortbatch, 'should sort batch') | ||
assert.notDeepEqual(keysNibbleOrder, nibbleStrings, 'keys should sort') | ||
assert.deepEqual(keysNibbleOrder, sortedNibleStrings, 'keys should sort') | ||
}) | ||
|
||
describe('Large Batch Test', async () => { | ||
const keys = Array.from({ length: 1000 }, () => randomBytes(20)) | ||
const batchOP: BatchDBOp[] = keys.map((k) => { | ||
return { | ||
type: 'put', | ||
key: k, | ||
value: randomBytes(32), | ||
} | ||
}) | ||
const trie_0 = new Trie() | ||
const trie_1 = new Trie() | ||
const trie_2 = new Trie() | ||
|
||
for (const op of batchOP) { | ||
if (op.type === 'put') { | ||
await trie_0.put(op.key, op.value) | ||
} | ||
} | ||
|
||
await trie_1.batch(batchOP) | ||
|
||
await _batch(trie_2, batchOP) | ||
|
||
it('batch should work', () => { | ||
assert.notDeepEqual(trie_0.root(), trie_0.EMPTY_TRIE_ROOT, 'trie is not empty') | ||
assert.deepEqual(trie_0.root(), trie_1.root(), 'trie roots should match (v1)') | ||
assert.deepEqual(trie_0.root(), trie_2.root(), 'trie roots should match (v2)') | ||
assert.deepEqual(trie_1.root(), trie_2.root(), 'trie roots should match (v3)') | ||
}) | ||
|
||
it('should remain same', async () => { | ||
const deleteKeys: Uint8Array[] = Array.from({ length: 100 }, () => { | ||
return batchOP[Math.floor(Math.random() * batchOP.length)].key | ||
}) | ||
const deleteBatch: BatchDBOp[] = deleteKeys.map((k) => { | ||
return { | ||
type: 'del', | ||
key: k, | ||
} | ||
}) | ||
for (const k of deleteKeys) { | ||
await trie_0.del(k) | ||
} | ||
await trie_1.batch(deleteBatch) | ||
await _batch(trie_2, deleteBatch) | ||
assert.deepEqual(trie_0.root(), trie_1.root(), 'roots should match') | ||
assert.deepEqual(trie_1.root(), trie_2.root(), 'roots should match') | ||
}) | ||
}) |