Skip to content

Commit

Permalink
refac wait for snap sync completion
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Mar 14, 2023
1 parent 3fe114d commit d0766a7
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 22 deletions.
1 change: 0 additions & 1 deletion packages/client/lib/sync/snapsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ export class SnapSynchronizer extends Synchronizer {

/**
* Start synchronizer.
* If passed a block, will initialize sync starting from the block.
*/
async start(): Promise<void> {
if (this.running) return
Expand Down
10 changes: 3 additions & 7 deletions packages/client/lib/sync/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export abstract class Synchronizer {
// Time (in ms) after which the synced state is reset
private SYNCED_STATE_REMOVAL_PERIOD = 60000
private _syncedStatusCheckInterval: NodeJS.Timeout | undefined /* global NodeJS */
private syncPromise: Promise<boolean> | null = null

/**
* Create new node
Expand Down Expand Up @@ -153,13 +152,12 @@ export abstract class Synchronizer {
numAttempts += 1
}

if (this.syncPromise || !(await this.syncWithPeer(peer))) return false
if (!(await this.syncWithPeer(peer))) return false

// eslint-disable-next-line no-async-promise-executor
this.syncPromise = new Promise(async (resolve, reject) => {
return new Promise(async (resolve, reject) => {
const resolveSync = (height?: number) => {
this.clearFetcher()
this.syncPromise = null
resolve(true)
const heightStr = typeof height === 'number' && height !== 0 ? ` height=${height}` : ''
this.config.logger.info(`Finishing up sync with the current fetcher ${heightStr}`)
Expand All @@ -176,11 +174,9 @@ export abstract class Synchronizer {
`Received sync error, stopping sync and clearing fetcher: ${error.message ?? error}`
)
this.clearFetcher()
this.syncPromise = null
reject(error)
}
})
return this.syncPromise
}

/**
Expand All @@ -198,10 +194,10 @@ export abstract class Synchronizer {
* Stop synchronizer.
*/
async stop(): Promise<boolean> {
this.clearFetcher()
if (!this.running) {
return false
}
this.clearFetcher()
clearInterval(this._syncedStatusCheckInterval as NodeJS.Timeout)
await new Promise((resolve) => setTimeout(resolve, this.interval))
this.running = false
Expand Down
3 changes: 3 additions & 0 deletions packages/client/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum Event {
SYNC_SYNCHRONIZED = 'sync:synchronized',
SYNC_ERROR = 'sync:error',
SYNC_FETCHER_ERROR = 'sync:fetcher:error',
SYNC_SNAPSYNC_COMPLETE = 'sync:snapsync:complete',
PEER_CONNECTED = 'peer:connected',
PEER_DISCONNECTED = 'peer:disconnected',
PEER_ERROR = 'peer:error',
Expand All @@ -40,6 +41,7 @@ export interface EventParams {
[Event.SYNC_FETCHED_BLOCKS]: [blocks: Block[]]
[Event.SYNC_FETCHED_HEADERS]: [headers: BlockHeader[]]
[Event.SYNC_SYNCHRONIZED]: [chainHeight: bigint]
[Event.SYNC_SNAPSYNC_COMPLETE]: [stateRoot: Uint8Array]
[Event.SYNC_ERROR]: [syncError: Error]
[Event.SYNC_FETCHER_ERROR]: [fetchError: Error, task: any, peer: Peer | null | undefined]
[Event.PEER_CONNECTED]: [connectedPeer: Peer]
Expand Down Expand Up @@ -67,6 +69,7 @@ export type EventBusType = EventBus<Event.CHAIN_UPDATED> &
EventBus<Event.SYNC_FETCHED_BLOCKS> &
EventBus<Event.SYNC_FETCHED_HEADERS> &
EventBus<Event.SYNC_SYNCHRONIZED> &
EventBus<Event.SYNC_SNAPSYNC_COMPLETE> &
EventBus<Event.SYNC_FETCHER_ERROR> &
EventBus<Event.PEER_CONNECTED> &
EventBus<Event.PEER_DISCONNECTED> &
Expand Down
42 changes: 28 additions & 14 deletions packages/client/test/sim/snapsync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const networkJson = require(`./configs/${network}.json`)
const common = Common.fromGethGenesis(networkJson, { chain: network })
const customGenesisState = parseGethGenesisState(networkJson)
let ejsClient: EthereumClient | null = null
let snapCompleted: Promise<unknown> | undefined = undefined

export async function runTx(data: string, to?: string, value?: bigint) {
return runTxHelper({ client, common, sender, pkey }, data, to, value)
Expand Down Expand Up @@ -106,16 +107,17 @@ tape('simple mainnet test run', async (t) => {

t.test('setup snap sync', { skip: process.env.SNAP_SYNC === undefined }, async (st) => {
// start client inline here for snap sync, no need for beacon
// eslint-disable-next-line @typescript-eslint/no-use-before-define
const { ejsInlineClient, peerConnectedPromise } = (await createSnapClient(
common,
customGenesisState,
[nodeInfo.enode]
).catch((e) => {
console.log(e)
return null
})) ?? { ejsInlineClient: null, peerConnectedPromise: Promise.reject('Client creation error') }
const { ejsInlineClient, peerConnectedPromise, snapSyncCompletedPromise } =
// eslint-disable-next-line @typescript-eslint/no-use-before-define
(await createSnapClient(common, customGenesisState, [nodeInfo.enode]).catch((e) => {
console.log(e)
return null
})) ?? {
ejsInlineClient: null,
peerConnectedPromise: Promise.reject('Client creation error'),
}
ejsClient = ejsInlineClient
snapCompleted = snapSyncCompletedPromise
st.ok(ejsClient !== null, 'ethereumjs client started')

const enode = (ejsClient!.server('rlpx') as RlpxServer)!.getRlpxInfo().enode
Expand All @@ -134,13 +136,22 @@ tape('simple mainnet test run', async (t) => {

t.test('should snap sync and finish', async (st) => {
try {
if (ejsClient !== null) {
if (ejsClient !== null && snapCompleted !== undefined) {
// call sync if not has been called yet
await ejsClient.services[0].synchronizer.sync()
void ejsClient.services[0].synchronizer.sync()
// wait on the sync promise to complete if it has been called independently
await ejsClient.services[0].synchronizer['syncPromise']
const snapSyncTimeout = new Promise((_resolve, reject) => setTimeout(reject, 40000))
try {
await Promise.race([snapCompleted, snapSyncTimeout])
st.pass('completed snap sync')
} catch (e) {
st.fail('could not complete snap sync in 40 seconds')
}
await ejsClient.stop()
} else {
st.fail('ethereumjs client not setup properly for snap sync')
}

await teardownCallBack()
st.pass('network cleaned')
} catch (e) {
Expand Down Expand Up @@ -168,11 +179,14 @@ async function createSnapClient(common: any, customGenesisState: any, bootnodes:
forceSnapSync: true,
})
const peerConnectedPromise = new Promise((resolve) => {
config.events.on(Event.PEER_CONNECTED, (peer: any) => resolve(peer))
config.events.once(Event.PEER_CONNECTED, (peer: any) => resolve(peer))
})
const snapSyncCompletedPromise = new Promise((resolve) => {
config.events.once(Event.SYNC_SNAPSYNC_COMPLETE, (stateRoot: any) => resolve(stateRoot))
})

const ejsInlineClient = await createInlineClient(config, common, customGenesisState)
return { ejsInlineClient, peerConnectedPromise }
return { ejsInlineClient, peerConnectedPromise, snapSyncCompletedPromise }
}

process.on('uncaughtException', (err, origin) => {
Expand Down

0 comments on commit d0766a7

Please sign in to comment.