Skip to content

Instantly share code, notes, and snippets.

@Georgi87
Last active February 2, 2018 20:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Georgi87/d94da8be70793eda7a327c82ab454b2b to your computer and use it in GitHub Desktop.
Save Georgi87/d94da8be70793eda7a327c82ab454b2b to your computer and use it in GitHub Desktop.
Generalized settlement for state channels using proxy contracts.
pragma solidity ^0.4.0;
contract Token {
function transfer(address to, uint256 value) returns (bool);
function transferFrom(address from, address to, uint256 value) returns (bool);
}
/// @title State Channel contract - Allows multiple parties to settle off-chain transactions.
/// @author Stefan George - <stefan.george@consensys.net>
contract StateChannelProxy {
address public owner;
address public stateChannel;
bool public isSettled = false;
modifier isOwnerOrStateChannel () {
if (!(msg.sender == stateChannel || isSettled && msg.sender == owner))
throw;
_;
}
modifier isStateChannel () {
if (msg.sender != stateChannel)
throw;
_;
}
function sendTransaction(address destination, uint value, bytes data) isOwnerOrStateChannel {
if (!destination.call.value(value)(data))
throw;
}
function setSettled() isStateChannel {
isSettled = true;
}
function StateChannelProxy(address _owner) {
owner = _owner;
stateChannel = msg.sender;
}
}
contract StateChannel {
mapping (address => StateChannelProxy) public proxyContracts;
address[] owners;
State public state;
Token public securityToken;
uint public securityValue;
uint public challengePeriod;
struct State {
uint timestamp;
uint nonce;
bytes32 hash;
address requester;
bytes32[] tradeHashes;
uint tradeIndex;
}
function requestSettleZeroState() {
if (state.timestamp > 0 || !securityToken.transferFrom(msg.sender, this, securityValue))
throw;
state.timestamp = block.timestamp;
state.requester = msg.sender;
}
function settleZeroState() {
if (!(state.timestamp > 0 && state.timestamp + challengePeriod <= block.timestamp && state.hash == 0)
|| !securityToken.transfer(msg.sender, securityValue))
throw;
for (uint i=0; i<owners.length; i++)
proxyContracts[owners[i]].setSettled();
}
function requestSettlement(bytes32 tradesHash, uint nonce, uint timestamp, bytes32 hash, bytes32 secret, uint8[] sigV, bytes32[] sigR, bytes32[] sigS) {
bytes32 stateHash = sha3(tradesHash, nonce, timestamp, hash);
for (uint i=0; i<owners.length; i++)
if (owners[i] != ecrecover(stateHash, sigV[i], sigR[i], sigS[i]))
throw;
if (state.timestamp > 0
|| block.timestamp > timestamp
|| hash > 0 && sha3(secret) != hash
|| !securityToken.transferFrom(msg.sender, this, securityValue))
throw;
state.timestamp = block.timestamp;
state.nonce = nonce;
state.hash = stateHash;
state.requester = msg.sender;
}
function submitTradeHashes(bytes32[] tradeHashes, uint nonce, uint timestamp, bytes32 hash) {
bytes32 tradesHash = sha3(tradeHashes);
bytes32 stateHash = sha3(tradesHash, nonce, timestamp, hash);
if (state.hash == stateHash && state.timestamp + challengePeriod <= block.timestamp)
state.tradeHashes = tradeHashes;
}
function executeTrade(address sender, address destination, uint value, bytes data) {
bytes32 tradeHash = sha3(sender, destination, value, data);
if (state.tradeHashes[state.tradeIndex] == tradeHash) {
StateChannelProxy(sender).sendTransaction(destination, value, data);
state.tradeIndex += 1;
if (state.tradeIndex == state.tradeHashes.length)
for (uint i=0; i<owners.length; i++)
proxyContracts[owners[i]].setSettled();
}
}
function punishWrongState(bytes32 tradeHash, uint nonce, uint timestamp, bytes32 hash, uint8[] sigV, bytes32[] sigR, bytes32[] sigS) {
bytes32 stateHash = sha3(tradeHash, nonce, timestamp, hash);
for (uint i=0; i<owners.length; i++)
if (owners[i] != ecrecover(stateHash, sigV[i], sigR[i], sigS[i]))
throw;
if (state.nonce > nonce
|| block.timestamp > timestamp
|| !securityToken.transfer(msg.sender, securityValue))
throw;
delete state;
}
function StateChannel(address[] _owners, address _securityToken, uint _securityValue, uint _challengePeriod) {
for (uint i=0; i<_owners.length; i++)
proxyContracts[_owners[i]] = new StateChannelProxy(_owners[i]);
owners = _owners;
securityToken = Token(_securityToken);
securityValue = _securityValue;
challengePeriod = _challengePeriod;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment