Advertisement
Guest User

State assertions channel

a guest
Jan 14th, 2019
285
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.13 KB | None | 0 0
  1. pragma solidity ^0.5.0;
  2.  
  3. contract App {
  4. // Returns new state hash, coins for player1 to withdraw and coins for player2 to withdraw.
  5. function transition(address signer, bytes memory oldstate, bytes memory input, uint command) public pure returns (bytes32 newhstate);
  6. }
  7.  
  8. contract StateAssertionChannel {
  9. bool turnParity;
  10. bool bondParty1;
  11. bool bondParty2;
  12. Status public status;
  13. uint deadline;
  14.  
  15. address payable[] public plist; // list of parties in our channel
  16. mapping (address => uint) public balance; // List of bonds to be refunded
  17.  
  18. // address public turn;
  19. enum Status {DEPOSIT, ON, DISPUTE }
  20.  
  21. uint256 public bestRound = 0;
  22. bytes32 public channelHash;
  23. uint256 public disputePeriod;
  24.  
  25. // Why is bond important?
  26. // An honest user should always be refudnded for challenging. Even if they get all coins in the app.
  27. uint256 public bond;
  28.  
  29. // address of contract app
  30. App public app;
  31.  
  32. event EventAssert (address asserter, bytes32 prevhstate, bytes32 hstate, uint command, bytes input);
  33. event EventTimeout (uint256 indexed bestround);
  34. event EventEvidence (uint256 indexed bestround, bytes32 hstate);
  35. event EventClose (uint256 indexed bestround, bytes32 hstate);
  36.  
  37. modifier onlyplayers { if (plist[0] == msg.sender || plist[1] == msg.sender) _; else revert(); }
  38.  
  39. // The application creates this state channel and updates it with the list of players.
  40. // Also sets a fixed dispute period.
  41. // We assume the app is keeping track of each party's balance (and it is holding their coins).
  42. // --> This can be tweaked so the state channel holds the coins, and app tracks the balance,
  43. // --> channel must instantatiate app - too much work for demo.
  44. constructor(address payable party1, address payable party2, uint _disputePeriod, address _app, uint _bond) public {
  45. plist.push(party1);
  46. plist.push(party2);
  47.  
  48. status = Status.DEPOSIT;
  49.  
  50. disputePeriod = _disputePeriod;
  51. app = App(_app);
  52. bond = _bond;
  53. }
  54.  
  55. // Both parties need to deposit coins.
  56. // Equal value turns on channel.
  57. function deposit() public payable onlyplayers {
  58. balance[msg.sender] = balance[msg.sender] + msg.value;
  59. if(balance[plist[0]] == balance[plist[1]]) {
  60. status = Status.ON;
  61. }
  62. }
  63.  
  64. // Set latest agreed state off-chain (can cancel commands in process)
  65. function setstate(uint256[] memory _sigs,
  66. uint256 _i,
  67. bool _turnParity,
  68. bytes32 _hstate
  69. ) public {
  70. require(_i > bestRound);
  71.  
  72. // Commitment to signed message for new state hash.
  73. bytes32 h = keccak256(abi.encodePacked(_hstate, _i, _turnParity, address(this)));
  74. bytes memory prefix = "\x19Ethereum Signed Message:\n32";
  75. h = keccak256(abi.encodePacked(prefix, h));
  76.  
  77. // Check all parties in channel have signed it.
  78. for (uint i = 0; i < plist.length; i++) {
  79. uint8 V = uint8(_sigs[i*3+0])+27;
  80. bytes32 R = bytes32(_sigs[i*3+1]);
  81. bytes32 S = bytes32(_sigs[i*3+2]);
  82. verifySignature(plist[i], h, V, R, S);
  83. }
  84.  
  85. // Cancel dispute
  86. status = Status.ON;
  87.  
  88. // Store new state!
  89. bestRound = _i;
  90. turnParity = _turnParity;
  91.  
  92. // clear the assertion states
  93. channelHash = keccak256(abi.encodePacked(_hstate, bytes32(0x00)));
  94.  
  95. // Refund everyone
  96. refundAllBonds();
  97.  
  98. // Tell the world about the new state!
  99. emit EventEvidence(bestRound, _hstate);
  100. }
  101.  
  102. // Trigger the dispute
  103. // Missing _prevhstate as we'll use the one already in the contract
  104. function triggerdispute() onlyplayers payable public {
  105. // Make sure dispute process is not already active
  106. require( status == Status.ON );
  107. status = Status.DISPUTE;
  108. deadline = block.number + disputePeriod;
  109. }
  110.  
  111. // Party in the channel can assert a new state.
  112. function assertState(bytes32 _hstate,
  113. bytes32 _assertedState,
  114. bytes memory _input,
  115. uint _command,
  116.  
  117.  
  118. bytes32 currenthstate,
  119. address currentAsserter,
  120. bytes32 currentInputHash,
  121. uint256 currentCommand,
  122. bytes32 currentAssertedState
  123.  
  124. ) onlyplayers payable public {
  125. require(status == Status.DISPUTE);
  126. require((turnParity && (msg.sender == plist[0])) || (!turnParity && (msg.sender == plist[1])));
  127.  
  128. // is there an assertion set?
  129. if(channelHash == keccak256(abi.encodePacked(currenthstate, bytes32(0x00)))) {
  130. // no
  131. require(currenthstate == _hstate);
  132. } else {
  133. // yes
  134. require(channelHash == keccak256(abi.encodePacked(currenthstate, keccak256(abi.encodePacked(currentAsserter, currentInputHash, currentCommand, currentAssertedState)))));
  135. require(currentAssertedState == _hstate);
  136. }
  137.  
  138. // We can confirm we always have the bond from sender.
  139. // We'll refund all bonds after dispute is resolved.
  140.  
  141. // is this party1 or two
  142. bool bondValue;
  143. uint playerIndex;
  144. if(plist[0] == msg.sender) {
  145. bondValue = bondParty1;
  146. playerIndex = 0;
  147. }
  148. else if(plist[1] == msg.sender) {
  149. bondValue = bondParty2;
  150. playerIndex = 1;
  151. }
  152.  
  153. require((msg.value == bond && !bondValue)
  154. || (msg.value == 0 && bondValue));
  155.  
  156. if(msg.value != 0) {
  157. if(playerIndex == 0) bondParty1 = true;
  158. else if(playerIndex == 1) bondParty2 = true;
  159. }
  160.  
  161. // update channel info
  162. channelHash = keccak256(abi.encodePacked(_hstate, keccak256(abi.encodePacked(msg.sender, keccak256(abi.encodePacked(_input)), _command, _assertedState))));
  163.  
  164. // New deadline
  165. deadline = block.number + disputePeriod;
  166. turnParity = !turnParity; // Cannot assert two states in a row.
  167.  
  168. emit EventAssert(msg.sender, _hstate, _assertedState, _command, _input);
  169. }
  170.  
  171. // Send old state, and the index for submitted command.
  172. // This is not PISA friendly. Ideally PISA will have a signed message from the honest party with PISA's address in it.
  173. // i.e. to stop front-running attacks.
  174. // Can easily be fixed (note: this means no more state privacy for PISA)
  175. function challengeCommand(bytes memory _oldstate, bytes memory _input,
  176. bytes32 currenthstate,
  177. bytes32 currentAssertionHash,
  178. address currentAsserter,
  179. bytes32 currentInputHash,
  180. uint256 currentCommand,
  181. bytes32 currentAssertedState
  182.  
  183. ) onlyplayers public {
  184. require(status == Status.DISPUTE);
  185. // Asserter cannot challenge their own command
  186. require((turnParity && (msg.sender == plist[0])) || (!turnParity && (msg.sender == plist[1])));
  187. // hstate is either accepted by all parties, or it was
  188. // extended as "correct" by the asserter
  189. if(currentAssertionHash != 0) {
  190. require(currentAssertionHash == keccak256(abi.encodePacked(currentAsserter, currentInputHash, currentCommand, currentAssertedState)));
  191. }
  192. require(channelHash == keccak256(abi.encodePacked(keccak256(abi.encodePacked(_oldstate)), currentAssertionHash)));
  193. require(currentInputHash == keccak256(abi.encodePacked(_input)));
  194.  
  195. // Fetch us new state
  196. // Note: we assume the input includes a digital signature
  197. // from the party executing this command
  198. bytes32 newhstate;
  199. (newhstate) = app.transition(currentAsserter, _oldstate, _input, currentCommand);
  200.  
  201. // Is this really the new state?
  202. // Can the user really withdraw this amount?
  203. if(newhstate != currentAssertedState) {
  204. // send all funds (including bonds) in the contract to other player
  205. msg.sender.transfer(address(this).balance);
  206. }
  207. }
  208.  
  209. // The app has reached the terminal state. Its state should just be a balance.
  210. function resolve(uint balance1, uint balance2,
  211. bytes32 currenthstate,
  212. bytes32 currentAssertionHash,
  213. address currentAsserter,
  214. bytes32 currentInputHash,
  215. uint256 currentCommand,
  216. bytes32 currentAssertedState
  217. ) onlyplayers public {
  218. // If the final state was reached via dispute process,
  219. // Make sure the counterparty accepts it (i.e. the one who didnt do an assertion)
  220. require(channelHash == keccak256(abi.encodePacked(currenthstate, currentAssertionHash)));
  221. bytes32 balanceHash;
  222. if(currentAssertionHash != 0) {
  223. // Must be accepted by the counterparty
  224. require(currentAssertionHash == keccak256(abi.encodePacked(currentAsserter, currentInputHash, currentCommand, currentAssertedState)));
  225. require((turnParity && (msg.sender == plist[0])) || (!turnParity && (msg.sender == plist[1])));
  226.  
  227. // finalise the hstate to be the asserted one
  228. balanceHash = currentAssertedState;
  229. }
  230. else {
  231. require(status == Status.ON);
  232. balanceHash = currenthstate;
  233. }
  234.  
  235. // In the app - the final state is "balance1,balance2".
  236. require(balanceHash == keccak256(abi.encodePacked(balance1, balance2)));
  237.  
  238. // There was no response from a party
  239. // i.e. both parties need to use "setstate" or finish protocol via state assertions.
  240. plist[0].transfer(balance1);
  241. plist[1].transfer(balance2);
  242. refundAllBonds();
  243. channelHash = keccak256(abi.encodePacked(balanceHash, bytes32(0x00)));
  244.  
  245. emit EventTimeout(bestRound);
  246. }
  247.  
  248. function timeout() onlyplayers public {
  249. require(block.number >= deadline);
  250. require(status == Status.DISPUTE);
  251.  
  252. // There was no response from a party
  253. // i.e. both parties need to use "setstate" or finish protocol via state assertions.
  254. // if we timeout penalise whoever's turn it is by sending everything
  255. // in the contract to the other player
  256. address payable otherPlayer;
  257. if(turnParity) otherPlayer = plist[1];
  258. else if(!turnParity) otherPlayer = plist[0];
  259. otherPlayer.transfer(address(this).balance);
  260.  
  261. emit EventTimeout(bestRound);
  262. }
  263.  
  264. // Refund all bonds - only callable when resolving dispute
  265. function refundAllBonds() internal {
  266. bool toSend1 = bondParty1;
  267. if(toSend1) {
  268. bondParty1 = false;
  269. plist[0].transfer(bond);
  270. }
  271.  
  272.  
  273. bool toSend2 = bondParty2;
  274. if(toSend2) {
  275. bondParty2 = false;
  276. plist[1].transfer(bond);
  277. }
  278. }
  279.  
  280. // Helper function to verify signatures
  281. function verifySignature(address pub, bytes32 h, uint8 v, bytes32 r, bytes32 s) public pure {
  282. address _signer = ecrecover(h,v,r,s);
  283. if (pub != _signer) revert();
  284. }
  285.  
  286.  
  287. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement