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

improve decoder #504

Merged
merged 2 commits into from
Nov 1, 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
17 changes: 15 additions & 2 deletions packages/chopsticks/src/plugins/run-block/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@ export interface RunBlockResponse {
/**
* Decoded storage diff. Only available when `includeParsed` is true.
*/
parsed?: any
parsed?: {
method: string
section: string
key: any[]
value: any
}
}[]
/**
* Runtime logs.
Expand Down Expand Up @@ -262,7 +267,15 @@ export const rpc = async ({ chain }: Context, [params]: [RunBlockParams]): Promi
obj.raw = { key, value }
}
if (includeParsed) {
obj.parsed = decodeKeyValue(await newBlock.meta, newBlock, key, value, false)
const decoded = decodeKeyValue(await newBlock.meta, newBlock, key, value, false)
if (decoded) {
obj.parsed = {
section: decoded.section,
method: decoded.method,
key: decoded.key?.map((x) => x.toString()),
value: decoded.value?.toString(),
}
}
}
resp.storageDiff.push(obj)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/blockchain/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,13 @@ export class Block {
/**
* Get the block storage by key.
*/
async get(key: string): Promise<string | undefined> {
async get(key: string): Promise<HexString | undefined> {
const val = await this.storage.get(key, true)
switch (val) {
case StorageValueKind.Deleted:
return undefined
default:
return val
return val as HexString
}
}

Expand Down
64 changes: 35 additions & 29 deletions packages/core/src/utils/decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const decodeKeyValue = (
const { storage, decodedKey } = decodeKey(meta, block, key)

if (!storage || !decodedKey) {
return { [key]: value }
return undefined
}

const decodeValue = () => {
Expand All @@ -69,33 +69,35 @@ export const decodeKeyValue = (
return meta.registry.createType(decodedKey.outputType, hexToU8a(value))[toHuman ? 'toHuman' : 'toJSON']()
}

switch (decodedKey.args.length) {
case 2: {
return {
[storage.section]: {
[storage.method]: {
[decodedKey.args[0].toString()]: {
[decodedKey.args[1].toString()]: decodeValue(),
},
},
},
}
}
case 1: {
return {
[storage.section]: {
[storage.method]: {
[decodedKey.args[0].toString()]: decodeValue(),
},
},
}
return {
section: storage.section,
method: storage.method,
key: decodedKey.args,
value: decodeValue(),
}
}

export const toStorageObject = (decoded: ReturnType<typeof decodeKeyValue>) => {
if (!decoded) {
return undefined
}

const { section, method, key, value } = decoded

let obj = value

if (key) {
for (let i = key.length - 1; i >= 0; i--) {
const k = key[i]
const newObj = { [k.toString()]: obj }
obj = newObj
}
default:
return {
[storage.section]: {
[storage.method]: decodeValue(),
},
}
}

return {
[section]: {
[method]: obj,
},
}
}

Expand All @@ -110,8 +112,12 @@ export const decodeBlockStorageDiff = async (block: Block, diff: [HexString, Hex
const newState = {}
const meta = await block.meta
for (const [key, value] of diff) {
_.merge(oldState, decodeKeyValue(meta, block, key, (await block.get(key)) as any))
_.merge(newState, decodeKeyValue(meta, block, key, value))
const oldValue = await block.get(key)
const oldDecoded = toStorageObject(decodeKeyValue(meta, block, key, oldValue)) ?? { [key]: oldValue }
_.merge(oldState, oldDecoded)

const newDecoded = toStorageObject(decodeKeyValue(meta, block, key, value)) ?? { [key]: value }
_.merge(newState, newDecoded)
}
return [oldState, newState]
}
64 changes: 63 additions & 1 deletion packages/e2e/src/__snapshots__/decoder.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`decoder > decode key-value 1`] = `
{
"key": [
"25fqepuLngYL2DK9ApTejNzqPadUUZ9ALYyKWX2jyvEiuZLa",
],
"method": "account",
"section": "system",
"value": {
"consumers": "0",
"data": {
"feeFrozen": "0",
"free": "10,000,000,000",
"miscFrozen": "0",
"reserved": "0",
},
"nonce": "0",
"providers": "0",
"sufficients": "0",
},
}
`;

exports[`decoder > decode key-value 2`] = `
{
"system": {
"account": {
Expand All @@ -21,7 +43,25 @@ exports[`decoder > decode key-value 1`] = `
}
`;

exports[`decoder > decode key-value 2`] = `
exports[`decoder > decode key-value 3`] = `
{
"key": [
"25fqepuLngYL2DK9ApTejNzqPadUUZ9ALYyKWX2jyvEiuZLa",
{
"token": "ACA",
},
],
"method": "accounts",
"section": "tokens",
"value": {
"free": "10,000,000,000",
"frozen": "0",
"reserved": "0",
},
}
`;

exports[`decoder > decode key-value 4`] = `
{
"tokens": {
"accounts": {
Expand All @@ -38,6 +78,28 @@ exports[`decoder > decode key-value 2`] = `
`;

exports[`decoder > works with multiple chains 1`] = `
{
"key": [
"15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5",
],
"method": "account",
"section": "system",
"value": {
"consumers": "0",
"data": {
"feeFrozen": "0",
"free": "10,000,000,000",
"miscFrozen": "0",
"reserved": "0",
},
"nonce": "0",
"providers": "0",
"sufficients": "0",
},
}
`;

exports[`decoder > works with multiple chains 2`] = `
{
"system": {
"account": {
Expand Down
14 changes: 10 additions & 4 deletions packages/e2e/src/decoder.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterAll, describe, expect, it } from 'vitest'
import { decodeKey, decodeKeyValue } from '@acala-network/chopsticks-core/utils/decoder'
import { decodeKey, decodeKeyValue, toStorageObject } from '@acala-network/chopsticks-core/utils/decoder'

import networks from './networks'

Expand All @@ -26,10 +26,14 @@ describe('decoder', async () => {
const meta = await chain.head.meta
const data = { data: { free: 10000000000 } }
const value = meta.registry.createType('AccountInfo', data)
expect(decodeKeyValue(meta, chain.head, SYSTEM_ACCOUNT, value.toHex())).toMatchSnapshot()
const decoded = decodeKeyValue(meta, chain.head, SYSTEM_ACCOUNT, value.toHex())
expect(decoded).toMatchSnapshot()
expect(toStorageObject(decoded)).toMatchSnapshot()

const ormlAccountData = meta.registry.createType('AccountData', data.data)
expect(decodeKeyValue(meta, chain.head, TOKENS_ACCOUNTS, ormlAccountData.toHex())).toMatchSnapshot()
const decoded2 = decodeKeyValue(meta, chain.head, TOKENS_ACCOUNTS, ormlAccountData.toHex())
expect(decoded2).toMatchSnapshot()
expect(toStorageObject(decoded2)).toMatchSnapshot()
})

it('works with multiple chains', async () => {
Expand All @@ -38,7 +42,9 @@ describe('decoder', async () => {
const meta = await chain.head.meta
const data = { data: { free: 10000000000 } }
const value = meta.registry.createType('AccountInfo', data)
expect(decodeKeyValue(meta, chain.head, SYSTEM_ACCOUNT, value.toHex())).toMatchSnapshot()
const decoded = decodeKeyValue(meta, chain.head, SYSTEM_ACCOUNT, value.toHex())
expect(decoded).toMatchSnapshot()
expect(toStorageObject(decoded)).toMatchSnapshot()
await teardown()
})
})