Skip to content

Commit

Permalink
option to pre-define or override state (#32)
Browse files Browse the repository at this point in the history
* option to pre-define or override state

* lint

* null instead of empty
  • Loading branch information
ermalkaleci authored Oct 31, 2022
1 parent f2370da commit 8f1d8c0
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 18 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,32 @@ Make sure you have setup Rust environment (>= 1.64).
- Use option `--output-path=<file_path>` to print out JSON file

- Run a test node

- `yarn start dev --endpoint=wss://acala-rpc-2.aca-api.network/ws`
- You have a test node running at `ws://localhost:8000`
- You can use [Polkadot.js Apps](https://polkadot.js.org/apps/) to connect to this node
- Submit any transaction to produce a new block in the in parallel reality
- (Optional) Pre-define/override state using option `--state-path=state.json`. See example state below.

```json
{
"Sudo": {
"Key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
},
"TechnicalCommittee": {
"Members": ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"]
},
"Tokens": {
"Accounts": [
[
["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", { "token": "KAR" }],
{
"free": 1000000000000000,
"reserved": 0,
"frozen": 0
}
]
]
}
}
```
8 changes: 4 additions & 4 deletions e2e/__snapshots__/dev.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Vitest Snapshot v1

exports[`dev rpc > setStroages 1`] = `"5F98oWfz2r5rcRVnP9VCndg33DAAsky3iuoBSpaPUbgN9AJn"`;
exports[`dev rpc > setStorages 1`] = `"5F98oWfz2r5rcRVnP9VCndg33DAAsky3iuoBSpaPUbgN9AJn"`;

exports[`dev rpc > setStroages 2`] = `"5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu"`;
exports[`dev rpc > setStorages 2`] = `"5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu"`;

exports[`dev rpc > setStroages 3`] = `
exports[`dev rpc > setStorages 3`] = `
{
"consumers": 0,
"data": {
Expand All @@ -19,7 +19,7 @@ exports[`dev rpc > setStroages 3`] = `
}
`;

exports[`dev rpc > setStroages 4`] = `
exports[`dev rpc > setStorages 4`] = `
{
"consumers": 0,
"data": {
Expand Down
2 changes: 1 addition & 1 deletion e2e/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { u8aToHex } from '@polkadot/util'
import { api, dev, expectJson, testingPairs } from './helper'

describe('dev rpc', () => {
it('setStroages', async () => {
it('setStorages', async () => {
const { alice, test1 } = testingPairs()

await expectJson(api.query.sudo.key()).toMatchSnapshot()
Expand Down
16 changes: 8 additions & 8 deletions src/blockchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { defaultLogger } from '../logger'
const logger = defaultLogger.child({ name: 'blockchain' })

export class Blockchain {
readonly #api: ApiPromise
readonly api: ApiPromise
readonly tasks: TaskManager
readonly #txpool: TxPool

Expand All @@ -32,7 +32,7 @@ export class Blockchain {
inherentProvider: InherentProvider,
header: { number: number; hash: string }
) {
this.#api = api
this.api = api
this.tasks = tasks
this.#head = new Block(api, this, header.number, header.hash)
this.#registerBlock(this.#head)
Expand All @@ -59,8 +59,8 @@ export class Blockchain {
return undefined
}
if (!this.#blocksByNumber[number]) {
const hash = await this.#api.rpc.chain.getBlockHash(number)
const block = new Block(this.#api, this, number, hash.toHex())
const hash = await this.api.rpc.chain.getBlockHash(number)
const block = new Block(this.api, this, number, hash.toHex())
this.#registerBlock(block)
}
return this.#blocksByNumber[number]
Expand All @@ -72,8 +72,8 @@ export class Blockchain {
}
if (!this.#blocksByHash[hash]) {
try {
const header = await this.#api.rpc.chain.getHeader(hash)
const block = new Block(this.#api, this, header.number.toNumber(), hash)
const header = await this.api.rpc.chain.getHeader(hash)
const block = new Block(this.api, this, header.number.toNumber(), hash)
this.#registerBlock(block)
} catch (e) {
logger.debug(`getBlock(${hash}) failed: ${e}`)
Expand All @@ -90,7 +90,7 @@ export class Blockchain {
Math.round(Math.random() * 100000000)
.toString(16)
.padEnd(64, '0')
const block = new Block(this.#api, this, number, hash, parent, { header, extrinsics: [], storage: parent.storage })
const block = new Block(this.api, this, number, hash, parent, { header, extrinsics: [], storage: parent.storage })
this.#blocksByHash[hash] = block
return block
}
Expand Down Expand Up @@ -122,7 +122,7 @@ export class Blockchain {
const source = '0x02' // External
const args = u8aToHex(u8aConcat(source, extrinsic, this.head.hash))
const res = await this.head.call('TaggedTransactionQueue_validate_transaction', args)
const validity: TransactionValidity = this.#api.createType('TransactionValidity', res.result)
const validity: TransactionValidity = this.api.createType('TransactionValidity', res.result)
if (validity.isOk) {
this.#txpool.submitExtrinsic(extrinsic)
return blake2AsHex(extrinsic, 256)
Expand Down
16 changes: 15 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { SetTimestamp } from './blockchain/inherents'
import { TaskManager } from './task'
import { createServer } from './server'
import { defaultLogger } from './logger'
import { existsSync, readFileSync, writeFileSync } from 'fs'
import { handler } from './rpc'
import { writeFileSync } from 'fs'
import { setStorage } from './utils/set-storage'
import assert from 'assert'

const setup = async (argv: any) => {
const port = argv.port || process.env.PORT || 8000
Expand Down Expand Up @@ -42,6 +44,14 @@ const setup = async (argv: any) => {

tasks.updateListeningPort(listeningPort)

const statePath = argv['state-path']
if (statePath) {
assert(existsSync(statePath), 'Invalid state path')
const state = JSON.parse(String(readFileSync(statePath)))
defaultLogger.trace({ state }, 'SetStorage')
await setStorage(chain, state)
}

return context
}

Expand Down Expand Up @@ -141,6 +151,10 @@ yargs(hideBin(process.argv))
desc: 'Build block mode. Default to Batch',
enum: [BuildBlockMode.Batch, BuildBlockMode.Manual, BuildBlockMode.Instant],
},
'state-path': {
desc: 'Pre-defined JSON state file path',
string: true,
},
}),
(argv) => {
setup(argv).catch((err) => {
Expand Down
15 changes: 11 additions & 4 deletions src/rpc/substrate/author.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,18 @@ const handlers: Handlers = {
unsubscribe(id)
}

context.chain.submitExtrinsic(extrinsic).then(() => {
callback({
Ready: null,
context.chain
.submitExtrinsic(extrinsic)
.then(() => {
callback({
Ready: null,
})
})
.catch((error) => {
logger.error({ error }, 'ExtrinsicFailed')
callback({ Invalid: null })
unsubscribe(id)
})
})
return id
},
author_unwatchExtrinsic: async (_context, [subid], { unsubscribe }) => {
Expand Down
File renamed without changes.
79 changes: 79 additions & 0 deletions src/utils/set-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { ApiPromise } from '@polkadot/api'
import { Metadata, StorageKey } from '@polkadot/types'
import { Registry } from '@polkadot/types/types'
import { StorageEntryMetadataLatest } from '@polkadot/types/interfaces'
import { createFunction } from '@polkadot/types/metadata/decorate/storage/createFunction'
import assert from 'assert'

import { Blockchain } from '../blockchain'

interface StorageKeyMaker {
meta: StorageEntryMetadataLatest
makeKey: (...keys: any[]) => StorageKey
}

const storageKeyMaker =
(registry: Registry, metadata: Metadata) =>
(section: string, method: string): StorageKeyMaker => {
const pallet = metadata.asLatest.pallets.filter((x) => x.name.toString() === section)[0]
assert(pallet)
const meta = pallet.storage
.unwrap()
.items.filter((x) => x.name.toString() === method)[0] as any as StorageEntryMetadataLatest
assert(meta)

const storageFn = createFunction(
registry,
{
meta,
prefix: section,
section,
method,
},
{}
)

return {
meta,
makeKey: (...keys: any[]): StorageKey => new StorageKey(registry, [storageFn, keys]),
}
}

function objectToStorageItems(
api: ApiPromise,
storage: Record<string, Record<string, any | [any, any][]>>
): [string, string | null][] {
const storageItems: [string, string | null][] = []
for (const sectionName in storage) {
const section = storage[sectionName]
for (const storageName in section) {
const storage = section[storageName]
const { makeKey, meta } = storageKeyMaker(api.registry, api.runtimeMetadata)(sectionName, storageName)
if (meta.type.isPlain) {
const key = makeKey()
storageItems.push([key.toHex(), storage ? api.createType(key.outputType, storage).toHex(true) : null])
} else {
for (const [keys, value] of storage) {
const key = makeKey(...keys)
storageItems.push([key.toHex(), value ? api.createType(key.outputType, value).toHex(true) : null])
}
}
}
}
return storageItems
}

export const setStorage = async (
chain: Blockchain,
storage: [string, string][] | Record<string, Record<string, any | Record<string, any>>>
): Promise<void> => {
let storageItems: [string, string | null][]
if (Array.isArray(storage)) {
storageItems = storage
} else {
storageItems = objectToStorageItems(chain.api, storage)
}
const block = await chain.getBlock()
assert(block)
block.pushStorageLayer().setAll(storageItems)
}

0 comments on commit 8f1d8c0

Please sign in to comment.