Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add feature to dry-run preimage #179

Merged
merged 1 commit into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/blockchain/block-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const getNewSlot = (digest: RawBabePreDigest, slotNumber: number) => {
return digest.toJSON()
}

const newHeader = async (head: Block) => {
export const newHeader = async (head: Block) => {
const meta = await head.meta
const parentHeader = await head.header

Expand Down
6 changes: 6 additions & 0 deletions src/blockchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,10 @@ export class Blockchain {
const inherents = await this.#inherentProvider.createInherents(head, { horizontalMessages: hrmp })
return dryRunInherents(head, inherents)
}

async getInherents(): Promise<HexString[]> {
await this.api.isReady
const inherents = await this.#inherentProvider.createInherents(this.head)
return inherents
}
}
13 changes: 11 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Blockchain, BuildBlockMode, connectParachains, connectVertical, setup,
import { configSchema } from './schema'
import { decodeKey } from './utils/decoder'
import { dryRun } from './dry-run'
import { dryRunPreimage } from './dry-run-preimage'
import { isUrl } from './utils'
import { runBlock } from './run-block'

Expand Down Expand Up @@ -88,12 +89,15 @@ yargs(hideBin(process.argv))
extrinsic: {
desc: 'Extrinsic or call to dry run. If you pass call here then address is required to fake signature',
string: true,
required: true,
},
address: {
desc: 'Address to fake sign extrinsic',
string: true,
},
preimage: {
desc: 'Preimage to dry run',
string: true,
},
at: {
desc: 'Block hash to dry run',
string: true,
Expand All @@ -110,7 +114,12 @@ yargs(hideBin(process.argv))
},
}),
async (argv) => {
await dryRun(await processArgv(argv))
const config = await processArgv(argv)
if (config.preimage) {
await dryRunPreimage(config)
} else {
await dryRun(config)
}
}
)
.command(
Expand Down
120 changes: 120 additions & 0 deletions src/dry-run-preimage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { HexString } from '@polkadot/util/types'
import { blake2AsHex } from '@polkadot/util-crypto'
import { hexToU8a } from '@polkadot/util'

import { Config } from './schema'
import { defaultLogger } from './logger'
import { generateHtmlDiffPreviewFile } from './utils/generate-html-diff'
import { newHeader } from './blockchain/block-builder'
import { openHtml } from './utils/open-html'
import { runTask, taskHandler } from './executor'
import { setStorage } from './utils/set-storage'
import { setup } from './setup'

export const dryRunPreimage = async (argv: Config) => {
const context = await setup(argv)

const extrinsic = argv['preimage']

const block = context.chain.head
const registry = await block.registry

const header = await newHeader(block)

const data = hexToU8a(extrinsic)
const hash = blake2AsHex(data, 256)

await setStorage(context.chain, {
Preimage: {
PreimageFor: [[[[hash, data.byteLength]], extrinsic]],
StatusFor: [
[
[hash],
{
Requested: {
count: 1,
len: data.byteLength,
},
},
],
],
},
Scheduler: {
Agenda: [
[
[block.number + 1],
[
{
maybeId: '0x64656d6f637261633a0000000000000000000000000000000000000000000000',
priority: 63,
call: {
Lookup: {
hash: hash,
len: data.byteLength,
},
},
origin: { system: { Root: null } },
},
],
],
],
Lookup: [[['0x64656d6f637261633a0000000000000000000000000000000000000000000000'], [block.number + 1, 0]]],
},
})

const calls: [string, HexString[]][] = [['Core_initialize_block', [header.toHex()]]]

for (const inherent of await block.chain.getInherents()) {
calls.push(['BlockBuilder_apply_extrinsic', [inherent]])
}

calls.push(['BlockBuilder_finalize_block', []])

defaultLogger.info({ preimage: registry.createType('Call', data).toHuman() }, 'Dry run preimage')

const result = await runTask(
{
wasm: await block.wasm,
calls,
storage: [],
mockSignatureHost: false,
allowUnresolvedImports: false,
},
taskHandler(block)
)

if (result.Error) {
throw new Error(result.Error)
}

const filePath = await generateHtmlDiffPreviewFile(block, result.Call.storageDiff, hash)
console.log(`Generated preview ${filePath}`)
if (argv['open']) {
openHtml(filePath)
}

// if dry-run preimage has extrinsic arguments then dry-run extrinsic
// this is usefull to test something after preimage is applied
if (argv['extrinsic']) {
await context.chain.newBlock()
const input = argv['address'] ? { call: argv['extrinsic'], address: argv['address'] } : argv['extrinsic']
const { outcome, storageDiff } = await context.chain.dryRunExtrinsic(input)
if (outcome.isErr) {
throw new Error(outcome.asErr.toString())
} else {
defaultLogger.info(outcome.toHuman(), 'dry_run_outcome')
}

const filePath = await generateHtmlDiffPreviewFile(
context.chain.head,
storageDiff,
blake2AsHex(argv['extrinsic'], 256)
)
console.log(`Generated preview ${filePath}`)
if (argv['open']) {
openHtml(filePath)
}
}

process.exit(0)
}
6 changes: 4 additions & 2 deletions src/utils/set-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ function objectToStorageItems(meta: DecoratedMeta, storage: StorageConfig): RawS

if (storageEntry.meta.type.isPlain) {
const key = new StorageKey(meta.registry, [storageEntry])
storageItems.push([key.toHex(), storage ? meta.registry.createType(key.outputType, storage).toHex(true) : null])
const type = storageEntry.meta.modifier.isOptional ? `Option<${key.outputType}>` : key.outputType
storageItems.push([key.toHex(), storage ? meta.registry.createType(type, storage).toHex() : null])
} else {
for (const [keys, value] of storage) {
const key = new StorageKey(meta.registry, [storageEntry, keys])
storageItems.push([key.toHex(), value ? meta.registry.createType(key.outputType, value).toHex(true) : null])
const type = storageEntry.meta.modifier.isOptional ? `Option<${key.outputType}>` : key.outputType
storageItems.push([key.toHex(), value ? meta.registry.createType(type, value).toHex() : null])
}
}
}
Expand Down