Skip to content
Open
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 packages/core/src/blockchain/head-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class HeadState {
delete this.#storageListeners[id]
}

async subscrubeRuntimeVersion(cb: (block: Block) => void) {
async subscribeRuntimeVersion(cb: (block: Block) => void) {
const id = randomId()
const codeKey = stringToHex(':code')
this.#storageListeners[id] = [[codeKey], cb]
Expand Down
37 changes: 37 additions & 0 deletions packages/core/src/rpc/substrate/grandpa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { u8aToHex } from '@polkadot/util'

import { Block } from '../../blockchain/block.js'
import { Handler } from '../shared.js'

export const grandpa_subscribeJustifications: Handler<void, string> = async (context, _params, { subscribe }) => {
let update = (_block: Block) => {}

const id = context.chain.headState.subscribeHead((block) => update(block))
const callback = subscribe('grandpa_justifications', id, () => context.chain.headState.unsubscribeHead(id))

update = async (block: Block) => {
const meta = await block.meta
const justification = meta.registry.createType('GrandpaJustification', {
round: 1,
commit: {
targetHash: block.hash,
targetNumber: block.number,
precommits: [],
},
votesAncestries: [],
})
callback(u8aToHex(justification.toU8a()))
}

setTimeout(() => update(context.chain.head), 50)

return id
}

export const grandpa_unsubscribeJustifications: Handler<[string], void> = async (
_context,
[subid],
{ unsubscribe },
) => {
unsubscribe(subid)
}
3 changes: 3 additions & 0 deletions packages/core/src/rpc/substrate/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import * as AuthorRPC from './author.js'
import * as ChainRPC from './chain.js'
import * as GrandpaRPC from './grandpa.js'
import * as PaymentRPC from './payment.js'
import * as StateRPC from './state.js'
import * as SystemRPC from './system.js'

export { AuthorRPC }
export { ChainRPC }
export { GrandpaRPC }
export { PaymentRPC }
export { StateRPC }
export { SystemRPC }

const handlers = {
...AuthorRPC,
...ChainRPC,
...GrandpaRPC,
...PaymentRPC,
...StateRPC,
...SystemRPC,
Expand Down
34 changes: 31 additions & 3 deletions packages/core/src/rpc/substrate/state.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Block } from '../../blockchain/block.js'
import { HexString } from '@polkadot/util/types'
import _ from 'lodash'

import { Block } from '../../blockchain/block.js'
import { Handler, ResponseError } from '../shared.js'
import { RuntimeVersion } from '../../wasm-executor/index.js'
import { RuntimeVersion, createProof } from '../../wasm-executor/index.js'
import { defaultLogger } from '../../logger.js'
import { isPrefixedChildKey, prefixedChildKey, stripChildPrefix } from '../../utils/index.js'

Expand Down Expand Up @@ -56,6 +57,33 @@ export const state_getKeysPaged: Handler<[string, number, string, HexString], st
return block?.getKeysPaged({ prefix, pageSize, startKey })
}

/**
* Get proof of the runtime storage value.
* NOTE: The retuned proof trie_root_hash does not match the block trie_root_hash.
*
* @param context
* @param params - [`keys`, `blockhash`]
*
* @return storage proof
*/
export const state_getReadProof: Handler<
[HexString[], HexString],
{ at: HexString; proof: HexString[] } | null
> = async (context, [keys, hash]) => {
const block = await context.chain.getBlock(hash)
if (!block) {
return null
}
const entries = await Promise.all(
_.sortedUniq(keys).map(async (key) => [key, await block.get(key)] as [HexString, HexString | null]),
)
const { nodes: proof } = await createProof([], entries)
return {
at: block.hash,
proof,
}
}

/**
* @param context
* @param params - [`keys`, `blockhash`]
Expand Down Expand Up @@ -110,7 +138,7 @@ export const state_call: Handler<[HexString, HexString, HexString], HexString> =
*/
export const state_subscribeRuntimeVersion: Handler<[], string> = async (context, _params, { subscribe }) => {
let update = (_block: Block) => {}
const id = await context.chain.headState.subscrubeRuntimeVersion((block) => update(block))
const id = await context.chain.headState.subscribeRuntimeVersion((block) => update(block))
const callback = subscribe('state_runtimeVersion', id)
update = async (block) => callback(await block.runtimeVersion)
setTimeout(() => {
Expand Down
21 changes: 21 additions & 0 deletions packages/e2e/src/__snapshots__/grandpa.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`grandpa rpc > subscribeJustifications 1`] = `
[
[
"0x0100000000000000b012d04c56b65cfa1f47cb1f884d920f95d0097b1ed42f5da18d5e2a436c2f3edcd2dc000000",
],
]
`;

exports[`grandpa rpc > subscribeJustifications 2`] = `"0x7ba5ba52f5b88fa5bcab5c1a0f7e158d845d93e1c70e9a5b42a7c3a9af681ee7"`;

exports[`grandpa rpc > subscribeJustifications 3`] = `
[
[
"0x01000000000000007ba5ba52f5b88fa5bcab5c1a0f7e158d845d93e1c70e9a5b42a7c3a9af681ee7ddd2dc000000",
],
]
`;

exports[`grandpa rpc > subscribeJustifications 4`] = `"0xaf0b8a9bb7abb768f0cac30c0bd31750eb4b8541c6012f65938e16415c3a024f"`;
9 changes: 9 additions & 0 deletions packages/e2e/src/__snapshots__/state.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`state rpc > getReadProof 1`] = `
{
"at": "0x0df086f32a9c3399f7fa158d3d77a1790830bd309134c5853718141c969299c7",
"proof": [
"0xa0f0c365c3cf59d671eb72da0e7a4113c41002505f0e7b9012096b41c4eb3aaf947f6ea429080000685f0f1f0515f462cdcf84e0f1d6045dfcbb20c6ad70bd88010000",
],
}
`;

exports[`state rpc > getXXX 1`] = `
{
"apis": [
Expand Down
36 changes: 36 additions & 0 deletions packages/e2e/src/grandpa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest'

import { api, delay, dev, mockCallback, setupApi } from './helper.js'

setupApi({
endpoint: ['wss://rpc.ibp.network/polkadot'],
blockHash: '0xb012d04c56b65cfa1f47cb1f884d920f95d0097b1ed42f5da18d5e2a436c2f3e',
})

describe('grandpa rpc', () => {
it('subscribeJustifications', async () => {
const { callback, next } = mockCallback()
const unsub = await api.rpc.grandpa.subscribeJustifications(callback)

await next()
expect(callback.mock.calls).toMatchSnapshot()

callback.mockClear()

expect(await dev.newBlock()).toMatchSnapshot()

await next()

expect(callback.mock.calls).toMatchSnapshot()

callback.mockClear()

unsub()

expect(await dev.newBlock()).toMatchSnapshot()

await delay(100)

expect(callback).not.toHaveBeenCalled()
})
})
12 changes: 12 additions & 0 deletions packages/e2e/src/state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,17 @@ describe('state rpc', () => {
expect(await api.rpc.state.getMetadata(genesisHash)).to.not.be.eq(await api.rpc.state.getMetadata())
})

it('getReadProof', async () => {
expect(
await api.rpc.state.getReadProof(
[
'0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429',
'0xf0c365c3cf59d671eb72da0e7a4113c49f1f0515f462cdcf84e0f1d6045dfcbb',
],
env.acala.blockHash,
),
).toMatchSnapshot()
})

it.todo('subscribeRuntimeVersion')
})