Files: c326c8af2a729760e833b861163acade71770ff5 / runTx.js
2323 bytesRaw
1 | |
2 | // run tx; the tx message handler |
3 | runTx (tx, environment = new Environment()) { |
4 | this.environment = environment |
5 | |
6 | if (Buffer.isBuffer(tx) || typeof tx === 'string') { |
7 | tx = new Transaction(tx) |
8 | if (!tx.valid) { |
9 | throw new Error('Invalid transaction signature') |
10 | } |
11 | } |
12 | |
13 | // look up sender |
14 | let fromAccount = this.environment.state.get(tx.from.toString()) |
15 | if (!fromAccount) { |
16 | throw new Error('Sender account not found: ' + tx.from.toString()) |
17 | } |
18 | |
19 | if (fromAccount.get('nonce').gt(tx.nonce)) { |
20 | throw new Error(`Invalid nonce: ${fromAccount.get('nonce')} > ${tx.nonce}`) |
21 | } |
22 | |
23 | fromAccount.set('nonce', fromAccount.get('nonce').add(new U256(1))) |
24 | |
25 | let isCreation = false |
26 | |
27 | // Special case: contract deployment |
28 | if (tx.to.isZero() && (tx.data.length !== 0)) { |
29 | console.log('This is a contract deployment transaction') |
30 | isCreation = true |
31 | } |
32 | |
33 | // This cost will not be refunded |
34 | let txCost = 21000 + (isCreation ? 32000 : 0) |
35 | tx.data.forEach((item) => { |
36 | if (item === 0) { |
37 | txCost += 4 |
38 | } else { |
39 | txCost += 68 |
40 | } |
41 | }) |
42 | |
43 | if (tx.gasLimit.lt(new U256(txCost))) { |
44 | throw new Error(`Minimum transaction gas limit not met: ${txCost}`) |
45 | } |
46 | |
47 | if (fromAccount.get('balance').lt(tx.gasLimit.mul(tx.gasPrice))) { |
48 | throw new Error(`Insufficient account balance: ${fromAccount.get('balance').toString()} < ${tx.gasLimit.mul(tx.gasPrice).toString()}`) |
49 | } |
50 | |
51 | // deduct gasLimit * gasPrice from sender |
52 | fromAccount.set('balance', fromAccount.get('balance').sub(tx.gasLimit.mul(tx.gasPrice))) |
53 | |
54 | const handler = isCreation ? this.createHandler.bind(this) : this.callHandler.bind(this) |
55 | let ret = handler({ |
56 | to: tx.to, |
57 | from: tx.from, |
58 | gasLimit: tx.gasLimit - txCost, |
59 | value: tx.value, |
60 | data: tx.data |
61 | }) |
62 | |
63 | // refund unused gas |
64 | if (ret.executionOutcome === 1) { |
65 | fromAccount.set('balance', fromAccount.get('balance').add(tx.gasPrice.mul(ret.gasLeft.add(ret.gasRefund)))) |
66 | } |
67 | |
68 | // save new state? |
69 | |
70 | return { |
71 | executionOutcome: ret.executionOutcome, |
72 | accountCreated: isCreation ? ret.accountCreated : undefined, |
73 | returnValue: isCreation ? undefined : ret.returnValue, |
74 | gasLeft: ret.gasLeft, |
75 | logs: ret.logs |
76 | } |
77 | } |
78 |
Built with git-ssb-web