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