-
Notifications
You must be signed in to change notification settings - Fork 759
/
index.ts
203 lines (184 loc) · 6.52 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import BN = require('bn.js')
import Account from 'ethereumjs-account'
import Blockchain from 'ethereumjs-blockchain'
import Common from 'ethereumjs-common'
import { StateManager } from './state'
import { default as runCode, RunCodeOpts } from './runCode'
import { default as runCall, RunCallOpts } from './runCall'
import { default as runTx, RunTxOpts, RunTxResult } from './runTx'
import { default as runBlock, RunBlockOpts, RunBlockResult } from './runBlock'
import { EVMResult, ExecResult } from './evm/evm'
import { OpcodeList, getOpcodesForHF } from './evm/opcodes'
import runBlockchain from './runBlockchain'
import PStateManager from './state/promisified'
const promisify = require('util.promisify')
const AsyncEventEmitter = require('async-eventemitter')
const Trie = require('merkle-patricia-tree/secure.js')
/**
* Options for instantiating a [[VM]].
*/
export interface VMOpts {
/**
* The chain the VM operates on
*/
chain?: string
/**
* Hardfork rules to be used
*/
hardfork?: string
/**
* A [[StateManager]] instance to use as the state store (Beta API)
*/
stateManager?: StateManager
/**
* A [merkle-patricia-tree](https://github.com/ethereumjs/merkle-patricia-tree) instance for the state tree (ignored if stateManager is passed)
* @deprecated
*/
state?: any // TODO
/**
* A [blockchain](https://github.com/ethereumjs/ethereumjs-blockchain) object for storing/retrieving blocks
*/
blockchain?: Blockchain
/**
* If true, create entries in the state tree for the precompiled contracts, saving some gas the
* first time each of them is called.
*
* If this parameter is false, the first call to each of them has to pay an extra 25000 gas
* for creating the account.
*
* Setting this to true has the effect of precompiled contracts' gas costs matching mainnet's from
* the very first call, which is intended for testing networks.
*/
activatePrecompiles?: boolean
/**
* Allows unlimited contract sizes while debugging. By setting this to `true`, the check for contract size limit of 24KB (see [EIP-170](https://git.io/vxZkK)) is bypassed
*/
allowUnlimitedContractSize?: boolean
common?: Common
}
/**
* Execution engine which can be used to run a blockchain, individual
* blocks, individual transactions, or snippets of EVM bytecode.
*
* This class is an AsyncEventEmitter, please consult the README to learn how to use it.
*/
export default class VM extends AsyncEventEmitter {
opts: VMOpts
_common: Common
stateManager: StateManager
blockchain: Blockchain
allowUnlimitedContractSize: boolean
_opcodes: OpcodeList
public readonly _emit: (topic: string, data: any) => Promise<void>
public readonly pStateManager: PStateManager
/**
* Instantiates a new [[VM]] Object.
* @param opts - Default values for the options are:
* - `chain`: 'mainnet'
* - `hardfork`: 'petersburg' [supported: 'byzantium', 'constantinople', 'petersburg', 'istanbul' (DRAFT) (will throw on unsupported)]
* - `activatePrecompiles`: false
* - `allowUnlimitedContractSize`: false [ONLY set to `true` during debugging]
*/
constructor(opts: VMOpts = {}) {
super()
this.opts = opts
if (opts.common) {
if (opts.chain || opts.hardfork) {
throw new Error(
'You can only instantiate the VM class with one of: opts.common, or opts.chain and opts.hardfork',
)
}
this._common = opts.common
} else {
const chain = opts.chain ? opts.chain : 'mainnet'
const hardfork = opts.hardfork ? opts.hardfork : 'petersburg'
const supportedHardforks = [
'byzantium',
'constantinople',
'petersburg',
'istanbul',
'muirGlacier',
]
this._common = new Common(chain, hardfork, supportedHardforks)
}
// Set list of opcodes based on HF
this._opcodes = getOpcodesForHF(this._common)
if (opts.stateManager) {
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 })
}
this.pStateManager = new PStateManager(this.stateManager)
this.blockchain = opts.blockchain || new Blockchain({ common: this._common })
this.allowUnlimitedContractSize =
opts.allowUnlimitedContractSize === undefined ? false : opts.allowUnlimitedContractSize
// We cache this promisified function as it's called from the main execution loop, and
// promisifying each time has a huge performance impact.
this._emit = promisify(this.emit.bind(this))
}
/**
* Processes blocks and adds them to the blockchain.
*
* This method modifies the state.
*
* @param blockchain - A [blockchain](https://github.com/ethereum/ethereumjs-blockchain) object to process
*/
runBlockchain(blockchain: any): Promise<void> {
return runBlockchain.bind(this)(blockchain)
}
/**
* Processes the `block` running all of the transactions it contains and updating the miner's account
*
* This method modifies the state. If `generate` is `true`, the state modifications will be
* reverted if an exception is raised. If it's `false`, it won't revert if the block's header is
* invalid. If an error is thrown from an event handler, the state may or may not be reverted.
*
* @param opts - Default values for options:
* - `generate`: false
*/
runBlock(opts: RunBlockOpts): Promise<RunBlockResult> {
return runBlock.bind(this)(opts)
}
/**
* Process a transaction. Run the vm. Transfers eth. Checks balances.
*
* This method modifies the state. If an error is thrown, the modifications are reverted, except
* 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> {
return runTx.bind(this)(opts)
}
/**
* runs a call (or create) operation.
*
* This method modifies the state.
*/
runCall(opts: RunCallOpts): Promise<EVMResult> {
return runCall.bind(this)(opts)
}
/**
* Runs EVM code.
*
* This method modifies the state.
*/
runCode(opts: RunCodeOpts): Promise<ExecResult> {
return runCode.bind(this)(opts)
}
/**
* Returns a copy of the [[VM]] instance.
*/
copy(): VM {
return new VM({
stateManager: this.stateManager.copy(),
blockchain: this.blockchain,
common: this._common,
})
}
}