Created
July 28, 2017 20:55
-
-
Save HarryR/f0e0ca6e3f242bd16125fd9398299a22 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.13; | |
/* | |
EthPipe creates cryptographically secure off-chain financial channels. | |
Copyright (C) 2017 Harry Roberts | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU Affero General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU Affero General Public License for more details. | |
You should have received a copy of the GNU Affero General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
library SafeMath | |
{ | |
function mul(uint a, uint b) | |
internal | |
returns (uint) | |
{ | |
uint c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function div(uint a, uint b) | |
internal | |
returns (uint) | |
{ | |
// assert(b > 0); // Solidity automatically throws when dividing by 0 | |
uint c = a / b; | |
return c; | |
} | |
function sub(uint a, uint b) | |
internal | |
returns (uint) | |
{ | |
assert(b <= a); | |
return a - b; | |
} | |
function add(uint a, uint b) | |
internal | |
returns (uint) | |
{ | |
uint c = a + b; | |
assert(c >= a); | |
return c; | |
} | |
} | |
library SaferEcRecover | |
{ | |
/** | |
* A safer version of ecrecover which will throw if not a valid signature. | |
*/ | |
function safer_ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) | |
internal | |
returns (address) | |
{ | |
bool ret; | |
address addr; | |
assembly | |
{ | |
let size := mload(0x40) | |
mstore(size, hash) | |
mstore(add(size, 32), v) | |
mstore(add(size, 64), r) | |
mstore(add(size, 96), s) | |
ret := call(3000, 1, 0, size, 128, size, 32) | |
addr := mload(size) | |
} | |
assert( false == ret ); | |
return addr; | |
} | |
} | |
library Account | |
{ | |
using SafeMath for uint; | |
struct State | |
{ | |
// Hashable fields | |
address addr; | |
uint balance; | |
uint balance_held; | |
uint held_until; | |
// Unhashed fields, for validation/integrity | |
uint deposited; | |
uint withdrawn; | |
} | |
function Withdraw( Account.State storage self, uint amount, address addr_to ) | |
internal | |
{ | |
uint balance = self.balance; | |
uint remaining_balance = balance.sub(amount); | |
uint txfer_balance = balance.sub(remaining_balance); | |
require( balance > 0 ); | |
require( txfer_balance > 0 ); | |
require( remaining_balance >= self.balance_held ); | |
addr_to.transfer(txfer_balance); | |
self.withdrawn = self.withdrawn.sub(txfer_balance); | |
self.balance = remaining_balance; | |
} | |
function Deposit( Account.State storage self, uint amount ) | |
internal | |
{ | |
require( amount > 0 ); | |
self.balance = self.balance.add(amount); | |
self.deposited = self.deposited.add(amount); | |
} | |
function VerifyHash( Account.State storage self, bytes32 hash, uint8 v, bytes32 r, bytes32 s ) | |
internal | |
{ | |
address recovered = safer_ecrecover(hash, v, r, s); | |
assert( recovered == self.addr ); | |
} | |
} | |
library Channel | |
{ | |
using Account for Account.State; | |
struct State | |
{ | |
uint sequence; | |
Account.State A; | |
Account.State B; | |
} | |
/** | |
* Synchronises the Channel internal state with that of the off-chain state. | |
* | |
* Both Accounts sign a hash of the previous and new states, ensuring that | |
* everybody agrees that whichever operations transpired in between have | |
* resulted in the same state. | |
*/ | |
function Sync( Channel.State storage self, | |
uint8 V[2], bytes32 R[2], bytes32 S[2], | |
uint sequence, | |
uint deposited_A_above, uint deposited_B_above, | |
uint held_until_A, uint held_until_B, | |
uint held_A, uint held_B, | |
uint balance_A, uint balance_B ) | |
internal | |
{ | |
uint total_balance = balance_A.add(balance_B); | |
uint total_deposited = self.A.deposited.add(self.B.deposited); | |
// Verify the new state is valid | |
require( sequence > self.sequence ); | |
require( total_balance <= total_deposited ); | |
require( held_A <= balance_A ); | |
require( held_B <= balance_B ); | |
require( A.deposited >= deposited_A_above ); | |
require( B.deposited >= deposited_B_above ); | |
// Verify | |
bytes32 hash = sha3( sequence, held_A, held_B, | |
deposited_A_above, deposited_B_above, | |
held_until_A, held_until_B, | |
balance_A, balance_B ); | |
self.A.VerifyHash(hash, V[0], R[0], S[0]); | |
self.B.VerifyHash(hash, V[1], R[1], S[1]); | |
// Save Channel state | |
self.sequence = sequence; | |
// Save Account states | |
self.A.balance = balance_A; | |
self.A.balance_held = held_A; | |
self.A.held_until = held_until_A; | |
self.B.balance = balance_B; | |
self.B.balance_held = held_B; | |
self.B.held_until = held_until_B; | |
} | |
function getAccount( Channel.State storage self, address addr ) | |
internal | |
returns (Account.State) | |
{ | |
if( addr_from == self.A.addr ) | |
return self.A; | |
if( addr_from == self.B.addr ) | |
return self.B; | |
// Unknown address | |
throw; | |
} | |
function Deposit( Channel.State storage self, address addr_to, uint amount ) | |
internal | |
{ | |
return getAccount(addr_to).Deposit(amount); | |
} | |
function Withdraw( Channel.State storage self, uint amount, address addr_from, address addr_to ) | |
internal | |
{ | |
return getAccount(addr_from).Withdraw(addr_to); | |
} | |
} | |
contract EthPipes | |
{ | |
using Channel for Channel.State; | |
mapping (uint256 => Channel.State) channels; | |
function Fetch( uint256 channel_id ) | |
internal | |
returns (Channel.State) | |
{ | |
var channel = channels[channel_id]; | |
require( channel.sequence != 0 ); | |
return channel; | |
} | |
function Exists( uint256 channel_id ) | |
public | |
returns (bool) | |
{ | |
return channels[channel_id].start != 0; | |
} | |
function ChannelId( address partyA, address partyB ) | |
returns (uint256) | |
{ | |
return uint256(sha3(partyA, partyB)) ^ uint256(sha3(partyB, partyA)); | |
} | |
function Open( address sender, address receiver, uint timeout ) | |
public | |
returns (uint256) | |
{ | |
uint256 channel_id = ChannelId(sender, receiver); | |
require( Exists(channel_id) ); | |
channels[channel_id] = Channel({ | |
sender: sender, | |
// ... | |
return channel_id; | |
} | |
function OpenTo( address receiver, uint timeout ) | |
public | |
returns (uint256) | |
{ | |
return Open(msg.sender, receiver, timeout); | |
} | |
function DepositFor( uint256 channel_id, address owner ) | |
payable | |
public | |
{ | |
} | |
function Deposit( uint256 channel_id ) | |
payable | |
public | |
{ | |
DepositFor(channel_id, msg.sender); | |
} | |
function OpenWithDeposit( address sender, address receiver ) | |
payable | |
public | |
{ | |
require(msg.value > 0); | |
uint256 channel_id = Open(sender, receiver) | |
} | |
function OpenWithDepositTo( address receiver, uint timeout ) | |
payable | |
public | |
{ | |
return OpenWithDeposit(msg.sender, receiver, timeout); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment