Skip to content

Instantly share code, notes, and snippets.

@cwhinfrey
Created December 21, 2018 17:09
Show Gist options
  • Save cwhinfrey/a700474f647ffed51c74ec5d6b76300b to your computer and use it in GitHub Desktop.
Save cwhinfrey/a700474f647ffed51c74ec5d6b76300b to your computer and use it in GitHub Desktop.
PaymentChannelPOC.sol
pragma solidity ^0.4.24;
library ECDSA {
/**
* @dev Recover signer address from a message by using their signature
* @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
* @param signature bytes signature, the signature is generated using web3.eth.sign()
*/
function recover(bytes32 hash, bytes signature) internal pure returns (address) {
bytes32 r;
bytes32 s;
uint8 v;
// Check the signature length
if (signature.length != 65) {
return (address(0));
}
// Divide the signature in r, s and v variables
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// Version of signature should be 27 or 28, but 0 and 1 are also possible versions
if (v < 27) {
v += 27;
}
// If the version is correct return the signer address
if (v != 27 && v != 28) {
return (address(0));
} else {
// solium-disable-next-line arg-overflow
return ecrecover(hash, v, r, s);
}
}
/**
* toEthSignedMessageHash
* @dev prefix a bytes32 value with "\x19Ethereum Signed Message:"
* and hash the result
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
contract PaymentChannel {
using ECDSA for bytes32;
uint256 public constant disputePeriod = 1 hours;
address public party1;
address public party2;
mapping(address => uint256) public balances;
uint256 public nonce;
int256 public delta;
bool public isClosed;
uint256 public disputePeriodEnd;
function() public payable {
// Only participants can send ETH
require(msg.sender == party1 || msg.sender == party2);
require(!isClosed);
// Add to balance
balances[msg.sender] += msg.value;
}
function openChannel(address _party1, address _party2) public payable {
require(party1 == address(0) && party2 == address(0));
// Set the two participants
party1 = _party1;
party2 = _party2;
// Add to balance is ETH was sent
balances[msg.sender] += msg.value;
}
function resolveChannel(uint256 _nonce, int256 _delta, bytes signature1, bytes signature2) public {
// TODO: Check signatures
// bytes32 messageHash = keccak256(abi.encodePacked(party1, party2, _nonce, _delta)).toEthSignedMessageHash();
// require(party1 == messageHash.recover(signature1));
// require(party2 == messageHash.recover(signature2));
require(_nonce >= nonce);
require(!isClosed);
//Start the dispute period if not started
if(disputePeriodEnd == 0) {
disputePeriodEnd = now + disputePeriod;
} else {
// Dispute period must not be over
require(disputePeriodEnd >= now);
}
// If nonce is the same, cooperatively close channel
if (_nonce == nonce) {
isClosed = true;
return;
}
// If nonce is greater, update the nonce and delta
nonce = _nonce;
delta = _delta;
}
function withdraw() public {
require(disputePeriodEnd != 0);
require(isClosed || now > disputePeriodEnd);
uint256 value;
if (msg.sender == party1) {
// Handle positive and negative deltas
if (delta >= 0) {
value = balances[party1] + uint256(delta);
} else {
value = balances[party1] - uint256(delta * -1);
}
party1.transfer(value);
} else if (msg.sender == party2) {
// Handle positive and negative deltas
// A more positive delta means less value for party 2
if (delta >= 0) {
value = balances[party2] - uint256(delta);
} else {
value = balances[party2] + uint256(delta * -1);
}
party2.transfer(value);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment