Skip to content

Commit 4387490

Browse files
author
Hugh Cunningham
committed
adds StateManager wrapper for reading at past state roots
reading the confirmed balance (for any confirmation range) for an account will require reading from the state trie at a past state root adds a wrapper function, withStateRoot, to temporarily set the state root of the stateManager, execute a handler function to read from the stateManager at that root, and finally restore the root to its current value defines getAccount and getBalance on the IronfishEvm using withStateRoot some RPC methods, like eth_getAccount and eth_getBalance, will require this functionality
1 parent 244e364 commit 4387490

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

ironfish/src/evm/evm.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44
import { LegacyTransaction } from '@ethereumjs/tx'
55
import { Account as EthAccount, Address } from '@ethereumjs/util'
6+
import { generateKey } from '@ironfish/rust-nodejs'
67
import { Assert } from '../assert'
78
import { createNodeTest, useAccountFixture } from '../testUtilities'
89
import { EvmStateEncoding, HexStringEncoding } from './database'
@@ -66,4 +67,48 @@ describe('IronfishEvm', () => {
6667
expect(dbSizeAfter).toEqual(dbSizeBefore)
6768
})
6869
})
70+
71+
describe('getBalance', () => {
72+
const nodeTest = createNodeTest()
73+
74+
it('fetches the account balance at the current state root', async () => {
75+
const key = generateKey()
76+
77+
const address = Address.fromPrivateKey(Buffer.from(key.spendingKey, 'hex'))
78+
79+
const { node } = nodeTest
80+
81+
await node.chain.blockchainDb.stateManager.checkpoint()
82+
await node.chain.blockchainDb.stateManager.putAccount(address, new EthAccount(0n, 10n))
83+
await node.chain.blockchainDb.stateManager.commit()
84+
85+
const balance = await node.chain.evm.getBalance(address)
86+
87+
expect(balance).toEqual(10n)
88+
})
89+
90+
it('fetches the account balance at the past state roots', async () => {
91+
const key = generateKey()
92+
93+
const address = Address.fromPrivateKey(Buffer.from(key.spendingKey, 'hex'))
94+
95+
const { node } = nodeTest
96+
97+
await node.chain.blockchainDb.stateManager.checkpoint()
98+
await node.chain.blockchainDb.stateManager.putAccount(address, new EthAccount(0n, 10n))
99+
await node.chain.blockchainDb.stateManager.commit()
100+
101+
const stateRoot = await node.chain.blockchainDb.stateManager.getStateRoot()
102+
103+
await node.chain.blockchainDb.stateManager.checkpoint()
104+
await node.chain.blockchainDb.stateManager.putAccount(address, new EthAccount(0n, 20n))
105+
await node.chain.blockchainDb.stateManager.commit()
106+
107+
const pastBalance = await node.chain.evm.getBalance(address, stateRoot)
108+
expect(pastBalance).toEqual(10n)
109+
110+
const currentBalance = await node.chain.evm.getBalance(address)
111+
expect(currentBalance).toEqual(20n)
112+
})
113+
})
69114
})

ironfish/src/evm/evm.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44
import { Block } from '@ethereumjs/block'
55
import { EVM, Log } from '@ethereumjs/evm'
6-
import { Address, hexToBytes } from '@ethereumjs/util'
6+
import { Account, Address, hexToBytes } from '@ethereumjs/util'
77
import { RunTxOpts, RunTxResult, VM } from '@ethereumjs/vm'
88
import ContractArtifact from '@ironfish/ironfish-contracts'
99
import { Asset, generateKeyFromPrivateKey } from '@ironfish/rust-nodejs'
@@ -153,6 +153,17 @@ export class IronfishEvm {
153153

154154
return asset.id()
155155
}
156+
157+
async getAccount(address: Address, stateRoot?: Uint8Array): Promise<Account | undefined> {
158+
return this.blockchainDb.stateManager.withStateRoot(stateRoot, async () => {
159+
return this.blockchainDb.stateManager.getAccount(address)
160+
})
161+
}
162+
163+
async getBalance(address: Address, stateRoot?: Uint8Array): Promise<bigint | undefined> {
164+
const account = await this.getAccount(address, stateRoot)
165+
return account?.balance
166+
}
156167
}
157168

158169
export type EvmShield = {

ironfish/src/evm/stateManager.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,21 @@ export class IronfishStateManager extends DefaultStateManager {
2626
common: this.common,
2727
})
2828
}
29+
30+
async withStateRoot<TResult>(
31+
stateRoot: Uint8Array | undefined,
32+
handler: () => Promise<TResult>,
33+
): Promise<TResult> {
34+
const currentRoot = await this.getStateRoot()
35+
36+
if (stateRoot) {
37+
await this.setStateRoot(stateRoot)
38+
}
39+
40+
try {
41+
return handler()
42+
} finally {
43+
await this.setStateRoot(currentRoot)
44+
}
45+
}
2946
}

0 commit comments

Comments
 (0)