Created
March 1, 2018 20:04
-
-
Save alex-miller-0/23098de84dd4ee8632acc08cbf67b746 to your computer and use it in GitHub Desktop.
Mockup of withdrawals in a UTXO plasma system
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
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