diff --git a/contracts/LedgerChannel.sol b/contracts/LedgerChannel.sol index b56bbc8..cf4446d 100644 --- a/contracts/LedgerChannel.sol +++ b/contracts/LedgerChannel.sol @@ -48,6 +48,19 @@ contract LedgerChannel { uint256 updateLCtimeout ); + event DidLCWithdrawal( + bytes32 indexed lcID, + address withdrawalAddress, + uint256 sequence, + uint256 ethBalanceA, + uint256 ethBalanceI, + uint256 tokenBalanceA, + uint256 tokenBalanceI, + uint256 ethWithdrawalAmount, + uint256 tokenWithdrawalAmount + ); + + event DidLCClose ( bytes32 indexed channelId, uint256 sequence, @@ -234,6 +247,88 @@ contract LedgerChannel { emit DidLCDeposit(_lcID, recipient, _balance, isToken); } + // A consensus withdraw is essentially doing a consensus close for a partial amount and then opening a new channel with the remaining balances + // we are doing it while keeping the same channel by changing initialDeposit + + // We may want to change initialDeposit to a different variable name like lastCheckedInBalances + // multiple consensusWithdraws can happen offchain before submitting to chain as long as both parties remain smart about what the balances truly are. + + // if multiple consensus withdraws happen off chain before getting submitted to chain some issues happen when trying to batch them. + // for example: partyA withdraws, partyI then wishes to batch their own withdraw with patyA. But now partyI can submit their first withdrawal to chain and invalidate the 2nd one whenever they wish + // this would also invalidate all state updates after the first withdrawal. + function consensusWithdraw( + bytes32 _lcID, + uint256[7] withdrawParams, // [sequence, ethBalanceA, ethBalanceI, tokenBalanceA, tokenBalanceI, ethWithdrawal, tokenWithdrawal] + address _withdrawalAddress, + string _sigA, + string _sigI + ) + public + { + // first we make sure the channel is open and the balances all add up. This means that the consensusWithdraw must be submitted to chain before trying to closeChannel or + // both parties can ignore the consensusWithdraw and submit a consensusClose for the correct balances (batching together the withdraw and the close) + require(Channels[_lcID].isOpen == true); + require(Channels[_lcID].initialDeposit[0] + Channels[_lcID].ethBalances[2] + Channels[_lcID].ethBalances[3] == withdrawParams[1] + withdrawParams[2] + withdrawParams[5]); + require(Channels[_lcID].initialDeposit[1] + Channels[_lcID].erc20Balances[2] + Channels[_lcID].erc20Balances[3] == withdrawParams[3] + withdrawParams[4] + withdrawParams[6]); + + // prevent replay attacks + require(withdrawParams[0] > Channels[_lcID].sequence); + + // verify sigs + bytes32 _state = keccak256( + abi.encodePacked( + _lcID, + true, + withdrawParams[0], + // what are these 2? + uint256(0), + bytes32(0x0), + Channels[_lcID].partyAddresses[0], + Channels[_lcID].partyAddresses[1], + // withdrawParams[1], // commenting this out because it's determinstic still (balances must add up to total) and getting a stack too deep error + withdrawParams[2], + withdrawParams[3], + withdrawParams[4], + withdrawParams[5], + withdrawParams[6] + ) + ); + + require(Channels[_lcID].partyAddresses[0] == ECTools.recoverSigner(_state, _sigA)); + require(Channels[_lcID].partyAddresses[1] == ECTools.recoverSigner(_state, _sigI)); + + // transfer eth + if(withdrawParams[5] != 0) { + _withdrawalAddress.transfer(withdrawParams[5]); + } + + // transfer Tokens + if(withdrawParams[6] != 0) { + require(Channels[_lcID].token.transfer(_withdrawalAddress, withdrawParams[6]), "happyWithdrawlChannel: token transfer failure"); + } + + // update the new channel as if it just now got created with the current sequence + Channels[_lcID].sequence = withdrawParams[0]; + Channels[_lcID].initialDeposit = [withdrawParams[1] + withdrawParams[2], withdrawParams[3] + withdrawParams[4]]; + + + + Channels[_lcID].ethBalances = [withdrawParams[1], withdrawParams[2]]; + Channels[_lcID].erc20Balances = [withdrawParams[3], withdrawParams[4]]; + + emit DidLCWithdrawal( + _lcID, + _withdrawalAddress, + withdrawParams[0], + withdrawParams[1], + withdrawParams[2], + withdrawParams[3], + withdrawParams[4], + withdrawParams[5], + withdrawParams[6] + ); + } + // TODO: Check there are no open virtual channels, the client should have cought this before signing a close LC state update function consensusCloseChannel( bytes32 _lcID,