Skip to content

Instantly share code, notes, and snippets.

@rumkin
Created March 2, 2020 11:36
Show Gist options
  • Save rumkin/93f6419213944d981427c7a64a391941 to your computer and use it in GitHub Desktop.
Save rumkin/93f6419213944d981427c7a64a391941 to your computer and use it in GitHub Desktop.
Ethereum VM initialize patch
--- new.patch 2020-03-02 14:15:28.000000000 +0300
+++ old.patch 2020-03-02 14:16:08.000000000 +0300
@@ -1,17 +1,17 @@
-From 00bb40d8982cd986a37a8072691060a8b72ce11d Mon Sep 17 00:00:00 2001
+From 054bc49da2332948df9e015d7353a94d3c162d52 Mon Sep 17 00:00:00 2001
From: Pavel Rumkin <dev@rumk.in>
Date: Sat, 22 Feb 2020 09:31:44 +0300
Subject: [PATCH 1/2] Add initialization method to prevent race conditions
---
- lib/index.ts | 39 +++++++++++++++++++++++++++++----------
- tests/api/index.js | 5 +++--
+ packages/vm/lib/index.ts | 39 +++++++++++++++++++++++++---------
+ packages/vm/tests/api/index.js | 5 +++--
2 files changed, 32 insertions(+), 12 deletions(-)
-diff --git a/lib/index.ts b/lib/index.ts
+diff --git a/packages/vm/lib/index.ts b/packages/vm/lib/index.ts
index 2271120..aa351b3 100644
---- a/lib/index.ts
-+++ b/lib/index.ts
+--- a/packages/vm/lib/index.ts
++++ b/packages/vm/lib/index.ts
@@ -73,6 +73,7 @@ export default class VM extends AsyncEventEmitter {
_opcodes: OpcodeList
public readonly _emit: (topic: string, data: any) => Promise<void>
@@ -107,10 +107,10 @@
return runCode.bind(this)(opts)
}
-diff --git a/tests/api/index.js b/tests/api/index.js
+diff --git a/packages/vm/tests/api/index.js b/packages/vm/tests/api/index.js
index 3c7e8d0..9714c0e 100644
---- a/tests/api/index.js
-+++ b/tests/api/index.js
+--- a/packages/vm/tests/api/index.js
++++ b/packages/vm/tests/api/index.js
@@ -17,9 +17,10 @@ tape('VM with default blockchain', (t) => {
st.end()
})
@@ -128,20 +128,20 @@
2.21.1 (Apple Git-122.3)
-From 0bc33e3c1168062c1ab1bd42fb17d272adb663f5 Mon Sep 17 00:00:00 2001
+From 05148b63a06a9e6a4bff1e8ae74e5aaf198252a4 Mon Sep 17 00:00:00 2001
From: Pavel Rumkin <dev@rumk.in>
Date: Wed, 26 Feb 2020 13:08:13 +0300
Subject: [PATCH 2/2] Added async constructor.
---
- lib/index.ts | 11 ++++++++++-
- tests/api/index.js | 27 +++++++++++++++++++++------
+ packages/vm/lib/index.ts | 11 ++++++++++-
+ packages/vm/tests/api/index.js | 27 +++++++++++++++++++++------
2 files changed, 31 insertions(+), 7 deletions(-)
-diff --git a/lib/index.ts b/lib/index.ts
+diff --git a/packages/vm/lib/index.ts b/packages/vm/lib/index.ts
index aa351b3..ecccaab 100644
---- a/lib/index.ts
-+++ b/lib/index.ts
+--- a/packages/vm/lib/index.ts
++++ b/packages/vm/lib/index.ts
@@ -74,7 +74,16 @@ export default class VM extends AsyncEventEmitter {
public readonly _emit: (topic: string, data: any) => Promise<void>
public readonly pStateManager: PStateManager
@@ -160,10 +160,10 @@
/**
* Instantiates a new [[VM]] Object.
* @param opts - Default values for the options are:
-diff --git a/tests/api/index.js b/tests/api/index.js
+diff --git a/packages/vm/tests/api/index.js b/packages/vm/tests/api/index.js
index 9714c0e..ed934e8 100644
---- a/tests/api/index.js
-+++ b/tests/api/index.js
+--- a/packages/vm/tests/api/index.js
++++ b/packages/vm/tests/api/index.js
@@ -24,11 +24,18 @@ tape('VM with default blockchain', (t) => {
st.end()
})
From 00bb40d8982cd986a37a8072691060a8b72ce11d Mon Sep 17 00:00:00 2001
From: Pavel Rumkin <dev@rumk.in>
Date: Sat, 22 Feb 2020 09:31:44 +0300
Subject: [PATCH 1/2] Add initialization method to prevent race conditions
---
lib/index.ts | 39 +++++++++++++++++++++++++++++----------
tests/api/index.js | 5 +++--
2 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/lib/index.ts b/lib/index.ts
index 2271120..aa351b3 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -73,6 +73,7 @@ export default class VM extends AsyncEventEmitter {
_opcodes: OpcodeList
public readonly _emit: (topic: string, data: any) => Promise<void>
public readonly pStateManager: PStateManager
+ protected isInitialized: boolean = false
/**
* Instantiates a new [[VM]] Object.
@@ -116,11 +117,6 @@ export default class VM extends AsyncEventEmitter {
this.stateManager = opts.stateManager
} else {
const trie = opts.state || new Trie()
- if (opts.activatePrecompiles) {
- for (let i = 1; i <= 8; i++) {
- trie.put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize())
- }
- }
this.stateManager = new StateManager({ trie, common: this._common })
}
@@ -136,6 +132,24 @@ export default class VM extends AsyncEventEmitter {
this._emit = promisify(this.emit.bind(this))
}
+ async init(): Promise<void> {
+ if (this.isInitialized) {
+ return
+ }
+
+ const { opts } = this
+
+ if (opts.activatePrecompiles && !opts.stateManager) {
+ const trie = this.stateManager._trie
+ const put = promisify(trie.put.bind(trie))
+ for (let i = 1; i <= 8; i++) {
+ await put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize())
+ }
+ }
+
+ this.isInitialized = true
+ }
+
/**
* Processes blocks and adds them to the blockchain.
*
@@ -143,7 +157,8 @@ export default class VM extends AsyncEventEmitter {
*
* @param blockchain - A [blockchain](https://github.com/ethereum/ethereumjs-blockchain) object to process
*/
- runBlockchain(blockchain: any): Promise<void> {
+ async runBlockchain(blockchain: any): Promise<void> {
+ await this.init()
return runBlockchain.bind(this)(blockchain)
}
@@ -157,7 +172,8 @@ export default class VM extends AsyncEventEmitter {
* @param opts - Default values for options:
* - `generate`: false
*/
- runBlock(opts: RunBlockOpts): Promise<RunBlockResult> {
+ async runBlock(opts: RunBlockOpts): Promise<RunBlockResult> {
+ await this.init()
return runBlock.bind(this)(opts)
}
@@ -168,7 +184,8 @@ export default class VM extends AsyncEventEmitter {
* when the error is thrown from an event handler. In the latter case the state may or may not be
* reverted.
*/
- runTx(opts: RunTxOpts): Promise<RunTxResult> {
+ async runTx(opts: RunTxOpts): Promise<RunTxResult> {
+ await this.init()
return runTx.bind(this)(opts)
}
@@ -177,7 +194,8 @@ export default class VM extends AsyncEventEmitter {
*
* This method modifies the state.
*/
- runCall(opts: RunCallOpts): Promise<EVMResult> {
+ async runCall(opts: RunCallOpts): Promise<EVMResult> {
+ await this.init()
return runCall.bind(this)(opts)
}
@@ -186,7 +204,8 @@ export default class VM extends AsyncEventEmitter {
*
* This method modifies the state.
*/
- runCode(opts: RunCodeOpts): Promise<ExecResult> {
+ async runCode(opts: RunCodeOpts): Promise<ExecResult> {
+ await this.init()
return runCode.bind(this)(opts)
}
diff --git a/tests/api/index.js b/tests/api/index.js
index 3c7e8d0..9714c0e 100644
--- a/tests/api/index.js
+++ b/tests/api/index.js
@@ -17,9 +17,10 @@ tape('VM with default blockchain', (t) => {
st.end()
})
- t.test('should be able to activate precompiles', (st) => {
+ t.test('should be able to activate precompiles', async (st) => {
let vm = new VM({ activatePrecompiles: true })
- st.notEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
+ await vm.init()
+ st.notDeepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
st.end()
})
--
2.21.1 (Apple Git-122.3)
From 0bc33e3c1168062c1ab1bd42fb17d272adb663f5 Mon Sep 17 00:00:00 2001
From: Pavel Rumkin <dev@rumk.in>
Date: Wed, 26 Feb 2020 13:08:13 +0300
Subject: [PATCH 2/2] Added async constructor.
---
lib/index.ts | 11 ++++++++++-
tests/api/index.js | 27 +++++++++++++++++++++------
2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/lib/index.ts b/lib/index.ts
index aa351b3..ecccaab 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -74,7 +74,16 @@ export default class VM extends AsyncEventEmitter {
public readonly _emit: (topic: string, data: any) => Promise<void>
public readonly pStateManager: PStateManager
protected isInitialized: boolean = false
-
+ /**
+ * VM async constructor. Creates engine instance and initializes it.
+ *
+ * @param opts VM engine constructor options
+ */
+ static async create(opts: VMOpts = {}): Promise<VM> {
+ const vm = new this(opts)
+ await vm.init()
+ return vm
+ }
/**
* Instantiates a new [[VM]] Object.
* @param opts - Default values for the options are:
diff --git a/tests/api/index.js b/tests/api/index.js
index 9714c0e..ed934e8 100644
--- a/tests/api/index.js
+++ b/tests/api/index.js
@@ -24,11 +24,18 @@ tape('VM with default blockchain', (t) => {
st.end()
})
- t.test('should work with trie (state) provided', (st) => {
+ t.test('should instantiate with async constructor', async (st) => {
+ let vm = await VM.create({ activatePrecompiles: true })
+ st.notDeepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
+ st.end()
+ })
+
+ t.test('should work with trie (state) provided', async (st) => {
let trie = new Trie()
trie.isTestTrie = true
let vm = new VM({ state: trie, activatePrecompiles: true })
- st.notEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
+ await vm.init()
+ st.notDeepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
st.ok(vm.stateManager._trie.isTestTrie, 'it works on trie provided')
st.end()
})
@@ -43,17 +50,19 @@ tape('VM with default blockchain', (t) => {
st.end()
})
- t.test('should accept a common object as option', (st) => {
+ t.test('should accept a common object as option', async (st) => {
const common = new Common('mainnet', 'istanbul')
const vm = new VM({ common })
+ await vm.init()
st.equal(vm._common, common)
st.end()
})
- t.test('should only accept valid chain and fork', (st) => {
+ t.test('should only accept valid chain and fork', async (st) => {
let vm = new VM({ chain: 'ropsten', hardfork: 'byzantium' })
+ await vm.init()
st.equal(vm.stateManager._common.param('gasPrices', 'ecAdd'), 500)
try {
@@ -74,8 +83,9 @@ tape('VM with default blockchain', (t) => {
})
tape('VM with blockchain', (t) => {
- t.test('should instantiate', (st) => {
+ t.test('should instantiate', async (st) => {
const vm = setupVM()
+ await vm.init()
st.deepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has default trie')
st.end()
})
@@ -88,6 +98,8 @@ tape('VM with blockchain', (t) => {
t.test('should run blockchain with mocked runBlock', async (st) => {
const vm = setupVM({ chain: 'goerli' })
+ await vm.init()
+
const genesis = new Block(Buffer.from(testData.genesisRLP.slice(2), 'hex'), { common: vm._common })
const block = new Block(Buffer.from(testData.blocks[0].rlp.slice(2), 'hex'), { common: vm._common })
@@ -115,6 +127,7 @@ tape('VM with blockchain', (t) => {
t.test('should run blockchain with blocks', async (st) => {
const vm = setupVM({ chain: 'goerli' })
+ await vm.init()
const genesis = new Block(Buffer.from(testData.genesisRLP.slice(2), 'hex'), { common: vm._common })
const block = new Block(Buffer.from(testData.blocks[0].rlp.slice(2), 'hex'), { common: vm._common })
@@ -136,8 +149,10 @@ tape('VM with blockchain', (t) => {
st.end()
})
- t.test('should pass the correct Common object when copying the VM', st => {
+ t.test('should pass the correct Common object when copying the VM', async (st) => {
const vm = setupVM({ chain: 'goerli', hardfork: 'byzantium' })
+ await vm.init()
+
st.equal(vm._common.chainName(), 'goerli')
st.equal(vm._common.hardfork(), 'byzantium')
--
2.21.1 (Apple Git-122.3)
From 054bc49da2332948df9e015d7353a94d3c162d52 Mon Sep 17 00:00:00 2001
From: Pavel Rumkin <dev@rumk.in>
Date: Sat, 22 Feb 2020 09:31:44 +0300
Subject: [PATCH 1/2] Add initialization method to prevent race conditions
---
packages/vm/lib/index.ts | 39 +++++++++++++++++++++++++---------
packages/vm/tests/api/index.js | 5 +++--
2 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/packages/vm/lib/index.ts b/packages/vm/lib/index.ts
index 2271120..aa351b3 100644
--- a/packages/vm/lib/index.ts
+++ b/packages/vm/lib/index.ts
@@ -73,6 +73,7 @@ export default class VM extends AsyncEventEmitter {
_opcodes: OpcodeList
public readonly _emit: (topic: string, data: any) => Promise<void>
public readonly pStateManager: PStateManager
+ protected isInitialized: boolean = false
/**
* Instantiates a new [[VM]] Object.
@@ -116,11 +117,6 @@ export default class VM extends AsyncEventEmitter {
this.stateManager = opts.stateManager
} else {
const trie = opts.state || new Trie()
- if (opts.activatePrecompiles) {
- for (let i = 1; i <= 8; i++) {
- trie.put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize())
- }
- }
this.stateManager = new StateManager({ trie, common: this._common })
}
@@ -136,6 +132,24 @@ export default class VM extends AsyncEventEmitter {
this._emit = promisify(this.emit.bind(this))
}
+ async init(): Promise<void> {
+ if (this.isInitialized) {
+ return
+ }
+
+ const { opts } = this
+
+ if (opts.activatePrecompiles && !opts.stateManager) {
+ const trie = this.stateManager._trie
+ const put = promisify(trie.put.bind(trie))
+ for (let i = 1; i <= 8; i++) {
+ await put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize())
+ }
+ }
+
+ this.isInitialized = true
+ }
+
/**
* Processes blocks and adds them to the blockchain.
*
@@ -143,7 +157,8 @@ export default class VM extends AsyncEventEmitter {
*
* @param blockchain - A [blockchain](https://github.com/ethereum/ethereumjs-blockchain) object to process
*/
- runBlockchain(blockchain: any): Promise<void> {
+ async runBlockchain(blockchain: any): Promise<void> {
+ await this.init()
return runBlockchain.bind(this)(blockchain)
}
@@ -157,7 +172,8 @@ export default class VM extends AsyncEventEmitter {
* @param opts - Default values for options:
* - `generate`: false
*/
- runBlock(opts: RunBlockOpts): Promise<RunBlockResult> {
+ async runBlock(opts: RunBlockOpts): Promise<RunBlockResult> {
+ await this.init()
return runBlock.bind(this)(opts)
}
@@ -168,7 +184,8 @@ export default class VM extends AsyncEventEmitter {
* when the error is thrown from an event handler. In the latter case the state may or may not be
* reverted.
*/
- runTx(opts: RunTxOpts): Promise<RunTxResult> {
+ async runTx(opts: RunTxOpts): Promise<RunTxResult> {
+ await this.init()
return runTx.bind(this)(opts)
}
@@ -177,7 +194,8 @@ export default class VM extends AsyncEventEmitter {
*
* This method modifies the state.
*/
- runCall(opts: RunCallOpts): Promise<EVMResult> {
+ async runCall(opts: RunCallOpts): Promise<EVMResult> {
+ await this.init()
return runCall.bind(this)(opts)
}
@@ -186,7 +204,8 @@ export default class VM extends AsyncEventEmitter {
*
* This method modifies the state.
*/
- runCode(opts: RunCodeOpts): Promise<ExecResult> {
+ async runCode(opts: RunCodeOpts): Promise<ExecResult> {
+ await this.init()
return runCode.bind(this)(opts)
}
diff --git a/packages/vm/tests/api/index.js b/packages/vm/tests/api/index.js
index 3c7e8d0..9714c0e 100644
--- a/packages/vm/tests/api/index.js
+++ b/packages/vm/tests/api/index.js
@@ -17,9 +17,10 @@ tape('VM with default blockchain', (t) => {
st.end()
})
- t.test('should be able to activate precompiles', (st) => {
+ t.test('should be able to activate precompiles', async (st) => {
let vm = new VM({ activatePrecompiles: true })
- st.notEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
+ await vm.init()
+ st.notDeepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
st.end()
})
--
2.21.1 (Apple Git-122.3)
From 05148b63a06a9e6a4bff1e8ae74e5aaf198252a4 Mon Sep 17 00:00:00 2001
From: Pavel Rumkin <dev@rumk.in>
Date: Wed, 26 Feb 2020 13:08:13 +0300
Subject: [PATCH 2/2] Added async constructor.
---
packages/vm/lib/index.ts | 11 ++++++++++-
packages/vm/tests/api/index.js | 27 +++++++++++++++++++++------
2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/packages/vm/lib/index.ts b/packages/vm/lib/index.ts
index aa351b3..ecccaab 100644
--- a/packages/vm/lib/index.ts
+++ b/packages/vm/lib/index.ts
@@ -74,7 +74,16 @@ export default class VM extends AsyncEventEmitter {
public readonly _emit: (topic: string, data: any) => Promise<void>
public readonly pStateManager: PStateManager
protected isInitialized: boolean = false
-
+ /**
+ * VM async constructor. Creates engine instance and initializes it.
+ *
+ * @param opts VM engine constructor options
+ */
+ static async create(opts: VMOpts = {}): Promise<VM> {
+ const vm = new this(opts)
+ await vm.init()
+ return vm
+ }
/**
* Instantiates a new [[VM]] Object.
* @param opts - Default values for the options are:
diff --git a/packages/vm/tests/api/index.js b/packages/vm/tests/api/index.js
index 9714c0e..ed934e8 100644
--- a/packages/vm/tests/api/index.js
+++ b/packages/vm/tests/api/index.js
@@ -24,11 +24,18 @@ tape('VM with default blockchain', (t) => {
st.end()
})
- t.test('should work with trie (state) provided', (st) => {
+ t.test('should instantiate with async constructor', async (st) => {
+ let vm = await VM.create({ activatePrecompiles: true })
+ st.notDeepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
+ st.end()
+ })
+
+ t.test('should work with trie (state) provided', async (st) => {
let trie = new Trie()
trie.isTestTrie = true
let vm = new VM({ state: trie, activatePrecompiles: true })
- st.notEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
+ await vm.init()
+ st.notDeepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has different root')
st.ok(vm.stateManager._trie.isTestTrie, 'it works on trie provided')
st.end()
})
@@ -43,17 +50,19 @@ tape('VM with default blockchain', (t) => {
st.end()
})
- t.test('should accept a common object as option', (st) => {
+ t.test('should accept a common object as option', async (st) => {
const common = new Common('mainnet', 'istanbul')
const vm = new VM({ common })
+ await vm.init()
st.equal(vm._common, common)
st.end()
})
- t.test('should only accept valid chain and fork', (st) => {
+ t.test('should only accept valid chain and fork', async (st) => {
let vm = new VM({ chain: 'ropsten', hardfork: 'byzantium' })
+ await vm.init()
st.equal(vm.stateManager._common.param('gasPrices', 'ecAdd'), 500)
try {
@@ -74,8 +83,9 @@ tape('VM with default blockchain', (t) => {
})
tape('VM with blockchain', (t) => {
- t.test('should instantiate', (st) => {
+ t.test('should instantiate', async (st) => {
const vm = setupVM()
+ await vm.init()
st.deepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has default trie')
st.end()
})
@@ -88,6 +98,8 @@ tape('VM with blockchain', (t) => {
t.test('should run blockchain with mocked runBlock', async (st) => {
const vm = setupVM({ chain: 'goerli' })
+ await vm.init()
+
const genesis = new Block(Buffer.from(testData.genesisRLP.slice(2), 'hex'), { common: vm._common })
const block = new Block(Buffer.from(testData.blocks[0].rlp.slice(2), 'hex'), { common: vm._common })
@@ -115,6 +127,7 @@ tape('VM with blockchain', (t) => {
t.test('should run blockchain with blocks', async (st) => {
const vm = setupVM({ chain: 'goerli' })
+ await vm.init()
const genesis = new Block(Buffer.from(testData.genesisRLP.slice(2), 'hex'), { common: vm._common })
const block = new Block(Buffer.from(testData.blocks[0].rlp.slice(2), 'hex'), { common: vm._common })
@@ -136,8 +149,10 @@ tape('VM with blockchain', (t) => {
st.end()
})
- t.test('should pass the correct Common object when copying the VM', st => {
+ t.test('should pass the correct Common object when copying the VM', async (st) => {
const vm = setupVM({ chain: 'goerli', hardfork: 'byzantium' })
+ await vm.init()
+
st.equal(vm._common.chainName(), 'goerli')
st.equal(vm._common.hardfork(), 'byzantium')
--
2.21.1 (Apple Git-122.3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment