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 |
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 Address = require('./address.js') |
25 | const U256 = require('./u256.js') |
26 | const Utils = require('./utils.js') |
27 | const Transaction = require('./transaction.js') |
28 | |
29 | module.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