git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 04af14cd7056250c22a08f3950f10cfa2dcbb8c8

Files: 04af14cd7056250c22a08f3950f10cfa2dcbb8c8 / index.js

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

Built with git-ssb-web