-
Notifications
You must be signed in to change notification settings - Fork 684
feat: replace legacyInstamine option with instamine=eager|strict
#2013
Changes from all commits
35be9aa
f81314a
864a882
ead0996
3bce76d
7d6438a
1f555a2
17be732
e355e5d
9e33e4a
337e637
1085ddc
e5d966e
f0c26ff
429d3ad
50ca402
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,7 +115,7 @@ export type BlockchainOptions = { | |
| coinbase: Account; | ||
| chainId: number; | ||
| common: Common; | ||
| legacyInstamine: boolean; | ||
| instamine: "eager" | "strict"; | ||
| vmErrorsOnRPCResponse: boolean; | ||
| logger: Logger; | ||
| }; | ||
|
|
@@ -215,36 +215,8 @@ export default class Blockchain extends Emittery<BlockchainTypedEvents> { | |
|
|
||
| this.#options = options; | ||
| this.fallback = fallback; | ||
|
|
||
| const instamine = (this.#instamine = | ||
| !options.miner.blockTime || options.miner.blockTime <= 0); | ||
| const legacyInstamine = options.miner.legacyInstamine; | ||
|
|
||
| { | ||
| // warnings and errors | ||
| if (legacyInstamine) { | ||
| console.info( | ||
| "Legacy instamining, where transactions are fully mined before the hash is returned, is deprecated and will be removed in the future." | ||
| ); | ||
| } | ||
|
|
||
| if (!instamine) { | ||
| if (legacyInstamine) { | ||
| console.info( | ||
| "Setting `legacyInstamine` to `true` has no effect when blockTime is non-zero" | ||
| ); | ||
| } | ||
|
|
||
| if (options.chain.vmErrorsOnRPCResponse) { | ||
| console.info( | ||
| "Setting `vmErrorsOnRPCResponse` to `true` has no effect on transactions when blockTime is non-zero" | ||
| ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| this.coinbase = coinbase; | ||
|
|
||
| this.#instamine = !options.miner.blockTime || options.miner.blockTime <= 0; | ||
| this.#database = new Database(options.database, this); | ||
| } | ||
|
|
||
|
|
@@ -339,9 +311,8 @@ export default class Blockchain extends Emittery<BlockchainTypedEvents> { | |
| options.miner.blockGasLimit, | ||
| initialAccounts | ||
| ); | ||
| blocks.earliest = blocks.latest = await this.#blockBeingSavedPromise.then( | ||
| ({ block }) => block | ||
| ); | ||
| blocks.earliest = blocks.latest = | ||
| await this.#blockBeingSavedPromise.then(({ block }) => block); | ||
|
Comment on lines
+314
to
+315
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (prettier change) |
||
| } | ||
| } | ||
|
|
||
|
|
@@ -476,6 +447,9 @@ export default class Blockchain extends Emittery<BlockchainTypedEvents> { | |
| }); | ||
| }; | ||
|
|
||
| /** | ||
| * Emit the block now that everything has been fully saved to the database | ||
| */ | ||
| #emitNewBlock = async (blockInfo: { | ||
| block: Block; | ||
| blockLogs: BlockLogs; | ||
|
|
@@ -484,15 +458,21 @@ export default class Blockchain extends Emittery<BlockchainTypedEvents> { | |
| const options = this.#options; | ||
| const { block, blockLogs, transactions } = blockInfo; | ||
|
|
||
| // emit the block once everything has been fully saved to the database | ||
| transactions.forEach(transaction => { | ||
| transaction.finalize("confirmed", transaction.execException); | ||
| }); | ||
|
|
||
| if (this.#instamine && options.miner.legacyInstamine) { | ||
| // in legacy instamine mode we must delay the broadcast of new blocks | ||
| if (this.#instamine && options.miner.instamine === "eager") { | ||
| // in eager instamine mode we must delay the broadcast of new blocks | ||
| await new Promise(resolve => { | ||
| process.nextTick(async () => { | ||
| // we delay emitting blocks and blockLogs because we need to allow for: | ||
| // ``` | ||
| // await provider.request({"method": "eth_sendTransaction"...) | ||
| // await provider.once("message") // <- should work | ||
| // ``` | ||
| // If we don't have this delay here the messages will be sent before | ||
| // the call has a chance to listen to the event. | ||
| setImmediate(async () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It turns out this was broken in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is broken? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The delay wasn't working. Our existing tests caught it when I switched the default. |
||
| // emit block logs first so filters can pick them up before | ||
| // block listeners are notified | ||
| await Promise.all([ | ||
|
|
@@ -975,11 +955,11 @@ export default class Blockchain extends Emittery<BlockchainTypedEvents> { | |
| if (this.#isPaused() || !this.#instamine) { | ||
| return hash; | ||
| } else { | ||
| if (this.#instamine && this.#options.miner.legacyInstamine) { | ||
| // in legacyInstamine mode we must wait for the transaction to be saved | ||
| if (this.#instamine && this.#options.miner.instamine === "eager") { | ||
| // in eager instamine mode we must wait for the transaction to be saved | ||
| // before we can return the hash | ||
| const { status, error } = await transaction.once("finalized"); | ||
| // in legacyInstamine mode we must throw on all rejected transaction | ||
| // in eager instamine mode we must throw on all rejected transaction | ||
| // errors. We must also throw on `confirmed` transactions when | ||
| // vmErrorsOnRPCResponse is enabled. | ||
| if ( | ||
|
|
@@ -1438,17 +1418,13 @@ export default class Blockchain extends Emittery<BlockchainTypedEvents> { | |
| ); | ||
|
|
||
| // #3 - Rerun every transaction in block prior to and including the requested transaction | ||
| const { | ||
| gas, | ||
| structLogs, | ||
| returnValue, | ||
| storage | ||
| } = await this.#traceTransaction( | ||
| newBlock.transactions[transaction.index.toNumber()], | ||
| trie, | ||
| newBlock, | ||
| options | ||
| ); | ||
| const { gas, structLogs, returnValue, storage } = | ||
| await this.#traceTransaction( | ||
| newBlock.transactions[transaction.index.toNumber()], | ||
| trie, | ||
| newBlock, | ||
| options | ||
| ); | ||
|
Comment on lines
+1421
to
+1427
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (prettier change) |
||
|
|
||
| // #4 - Send results back | ||
| return { gas, structLogs, returnValue, storage }; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -223,7 +223,6 @@ export default class Miner extends Emittery<{ | |
|
|
||
| let keepMining = true; | ||
| const priced = this.#priced; | ||
| const legacyInstamine = this.#options.legacyInstamine; | ||
| const storageKeys: StorageKeys = new Map(); | ||
| let blockTransactions: TypedTransaction[]; | ||
| do { | ||
|
|
@@ -424,13 +423,7 @@ export default class Miner extends Emittery<{ | |
| storageKeys | ||
| ); | ||
| block = finalizedBlockData.block; | ||
| const emitBlockProm = this.emit("block", finalizedBlockData); | ||
| if (legacyInstamine === true) { | ||
| // we need to wait for each block to be done mining when in legacy | ||
| // mode because things like `mine` and `miner_start` must wait for the | ||
| // first mine operation to be fully complete. | ||
| await emitBlockProm; | ||
| } | ||
| this.emit("block", finalizedBlockData); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to wait because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you feel it necessary to explain this in the review it likely belongs as a comment in the code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It explains why the old code was removed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm actually having to revisit this because I noticed if someone sends stops the miner via miner_stop, send enough transactions to fill two blocks, then calls I think These issues might be rare enough that I can wait until after v7 stable to fix them, as it might take a bit of refactoring to get the timings correct. |
||
|
|
||
| if (onlyOneBlock) { | ||
| this.#currentlyExecutingPrice = 0n; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import getProvider from "../../helpers/getProvider"; | ||
| import getProvider, { mnemonic } from "../../helpers/getProvider"; | ||
| import assert from "assert"; | ||
| import EthereumProvider from "../../../src/provider"; | ||
|
|
||
|
|
@@ -21,7 +21,10 @@ describe("api", () => { | |
| } | ||
| } | ||
| }; | ||
| provider = await getProvider({ logging: { logger } }); | ||
| provider = await getProvider({ | ||
| logging: { logger }, | ||
| wallet: { mnemonic } | ||
| }); | ||
| [from] = await provider.send("eth_accounts"); | ||
| }); | ||
|
|
||
|
|
@@ -61,16 +64,23 @@ describe("api", () => { | |
| ); | ||
| }); | ||
|
|
||
| describe("legacy instamine detection and notice", () => { | ||
| describe("strict instamine detection and notice", () => { | ||
| it("logs a warning if the transaction hasn't been mined yet", async () => { | ||
| const hash = await provider.send("eth_sendTransaction", [ | ||
| { from, to: from } | ||
| ]); | ||
| const strictInstamineProvider = await getProvider({ | ||
| logging: { logger }, | ||
| miner: { instamine: "strict" }, | ||
| wallet: { mnemonic } | ||
| }); | ||
| const hash = await strictInstamineProvider.send( | ||
| "eth_sendTransaction", | ||
| [{ from, to: from }] | ||
| ); | ||
|
|
||
| // do not wait for the tx to be mined which will create a warning | ||
| const result = await provider.send("eth_getTransactionReceipt", [ | ||
| hash | ||
| ]); | ||
| const result = await strictInstamineProvider.send( | ||
| "eth_getTransactionReceipt", | ||
| [hash] | ||
| ); | ||
|
Comment on lines
+80
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (prettier change) |
||
|
|
||
| assert.strictEqual(result, null); | ||
| assert( | ||
|
|
@@ -125,26 +135,26 @@ describe("api", () => { | |
| ); | ||
| }); | ||
|
|
||
| it("doesn't log if legacyInstamine is enabled", async () => { | ||
| const legacyInstamineProvider = await getProvider({ | ||
| it("doesn't log if instamine is set to 'eager' (default)", async () => { | ||
| const eagerInstamineProvider = await getProvider({ | ||
| logging: { logger }, | ||
| miner: { legacyInstamine: true } | ||
| miner: { instamine: "eager" } | ||
| }); | ||
|
|
||
| const [from] = await legacyInstamineProvider.send("eth_accounts"); | ||
| const [from] = await eagerInstamineProvider.send("eth_accounts"); | ||
|
|
||
| const hash = await legacyInstamineProvider.send( | ||
| const hash = await eagerInstamineProvider.send( | ||
| "eth_sendTransaction", | ||
| [{ from, to: from }] | ||
| ); | ||
|
|
||
| const result = await legacyInstamineProvider.send( | ||
| const result = await eagerInstamineProvider.send( | ||
| "eth_getTransactionReceipt", | ||
| [hash] | ||
| ); | ||
|
|
||
| // the tx is mined before sending the tx hash back to the user | ||
| // if legacyInstamine is enabled - so they will get a receipt | ||
| // if instamine is set to 'eager' - so they will get a receipt | ||
| assert(result); | ||
| assert( | ||
| !logger.loggedStuff.includes( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.