git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 20390909a4485d06cd4a720d5f69fc9deca7a9b3

Files: 20390909a4485d06cd4a720d5f69fc9deca7a9b3 / index.js

6192 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')
21
22const DebugInterface = require('./debugInterface.js')
23
24const Address = require('./address.js')
25const U256 = require('./u256.js')
26const Utils = require('./utils.js')
27const Transaction = require('./transaction.js')
28
29module.exports = class Kernel {
30 // runs some code in the VM
31 constructor (environment = new Environment()) {
32 this.environment = environment
33 }
34
35 // handles running code.
36 // NOTE: it assumes that wasm will raise an exception if something went wrong,
37 // otherwise execution succeeded
38 codeHandler (code, ethInterface = new Interface(new Environment())) {
39 const debugInterface = new DebugInterface(ethInterface.environment)
40
41 const instance = Wasm.instantiateModule(code, {
42 'ethereum': ethInterface.exportTable,
43 'debug': debugInterface.exportTable,
44
45 // export this for Rust
46 // FIXME: remove once Rust has proper imports, see https://github.com/ethereum/evm2.0-design/issues/15
47 'spectest': ethInterface.exportTable,
48
49 // export this for Binaryen
50 // FIXME: remove once C has proper imports, see https://github.com/ethereum/evm2.0-design/issues/16
51 'env': ethInterface.exportTable
52 })
53
54 ethInterface.setModule(instance)
55 debugInterface.setModule(instance)
56
57 if (instance.exports.main) {
58 instance.exports.main()
59 }
60 return instance
61 }
62
63 // loads code from the merkle trie and delegates the message
64 // Detects if code is EVM or WASM
65 // Detects if the code injection is needed
66 // Detects if transcompilation is needed
67 callHandler (call) {
68 let account = this.environment.state.get(call.to.toString())
69 if (!account) {
70 throw new Error('Account not found')
71 }
72
73 const code = Uint8Array.from(account.get('code'))
74 if (code.length === 0) {
75 throw new Error('Contract not found')
76 }
77
78 if (!Utils.isWASMCode(code)) {
79 throw new Error('Not an eWASM contract')
80 }
81
82 // creats a new Kernel
83 const environment = new Environment()
84 environment.parent = this
85
86 // copy the transaction details
87 environment.code = code
88 environment.address = call.to
89 // FIXME: make distinction between origin and caller
90 environment.origin = call.from
91 environment.caller = call.from
92 environment.callData = call.data
93 environment.callValue = call.value
94 environment.gasLimit = call.gasLimit
95
96 //environment.setCallHandler(callHandler)
97
98 const kernel = new Kernel(this, environment)
99 kernel.codeHandler(code, new Interface(environment))
100
101 // generate new stateroot
102 //this.environment.state.set(address, { stateRoot: stateRoot })
103
104 return {
105 executionOutcome: 1, // success
106 gasLeft: new U256(environment.gasLimit), // this starts as the limit and results as the gas left
107 gasRefund: new U256(environment.gasRefund),
108 returnValue: environment.returnValue,
109 selfDestructAddress: environment.selfDestructAddress,
110 logs: environment.logs
111 }
112 }
113
114 // run tx; the tx message handler
115 runTx (tx, environment = new Environment()) {
116 // verify tx then send to call Handler
117 // - from account has enough balance
118 // - check nonce
119 // - ecrecover
120 // new ethTx(tx).validate(tx)
121 // - reduce balance
122
123 this.environment = environment
124
125 //
126 // environment.state - the merkle tree
127 // key: address (20 byte, hex string, without 0x prefix)
128 // every path has an account
129 //
130 // { balance, codeHash, stateRoot }
131 //
132
133 if (Buffer.isBuffer(tx) || typeof tx === 'string') {
134 tx = new Transaction(tx)
135 if (!tx.valid) {
136 throw new Error('Invalid transaction signature')
137 }
138 }
139
140 // look up sender
141 let fromAccount = this.environment.state.get(tx.from.toString())
142 if (!fromAccount) {
143 throw new Error('Sender account not found')
144 }
145
146 // Special case: contract deployment
147 if (tx.to.isZero()) {
148 if (tx.data.length !== 0) {
149 console.log('This is a contract deployment transaction')
150
151 let address = Utils.newAccountAddress(tx.from, tx.data)
152
153 this.environment.addAccount(address.toString(), {
154 balance: tx.value,
155 code: tx.data
156 })
157
158 // FIXME: deduct fees
159
160 return {
161 accountCreated: address
162 }
163 }
164 }
165
166 // deduct gasLimit * gasPrice from sender
167 if (fromAccount.get('balance').lt(tx.gasLimit.mul(tx.gasPrice))) {
168 throw new Error(`Insufficient account balance: ${fromAccount.get('balance').toString()} < ${tx.gasLimit.mul(tx.gasPrice).toString()}`)
169 }
170
171 fromAccount.set('balance', fromAccount.get('balance').sub(tx.gasLimit.mul(tx.gasPrice)))
172
173 let ret = this.callHandler({
174 to: tx.to,
175 from: tx.from,
176 gasLimit: tx.gasLimit,
177 value: tx.value,
178 data: tx.data
179 })
180
181 // refund gas
182 if (ret.executionOutcome === 1) {
183 fromAccount.set('balance', fromAccount.get('balance').add(tx.gasPrice.mul(ret.gasLeft.add(ret.gasRefund))))
184 }
185
186 // save new state?
187
188 return {
189 returnValue: ret.returnValue,
190 gasLeft: ret.gasLeft,
191 logs: ret.logs
192 }
193 }
194
195 // run block; the block message handler
196 runBlock (block, environment = new Environment()) {
197 // verify block then run each tx
198 block.tx.forEach((tx) => {
199 this.runTx(tx, environment)
200 })
201 }
202
203 // run blockchain
204 // runBlockchain () {}
205}
206

Built with git-ssb-web