Commit 4deaaf0620b832ca0e463a1327d1d7d0ee6c8d6e
rewrote core kernel api, and added internel kernel api
wanderer committed on 11/4/2016, 6:59:56 PMParent: c4a3c214a45ab12b8140aa229f758297f1051af2
Files changed
deps/address.js | changed |
deps/kernelVertex.js | added |
deps/rootVertex.js | added |
index.js | changed |
interface.js | changed |
debugKernel.js | deleted |
kernelInterface.js | deleted |
precompiles/create.js | added |
precompiles/precompile.js | added |
precompile.js | deleted |
deps/address.js | ||
---|---|---|
@@ -22,8 +22,12 @@ | ||
22 | 22 | toBuffer () { |
23 | 23 | return super.toBuffer(20) |
24 | 24 | } |
25 | 25 | |
26 | + toArray () { | |
27 | + return [...this.toBuffer()] | |
28 | + } | |
29 | + | |
26 | 30 | // Needs to be displayed as a hex always |
27 | 31 | toString () { |
28 | 32 | return '0x' + this._value.toString('hex', 40) |
29 | 33 | } |
deps/kernelVertex.js | ||
---|---|---|
@@ -1,0 +1,12 @@ | ||
1 | +const Vertex = require('merkle-trie') | |
2 | + | |
3 | +module.exports = class KernelVertex extends Vertex { | |
4 | + get kernel () { | |
5 | + return this._cache.kernel | |
6 | + } | |
7 | + | |
8 | + set kernel (instance) { | |
9 | + this._cache.kernel = instance | |
10 | + } | |
11 | +} | |
12 | + |
deps/rootVertex.js | ||
---|---|---|
@@ -1,0 +1,50 @@ | ||
1 | +const KernelVertex = require('./kernelVertex') | |
2 | +const Kernel = require('../') | |
3 | +const Precompiles = require('../precomiles/precompile.js') | |
4 | +const Address = require('./address') | |
5 | + | |
6 | +const identityAddress = new Address('0x0000000000000000000000000000000000000004') | |
7 | +const meteringAddress = new Address('0x000000000000000000000000000000000000000A') | |
8 | +const transcompilerAddress = new Address('0x000000000000000000000000000000000000000B') | |
9 | + | |
10 | +module.exports = class RootKernelVertex extends KernelVertex { | |
11 | + constructor (opts) { | |
12 | + super(opts) | |
13 | + if (opts.root) { | |
14 | + this.set(identityAddress.toArray(), new PrecomileVertex(Precompiles.identity)) | |
15 | + this.set(meteringAddress.toArray(), new PrecomileVertex(Precompiles.meteringInjector)) | |
16 | + this.set(transcompilerAddress.toArray(), new PrecomileVertex(Precompiles.transcompiler)) | |
17 | + this.kernel = new Kernel({state: this}) | |
18 | + } | |
19 | + } | |
20 | +} | |
21 | + | |
22 | +class PrecomileVertex extends KernelVertex { | |
23 | + /** | |
24 | + * Creates a Vertex for precomiles. This will alwasy return false when hashed | |
25 | + * so that its contents will never be stored in the merkle trie run serialized | |
26 | + */ | |
27 | + constructor (precomiled) { | |
28 | + super() | |
29 | + this.kernel = precomiled | |
30 | + } | |
31 | + | |
32 | + hash () { | |
33 | + return false | |
34 | + } | |
35 | +} | |
36 | + | |
37 | +// detects the correct kernel to load given some code | |
38 | +KernelVertex.codeHandler = (code) => { | |
39 | + return KernelVertex['default'] | |
40 | +} | |
41 | + | |
42 | +KernelVertex.codeHandles = { | |
43 | + 'default': Kernel | |
44 | +} | |
45 | + | |
46 | +// KernelVertex.linkHander = (link) => { | |
47 | +// } | |
48 | + | |
49 | +// KernelVertex.linkHanders = { | |
50 | +// } |
index.js | ||
---|---|---|
@@ -1,6 +1,6 @@ | ||
1 | 1 | /** |
2 | - * This implements the Ethereum Kernel | |
2 | + * This implements the Kernel | |
3 | 3 | * Kernels must implement two methods `codeHandler` and `callHandler` (and `linkHandler` for sharding) |
4 | 4 | * The Kernel Contract handles the following |
5 | 5 | * - Interprocess communications |
6 | 6 | * - Intializing the VM and exposes ROM to it (codeHandler) |
@@ -8,175 +8,64 @@ | ||
8 | 8 | * - Provides some built in contract (runTx, runBlock) |
9 | 9 | * - Provides resource sharing and limiting via gas |
10 | 10 | * |
11 | 11 | * All State should be stored in the Environment. |
12 | - * | |
13 | 12 | */ |
14 | 13 | |
15 | -const Vertex = require('merkle-trie') | |
14 | +const Vertex = require('./deps/kernelVertex') | |
16 | 15 | // The Kernel Exposes this Interface to VM instances it makes |
17 | 16 | const Interface = require('./interface.js') |
18 | -const KernelInterface = require('./kernelInterface.js') | |
17 | +const InterfaceAPI = require('./interfaceAPI.js') | |
19 | 18 | |
20 | 19 | // The Kernel Stores all of its state in the Environment. The Interface is used |
21 | 20 | // to by the VM to retrive infromation from the Environment. |
22 | 21 | const Environment = require('./environment.js') |
23 | -const Address = require('./deps/address.js') | |
24 | -const U256 = require('./deps/u256.js') | |
25 | -const Utils = require('./deps/utils.js') | |
26 | -const Precompile = require('./precompile.js') | |
22 | +module.exports = class Kernel { | |
23 | + constructor (opts = {}) { | |
24 | + this.state = opts.state || new Vertex() | |
25 | + this.parent = opts.parent | |
27 | 26 | |
28 | -const identityContract = new Address('0x0000000000000000000000000000000000000004') | |
29 | -const meteringContract = new Address('0x000000000000000000000000000000000000000A') | |
30 | -const transcompilerContract = new Address('0x000000000000000000000000000000000000000B') | |
27 | + if (opts.code) { | |
28 | + this.interfaceAPI = new InterfaceAPI(opts.code) | |
29 | + } | |
30 | + this.imports = this.buildImports(opts.interfaces) | |
31 | + } | |
31 | 32 | |
32 | -module.exports = class Kernel { | |
33 | - // runs some code in the VM | |
34 | - constructor (state = new Vertex(), interfaces = [Interface]) { | |
35 | - this.state = state | |
36 | - this.interfaces = interfaces | |
33 | + buildImports (interfaces = [Interface]) { | |
34 | + return interfaces.reduce((obj, Interface) => { | |
35 | + obj[Interface.name] = new Interface(this.interfaceAPI).exports | |
36 | + return obj | |
37 | + }, {}) | |
37 | 38 | } |
38 | 39 | |
39 | - // handles running code. | |
40 | - codeHandler (code, environment = new Environment({state: this.state})) { | |
41 | - const kernelInterface = new KernelInterface(this, environment) | |
42 | - return kernelInterface.run(code) | |
40 | + // run the kernels code with a given enviroment | |
41 | + async run (environment = new Environment({state: this.state}), imports = this.imports) { | |
42 | + await this.interfaceAPI.run(environment, imports) | |
43 | + return environment | |
43 | 44 | } |
44 | 45 | |
45 | - // loads code from the merkle trie and delegates the message | |
46 | - // Detects if code is EVM or WASM | |
47 | - // Detects if the code injection is needed | |
48 | - // Detects if transcompilation is needed | |
49 | - messageHandler (call) { | |
50 | - // FIXME: this is here until these two contracts are compiled to WASM | |
51 | - // The two special contracts (precompiles now, but will be real ones later) | |
52 | - if (call.to.equals(meteringContract)) { | |
53 | - return Precompile.meteringInjector(call) | |
54 | - } else if (call.to.equals(transcompilerContract)) { | |
55 | - return Precompile.transcompiler(call) | |
56 | - } else if (call.to.equals(identityContract)) { | |
57 | - return Precompile.identity(call) | |
58 | - } | |
59 | - | |
60 | - let account = this.environment.state.get(call.to.toString()) | |
61 | - if (!account) { | |
62 | - throw new Error('Account not found: ' + call.to.toString()) | |
63 | - } | |
64 | - | |
65 | - let code = Uint8Array.from(account.get('code')) | |
66 | - if (code.length === 0) { | |
67 | - throw new Error('Contract not found') | |
68 | - } | |
69 | - | |
70 | - if (!Utils.isWASMCode(code)) { | |
71 | - // throw new Error('Not an eWASM contract') | |
72 | - | |
73 | - // Transcompile code | |
74 | - // FIXME: decide if these are the right values here: from: 0, gasLimit: 0, value: 0 | |
75 | - code = this.messageHandler({ from: Address.zero(), to: transcompilerContract, gasLimit: 0, value: new U256(0), data: code }).returnValue | |
76 | - | |
77 | - if (code[0] === 0) { | |
78 | - code = code.slice(1) | |
79 | - } else { | |
80 | - throw new Error('Transcompilation failed: ' + Buffer.from(code).slice(1).toString()) | |
46 | + async messageReceiver (message) { | |
47 | + // let the code handle the message if there is code | |
48 | + if (this.code) { | |
49 | + const environment = new Environment(message) | |
50 | + let result = await this.run(environment) | |
51 | + if (!result.execption) { | |
52 | + this.state = result.state | |
81 | 53 | } |
54 | + } else if (message.to.length) { | |
55 | + // else forward the message on to the destination contract | |
56 | + let [vertex, done] = await this.state.update(message.to) | |
57 | + message.to = [] | |
58 | + await vertex.kernel.messageReceiver(message) | |
59 | + done(vertex) | |
82 | 60 | } |
83 | - | |
84 | - // creats a new Kernel | |
85 | - const environment = new Environment() | |
86 | - environment.parent = this | |
87 | - | |
88 | - // copy the transaction details | |
89 | - environment.code = code | |
90 | - environment.address = call.to | |
91 | - // FIXME: make distinction between origin and caller | |
92 | - environment.origin = call.from | |
93 | - environment.caller = call.from | |
94 | - environment.callData = call.data | |
95 | - environment.callValue = call.value | |
96 | - environment.gasLeft = call.gasLimit | |
97 | - | |
98 | - environment.callHandler = this.callHandler.bind(this) | |
99 | - environment.createHandler = this.createHandler.bind(this) | |
100 | - | |
101 | - const kernel = new Kernel(environment) | |
102 | - kernel.codeHandler(code, new Interface(environment)) | |
103 | - | |
104 | - // self destructed | |
105 | - if (environment.selfDestruct) { | |
106 | - const balance = this.state.get(call.to.toString()).get('balance') | |
107 | - const beneficiary = this.state.get(environment.selfDestructAddress) | |
108 | - beneficiary.set('balance', beneficiary.get('balance').add(balance)) | |
109 | - this.state.delete(call.to.toString()) | |
110 | - } | |
111 | - | |
112 | - // generate new stateroot | |
113 | - // this.environment.state.set(address, { stateRoot: stateRoot }) | |
114 | - | |
115 | - return { | |
116 | - executionOutcome: 1, // success | |
117 | - gasLeft: new U256(environment.gasLeft), | |
118 | - gasRefund: new U256(environment.gasRefund), | |
119 | - returnValue: environment.returnValue, | |
120 | - selfDestruct: environment.selfDestruct, | |
121 | - selfDestructAddress: environment.selfDestructAddress, | |
122 | - logs: environment.logs | |
123 | - } | |
124 | 61 | } |
125 | 62 | |
126 | - createHandler (create) { | |
127 | - let code = create.data | |
128 | - | |
129 | - // Inject metering | |
130 | - if (Utils.isWASMCode(code)) { | |
131 | - // FIXME: decide if these are the right values here: from: 0, gasLimit: 0, value: 0 | |
132 | - code = this.callHandler({ | |
133 | - from: Address.zero(), | |
134 | - to: meteringContract, | |
135 | - gasLimit: 0, | |
136 | - value: new U256(0), | |
137 | - data: code | |
138 | - }).returnValue | |
139 | - | |
140 | - if (code[0] === 0) { | |
141 | - code = code.slice(1) | |
142 | - } else { | |
143 | - throw new Error('Metering injection failed: ' + Buffer.from(code).slice(1).toString()) | |
144 | - } | |
145 | - } | |
146 | - | |
147 | - let account = this.environment.state.get(create.from.toString()) | |
148 | - if (!account) { | |
149 | - throw new Error('Account not found: ' + create.from.toString()) | |
150 | - } | |
151 | - | |
152 | - let address = Utils.newAccountAddress(create.from, account.get('nonce')) | |
153 | - | |
154 | - this.environment.addAccount(address.toString(), { | |
155 | - balance: create.value, | |
156 | - code: code | |
63 | + copy () { | |
64 | + return new Kernel({ | |
65 | + state: this.state.copy(), | |
66 | + code: this.code, | |
67 | + interfaces: this.interfaces, | |
68 | + parent: this.parent | |
157 | 69 | }) |
158 | - | |
159 | - // Run code and take return value as contract code | |
160 | - // FIXME: decide if these are the right values here: value: 0, data: '' | |
161 | - code = this.messageHandler({ | |
162 | - from: create.from, | |
163 | - to: address, | |
164 | - gasLimit: create.gasLimit, | |
165 | - value: new U256(0), | |
166 | - data: new Uint8Array() | |
167 | - }).returnValue | |
168 | - | |
169 | - // FIXME: special handling for selfdestruct | |
170 | - | |
171 | - this.environment.state.get(address.toString()).set('code', code) | |
172 | - | |
173 | - return { | |
174 | - executionOutcome: 1, // success | |
175 | - gasLeft: new U256(this.environment.gasLeft), | |
176 | - gasRefund: new U256(this.environment.gasRefund), | |
177 | - accountCreated: address, | |
178 | - logs: this.environment.logs | |
179 | - } | |
180 | 70 | } |
181 | 71 | } |
182 | - |
interface.js | ||
---|---|---|
@@ -14,9 +14,8 @@ | ||
14 | 14 | // The interface exposed to the WebAessembly Core |
15 | 15 | module.exports = class Interface { |
16 | 16 | constructor (kernel) { |
17 | 17 | this.kernel = kernel |
18 | - this.environment = kernel.environment | |
19 | 18 | const shimBin = fs.readFileSync(path.join(__dirname, '/wasm/interface.wasm')) |
20 | 19 | const shimMod = WebAssembly.Module(shimBin) |
21 | 20 | this.shims = WebAssembly.Instance(shimMod, { |
22 | 21 | 'interface': { |
@@ -91,17 +90,17 @@ | ||
91 | 90 | * Returns the current amount of gas |
92 | 91 | * @return {integer} |
93 | 92 | */ |
94 | 93 | _getGasLeftHigh () { |
95 | - return Math.floor(this.environment.gasLeft / 4294967296) | |
94 | + return Math.floor(this.kernel.environment.gasLeft / 4294967296) | |
96 | 95 | } |
97 | 96 | |
98 | 97 | /** |
99 | 98 | * Returns the current amount of gas |
100 | 99 | * @return {integer} |
101 | 100 | */ |
102 | 101 | _getGasLeftLow () { |
103 | - return this.environment.gasLeft | |
102 | + return this.kernel.environment.gasLeft | |
104 | 103 | } |
105 | 104 | |
106 | 105 | /** |
107 | 106 | * Gets address of currently executing account and loads it into memory at |
@@ -110,9 +109,9 @@ | ||
110 | 109 | */ |
111 | 110 | getAddress (offset) { |
112 | 111 | this.takeGas(2) |
113 | 112 | |
114 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, this.environment.address.toMemory()) | |
113 | + this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.address.toMemory()) | |
115 | 114 | } |
116 | 115 | |
117 | 116 | /** |
118 | 117 | * Gets balance of the given account and loads it into memory at the given |
@@ -124,9 +123,9 @@ | ||
124 | 123 | this.takeGas(20) |
125 | 124 | |
126 | 125 | const address = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) |
127 | 126 | // call the parent contract and ask for the balance of one of its child contracts |
128 | - const balance = this.environment.getBalance(address) | |
127 | + const balance = this.kernel.environment.getBalance(address) | |
129 | 128 | this.setMemory(offset, U128_SIZE_BYTES, balance.toMemory(U128_SIZE_BYTES)) |
130 | 129 | } |
131 | 130 | |
132 | 131 | /** |
@@ -137,9 +136,9 @@ | ||
137 | 136 | */ |
138 | 137 | getTxOrigin (offset) { |
139 | 138 | this.takeGas(2) |
140 | 139 | |
141 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, this.environment.origin.toMemory()) | |
140 | + this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.origin.toMemory()) | |
142 | 141 | } |
143 | 142 | |
144 | 143 | /** |
145 | 144 | * Gets caller address and loads it into memory at the given offset. This is |
@@ -148,9 +147,9 @@ | ||
148 | 147 | */ |
149 | 148 | getCaller (offset) { |
150 | 149 | this.takeGas(2) |
151 | 150 | |
152 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, this.environment.caller.toMemory()) | |
151 | + this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.caller.toMemory()) | |
153 | 152 | } |
154 | 153 | |
155 | 154 | /** |
156 | 155 | * Gets the deposited value by the instruction/transaction responsible for |
@@ -159,9 +158,9 @@ | ||
159 | 158 | */ |
160 | 159 | getCallValue (offset) { |
161 | 160 | this.takeGas(2) |
162 | 161 | |
163 | - this.setMemory(offset, U128_SIZE_BYTES, this.environment.callValue.toMemory(U128_SIZE_BYTES)) | |
162 | + this.setMemory(offset, U128_SIZE_BYTES, this.kernel.environment.callValue.toMemory(U128_SIZE_BYTES)) | |
164 | 163 | } |
165 | 164 | |
166 | 165 | /** |
167 | 166 | * Get size of input data in current environment. This pertains to the input |
@@ -170,9 +169,9 @@ | ||
170 | 169 | */ |
171 | 170 | getCallDataSize () { |
172 | 171 | this.takeGas(2) |
173 | 172 | |
174 | - return this.environment.callData.length | |
173 | + return this.kernel.environment.callData.length | |
175 | 174 | } |
176 | 175 | |
177 | 176 | /** |
178 | 177 | * Copys the input data in current environment to memory. This pertains to |
@@ -184,9 +183,9 @@ | ||
184 | 183 | callDataCopy (offset, dataOffset, length) { |
185 | 184 | this.takeGas(3 + Math.ceil(length / 32) * 3) |
186 | 185 | |
187 | 186 | if (length) { |
188 | - const callData = this.environment.callData.slice(dataOffset, dataOffset + length) | |
187 | + const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + length) | |
189 | 188 | this.setMemory(offset, length, callData) |
190 | 189 | } |
191 | 190 | } |
192 | 191 | |
@@ -197,9 +196,9 @@ | ||
197 | 196 | * @param {integer} dataOffset the offset in the input data |
198 | 197 | */ |
199 | 198 | callDataCopy256 (offset, dataOffset) { |
200 | 199 | this.takeGas(3) |
201 | - const callData = this.environment.callData.slice(dataOffset, dataOffset + 32) | |
200 | + const callData = this.kernel.environment.callData.slice(dataOffset, dataOffset + 32) | |
202 | 201 | this.setMemory(offset, U256_SIZE_BYTES, callData) |
203 | 202 | } |
204 | 203 | |
205 | 204 | /** |
@@ -208,9 +207,9 @@ | ||
208 | 207 | */ |
209 | 208 | getCodeSize () { |
210 | 209 | this.takeGas(2) |
211 | 210 | |
212 | - return this.environment.code.length | |
211 | + return this.kernel.environment.code.length | |
213 | 212 | } |
214 | 213 | |
215 | 214 | /** |
216 | 215 | * Copys the code running in current environment to memory. |
@@ -221,9 +220,9 @@ | ||
221 | 220 | codeCopy (resultOffset, codeOffset, length) { |
222 | 221 | this.takeGas(3 + Math.ceil(length / 32) * 3) |
223 | 222 | |
224 | 223 | if (length) { |
225 | - const code = this.environment.code.slice(codeOffset, codeOffset + length) | |
224 | + const code = this.kernel.environment.code.slice(codeOffset, codeOffset + length) | |
226 | 225 | this.setMemory(resultOffset, length, code) |
227 | 226 | } |
228 | 227 | } |
229 | 228 | |
@@ -235,9 +234,9 @@ | ||
235 | 234 | getExternalCodeSize (addressOffset) { |
236 | 235 | this.takeGas(20) |
237 | 236 | |
238 | 237 | const address = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) |
239 | - const code = this.environment.getCode(address) | |
238 | + const code = this.kernel.environment.getCode(address) | |
240 | 239 | return code.length |
241 | 240 | } |
242 | 241 | |
243 | 242 | /** |
@@ -251,9 +250,9 @@ | ||
251 | 250 | this.takeGas(20 + Math.ceil(length / 32) * 3) |
252 | 251 | |
253 | 252 | if (length) { |
254 | 253 | const address = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) |
255 | - let code = this.environment.getCode(address) | |
254 | + let code = this.kernel.environment.getCode(address) | |
256 | 255 | code = code.slice(codeOffset, codeOffset + length) |
257 | 256 | this.setMemory(resultOffset, length, code) |
258 | 257 | } |
259 | 258 | } |
@@ -264,9 +263,9 @@ | ||
264 | 263 | */ |
265 | 264 | getTxGasPrice () { |
266 | 265 | this.takeGas(2) |
267 | 266 | |
268 | - return this.environment.gasPrice | |
267 | + return this.kernel.environment.gasPrice | |
269 | 268 | } |
270 | 269 | |
271 | 270 | /** |
272 | 271 | * Gets the hash of one of the 256 most recent complete blocks. |
@@ -275,15 +274,15 @@ | ||
275 | 274 | */ |
276 | 275 | getBlockHash (number, offset, cbOffset) { |
277 | 276 | this.takeGas(20) |
278 | 277 | |
279 | - const diff = this.environment.block.number - number | |
278 | + const diff = this.kernel.environment.block.number - number | |
280 | 279 | let opPromise |
281 | 280 | |
282 | 281 | if (diff > 256 || diff <= 0) { |
283 | 282 | opPromise = Promise.resolve(new U256(0)) |
284 | 283 | } else { |
285 | - opPromise = this.environment.getBlockHash(number) | |
284 | + opPromise = this.kernel.environment.getBlockHash(number) | |
286 | 285 | } |
287 | 286 | |
288 | 287 | // wait for all the prevouse async ops to finish before running the callback |
289 | 288 | this.kernel.pushOpsQueue(opPromise, cbOffset, hash => { |
@@ -297,9 +296,9 @@ | ||
297 | 296 | */ |
298 | 297 | getBlockCoinbase (offset) { |
299 | 298 | this.takeGas(2) |
300 | 299 | |
301 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, this.environment.block.coinbase.toMemory()) | |
300 | + this.setMemory(offset, ADDRESS_SIZE_BYTES, this.kernel.environment.block.coinbase.toMemory()) | |
302 | 301 | } |
303 | 302 | |
304 | 303 | /** |
305 | 304 | * Get the block’s timestamp. |
@@ -307,9 +306,9 @@ | ||
307 | 306 | */ |
308 | 307 | getBlockTimestamp () { |
309 | 308 | this.takeGas(2) |
310 | 309 | |
311 | - return this.environment.block.timestamp | |
310 | + return this.kernel.environment.block.timestamp | |
312 | 311 | } |
313 | 312 | |
314 | 313 | /** |
315 | 314 | * Get the block’s number. |
@@ -317,9 +316,9 @@ | ||
317 | 316 | */ |
318 | 317 | getBlockNumber () { |
319 | 318 | this.takeGas(2) |
320 | 319 | |
321 | - return this.environment.block.number | |
320 | + return this.kernel.environment.block.number | |
322 | 321 | } |
323 | 322 | |
324 | 323 | /** |
325 | 324 | * Get the block’s difficulty. |
@@ -327,9 +326,9 @@ | ||
327 | 326 | */ |
328 | 327 | getBlockDifficulty (offset) { |
329 | 328 | this.takeGas(2) |
330 | 329 | |
331 | - this.setMemory(offset, U256_SIZE_BYTES, this.environment.block.difficulty.toMemory()) | |
330 | + this.setMemory(offset, U256_SIZE_BYTES, this.kernel.environment.block.difficulty.toMemory()) | |
332 | 331 | } |
333 | 332 | |
334 | 333 | /** |
335 | 334 | * Get the block’s gas limit. |
@@ -337,9 +336,9 @@ | ||
337 | 336 | */ |
338 | 337 | getBlockGasLimit () { |
339 | 338 | this.takeGas(2) |
340 | 339 | |
341 | - return this.environment.block.gasLimit | |
340 | + return this.kernel.environment.block.gasLimit | |
342 | 341 | } |
343 | 342 | |
344 | 343 | /** |
345 | 344 | * Creates a new log in the current environment |
@@ -372,9 +371,9 @@ | ||
372 | 371 | if (numberOfTopics > 3) { |
373 | 372 | topics.push(U256.fromMemory(this.getMemory(topic4, U256_SIZE_BYTES))) |
374 | 373 | } |
375 | 374 | |
376 | - this.environment.logs.push({ | |
375 | + this.kernel.environment.logs.push({ | |
377 | 376 | data: data, |
378 | 377 | topics: topics |
379 | 378 | }) |
380 | 379 | } |
@@ -395,9 +394,9 @@ | ||
395 | 394 | const data = this.getMemory(dataOffset, length).slice(0) |
396 | 395 | // const [errorCode, address] = this.environment.create(value, data) |
397 | 396 | } |
398 | 397 | let address |
399 | - if (value.gt(this.environment.value)) { | |
398 | + if (value.gt(this.kernel.environment.value)) { | |
400 | 399 | address = new Address() |
401 | 400 | } else { |
402 | 401 | address = new Address('0x945304eb96065b2a98b57a48a06ae28d285a71b5') |
403 | 402 | } |
@@ -517,9 +516,9 @@ | ||
517 | 516 | const path = [...this.getMemory(pathOffset, U256_SIZE_BYTES)] |
518 | 517 | // copy the value |
519 | 518 | const value = this.getMemory(valueOffset, U256_SIZE_BYTES).slice(0) |
520 | 519 | const valIsZero = value.every((i) => i === 0) |
521 | - const opPromise = this.environment.state.get(path) | |
520 | + const opPromise = this.kernel.environment.state.get(path) | |
522 | 521 | .catch(() => { |
523 | 522 | // TODO: handle errors |
524 | 523 | // the value was not found |
525 | 524 | return null |
@@ -527,17 +526,17 @@ | ||
527 | 526 | |
528 | 527 | this.kernel.pushOpsQueue(opPromise, cbDest, oldValue => { |
529 | 528 | if (valIsZero && oldValue) { |
530 | 529 | // delete a value |
531 | - this.environment.gasRefund += 15000 | |
532 | - this.environment.state.del(path) | |
530 | + this.kernel.environment.gasRefund += 15000 | |
531 | + this.kernel.environment.state.del(path) | |
533 | 532 | } else { |
534 | 533 | if (!valIsZero && !oldValue) { |
535 | 534 | // creating a new value |
536 | 535 | this.takeGas(15000) |
537 | 536 | } |
538 | 537 | // update |
539 | - this.environment.state.set(path, value) | |
538 | + this.kernel.environment.state.set(path, value) | |
540 | 539 | } |
541 | 540 | }) |
542 | 541 | } |
543 | 542 | |
@@ -550,9 +549,9 @@ | ||
550 | 549 | this.takeGas(50) |
551 | 550 | |
552 | 551 | // convert the path to an array |
553 | 552 | const path = [...this.getMemory(pathOffset, U256_SIZE_BYTES)] |
554 | - const opPromise = this.environment.state.get(path) | |
553 | + const opPromise = this.kernel.environment.state.get(path) | |
555 | 554 | .catch(() => { |
556 | 555 | // TODO: handle other possible errors |
557 | 556 | // if the value was not found return a empty array |
558 | 557 | return new Uint8Array(32) |
@@ -569,9 +568,9 @@ | ||
569 | 568 | * @param {integer} length the length of the output data. |
570 | 569 | */ |
571 | 570 | return (offset, length) { |
572 | 571 | if (length) { |
573 | - this.environment.returnValue = this.getMemory(offset, length).slice(0) | |
572 | + this.kernel.environment.returnValue = this.getMemory(offset, length).slice(0) | |
574 | 573 | } |
575 | 574 | } |
576 | 575 | |
577 | 576 | /** |
@@ -579,11 +578,11 @@ | ||
579 | 578 | * balance to an address path |
580 | 579 | * @param {integer} offset the offset to load the address from |
581 | 580 | */ |
582 | 581 | selfDestruct (addressOffset) { |
583 | - this.environment.selfDestruct = true | |
584 | - this.environment.selfDestructAddress = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) | |
585 | - this.environment.gasRefund += 24000 | |
582 | + this.kernel.environment.selfDestruct = true | |
583 | + this.kernel.environment.selfDestructAddress = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) | |
584 | + this.kernel.environment.gasRefund += 24000 | |
586 | 585 | } |
587 | 586 | |
588 | 587 | getMemory (offset, length) { |
589 | 588 | return new Uint8Array(this.kernel.memory, offset, length) |
@@ -598,12 +597,12 @@ | ||
598 | 597 | * Takes gas from the tank. Only needs to check if there's gas left to be taken, |
599 | 598 | * because every caller of this method is trusted. |
600 | 599 | */ |
601 | 600 | takeGas (amount) { |
602 | - if (this.environment.gasLeft < amount) { | |
601 | + if (this.kernel.environment.gasLeft < amount) { | |
603 | 602 | throw new Error('Ran out of gas') |
604 | 603 | } |
605 | - this.environment.gasLeft -= amount | |
604 | + this.kernel.environment.gasLeft -= amount | |
606 | 605 | } |
607 | 606 | } |
608 | 607 | |
609 | 608 | // converts a 64 bit number to a JS number |
debugKernel.js | ||
---|---|---|
@@ -1,15 +1,0 @@ | ||
1 | -const Kernel = require('./index.js') | |
2 | -const KernelInterface = require('./kernelInterface.js') | |
3 | -const Environment = require('./environment.js') | |
4 | - | |
5 | -module.exports = class DebugKernel extends Kernel { | |
6 | - codeHandler (code, environment = new Environment({state: this.state})) { | |
7 | - const kernelInterface = new KernelInterface(this, environment) | |
8 | - const promise = kernelInterface.run(code) | |
9 | - // expose the memory for debugging | |
10 | - this.memory = kernelInterface.memory | |
11 | - this.instance = kernelInterface._instance | |
12 | - this.onDone = kernelInterface.onDone.bind(kernelInterface) | |
13 | - return promise | |
14 | - } | |
15 | -} |
kernelInterface.js | ||
---|---|---|
@@ -1,50 +1,0 @@ | ||
1 | -module.exports = class KernelInterface { | |
2 | - constructor (kernel, environment) { | |
3 | - this._kernel = kernel | |
4 | - this._environment = environment | |
5 | - } | |
6 | - | |
7 | - run (code) { | |
8 | - const imports = this._kernel.interfaces.reduce((obj, Interface) => { | |
9 | - obj[Interface.name] = new Interface(this).exports | |
10 | - return obj | |
11 | - }, {}) | |
12 | - | |
13 | - const module = WebAssembly.Module(code) | |
14 | - const instance = this._instance = WebAssembly.Instance(module, imports) | |
15 | - | |
16 | - if (instance.exports.main) { | |
17 | - instance.exports.main() | |
18 | - } | |
19 | - return this.onDone() | |
20 | - } | |
21 | - | |
22 | - // returns a promise that resolves when the wasm instance is done running | |
23 | - async onDone () { | |
24 | - let prevOps | |
25 | - while (prevOps !== this._opsQueue) { | |
26 | - prevOps = this._opsQueue | |
27 | - await this._opsQueue | |
28 | - } | |
29 | - } | |
30 | - | |
31 | - pushOpsQueue (promise, callbackIndex, intefaceCallback) { | |
32 | - this._opsQueue = Promise.all([this._opsQueue, promise]).then(values => { | |
33 | - intefaceCallback(values.pop()) | |
34 | - this._instance.exports[callbackIndex.toString()]() | |
35 | - }) | |
36 | - } | |
37 | - | |
38 | - sendMessage (message) { | |
39 | - | |
40 | - } | |
41 | - | |
42 | - get environment () { | |
43 | - return this._environment | |
44 | - } | |
45 | - | |
46 | - get memory () { | |
47 | - return this._instance.exports.memory | |
48 | - } | |
49 | - | |
50 | -} |
precompiles/create.js | ||
---|---|---|
@@ -1,0 +1,55 @@ | ||
1 | + createHandler (create) { | |
2 | + let code = create.data | |
3 | + | |
4 | + // Inject metering | |
5 | + if (Utils.isWASMCode(code)) { | |
6 | + // FIXME: decide if these are the right values here: from: 0, gasLimit: 0, value: 0 | |
7 | + code = this.callHandler({ | |
8 | + from: Address.zero(), | |
9 | + to: meteringContract, | |
10 | + gasLimit: 0, | |
11 | + value: new U256(0), | |
12 | + data: code | |
13 | + }).returnValue | |
14 | + | |
15 | + if (code[0] === 0) { | |
16 | + code = code.slice(1) | |
17 | + } else { | |
18 | + throw new Error('Metering injection failed: ' + Buffer.from(code).slice(1).toString()) | |
19 | + } | |
20 | + } | |
21 | + | |
22 | + let account = this.environment.state.get(create.from.toString()) | |
23 | + if (!account) { | |
24 | + throw new Error('Account not found: ' + create.from.toString()) | |
25 | + } | |
26 | + | |
27 | + let address = Utils.newAccountAddress(create.from, account.get('nonce')) | |
28 | + | |
29 | + this.environment.addAccount(address.toString(), { | |
30 | + balance: create.value, | |
31 | + code: code | |
32 | + }) | |
33 | + | |
34 | + // Run code and take return value as contract code | |
35 | + // FIXME: decide if these are the right values here: value: 0, data: '' | |
36 | + code = this.messageHandler({ | |
37 | + from: create.from, | |
38 | + to: address, | |
39 | + gasLimit: create.gasLimit, | |
40 | + value: new U256(0), | |
41 | + data: new Uint8Array() | |
42 | + }).returnValue | |
43 | + | |
44 | + // FIXME: special handling for selfdestruct | |
45 | + | |
46 | + this.environment.state.get(address.toString()).set('code', code) | |
47 | + | |
48 | + return { | |
49 | + executionOutcome: 1, // success | |
50 | + gasLeft: new U256(this.environment.gasLeft), | |
51 | + gasRefund: new U256(this.environment.gasRefund), | |
52 | + accountCreated: address, | |
53 | + logs: this.environment.logs | |
54 | + } | |
55 | + } |
precompiles/precompile.js | ||
---|---|---|
@@ -1,0 +1,29 @@ | ||
1 | +// const evm2wasm = require('evm2wasm') | |
2 | +// const metering = require('wasm-metering') | |
3 | + | |
4 | +function craftResponse (status, msg) { | |
5 | + // NOTE: why does this has to be so hard? | |
6 | + return Uint8Array.from(Buffer.concat([ new Buffer([ status ]), new Buffer(msg) ])) | |
7 | +} | |
8 | + | |
9 | +module.exports.meteringInjector = function (call) { | |
10 | + console.log('Executing metering injector') | |
11 | + return { | |
12 | + // returnValue: metering.injectWAST(call.data, 2).slice(0) | |
13 | + returnValue: craftResponse(0, call.data) | |
14 | + } | |
15 | +} | |
16 | + | |
17 | +module.exports.transcompiler = function (call) { | |
18 | + console.log('Executing transcompiler') | |
19 | + return { | |
20 | + // returnValue: evm2wasm.compileEVM(call.data).slice(0) | |
21 | + returnValue: craftResponse(1, 'Code not supported: ' + Buffer.from(call.data.slice(0, 8)).toString('hex') + '...') | |
22 | + } | |
23 | +} | |
24 | + | |
25 | +module.exports.identity = function (call) { | |
26 | + return { | |
27 | + returnValue: call.data.slice(0) | |
28 | + } | |
29 | +} |
precompile.js | ||
---|---|---|
@@ -1,29 +1,0 @@ | ||
1 | -// const evm2wasm = require('evm2wasm') | |
2 | -// const metering = require('wasm-metering') | |
3 | - | |
4 | -function craftResponse (status, msg) { | |
5 | - // NOTE: why does this has to be so hard? | |
6 | - return Uint8Array.from(Buffer.concat([ new Buffer([ status ]), new Buffer(msg) ])) | |
7 | -} | |
8 | - | |
9 | -module.exports.meteringInjector = function (call) { | |
10 | - console.log('Executing metering injector') | |
11 | - return { | |
12 | - // returnValue: metering.injectWAST(call.data, 2).slice(0) | |
13 | - returnValue: craftResponse(0, call.data) | |
14 | - } | |
15 | -} | |
16 | - | |
17 | -module.exports.transcompiler = function (call) { | |
18 | - console.log('Executing transcompiler') | |
19 | - return { | |
20 | - // returnValue: evm2wasm.compileEVM(call.data).slice(0) | |
21 | - returnValue: craftResponse(1, 'Code not supported: ' + Buffer.from(call.data.slice(0, 8)).toString('hex') + '...') | |
22 | - } | |
23 | -} | |
24 | - | |
25 | -module.exports.identity = function (call) { | |
26 | - return { | |
27 | - returnValue: call.data.slice(0) | |
28 | - } | |
29 | -} |
Built with git-ssb-web