| 1 | [ |
| 2 | "autonomous agent", |
| 3 | { |
| 4 | "init": "{ |
| 5 | $close_timeout = 300; |
| 6 | $addressA = '2QHG44PZLJWD2H7C5ZIWH4NZZVB6QCC7'; |
| 7 | $addressB = 'X55IWSNMHNDUIYKICDW3EOYAWHRUKANP'; |
| 8 | $bFromA = (trigger.address == $addressA); |
| 9 | $bFromB = (trigger.address == $addressB); |
| 10 | $bFromParties = ($bFromA OR $bFromB); |
| 11 | if ($bFromParties) |
| 12 | $party = $bFromA ? 'A' : 'B'; |
| 13 | }", |
| 14 | "messages": { |
| 15 | "cases": [ |
| 16 | { |
| 17 | "if": "{ $bFromParties AND trigger.output[[asset=base]] >= 1e5 }", |
| 18 | "messages": [ |
| 19 | { |
| 20 | "app": "state", |
| 21 | "state": "{ |
| 22 | if (var['close_initiated_by']) |
| 23 | bounce('already closing'); |
| 24 | if (!var['period']) |
| 25 | var['period'] = 1; |
| 26 | $key = 'balance' || $party; |
| 27 | var[$key] += trigger.output[[asset=base]]; |
| 28 | response[$key] = var[$key]; |
| 29 | }" |
| 30 | } |
| 31 | ] |
| 32 | }, |
| 33 | { |
| 34 | "if": "{ $bFromParties AND trigger.data.close AND !var['close_initiated_by'] }", |
| 35 | "messages": [ |
| 36 | { |
| 37 | "app": "state", |
| 38 | "state": "{ |
| 39 | $transferredFromMe = trigger.data.transferredFromMe otherwise 0; |
| 40 | if ($transferredFromMe < 0) |
| 41 | bounce('bad amount spent by me: ' || $transferredFromMe); |
| 42 | if (trigger.data.sentByPeer){ |
| 43 | if (trigger.data.sentByPeer.signed_message.channel != this_address) |
| 44 | bounce('signed for another channel'); |
| 45 | if (trigger.data.sentByPeer.signed_message.period != var['period']) |
| 46 | bounce('signed for a different period of this channel'); |
| 47 | if (!is_valid_signed_package(trigger.data.sentByPeer, $bFromB ? $addressA : $addressB)) |
| 48 | bounce('invalid signature by peer'); |
| 49 | $transferredFromPeer = trigger.data.sentByPeer.signed_message.amount_spent; |
| 50 | if ($transferredFromPeer < 0) |
| 51 | bounce('bad amount spent by peer: ' || $transferredFromPeer); |
| 52 | } |
| 53 | else |
| 54 | $transferredFromPeer = 0; |
| 55 | var['spentByA'] = $bFromA ? $transferredFromMe : $transferredFromPeer; |
| 56 | var['spentByB'] = $bFromB ? $transferredFromMe : $transferredFromPeer; |
| 57 | $finalBalanceA = var['balanceA'] - var['spentByA'] + var['spentByB']; |
| 58 | $finalBalanceB = var['balanceB'] - var['spentByB'] + var['spentByA']; |
| 59 | if ($finalBalanceA < 0 OR $finalBalanceB < 0) |
| 60 | bounce('one of the balances would become negative'); |
| 61 | var['close_initiated_by'] = $party; |
| 62 | var['close_start_ts'] = timestamp; |
| 63 | response['close_start_ts'] = timestamp; |
| 64 | response['finalBalanceA'] = $finalBalanceA; |
| 65 | response['finalBalanceB'] = $finalBalanceB; |
| 66 | }" |
| 67 | } |
| 68 | ] |
| 69 | }, |
| 70 | { |
| 71 | "if": "{ trigger.data.confirm AND var['close_initiated_by'] }", |
| 72 | "init": "{ |
| 73 | if (!($bFromParties AND var['close_initiated_by'] != $party OR timestamp > var['close_start_ts'] + $close_timeout)) |
| 74 | bounce('too early'); |
| 75 | $finalBalanceA = var['balanceA'] - var['spentByA'] + var['spentByB']; |
| 76 | $finalBalanceB = var['balanceB'] - var['spentByB'] + var['spentByA']; |
| 77 | }", |
| 78 | "messages": [ |
| 79 | { |
| 80 | "app": "payment", |
| 81 | "payload": { |
| 82 | "asset": "base", |
| 83 | "outputs": [ |
| 84 | { |
| 85 | "address": "{$addressA}", |
| 86 | "amount": "{ $finalBalanceA < $finalBalanceB ? $finalBalanceA : '' }" |
| 87 | }, |
| 88 | { |
| 89 | "address": "{$addressB}", |
| 90 | "amount": "{ $finalBalanceA >= $finalBalanceB ? $finalBalanceB : '' }" |
| 91 | } |
| 92 | ] |
| 93 | } |
| 94 | }, |
| 95 | { |
| 96 | "app": "state", |
| 97 | "state": "{ |
| 98 | var['period'] += 1; |
| 99 | var['close_initiated_by'] = false; |
| 100 | var['close_start_ts'] = false; |
| 101 | var['balanceA'] = false; |
| 102 | var['balanceB'] = false; |
| 103 | var['spentByA'] = false; |
| 104 | var['spentByB'] = false; |
| 105 | }" |
| 106 | } |
| 107 | ] |
| 108 | }, |
| 109 | { |
| 110 | "if": "{ trigger.data.fraud_proof AND var['close_initiated_by'] AND trigger.data.sentByPeer }", |
| 111 | "init": "{ |
| 112 | $bInitiatedByA = (var['close_initiated_by'] == 'A'); |
| 113 | if (trigger.data.sentByPeer.signed_message.channel != this_address) |
| 114 | bounce('signed for another channel'); |
| 115 | if (trigger.data.sentByPeer.signed_message.period != var['period']) |
| 116 | bounce('signed for a different period of this channel'); |
| 117 | if (!is_valid_signed_package(trigger.data.sentByPeer, $bInitiatedByA ? $addressA : $addressB)) |
| 118 | bounce('invalid signature by peer'); |
| 119 | $transferredFromPeer = trigger.data.sentByPeer.signed_message.amount_spent; |
| 120 | if ($transferredFromPeer < 0) |
| 121 | bounce('bad amount spent by peer: ' || $transferredFromPeer); |
| 122 | $transferredFromPeerAsClaimedByPeer = var['spentBy' || ($bInitiatedByA ? 'A' : 'B')]; |
| 123 | if ($transferredFromPeer <= $transferredFromPeerAsClaimedByPeer) |
| 124 | bounce("the peer didn't lie in his favor"); |
| 125 | }", |
| 126 | "messages": [ |
| 127 | { |
| 128 | "app": "payment", |
| 129 | "payload": { |
| 130 | "asset": "base", |
| 131 | "outputs": [ |
| 132 | { |
| 133 | "address": "{trigger.address}" |
| 134 | } |
| 135 | ] |
| 136 | } |
| 137 | }, |
| 138 | { |
| 139 | "app": "state", |
| 140 | "state": "{ |
| 141 | var['period'] += 1; |
| 142 | var['close_initiated_by'] = false; |
| 143 | var['close_start_ts'] = false; |
| 144 | var['balanceA'] = false; |
| 145 | var['balanceB'] = false; |
| 146 | var['spentByA'] = false; |
| 147 | var['spentByB'] = false; |
| 148 | }" |
| 149 | } |
| 150 | ] |
| 151 | } |
| 152 | ] |
| 153 | } |
| 154 | } |
| 155 | ] |