Skip to content

Commit

Permalink
Refactor api (#45)
Browse files Browse the repository at this point in the history
* reafactor api

* use metadata from new wasm
  • Loading branch information
xlc authored Nov 8, 2022
1 parent ae00df8 commit 2f3aeed
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 58 deletions.
2 changes: 1 addition & 1 deletion e2e/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const setupAll = async ({ endpoint, blockHash, mockSignatureHost }: SetupOption)
const inherents = new InherentProviders(setTimestamp, [new SetValidationData(tasks, 1)])

const chain = new Blockchain({
api,
upstreamApi: api,
tasks,
buildBlockMode: BuildBlockMode.Manual,
inherentProvider: inherents,
Expand Down
35 changes: 24 additions & 11 deletions src/blockchain/block.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { ApiPromise } from '@polkadot/api'
import { Header } from '@polkadot/types/interfaces'
import { stringToHex } from '@polkadot/util'
import { Metadata, TypeRegistry } from '@polkadot/types'
import { compactStripLength, hexToU8a, stringToHex, u8aToHex } from '@polkadot/util'

import { Blockchain } from '.'
import { RemoteStorageLayer, StorageLayer, StorageLayerProvider, StorageValueKind } from './storage-layer'
import { ResponseError } from '../rpc/shared'
import { TaskResponseCall } from '../task'

export class Block {
#api: ApiPromise
#chain: Blockchain

#header?: Header | Promise<Header>
Expand All @@ -18,37 +17,38 @@ export class Block {
#wasm?: Promise<string>
#runtimeVersion?: Promise<any>
#metadata?: Promise<string>
#registry?: Promise<TypeRegistry>

#baseStorage: StorageLayerProvider
#storages: StorageLayer[]

constructor(
api: ApiPromise,
chain: Blockchain,
public readonly number: number,
public readonly hash: string,
parentBlock?: Block,
block?: { header: Header; extrinsics: string[]; storage?: StorageLayerProvider }
) {
this.#api = api
this.#chain = chain
this.#parentBlock = parentBlock
this.#header = block?.header
this.#extrinsics = block?.extrinsics
this.#baseStorage = block?.storage ?? new RemoteStorageLayer(api, hash, chain.db)
this.#baseStorage = block?.storage ?? new RemoteStorageLayer(chain.upstreamApi, hash, chain.db)
this.#storages = []
}

get header(): Header | Promise<Header> {
if (!this.#header) {
this.#header = this.#api.rpc.chain.getHeader(this.hash)
this.#header = this.#chain.upstreamApi.rpc.chain.getHeader(this.hash)
}
return this.#header
}

get extrinsics(): string[] | Promise<string[]> {
if (!this.#extrinsics) {
this.#extrinsics = this.#api.rpc.chain.getBlock(this.hash).then((b) => b.block.extrinsics.map((e) => e.toHex()))
this.#extrinsics = this.#chain.upstreamApi.rpc.chain
.getBlock(this.hash)
.then((b) => b.block.extrinsics.map((e) => e.toHex()))
}
return this.#extrinsics
}
Expand Down Expand Up @@ -128,6 +128,18 @@ export class Block {
const wasmKey = stringToHex(':code')
this.pushStorageLayer().set(wasmKey, wasm)
this.#wasm = Promise.resolve(wasm)
this.#registry = undefined
}

get registry(): Promise<TypeRegistry> {
if (!this.#registry) {
this.#registry = this.metadata.then((metadata) => {
const registry = new TypeRegistry()
registry.setMetadata(new Metadata(registry, metadata as any))
return registry
})
}
return this.#registry
}

get runtimeVersion(): Promise<any> {
Expand All @@ -144,8 +156,7 @@ export class Block {
(resp) => {
if ('RuntimeVersion' in resp) {
const ver = resp.RuntimeVersion
const decoded = this.#api.createType('RuntimeVersion', ver)
resolve(decoded.toJSON())
resolve(this.registry.then((registry) => registry.createType('RuntimeVersion', ver).toJSON()))
} else if ('Error' in resp) {
reject(new ResponseError(1, resp.Error))
}
Expand All @@ -161,7 +172,9 @@ export class Block {

get metadata(): Promise<string> {
if (!this.#metadata) {
this.#metadata = this.call('Metadata_metadata', '0x').then((x) => x.result)
this.#metadata = this.call('Metadata_metadata', '0x').then((x) =>
u8aToHex(compactStripLength(hexToU8a(x.result))[1])
)
}
return this.#metadata
}
Expand Down
25 changes: 13 additions & 12 deletions src/blockchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { defaultLogger } from '../logger'
const logger = defaultLogger.child({ name: 'blockchain' })

export interface Options {
api: ApiPromise
upstreamApi: ApiPromise
tasks: TaskManager
buildBlockMode?: BuildBlockMode
inherentProvider: InherentProvider
Expand All @@ -25,7 +25,7 @@ export interface Options {
}

export class Blockchain {
readonly api: ApiPromise
readonly upstreamApi: ApiPromise
readonly tasks: TaskManager
readonly db: DataSource | undefined

Expand All @@ -37,15 +37,15 @@ export class Blockchain {

readonly headState: HeadState

constructor({ api, tasks, buildBlockMode, inherentProvider, db, header }: Options) {
this.api = api
constructor({ upstreamApi, tasks, buildBlockMode, inherentProvider, db, header }: Options) {
this.upstreamApi = upstreamApi
this.tasks = tasks
this.db = db

this.#head = new Block(api, this, header.number, header.hash)
this.#head = new Block(this, header.number, header.hash)
this.#registerBlock(this.#head)

this.#txpool = new TxPool(this, api, inherentProvider, buildBlockMode)
this.#txpool = new TxPool(this, inherentProvider, buildBlockMode)

this.headState = new HeadState(this.#head)
}
Expand All @@ -67,8 +67,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.upstreamApi.rpc.chain.getBlockHash(number)
const block = new Block(this, number, hash.toHex())
this.#registerBlock(block)
}
return this.#blocksByNumber[number]
Expand All @@ -80,8 +80,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.upstreamApi.rpc.chain.getHeader(hash)
const block = new Block(this, header.number.toNumber(), hash)
this.#registerBlock(block)
} catch (e) {
logger.debug(`getBlock(${hash}) failed: ${e}`)
Expand All @@ -98,7 +98,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, number, hash, parent, { header, extrinsics: [], storage: parent.storage })
this.#blocksByHash[hash] = block
return block
}
Expand Down Expand Up @@ -130,7 +130,8 @@ 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 registry = await this.head.registry
const validity: TransactionValidity = registry.createType('TransactionValidity', res.result)
if (validity.isOk) {
this.#txpool.submitExtrinsic(extrinsic)
return blake2AsHex(extrinsic, 256)
Expand Down
24 changes: 16 additions & 8 deletions src/blockchain/inherents.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ApiPromise } from '@polkadot/api'
import { TaskManager } from '../task'
import { TypeRegistry } from '@polkadot/types'

import { Block } from './block'
import { TaskManager } from '../task'

export interface CreateInherents {
createInherents(api: ApiPromise, timestamp: number, parent: Block): Promise<string[]>
createInherents(api: ApiPromise, registry: TypeRegistry, timestamp: number, parent: Block): Promise<string[]>
}

export interface InherentProvider extends CreateInherents {
Expand All @@ -18,7 +19,12 @@ export class SetTimestamp implements InherentProvider {
this.#getTimestamp = getTimestamp
}

async createInherents(api: ApiPromise, timestamp: number, _parent: Block): Promise<string[]> {
async createInherents(
api: ApiPromise,
_registry: TypeRegistry,
timestamp: number,
_parent: Block
): Promise<string[]> {
return [api.tx.timestamp.set(timestamp).toHex()]
}

Expand All @@ -36,9 +42,11 @@ export class InherentProviders implements InherentProvider {
this.#providers = providers
}

async createInherents(api: ApiPromise, timestamp: number, parent: Block): Promise<string[]> {
const base = await this.#base.createInherents(api, timestamp, parent)
const extra = await Promise.all(this.#providers.map((provider) => provider.createInherents(api, timestamp, parent)))
async createInherents(api: ApiPromise, registry: TypeRegistry, timestamp: number, parent: Block): Promise<string[]> {
const base = await this.#base.createInherents(api, registry, timestamp, parent)
const extra = await Promise.all(
this.#providers.map((provider) => provider.createInherents(api, registry, timestamp, parent))
)
return [...base, ...extra.flat()]
}

Expand All @@ -56,7 +64,7 @@ export class SetValidationData implements CreateInherents {
this.#expectedIndex = expectedIndex
}

async createInherents(api: ApiPromise, _timestamp: number, parent: Block): Promise<string[]> {
async createInherents(api: ApiPromise, registry: TypeRegistry, _timestamp: number, parent: Block): Promise<string[]> {
if (!api.tx.parachainSystem?.setValidationData) {
return []
}
Expand All @@ -67,7 +75,7 @@ export class SetValidationData implements CreateInherents {
throw new Error('Parent block not found')
}
const extrinsics = await parentBlock.extrinsics
const method = api.createType('GenericExtrinsic', extrinsics[this.#expectedIndex])
const method = registry.createType('GenericExtrinsic', extrinsics[this.#expectedIndex])
const validationData = (method as any).args[0].toJSON()

const newData = {
Expand Down
35 changes: 15 additions & 20 deletions src/blockchain/txpool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ApiPromise } from '@polkadot/api'
import { Header } from '@polkadot/types/interfaces'
import { bnToHex, bnToU8a, compactAddLength, u8aConcat } from '@polkadot/util'
import _ from 'lodash'
Expand All @@ -18,22 +17,15 @@ export enum BuildBlockMode {
}

export class TxPool {
readonly #api: ApiPromise
readonly #chain: Blockchain
readonly #pool: string[] = []
readonly #mode: BuildBlockMode
readonly #inherentProvider: InherentProvider

#lastBuildBlockPromise: Promise<void> = Promise.resolve()

constructor(
chain: Blockchain,
api: ApiPromise,
inherentProvider: InherentProvider,
mode: BuildBlockMode = BuildBlockMode.Batch
) {
constructor(chain: Blockchain, inherentProvider: InherentProvider, mode: BuildBlockMode = BuildBlockMode.Batch) {
this.#chain = chain
this.#api = api
this.#mode = mode
this.#inherentProvider = inherentProvider
}
Expand Down Expand Up @@ -77,7 +69,9 @@ export class TxPool {
const [consensusEngine] = preRuntime
const rest = parentHeader.digest.logs.slice(1)
let newLogs = parentHeader.digest.logs as any
const expectedSlot = Math.floor(time / ((this.#api.consts.timestamp.minimumPeriod as any).toNumber() * 2))
const expectedSlot = Math.floor(
time / ((this.#chain.upstreamApi.consts.timestamp.minimumPeriod as any).toNumber() * 2)
)
if (consensusEngine.isAura) {
const newSlot = compactAddLength(bnToU8a(expectedSlot, { isLe: true, bitLength: 64 }))
newLogs = [{ PreRuntime: [consensusEngine, newSlot] }, ...rest]
Expand All @@ -87,7 +81,8 @@ export class TxPool {
newLogs = [{ PreRuntime: [consensusEngine, newSlot] }, ...rest]
}

const header = this.#api.createType('Header', {
const registry = await head.registry
const header = registry.createType('Header', {
parentHash: head.hash,
number: head.number + 1,
stateRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
Expand All @@ -114,14 +109,14 @@ export class TxPool {

newBlock.pushStorageLayer().setAll(resp.storageDiff)

if ((this.#api.query as any).babe?.currentSlot) {
if ((this.#chain.upstreamApi.query as any).babe?.currentSlot) {
// TODO: figure out how to generate a valid babe slot digest instead of just modify the data
// but hey, we can get it working without a valid digest
const key = this.#api.query.babe.currentSlot.key()
const key = this.#chain.upstreamApi.query.babe.currentSlot.key()
newBlock.pushStorageLayer().set(key, bnToHex(expectedSlot, { isLe: true, bitLength: 64 }))
}

const inherents = await this.#inherentProvider.createInherents(this.#api, time, newBlock)
const inherents = await this.#inherentProvider.createInherents(this.#chain.upstreamApi, registry, time, newBlock)
for (const extrinsic of inherents) {
try {
const resp = await newBlock.call('BlockBuilder_apply_extrinsic', extrinsic)
Expand All @@ -133,9 +128,9 @@ export class TxPool {
}
}

if (this.#api.query.parachainSystem?.validationData) {
if (this.#chain.upstreamApi.query.parachainSystem?.validationData) {
// this is a parachain
const validationDataKey = this.#api.query.parachainSystem.validationData.key()
const validationDataKey = this.#chain.upstreamApi.query.parachainSystem.validationData.key()
const validationData = await newBlock.get(validationDataKey)
if (!validationData) {
// there is no set validation data inherent
Expand All @@ -156,11 +151,11 @@ export class TxPool {
}
}

if (this.#api.query.paraInherent?.included) {
if (this.#chain.upstreamApi.query.paraInherent?.included) {
// TODO: remvoe this once paraInherent.enter is implemented
// we are relaychain, however as we have not yet implemented the paraInherent.enter
// so need to do some trick to make the on_finalize check happy
const paraInherentIncludedKey = this.#api.query.paraInherent.included.key()
const paraInherentIncludedKey = this.#chain.upstreamApi.query.paraInherent.included.key()
newBlock.pushStorageLayer().set(paraInherentIncludedKey, '0x01')
}

Expand All @@ -169,12 +164,12 @@ export class TxPool {
newBlock.pushStorageLayer().setAll(resp2.storageDiff)
logger.trace(resp2.storageDiff, 'Finalize block')

const blockData = this.#api.createType('Block', {
const blockData = registry.createType('Block', {
header,
extrinsics,
})

const finalBlock = new Block(this.#api, this.#chain, newBlock.number, blockData.hash.toHex(), head, {
const finalBlock = new Block(this.#chain, newBlock.number, blockData.hash.toHex(), head, {
header,
extrinsics: [...inherents, ...extrinsics],
storage: head.storage,
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const setup = async (argv: any) => {
const inherents = new InherentProviders(setTimestamp, [new SetValidationData(tasks, 1)])

const chain = new Blockchain({
api,
upstreamApi: api,
tasks,
buildBlockMode: argv['build-block-mode'],
inherentProvider: inherents,
Expand Down
5 changes: 1 addition & 4 deletions src/rpc/substrate/state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { compactStripLength, hexToU8a, u8aToHex } from '@polkadot/util'

import { Block } from '../../blockchain/block'
import { Handlers } from '../shared'
import { defaultLogger } from '../../logger'
Expand All @@ -11,8 +9,7 @@ const handlers: Handlers = {
return (await context.chain.getBlock(hash))?.runtimeVersion
},
state_getMetadata: async (context) => {
const metadata = await context.chain.head.metadata
return u8aToHex(compactStripLength(hexToU8a(metadata))[1])
return context.chain.head.metadata
},
state_getStorage: async (context, [key, hash]) => {
return (await context.chain.getBlock(hash))?.get(key)
Expand Down
2 changes: 1 addition & 1 deletion src/utils/set-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const setStorage = async (chain: Blockchain, storage: StorageValues, bloc
if (Array.isArray(storage)) {
storageItems = storage
} else {
storageItems = objectToStorageItems(chain.api, storage)
storageItems = objectToStorageItems(chain.upstreamApi, storage)
}
const block = await chain.getBlock(blockHash)
if (!block) throw Error(`Cannot find block ${blockHash || 'latest'}`)
Expand Down

0 comments on commit 2f3aeed

Please sign in to comment.