git ssb

0+

wanderer🌟 / js-primea-hypervisor



Tree: 4c7c54ec09858f22c767210f0814625380b32c55

Files: 4c7c54ec09858f22c767210f0814625380b32c55 / index.js

5822 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 (address, gaslimit, gasprice, value, data) {
68 let account = this.environment.state.get(address.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(data)
84 environment.parent = this
85
86 //environment.setCallHandler(callHandler)
87
88 const kernel = new Kernel(this, environment)
89 kernel.codeHandler(code, new Interface(environment))
90
91 // generate new stateroot
92 //this.environment.state.set(address, { stateRoot: stateRoot })
93
94 return {
95 executionOutcome: 1, // success
96 gasLeft: new U256(environment.gasLimit), // this starts as the limit and results as the gas left
97 gasRefund: new U256(environment.gasRefund),
98 returnValue: environment.returnValue,
99 selfDestructAddress: environment.selfDestructAddress,
100 logs: environment.logs
101 }
102 }
103
104 // run tx; the tx message handler
105 runTx (tx, environment = new Environment()) {
106 // verify tx then send to call Handler
107 // - from account has enough balance
108 // - check nonce
109 // - ecrecover
110 // new ethTx(tx).validate(tx)
111 // - reduce balance
112
113 this.environment = environment
114
115 //
116 // environment.state - the merkle tree
117 // key: address (20 byte, hex string, without 0x prefix)
118 // every path has an account
119 //
120 // { balance, codeHash, stateRoot }
121 //
122
123 if (Buffer.isBuffer(tx) || typeof tx === 'string') {
124 tx = new Transaction(tx)
125 if (!tx.valid) {
126 throw new Error('Invalid transaction signature')
127 }
128 }
129
130 // look up sender
131 let fromAccount = this.environment.state.get(tx.from.toString())
132 if (!fromAccount) {
133 throw new Error('Sender account not found')
134 }
135
136 // Special case: contract deployment
137 if (tx.to.isZero()) {
138 if (tx.data.length !== 0) {
139 console.log('This is a contract deployment transaction')
140
141 let address = Utils.newAccountAddress(tx.from, tx.data)
142
143 this.environment.addAccount(address.toString(), {
144 balance: tx.value,
145 code: tx.data
146 })
147
148 // FIXME: deduct fees
149
150 return {
151 accountCreated: address
152 }
153 }
154 }
155
156 // deduct gasLimit * gasPrice from sender
157 if (fromAccount.get('balance').lt(tx.gasLimit.mul(tx.gasPrice))) {
158 throw new Error(`Insufficient account balance: ${fromAccount.get('balance').toString()} < ${tx.gasLimit.mul(tx.gasPrice).toString()}`)
159 }
160
161 fromAccount.set('balance', fromAccount.get('balance').sub(tx.gasLimit.mul(tx.gasPrice)))
162
163 let ret = this.callHandler(tx.to, tx.gasLimit, tx.gasPrice, tx.value, tx.data)
164
165 // refund gas
166 if (ret.executionOutcome === 1) {
167 fromAccount.set('balance', fromAccount.get('balance').add(tx.gasPrice.mul(ret.gasLeft.add(ret.gasRefund))))
168 }
169
170 // save new state?
171
172 return {
173 returnValue: ret.returnValue,
174 gasLeft: ret.gasLeft,
175 logs: ret.logs
176 }
177 }
178
179 // run block; the block message handler
180 runBlock (block, environment = new Environment()) {
181 // verify block then run each tx
182 block.tx.forEach((tx) => {
183 this.runTx(tx, environment)
184 })
185 }
186
187 // run blockchain
188 // runBlockchain () {}
189}
190

Built with git-ssb-web