Commit 49dfa5e4a80eda9422f7a27a0d55fb30ffc77639
Merge pull request #46 from ewasm/call
Merge create/call paths in runTxAlex Beregszaszi authored on 8/28/2016, 9:25:41 PM
GitHub committed on 8/28/2016, 9:25:41 PM
Parent: 807065e92512168f27868090b0395f77d4c52705
Parent: a7368ae614730b38e21a3792049310c00d65a725
Files changed
environment.js | changed |
index.js | changed |
interface.js | changed |
transaction.js | changed |
environment.js | ||
---|---|---|
@@ -21,8 +21,9 @@ | ||
21 | 21 | // the ROM |
22 | 22 | code: new Uint8Array(), // the current running code |
23 | 23 | // output calls |
24 | 24 | logs: [], |
25 | + selfDestruct: false, | |
25 | 26 | selfDestructAddress: new Address('0x0000000000000000000000000000000000000000'), |
26 | 27 | // more output calls |
27 | 28 | returnValue: new Uint8Array() |
28 | 29 | } |
index.js | ||
---|---|---|
@@ -119,16 +119,25 @@ | ||
119 | 119 | |
120 | 120 | const kernel = new Kernel(environment) |
121 | 121 | kernel.codeHandler(code, new Interface(environment)) |
122 | 122 | |
123 | + // self destructed | |
124 | + if (environment.selfDestruct) { | |
125 | + const balance = this.state.get(call.to.toString()).get('balance') | |
126 | + const beneficiary = this.state.get(environment.selfDestructAddress) | |
127 | + beneficiary.set('balance', beneficiary.get('balance').add(balance)) | |
128 | + this.state.delete(call.to.toString()) | |
129 | + } | |
130 | + | |
123 | 131 | // generate new stateroot |
124 | 132 | // this.environment.state.set(address, { stateRoot: stateRoot }) |
125 | 133 | |
126 | 134 | return { |
127 | 135 | executionOutcome: 1, // success |
128 | 136 | gasLeft: new U256(environment.gasLeft), |
129 | 137 | gasRefund: new U256(environment.gasRefund), |
130 | 138 | returnValue: environment.returnValue, |
139 | + selfDestruct: environment.selfDestruct, | |
131 | 140 | selfDestructAddress: environment.selfDestructAddress, |
132 | 141 | logs: environment.logs |
133 | 142 | } |
134 | 143 | } |
@@ -152,12 +161,18 @@ | ||
152 | 161 | // Run code and take return value as contract code |
153 | 162 | // FIXME: decide if these are the right values here: value: 0, data: '' |
154 | 163 | code = this.callHandler({ from: create.from, to: address, gasLimit: create.gasLimit, value: new U256(0), data: new Uint8Array() }).returnValue |
155 | 164 | |
165 | + // FIXME: special handling for selfdestruct | |
166 | + | |
156 | 167 | this.environment.state.get(address.toString()).set('code', code) |
157 | 168 | |
158 | 169 | return { |
159 | - accountCreated: address | |
170 | + executionOutcome: 1, // success | |
171 | + gasLeft: new U256(this.environment.gasLeft), | |
172 | + gasRefund: new U256(this.environment.gasRefund), | |
173 | + accountCreated: address, | |
174 | + logs: this.environment.logs | |
160 | 175 | } |
161 | 176 | } |
162 | 177 | |
163 | 178 | // run tx; the tx message handler |
@@ -182,58 +197,57 @@ | ||
182 | 197 | } |
183 | 198 | |
184 | 199 | fromAccount.set('nonce', fromAccount.get('nonce').add(new U256(1))) |
185 | 200 | |
201 | + let isCreation = false | |
202 | + | |
186 | 203 | // Special case: contract deployment |
187 | - if (tx.to.isZero()) { | |
188 | - if (tx.data.length !== 0) { | |
189 | - console.log('This is a contract deployment transaction') | |
190 | - | |
191 | - // FIXME: deduct fees | |
192 | - | |
193 | - return this.createHandler({ | |
194 | - from: tx.from, | |
195 | - gasLimit: tx.gasLimit, | |
196 | - value: tx.value, | |
197 | - data: tx.data | |
198 | - }) | |
199 | - } | |
204 | + if (tx.to.isZero() && (tx.data.length !== 0)) { | |
205 | + console.log('This is a contract deployment transaction') | |
206 | + isCreation = true | |
200 | 207 | } |
201 | 208 | |
202 | - // deduct gasLimit * gasPrice from sender | |
203 | - if (fromAccount.get('balance').lt(tx.gasLimit.mul(tx.gasPrice))) { | |
204 | - throw new Error(`Insufficient account balance: ${fromAccount.get('balance').toString()} < ${tx.gasLimit.mul(tx.gasPrice).toString()}`) | |
205 | - } | |
206 | - | |
207 | - fromAccount.set('balance', fromAccount.get('balance').sub(tx.gasLimit.mul(tx.gasPrice))) | |
208 | - | |
209 | 209 | // This cost will not be refunded |
210 | - let txCost = 21000 | |
210 | + let txCost = 21000 + (isCreation ? 32000 : 0) | |
211 | 211 | tx.data.forEach((item) => { |
212 | 212 | if (item === 0) { |
213 | 213 | txCost += 4 |
214 | 214 | } else { |
215 | 215 | txCost += 68 |
216 | 216 | } |
217 | 217 | }) |
218 | 218 | |
219 | - let ret = this.callHandler({ | |
219 | + if (tx.gasLimit.lt(new U256(txCost))) { | |
220 | + throw new Error(`Minimum transaction gas limit not met: ${txCost}`) | |
221 | + } | |
222 | + | |
223 | + if (fromAccount.get('balance').lt(tx.gasLimit.mul(tx.gasPrice))) { | |
224 | + throw new Error(`Insufficient account balance: ${fromAccount.get('balance').toString()} < ${tx.gasLimit.mul(tx.gasPrice).toString()}`) | |
225 | + } | |
226 | + | |
227 | + // deduct gasLimit * gasPrice from sender | |
228 | + fromAccount.set('balance', fromAccount.get('balance').sub(tx.gasLimit.mul(tx.gasPrice))) | |
229 | + | |
230 | + const handler = isCreation ? this.createHandler.bind(this) : this.callHandler.bind(this) | |
231 | + let ret = handler({ | |
220 | 232 | to: tx.to, |
221 | 233 | from: tx.from, |
222 | 234 | gasLimit: tx.gasLimit - txCost, |
223 | 235 | value: tx.value, |
224 | 236 | data: tx.data |
225 | 237 | }) |
226 | 238 | |
227 | - // refund gas | |
239 | + // refund unused gas | |
228 | 240 | if (ret.executionOutcome === 1) { |
229 | 241 | fromAccount.set('balance', fromAccount.get('balance').add(tx.gasPrice.mul(ret.gasLeft.add(ret.gasRefund)))) |
230 | 242 | } |
231 | 243 | |
232 | 244 | // save new state? |
233 | 245 | |
234 | 246 | return { |
235 | - returnValue: ret.returnValue, | |
247 | + executionOutcome: ret.executionOutcome, | |
248 | + accountCreated: isCreation ? ret.accountCreated : undefined, | |
249 | + returnValue: isCreation ? undefined : ret.returnValue, | |
236 | 250 | gasLeft: ret.gasLeft, |
237 | 251 | logs: ret.logs |
238 | 252 | } |
239 | 253 | } |
interface.js | ||
---|---|---|
@@ -392,9 +392,9 @@ | ||
392 | 392 | |
393 | 393 | // Special case for non-zero value |
394 | 394 | if (!value.isZero()) { |
395 | 395 | this.takeGas(9000) |
396 | - gas += 2300; | |
396 | + gas += 2300 | |
397 | 397 | } |
398 | 398 | |
399 | 399 | const [errorCode, result] = this.environment.call(gas, address, value, data) |
400 | 400 | this.setMemory(resultOffset, resultLength, result) |
@@ -504,9 +504,10 @@ | ||
504 | 504 | * balance to an address path |
505 | 505 | * @param {integer} offset the offset to load the address from |
506 | 506 | */ |
507 | 507 | selfDestruct (addressOffset) { |
508 | - this.environment.suicideAddress = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) | |
508 | + this.environment.selfDestruct = true | |
509 | + this.environment.selfDestructAddress = Address.fromMemory(this.getMemory(addressOffset, ADDRESS_SIZE_BYTES)) | |
509 | 510 | this.environment.gasRefund += 24000 |
510 | 511 | } |
511 | 512 | |
512 | 513 | getMemory (offset, length) { |
Built with git-ssb-web