Skip to content

Commit 44f6bfe

Browse files
ryanioacolytec3
authored andcommitted
VM, Blockchain, Client: fix storing unsettled promises (develop) (#1811)
* blockchain: remove storing unsettled promise * vm: remove storing unsettled promises. make copy() async * client: update to new async changes * block: doc nit * nits * fix vm benchmarks, add _init to mockchain * Make blockchain and vm constructors protected * lint * vm: update new tests to await Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>
1 parent 5ce06ba commit 44f6bfe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+408
-357
lines changed

packages/block/src/header.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export class BlockHeader {
167167
/**
168168
* This constructor takes the values, validates them, assigns them and freezes the object.
169169
*
170-
* @deprecated - Use the public static factory methods to assist in creating a Header object from
170+
* @deprecated Use the public static factory methods to assist in creating a Header object from
171171
* varying data types. For a default empty header, use {@link BlockHeader.fromHeaderData}.
172172
*
173173
*/

packages/blockchain/src/index.ts

Lines changed: 39 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@ import {
1717
CLIQUE_NONCE_AUTH,
1818
CLIQUE_NONCE_DROP,
1919
} from './clique'
20-
21-
const debug = createDebugLogger('blockchain:clique')
22-
2320
// eslint-disable-next-line implicit-dependencies/no-implicit
2421
import type { LevelUp } from 'levelup'
2522
const level = require('level-mem')
2623

24+
const debug = createDebugLogger('blockchain:clique')
25+
2726
type OnBlock = (block: Block, reorg: boolean) => Promise<void> | void
2827

2928
export interface BlockchainInterface {
@@ -112,7 +111,7 @@ export interface BlockchainOptions {
112111
validateBlocks?: boolean
113112

114113
/**
115-
* The blockchain only initializes succesfully if it has a genesis block. If
114+
* The blockchain only initializes successfully if it has a genesis block. If
116115
* there is no block available in the DB and a `genesisBlock` is provided,
117116
* then the provided `genesisBlock` will be used as genesis. If no block is
118117
* present in the DB and no block is provided, then the genesis block as
@@ -130,18 +129,25 @@ export default class Blockchain implements BlockchainInterface {
130129

131130
private _genesis?: Buffer // the genesis hash of this blockchain
132131

133-
// The following two heads and the heads stored within the `_heads` always point
134-
// to a hash in the canonical chain and never to a stale hash.
135-
// With the exception of `_headHeaderHash` this does not necessarily need to be
136-
// the hash with the highest total difficulty.
137-
private _headBlockHash?: Buffer // the hash of the current head block
138-
private _headHeaderHash?: Buffer // the hash of the current head header
139-
// A Map which stores the head of each key (for instance the "vm" key) which is
140-
// updated along a {@link Blockchain.iterator} method run and can be used to (re)run
141-
// non-verified blocks (for instance in the VM).
132+
/**
133+
* The following two heads and the heads stored within the `_heads` always point
134+
* to a hash in the canonical chain and never to a stale hash.
135+
* With the exception of `_headHeaderHash` this does not necessarily need to be
136+
* the hash with the highest total difficulty.
137+
*/
138+
/** The hash of the current head block */
139+
private _headBlockHash?: Buffer
140+
/** The hash of the current head header */
141+
private _headHeaderHash?: Buffer
142+
143+
/**
144+
* A Map which stores the head of each key (for instance the "vm" key) which is
145+
* updated along a {@link Blockchain.iterator} method run and can be used to (re)run
146+
* non-verified blocks (for instance in the VM).
147+
*/
142148
private _heads: { [key: string]: Buffer }
143149

144-
public initPromise: Promise<void>
150+
protected _isInitialized = false
145151
private _lock: Semaphore
146152

147153
private _common: Common
@@ -207,9 +213,7 @@ export default class Blockchain implements BlockchainInterface {
207213

208214
public static async create(opts: BlockchainOptions = {}) {
209215
const blockchain = new Blockchain(opts)
210-
await blockchain.initPromise!.catch((e) => {
211-
throw e
212-
})
216+
await blockchain._init(opts.genesisBlock)
213217
return blockchain
214218
}
215219

@@ -233,16 +237,16 @@ export default class Blockchain implements BlockchainInterface {
233237
}
234238

235239
/**
236-
* Creates new Blockchain object
240+
* Creates new Blockchain object.
237241
*
238-
* @deprecated - The direct usage of this constructor is discouraged since
242+
* @deprecated The direct usage of this constructor is discouraged since
239243
* non-finalized async initialization might lead to side effects. Please
240244
* use the async {@link Blockchain.create} constructor instead (same API).
241245
*
242-
* @param opts - An object with the options that this constructor takes. See
246+
* @param opts An object with the options that this constructor takes. See
243247
* {@link BlockchainOptions}.
244248
*/
245-
constructor(opts: BlockchainOptions = {}) {
249+
protected constructor(opts: BlockchainOptions = {}) {
246250
// Throw on chain or hardfork options removed in latest major release to
247251
// prevent implicit chain setup on a wrong chain
248252
if ('chain' in opts || 'hardfork' in opts) {
@@ -291,9 +295,6 @@ export default class Blockchain implements BlockchainInterface {
291295
if (opts.genesisBlock && !opts.genesisBlock.isGenesis()) {
292296
throw 'supplied block is not a genesis block'
293297
}
294-
295-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
296-
this.initPromise = this._init(opts.genesisBlock)
297298
}
298299

299300
/**
@@ -329,13 +330,14 @@ export default class Blockchain implements BlockchainInterface {
329330
}
330331

331332
/**
332-
* This method is called in the constructor and either sets up the DB or reads
333+
* This method is called in {@link Blockchain.create} and either sets up the DB or reads
333334
* values from the DB and makes these available to the consumers of
334335
* Blockchain.
335336
*
336337
* @hidden
337338
*/
338339
private async _init(genesisBlock?: Block): Promise<void> {
340+
if (this._isInitialized) return
339341
let dbGenesisBlock
340342
try {
341343
const genesisHash = await this.dbManager.numberToHash(BigInt(0))
@@ -421,23 +423,14 @@ export default class Blockchain implements BlockchainInterface {
421423
}
422424
this._headBlockHash = genesisHash
423425
}
426+
424427
if (this._hardforkByHeadBlockNumber) {
425428
const latestHeader = await this._getHeader(this._headHeaderHash)
426429
const td = await this.getTotalDifficulty(this._headHeaderHash)
427430
this._common.setHardforkByBlockNumber(latestHeader.number, td)
428431
}
429-
}
430432

431-
/**
432-
* Perform the `action` function after we have initialized this module and
433-
* have acquired a lock
434-
* @param action - the action function to run after initializing and acquiring
435-
* a lock
436-
* @hidden
437-
*/
438-
private async initAndLock<T>(action: () => Promise<T>): Promise<T> {
439-
await this.initPromise
440-
return await this.runWithLock(action)
433+
this._isInitialized = true
441434
}
442435

443436
/**
@@ -763,7 +756,7 @@ export default class Blockchain implements BlockchainInterface {
763756
* @param name - Optional name of the iterator head (default: 'vm')
764757
*/
765758
async getIteratorHead(name = 'vm'): Promise<Block> {
766-
return await this.initAndLock<Block>(async () => {
759+
return await this.runWithLock<Block>(async () => {
767760
// if the head is not found return the genesis hash
768761
const hash = this._heads[name] || this._genesis
769762
if (!hash) {
@@ -787,7 +780,7 @@ export default class Blockchain implements BlockchainInterface {
787780
* on a first run)
788781
*/
789782
async getHead(name = 'vm'): Promise<Block> {
790-
return await this.initAndLock<Block>(async () => {
783+
return await this.runWithLock<Block>(async () => {
791784
// if the head is not found return the headHeader
792785
const hash = this._heads[name] || this._headBlockHash
793786
if (!hash) {
@@ -803,7 +796,7 @@ export default class Blockchain implements BlockchainInterface {
803796
* Returns the latest header in the canonical chain.
804797
*/
805798
async getLatestHeader(): Promise<BlockHeader> {
806-
return await this.initAndLock<BlockHeader>(async () => {
799+
return await this.runWithLock<BlockHeader>(async () => {
807800
if (!this._headHeaderHash) {
808801
throw new Error('No head header set')
809802
}
@@ -816,7 +809,7 @@ export default class Blockchain implements BlockchainInterface {
816809
* Returns the latest full block in the canonical chain.
817810
*/
818811
async getLatestBlock(): Promise<Block> {
819-
return this.initAndLock<Block>(async () => {
812+
return this.runWithLock<Block>(async () => {
820813
if (!this._headBlockHash) {
821814
throw new Error('No head block set')
822815
}
@@ -836,7 +829,6 @@ export default class Blockchain implements BlockchainInterface {
836829
* @param blocks - The blocks to be added to the blockchain
837830
*/
838831
async putBlocks(blocks: Block[]) {
839-
await this.initPromise
840832
for (let i = 0; i < blocks.length; i++) {
841833
await this.putBlock(blocks[i])
842834
}
@@ -851,7 +843,6 @@ export default class Blockchain implements BlockchainInterface {
851843
* @param block - The block to be added to the blockchain
852844
*/
853845
async putBlock(block: Block) {
854-
await this.initPromise
855846
await this._putBlockOrHeader(block)
856847
}
857848

@@ -865,7 +856,6 @@ export default class Blockchain implements BlockchainInterface {
865856
* @param headers - The headers to be added to the blockchain
866857
*/
867858
async putHeaders(headers: Array<any>) {
868-
await this.initPromise
869859
for (let i = 0; i < headers.length; i++) {
870860
await this.putHeader(headers[i])
871861
}
@@ -880,7 +870,6 @@ export default class Blockchain implements BlockchainInterface {
880870
* @param header - The header to be added to the blockchain
881871
*/
882872
async putHeader(header: BlockHeader) {
883-
await this.initPromise
884873
await this._putBlockOrHeader(header)
885874
}
886875

@@ -1061,7 +1050,6 @@ export default class Blockchain implements BlockchainInterface {
10611050
// in the `VM` if we encounter a `BLOCKHASH` opcode: then a bigint is used we
10621051
// need to then read the block from the canonical chain Q: is this safe? We
10631052
// know it is OK if we call it from the iterator... (runBlock)
1064-
await this.initPromise
10651053
return await this._getBlock(blockId)
10661054
}
10671055

@@ -1096,7 +1084,7 @@ export default class Blockchain implements BlockchainInterface {
10961084
skip: number,
10971085
reverse: boolean
10981086
): Promise<Block[]> {
1099-
return await this.initAndLock<Block[]>(async () => {
1087+
return await this.runWithLock<Block[]>(async () => {
11001088
const blocks: Block[] = []
11011089
let i = -1
11021090

@@ -1133,7 +1121,7 @@ export default class Blockchain implements BlockchainInterface {
11331121
* @param hashes - Ordered array of hashes (ordered on `number`).
11341122
*/
11351123
async selectNeededHashes(hashes: Array<Buffer>): Promise<Buffer[]> {
1136-
return await this.initAndLock<Buffer[]>(async () => {
1124+
return await this.runWithLock<Buffer[]>(async () => {
11371125
let max: number
11381126
let mid: number
11391127
let min: number
@@ -1178,7 +1166,6 @@ export default class Blockchain implements BlockchainInterface {
11781166
// But is this the way to go? If we know this is called from the
11791167
// iterator/runBlockchain we are safe, but if this is called from anywhere
11801168
// else then this might lead to a concurrency problem?
1181-
await this.initPromise
11821169
await this._delBlock(blockHash)
11831170
}
11841171

@@ -1278,7 +1265,7 @@ export default class Blockchain implements BlockchainInterface {
12781265
* @hidden
12791266
*/
12801267
private async _iterator(name: string, onBlock: OnBlock, maxBlocks?: number): Promise<number> {
1281-
return await this.initAndLock<number>(async (): Promise<number> => {
1268+
return await this.runWithLock<number>(async (): Promise<number> => {
12821269
const headHash = this._heads[name] || this._genesis
12831270
let lastBlock: Block | undefined
12841271

@@ -1319,7 +1306,7 @@ export default class Blockchain implements BlockchainInterface {
13191306

13201307
/**
13211308
* Set header hash of a certain `tag`.
1322-
* When calling the iterator, the iterator will start running the first child block after the header hash currenntly stored.
1309+
* When calling the iterator, the iterator will start running the first child block after the header hash currently stored.
13231310
* @param tag - The tag to save the headHash to
13241311
* @param headHash - The head hash to save
13251312
*/
@@ -1329,14 +1316,14 @@ export default class Blockchain implements BlockchainInterface {
13291316

13301317
/**
13311318
* Set header hash of a certain `tag`.
1332-
* When calling the iterator, the iterator will start running the first child block after the header hash currenntly stored.
1319+
* When calling the iterator, the iterator will start running the first child block after the header hash currently stored.
13331320
* @param tag - The tag to save the headHash to
13341321
* @param headHash - The head hash to save
13351322
*
13361323
* @deprecated use {@link Blockchain.setIteratorHead()} instead
13371324
*/
13381325
async setHead(tag: string, headHash: Buffer) {
1339-
await this.initAndLock<void>(async () => {
1326+
await this.runWithLock<void>(async () => {
13401327
this._heads[tag] = headHash
13411328
await this._saveHeads()
13421329
})

packages/blockchain/test/clique.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { CLIQUE_NONCE_AUTH, CLIQUE_NONCE_DROP } from '../src/clique'
88
tape('Clique: Initialization', (t) => {
99
t.test('should initialize a clique blockchain', async (st) => {
1010
const common = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.Chainstart })
11-
const blockchain = new Blockchain({ common })
11+
const blockchain = await Blockchain.create({ common })
1212

1313
const head = await blockchain.getHead()
1414
st.equal(head.hash().toString('hex'), common.genesis().hash.slice(2), 'correct genesis hash')

0 commit comments

Comments
 (0)