Skip to content

Commit

Permalink
add rpc types and docs (#409)
Browse files Browse the repository at this point in the history
* add rpc types and docs

* refactor and add types
  • Loading branch information
qiweiii authored Sep 22, 2023
1 parent bf9a76a commit ef0cd76
Show file tree
Hide file tree
Showing 10 changed files with 503 additions and 309 deletions.
5 changes: 5 additions & 0 deletions .vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@ export default defineConfig({
socialLinks: [{ icon: 'github', link: 'https://github.com/AcalaNetwork/chopsticks' }],
outline: 3,
},
markdown: {
anchor: {
slugify: (s) => encodeURIComponent(String(s).trim().replace(/\s+/g, '-')).toLocaleLowerCase(),
},
},
})
8 changes: 4 additions & 4 deletions packages/chopsticks/src/rpc/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class ResponseError extends Error {

export interface Context {
/**
* The blockchain instance, see types in the `core` package
* The blockchain instance, see `Blockchain` type in the `core` package
*/
chain: Blockchain
}
Expand All @@ -32,9 +32,9 @@ export interface SubscriptionManager {
unsubscribe: (subid: string) => void
}

export type Handler = (
export type Handler<TParams = any, TReturn = any> = (
context: Context,
params: any,
params: TParams,
subscriptionManager: SubscriptionManager,
) => Promise<object | string | number | void | undefined | null>
) => Promise<TReturn>
export type Handlers = Record<string, Handler>
135 changes: 81 additions & 54 deletions packages/chopsticks/src/rpc/substrate/author.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,98 @@
import { APPLY_EXTRINSIC_ERROR, Block } from '@acala-network/chopsticks-core'
import { Handlers, ResponseError } from '../shared'
import { HexString } from '@polkadot/util/types'
import { TransactionValidityError } from '@polkadot/types/interfaces'

import { Handler, ResponseError, SubscriptionManager } from '../shared'
import { defaultLogger } from '../../logger'

const logger = defaultLogger.child({ name: 'rpc-author' })

const handlers: Handlers = {
author_submitExtrinsic: async (context, [extrinsic]) => {
return context.chain.submitExtrinsic(extrinsic).catch((error: TransactionValidityError) => {
const code = error.isInvalid ? 1010 : 1011
throw new ResponseError(code, error.toString())
})
},
author_submitAndWatchExtrinsic: async (context, [extrinsic], { subscribe, unsubscribe }) => {
let update = (_block: Block) => {}
/**
* @param context
* @param params - [`extrinsic`]
*
* @return Hash
*/
export const author_submitExtrinsic: Handler<[HexString], HexString> = async (context, [extrinsic]) => {
return context.chain.submitExtrinsic(extrinsic).catch((error: TransactionValidityError) => {
const code = error.isInvalid ? 1010 : 1011
throw new ResponseError(code, error.toString())
})
}

/**
* @param context
* @param params - [`extrinsic`]
* @param subscriptionManager
*
* @return subscription id
*/
export const author_submitAndWatchExtrinsic: Handler<[HexString], string> = async (
context,
[extrinsic],
{ subscribe, unsubscribe }: SubscriptionManager,
) => {
let update = (_block: Block) => {}

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

const onExtrinsicFail = ([failedExtrinsic, error]: [string, TransactionValidityError]) => {
if (failedExtrinsic === extrinsic) {
callback(error.toJSON())
done(id)
}
const onExtrinsicFail = ([failedExtrinsic, error]: [string, TransactionValidityError]) => {
if (failedExtrinsic === extrinsic) {
callback(error.toJSON())
done(id)
}
}

context.chain.txPool.event.on(APPLY_EXTRINSIC_ERROR, onExtrinsicFail)
context.chain.txPool.event.on(APPLY_EXTRINSIC_ERROR, onExtrinsicFail)

const done = (id: string) => {
context.chain.txPool.event.removeListener(APPLY_EXTRINSIC_ERROR, onExtrinsicFail)
unsubscribe(id)
}
const done = (id: string) => {
context.chain.txPool.event.removeListener(APPLY_EXTRINSIC_ERROR, onExtrinsicFail)
unsubscribe(id)
}

update = async (block) => {
const extrisnics = await block.extrinsics
if (!extrisnics.includes(extrinsic)) return
update = async (block) => {
const extrisnics = await block.extrinsics
if (!extrisnics.includes(extrinsic)) return

logger.debug({ block: block.hash }, 'author_extrinsicUpdate')
logger.debug({ block: block.hash }, 'author_extrinsicUpdate')

callback({
InBlock: block.hash,
})
callback({
Finalized: block.hash,
})
done(id)
}
callback({
InBlock: block.hash,
})
callback({
Finalized: block.hash,
})
done(id)
}

try {
await context.chain.submitExtrinsic(extrinsic)
callback({
Ready: null,
})
} catch (error) {
logger.error({ error }, 'ExtrinsicFailed')
const code = (error as TransactionValidityError).isInvalid ? 1010 : 1011
done(id)
throw new ResponseError(code, (error as TransactionValidityError).toString())
}
return id
},
author_unwatchExtrinsic: async (_context, [subid], { unsubscribe }) => {
unsubscribe(subid)
},
author_pendingExtrinsics: async (context) => {
return context.chain.txPool.pendingExtrinsics
},
try {
await context.chain.submitExtrinsic(extrinsic)
callback({
Ready: null,
})
} catch (error) {
logger.error({ error }, 'ExtrinsicFailed')
const code = (error as TransactionValidityError).isInvalid ? 1010 : 1011
done(id)
throw new ResponseError(code, (error as TransactionValidityError).toString())
}
return id
}

/**
* @param _context
* @param params - [`subid`]
*/
export const author_unwatchExtrinsic: Handler<[string], void> = async (_context, [subid], { unsubscribe }) => {
unsubscribe(subid)
}

export default handlers
/**
* Get pending extrinsics
*
* @return Array of pending extrinsics
*/
export const author_pendingExtrinsics: Handler<void, HexString[]> = async (context) => {
return context.chain.txPool.pendingExtrinsics
}
165 changes: 95 additions & 70 deletions packages/chopsticks/src/rpc/substrate/chain.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,108 @@
import { Handlers, ResponseError } from '../shared'
import { Header } from '@polkadot/types/interfaces'
import { HexString } from '@polkadot/util/types'

import { Handler, ResponseError } from '../shared'

const processHeader = (header: Header) => {
const res = header.toJSON() as any
res.number = '0x' + res.number.toString(16) // number is hex format
return res
}

const handlers: Handlers = {
chain_getBlockHash: async (context, [blockNumber]) => {
const block = await context.chain.getBlockAt(blockNumber)
if (!block) {
throw new ResponseError(1, `Block #${blockNumber} not found`)
}
return block.hash
},
chain_getHeader: async (context, [hash]) => {
const block = await context.chain.getBlock(hash)
if (!block) {
throw new ResponseError(1, `Block ${hash} not found`)
}
return processHeader(await block.header)
},
chain_getBlock: async (context, [hash]) => {
const block = await context.chain.getBlock(hash)
if (!block) {
throw new ResponseError(1, `Block ${hash} not found`)
}
return {
block: {
header: await block.header,
extrinsics: await block.extrinsics,
},
justifications: null,
}
},
chain_getFinalizedHead: async (context) => {
return context.chain.head.hash
},
chain_subscribeNewHead: async (context, _params, { subscribe }) => {
let update = () => {}

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

update = async () => {
callback(processHeader(await context.chain.head.header))
}

update()

return id
},
chain_subscribeFinalizedHeads: async (context, _params, { subscribe }) => {
let update = () => {}

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

update = async () => {
callback(processHeader(await context.chain.head.header))
}

update()

return id
},
chain_unsubscribeNewHead: async (_context, [subid], { unsubscribe }) => {
unsubscribe(subid)
},
/**
* @param context
* @param params - [`blockNumber`]
*
* @return Block hash
*/
export const chain_getBlockHash: Handler<[number], HexString> = async (context, [blockNumber]) => {
const block = await context.chain.getBlockAt(blockNumber)
if (!block) {
throw new ResponseError(1, `Block #${blockNumber} not found`)
}
return block.hash
}

/**
* @param context
* @param params - [`blockhash`]
*
* @return Header - see `@polkadot/types/interfaces`
*/
export const chain_getHeader: Handler<[HexString], Header> = async (context, [hash]) => {
const block = await context.chain.getBlock(hash)
if (!block) {
throw new ResponseError(1, `Block ${hash} not found`)
}
return processHeader(await block.header)
}

/**
* @param context
* @param params - [`blockhash`]
*
* @return Block header and extrinsics
*/
export const chain_getBlock: Handler<
[HexString],
{ block: { header: Header; extrinsics: HexString[] }; justifications: null }
> = async (context, [hash]) => {
const block = await context.chain.getBlock(hash)
if (!block) {
throw new ResponseError(1, `Block ${hash} not found`)
}
return {
block: {
header: await block.header,
extrinsics: await block.extrinsics,
},
justifications: null,
}
}

/**
* @param context
*
* @return head hash
*/
export const chain_getFinalizedHead: Handler<void, HexString> = async (context) => {
return context.chain.head.hash
}

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

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

update = async () => {
callback(processHeader(await context.chain.head.header))
}

update()

return id
}

const alias = {
chain_subscribeNewHeads: handlers.chain_subscribeNewHead,
chain_unsubscribeNewHeads: handlers.chain_unsubscribeNewHead,
chain_unsubscribeFinalizedHeads: handlers.chain_unsubscribeNewHead,
export const chain_subscribeFinalizedHeads: Handler<void, string> = async (context, _params, { subscribe }) => {
let update = () => {}

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

update = async () => {
callback(processHeader(await context.chain.head.header))
}

update()

return id
}

export default {
...handlers,
...alias,
export const chain_unsubscribeNewHead: Handler<[string], void> = async (_context, [subid], { unsubscribe }) => {
unsubscribe(subid)
}

export const chain_subscribeNewHeads = chain_subscribeNewHead
export const chain_unsubscribeNewHeads = chain_unsubscribeNewHead
export const chain_unsubscribeFinalizedHeads = chain_unsubscribeNewHead
28 changes: 16 additions & 12 deletions packages/chopsticks/src/rpc/substrate/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import author from './author'
import chain from './chain'
import payment from './payment'
import state from './state'
import system from './system'
import * as AuthorRPC from './author'
import * as ChainRPC from './chain'
import * as PaymentRPC from './payment'
import * as StateRPC from './state'
import * as SystemRPC from './system'

import { Handlers } from '../shared'
export { AuthorRPC }
export { ChainRPC }
export { PaymentRPC }
export { StateRPC }
export { SystemRPC }

const handlers: Handlers = {
...author,
...chain,
...state,
...system,
...payment,
const handlers = {
...AuthorRPC,
...ChainRPC,
...PaymentRPC,
...StateRPC,
...SystemRPC,
}

export default handlers
Loading

0 comments on commit ef0cd76

Please sign in to comment.