Skip to content

Commit

Permalink
improve genesis slot (#742)
Browse files Browse the repository at this point in the history
  • Loading branch information
ermalkaleci authored Apr 24, 2024
1 parent 7703263 commit 8319838
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 80 deletions.
64 changes: 51 additions & 13 deletions packages/core/src/blockchain/block-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
AccountInfo,
ApplyExtrinsicResult,
Call,
ConsensusEngineId,
DigestItem,
Header,
RawBabePreDigest,
TransactionValidityError,
Expand All @@ -19,14 +21,42 @@ import { defaultLogger, truncate } from '../logger.js'

const logger = defaultLogger.child({ name: 'block-builder' })

export const genesisDigestLogs = async (head: Block) => {
const meta = await head.meta
const currentSlot = await getCurrentSlot(head)
if (meta.consts.babe) {
const newSlot = meta.registry.createType('Slot', currentSlot + 1)
const consensusEngine = meta.registry.createType('ConsensusEngineId', 'BABE')
const preDigest = meta.registry.createType('RawBabePreDigest', {
SecondaryVRF: {
authorityIndex: 514,
slotNumber: newSlot,
vrfOutput: '0x44cadd14aaefbda13ac8d85e1a6d58be082e7e2f56a4f95a3c612c784aaa4063',
vrfProof:
'0xf5517bf67d93ce633cde2fde7fbcf8ddca80017aaf8cd48436514687c662f60eda0ffa2c4781906416f4e71a196c9783c60c1b83d54c3a29365d03706714570b',
},
})
const digest = meta.registry.createType<DigestItem>('DigestItem', {
PreRuntime: [consensusEngine, compactAddLength(preDigest.toU8a())],
})
return [digest]
} else {
const newSlot = meta.registry.createType('Slot', currentSlot + 1)
const consensusEngine = meta.registry.createType<ConsensusEngineId>('ConsensusEngineId', 'aura')
const digest = meta.registry.createType<DigestItem>('DigestItem', {
PreRuntime: [consensusEngine, compactAddLength(newSlot.toU8a())],
})
return [digest]
}
}

const getConsensus = (header: Header) => {
if (header.digest.logs.length === 0) return
const preRuntime = header.digest.logs[0].asPreRuntime
const [consensusEngine, slot] = preRuntime
return { consensusEngine, slot, rest: header.digest.logs.slice(1) }
const [consensusEngine, preDigest] = header.digest.logs[0].asPreRuntime
return { consensusEngine, preDigest, rest: header.digest.logs.slice(1) }
}

const getNewSlot = (digest: RawBabePreDigest, slotNumber: number) => {
const babePreDigestSetSlot = (digest: RawBabePreDigest, slotNumber: number) => {
if (digest.isPrimary) {
return {
primary: {
Expand Down Expand Up @@ -58,29 +88,37 @@ export const newHeader = async (head: Block, unsafeBlockHeight?: number) => {
const meta = await head.meta
const parentHeader = await head.header

let newLogs = parentHeader.digest.logs as any
let newLogs = !head.number ? await genesisDigestLogs(head) : parentHeader.digest.logs.toArray()
const consensus = getConsensus(parentHeader)
if (consensus?.consensusEngine.isAura) {
const slot = await getCurrentSlot(head.chain)
const slot = await getCurrentSlot(head)
const newSlot = compactAddLength(meta.registry.createType('Slot', slot + 1).toU8a())
newLogs = [{ PreRuntime: [consensus.consensusEngine, newSlot] }, ...consensus.rest]
newLogs = [
meta.registry.createType<DigestItem>('DigestItem', { PreRuntime: [consensus.consensusEngine, newSlot] }),
...consensus.rest,
]
} else if (consensus?.consensusEngine.isBabe) {
const slot = await getCurrentSlot(head.chain)
const digest = meta.registry.createType<RawBabePreDigest>('RawBabePreDigest', consensus.slot)
const newSlot = compactAddLength(meta.registry.createType('RawBabePreDigest', getNewSlot(digest, slot + 1)).toU8a())
newLogs = [{ PreRuntime: [consensus.consensusEngine, newSlot] }, ...consensus.rest]
const slot = await getCurrentSlot(head)
const digest = meta.registry.createType<RawBabePreDigest>('RawBabePreDigest', consensus.preDigest)
const newSlot = compactAddLength(
meta.registry.createType('RawBabePreDigest', babePreDigestSetSlot(digest, slot + 1)).toU8a(),
)
newLogs = [
meta.registry.createType<DigestItem>('DigestItem', { PreRuntime: [consensus.consensusEngine, newSlot] }),
...consensus.rest,
]
} else if (consensus?.consensusEngine?.toString() == 'nmbs') {
const nmbsKey = stringToHex('nmbs')
newLogs = [
{
meta.registry.createType<DigestItem>('DigestItem', {
// Using previous block author
PreRuntime: [
consensus.consensusEngine,
parentHeader.digest.logs
.find((log) => log.isPreRuntime && log.asPreRuntime[0].toHex() == nmbsKey)
?.asPreRuntime[1].toHex(),
],
},
}),
...consensus.rest,
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class SetValidationData implements InherentProvider {
// increment current slot
const relayCurrentSlot = decoded[key]
? meta.registry.createType<Slot>('Slot', hexToU8a(decoded[key])).toNumber()
: (await getCurrentSlot(parent.chain)) * slotIncrease
: (await getCurrentSlot(parent)) * slotIncrease
const newSlot = meta.registry.createType<Slot>('Slot', relayCurrentSlot + slotIncrease)
newEntries.push([key, u8aToHex(newSlot.toU8a())])
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/blockchain/inherent/timestamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export class SetTimestamp implements InherentProvider {
const parent = await newBlock.parentBlock
if (!parent) throw new Error('parent block not found')
const meta = await parent.meta
const slotDuration = await getSlotDuration(parent.chain)
const currentTimestamp = await getCurrentTimestamp(parent.chain)
const slotDuration = await getSlotDuration(parent)
const currentTimestamp = await getCurrentTimestamp(parent)
return [new GenericExtrinsic(meta.registry, meta.tx.timestamp.set(currentTimestamp + BigInt(slotDuration))).toHex()]
}
}
44 changes: 2 additions & 42 deletions packages/core/src/setup.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import '@polkadot/types-codec'
import { DigestItem } from '@polkadot/types/interfaces'
import { HexString } from '@polkadot/util/types'
import { HttpProvider, WsProvider } from '@polkadot/rpc-provider'
import { ProviderInterface } from '@polkadot/rpc-provider/types'
import { RegisteredTypes } from '@polkadot/types/types'
import { compactAddLength } from '@polkadot/util'

import { Api } from './api.js'
import { Blockchain } from './blockchain/index.js'
import { BuildBlockMode } from './blockchain/txpool.js'
import { Database } from './database.js'
import { GenesisProvider } from './genesis-provider.js'
import { defaultLogger } from './logger.js'
import { getSlotDuration, setStorage } from './index.js'
import { inherentProviders } from './blockchain/inherent/index.js'

export type SetupOptions = {
Expand All @@ -30,44 +27,6 @@ export type SetupOptions = {
processQueuedMessages?: boolean
}

export const genesisSetup = async (chain: Blockchain, genesis: GenesisProvider) => {
const meta = await chain.head.meta
const timestamp = Date.now()
await setStorage(chain, {
Timestamp: {
Now: timestamp,
},
})

const slotDuration = await getSlotDuration(chain)
const currentSlot = Math.floor(timestamp / slotDuration)

if (meta.consts.babe) {
await setStorage(chain, {
Babe: {
CurrentSlot: currentSlot,
},
})

genesis.genesisHeaderLogs = [
'0x0642414245b50103020200001c5fef100000000044cadd14aaefbda13ac8d85e1a6d58be082e7e2f56a4f95a3c612c784aaa4063f5517bf67d93ce633cde2fde7fbcf8ddca80017aaf8cd48436514687c662f60eda0ffa2c4781906416f4e71a196c9783c60c1b83d54c3a29365d03706714570b',
]
} else {
await setStorage(chain, {
Aura: {
CurrentSlot: currentSlot,
},
})

const newSlot = compactAddLength(meta.registry.createType('Slot', currentSlot + 1).toU8a())
const consensusEngine = meta.registry.createType('ConsensusEngineId', 'aura')
const digest = meta.registry.createType<DigestItem>('DigestItem', { PreRuntime: [consensusEngine, newSlot] })
genesis.genesisHeaderLogs = [digest.toHex()]
}

await chain.newBlock()
}

export const processOptions = async (options: SetupOptions) => {
defaultLogger.debug(options, 'Setup options')

Expand Down Expand Up @@ -136,7 +95,8 @@ export const setup = async (options: SetupOptions) => {
})

if (opts.genesis) {
await genesisSetup(chain, opts.genesis)
// build 1st block
await chain.newBlock()
}

return chain
Expand Down
25 changes: 14 additions & 11 deletions packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { StorageKey } from '@polkadot/types'
import { getAuraSlotDuration } from '../wasm-executor/index.js'
import { hexAddPrefix, hexStripPrefix } from '@polkadot/util/hex'

import { Block } from '../blockchain/block.js'
import { Blockchain } from '../blockchain/index.js'

export * from './set-storage.js'
Expand Down Expand Up @@ -121,29 +122,31 @@ const POTENTIAL_SLOT_KEYS = [
'0xab2a8d5eca218f218c6fda6b1d22bb926bc171ab77f6a731a6e80c34ee1eda19', // authorInherent.highestSlotSeen
]

export const getCurrentSlot = async (chain: Blockchain) => {
const meta = await chain.head.meta
export const getCurrentSlot = async (head: Block) => {
const meta = await head.meta
for (const key of POTENTIAL_SLOT_KEYS) {
const slotRaw = await chain.head.get(key)
const slotRaw = await head.get(key)
if (slotRaw) {
return meta.registry.createType<Slot>('Slot', hexToU8a(slotRaw)).toNumber()
}
}
throw new Error('Cannot find current slot')
const timestamp = await getCurrentTimestamp(head)
const slotDuration = await getSlotDuration(head)
return Math.floor(Number(timestamp / BigInt(slotDuration)))
}

export const getCurrentTimestamp = async (chain: Blockchain) => {
const meta = await chain.head.meta
const timestamp = await chain.head.read('u64', meta.query.timestamp.now)
return timestamp?.toBigInt() ?? 0n
export const getCurrentTimestamp = async (head: Block) => {
const meta = await head.meta
const timestamp = await head.read('u64', meta.query.timestamp.now)
return timestamp?.toBigInt() ?? BigInt(Date.now())
}

export const getSlotDuration = async (chain: Blockchain) => {
const meta = await chain.head.meta
export const getSlotDuration = async (head: Block) => {
const meta = await head.meta
return meta.consts.babe
? (meta.consts.babe.expectedBlockTime as any as BN).toNumber()
: meta.query.aura
? getAuraSlotDuration(await chain.head.wasm)
? getAuraSlotDuration(await head.wasm)
: meta.consts.asyncBacking
? (meta.consts.asyncBacking.expectedBlockTime as any as BN).toNumber()
: 12_000
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils/time-travel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { setStorage } from './set-storage.js'
export const timeTravel = async (chain: Blockchain, timestamp: number) => {
const meta = await chain.head.meta

const slotDuration = await getSlotDuration(chain)
const slotDuration = await getSlotDuration(chain.head)
const newSlot = Math.floor(timestamp / slotDuration)

// new timestamp
Expand Down
11 changes: 3 additions & 8 deletions packages/e2e/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ import { RegisteredTypes } from '@polkadot/types/types'
import { beforeAll, beforeEach, expect, vi } from 'vitest'

import { Api } from '@acala-network/chopsticks'
import {
Blockchain,
BuildBlockMode,
GenesisProvider,
StorageValues,
genesisSetup,
} from '@acala-network/chopsticks-core'
import { Blockchain, BuildBlockMode, StorageValues } from '@acala-network/chopsticks-core'
import { SqliteDatabase } from '@acala-network/chopsticks-db'
import { createServer } from '@acala-network/chopsticks/server.js'
import { defer } from '@acala-network/chopsticks-core/utils/index.js'
Expand Down Expand Up @@ -96,7 +90,8 @@ export const setupAll = async ({
})

if (genesis) {
await genesisSetup(chain, provider as GenesisProvider)
// build 1st block
await chain.newBlock()
}

const { port, close } = await createServer(handler({ chain }), 0)
Expand Down
4 changes: 2 additions & 2 deletions packages/e2e/src/time-travel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ describe.each(['polkadot', 'acala'])('Can time-travel on %s', async (name) => {

await timeTravel(chain, timestamp)

expect(await getCurrentTimestamp(chain)).eq(BigInt(timestamp))
expect(await getCurrentTimestamp(chain.head)).eq(BigInt(timestamp))

// can build block successfully
await ws.send('dev_newBlock', [])

expect(await getCurrentTimestamp(chain)).eq(BigInt(timestamp + (await getSlotDuration(chain))))
expect(await getCurrentTimestamp(chain.head)).eq(BigInt(timestamp + (await getSlotDuration(chain.head))))
})
})

0 comments on commit 8319838

Please sign in to comment.