1 | import { Trie as BaseTrie } from './baseTrie'
|
2 | import { CheckpointDB } from './checkpointDb'
|
3 |
|
4 | /**
|
5 | * Adds checkpointing to the {@link BaseTrie}
|
6 | */
|
7 | export class CheckpointTrie extends BaseTrie {
|
8 | db: CheckpointDB
|
9 |
|
10 | constructor(...args: any) {
|
11 | super(...args)
|
12 | this.db = new CheckpointDB(...args)
|
13 | }
|
14 |
|
15 | /**
|
16 | * Is the trie during a checkpoint phase?
|
17 | */
|
18 | get isCheckpoint() {
|
19 | return this.db.isCheckpoint
|
20 | }
|
21 |
|
22 | /**
|
23 | * Creates a checkpoint that can later be reverted to or committed.
|
24 | * After this is called, all changes can be reverted until `commit` is called.
|
25 | */
|
26 | checkpoint() {
|
27 | this.db.checkpoint(this.root)
|
28 | }
|
29 |
|
30 | /**
|
31 | * Commits a checkpoint to disk, if current checkpoint is not nested.
|
32 | * If nested, only sets the parent checkpoint as current checkpoint.
|
33 | * @throws If not during a checkpoint phase
|
34 | */
|
35 | async commit(): Promise<void> {
|
36 | if (!this.isCheckpoint) {
|
37 | throw new Error('trying to commit when not checkpointed')
|
38 | }
|
39 |
|
40 | await this.lock.wait()
|
41 | await this.db.commit()
|
42 | this.lock.signal()
|
43 | }
|
44 |
|
45 | /**
|
46 | * Reverts the trie to the state it was at when `checkpoint` was first called.
|
47 | * If during a nested checkpoint, sets root to most recent checkpoint, and sets
|
48 | * parent checkpoint as current.
|
49 | */
|
50 | async revert(): Promise<void> {
|
51 | if (!this.isCheckpoint) {
|
52 | throw new Error('trying to revert when not checkpointed')
|
53 | }
|
54 |
|
55 | await this.lock.wait()
|
56 | this.root = await this.db.revert()
|
57 | this.lock.signal()
|
58 | }
|
59 |
|
60 | /**
|
61 | * Returns a copy of the underlying trie with the interface of CheckpointTrie.
|
62 | * @param includeCheckpoints - If true and during a checkpoint, the copy will contain the checkpointing metadata and will use the same scratch as underlying db.
|
63 | */
|
64 | copy(includeCheckpoints = true): CheckpointTrie {
|
65 | const db = this.db.copy()
|
66 | const trie = new CheckpointTrie(db._leveldb, this.root)
|
67 | if (includeCheckpoints && this.isCheckpoint) {
|
68 | trie.db.checkpoints = [...this.db.checkpoints]
|
69 | }
|
70 | return trie
|
71 | }
|
72 | }
|