Skip to content

Instantly share code, notes, and snippets.

@alex-miller-0
Created March 1, 2018 20:04
Show Gist options
  • Save alex-miller-0/23098de84dd4ee8632acc08cbf67b746 to your computer and use it in GitHub Desktop.
Save alex-miller-0/23098de84dd4ee8632acc08cbf67b746 to your computer and use it in GitHub Desktop.
Mockup of withdrawals in a UTXO plasma system
struct Deposit {
address to;
uint value;
}
struct Withdrawal {
uint status; // number (timestamp) = started, 2 = finished, 3 = failed
address owner;
uint value;
}
mapping (bytes32 => Deposit) deposits;
// Withdrawal codes: 0 = not started, 1 = started, 2 = successful, 3 = failed
mapping (bytes32 => Withdrawal) withdrawals;
uint waitPeriod = 100000;
function deposit() payable {
bytes32 depositId = keccak256(msg.value, msg.sender, block.header, now);
deposits[depositId].to = msg.sender;
deposits[depositId].value = msg.value;
}
// Start a withdrawal on a UTXO that has provenance attached
// data items include chunks of: [utxoId,to,amount, ... ]
// sigs items include chunks of: [v, r, s]
function beginWithdraw(bytes data, bytes sigs) {
// Make sure no one has started a withdrawal on this
assert(withdrawals[utxoId] == 0);
uint endAmount;
bytes32 endId;
address endRecipient;
// Check the provenance against provided signatures
for (uint i = 128; i < data.length; i += 128) {
bytes32 currentId = slice(data, i, i + 32);
address sender = slice(data, i - 64, i - 32); // The previous owner
address to = slice(data, i + 32, i + 64); // The new owner
bytes32 prevId = slice(data, i - 128, i - 96);
uint amount = slice(data, i + 64, i + 96);
// Get the previous hash, i.e. hash(id, to, amount)
bytes32 prevHash = keccak256(prevId, to, amount);
// If the previous hash is not the current id, it must be the change id
if (prevHash != currentId) {
// hash(id, from, amount)
bytes32 changeHash = keccak(prevId, sender, amount);
assert(changeHash == currentId);
endRecipient = sender;
} else {
endRecipient = to;
}
// Make sure the signer actually signed this data
// NOTE: The spender only signs data on the outgoing UTXO. This is used even for change UTXOs in provenance.
assert(ecrecover(prevHash, v, r, s) == sender);
endAmount = amount;
endId = currentId;
}
// Start the withdrawal
withdrawals[endId].value = endAmount;
withdrawals[endId].owner = endRecipient;
withdrawals[endId].status = now;
}
startWithdrawalOfDeposit(bytes32 depositId) {
assert(deposits[depositId].to == msg.sender);
assert(withdrawals[depositId].value == 0);
withdrawals[depositId].value = deposits[depositId].value;
withdrawals[depositId].owner = deposits[depositId].to;
withdrawals[depositId].status = now;
}
challengeWithdrawal(bytes32 utxoId, address to, uint value, bytes sig) {
assert(withdrawals[utxoId].amount > 0));
assert(withdrawals[utxoId].owner != address(0));
bytes32 h = keccak256(utxoId, to, value);
uint8 v = uint8(slice(sig.length - 96, sig.length - 64));
bytes32 r = bytes32(sig.length - 64, sig.length - 32));
bytes32 s = bytes32(sig.length - 32, sig.length));
assert(ecrecover(h, v, r, s) == withdrawals[utxoId].owner);
withdrawals[utxoId].status = 3;
}
finishWithdrawal(id) {
assert(withdrawals[id].owner == msg.sender);
assert(withdrawals[id].status != 2);
assert(withdrawals[id].status != 3);
assert(withdrawals[id].status + waitPeriod > now);
withdrawals[id].status = 2;
msg.sender.send(withdrawals[id].amount);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment