diff --git a/.gitignore b/.gitignore index 10cf122..1edea84 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.tsbuildinfo /.coverage /.db +/.ethstatedb* /.nyc_output /.test-data /bin diff --git a/README.md b/README.md index 324ed73..694f567 100644 --- a/README.md +++ b/README.md @@ -77,3 +77,7 @@ COMMANDS help display help for ethstatedb ``` + +# More information +1. When playing with data generated by geth, make sure to stop geth so that it flushes all data to database. This command by default copies the data to a temp location, i.e. `.ethstatedb`, to avoid messing up the original database. However, you can use `--skip-copy` to use original database. +2. To get stateRoot, use `geth attach ipc://path/to/geth.ipc` to connect to a node. Then use `eth.getBlock(eth.blockNumber).stateRoot` to get stateRoot for the latest block. You can change `eth.blockNumber` to be any one. \ No newline at end of file diff --git a/package.json b/package.json index f5daa0a..0240f35 100644 --- a/package.json +++ b/package.json @@ -85,12 +85,11 @@ "lines": 90, "statements": 90, "functions": 90, - "branches": 50, + "branches": 55, "reporter": [ "lcov", "text", - "text-summary", - "cobertura" + "text-summary" ], "report-dir": ".coverage", "exclude": [ diff --git a/src/commands/account.ts b/src/commands/account.ts index c7332c0..fa2c23c 100644 --- a/src/commands/account.ts +++ b/src/commands/account.ts @@ -2,6 +2,7 @@ import {Command, flags} from '@oclif/command' import cli from 'cli-ux' import EAccount from 'ethereumjs-account' import {BN, KECCAK256_NULL_S, KECCAK256_RLP_S} from 'ethereumjs-util' +import * as fs from 'fs-extra' export default class Account extends Command { static description = 'load information of an account' @@ -23,6 +24,15 @@ export default class Account extends Command { required: true, description: 'value of stateRoot', }), + 'skip-copy': flags.boolean({ + required: false, + description: + 'skip copying database to .ethstatedb. By default, database is copied to avoid messing the original database.', + }), + 'work-dir': flags.string({ + required: false, + description: 'directory to pull down database', + }), } static args = [ @@ -39,7 +49,15 @@ export default class Account extends Command { const level = require('level') const {args, flags} = this.parse(Account) - const db = level(flags.dbdir) + + let workDir = flags.dbdir + if (!flags['skip-copy']) { + workDir = flags['work-dir'] || '.ethstatedb' + await fs.emptyDir(workDir) + await fs.copy(flags.dbdir, workDir) + } + + const db = level(workDir) const trie = new Trie(db, flags.root) const found = await new Promise((resolve, reject) => { diff --git a/test/commands/account.test.ts b/test/commands/account.test.ts index f9951d0..72bf4d0 100644 --- a/test/commands/account.test.ts +++ b/test/commands/account.test.ts @@ -1,34 +1,65 @@ import {expect, test} from '@oclif/test' +import * as fs from 'fs-extra' import * as os from 'os' -import {BlockChainConfig, runBlockchain, SupportedHardforkType} from '../utils/run-blockchain' +import {runBlockchain, SupportedHardforkType} from '../utils/run-blockchain' const testData = require('../fixtures/test-blockchain-data.json') -const testOnEthereum = test.register('runEVM', (config: BlockChainConfig) => { - return { - async run(ctx: any) { - ctx.evm = await runBlockchain(config) - } - } -}) - describe('account', () => { - testOnEthereum - .runEVM({dbPath: '.db/chaindata', + before(async () => { + const evm = await runBlockchain({ + dbPath: '.db/chaindata', cachePath: '.db/cachedata', validate: true, data: testData, hardfork: SupportedHardforkType.byzantium, - reset: true}) + reset: true + }) + + await new Promise((resolve, reject) => { + evm.blockchain.db.close((err: Error, result: any) => { + if (err) reject(err) + + resolve(result) + }) + }) + }) + + test + .stdout() + .do(() => { + fs.copySync('.db/chaindata', '.db/chaindata-1') + }) + .command(['account', + '--dbdir', '.db/chaindata-1', + '--root', '0xecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217', + '--work-dir', '.ethstatedb-1', + '0x095e7baea6a6c7c4c2dfeb977efac326af552d87']) + .it('runs account', async ctx => { + const result: any = {} + ctx.stdout.split(os.EOL).map((element: string) => { + const tokens = element.trim().split(/\s+/) + if (tokens.length > 1) { + result[tokens[0]] = tokens[1] + } + }) + + expect(result.Balance).to.equal('10') + }) + + test .stdout() - .do(ctx => { - ctx.evm.blockchain.db.close() + .do(() => { + fs.copySync('.db/chaindata', '.db/chaindata-2') + fs.removeSync('.ethstatedb') }) .command(['account', - '--dbdir', '.db/chaindata', - '--root', '0xecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217', '0x095e7baea6a6c7c4c2dfeb977efac326af552d87']) - .it('runs account', ctx => { + '--dbdir', '.db/chaindata-2', + '--root', '0xecc60e00b3fe5ce9f6e1a10e5469764daf51f1fe93c22ec3f9a7583a80357217', + '--skip-copy', + '0x095e7baea6a6c7c4c2dfeb977efac326af552d87']) + .it('runs account --skip-copy', async ctx => { const result: any = {} ctx.stdout.split(os.EOL).map((element: string) => { const tokens = element.trim().split(/\s+/) @@ -38,5 +69,6 @@ describe('account', () => { }) expect(result.Balance).to.equal('10') + expect(await fs.pathExists('.ethstatedb')).to.be.false }) }) diff --git a/test/utils/run-blockchain.ts b/test/utils/run-blockchain.ts index 12eb37f..514cefa 100644 --- a/test/utils/run-blockchain.ts +++ b/test/utils/run-blockchain.ts @@ -1,10 +1,10 @@ import Account from 'ethereumjs-account' import * as ethereumUtil from 'ethereumjs-util' +import * as fs from 'fs-extra' import leveldown from 'leveldown' import levelup from 'levelup' import * as util from 'util' -const fs = require('fs-extra') const ethjsUtil = require('ethjs-util') const Trie = require('merkle-patricia-tree/secure') const Block = require('ethereumjs-block')