Commit 26423b152859d2a3cccf24570ef55cf2770aee41
init non-atomic
wanderer committed on 4/14/2017, 7:23:09 PMParent: 9224a38130d9646acdb70e89eadcd3de8b626082
Files changed
index.js | ||
---|---|---|
@@ -1,111 +1,44 @@ | ||
1 | -const EventEmitter = require('events') | |
2 | -const PortManager = require('./portManager.js') | |
3 | -const codeHandler = require('./codeHandler.js') | |
4 | -const AtomicMessage = require('primea-message/atomic') | |
1 | +const Kernel = require('./index.js') | |
5 | 2 | |
6 | -module.exports = class Kernel extends EventEmitter { | |
7 | - constructor (opts = {}) { | |
8 | - super() | |
9 | - // set up the state | |
10 | - this.graph = opts.graph | |
11 | - this.path = opts.path || '' | |
12 | - this.imports = opts.imports | |
13 | - const state = this.state = opts.state || {} | |
3 | +module.exports = class Hypervisor { | |
4 | + constructor (opts) { | |
5 | + this._opts = { | |
6 | + state: {}, | |
7 | + imports: [], | |
8 | + hypervisor: this | |
9 | + } | |
14 | 10 | |
15 | - // set up the vm | |
16 | - this._vm = (opts.codeHandler || codeHandler).init(opts.code || state) | |
17 | - this._vmstate = 'idle' | |
18 | - | |
19 | - // set up ports | |
20 | - this.ports = new PortManager({ | |
21 | - state: state, | |
22 | - graph: this.graph, | |
23 | - parentPort: opts.parentPort, | |
24 | - Kernel: Kernel, | |
25 | - imports: this.imports, | |
26 | - path: this.path | |
27 | - }) | |
28 | - | |
29 | - this.ports.on('message', index => { | |
30 | - this.runNextMessage(index) | |
31 | - }) | |
11 | + Object.assign(this._opts, opts) | |
12 | + this._runningVMs = new Map() | |
32 | 13 | } |
33 | 14 | |
34 | - runNextMessage (index = 0) { | |
35 | - // load the next message from port space | |
36 | - return this.ports.peek(index).then(message => { | |
37 | - if (message && | |
38 | - (this._vmstate === 'idle' || | |
39 | - (AtomicMessage.isAtomic(message) && message.isCyclic(this)))) { | |
40 | - this._currentMessage = message | |
41 | - this.ports.remove(index) | |
42 | - return this.run(message) | |
43 | - } else { | |
44 | - this._vmstate = 'idle' | |
45 | - this.emit('idle') | |
46 | - } | |
47 | - }) | |
15 | + set (path, value) { | |
16 | + return this._opts.graph.set(this._opts.state, path, value) | |
48 | 17 | } |
49 | 18 | |
50 | - /** | |
51 | - * run the kernels code with a given enviroment | |
52 | - * The Kernel Stores all of its state in the Environment. The Interface is used | |
53 | - * to by the VM to retrive infromation from the Environment. | |
54 | - */ | |
55 | - async run (message, imports = this.imports) { | |
56 | - const self = this | |
57 | - function revert (oldState) { | |
58 | - // revert the state | |
59 | - clearObject(self.state) | |
60 | - Object.assign(self.state, oldState) | |
61 | - } | |
62 | - | |
63 | - // shallow copy | |
64 | - const oldState = Object.assign({}, this.state) | |
65 | - let result | |
66 | - this._vmstate = 'running' | |
67 | - try { | |
68 | - result = await this._vm.run(message, this, imports) || {} | |
69 | - } catch (e) { | |
70 | - result = { | |
71 | - exception: true, | |
72 | - exceptionError: e | |
73 | - } | |
74 | - } | |
75 | - | |
76 | - // if we trapped revert all the sent messages | |
77 | - if (result.exception) { | |
78 | - // revert to the old state | |
79 | - revert(oldState) | |
80 | - message._reject(result) | |
81 | - } else if (AtomicMessage.isAtomic(message) && !message.hasResponded) { | |
82 | - message.respond(result) | |
83 | - } | |
84 | - | |
85 | - message._committed().then(() => { | |
86 | - this.runNextMessage(0) | |
87 | - }).catch((e) => { | |
88 | - revert(oldState) | |
89 | - }) | |
90 | - return result | |
19 | + sendMessage (portObj, message) { | |
20 | + const vm = this.getInstaceFromPort(portObj) | |
21 | + vm.queue(message) | |
91 | 22 | } |
92 | 23 | |
93 | - async send (portName, message) { | |
94 | - if (AtomicMessage.isAtomic(message)) { | |
95 | - // record that this message has traveled thourgh this kernel. This is used | |
96 | - // to detect re-entry | |
97 | - message._visited(this, this._currentMessage) | |
24 | + getVMFromPort (port) { | |
25 | + const id = Kernel.id(port) | |
26 | + let kernel = this._vms.get(id) | |
27 | + if (!kernel) { | |
28 | + kernel = new Kernel(port) | |
29 | + kernel.on('idle', () => { | |
30 | + this._vms.delete(id) | |
31 | + }) | |
32 | + this._vms.set(id, kernel) | |
98 | 33 | } |
99 | - return this.ports.send(portName, message) | |
100 | 34 | } |
101 | 35 | |
102 | - shutdown () { | |
103 | - this.ports.close() | |
36 | + // given a port, wait untill its source contract has reached the threshold | |
37 | + // tick count | |
38 | + async waitOnPort (port, ticks) { | |
39 | + let kernel = this.getVMFromPort(port) | |
40 | + const tickCount = await kernel.wait(ticks) | |
41 | + port.ticks = tickCount | |
42 | + return tickCount | |
104 | 43 | } |
105 | 44 | } |
106 | - | |
107 | -function clearObject (myObject) { | |
108 | - for (var member in myObject) { | |
109 | - delete myObject[member] | |
110 | - } | |
111 | -} |
package.json | ||
---|---|---|
@@ -48,7 +48,8 @@ | ||
48 | 48 | "ethereumjs-util": "^5.1.0", |
49 | 49 | "fixed-bn.js": "0.0.2", |
50 | 50 | "ipld-graph-builder": "1.0.1", |
51 | 51 | "primea-message": "0.0.0", |
52 | - "primea-wasm-container": "0.0.0" | |
52 | + "primea-wasm-container": "0.0.0", | |
53 | + "webcrypto-liner": "^0.1.20" | |
53 | 54 | } |
54 | 55 | } |
port.js | ||
---|---|---|
@@ -1,26 +1,37 @@ | ||
1 | -const EventEmitter = require('events') | |
2 | - | |
3 | -module.exports = class Port extends EventEmitter { | |
1 | +module.exports = class Port { | |
4 | 2 | constructor (name) { |
5 | - super() | |
6 | 3 | this.name = name |
7 | - this.connected = false | |
4 | + this._queue = [] | |
5 | + this.ticks = 0 | |
8 | 6 | } |
9 | 7 | |
10 | - connect (destPort) { | |
11 | - if (!this.connected) { | |
12 | - this.destPort = destPort | |
13 | - destPort.destPort = this | |
14 | - this.connected = true | |
8 | + queue (message) { | |
9 | + this.ticks = message.ticks | |
10 | + if (this._resolve) { | |
11 | + return this._resolve(message) | |
12 | + } else { | |
13 | + this.queue.push(message) | |
15 | 14 | } |
16 | 15 | } |
17 | 16 | |
18 | - async send (message) { | |
19 | - message._hops++ | |
20 | - this.destPort.recieve(message) | |
17 | + // this only workls for one Promise | |
18 | + nextMessage () { | |
19 | + const message = this.queue.shift() | |
20 | + | |
21 | + return new Promise((resolve, reject) => { | |
22 | + if (message) { | |
23 | + resolve(message) | |
24 | + } else { | |
25 | + this._resolve = resolve | |
26 | + } | |
27 | + }) | |
21 | 28 | } |
22 | 29 | |
23 | - recieve (message) { | |
24 | - this.emit('message', message) | |
30 | + peek () { | |
31 | + return this._queue[0] | |
25 | 32 | } |
33 | + | |
34 | + shift () { | |
35 | + this._queue.shift() | |
36 | + } | |
26 | 37 | } |
portManager.js | ||
---|---|---|
@@ -1,80 +1,98 @@ | ||
1 | -const EventEmitter = require('events') | |
2 | -const path = require('path') | |
3 | -const AtomicMessage = require('primea-message/atomic') | |
4 | 1 | const Port = require('./port.js') |
5 | 2 | const common = require('./common.js') |
6 | 3 | |
7 | -module.exports = class PortManager extends EventEmitter { | |
8 | - constructor (opts) { | |
9 | - super() | |
10 | - Object.assign(this, opts) | |
11 | - this._queue = [] | |
12 | - // set up the parent port | |
13 | - const parentPort = new Port(common.PARENT) | |
14 | - parentPort.on('message', message => { | |
15 | - this._recieveMessage(message) | |
4 | +module.exports = class PortManager { | |
5 | + constructor (ports, kernel) { | |
6 | + this.kernel = kernel | |
7 | + this.ports = ports | |
8 | + this._portMap = new Map() | |
9 | + this._hasMappedPorts = false | |
10 | + this._tempQueue = [] | |
11 | + this._mapPorts(this.ports).then(ports => { | |
12 | + this._portMap = ports | |
13 | + this.queue = this._queue | |
14 | + for (const message of this._tempQueue) { | |
15 | + this.queue(message) | |
16 | + } | |
16 | 17 | }) |
18 | + } | |
17 | 19 | |
18 | - // create the cache | |
19 | - this.cache = new Map() | |
20 | - this.cache.set(common.PARENT, parentPort) | |
20 | + queue (message) { | |
21 | + this._tempQueue.push(message) | |
21 | 22 | } |
22 | 23 | |
23 | - _recieveMessage (message) { | |
24 | - const index = this._queue.push(message) - 1 | |
25 | - this.emit('message', index) | |
24 | + _queue (message) { | |
25 | + this._portMap.get(message.from).push(message) | |
26 | 26 | } |
27 | 27 | |
28 | - async get (name) { | |
29 | - let port = this.cache.get(name) | |
30 | - if (!port) { | |
31 | - port = new Port(name) | |
32 | - port.on('message', message => { | |
33 | - this._recieveMessage(message) | |
28 | + async _mapPorts (ports) { | |
29 | + ports = Object.key(ports).map(name => { | |
30 | + const port = ports[name] | |
31 | + this.kernel.id(port).then(id => { | |
32 | + return [id, new Port(name)] | |
34 | 33 | }) |
35 | - // create destination kernel | |
36 | - const state = await this.graph.get(this.state, name) | |
34 | + }) | |
35 | + ports = await Promise.all(ports) | |
36 | + return new Map(ports) | |
37 | + } | |
37 | 38 | |
38 | - const destKernel = new this.Kernel({ | |
39 | - state: state, | |
40 | - graph: this.graph, | |
41 | - parentPort: port, | |
42 | - imports: this.imports, | |
43 | - path: path.join(this.path, name) | |
44 | - }) | |
39 | + create (name, value) { | |
40 | + this.ports[name] = value | |
41 | + } | |
45 | 42 | |
46 | - // shutdown the kernel when it is done doing it work | |
47 | - destKernel.on('idle', () => { | |
48 | - destKernel.shutdown() | |
49 | - this.cache.delete(name) | |
50 | - }) | |
43 | + del (name) { | |
44 | + delete this.ports[name] | |
45 | + } | |
51 | 46 | |
52 | - // find and connect the destination port | |
53 | - const destPort = await destKernel.ports.get(common.PARENT) | |
54 | - port.connect(destPort) | |
55 | - this.cache.set(name, port) | |
56 | - } | |
57 | - return port | |
47 | + move (from, to) { | |
48 | + this.ports[to] = this.ports[from] | |
49 | + delete this.ports[from] | |
58 | 50 | } |
59 | 51 | |
60 | - async peek (index = 0) { | |
61 | - return this._queue[index] | |
52 | + async get (name) { | |
53 | + const port = await name === common.PARENT ? this.graph.get(this.state.ports, name) : this.parentId | |
54 | + const id = await this.kernel.id(port) | |
55 | + return this._portMap.get(id) | |
62 | 56 | } |
63 | 57 | |
64 | - remove (index) { | |
65 | - return this._queue.splice(index, index + 1) | |
58 | + // waits till all ports have reached a threshold tick count | |
59 | + async wait (ticks) { | |
60 | + const unkownPorts = [...this._ports].filter((id, port) => { | |
61 | + const message = port.peek() | |
62 | + return !message || message.ticks < ticks | |
63 | + }) | |
64 | + | |
65 | + const promises = [] | |
66 | + for (const id in unkownPorts) { | |
67 | + promises.push(this.hypervisor.waitOnVM(id, ticks)) | |
68 | + } | |
69 | + await Promise.all(promises) | |
66 | 70 | } |
67 | 71 | |
68 | - async send (portName, message) { | |
69 | - const port = await this.get(portName) | |
70 | - port.send(message) | |
71 | - return AtomicMessage.isAtomic(message) ? message.result() : {} | |
72 | + async getNextMessage (ticks) { | |
73 | + await this.wait(ticks) | |
74 | + return [...this._ports].reduce(messageArbiter).shift() | |
72 | 75 | } |
76 | +} | |
73 | 77 | |
74 | - close () { | |
75 | - for (let port in this.cache) { | |
76 | - port.emit('close') | |
77 | - } | |
78 | - this.cache.clear() | |
78 | +// decides which message to go firts | |
79 | +function messageArbiter (portA, portB) { | |
80 | + const a = portA.peek() | |
81 | + const b = portB.peek() | |
82 | + | |
83 | + if (!a) { | |
84 | + return b | |
85 | + } else if (!b) { | |
86 | + return a | |
79 | 87 | } |
88 | + | |
89 | + const aGasPrice = a.resources.gasPrice | |
90 | + const bGasPrice = b.resources.gasPrice | |
91 | + if (a.ticks !== b.ticks) { | |
92 | + return a.ticks < b.ticks ? a : b | |
93 | + } else if (aGasPrice === bGasPrice) { | |
94 | + return a.hash() > b.hash() ? a : b | |
95 | + } else { | |
96 | + return aGasPrice > bGasPrice ? a : b | |
97 | + } | |
80 | 98 | } |
EVMinterface.js | ||
---|---|---|
@@ -1,621 +1,0 @@ | ||
1 | -/** | |
2 | - * This is the Ethereum interface that is exposed to the WASM instance which | |
3 | - * enables to interact with the Ethereum Environment | |
4 | - */ | |
5 | -const fs = require('fs') | |
6 | -const ethUtil = require('ethereumjs-util') | |
7 | -const Vertex = require('ipld-graph-builder') | |
8 | -const U256 = require('fixed-bn.js').U256 | |
9 | -const U128 = require('fixed-bn.js').U128 | |
10 | -const Message = require('primea-message') | |
11 | -const common = require('./common.js') | |
12 | - | |
13 | -const U128_SIZE_BYTES = 16 | |
14 | -const ADDRESS_SIZE_BYTES = 20 | |
15 | -const U256_SIZE_BYTES = 32 | |
16 | - | |
17 | -// The interface exposed to the WebAessembly VM | |
18 | -module.exports = class Interface { | |
19 | - constructor (opts) { | |
20 | - opts.response.gasRefund = 0 | |
21 | - this.message = opts.message | |
22 | - this.kernel = opts.kernel | |
23 | - this.vm = opts.vm | |
24 | - this.results = opts.response | |
25 | - | |
26 | - const shimBin = fs.readFileSync(`${__dirname}/wasm/interface.wasm`) | |
27 | - const shimMod = WebAssembly.Module(shimBin) | |
28 | - | |
29 | - this.shims = WebAssembly.Instance(shimMod, { | |
30 | - 'interface': { | |
31 | - 'useGas': this._useGas.bind(this), | |
32 | - 'getGasLeftHigh': this._getGasLeftHigh.bind(this), | |
33 | - 'getGasLeftLow': this._getGasLeftLow.bind(this), | |
34 | - 'call': this._call.bind(this) | |
35 | - } | |
36 | - }) | |
37 | - this.useGas = this.shims.exports.useGas | |
38 | - this.getGasLeft = this.shims.exports.getGasLeft | |
39 | - this.call = this.shims.exports.call | |
40 | - } | |
41 | - | |
42 | - static get name () { | |
43 | - return 'ethereum' | |
44 | - } | |
45 | - | |
46 | - static get hostContainer () { | |
47 | - return 'wasm' | |
48 | - } | |
49 | - | |
50 | - /** | |
51 | - * Subtracts an amount to the gas counter | |
52 | - * @param {integer} amount the amount to subtract to the gas counter | |
53 | - */ | |
54 | - _useGas (high, low) { | |
55 | - this.takeGas(from64bit(high, low)) | |
56 | - } | |
57 | - | |
58 | - /** | |
59 | - * Returns the current amount of gas | |
60 | - * @return {integer} | |
61 | - */ | |
62 | - _getGasLeftHigh () { | |
63 | - return Math.floor(this.message.gas / 4294967296) | |
64 | - } | |
65 | - | |
66 | - /** | |
67 | - * Returns the current amount of gas | |
68 | - * @return {integer} | |
69 | - */ | |
70 | - _getGasLeftLow () { | |
71 | - return this.message.gas | |
72 | - } | |
73 | - | |
74 | - /** | |
75 | - * Gets address of currently executing account and loads it into memory at | |
76 | - * the given offset. | |
77 | - * @param {integer} offset | |
78 | - */ | |
79 | - getAddress (offset) { | |
80 | - this.takeGas(2) | |
81 | - const path = this.kernel.path | |
82 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, Buffer.from(path[1].slice(2), 'hex')) | |
83 | - } | |
84 | - | |
85 | - /** | |
86 | - * Gets balance of the given account and loads it into memory at the given | |
87 | - * offset. | |
88 | - * @param {integer} addressOffset the memory offset to laod the address | |
89 | - * @param {integer} resultOffset | |
90 | - */ | |
91 | - getBalance (addressOffset, offset, cbIndex) { | |
92 | - this.takeGas(20) | |
93 | - | |
94 | - const path = [common.PARENT, common.PARENT, '0x' + Buffer.from(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)).toString('hex')] | |
95 | - const opPromise = this.kernel.send(new Message({ | |
96 | - to: path, | |
97 | - data: { | |
98 | - getValue: 'balance' | |
99 | - }, | |
100 | - sync: true | |
101 | - })) | |
102 | - .catch(() => Buffer.from([])) | |
103 | - | |
104 | - this.vm.pushOpsQueue(opPromise, cbIndex, balance => { | |
105 | - this.setMemory(offset, U128_SIZE_BYTES, new U128(balance).toBuffer()) | |
106 | - }) | |
107 | - } | |
108 | - | |
109 | - /** | |
110 | - * Gets the execution's origination address and loads it into memory at the | |
111 | - * given offset. This is the sender of original transaction; it is never an | |
112 | - * account with non-empty associated code. | |
113 | - * @param {integer} offset | |
114 | - */ | |
115 | - getTxOrigin (offset) { | |
116 | - this.takeGas(2) | |
117 | - | |
118 | - const origin = Buffer.from(this.message.from[2].slice(2), 'hex') | |
119 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, origin) | |
120 | - } | |
121 | - | |
122 | - /** | |
123 | - * Gets caller address and loads it into memory at the given offset. This is | |
124 | - * the address of the account that is directly responsible for this execution. | |
125 | - * @param {integer} offset | |
126 | - */ | |
127 | - getCaller (offset) { | |
128 | - this.takeGas(2) | |
129 | - const caller = this.message.from[2] | |
130 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, Buffer.from(caller.slice(2), 'hex')) | |
131 | - } | |
132 | - | |
133 | - /** | |
134 | - * Gets the deposited value by the instruction/transaction responsible for | |
135 | - * this execution and loads it into memory at the given location. | |
136 | - * @param {integer} offset | |
137 | - */ | |
138 | - getCallValue (offset) { | |
139 | - this.takeGas(2) | |
140 | - | |
141 | - this.setMemory(offset, U128_SIZE_BYTES, this.message.value.toBuffer()) | |
142 | - } | |
143 | - | |
144 | - /** | |
145 | - * Get size of input data in current environment. This pertains to the input | |
146 | - * data passed with the message call instruction or transaction. | |
147 | - * @return {integer} | |
148 | - */ | |
149 | - getCallDataSize () { | |
150 | - this.takeGas(2) | |
151 | - | |
152 | - return this.message.data.length | |
153 | - } | |
154 | - | |
155 | - /** | |
156 | - * Copys the input data in current environment to memory. This pertains to | |
157 | - * the input data passed with the message call instruction or transaction. | |
158 | - * @param {integer} offset the offset in memory to load into | |
159 | - * @param {integer} dataOffset the offset in the input data | |
160 | - * @param {integer} length the length of data to copy | |
161 | - */ | |
162 | - callDataCopy (offset, dataOffset, length) { | |
163 | - this.takeGas(3 + (3 * Math.ceil(length / 32))) | |
164 | - | |
165 | - if (length) { | |
166 | - const callData = this.message.data.slice(dataOffset, dataOffset + length) | |
167 | - this.setMemory(offset, length, callData) | |
168 | - } | |
169 | - } | |
170 | - | |
171 | - /** | |
172 | - * Copys the input data in current environment to memory. This pertains to | |
173 | - * the input data passed with the message call instruction or transaction. | |
174 | - * @param {integer} offset the offset in memory to load into | |
175 | - * @param {integer} dataOffset the offset in the input data | |
176 | - */ | |
177 | - callDataCopy256 (offset, dataOffset) { | |
178 | - this.takeGas(3) | |
179 | - const callData = this.message.data.slice(dataOffset, dataOffset + 32) | |
180 | - this.setMemory(offset, U256_SIZE_BYTES, callData) | |
181 | - } | |
182 | - | |
183 | - /** | |
184 | - * Gets the size of code running in current environment. | |
185 | - * @return {interger} | |
186 | - */ | |
187 | - getCodeSize (cbIndex) { | |
188 | - this.takeGas(2) | |
189 | - | |
190 | - // wait for all the prevouse async ops to finish before running the callback | |
191 | - this.vm.pushOpsQueue(this.kernel.code.length, cbIndex, length => length) | |
192 | - } | |
193 | - | |
194 | - /** | |
195 | - * Copys the code running in current environment to memory. | |
196 | - * @param {integer} offset the memory offset | |
197 | - * @param {integer} codeOffset the code offset | |
198 | - * @param {integer} length the length of code to copy | |
199 | - */ | |
200 | - codeCopy (resultOffset, codeOffset, length, cbIndex) { | |
201 | - this.takeGas(3 + (Math.ceil(length / 32) * 3)) | |
202 | - | |
203 | - // wait for all the prevouse async ops to finish before running the callback | |
204 | - this.vm.pushOpsQueue(this.kernel.code, cbIndex, code => { | |
205 | - if (code.length) { | |
206 | - code = code.slice(codeOffset, codeOffset + length) | |
207 | - this.setMemory(resultOffset, length, code) | |
208 | - } | |
209 | - }) | |
210 | - } | |
211 | - | |
212 | - /** | |
213 | - * Get size of an account’s code. | |
214 | - * @param {integer} addressOffset the offset in memory to load the address from | |
215 | - * @return {integer} | |
216 | - */ | |
217 | - getExternalCodeSize (addressOffset, cbOffset) { | |
218 | - this.takeGas(20) | |
219 | - const address = ['accounts', ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)] | |
220 | - const opPromise = this.kernel.sendMessage(common.ROOT, common.getterMessage('code', address)) | |
221 | - .then(vertex => vertex.value.length) | |
222 | - .catch(() => 0) | |
223 | - | |
224 | - // wait for all the prevouse async ops to finish before running the callback | |
225 | - this.vm.pushOpsQueue(opPromise, cbOffset, length => length) | |
226 | - } | |
227 | - | |
228 | - /** | |
229 | - * Copys the code of an account to memory. | |
230 | - * @param {integer} addressOffset the memory offset of the address | |
231 | - * @param {integer} resultOffset the memory offset | |
232 | - * @param {integer} codeOffset the code offset | |
233 | - * @param {integer} length the length of code to copy | |
234 | - */ | |
235 | - externalCodeCopy (addressOffset, resultOffset, codeOffset, length, cbIndex) { | |
236 | - this.takeGas(20 + (Math.ceil(length / 32) * 3)) | |
237 | - | |
238 | - const address = ['accounts', ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)] | |
239 | - let opPromise | |
240 | - | |
241 | - if (length) { | |
242 | - opPromise = this.kernel.sendMessage(common.ROOT, common.getterMessage('code', address)) | |
243 | - .get(address) | |
244 | - .then(vertex => vertex.value) | |
245 | - .catch(() => []) | |
246 | - } else { | |
247 | - opPromise = Promise.resolve([]) | |
248 | - } | |
249 | - | |
250 | - // wait for all the prevouse async ops to finish before running the callback | |
251 | - this.vm.pushOpsQueue(opPromise, cbIndex, code => { | |
252 | - if (code.length) { | |
253 | - code = code.slice(codeOffset, codeOffset + length) | |
254 | - this.setMemory(resultOffset, length, code) | |
255 | - } | |
256 | - }) | |
257 | - } | |
258 | - | |
259 | - /** | |
260 | - * Gets price of gas in current environment. | |
261 | - * @return {integer} | |
262 | - */ | |
263 | - getTxGasPrice () { | |
264 | - this.takeGas(2) | |
265 | - | |
266 | - return this.message.gasPrice | |
267 | - } | |
268 | - | |
269 | - /** | |
270 | - * Gets the hash of one of the 256 most recent complete blocks. | |
271 | - * @param {integer} number which block to load | |
272 | - * @param {integer} offset the offset to load the hash into | |
273 | - */ | |
274 | - getBlockHash (number, offset, cbOffset) { | |
275 | - this.takeGas(20) | |
276 | - | |
277 | - const diff = this.message.block.number - number | |
278 | - let opPromise | |
279 | - | |
280 | - if (diff > 256 || diff <= 0) { | |
281 | - opPromise = Promise.resolve(new U256(0)) | |
282 | - } else { | |
283 | - opPromise = this.state.get(['blockchain', number]).then(vertex => vertex.hash()) | |
284 | - } | |
285 | - | |
286 | - // wait for all the prevouse async ops to finish before running the callback | |
287 | - this.vm.pushOpsQueue(opPromise, cbOffset, hash => { | |
288 | - this.setMemory(offset, U256_SIZE_BYTES, hash.toBuffer()) | |
289 | - }) | |
290 | - } | |
291 | - | |
292 | - /** | |
293 | - * Gets the block’s beneficiary address and loads into memory. | |
294 | - * @param offset | |
295 | - */ | |
296 | - getBlockCoinbase (offset) { | |
297 | - this.takeGas(2) | |
298 | - | |
299 | - this.setMemory(offset, ADDRESS_SIZE_BYTES, this.message.block.header.coinbase) | |
300 | - } | |
301 | - | |
302 | - /** | |
303 | - * Get the block’s timestamp. | |
304 | - * @return {integer} | |
305 | - */ | |
306 | - getBlockTimestamp () { | |
307 | - this.takeGas(2) | |
308 | - | |
309 | - return this.message.block.timestamp | |
310 | - } | |
311 | - | |
312 | - /** | |
313 | - * Get the block’s number. | |
314 | - * @return {integer} | |
315 | - */ | |
316 | - getBlockNumber () { | |
317 | - this.takeGas(2) | |
318 | - | |
319 | - return this.message.block.number | |
320 | - } | |
321 | - | |
322 | - /** | |
323 | - * Get the block’s difficulty. | |
324 | - * @return {integer} | |
325 | - */ | |
326 | - getBlockDifficulty (offset) { | |
327 | - this.takeGas(2) | |
328 | - | |
329 | - this.setMemory(offset, U256_SIZE_BYTES, this.message.block.difficulty.toBuffer()) | |
330 | - } | |
331 | - | |
332 | - /** | |
333 | - * Get the block’s gas limit. | |
334 | - * @return {integer} | |
335 | - */ | |
336 | - getBlockGasLimit () { | |
337 | - this.takeGas(2) | |
338 | - | |
339 | - return this.message.gasLimit | |
340 | - } | |
341 | - | |
342 | - /** | |
343 | - * Creates a new log in the current environment | |
344 | - * @param {integer} dataOffset the offset in memory to load the memory | |
345 | - * @param {integer} length the data length | |
346 | - * @param {integer} number of topics | |
347 | - */ | |
348 | - log (dataOffset, length, numberOfTopics, topic1, topic2, topic3, topic4) { | |
349 | - if (numberOfTopics < 0 || numberOfTopics > 4) { | |
350 | - throw new Error('Invalid numberOfTopics') | |
351 | - } | |
352 | - | |
353 | - this.takeGas(375 + (length * 8) + (numberOfTopics * 375)) | |
354 | - | |
355 | - const data = length ? this.getMemory(dataOffset, length).slice(0) : new Uint8Array([]) | |
356 | - const topics = [] | |
357 | - | |
358 | - if (numberOfTopics > 0) { | |
359 | - topics.push(U256.fromBuffer(this.getMemory(topic1, U256_SIZE_BYTES))) | |
360 | - } | |
361 | - | |
362 | - if (numberOfTopics > 1) { | |
363 | - topics.push(U256.fromBuffer(this.getMemory(topic2, U256_SIZE_BYTES))) | |
364 | - } | |
365 | - | |
366 | - if (numberOfTopics > 2) { | |
367 | - topics.push(U256.fromBuffer(this.getMemory(topic3, U256_SIZE_BYTES))) | |
368 | - } | |
369 | - | |
370 | - if (numberOfTopics > 3) { | |
371 | - topics.push(U256.fromBuffer(this.getMemory(topic4, U256_SIZE_BYTES))) | |
372 | - } | |
373 | - | |
374 | - this.kernel.sendMessage([this.kernel.root, 'logs'], new Message({ | |
375 | - data: data, | |
376 | - topics: topics | |
377 | - })) | |
378 | - } | |
379 | - | |
380 | - /** | |
381 | - * Creates a new contract with a given value. | |
382 | - * @param {integer} valueOffset the offset in memory to the value from | |
383 | - * @param {integer} dataOffset the offset to load the code for the new contract from | |
384 | - * @param {integer} length the data length | |
385 | - * @param (integer} resultOffset the offset to write the new contract address to | |
386 | - * @return {integer} Return 1 or 0 depending on if the VM trapped on the message or not | |
387 | - */ | |
388 | - create (valueOffset, dataOffset, length, resultOffset, cbIndex) { | |
389 | - this.takeGas(32000) | |
390 | - | |
391 | - const value = U256.fromBuffer(this.getMemory(valueOffset, U128_SIZE_BYTES)) | |
392 | - // if (length) { | |
393 | - // const code = this.getMemory(dataOffset, length).slice(0) | |
394 | - // } | |
395 | - | |
396 | - let opPromise | |
397 | - | |
398 | - if (value.gt(this.kernel.environment.value)) { | |
399 | - opPromise = Promise.resolve(Buffer.alloc(20).fill(0)) | |
400 | - } else { | |
401 | - // todo actully run the code | |
402 | - opPromise = Promise.resolve(ethUtil.generateAddress(this.kernel.environment.address, this.kernel.environment.nonce)) | |
403 | - } | |
404 | - | |
405 | - // wait for all the prevouse async ops to finish before running the callback | |
406 | - this.vm.pushOpsQueue(opPromise, cbIndex, address => { | |
407 | - this.setMemory(resultOffset, ADDRESS_SIZE_BYTES, address) | |
408 | - }) | |
409 | - } | |
410 | - | |
411 | - /** | |
412 | - * Sends a message with arbiatary data to a given address path | |
413 | - * @param {integer} addressOffset the offset to load the address path from | |
414 | - * @param {integer} valueOffset the offset to load the value from | |
415 | - * @param {integer} dataOffset the offset to load data from | |
416 | - * @param {integer} dataLength the length of data | |
417 | - * @param {integer} resultOffset the offset to store the result data at | |
418 | - * @param {integer} resultLength | |
419 | - * @param {integer} gas | |
420 | - * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not | |
421 | - */ | |
422 | - _call (gasHigh, gasLow, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength, cbIndex) { | |
423 | - this.takeGas(40) | |
424 | - const gas = from64bit(gasHigh, gasLow) | |
425 | - // Load the params from mem | |
426 | - const address = [common.PARENT, common.PARENT, ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)] | |
427 | - const value = new U256(this.getMemory(valueOffset, U128_SIZE_BYTES)) | |
428 | - | |
429 | - // Special case for non-zero value; why does this exist? | |
430 | - if (!value.isZero()) { | |
431 | - this.takeGas(9000 - 2300 + gas) | |
432 | - this.takeGas(-gas) | |
433 | - } | |
434 | - | |
435 | - const message = new Message({ | |
436 | - to: address, | |
437 | - value: value | |
438 | - }) | |
439 | - | |
440 | - const messagePromise = this.kernel.send(message).then(result => { | |
441 | - if (result.exception) { | |
442 | - this.takeGas(25000) | |
443 | - } | |
444 | - }) | |
445 | - | |
446 | - // wait for all the prevouse async ops to finish before running the callback | |
447 | - this.vm.pushOpsQueue(messagePromise, cbIndex, () => { | |
448 | - return 1 | |
449 | - }) | |
450 | - return 1 | |
451 | - } | |
452 | - | |
453 | - /** | |
454 | - * Message-call into this account with an alternative account’s code. | |
455 | - * @param {integer} addressOffset the offset to load the address path from | |
456 | - * @param {integer} valueOffset the offset to load the value from | |
457 | - * @param {integer} dataOffset the offset to load data from | |
458 | - * @param {integer} dataLength the length of data | |
459 | - * @param {integer} resultOffset the offset to store the result data at | |
460 | - * @param {integer} resultLength | |
461 | - * @param {integer} gas | |
462 | - * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not | |
463 | - */ | |
464 | - callCode (gas, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength, cbIndex) { | |
465 | - this.takeGas(40) | |
466 | - // Load the params from mem | |
467 | - const path = ['accounts', ...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES), 'code'] | |
468 | - const value = U256.fromBuffer(this.getMemory(valueOffset, U128_SIZE_BYTES)) | |
469 | - | |
470 | - // Special case for non-zero value; why does this exist? | |
471 | - if (!value.isZero()) { | |
472 | - this.takeGas(6700) | |
473 | - } | |
474 | - | |
475 | - // TODO: should be message? | |
476 | - const opPromise = this.state.root.get(path) | |
477 | - .catch(() => { | |
478 | - // TODO: handle errors | |
479 | - // the value was not found | |
480 | - return null | |
481 | - }) | |
482 | - | |
483 | - this.vm.pushOpsQueue(opPromise, cbIndex, oldValue => { | |
484 | - return 1 | |
485 | - }) | |
486 | - } | |
487 | - | |
488 | - /** | |
489 | - * Message-call into this account with an alternative account’s code, but | |
490 | - * persisting the current values for sender and value. | |
491 | - * @param {integer} gas | |
492 | - * @param {integer} addressOffset the offset to load the address path from | |
493 | - * @param {integer} valueOffset the offset to load the value from | |
494 | - * @param {integer} dataOffset the offset to load data from | |
495 | - * @param {integer} dataLength the length of data | |
496 | - * @param {integer} resultOffset the offset to store the result data at | |
497 | - * @param {integer} resultLength | |
498 | - * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not | |
499 | - */ | |
500 | - callDelegate (gas, addressOffset, dataOffset, dataLength, resultOffset, resultLength) { | |
501 | - // FIXME: count properly | |
502 | - this.takeGas(40) | |
503 | - | |
504 | - const data = this.getMemory(dataOffset, dataLength).slice(0) | |
505 | - const address = [...this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)] | |
506 | - const [errorCode, result] = this.environment.callDelegate(gas, address, data) | |
507 | - this.setMemory(resultOffset, resultLength, result) | |
508 | - return errorCode | |
509 | - } | |
510 | - | |
511 | - /** | |
512 | - * store a value at a given path in long term storage which are both loaded | |
513 | - * from Memory | |
514 | - * @param {interger} pathOffest the memory offset to load the the path from | |
515 | - * @param {interger} valueOffset the memory offset to load the value from | |
516 | - */ | |
517 | - storageStore (pathOffset, valueOffset, cbIndex) { | |
518 | - this.takeGas(5000) | |
519 | - const key = Buffer.from(this.getMemory(pathOffset, U256_SIZE_BYTES)).toString('hex') | |
520 | - // copy the value | |
521 | - const value = this.getMemory(valueOffset, U256_SIZE_BYTES).slice(0) | |
522 | - const valIsZero = value.every((i) => i === 0) | |
523 | - const opPromise = this.kernel.state.get(key) | |
524 | - .then(vertex => vertex.value) | |
525 | - .catch(() => null) | |
526 | - .then() | |
527 | - | |
528 | - this.vm.pushOpsQueue(opPromise, cbIndex, oldValue => { | |
529 | - if (valIsZero && oldValue) { | |
530 | - // delete a value | |
531 | - this.results.gasRefund += 15000 | |
532 | - this.kernel.state.del(key) | |
533 | - } else { | |
534 | - if (!valIsZero && !oldValue) { | |
535 | - // creating a new value | |
536 | - this.takeGas(15000) | |
537 | - } | |
538 | - // update | |
539 | - this.kernel.state.set(key, new Vertex({ | |
540 | - value: value | |
541 | - })) | |
542 | - } | |
543 | - }) | |
544 | - } | |
545 | - | |
546 | - /** | |
547 | - * reterives a value at a given path in long term storage | |
548 | - * @param {interger} pathOffest the memory offset to load the the path from | |
549 | - * @param {interger} resultOffset the memory offset to load the value from | |
550 | - */ | |
551 | - storageLoad (pathOffset, resultOffset, cbIndex) { | |
552 | - this.takeGas(50) | |
553 | - | |
554 | - // convert the path to an array | |
555 | - const key = Buffer.from(this.getMemory(pathOffset, U256_SIZE_BYTES)).toString('hex') | |
556 | - // get the value from the state | |
557 | - const opPromise = this.kernel.state.get([key]) | |
558 | - .then(vertex => vertex.value) | |
559 | - .catch(() => new Uint8Array(32)) | |
560 | - | |
561 | - this.vm.pushOpsQueue(opPromise, cbIndex, value => { | |
562 | - this.setMemory(resultOffset, U256_SIZE_BYTES, value) | |
563 | - }) | |
564 | - } | |
565 | - | |
566 | - /** | |
567 | - * Halt execution returning output data. | |
568 | - * @param {integer} offset the offset of the output data. | |
569 | - * @param {integer} length the length of the output data. | |
570 | - */ | |
571 | - return (offset, length) { | |
572 | - if (length) { | |
573 | - this.results.returnValue = this.getMemory(offset, length).slice(0) | |
574 | - } | |
575 | - } | |
576 | - | |
577 | - /** | |
578 | - * Halt execution and register account for later deletion giving the remaining | |
579 | - * balance to an address path | |
580 | - * @param {integer} offset the offset to load the address from | |
581 | - */ | |
582 | - selfDestruct (addressOffset) { | |
583 | - this.results.selfDestruct = true | |
584 | - this.results.selfDestructAddress = this.getMemory(addressOffset, ADDRESS_SIZE_BYTES) | |
585 | - this.results.gasRefund += 24000 | |
586 | - } | |
587 | - | |
588 | - getMemory (offset, length) { | |
589 | - return new Uint8Array(this.vm.memory(), offset, length) | |
590 | - } | |
591 | - | |
592 | - setMemory (offset, length, value) { | |
593 | - const memory = new Uint8Array(this.vm.memory(), offset, length) | |
594 | - memory.set(value) | |
595 | - } | |
596 | - | |
597 | - /* | |
598 | - * Takes gas from the tank. Only needs to check if there's gas left to be taken, | |
599 | - * because every caller of this method is trusted. | |
600 | - */ | |
601 | - takeGas (amount) { | |
602 | - if (this.message.gas < amount) { | |
603 | - throw new Error('Ran out of gas') | |
604 | - } | |
605 | - this.message.gas -= amount | |
606 | - } | |
607 | -} | |
608 | - | |
609 | -// converts a 64 bit number to a JS number | |
610 | -function from64bit (high, low) { | |
611 | - if (high < 0) { | |
612 | - // convert from a 32-bit two's compliment | |
613 | - high = 0x100000000 - high | |
614 | - } | |
615 | - if (low < 0) { | |
616 | - // convert from a 32-bit two's compliment | |
617 | - low = 0x100000000 - low | |
618 | - } | |
619 | - // JS only bitshift 32bits, so instead of high << 32 we have high * 2 ^ 32 | |
620 | - return (high * 4294967296) + low | |
621 | -} |
debugInterface.js | ||
---|---|---|
@@ -1,47 +1,0 @@ | ||
1 | -const opcodes = require('./opcodes.js') | |
2 | - | |
3 | -/** | |
4 | - * Debug Interface | |
5 | - * This expose some functions that can help with debugging wast | |
6 | - */ | |
7 | -module.exports = class DebugInterface { | |
8 | - constructor (kernel) { | |
9 | - this.kernel = kernel | |
10 | - } | |
11 | - | |
12 | - static get name () { | |
13 | - return 'debug' | |
14 | - } | |
15 | - | |
16 | - get exports () { | |
17 | - return { | |
18 | - 'print': function (a) { | |
19 | - console.log(a) | |
20 | - }, | |
21 | - 'printMem': function (offset, length) { | |
22 | - console.log(`<DEBUG(str): ${this.getMemoryBuffer(offset, length).toString()}>`) | |
23 | - }.bind(this), | |
24 | - | |
25 | - 'printMemHex': function (offset, length) { | |
26 | - console.log(`<DEBUG(hex): ${this.getMemoryBuffer(offset, length).toString('hex')}>`) | |
27 | - }.bind(this), | |
28 | - | |
29 | - 'evmStackTrace': function (sp, op) { | |
30 | - const opcode = opcodes(op) | |
31 | - if (opcode.number) { | |
32 | - opcode.name += opcode.number | |
33 | - } | |
34 | - console.error(`op: ${opcode.name} gas: ${this.kernel.environment.gasLeft} sp: ${sp}`) | |
35 | - console.log('-------------stack--------------') | |
36 | - for (let i = sp; i >= 0; i -= 32) { | |
37 | - console.log(`${(sp - i) / 32} ${this.getMemoryBuffer(i).toString('hex')}`) | |
38 | - } | |
39 | - }.bind(this) | |
40 | - } | |
41 | - } | |
42 | - | |
43 | - getMemoryBuffer (offset, length = 32) { | |
44 | - const mem = this.kernel.memory.slice(offset, offset + length) | |
45 | - return Buffer.from(mem).reverse() | |
46 | - } | |
47 | -} |
deps/block.js | ||
---|---|---|
@@ -1,26 +1,0 @@ | ||
1 | -const Address = require('fixed-bn.js').Address | |
2 | -const U256 = require('fixed-bn.js').U256 | |
3 | -const ethUtil = require('ethereumjs-util') | |
4 | -const OldBlock = require('ethereumjs-block') | |
5 | - | |
6 | -module.exports = class Block extends OldBlock { | |
7 | - get number () { | |
8 | - return ethUtil.bufferToInt(this.header.number) | |
9 | - } | |
10 | - | |
11 | - get gasLimit () { | |
12 | - return ethUtil.bufferToInt(this.header.gasLimit) | |
13 | - } | |
14 | - | |
15 | - get difficulty () { | |
16 | - return new U256(this.header.difficulty) | |
17 | - } | |
18 | - | |
19 | - get timestamp () { | |
20 | - return ethUtil.bufferToInt(this.header.timestamp) | |
21 | - } | |
22 | - | |
23 | - get coinbase () { | |
24 | - return new Address(this.header.coinbase) | |
25 | - } | |
26 | -} |
deps/rootVertex.js | ||
---|---|---|
@@ -1,50 +1,0 @@ | ||
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 | -// } |
deps/transaction.js | ||
---|---|---|
@@ -1,49 +1,0 @@ | ||
1 | -// | |
2 | -// This class parses a serialised Ethereum transaction | |
3 | -// | |
4 | -// The input is a Buffer. | |
5 | -// | |
6 | -const Address = require('./address.js') | |
7 | -const U256 = require('./u256.js') | |
8 | -const OldTx = require('ethereumjs-tx') | |
9 | - | |
10 | -module.exports = class Transaction { | |
11 | - constructor (tx) { | |
12 | - this._tx = new OldTx(tx) | |
13 | - } | |
14 | - | |
15 | - get valid () { | |
16 | - return this._tx.verifySignature() | |
17 | - } | |
18 | - | |
19 | - get nonce () { | |
20 | - return new U256(this._tx.nonce) | |
21 | - } | |
22 | - | |
23 | - get gasPrice () { | |
24 | - return new U256(this._tx.gasPrice) | |
25 | - } | |
26 | - | |
27 | - get gasLimit () { | |
28 | - return new U256(this._tx.gasLimit) | |
29 | - } | |
30 | - | |
31 | - get value () { | |
32 | - return new U256(this._tx.value) | |
33 | - } | |
34 | - | |
35 | - get data () { | |
36 | - return Uint8Array.from(this._tx.data) | |
37 | - } | |
38 | - | |
39 | - get from () { | |
40 | - return new Address(this._tx.getSenderAddress()) | |
41 | - } | |
42 | - | |
43 | - get to () { | |
44 | - if (this._tx.to.length === 0) { | |
45 | - return new Address('0x0000000000000000000000000000000000000000') | |
46 | - } | |
47 | - return new Address(this._tx.to) | |
48 | - } | |
49 | -} |
deps/utils.js | ||
---|---|---|
@@ -1,14 +1,0 @@ | ||
1 | -const ethUtil = require('ethereumjs-util') | |
2 | -const Address = require('./address.js') | |
3 | - | |
4 | -var Utils = {} | |
5 | - | |
6 | -Utils.isWASMCode = function (code) { | |
7 | - return code.slice(0, 4).toString() === new Uint8Array([0, 0x61, 0x73, 0x6d]).toString() | |
8 | -} | |
9 | - | |
10 | -Utils.newAccountAddress = function (sender, nonce) { | |
11 | - return new Address('0x' + ethUtil.generateAddress(sender.toString(), nonce.toString()).toString('hex')) | |
12 | -} | |
13 | - | |
14 | -module.exports = Utils |
kernel.js | ||
---|---|---|
@@ -1,0 +1,136 @@ | ||
1 | +const crypto = require('webcrypto-liner') | |
2 | +const PriorityQueue = require('fastpriorityqueue') | |
3 | +const EventEmitter = require('events') | |
4 | +const PortManager = require('./portManager.js') | |
5 | + | |
6 | +const STATES = ['idle', 'running', 'result'] | |
7 | + | |
8 | +module.exports = class Kernel extends EventEmitter { | |
9 | + constructor (opts = {}) { | |
10 | + super() | |
11 | + // set up the state | |
12 | + this.opts = {} | |
13 | + this._stateIndex = 0 | |
14 | + Object.assign(this.opts, opts) | |
15 | + // set up the vm | |
16 | + this.vm = (this.opts.codeHandler).init(this.opts.state) | |
17 | + this.ports = new PortManager(this) | |
18 | + this._waitingQueue = new PriorityQueue((a, b) => { | |
19 | + return a.threshold > b.threshold | |
20 | + }) | |
21 | + | |
22 | + this.on('result', this._runNextMessage) | |
23 | + } | |
24 | + | |
25 | + _updateState (message) { | |
26 | + this._stateIndex++ | |
27 | + const state = STATES[this._stateIndex] | |
28 | + this._emit(state, message) | |
29 | + } | |
30 | + | |
31 | + get state () { | |
32 | + return STATES[this._stateIndex] | |
33 | + } | |
34 | + | |
35 | + queue (message) { | |
36 | + this.portManager.queue(message) | |
37 | + if (this.state === 'idle') { | |
38 | + this._runNextMessage() | |
39 | + } | |
40 | + } | |
41 | + | |
42 | + _runNextMessage () { | |
43 | + this.portManager.getNextMessage(this.ticks).then(message => { | |
44 | + if (message) { | |
45 | + this.run(message) | |
46 | + } else { | |
47 | + this._updateState() | |
48 | + } | |
49 | + }) | |
50 | + } | |
51 | + | |
52 | + /** | |
53 | + * run the kernels code with a given enviroment | |
54 | + * The Kernel Stores all of its state in the Environment. The Interface is used | |
55 | + * to by the VM to retrive infromation from the Environment. | |
56 | + */ | |
57 | + async run (message, imports = this.imports) { | |
58 | + // shallow copy | |
59 | + const oldState = Object.assign({}, this.state) | |
60 | + let result | |
61 | + this._updateState(message) | |
62 | + try { | |
63 | + result = await this._vm.run(message, this, imports) || {} | |
64 | + } catch (e) { | |
65 | + result = { | |
66 | + exception: true, | |
67 | + exceptionError: e | |
68 | + } | |
69 | + } | |
70 | + | |
71 | + if (result.exception) { | |
72 | + // revert to the old state | |
73 | + clearObject(this.opts.state) | |
74 | + Object.assign(this.opts.state, oldState) | |
75 | + } | |
76 | + | |
77 | + this._updateState(result) | |
78 | + return result | |
79 | + } | |
80 | + | |
81 | + // returns a promise that resolves once the kernel hits the threshould tick | |
82 | + // count | |
83 | + async wait (threshold) { | |
84 | + if (this._state === 'idle' && threshold > this.ticks) { | |
85 | + // the cotract is at idle so wait | |
86 | + return this.portManager.wait(threshold) | |
87 | + } else { | |
88 | + return new Promise((resolve, reject) => { | |
89 | + if (threshold <= this.ticks) { | |
90 | + resolve(this.ticks) | |
91 | + } else { | |
92 | + this._waitingQueue.add({ | |
93 | + threshold: threshold, | |
94 | + resolve: resolve | |
95 | + }) | |
96 | + } | |
97 | + }) | |
98 | + } | |
99 | + } | |
100 | + | |
101 | + _updateTickCount (count) { | |
102 | + this.ticks = count | |
103 | + while (this._waitingQueue.peek().threshold <= count) { | |
104 | + this._waitingQueue.poll().resolve(count) | |
105 | + } | |
106 | + } | |
107 | + | |
108 | + createPort () { | |
109 | + return { | |
110 | + id: { | |
111 | + '/': [this.nonce++, this.id] | |
112 | + }, | |
113 | + link: { | |
114 | + '/': {} | |
115 | + } | |
116 | + } | |
117 | + } | |
118 | + | |
119 | + async send (port, message) { | |
120 | + return this.opts.hypervisor.send(port, message) | |
121 | + } | |
122 | + | |
123 | + id () { | |
124 | + return Kernel.id(this._opts, this._opts) | |
125 | + } | |
126 | + | |
127 | + static id (id) { | |
128 | + return crypto.subtle.digest('SHA-256', Buffer.concat(id.parentId, id.nonce)) | |
129 | + } | |
130 | +} | |
131 | + | |
132 | +function clearObject (myObject) { | |
133 | + for (var member in myObject) { | |
134 | + delete myObject[member] | |
135 | + } | |
136 | +} |
fakeBlockChain.js | ||
---|---|---|
@@ -1,14 +1,0 @@ | ||
1 | -const utils = require('ethereumjs-util') | |
2 | -const U256 = require('fixed-bn.js').U256 | |
3 | - | |
4 | -module.exports = { | |
5 | - getBlock: (n) => { | |
6 | - const hash = utils.sha3(Buffer.from(utils.bufferToInt(n).toString())) | |
7 | - const block = { | |
8 | - hash: () => { | |
9 | - return new U256(hash) | |
10 | - } | |
11 | - } | |
12 | - return block | |
13 | - } | |
14 | -} |
hypervisor.js | ||
---|---|---|
@@ -1,26 +1,0 @@ | ||
1 | -const Kernel = require('./index.js') | |
2 | -const codeHandlers = require('./codeHandler.js') | |
3 | - | |
4 | -module.exports = class Hypervisor { | |
5 | - constructor (graph, state, imports = []) { | |
6 | - this.state = state | |
7 | - this.graph = graph | |
8 | - this.root = new Kernel({ | |
9 | - imports: imports, | |
10 | - graph: graph, | |
11 | - state: state | |
12 | - }) | |
13 | - } | |
14 | - | |
15 | - set (path, value) { | |
16 | - return this.graph.set(this.state, path, value) | |
17 | - } | |
18 | - | |
19 | - send (portName, message) { | |
20 | - return this.root.send(portName, message) | |
21 | - } | |
22 | - | |
23 | - addVM (type, handler) { | |
24 | - codeHandlers.handlers.type = handler | |
25 | - } | |
26 | -} |
opcodes.js | ||
---|---|---|
@@ -1,187 +1,0 @@ | ||
1 | -const codes = { | |
2 | - // 0x0 range - arithmetic ops | |
3 | - // name, baseCost, off stack, on stack, dynamic | |
4 | - 0x00: ['STOP', 0, 0, 0, false], | |
5 | - 0x01: ['ADD', 3, 2, 1, false], | |
6 | - 0x02: ['MUL', 5, 2, 1, false], | |
7 | - 0x03: ['SUB', 3, 2, 1, false], | |
8 | - 0x04: ['DIV', 5, 2, 1, false], | |
9 | - 0x05: ['SDIV', 5, 2, 1, false], | |
10 | - 0x06: ['MOD', 5, 2, 1, false], | |
11 | - 0x07: ['SMOD', 5, 2, 1, false], | |
12 | - 0x08: ['ADDMOD', 8, 3, 1, false], | |
13 | - 0x09: ['MULMOD', 8, 3, 1, false], | |
14 | - 0x0a: ['EXP', 10, 2, 1, false], | |
15 | - 0x0b: ['SIGNEXTEND', 5, 1, 1, false], | |
16 | - | |
17 | - // 0x10 range - bit ops | |
18 | - 0x10: ['LT', 3, 2, 1, false], | |
19 | - 0x11: ['GT', 3, 2, 1, false], | |
20 | - 0x12: ['SLT', 3, 2, 1, false], | |
21 | - 0x13: ['SGT', 3, 2, 1, false], | |
22 | - 0x14: ['EQ', 3, 2, 1, false], | |
23 | - 0x15: ['ISZERO', 3, 1, 1, false], | |
24 | - 0x16: ['AND', 3, 2, 1, false], | |
25 | - 0x17: ['OR', 3, 2, 1, false], | |
26 | - 0x18: ['XOR', 3, 2, 1, false], | |
27 | - 0x19: ['NOT', 3, 1, 1, false], | |
28 | - 0x1a: ['BYTE', 3, 2, 1, false], | |
29 | - | |
30 | - // 0x20 range - crypto | |
31 | - 0x20: ['SHA3', 30, 2, 1, false], | |
32 | - | |
33 | - // 0x30 range - closure state | |
34 | - 0x30: ['ADDRESS', 2, 0, 1, true], | |
35 | - 0x31: ['BALANCE', 20, 1, 1, true], | |
36 | - 0x32: ['ORIGIN', 2, 0, 1, true], | |
37 | - 0x33: ['CALLER', 2, 0, 1, true], | |
38 | - 0x34: ['CALLVALUE', 2, 0, 1, true], | |
39 | - 0x35: ['CALLDATALOAD', 3, 1, 1, true], | |
40 | - 0x36: ['CALLDATASIZE', 2, 0, 1, true], | |
41 | - 0x37: ['CALLDATACOPY', 3, 3, 0, true], | |
42 | - 0x38: ['CODESIZE', 2, 0, 1, false], | |
43 | - 0x39: ['CODECOPY', 3, 3, 0, false], | |
44 | - 0x3a: ['GASPRICE', 2, 0, 1, false], | |
45 | - 0x3b: ['EXTCODESIZE', 20, 1, 1, true], | |
46 | - 0x3c: ['EXTCODECOPY', 20, 4, 0, true], | |
47 | - | |
48 | - // '0x40' range - block operations | |
49 | - 0x40: ['BLOCKHASH', 20, 1, 1, true], | |
50 | - 0x41: ['COINBASE', 2, 0, 1, true], | |
51 | - 0x42: ['TIMESTAMP', 2, 0, 1, true], | |
52 | - 0x43: ['NUMBER', 2, 0, 1, true], | |
53 | - 0x44: ['DIFFICULTY', 2, 0, 1, true], | |
54 | - 0x45: ['GASLIMIT', 2, 0, 1, true], | |
55 | - | |
56 | - // 0x50 range - 'storage' and execution | |
57 | - 0x50: ['POP', 2, 1, 0, false], | |
58 | - 0x51: ['MLOAD', 3, 1, 1, false], | |
59 | - 0x52: ['MSTORE', 3, 2, 0, false], | |
60 | - 0x53: ['MSTORE8', 3, 2, 0, false], | |
61 | - 0x54: ['SLOAD', 0, 1, 1, true], | |
62 | - 0x55: ['SSTORE', 0, 2, 0, true], | |
63 | - 0x56: ['JUMP', 8, 1, 0, false], | |
64 | - 0x57: ['JUMPI', 10, 2, 0, false], | |
65 | - 0x58: ['PC', 2, 0, 1, false], | |
66 | - 0x59: ['MSIZE', 2, 0, 1, false], | |
67 | - 0x5a: ['GAS', 2, 0, 1, false], | |
68 | - 0x5b: ['JUMPDEST', 1, 0, 0, false], | |
69 | - | |
70 | - // 0x60, range | |
71 | - 0x60: ['PUSH', 3, 0, 1, false], | |
72 | - 0x61: ['PUSH', 3, 0, 1, false], | |
73 | - 0x62: ['PUSH', 3, 0, 1, false], | |
74 | - 0x63: ['PUSH', 3, 0, 1, false], | |
75 | - 0x64: ['PUSH', 3, 0, 1, false], | |
76 | - 0x65: ['PUSH', 3, 0, 1, false], | |
77 | - 0x66: ['PUSH', 3, 0, 1, false], | |
78 | - 0x67: ['PUSH', 3, 0, 1, false], | |
79 | - 0x68: ['PUSH', 3, 0, 1, false], | |
80 | - 0x69: ['PUSH', 3, 0, 1, false], | |
81 | - 0x6a: ['PUSH', 3, 0, 1, false], | |
82 | - 0x6b: ['PUSH', 3, 0, 1, false], | |
83 | - 0x6c: ['PUSH', 3, 0, 1, false], | |
84 | - 0x6d: ['PUSH', 3, 0, 1, false], | |
85 | - 0x6e: ['PUSH', 3, 0, 1, false], | |
86 | - 0x6f: ['PUSH', 3, 0, 1, false], | |
87 | - 0x70: ['PUSH', 3, 0, 1, false], | |
88 | - 0x71: ['PUSH', 3, 0, 1, false], | |
89 | - 0x72: ['PUSH', 3, 0, 1, false], | |
90 | - 0x73: ['PUSH', 3, 0, 1, false], | |
91 | - 0x74: ['PUSH', 3, 0, 1, false], | |
92 | - 0x75: ['PUSH', 3, 0, 1, false], | |
93 | - 0x76: ['PUSH', 3, 0, 1, false], | |
94 | - 0x77: ['PUSH', 3, 0, 1, false], | |
95 | - 0x78: ['PUSH', 3, 0, 1, false], | |
96 | - 0x79: ['PUSH', 3, 0, 1, false], | |
97 | - 0x7a: ['PUSH', 3, 0, 1, false], | |
98 | - 0x7b: ['PUSH', 3, 0, 1, false], | |
99 | - 0x7c: ['PUSH', 3, 0, 1, false], | |
100 | - 0x7d: ['PUSH', 3, 0, 1, false], | |
101 | - 0x7e: ['PUSH', 3, 0, 1, false], | |
102 | - 0x7f: ['PUSH', 3, 0, 1, false], | |
103 | - | |
104 | - 0x80: ['DUP', 3, 0, 1, false], | |
105 | - 0x81: ['DUP', 3, 0, 1, false], | |
106 | - 0x82: ['DUP', 3, 0, 1, false], | |
107 | - 0x83: ['DUP', 3, 0, 1, false], | |
108 | - 0x84: ['DUP', 3, 0, 1, false], | |
109 | - 0x85: ['DUP', 3, 0, 1, false], | |
110 | - 0x86: ['DUP', 3, 0, 1, false], | |
111 | - 0x87: ['DUP', 3, 0, 1, false], | |
112 | - 0x88: ['DUP', 3, 0, 1, false], | |
113 | - 0x89: ['DUP', 3, 0, 1, false], | |
114 | - 0x8a: ['DUP', 3, 0, 1, false], | |
115 | - 0x8b: ['DUP', 3, 0, 1, false], | |
116 | - 0x8c: ['DUP', 3, 0, 1, false], | |
117 | - 0x8d: ['DUP', 3, 0, 1, false], | |
118 | - 0x8e: ['DUP', 3, 0, 1, false], | |
119 | - 0x8f: ['DUP', 3, 0, 1, false], | |
120 | - | |
121 | - 0x90: ['SWAP', 3, 0, 0, false], | |
122 | - 0x91: ['SWAP', 3, 0, 0, false], | |
123 | - 0x92: ['SWAP', 3, 0, 0, false], | |
124 | - 0x93: ['SWAP', 3, 0, 0, false], | |
125 | - 0x94: ['SWAP', 3, 0, 0, false], | |
126 | - 0x95: ['SWAP', 3, 0, 0, false], | |
127 | - 0x96: ['SWAP', 3, 0, 0, false], | |
128 | - 0x97: ['SWAP', 3, 0, 0, false], | |
129 | - 0x98: ['SWAP', 3, 0, 0, false], | |
130 | - 0x99: ['SWAP', 3, 0, 0, false], | |
131 | - 0x9a: ['SWAP', 3, 0, 0, false], | |
132 | - 0x9b: ['SWAP', 3, 0, 0, false], | |
133 | - 0x9c: ['SWAP', 3, 0, 0, false], | |
134 | - 0x9d: ['SWAP', 3, 0, 0, false], | |
135 | - 0x9e: ['SWAP', 3, 0, 0, false], | |
136 | - 0x9f: ['SWAP', 3, 0, 0, false], | |
137 | - | |
138 | - 0xa0: ['LOG', 375, 2, 0, false], | |
139 | - 0xa1: ['LOG', 375, 3, 0, false], | |
140 | - 0xa2: ['LOG', 375, 4, 0, false], | |
141 | - 0xa3: ['LOG', 375, 5, 0, false], | |
142 | - 0xa4: ['LOG', 375, 6, 0, false], | |
143 | - | |
144 | - // '0xf0' range - closures | |
145 | - 0xf0: ['CREATE', 32000, 3, 1, true], | |
146 | - 0xf1: ['CALL', 40, 7, 1, true], | |
147 | - 0xf2: ['CALLCODE', 40, 7, 1, true], | |
148 | - 0xf3: ['RETURN', 0, 2, 0, false], | |
149 | - 0xf4: ['DELEGATECALL', 40, 6, 1, true], | |
150 | - | |
151 | - // '0x70', range - other | |
152 | - 0xff: ['SUICIDE', 0, 1, 0, false] | |
153 | -} | |
154 | - | |
155 | -module.exports = function (op) { | |
156 | - const code = codes[op] ? codes[op] : ['INVALID', 0] | |
157 | - let opcode = code[0] | |
158 | - let number | |
159 | - | |
160 | - switch (opcode) { | |
161 | - case 'LOG': | |
162 | - number = op - 0xa0 | |
163 | - break | |
164 | - | |
165 | - case 'PUSH': | |
166 | - number = op - 0x5f | |
167 | - break | |
168 | - | |
169 | - case 'DUP': | |
170 | - number = op - 0x7f | |
171 | - break | |
172 | - | |
173 | - case 'SWAP': | |
174 | - number = op - 0x8f | |
175 | - break | |
176 | - } | |
177 | - | |
178 | - return { | |
179 | - name: opcode, | |
180 | - fee: code[1], | |
181 | - in: code[2], | |
182 | - out: code[3], | |
183 | - dynamic: code[4], | |
184 | - async: code[5], | |
185 | - number: number | |
186 | - } | |
187 | -} |
runBlock.js | ||
---|---|---|
@@ -1,11 +1,0 @@ | ||
1 | -// run block; the block message handler:w | |
2 | -const Environment = require('./environment') | |
3 | - | |
4 | -module.exports = class runBlock { | |
5 | - constuctor (block, environment = new Environment()) { | |
6 | - // verify block then run each tx | |
7 | - block.tx.forEach((tx) => { | |
8 | - this.runTx(tx, environment) | |
9 | - }) | |
10 | - } | |
11 | -} |
tests/apiTests.js | ||
---|---|---|
@@ -1,68 +1,0 @@ | ||
1 | -const tape = require('tape') | |
2 | -const Hypervisor = require('../hypervisor.js') | |
3 | -const Message = require('primea-message/atomic') | |
4 | -const Graph = require('ipld-graph-builder') | |
5 | -const IPFS = require('ipfs') | |
6 | - | |
7 | -const ipfs = new IPFS() | |
8 | -const graph = new Graph(ipfs) | |
9 | - | |
10 | -ipfs.on('start', async () => { | |
11 | - tape('send and reciving messages', async t => { | |
12 | - const root = {} | |
13 | - try { | |
14 | - const hypervisor = new Hypervisor(graph, root) | |
15 | - const path = 'two/three' | |
16 | - await hypervisor.set(path, { | |
17 | - code: message => { | |
18 | - t.pass('got message') | |
19 | - t.end() | |
20 | - return {} | |
21 | - } | |
22 | - }) | |
23 | - hypervisor.send('one', new Message({ | |
24 | - to: path | |
25 | - })) | |
26 | - } catch (e) { | |
27 | - console.log(e) | |
28 | - } | |
29 | - }) | |
30 | - | |
31 | - tape('reverts', async t => { | |
32 | - try { | |
33 | - const root = {} | |
34 | - const hypervisor = new Hypervisor(graph, root) | |
35 | - const path = 'one/two/three' | |
36 | - const path2 = 'one/two/three/four' | |
37 | - await hypervisor.set(path, { | |
38 | - code: async (message, kernel) => { | |
39 | - console.log('here!!') | |
40 | - await kernel.send('four', new Message()) | |
41 | - throw new Error('vm exception') | |
42 | - } | |
43 | - }) | |
44 | - | |
45 | - await hypervisor.set(path2, { | |
46 | - code: (message, kernel) => { | |
47 | - kernel.graph.set(kernel.state, 'something', { | |
48 | - somevalue: 'value' | |
49 | - }) | |
50 | - return 'done!' | |
51 | - } | |
52 | - }) | |
53 | - | |
54 | - const message = new Message({ | |
55 | - to: path.split('/').slice(1) | |
56 | - }) | |
57 | - hypervisor.send(path.split('/')[0], message) | |
58 | - const result = await message.result() | |
59 | - t.equals(result.exception, true) | |
60 | - const expectedRoot = '{"one":{"two":{"three":{"/":{"four":{"/":{}}}}}}}' | |
61 | - t.equals(JSON.stringify(root), expectedRoot, 'should produce correct root') | |
62 | - } catch (e) { | |
63 | - console.log(e) | |
64 | - } | |
65 | - t.end() | |
66 | - process.exit() | |
67 | - }) | |
68 | -}) |
tests/buildTests.js | ||
---|---|---|
@@ -1,15 +1,0 @@ | ||
1 | -const fs = require('fs') | |
2 | -const cp = require('child_process') | |
3 | -const path = require('path') | |
4 | - | |
5 | -const dir = path.join(__dirname, '/interface') | |
6 | -// get the test names | |
7 | -let tests = fs.readdirSync(dir).filter((file) => file.endsWith('.wast')) | |
8 | -// tests = ['balance.wast'] | |
9 | -// run the tests | |
10 | -for (let testName of tests) { | |
11 | - console.log(testName) | |
12 | - testName = testName.split('.')[0] | |
13 | - // Compile Command | |
14 | - cp.execSync(`${__dirname}/../tools/wabt/out/wast2wasm ${dir}/${testName}.wast -o ${dir}/${testName}.wasm`) | |
15 | -} |
tests/interface/address.json | ||
---|---|---|
@@ -1,14 +1,0 @@ | ||
1 | -{ | |
2 | - "callValue": "0x00", | |
3 | - "callData": "0x00", | |
4 | - "state": { | |
5 | - "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": { | |
6 | - "balance": "0x056bc75e2d63100000", | |
7 | - "code": "0x00", | |
8 | - "nonce": "0x00" | |
9 | - } | |
10 | - }, | |
11 | - "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
12 | - "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
13 | - "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b" | |
14 | -} |
tests/interface/address.wasm | ||
---|---|---|
@@ -1,3 +1,0 @@ | ||
1 | - asm ` ` ethereum | |
2 | -getAddress main memory | |
3 | -! @A A ) Bݐ������� Q@ |
tests/interface/address.wast | ||
---|---|---|
@@ -1,18 +1,0 @@ | ||
1 | -;; starts with an address of 5d48c1018904a172886829bbbd9c6f4a2d06c47b | |
2 | -(module | |
3 | - (import "ethereum" "getAddress" (func $address (param i32))) | |
4 | - | |
5 | - (memory 1) | |
6 | - (export "main" (func 0)) | |
7 | - (export "memory" (memory 0)) | |
8 | - (func | |
9 | - (block | |
10 | - ;; loads the address into memory | |
11 | - (call $address (i32.const 0)) | |
12 | - (if (i64.eq (i64.load (i32.const 0)) (i64.const 0x72a1048901c1485d)) | |
13 | - (return) | |
14 | - ) | |
15 | - (unreachable) | |
16 | - ) | |
17 | - ) | |
18 | -) |
tests/interface/balance.json | ||
---|---|---|
@@ -1,14 +1,0 @@ | ||
1 | -{ | |
2 | - "callValue": "0x00", | |
3 | - "callData": "0x00", | |
4 | - "state": { | |
5 | - "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": { | |
6 | - "balance": "0x056bc75e2d63100000", | |
7 | - "code": "0x00", | |
8 | - "nonce": "0x00" | |
9 | - } | |
10 | - }, | |
11 | - "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
12 | - "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
13 | - "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b" | |
14 | -} |
tests/interface/balance.wasm | ||
---|---|---|
@@ -1,5 +1,0 @@ | ||
1 | - asm | |
2 | -` ` ethereum | |
3 | -getBalance p memory main callback A | |
4 | -' | |
5 | - A A A @A ) B��������Q@ A ]H���r�h)���oJ-�{ |
tests/interface/balance.wast | ||
---|---|---|
@@ -1,30 +1,0 @@ | ||
1 | -;; address of 5d48c1018904a172886829bbbd9c6f4a2d06c47b has a balance of 0x056bc75e2d63100000 (100 ETH) | |
2 | -(module | |
3 | - (import "ethereum" "getBalance" (func $balance (param i32 i32 i32))) | |
4 | - (memory 1 ) | |
5 | - (data (i32.const 0) "\5d\48\c1\01\89\04\a1\72\88\68\29\bb\bd\9c\6f\4a\2d\06\c4\7b") | |
6 | - (export "memory" (memory 0)) | |
7 | - (export "main" (func $main)) | |
8 | - | |
9 | - (table | |
10 | - (export "callback") | |
11 | - anyfunc | |
12 | - (elem | |
13 | - $callback | |
14 | - ) | |
15 | - ) | |
16 | - | |
17 | - (func $main | |
18 | - (call $balance (i32.const 0) (i32.const 0) (i32.const 0)) | |
19 | - ) | |
20 | - | |
21 | - (func $callback | |
22 | - (block | |
23 | - (if (i64.eq (i64.load (i32.const 0)) (i64.const 0x0500000000000000)) | |
24 | - (return) | |
25 | - ) | |
26 | - (unreachable) | |
27 | - ) | |
28 | - ) | |
29 | -) | |
30 | - |
tests/interface/basic_gas_ops.json | ||
---|---|---|
@@ -1,15 +1,0 @@ | ||
1 | -{ | |
2 | - "callValue": "0x00", | |
3 | - "callData": "0x00", | |
4 | - "state": { | |
5 | - "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": { | |
6 | - "balance": "0x056bc75e2d63100000", | |
7 | - "code": "0x00", | |
8 | - "nonce": "0x00" | |
9 | - } | |
10 | - }, | |
11 | - "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
12 | - "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
13 | - "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
14 | - "gasLeft": 1000 | |
15 | -} |
tests/interface/basic_gas_ops.wasm | ||
---|---|---|
@@ -1,3 +1,0 @@ | ||
1 | - asm `~ ` ~` )ethereumuseGas ethereum | |
2 | -getGasLeft main | |
3 | - @B B�Q@ |
tests/interface/basic_gas_ops.wast | ||
---|---|---|
@@ -1,24 +1,0 @@ | ||
1 | -;; starts with 1000 gas | |
2 | -(module | |
3 | - (import "ethereum" "useGas" (func $useGas (param i64))) | |
4 | - (import "ethereum" "getGasLeft" (func $gas (result i64))) | |
5 | - | |
6 | - (export "main" (func $main)) | |
7 | - (func $main | |
8 | - ;; test adding gas | |
9 | - (block | |
10 | - (call $useGas (i64.const 1)) | |
11 | - (if (i64.eq (call $gas) (i64.const 997)) | |
12 | - (return) | |
13 | - ) | |
14 | - (unreachable) | |
15 | - ) | |
16 | - (block | |
17 | - (call $useGas (i64.const 1)) | |
18 | - (if (i64.eq (call $gas) (i64.const 996)) | |
19 | - (return) | |
20 | - ) | |
21 | - (unreachable) | |
22 | - ) | |
23 | - ) | |
24 | -) |
tests/interface/call.json | ||
---|---|---|
@@ -1,14 +1,0 @@ | ||
1 | -{ | |
2 | - "callValue": "0x00", | |
3 | - "callData": "0x00", | |
4 | - "state": { | |
5 | - "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": { | |
6 | - "balance": "0x056bc75e2d63100000", | |
7 | - "code": "0x00", | |
8 | - "nonce": "0x00" | |
9 | - } | |
10 | - }, | |
11 | - "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
12 | - "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
13 | - "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b" | |
14 | -} |
tests/interface/call.wasm | ||
---|---|---|
@@ -1,2 +1,0 @@ | ||
1 | - asm `~` ` ethereumcall p memory main callback A | |
2 | -:+ @A A6 A4A����6 B�A AA4AA8AA A F@ |
tests/interface/call.wast | ||
---|---|---|
@@ -1,36 +1,0 @@ | ||
1 | -;; starts with an address of 5d48c1018904a172886829bbbd9c6f4a2d06c47b | |
2 | -(module | |
3 | - (import "ethereum" "call" (func $call (param i64 i32 i32 i32 i32 i32 i32 i32) (result i32))) | |
4 | - (memory 1) | |
5 | - (export "memory" (memory 0)) | |
6 | - (export "main" (func $main)) | |
7 | - | |
8 | - (table | |
9 | - (export "callback") | |
10 | - anyfunc | |
11 | - (elem | |
12 | - $callback | |
13 | - ) | |
14 | - ) | |
15 | - | |
16 | - (func $main | |
17 | - (block | |
18 | - ;; Memory layout: | |
19 | - ;; 0 - 20 bytes: address (4) | |
20 | - ;; 20 - 52 bytes: value (0) | |
21 | - ;; 52 - 56 bytes: data (0x42004200) | |
22 | - ;; 56 - 60 bytes: result | |
23 | - (i32.store (i32.const 0) (i32.const 0x4)) | |
24 | - (i32.store (i32.const 52) (i32.const 0x42004200)) | |
25 | - (call $call (i64.const 2000) (i32.const 0) (i32.const 20) (i32.const 52) (i32.const 4) (i32.const 56) (i32.const 4) (i32.const 0)) | |
26 | - drop | |
27 | - ) | |
28 | - ) | |
29 | - | |
30 | - (func $callback (param $result i32) | |
31 | - (if (i32.eq (i32.const 1) (get_local $result)) | |
32 | - (return) | |
33 | - ) | |
34 | - (unreachable) | |
35 | - ) | |
36 | -) |
tests/interface/callDataCopy.json | ||
---|---|---|
@@ -1,14 +1,0 @@ | ||
1 | -{ | |
2 | - "callValue": "0x00", | |
3 | - "callData": "0x596f75206172652077616974696e6720666f7220746865207265766f6c7574696f6e3f204c657420697420626521204d79206f776e20626567616e2061206c6f6e672074696d652061676f21205768656e20796f752077696c6c2062652072656164792e2e2e4920776f6ee2809974206d696e6420676f696e6720616c6f6e67207769746820796f7520666f722061207768696c652e20427574207768656e20796f75e280996c6c2073746f702c2049207368616c6c20636f6e74696e7565206f6e206d7920696e73616e6520616e6420747269756d7068616e742077617920746f776172642074686520677265617420616e64207375626c696d6520636f6e7175657374206f6620746865206e6f7468696e6721", | |
4 | - "state": { | |
5 | - "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b": { | |
6 | - "balance": "0x056bc75e2d63100000", | |
7 | - "code": "0x00", | |
8 | - "nonce": "0x00" | |
9 | - } | |
10 | - }, | |
11 | - "coinbase": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
12 | - "caller": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b", | |
13 | - "address": "0x5d48c1018904a172886829bbbd9c6f4a2d06c47b" | |
14 | -} |