Commit 865f135bbd80624eceea4b85923acc13137ffd39
Merge pull request #30 from ewasm/gas
Calculate gas for every methodAlex Beregszaszi authored on 8/18/2016, 10:40:24 PM
GitHub committed on 8/18/2016, 10:40:24 PM
Parent: f488805c0df12c74d2cb8ed0c5d0e439495676df
Parent: db70561842aa908ed95d11fc1aea28b6835dde37
Files changed
interface.js | changed |
tests/interface/basic_gas_ops.wast | changed |
interface.js | ||
---|---|---|
@@ -60,21 +60,22 @@ | ||
60 | 60 | * Subtracts an amount to the gas counter |
61 | 61 | * @param {integer} amount the amount to subtract to the gas counter |
62 | 62 | */ |
63 | 63 | useGas (amount) { |
64 | - if (amount > 0) { | |
65 | - if (this.environment.gasLimit < amount) { | |
66 | - throw new Error('Ran out of gas') | |
67 | - } | |
68 | - this.environment.gasLimit -= amount | |
64 | + if (amount < 0) { | |
65 | + throw new Error('Negative gas deduction requested') | |
69 | 66 | } |
67 | + | |
68 | + this.takeGas(amount) | |
70 | 69 | } |
71 | 70 | |
72 | 71 | /** |
73 | 72 | * Returns the current amount of gas |
74 | 73 | * @return {integer} |
75 | 74 | */ |
76 | 75 | getGasLeft () { |
76 | + this.takeGas(2) | |
77 | + | |
77 | 78 | return this.environment.gasLimit |
78 | 79 | } |
79 | 80 | |
80 | 81 | /** |
@@ -82,8 +83,10 @@ | ||
82 | 83 | * the given offset. |
83 | 84 | * @param {integer} offset |
84 | 85 | */ |
85 | 86 | getAddress (offset) { |
87 | + this.takeGas(2) | |
88 | + | |
86 | 89 | this.setMemory(offset, constants.ADDRESS_SIZE_BYTES, this.environment.address) |
87 | 90 | } |
88 | 91 | |
89 | 92 | /** |
@@ -92,8 +95,10 @@ | ||
92 | 95 | * @param {integer} addressOffset the memory offset to laod the address |
93 | 96 | * @param {integer} resultOffset |
94 | 97 | */ |
95 | 98 | getBalance (addressOffset, offset) { |
99 | + this.takeGas(20) | |
100 | + | |
96 | 101 | const address = new Address(this.getMemory(addressOffset, constants.ADDRESS_SIZE_BYTES)) |
97 | 102 | // call the parent contract and ask for the balance of one of its child contracts |
98 | 103 | const balance = this.environment.parent.environment.getBalance(address) |
99 | 104 | this.setMemory(offset, constants.BALANCE_SIZE_BYTES, balance.toBuffer(constants.BALANCE_SIZE_BYTES)) |
@@ -105,8 +110,10 @@ | ||
105 | 110 | * account with non-empty associated code. |
106 | 111 | * @param {integer} offset |
107 | 112 | */ |
108 | 113 | getTxOrigin (offset) { |
114 | + this.takeGas(2) | |
115 | + | |
109 | 116 | this.setMemory(offset, constants.ADDRESS_SIZE_BYTES, this.environment.origin) |
110 | 117 | } |
111 | 118 | |
112 | 119 | /** |
@@ -114,8 +121,10 @@ | ||
114 | 121 | * the address of the account that is directly responsible for this execution. |
115 | 122 | * @param {integer} offset |
116 | 123 | */ |
117 | 124 | getCaller (offset) { |
125 | + this.takeGas(2) | |
126 | + | |
118 | 127 | this.setMemory(offset, constants.ADDRESS_SIZE_BYTES, this.environment.caller) |
119 | 128 | } |
120 | 129 | |
121 | 130 | /** |
@@ -123,8 +132,10 @@ | ||
123 | 132 | * this execution and loads it into memory at the given location. |
124 | 133 | * @param {integer} offset |
125 | 134 | */ |
126 | 135 | getCallValue (offset) { |
136 | + this.takeGas(2) | |
137 | + | |
127 | 138 | this.setMemory(offset, constants.BALANCE_SIZE_BYTES, this.environment.callValue.toBuffer(constants.BALANCE_SIZE_BYTES)) |
128 | 139 | } |
129 | 140 | |
130 | 141 | /** |
@@ -132,8 +143,10 @@ | ||
132 | 143 | * data passed with the message call instruction or transaction. |
133 | 144 | * @return {integer} |
134 | 145 | */ |
135 | 146 | getCallDataSize () { |
147 | + this.takeGas(2) | |
148 | + | |
136 | 149 | return this.environment.callData.length |
137 | 150 | } |
138 | 151 | |
139 | 152 | /** |
@@ -143,8 +156,10 @@ | ||
143 | 156 | * @param {integer} dataOffset the offset in the input data |
144 | 157 | * @param {integer} length the length of data to copy |
145 | 158 | */ |
146 | 159 | callDataCopy (offset, dataOffset, length) { |
160 | + this.takeGas(3 + ((length / 32) * 3)) | |
161 | + | |
147 | 162 | const callData = this.environment.callData.slice(dataOffset, dataOffset + length) |
148 | 163 | this.setMemory(offset, length, callData) |
149 | 164 | } |
150 | 165 | |
@@ -152,8 +167,10 @@ | ||
152 | 167 | * Gets the size of code running in current environment. |
153 | 168 | * @return {interger} |
154 | 169 | */ |
155 | 170 | getCodeSize () { |
171 | + this.takeGas(2) | |
172 | + | |
156 | 173 | return this.environment.code.length |
157 | 174 | } |
158 | 175 | |
159 | 176 | /** |
@@ -162,8 +179,10 @@ | ||
162 | 179 | * @param {integer} codeOffset the code offset |
163 | 180 | * @param {integer} length the length of code to copy |
164 | 181 | */ |
165 | 182 | codeCopy (resultOffset, codeOffset, length) { |
183 | + this.takeGas(3 + ((length / 32) * 3)) | |
184 | + | |
166 | 185 | const code = new Uint8Array(this.environment.code, codeOffset, length) |
167 | 186 | this.setMemory(resultOffset, length, code) |
168 | 187 | } |
169 | 188 | |
@@ -172,8 +191,10 @@ | ||
172 | 191 | * @param {integer} addressOffset the offset in memory to load the address from |
173 | 192 | * @return {integer} |
174 | 193 | */ |
175 | 194 | getExternalCodeSize (addressOffset) { |
195 | + this.takeGas(20) | |
196 | + | |
176 | 197 | const address = new Address(this.getMemory(addressOffset, constants.ADDRESS_SIZE_BYTES)) |
177 | 198 | const code = this.environment.getCode(address) |
178 | 199 | return code.length |
179 | 200 | } |
@@ -185,8 +206,10 @@ | ||
185 | 206 | * @param {integer} codeOffset the code offset |
186 | 207 | * @param {integer} length the length of code to copy |
187 | 208 | */ |
188 | 209 | externalCodeCopy (addressOffset, resultOffset, codeOffset, length) { |
210 | + this.takeGas(20 + ((length / 32) * 3)) | |
211 | + | |
189 | 212 | const address = new Address(this.getMemory(addressOffset, constants.ADDRESS_SIZE_BYTES)) |
190 | 213 | let code = this.environment.getCode(address) |
191 | 214 | code = new Uint8Array(code, codeOffset, length) |
192 | 215 | this.setMemory(resultOffset, length, code) |
@@ -196,8 +219,10 @@ | ||
196 | 219 | * Gets price of gas in current environment. |
197 | 220 | * @return {integer} |
198 | 221 | */ |
199 | 222 | getTxGasPrice () { |
223 | + this.takeGas(2) | |
224 | + | |
200 | 225 | return this.environment.gasPrice |
201 | 226 | } |
202 | 227 | |
203 | 228 | /** |
@@ -205,8 +230,10 @@ | ||
205 | 230 | * @param {integer} number which block to load |
206 | 231 | * @param {integer} offset the offset to load the hash into |
207 | 232 | */ |
208 | 233 | getBlockHash (number, offset) { |
234 | + this.takeGas(20) | |
235 | + | |
209 | 236 | const diff = this.environment.number - number |
210 | 237 | let hash |
211 | 238 | |
212 | 239 | if (diff > 256 || diff <= 0) { |
@@ -221,40 +248,50 @@ | ||
221 | 248 | * Gets the block’s beneficiary address and loads into memory. |
222 | 249 | * @param offset |
223 | 250 | */ |
224 | 251 | getBlockCoinbase (offset) { |
252 | + this.takeGas(2) | |
253 | + | |
225 | 254 | this.setMemory(offset, constants.ADDRESS_SIZE_BYTES, this.environment.coinbase) |
226 | 255 | } |
227 | 256 | |
228 | 257 | /** |
229 | 258 | * Get the block’s timestamp. |
230 | 259 | * @return {integer} |
231 | 260 | */ |
232 | 261 | getBlockTimestamp () { |
262 | + this.takeGas(2) | |
263 | + | |
233 | 264 | return this.environment.timestamp |
234 | 265 | } |
235 | 266 | |
236 | 267 | /** |
237 | 268 | * Get the block’s number. |
238 | 269 | * @return {integer} |
239 | 270 | */ |
240 | 271 | getBlockNumber () { |
272 | + this.takeGas(2) | |
273 | + | |
241 | 274 | return this.environment.number |
242 | 275 | } |
243 | 276 | |
244 | 277 | /** |
245 | 278 | * Get the block’s difficulty. |
246 | 279 | * @return {integer} |
247 | 280 | */ |
248 | 281 | getBlockDifficulty () { |
282 | + this.takeGas(2) | |
283 | + | |
249 | 284 | return this.environment.difficulty |
250 | 285 | } |
251 | 286 | |
252 | 287 | /** |
253 | 288 | * Get the block’s gas limit. |
254 | 289 | * @return {integer} |
255 | 290 | */ |
256 | 291 | getBlockGasLimit () { |
292 | + this.takeGas(2) | |
293 | + | |
257 | 294 | return this.environment.gasLimit |
258 | 295 | } |
259 | 296 | |
260 | 297 | /** |
@@ -263,8 +300,11 @@ | ||
263 | 300 | * @param {integer} length the data length |
264 | 301 | * TODO: replace with variadic |
265 | 302 | */ |
266 | 303 | log (dataOffset, length, topic1, topic2, topic3, topic4, topic5) { |
304 | + // FIXME: calculate gas for topics set | |
305 | + this.takeGas(375 + length * 8) | |
306 | + | |
267 | 307 | const data = this.getMemory(dataOffset, length) |
268 | 308 | this.environment.logs.push({ |
269 | 309 | data: data, |
270 | 310 | topics: [topic1, topic2, topic3, topic4, topic5] |
@@ -277,8 +317,10 @@ | ||
277 | 317 | * @param {integer} dataOffset the offset to load the code for the new contract from |
278 | 318 | * @param {integer} length the data length |
279 | 319 | */ |
280 | 320 | create (valueOffset, dataOffset, length) { |
321 | + this.takeGas(32000) | |
322 | + | |
281 | 323 | const value = new U256(this.getMemory(valueOffset, constants.BALANCE_SIZE_BYTES)) |
282 | 324 | const data = this.getMemory(dataOffset, length) |
283 | 325 | const result = this.environment.create(value, data) |
284 | 326 | return result |
@@ -293,11 +335,13 @@ | ||
293 | 335 | * @param {integer} resultOffset the offset to store the result data at |
294 | 336 | * @param {integer} resultLength |
295 | 337 | * @param {integer} gas |
296 | 338 | * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not |
297 | - * TODO: add proper gas counting | |
298 | 339 | */ |
299 | 340 | call (gas, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength) { |
341 | + // FIXME: count properly | |
342 | + this.takeGas(40) | |
343 | + | |
300 | 344 | if (gas === undefined) { |
301 | 345 | gas = this.gasLeft() |
302 | 346 | } |
303 | 347 | // Load the params from mem |
@@ -319,11 +363,13 @@ | ||
319 | 363 | * @param {integer} resultOffset the offset to store the result data at |
320 | 364 | * @param {integer} resultLength |
321 | 365 | * @param {integer} gas |
322 | 366 | * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not |
323 | - * TODO: add proper gas counting | |
324 | 367 | */ |
325 | 368 | callCode (gas, addressOffset, valueOffset, dataOffset, dataLength, resultOffset, resultLength) { |
369 | + // FIXME: count properly | |
370 | + this.takeGas(40) | |
371 | + | |
326 | 372 | // Load the params from mem |
327 | 373 | const address = new Address(this.getMemory(addressOffset, constants.ADDRESS_SIZE_BYTES)) |
328 | 374 | const value = new U256(this.getMemory(valueOffset, constants.BALANCE_SIZE_BYTES)) |
329 | 375 | const data = this.getMemory(dataOffset, dataLength) |
@@ -345,8 +391,11 @@ | ||
345 | 391 | * @param {integer} resultLength |
346 | 392 | * @return {integer} Returns 1 or 0 depending on if the VM trapped on the message or not |
347 | 393 | */ |
348 | 394 | callDelegate (gas, addressOffset, dataOffset, dataLength, resultOffset, resultLength) { |
395 | + // FIXME: count properly | |
396 | + this.takeGas(40) | |
397 | + | |
349 | 398 | const data = this.getMemory(dataOffset, dataLength) |
350 | 399 | const address = new Address(this.getMemory(addressOffset, constants.ADDRESS_SIZE_BYTES)) |
351 | 400 | const [result, errorCode] = this.environment.callDelegate(gas, address, data) |
352 | 401 | this.setMemory(resultOffset, resultLength, result) |
@@ -365,11 +414,13 @@ | ||
365 | 414 | const value = this.getMemory(valueOffset, 32).slice(0) |
366 | 415 | const oldValue = this.environment.state.get(path) |
367 | 416 | const valIsZero = value.every((i) => i === 0) |
368 | 417 | |
418 | + // FIXME: gas counting has more cases then the below | |
419 | + | |
369 | 420 | // write |
370 | 421 | if (!valIsZero && !oldValue) { |
371 | - this.environment.gasLimit -= 15000 | |
422 | + this.takeGas(15000) | |
372 | 423 | } |
373 | 424 | |
374 | 425 | // delete |
375 | 426 | if (valIsZero && oldValue) { |
@@ -385,8 +436,10 @@ | ||
385 | 436 | * @param {interger} pathOffest the memory offset to load the the path from |
386 | 437 | * @param {interger} resultOffset the memory offset to load the value from |
387 | 438 | */ |
388 | 439 | storageLoad (pathOffset, resultOffset) { |
440 | + this.takeGas(50) | |
441 | + | |
389 | 442 | const path = new Buffer(this.getMemory(pathOffset, 32)).toString('hex') |
390 | 443 | const result = this.environment.state.get(path) |
391 | 444 | this.setMemory(resultOffset, 32, result) |
392 | 445 | } |
@@ -406,8 +459,9 @@ | ||
406 | 459 | * @param {integer} offset the offset to load the address from |
407 | 460 | */ |
408 | 461 | selfDestruct (addressOffset) { |
409 | 462 | this.environment.suicideAddress = new Address(this.getMemory(addressOffset, constants.ADDRESS_SIZE_BYTES)) |
463 | + this.environment.gasRefund += 24000 | |
410 | 464 | } |
411 | 465 | |
412 | 466 | getMemory (offset, length) { |
413 | 467 | return new Uint8Array(this.module.exports.memory, offset, length) |
@@ -416,8 +470,19 @@ | ||
416 | 470 | setMemory (offset, length, value) { |
417 | 471 | const memory = new Uint8Array(this.module.exports.memory, offset, length) |
418 | 472 | memory.set(value) |
419 | 473 | } |
474 | + | |
475 | + /* | |
476 | + * Takes gas from the tank. Only needs to check if there's gas left to be taken, | |
477 | + * because every caller of this method is trusted. | |
478 | + */ | |
479 | + takeGas (amount) { | |
480 | + if (this.environment.gasLimit < amount) { | |
481 | + throw new Error('Ran out of gas') | |
482 | + } | |
483 | + this.environment.gasLimit -= amount | |
484 | + } | |
420 | 485 | } |
421 | 486 | |
422 | 487 | // |
423 | 488 | // Polyfill required unless this is sorted: https://bugs.chromium.org/p/chromium/issues/detail?id=633895 |
tests/interface/basic_gas_ops.wast | ||
---|---|---|
@@ -7,26 +7,18 @@ | ||
7 | 7 | (func |
8 | 8 | ;; test adding gas |
9 | 9 | (block |
10 | 10 | (call_import $useGas (i32.const 1)) |
11 | - (if (i32.eq (call_import $gas) (i32.const 999)) | |
11 | + (if (i32.eq (call_import $gas) (i32.const 997)) | |
12 | 12 | (return) |
13 | 13 | ) |
14 | 14 | (unreachable) |
15 | 15 | ) |
16 | 16 | (block |
17 | 17 | (call_import $useGas (i32.const 1)) |
18 | - (if (i32.eq (call_import $gas) (i32.const 998)) | |
18 | + (if (i32.eq (call_import $gas) (i32.const 996)) | |
19 | 19 | (return) |
20 | 20 | ) |
21 | 21 | (unreachable) |
22 | 22 | ) |
23 | - ;; should disregard negative values | |
24 | - (block | |
25 | - (call_import $useGas (i32.const -1)) | |
26 | - (if (i32.eq (call_import $gas) (i32.const 998)) | |
27 | - (return) | |
28 | - ) | |
29 | - (unreachable) | |
30 | - ) | |
31 | 23 | ) |
32 | 24 | ) |
Built with git-ssb-web