git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 1c2e46867f5b53672c2ec9beb976209e90ebc8db

Files: 1c2e46867f5b53672c2ec9beb976209e90ebc8db / index.js

7723 bytesRaw
1/**
2 * This implements the Ethereum Kernel
3 * Kernels must implement two methods `codeHandler` and `callHandler` (and `linkHandler` for sharding)
4 * The Kernel Contract handles the following
5 * - Interprocess communications
6 * - Intializing the VM and exposes ROM to it (codeHandler)
7 * - Expose namespace which VM instance exists and Intializes the Environment (callHandler)
8 * - Provides some built in contract (runTx, runBlock)
9 * - Provides resource sharing and limiting via gas
10 *
11 * All State should be stored in the Environment.
12 *
13 */
14
15// The Kernel Exposes this Interface to VM instances it makes
16const Interface = require('./interface.js')
17
18// The Kernel Stores all of its state in the Environment. The Interface is used
19// to by the VM to retrive infromation from the Environment.
20const Environment = require('./environment.js')
21const DebugInterface = require('./debugInterface.js')
22const Address = require('./address.js')
23const U256 = require('./u256.js')
24const Utils = require('./utils.js')
25const Transaction = require('./transaction.js')
26const Precompile = require('./precompile.js')
27
28const meteringContract = new Address('0x000000000000000000000000000000000000000A')
29const transcompilerContract = new Address('0x000000000000000000000000000000000000000B')
30
31module.exports = class Kernel {
32 // runs some code in the VM
33 constructor (environment = new Environment()) {
34 this.environment = environment
35 }
36
37 // handles running code.
38 // NOTE: it assumes that wasm will raise an exception if something went wrong,
39 // otherwise execution succeeded
40 codeHandler (code, ethInterface = new Interface(new Environment())) {
41 const debugInterface = new DebugInterface(ethInterface.environment)
42
43 const instance = Wasm.instantiateModule(code, {
44 'ethereum': ethInterface.exportTable,
45 'debug': debugInterface.exportTable,
46
47 // export this for Rust
48 // FIXME: remove once Rust has proper imports, see https://github.com/ethereum/evm2.0-design/issues/15
49 'spectest': ethInterface.exportTable,
50
51 // export this for Binaryen
52 // FIXME: remove once C has proper imports, see https://github.com/ethereum/evm2.0-design/issues/16
53 'env': ethInterface.exportTable
54 })
55
56 ethInterface.setModule(instance)
57 debugInterface.setModule(instance)
58
59 if (instance.exports.main) {
60 instance.exports.main()
61 }
62 return instance
63 }
64
65 // loads code from the merkle trie and delegates the message
66 // Detects if code is EVM or WASM
67 // Detects if the code injection is needed
68 // Detects if transcompilation is needed
69 callHandler (call) {
70 // FIXME: this is here until these two contracts are compiled to WASM
71 // The two special contracts (precompiles now, but will be real ones later)
72 if (call.to.equals(meteringContract)) {
73 return Precompile.meteringInjector(call)
74 } else if (call.to.equals(transcompilerContract)) {
75 return Precompile.transcompiler(call)
76 }
77
78 let account = this.environment.state.get(call.to.toString())
79 if (!account) {
80 throw new Error('Account not found: ' + call.to.toString())
81 }
82
83 let code = Uint8Array.from(account.get('code'))
84 if (code.length === 0) {
85 throw new Error('Contract not found')
86 }
87
88 if (!Utils.isWASMCode(code)) {
89 // throw new Error('Not an eWASM contract')
90
91 // Transcompile code
92 // FIXME: decide if these are the right values here: from: 0, gasLimit: 0, value: 0
93 code = this.callHandler({ from: Address.zero(), to: transcompilerContract, gasLimit: 0, value: new U256(0), data: code }).returnValue
94 }
95
96 // creats a new Kernel
97 const environment = new Environment()
98 environment.parent = this
99
100 // copy the transaction details
101 environment.code = code
102 environment.address = call.to
103 // FIXME: make distinction between origin and caller
104 environment.origin = call.from
105 environment.caller = call.from
106 environment.callData = call.data
107 environment.callValue = call.value
108 environment.gasLeft = call.gasLimit
109
110 // environment.setCallHandler(this.callHandler)
111 // environment.setCreateHandler(this.createHandler)
112
113 const kernel = new Kernel(this, environment)
114 kernel.codeHandler(code, new Interface(environment))
115
116 // generate new stateroot
117 // this.environment.state.set(address, { stateRoot: stateRoot })
118
119 return {
120 executionOutcome: 1, // success
121 gasLeft: new U256(environment.gasLeft),
122 gasRefund: new U256(environment.gasRefund),
123 returnValue: environment.returnValue,
124 selfDestructAddress: environment.selfDestructAddress,
125 logs: environment.logs
126 }
127 }
128
129 createHandler (create) {
130 let code = create.data
131
132 // Inject metering
133 if (Utils.isWASMCode(code)) {
134 // FIXME: decide if these are the right values here: from: 0, gasLimit: 0, value: 0
135 code = this.callHandler({ from: Address.zero(), to: meteringContract, gasLimit: 0, value: new U256(0), data: code }).returnValue
136 }
137
138 let address = Utils.newAccountAddress(create.from, code)
139
140 this.environment.addAccount(address.toString(), {
141 balance: create.value,
142 code: code
143 })
144
145 // Run code and take return value as contract code
146 // FIXME: decide if these are the right values here: value: 0, data: ''
147 code = this.callHandler({ from: create.from, to: address, gasLimit: create.gasLimit, value: new U256(0), data: new Uint8Array() }).returnValue
148
149 this.environment.state.get(address.toString()).set('code', code)
150
151 return {
152 accountCreated: address
153 }
154 }
155
156 // run tx; the tx message handler
157 runTx (tx, environment = new Environment()) {
158 this.environment = environment
159
160 if (Buffer.isBuffer(tx) || typeof tx === 'string') {
161 tx = new Transaction(tx)
162 if (!tx.valid) {
163 throw new Error('Invalid transaction signature')
164 }
165 }
166
167 // look up sender
168 let fromAccount = this.environment.state.get(tx.from.toString())
169 if (!fromAccount) {
170 throw new Error('Sender account not found: ' + tx.from.toString())
171 }
172
173 if (fromAccount.get('nonce').gt(tx.nonce)) {
174 throw new Error(`Invalid nonce: ${fromAccount.get('nonce')} > ${tx.nonce}`)
175 }
176
177 fromAccount.set('nonce', fromAccount.get('nonce').add(new U256(1)))
178
179 // Special case: contract deployment
180 if (tx.to.isZero()) {
181 if (tx.data.length !== 0) {
182 console.log('This is a contract deployment transaction')
183
184 // FIXME: deduct fees
185
186 return this.createHandler({
187 from: tx.from,
188 gasLimit: tx.gasLimit,
189 value: tx.value,
190 data: tx.data
191 })
192 }
193 }
194
195 // deduct gasLimit * gasPrice from sender
196 if (fromAccount.get('balance').lt(tx.gasLimit.mul(tx.gasPrice))) {
197 throw new Error(`Insufficient account balance: ${fromAccount.get('balance').toString()} < ${tx.gasLimit.mul(tx.gasPrice).toString()}`)
198 }
199
200 fromAccount.set('balance', fromAccount.get('balance').sub(tx.gasLimit.mul(tx.gasPrice)))
201
202 let ret = this.callHandler({
203 to: tx.to,
204 from: tx.from,
205 gasLimit: tx.gasLimit,
206 value: tx.value,
207 data: tx.data
208 })
209
210 // refund gas
211 if (ret.executionOutcome === 1) {
212 fromAccount.set('balance', fromAccount.get('balance').add(tx.gasPrice.mul(ret.gasLeft.add(ret.gasRefund))))
213 }
214
215 // save new state?
216
217 return {
218 returnValue: ret.returnValue,
219 gasLeft: ret.gasLeft,
220 logs: ret.logs
221 }
222 }
223
224 // run block; the block message handler
225 runBlock (block, environment = new Environment()) {
226 // verify block then run each tx
227 block.tx.forEach((tx) => {
228 this.runTx(tx, environment)
229 })
230 }
231
232 // run blockchain
233 // runBlockchain () {}
234}
235

Built with git-ssb-web