diff --git a/channel.sol b/channel.sol index 8bdb7f6..d8e56ba 100644 --- a/channel.sol +++ b/channel.sol @@ -1,51 +1,58 @@ -pragma solidity ^0.4.0; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; contract Channel { - - address public channelSender; - address public channelRecipient; - uint public startDate; - uint public channelTimeout; - mapping (bytes32 => address) signatures; - - function Channel(address to, uint timeout) payable { - channelRecipient = to; - channelSender = msg.sender; - startDate = now; - channelTimeout = timeout; - } - - function CloseChannel(bytes32 h, uint8 v, bytes32 r, bytes32 s, uint value){ - - address signer; - bytes32 proof; - - // get signer from signature - signer = ecrecover(h, v, r, s); - - // signature is invalid, throw - if (signer != channelSender && signer != channelRecipient) throw; - - proof = sha3(this, value); - - // signature is valid but doesn't match the data provided - if (proof != h) throw; - - if (signatures[proof] == 0) - signatures[proof] = signer; - else if (signatures[proof] != signer){ - // channel completed, both signatures provided - if (!channelRecipient.send(value)) throw; - selfdestruct(channelSender); - } - - } - - function ChannelTimeout(){ - if (startDate + channelTimeout > now) - throw; - - selfdestruct(channelSender); - } - + address public channelSender; + address public channelRecipient; + uint public startDate; + uint public channelDuration; + uint public channelMargin; + bytes32 public channelTip; + bool public isActive; + + constructor(address to, uint timeout, uint margin, bytes32 tip) payable { + channelRecipient = to; + channelSender = msg.sender; + startDate = block.timestamp; + channelDuration = timeout; + channelMargin = margin; + channelTip = tip; + isActive = true; + } + + modifier onlyActive() { + require(isActive, "Channel is not active."); + _; + } + + function closeChannel(bytes32 _word, uint8 _wordCount) public onlyActive { + require(msg.sender == channelRecipient, "Only the recipient can close the channel."); + bytes32 wordScratch = _word; + for (uint i = 1; i <= _wordCount; i++) { + wordScratch = keccak256(abi.encodePacked(wordScratch)); + } + + require(wordScratch == channelTip, "Invalid word or word count."); + (bool sent, ) = channelRecipient.call{value: _wordCount * channelMargin}(""); + require(sent, "Failed to send Ether"); + deactivate(); + } + + function expireChannel() public onlyActive { + require(block.timestamp >= startDate + channelDuration, "Channel timeout has not been reached."); + deactivate(); + } + + function deactivate() private { + isActive = false; + uint balance = address(this).balance; + if (balance > 0) { + (bool sent, ) = channelSender.call{value: balance}(""); + require(sent, "Failed to return remaining Ether to sender"); + } + } + + receive() external payable {} + + fallback() external payable {} }