[
  {
    "path": ".gitignore",
    "content": "node_modules\nnpm-debug.log\n.idea\nbuild/\n"
  },
  {
    "path": "README.md",
    "content": "# Simple plasma implementation\n\nPlasma consists of three major parts.\n1. Plasma chain: A simple proof-of-authority chain where the actual transactions take place.\n2. Plasma contract: A smart contract deployed on root chain which handles the deposits and withdrawals for the child chain (plasma chain).\n3. Ethereum blockchain: The root chain which only records the block headers of the plasma chain.\n\nThe complete cycle of interacting with plasma is made up of three stages.\n\n### Deposit\n\nParticipants deposit to the plasma contract on the root chain. Then the operator at the plasma chain will construct a deposit transaction according to the contract event when building a new block.\n\n### Transact\n\nParticipants could transact with each other on the plasma chain without notifying the root chain. Only when every block is created by the operator, it will submit the block header to the plasma contract on the root chain.\n\n### Withdraw\n\nA withdrawal is initiated by calling the plasma contract. After creating a withdrawal, user needs to wait 7 days for other participants to challenge it. If anyone could prove the given withdrawal that has been spent later on the plasma chain, the withdrawal will be canceled. Otherwise, after 7 days and without any other withdrawals with higher priority, user could withdraw his funds back to the root chain.\n\n## Prerequisite\n\n1. [Truffle](http://truffleframework.com/): An Ethereum development framework which helps us compiling, deploying, and interacting with smart contract.\n2. Testrpc: A test Ethereum RPC client for fast development. Here, we use [ganache-cli](https://github.com/trufflesuite/ganache-cli). If you prefer a GUI version, you could replace it with [ganache](http://truffleframework.com/ganache/). Note that you could also launch an Ethereum private chain (geth) to replace testrpc.\n\n## Run\n1. Install dependency\n```\nnpm install\n```\n2. Run ganache\n```\nganache-cli\n```\nTestrpc will generate ten default accounts for us. For convenience, you could specify a HD wallet mnemonic to get fixed addresses. For example:\n```\nganache-cli -m pink two example move shop length clean crop cheese tent strike field\n```\nThe corresponding initial addresses are:\n```\n(0) 0x0bf5f0f213b0b752858e9352fd6081f5d730dc17\n(1) 0x87dbd8ab1bd9d4fce07db12743594a5f456435ff\n(2) 0x3b0ba3134ac12cc065d4dba498a60cba5ef16098\n(3) 0x6c7f749d0e21aa6478af8e7adc362a8bf76be826\n(4) 0x9a404f89ad853e592d7b48242b4745c17a8ee852\n(5) 0x34d5e94fc3e7ecac3859a03176cb57534f30b71c\n(6) 0x8c0e1d37680a03eeb4c85880ffe1edf61ffa76f4\n(7) 0x6017db4acdfed284da94485d715a0f758048ac0b\n(8) 0x239ddceb1d7cebf07435a52f569b86f310af9cad\n(9) 0x1166d1f78f44c79e8e3f0d74419940e521790d7c\n```\n3. Compile contracts\n```\ntruffle compile\n```\n4. Deploy contracts\n```\ntruffle migrate\n```\nIf you need to deploy contracts on this testrpc again, don't forget to add the `--reset` argument.\n\n5. Set the contract configuration (config.js).\n    1. After deploying contracts, fill in the `PlasmaChainManager` contract address.\n    2. Choose one of the initial addresses as the operator address, for example, `0x87dbd8ab1bd9d4fce07db12743594a5f456435ff`.\n6. Run the plasma chain.\n```\nnpm start\n```\nor\n```\nnode main.js [options]\n```\nthe available options are:\n\n|Option|Description|\n|---|---|\n|--port|Specify HTTP API port, default 3001|\n|--contract|Specify contract address, otherwise use value in config.js|\n|--operator|Specify operator address, otherwise use value in config.js|\n\n## Testing\n\n1. Run ganache\n```\nganache-cli\n```\n\n2. Run tests\n```\ntruffle test\n```\n\n## HTTP API\n### Block related\n#### Get blockchain\nGet the whole blockchain.\n##### Parameter\nNone\n##### Sample\n```\ncurl http://localhost:3001/blocks\n```\n#### Mine blocks\nMiner mines a new block.\n##### Parameter\nNone\n##### Sample\n```\ncurl -X POST http://localhost:3001/mineBlock\n```\n\n### Transaction related\n#### Create a transaction\nCreate a transaction to other participants. User could specify at most two UTXOs to spend. Also note that the units used in field `amount` is ether.\n##### Parameter\n|Name|Type|Required|Description|\n|---|---|---|---|\n|from|Address|Yes|Transfer funds from whom|\n|to|Address|Yes|Transfer funds to whom|\n|amount|Decimal|Yes|How much ether (in ether)|\n##### Sample\n```\ncurl -H \"Content-type:application/json\" --data '{\"from\": \"0x6C7f749d0E21aA6478aF8e7Adc362a8bF76Be826\", \"to\": \"0x3B0bA3134Ac12Cc065d4dBa498a60cba5Ef16098\", \"amount\": 2}' http://localhost:3001/transact\n```\n\n### Deposit related\n#### Deposit\nDeposit funds to Plasma smart contract.\n##### Parameter\n|Name|Type|Required|Description|\n|---|---|---|---|\n|address|Address|Yes|Deposit from whom|\n|amount|Integer|Yes|How much funds to deposit|\n##### Sample\n```\ncurl -H \"Content-type:application/json\" --data '{\"address\": \"0x6C7f749d0E21aA6478aF8e7Adc362a8bF76Be826\", \"amount\": 4}' http://localhost:3001/deposit\n```\n\n### Withdrawal related\n#### Create withdrawal\nCreate a new withdrawal.\n##### Parameter\n|Name|Type|Required|Description|\n|---|---|---|---|\n|blkNum|Integer|Yes|The position of the UTXO user wants to withdraw|\n|txIndex|Integer|Yes|The position of the UTXO user wants to withdraw|\n|oIndex|Integer|Yes|The position of the UTXO user wants to withdraw|\n|from|Address|Yes|The owner of the UTXO|\n##### Sample\n```\ncurl -H \"Content-type:application/json\" --data '{\"blkNum\": 3, \"txIndex\": 1, \"oIndex\": 0, \"from\": \"0x6C7f749d0E21aA6478aF8e7Adc362a8bF76Be826\"}' http://localhost:3001/withdraw/create\n```\n#### Challenge withdrawal\nCreate a withdrawal challenge.\n##### Parameter\n|Name|Type|Required|Description|\n|---|---|---|---|\n|withdrawalId|Integer|Yes|The withdrawal ID user wants to challenge|\n|blkNum|Integer|Yes|The position of the UTXO user wants to challenge|\n|txIndex|Integer|Yes|The position of the UTXO user wants to challenge|\n|oIndex|Integer|Yes|The position of the UTXO user wants to challenge|\n|from|Address|Yes|The owner of the UTXO|\n```\ncurl -H \"Content-type:application/json\" --data '{\"withdrawalId\": 4000000000, \"blkNum\": 4, \"txIndex\": 2, \"oIndex\": 1, \"from\": \"0x6C7f749d0E21aA6478aF8e7Adc362a8bF76Be826\"}' http://localhost:3001/withdraw/challenge\n```\n#### Finalize withdrawal\nFinalize withdrawals manually.\n##### Parameter\n|Name|Type|Required|Description|\n|---|---|---|---|\n|from|Address|Yes|Who initiates the withdrawal finalization|\n##### Sample\n```\ncurl -H \"Content-type:application/json\" --data '{\"from\": \"0x6C7f749d0E21aA6478aF8e7Adc362a8bF76Be826\"}' http://localhost:3001/withdraw/finalize\n```\n"
  },
  {
    "path": "block.js",
    "content": "'use strict';\n\nconst crypto = require('crypto');\n\nconst tx = require(\"./transaction\");\nconst utils = require(\"./utils\");\n\nconst Merkle = require(\"./merkle\");\n\nclass Block {\n    constructor(blockNumber, previousHash, transactions) {\n        let data = [];\n        transactions.forEach(tx => data.push(tx.toString(true)));\n\n        this.blockHeader = new BlockHeader(blockNumber, previousHash, data);\n        this.transactions = transactions;\n    }\n\n    get hash() {\n        return crypto.createHash('sha256').update(this.toString()).digest('hex');\n    }\n\n    toString() {\n        let txsHex = \"\";\n        this.transactions.forEach(tx => txsHex += tx);\n        return this.blockHeader.toString(true) + txsHex;\n    }\n\n    printBlock() {\n        return {\n            'blockNumber': this.blockHeader.blockNumber,\n            'previousHash': this.blockHeader.previousHash,\n            'merkleRoot': this.blockHeader.merkleRoot,\n            'signature': this.blockHeader.sigR + this.blockHeader.sigS + this.blockHeader.sigV,\n            'transactions': this.transactions.filter(tx => tx.length > 0)\n        };\n    }\n}\n\nclass BlockHeader {\n    constructor(blockNumber, previousHash, data) {\n        this.blockNumber = blockNumber;  // 32 bytes\n        this.previousHash = previousHash;  // 32 bytes\n        if (blockNumber == 0) {\n            this.merkle = null;\n            this.merkleRoot = \"\";\n        } else {\n            this.merkle = new Merkle(data);\n            this.merkle.makeTree();\n            this.merkleRoot = utils.bufferToHex(this.merkle.getRoot(), false);  // 32 bytes\n        }\n        this.sigR = '';  // 32 bytes\n        this.sigS = '';  // 32 bytes\n        this.sigV = '';  // 1 byte\n    }\n\n    setSignature(signature) {\n        let sig = utils.removeHexPrefix(signature);\n        let sigR = sig.substring(0, 64);\n        let sigS = sig.substring(64, 128);\n        let sigV = parseInt(sig.substring(128, 130), 16);\n        if (sigV < 27) {\n            sigV += 27;\n        }\n        this.sigR = sigR;\n        this.sigS = sigS;\n        this.sigV = sigV.toString(16).padStart(2, \"0\");\n    }\n\n    toString(includingSig) {\n        let blkNumHexString = this.blockNumber.toString(16).padStart(64, \"0\");\n        let rawBlockHeader = blkNumHexString + this.previousHash + this.merkleRoot;\n        if (includingSig) {\n            rawBlockHeader += this.sigR + this.sigS + this.sigV;\n        }\n        return rawBlockHeader;\n    }\n}\n\nconst getGenesisBlock = () => {\n    // Create a hard coded genesis block.\n    return new Block(0, '46182d20ccd7006058f3e801a1ff3de78b740b557bba686ced70f8e3d8a009a6', []);\n};\n\nlet blockchain = [getGenesisBlock()];\n\nconst generateNextBlock = async (geth) => {\n    let previousBlock = getLatestBlock();\n    let previousHash = previousBlock.hash;\n    let nextIndex = previousBlock.blockHeader.blockNumber + 1;\n\n    // Query contract past event for deposits / withdrawals and collect transactions.\n    let deposits = await geth.getDeposits(nextIndex - 1);\n    let withdrawals = await geth.getWithdrawals(nextIndex - 1);\n    let transactions = await tx.collectTransactions(nextIndex, deposits, withdrawals);\n    let newBlock = new Block(nextIndex, previousHash, transactions);\n\n    // Operator signs the new block.\n    let messageToSign = utils.addHexPrefix(newBlock.blockHeader.toString(false));\n    let signature = await geth.signBlock(messageToSign);\n    newBlock.blockHeader.setSignature(signature);\n\n    // Submit the block header to plasma contract.\n    let hexPrefixHeader = utils.addHexPrefix(newBlock.blockHeader.toString(true));\n    await geth.submitBlockHeader(hexPrefixHeader);\n\n    // Add the new block to blockchain.\n    console.log('New block added.');\n    console.log(newBlock.printBlock());\n    blockchain.push(newBlock);\n\n    return newBlock;\n};\n\nconst getTransactionProofInBlock = (blockNumber, txIndex) => {\n    let block = getBlock(blockNumber);\n    let tx = utils.addHexPrefix(block.transactions[txIndex]);\n    let proof = utils.bufferToHex(Buffer.concat(block.blockHeader.merkle.getProof(txIndex)), true);\n    return {\n        root: block.blockHeader.merkleRoot,\n        tx: tx,\n        proof: proof\n    };\n};\n\nconst getLatestBlock = () => blockchain[blockchain.length - 1];\nconst getBlocks = () => blockchain;\nconst getBlock = (index) => blockchain[index];\n\nmodule.exports = {getLatestBlock, getBlocks, generateNextBlock,\n    getTransactionProofInBlock};\n"
  },
  {
    "path": "config.js",
    "content": "module.exports = {\n    get plasmaContractAddress() {\n        return \"\";\n    },\n    get plasmaOperatorAddress() {\n        return \"\";\n    }\n};\n"
  },
  {
    "path": "contracts/ArrayLib.sol",
    "content": "pragma solidity ^0.4.19;\n\nlibrary ArrayLib {\n    function remove(uint256[] storage _array, uint256 _value)\n        internal\n        returns (bool success)\n    {\n        int256 index = indexOf(_array, _value);\n        if (index == -1) {\n            return false;\n        }\n        uint256 lastElement = _array[_array.length - 1];\n        _array[uint256(index)] = lastElement;\n\n        delete _array[_array.length - 1];\n        _array.length -= 1;\n        return true;\n    }\n\n    function indexOf(uint256[] _array, uint256 _value)\n        internal\n        pure\n        returns(int256 index)\n    {\n        for (uint256 i = 0; i < _array.length; i++) {\n            if (_array[i] == _value) {\n                return int256(i);\n            }\n        }\n        return -1;\n    }\n}\n"
  },
  {
    "path": "contracts/Migrations.sol",
    "content": "pragma solidity ^0.4.17;\n\ncontract Migrations {\n  address public owner;\n  uint public last_completed_migration;\n\n  modifier restricted() {\n    if (msg.sender == owner) _;\n  }\n\n  function Migrations() public {\n    owner = msg.sender;\n  }\n\n  function setCompleted(uint completed) public restricted {\n    last_completed_migration = completed;\n  }\n\n  function upgrade(address new_address) public restricted {\n    Migrations upgraded = Migrations(new_address);\n    upgraded.setCompleted(last_completed_migration);\n  }\n}\n"
  },
  {
    "path": "contracts/MinHeapLib.sol",
    "content": "pragma solidity ^0.4.19;\n\nlibrary MinHeapLib {\n    struct Heap {\n        uint256[] data;\n    }\n\n    function add(Heap storage _heap, uint256 value) internal {\n        uint index = _heap.data.length;\n        _heap.data.length += 1;\n        _heap.data[index] = value;\n\n        // Fix the min heap if it is violated.\n        while (index != 0 && _heap.data[index] < _heap.data[(index - 1) / 2]) {\n            uint256 temp = _heap.data[index];\n            _heap.data[index] = _heap.data[(index - 1) / 2];\n            _heap.data[(index - 1) / 2] = temp;\n            index = (index - 1) / 2;\n        }\n    }\n\n    function peek(Heap storage _heap) view internal returns (uint256 value) {\n        require(_heap.data.length > 0);\n        return _heap.data[0];\n    }\n\n    function pop(Heap storage _heap) internal returns (uint256 value) {\n        require(_heap.data.length > 0);\n        uint256 root = _heap.data[0];\n        _heap.data[0] = _heap.data[_heap.data.length - 1];\n        _heap.data.length -= 1;\n        heapify(_heap, 0);\n        return root;\n    }\n\n    function heapify(Heap storage _heap, uint i) internal {\n        uint left = 2 * i + 1;\n        uint right = 2 * i + 2;\n        uint smallest = i;\n        if (left < _heap.data.length && _heap.data[left] < _heap.data[i]) {\n            smallest = left;\n        }\n        if (right < _heap.data.length && _heap.data[right] < _heap.data[smallest]) {\n            smallest = right;\n        }\n        if (smallest != i) {\n            uint256 temp = _heap.data[i];\n            _heap.data[i] = _heap.data[smallest];\n            _heap.data[smallest] = temp;\n            heapify(_heap, smallest);\n        }\n    }\n\n    function isEmpty(Heap storage _heap) view internal returns (bool empty) {\n        return _heap.data.length == 0;\n    }\n}\n"
  },
  {
    "path": "contracts/PlasmaChainManager.sol",
    "content": "pragma solidity ^0.4.19;\nimport './RLP.sol';\nimport './MinHeapLib.sol';\nimport './ArrayLib.sol';\n\ncontract PlasmaChainManager {\n    using ArrayLib for uint256[];\n    using RLP for bytes;\n    using RLP for RLP.RLPItem;\n    using RLP for RLP.Iterator;\n    using MinHeapLib for MinHeapLib.Heap;\n\n    bytes constant PersonalMessagePrefixBytes = \"\\x19Ethereum Signed Message:\\n96\";\n    uint32 constant blockHeaderLength = 161;\n\n    uint256 exitAgeOffset;\n    uint256 exitWaitOffset;\n\n    struct BlockHeader {\n        uint256 blockNumber;\n        bytes32 previousHash;\n        bytes32 merkleRoot;\n        bytes32 r;\n        bytes32 s;\n        uint8 v;\n        uint256 timeSubmitted;\n    }\n\n    struct DepositRecord {\n        uint256 blockNumber;\n        uint256 txIndex;\n        address depositor;\n        uint256 amount;\n        uint256 timeCreated;\n    }\n\n    struct WithdrawRecord {\n        uint256 blockNumber;\n        uint256 txIndex;\n        uint256 oIndex;\n        address beneficiary;\n        uint256 amount;\n        uint256 priority;\n    }\n\n    address public owner;\n    uint256 public lastBlockNumber;\n    uint256 public txCounter;\n    mapping(uint256 => BlockHeader) public headers;\n    mapping(address => DepositRecord[]) public depositRecords;\n    mapping(uint256 => uint256[]) public withdrawalIds;\n    mapping(uint256 => WithdrawRecord) public withdrawRecords;\n    MinHeapLib.Heap exits;\n\n    function PlasmaChainManager(uint256 exitAge, uint256 exitWait) public {\n        owner = msg.sender;\n        lastBlockNumber = 0;\n        txCounter = 0;\n        exitAgeOffset = exitAge;\n        exitWaitOffset = exitWait;\n    }\n\n    event HeaderSubmittedEvent(address signer, uint32 blockNumber);\n\n    function submitBlockHeader(bytes header) public returns (bool success) {\n        require(header.length == blockHeaderLength);\n\n        bytes32 blockNumber;\n        bytes32 previousHash;\n        bytes32 merkleRoot;\n        bytes32 sigR;\n        bytes32 sigS;\n        bytes1 sigV;\n        assembly {\n            let data := add(header, 0x20)\n            blockNumber := mload(data)\n            previousHash := mload(add(data, 32))\n            merkleRoot := mload(add(data, 64))\n            sigR := mload(add(data, 96))\n            sigS := mload(add(data, 128))\n            sigV := mload(add(data, 160))\n            if lt(sigV, 27) { sigV := add(sigV, 27) }\n        }\n\n        // Check the block number.\n        require(uint8(blockNumber) == lastBlockNumber + 1);\n\n        // Check the signature.\n        bytes32 blockHash = keccak256(PersonalMessagePrefixBytes, blockNumber,\n            previousHash, merkleRoot);\n        address signer = ecrecover(blockHash, uint8(sigV), sigR, sigS);\n        require(msg.sender == signer);\n\n        // Append the new header.\n        BlockHeader memory newHeader = BlockHeader({\n            blockNumber: uint8(blockNumber),\n            previousHash: previousHash,\n            merkleRoot: merkleRoot,\n            r: sigR,\n            s: sigS,\n            v: uint8(sigV),\n            timeSubmitted: now\n        });\n        headers[uint8(blockNumber)] = newHeader;\n\n        // Increment the block number by 1 and reset the transaction counter.\n        lastBlockNumber += 1;\n        txCounter = 0;\n\n        HeaderSubmittedEvent(signer, uint8(blockNumber));\n        return true;\n    }\n\n    event DepositEvent(address from, uint256 amount,\n        uint256 indexed blockNumber, uint256 txIndex);\n\n    function deposit() payable public returns (bool success) {\n        DepositRecord memory newDeposit = DepositRecord({\n            blockNumber: lastBlockNumber,\n            txIndex: txCounter,\n            depositor: msg.sender,\n            amount: msg.value,\n            timeCreated: now\n        });\n        depositRecords[msg.sender].push(newDeposit);\n        txCounter += 1;\n        DepositEvent(msg.sender, msg.value, newDeposit.blockNumber,\n            newDeposit.txIndex);\n        return true;\n    }\n\n    event WithdrawalStartedEvent(uint256 withdrawalId);\n\n    function startWithdrawal(\n        uint256 blockNumber,\n        uint256 txIndex,\n        uint256 oIndex,\n        bytes targetTx,\n        bytes proof\n    )\n        public\n        returns (uint256 withdrawalId)\n    {\n        BlockHeader memory header = headers[blockNumber];\n        require(header.blockNumber > 0);\n\n        var txList = targetTx.toRLPItem().toList();\n        require(txList.length == 13);\n\n        // Check if the target transaction is in the block.\n        require(isValidProof(header.merkleRoot, targetTx, proof));\n\n        // Check if the transaction owner is the sender.\n        address txOwner = txList[6 + 2 * oIndex].toAddress();\n        require(txOwner == msg.sender);\n\n        // Generate a new withdrawal ID.\n        uint256 priority = max(header.timeSubmitted, now - exitAgeOffset);\n        withdrawalId = blockNumber * 1000000 + txIndex * 1000 + oIndex;\n        WithdrawRecord storage record = withdrawRecords[withdrawalId];\n        require(record.blockNumber == 0);\n\n        // Construct a new withdrawal.\n        record.blockNumber = blockNumber;\n        record.txIndex = txIndex;\n        record.oIndex = oIndex;\n        record.beneficiary = txOwner;\n        record.amount = txList[7 + 2 * oIndex].toUint();\n        record.priority = priority;\n\n        exits.add(priority);\n        withdrawalIds[priority].push(withdrawalId);\n\n        WithdrawalStartedEvent(withdrawalId);\n        return withdrawalId;\n    }\n\n    event WithdrawalChallengedEvent(uint256 withdrawalId);\n\n    function challengeWithdrawal(\n        uint256 withdrawalId,\n        uint256 blockNumber,\n        uint256 txIndex,\n        uint256 oIndex,\n        bytes targetTx,\n        bytes proof\n    )\n        public\n        returns (bool success)\n    {\n        BlockHeader memory header = headers[blockNumber];\n        require(header.blockNumber > 0);\n\n        var txList = targetTx.toRLPItem().toList();\n        require(txList.length == 13);\n\n        // Check if the transaction is in the block.\n        require(isValidProof(header.merkleRoot, targetTx, proof));\n\n        // Check if the withdrawal exists.\n        WithdrawRecord memory record = withdrawRecords[withdrawalId];\n        require(record.blockNumber > 0);\n\n        // The transaction spends the given withdrawal on plasma chain.\n        if (isWithdrawalSpent(targetTx, record)) {\n            withdrawalIds[record.priority].remove(withdrawalId);\n            delete withdrawRecords[withdrawalId];\n\n            WithdrawalChallengedEvent(withdrawalId);\n            return true;\n        }\n\n        return false;\n    }\n\n    event WithdrawalCompleteEvent(uint256 indexed blockNumber,\n        uint256 exitBlockNumber, uint256 exitTxIndex, uint256 exitOIndex);\n\n    function finalizeWithdrawal() public returns (bool success) {\n        while (!exits.isEmpty() && now > exits.peek() + exitWaitOffset) {\n            uint256 priority = exits.pop();\n            for (uint256 i = 0; i < withdrawalIds[priority].length; i++) {\n                uint256 index = withdrawalIds[priority][i];\n                WithdrawRecord memory record = withdrawRecords[index];\n                record.beneficiary.transfer(record.amount);\n\n                WithdrawalCompleteEvent(lastBlockNumber, record.blockNumber,\n                    record.txIndex, record.oIndex);\n                delete withdrawRecords[index];\n            }\n            delete withdrawalIds[priority];\n        }\n        return true;\n    }\n\n    function isValidProof(bytes32 root, bytes target, bytes proof)\n        pure\n        internal\n        returns (bool valid)\n    {\n        bytes32 hash = keccak256(target);\n        for (uint i = 32; i < proof.length; i += 33) {\n            bytes1 flag;\n            bytes32 sibling;\n            assembly {\n                flag := mload(add(proof, i))\n                sibling := mload(add(add(proof, i), 1))\n            }\n            if (flag == 0) {\n                hash = keccak256(sibling, hash);\n            } else if (flag == 1) {\n                hash = keccak256(hash, sibling);\n            }\n        }\n        return hash == root;\n    }\n\n    function max(uint256 a, uint256 b) pure internal returns (uint256 result) {\n        return (a > b) ? a : b;\n    }\n\n    function isWithdrawalSpent(bytes targetTx, WithdrawRecord record)\n        view\n        internal\n        returns (bool spent)\n    {\n        var txList = targetTx.toRLPItem().toList();\n        require(txList.length == 13);\n\n        // Check two inputs individually if it spent the given withdrawal.\n        for (uint256 i = 0; i < 2; i++) {\n            if (!txList[3 * i].isEmpty()) {\n                uint256 blockNumber = txList[3 * i].toUint();\n                // RLP will encode integer 0 to 0x80 just like empty content...\n                uint256 txIndex = txList[3 * i + 1].isEmpty() ? 0 : txList[3 * i + 1].toUint();\n                uint256 oIndex = txList[3 * i + 2].isEmpty() ? 0 : txList[3 * i + 2].toUint();\n                if (record.blockNumber == blockNumber &&\n                    record.txIndex == txIndex &&\n                    record.oIndex == oIndex) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "contracts/RLP.sol",
    "content": "pragma solidity ^0.4.19;\n\n/**\n* @title RLPReader\n*\n* RLPReader is used to read and parse RLP encoded data in memory.\n*\n* @author Andreas Olofsson (androlo1980@gmail.com)\n*/\nlibrary RLP {\n\n uint constant DATA_SHORT_START = 0x80;\n uint constant DATA_LONG_START = 0xB8;\n uint constant LIST_SHORT_START = 0xC0;\n uint constant LIST_LONG_START = 0xF8;\n\n uint constant DATA_LONG_OFFSET = 0xB7;\n uint constant LIST_LONG_OFFSET = 0xF7;\n\n\n struct RLPItem {\n     uint _unsafe_memPtr;    // Pointer to the RLP-encoded bytes.\n     uint _unsafe_length;    // Number of bytes. This is the full length of the string.\n }\n\n struct Iterator {\n     RLPItem _unsafe_item;   // Item that's being iterated over.\n     uint _unsafe_nextPtr;   // Position of the next item in the list.\n }\n\n /* Iterator */\n\n function next(Iterator memory self) internal constant returns (RLPItem memory subItem) {\n     if(hasNext(self)) {\n         var ptr = self._unsafe_nextPtr;\n         var itemLength = _itemLength(ptr);\n         subItem._unsafe_memPtr = ptr;\n         subItem._unsafe_length = itemLength;\n         self._unsafe_nextPtr = ptr + itemLength;\n     }\n     else\n         throw;\n }\n\n function next(Iterator memory self, bool strict) internal constant returns (RLPItem memory subItem) {\n     subItem = next(self);\n     if(strict && !_validate(subItem))\n         throw;\n     return;\n }\n\n function hasNext(Iterator memory self) internal constant returns (bool) {\n     var item = self._unsafe_item;\n     return self._unsafe_nextPtr < item._unsafe_memPtr + item._unsafe_length;\n }\n\n /* RLPItem */\n\n /// @dev Creates an RLPItem from an array of RLP encoded bytes.\n /// @param self The RLP encoded bytes.\n /// @return An RLPItem\n function toRLPItem(bytes memory self) internal constant returns (RLPItem memory) {\n     uint len = self.length;\n     if (len == 0) {\n         return RLPItem(0, 0);\n     }\n     uint memPtr;\n     assembly {\n         memPtr := add(self, 0x20)\n     }\n     return RLPItem(memPtr, len);\n }\n\n /// @dev Creates an RLPItem from an array of RLP encoded bytes.\n /// @param self The RLP encoded bytes.\n /// @param strict Will throw if the data is not RLP encoded.\n /// @return An RLPItem\n function toRLPItem(bytes memory self, bool strict) internal constant returns (RLPItem memory) {\n     var item = toRLPItem(self);\n     if(strict) {\n         uint len = self.length;\n         if(_payloadOffset(item) > len)\n             throw;\n         if(_itemLength(item._unsafe_memPtr) != len)\n             throw;\n         if(!_validate(item))\n             throw;\n     }\n     return item;\n }\n\n /// @dev Check if the RLP item is null.\n /// @param self The RLP item.\n /// @return 'true' if the item is null.\n function isNull(RLPItem memory self) internal constant returns (bool ret) {\n     return self._unsafe_length == 0;\n }\n\n /// @dev Check if the RLP item is a list.\n /// @param self The RLP item.\n /// @return 'true' if the item is a list.\n function isList(RLPItem memory self) internal constant returns (bool ret) {\n     if (self._unsafe_length == 0)\n         return false;\n     uint memPtr = self._unsafe_memPtr;\n     assembly {\n         ret := iszero(lt(byte(0, mload(memPtr)), 0xC0))\n     }\n }\n\n /// @dev Check if the RLP item is data.\n /// @param self The RLP item.\n /// @return 'true' if the item is data.\n function isData(RLPItem memory self) internal constant returns (bool ret) {\n     if (self._unsafe_length == 0)\n         return false;\n     uint memPtr = self._unsafe_memPtr;\n     assembly {\n         ret := lt(byte(0, mload(memPtr)), 0xC0)\n     }\n }\n\n /// @dev Check if the RLP item is empty (string or list).\n /// @param self The RLP item.\n /// @return 'true' if the item is null.\n function isEmpty(RLPItem memory self) internal constant returns (bool ret) {\n     if(isNull(self))\n         return false;\n     uint b0;\n     uint memPtr = self._unsafe_memPtr;\n     assembly {\n         b0 := byte(0, mload(memPtr))\n     }\n     return (b0 == DATA_SHORT_START || b0 == LIST_SHORT_START);\n }\n\n /// @dev Get the number of items in an RLP encoded list.\n /// @param self The RLP item.\n /// @return The number of items.\n function items(RLPItem memory self) internal constant returns (uint) {\n     if (!isList(self))\n         return 0;\n     uint b0;\n     uint memPtr = self._unsafe_memPtr;\n     assembly {\n         b0 := byte(0, mload(memPtr))\n     }\n     uint pos = memPtr + _payloadOffset(self);\n     uint last = memPtr + self._unsafe_length - 1;\n     uint itms;\n     while(pos <= last) {\n         pos += _itemLength(pos);\n         itms++;\n     }\n     return itms;\n }\n\n /// @dev Create an iterator.\n /// @param self The RLP item.\n /// @return An 'Iterator' over the item.\n function iterator(RLPItem memory self) internal constant returns (Iterator memory it) {\n     if (!isList(self))\n         throw;\n     uint ptr = self._unsafe_memPtr + _payloadOffset(self);\n     it._unsafe_item = self;\n     it._unsafe_nextPtr = ptr;\n }\n\n /// @dev Return the RLP encoded bytes.\n /// @param self The RLPItem.\n /// @return The bytes.\n function toBytes(RLPItem memory self) internal constant returns (bytes memory bts) {\n     var len = self._unsafe_length;\n     if (len == 0)\n         return;\n     bts = new bytes(len);\n     _copyToBytes(self._unsafe_memPtr, bts, len);\n }\n\n /// @dev Decode an RLPItem into bytes. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toData(RLPItem memory self) internal constant returns (bytes memory bts) {\n     if(!isData(self))\n         throw;\n     var (rStartPos, len) = _decode(self);\n     bts = new bytes(len);\n     _copyToBytes(rStartPos, bts, len);\n }\n\n /// @dev Get the list of sub-items from an RLP encoded list.\n /// Warning: This is inefficient, as it requires that the list is read twice.\n /// @param self The RLP item.\n /// @return Array of RLPItems.\n function toList(RLPItem memory self) internal constant returns (RLPItem[] memory list) {\n     if(!isList(self))\n         throw;\n     var numItems = items(self);\n     list = new RLPItem[](numItems);\n     var it = iterator(self);\n     uint idx;\n     while(hasNext(it)) {\n         list[idx] = next(it);\n         idx++;\n     }\n }\n\n /// @dev Decode an RLPItem into an ascii string. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toAscii(RLPItem memory self) internal constant returns (string memory str) {\n     if(!isData(self))\n         throw;\n     var (rStartPos, len) = _decode(self);\n     bytes memory bts = new bytes(len);\n     _copyToBytes(rStartPos, bts, len);\n     str = string(bts);\n }\n\n /// @dev Decode an RLPItem into a uint. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toUint(RLPItem memory self) internal constant returns (uint data) {\n     if(!isData(self))\n         throw;\n     var (rStartPos, len) = _decode(self);\n     if (len > 32 || len == 0)\n         throw;\n     assembly {\n         data := div(mload(rStartPos), exp(256, sub(32, len)))\n     }\n }\n\n /// @dev Decode an RLPItem into a boolean. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toBool(RLPItem memory self) internal constant returns (bool data) {\n     if(!isData(self))\n         throw;\n     var (rStartPos, len) = _decode(self);\n     if (len != 1)\n         throw;\n     uint temp;\n     assembly {\n         temp := byte(0, mload(rStartPos))\n     }\n     if (temp > 1)\n         throw;\n     return temp == 1 ? true : false;\n }\n\n /// @dev Decode an RLPItem into a byte. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toByte(RLPItem memory self) internal constant returns (byte data) {\n     if(!isData(self))\n         throw;\n     var (rStartPos, len) = _decode(self);\n     if (len != 1)\n         throw;\n     uint temp;\n     assembly {\n         temp := byte(0, mload(rStartPos))\n     }\n     return byte(temp);\n }\n\n /// @dev Decode an RLPItem into an int. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toInt(RLPItem memory self) internal constant returns (int data) {\n     return int(toUint(self));\n }\n\n /// @dev Decode an RLPItem into a bytes32. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toBytes32(RLPItem memory self) internal constant returns (bytes32 data) {\n     return bytes32(toUint(self));\n }\n\n /// @dev Decode an RLPItem into an address. This will not work if the\n /// RLPItem is a list.\n /// @param self The RLPItem.\n /// @return The decoded string.\n function toAddress(RLPItem memory self) internal constant returns (address data) {\n     if(!isData(self))\n         throw;\n     var (rStartPos, len) = _decode(self);\n     if (len != 20)\n         throw;\n     assembly {\n         data := div(mload(rStartPos), exp(256, 12))\n     }\n }\n\n // Get the payload offset.\n function _payloadOffset(RLPItem memory self) private constant returns (uint) {\n     if(self._unsafe_length == 0)\n         return 0;\n     uint b0;\n     uint memPtr = self._unsafe_memPtr;\n     assembly {\n         b0 := byte(0, mload(memPtr))\n     }\n     if(b0 < DATA_SHORT_START)\n         return 0;\n     if(b0 < DATA_LONG_START || (b0 >= LIST_SHORT_START && b0 < LIST_LONG_START))\n         return 1;\n     if(b0 < LIST_SHORT_START)\n         return b0 - DATA_LONG_OFFSET + 1;\n     return b0 - LIST_LONG_OFFSET + 1;\n }\n\n // Get the full length of an RLP item.\n function _itemLength(uint memPtr) private constant returns (uint len) {\n     uint b0;\n     assembly {\n         b0 := byte(0, mload(memPtr))\n     }\n     if (b0 < DATA_SHORT_START)\n         len = 1;\n     else if (b0 < DATA_LONG_START)\n         len = b0 - DATA_SHORT_START + 1;\n     else if (b0 < LIST_SHORT_START) {\n         assembly {\n             let bLen := sub(b0, 0xB7) // bytes length (DATA_LONG_OFFSET)\n             let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length\n             len := add(1, add(bLen, dLen)) // total length\n         }\n     }\n     else if (b0 < LIST_LONG_START)\n         len = b0 - LIST_SHORT_START + 1;\n     else {\n         assembly {\n             let bLen := sub(b0, 0xF7) // bytes length (LIST_LONG_OFFSET)\n             let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length\n             len := add(1, add(bLen, dLen)) // total length\n         }\n     }\n }\n\n // Get start position and length of the data.\n function _decode(RLPItem memory self) private constant returns (uint memPtr, uint len) {\n     if(!isData(self))\n         throw;\n     uint b0;\n     uint start = self._unsafe_memPtr;\n     assembly {\n         b0 := byte(0, mload(start))\n     }\n     if (b0 < DATA_SHORT_START) {\n         memPtr = start;\n         len = 1;\n         return;\n     }\n     if (b0 < DATA_LONG_START) {\n         len = self._unsafe_length - 1;\n         memPtr = start + 1;\n     } else {\n         uint bLen;\n         assembly {\n             bLen := sub(b0, 0xB7) // DATA_LONG_OFFSET\n         }\n         len = self._unsafe_length - 1 - bLen;\n         memPtr = start + bLen + 1;\n     }\n     return;\n }\n\n // Assumes that enough memory has been allocated to store in target.\n function _copyToBytes(uint btsPtr, bytes memory tgt, uint btsLen) private constant {\n     // Exploiting the fact that 'tgt' was the last thing to be allocated,\n     // we can write entire words, and just overwrite any excess.\n     assembly {\n         {\n                 let i := 0 // Start at arr + 0x20\n                 let words := div(add(btsLen, 31), 32)\n                 let rOffset := btsPtr\n                 let wOffset := add(tgt, 0x20)\n             tag_loop:\n                 jumpi(end, eq(i, words))\n                 {\n                     let offset := mul(i, 0x20)\n                     mstore(add(wOffset, offset), mload(add(rOffset, offset)))\n                     i := add(i, 1)\n                 }\n                 jump(tag_loop)\n             end:\n                 mstore(add(tgt, add(0x20, mload(tgt))), 0)\n         }\n     }\n }\n\n // Check that an RLP item is valid.\n     function _validate(RLPItem memory self) private constant returns (bool ret) {\n         // Check that RLP is well-formed.\n         uint b0;\n         uint b1;\n         uint memPtr = self._unsafe_memPtr;\n         assembly {\n             b0 := byte(0, mload(memPtr))\n             b1 := byte(1, mload(memPtr))\n         }\n         if(b0 == DATA_SHORT_START + 1 && b1 < DATA_SHORT_START)\n             return false;\n         return true;\n     }\n}\n"
  },
  {
    "path": "geth.js",
    "content": "'use strict';\n\nconst Web3 = require(\"web3\");\n\nconst utils = require(\"./utils\");\nconst artifacts = require(\"./build/contracts/PlasmaChainManager.json\");\n\nconst provider = new Web3.providers.HttpProvider('http://localhost:8545');\nconst web3 = new Web3(provider);\n\nvar plasmaContract, plasmaOperator;\n\nconst init = (contractAddress, operatorAddress) => {\n    plasmaContract = new web3.eth.Contract(artifacts.abi, contractAddress, {gas: 1000000});\n    plasmaOperator = operatorAddress;\n}\n\nconst submitBlockHeader = async (header) => {\n    let result = await plasmaContract.methods.submitBlockHeader(header).send({\n        from: plasmaOperator, gas: 300000\n    });\n    let ev = result.events.HeaderSubmittedEvent.returnValues;\n    console.log(ev);\n};\n\nconst signBlock = async (message) => {\n    return await web3.eth.sign(message, plasmaOperator);\n};\n\nconst signTransaction = async (message, address) => {\n    return await web3.eth.sign(message, address);\n};\n\nconst isValidSignature = async (message, signature, address) => {\n    const hash = await web3.eth.accounts.hashMessage(message);\n    const signer = await web3.eth.accounts.recover(hash, signature);\n    return utils.removeHexPrefix(address.toLowerCase()) == utils.removeHexPrefix(signer.toLowerCase());\n};\n\nconst deposit = async (address, amount) => {\n    amount = utils.etherToWei(amount);\n    const result = await plasmaContract.methods.deposit().send({\n        from: address, value: amount, gas: 300000\n    });\n    console.log(result);\n};\n\nconst getDeposits = async (blockNumber) => {\n    let depositEvents = await plasmaContract.getPastEvents('DepositEvent', {\n        filter: {blockNumber: blockNumber.toString()},\n        fromBlock: 0,\n        toBlock: 'latest'\n    });\n\n    let deposits = [];\n    depositEvents.forEach(ev => deposits.push(ev.returnValues));\n    deposits.sort((d1, d2) => (d1.ctr - d2.ctr));\n    return deposits;\n}\n\nconst startWithdrawal = async (blkNum, txIndex, oIndex, targetTx, proof, from) => {\n    let result = await plasmaContract.methods.startWithdrawal(blkNum, txIndex, oIndex, targetTx, proof).send({\n        from: from, gas: 300000\n    });\n    let ev = result.events.WithdrawalStartedEvent.returnValues;\n    console.log(ev);\n    return ev.withdrawalId;\n};\n\nconst challengeWithdrawal = async (withdrawalId, blkNum, txIndex, oIndex, targetTx, proof, from) => {\n    let result = await plasmaContract.methods.challengeWithdrawal(withdrawalId, blkNum, txIndex, oIndex, targetTx, proof).send({\n        from: from, gas: 300000\n    });\n    console.log(result);\n};\n\nconst finalizeWithdrawal = async (from) => {\n    let result = await plasmaContract.methods.finalizeWithdrawal().send({\n        from: from, gas: 300000\n    });\n    if (result.events.WithdrawalCompleteEvent) {\n        console.log(result.events.WithdrawalCompleteEvent.returnValues);\n    }\n};\n\nconst getWithdrawals = async (blockNumber) => {\n    let withdrawalEvents = await plasmaContract.getPastEvents('WithdrawalCompleteEvent', {\n        filter: {blockNumber: blockNumber.toString()},\n        fromBlock: 0,\n        toBlock: 'latest'\n    });\n\n    return withdrawalEvents.map(ev => ev.returnValues);\n};\n\nmodule.exports = {init, signBlock, signTransaction, submitBlockHeader, deposit,\n    getDeposits, isValidSignature, startWithdrawal, challengeWithdrawal,\n    finalizeWithdrawal, getWithdrawals};\n"
  },
  {
    "path": "main.js",
    "content": "'use strict';\n\nconst express = require(\"express\");\nconst bodyParser = require('body-parser');\nconst minimist = require('minimist');\n\nconst block = require(\"./block\");\nconst tx = require(\"./transaction\");\nconst config = require(\"./config\");\nconst geth = require(\"./geth\");\n\nconst initHttpServer = (http_port) => {\n    const app = express();\n    app.use(bodyParser.json());\n\n    // Block related\n    app.get('/blocks', (req, res) => {\n        res.send(JSON.stringify(block.getBlocks().map(b => b.printBlock())));\n    });\n    app.post('/mineBlock', async (req, res) => {\n        try {\n            const newBlock = await block.generateNextBlock(geth);\n            res.send(JSON.stringify(newBlock.printBlock()));\n        } catch (e) {\n            res.send(JSON.stringify(e));\n        }\n    });\n\n    // Transaction related\n    app.post('/transact', async (req, res) => {\n        try {\n            const rawTx = await tx.createTransaction(req.body, geth);\n            console.log('New transaction created: ' + JSON.stringify(rawTx));\n            res.send(JSON.stringify(rawTx.toString(true)));\n        } catch (e) {\n            res.send(JSON.stringify(e));\n        }\n    });\n\n    // Deposit related\n    app.post('/deposit', async (req, res) => {\n        await geth.deposit(req.body.address, req.body.amount);\n        res.send();\n    });\n\n    // Withdrawal related\n    app.post('/withdraw/create', async (req, res) => {\n        const p = block.getTransactionProofInBlock(req.body.blkNum,\n            req.body.txIndex);\n        const withdrawalId = await geth.startWithdrawal(req.body.blkNum,\n            req.body.txIndex, req.body.oIndex, p.tx, p.proof, req.body.from);\n        res.send(withdrawalId);\n    });\n    app.post('/withdraw/challenge', async (req, res) => {\n        const p = block.getTransactionProofInBlock(req.body.blkNum,\n            req.body.txIndex);\n        await geth.challengeWithdrawal(req.body.withdrawalId, req.body.blkNum,\n            req.body.txIndex, req.body.oIndex, p.tx, p.proof, req.body.from);\n        res.send();\n    });\n    app.post('/withdraw/finalize', async (req, res) => {\n        await geth.finalizeWithdrawal(req.body.from);\n        res.send();\n    });\n\n    // Debug function\n    app.get('/utxo', (req, res) => {\n        res.send(tx.getUTXO());\n    });\n    app.get('/pool', (req, res) => {\n        res.send(tx.getPool());\n    });\n\n    app.listen(http_port, () => console.log('Listening http on port: ' + http_port));\n};\n\nconst main = () => {\n    const argv = minimist(process.argv.slice(2), { string: [\"port\", \"contract\", \"operator\"]});\n    const http_port = argv.port || 3001;\n    const contract_address = argv.contract || config.plasmaContractAddress;\n    const operator_address = argv.operator || config.plasmaOperatorAddress;\n\n    geth.init(contract_address, operator_address);\n    initHttpServer(http_port);\n};\n\nmain();\n"
  },
  {
    "path": "merkle.js",
    "content": "'use strict';\n\nconst createKeccakHash = require('keccak');\n\nclass Merkle {\n    constructor(data) {\n        this.isReady = false;\n        this.leaves = data.map(str => this._hash(this._getBuffer(str)));\n        this.levels = [];\n    }\n\n    makeTree() {\n        this.isReady = false;\n        this.levels.unshift(this.leaves);\n        while (this.levels[0].length > 1) {\n            this.levels.unshift(this._getNextLevel());\n        }\n        this.isReady = true;\n    }\n\n    getRoot() {\n        return this.isReady ? this.levels[0][0] : null;\n    }\n\n    getProof(index) {\n        let proof = [];\n        for (let i = this.levels.length - 1; i > 0; i--) {\n            let isRightNode = index % 2;\n            let siblingIndex = isRightNode ? (index - 1) : (index + 1);\n            proof.push(new Buffer(isRightNode ? [0x00] : [0x01]));\n            proof.push(this.levels[i][siblingIndex]);\n            index = Math.floor(index / 2);\n        }\n        return proof;\n    }\n\n    _hash(value) {\n        return createKeccakHash('keccak256').update(value).digest();\n    }\n\n    _getBuffer(value) {\n        return new Buffer(value, 'hex');\n    }\n\n    _getNextLevel() {\n        let nodes = [];\n        for (let i = 0; i < this.levels[0].length - 1; i += 2) {\n            let left = this.levels[0][i];\n            let right = this.levels[0][i + 1];\n            nodes.push(this._hash(Buffer.concat([left, right])));\n        }\n        return nodes;\n    }\n}\n\nmodule.exports = Merkle;\n"
  },
  {
    "path": "migrations/1_initial_migration.js",
    "content": "var Migrations = artifacts.require(\"./Migrations.sol\");\n\nmodule.exports = function(deployer) {\n  deployer.deploy(Migrations);\n};\n"
  },
  {
    "path": "migrations/2_deploy_contracts.js",
    "content": "const PlasmaChainManager = artifacts.require(\"./PlasmaChainManager\");\nconst minHeapLib = artifacts.require(\"./MinHeapLib\");\nconst arrayLib = artifacts.require(\"./ArrayLib\");\nconst RLP = artifacts.require(\"./RLP\");\n\nmodule.exports = function(deployer) {\n    deployer.deploy(minHeapLib);\n    deployer.deploy(arrayLib);\n    deployer.deploy(RLP);\n    deployer.link(minHeapLib, PlasmaChainManager);\n    deployer.link(arrayLib, PlasmaChainManager);\n    deployer.link(RLP, PlasmaChainManager);\n    deployer.deploy(PlasmaChainManager, 7 * 86400, 14 * 86400);\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"plasma\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"scripts\": {\n    \"start\": \"node main.js\"\n  },\n  \"dependencies\": {\n    \"body-parser\": \"^1.15.2\",\n    \"express\": \"~4.11.1\",\n    \"ganache-cli\": \"^6.0.3\",\n    \"keccak\": \"^1.4.0\",\n    \"minimist\": \"^1.2.0\",\n    \"rlp\": \"^2.0.0\",\n    \"truffle\": \"^4.0.6\",\n    \"web3\": \"^1.0.0-beta.27\"\n  },\n  \"engines\": {\n    \"node\": \">=9.8.0\"\n  }\n}\n"
  },
  {
    "path": "test/TestMinHeapLib.sol",
    "content": "pragma solidity ^0.4.18;\n\nimport \"truffle/Assert.sol\";\nimport \"truffle/DeployedAddresses.sol\";\nimport \"../contracts/MinHeapLib.sol\";\n\ncontract TestMinHeapLib {\n  using MinHeapLib for MinHeapLib.Heap;\n\n  MinHeapLib.Heap heap;\n\n  function initHeap() internal {\n    heap.data.length = 0;\n  }\n\n  function testAdd() public {\n    initHeap();\n    heap.add(5);\n    heap.add(3);\n    heap.add(4);\n    heap.add(1);\n    heap.add(2);\n    Assert.equal(heap.data[0], uint(1), \"top of heap data should be 1\");\n    Assert.equal(heap.data.length, uint(5), \"heap should have 5 elements\");\n  }\n\n  function testPeek() public {\n    initHeap();\n    heap.data = [1, 2, 3];\n\n    Assert.equal(heap.peek(), uint(1), \"heap.peek() should be 1\");\n  }\n\n  function testPop() public {\n    initHeap();\n    heap.data = [1, 2, 3];\n\n    Assert.equal(heap.pop(), uint(1), \"should pop 1\");\n    Assert.equal(heap.pop(), uint(2), \"should pop 2\");\n    Assert.equal(heap.pop(), uint(3), \"should pop 3\");\n    Assert.isTrue(heap.isEmpty(), \"heap should be empty\");\n  }\n\n  function testSimpleIntegration() public {\n    initHeap();\n    uint TEST_NUM = 10;\n    uint i;\n\n    for(i = TEST_NUM ; i > 0 ; i--) {\n      heap.add(i);\n      Assert.equal(heap.peek(), i, \"should have correct peek while adding\");\n    }\n\n    for(i = 1 ; i <= TEST_NUM ; i++) {\n      Assert.equal(heap.pop(), i, \"should pop correctly\");\n    }\n\n    Assert.isTrue(heap.isEmpty(), \"heap should be empty\");\n  }\n}\n"
  },
  {
    "path": "test/TestPlasmaManager.sol",
    "content": "pragma solidity ^0.4.18;\n\nimport \"truffle/Assert.sol\";\nimport \"truffle/DeployedAddresses.sol\";\nimport \"../contracts/PlasmaChainManager.sol\";\n\ncontract TestPlasmaManager {\n  function testTrue() public {\n    Assert.equal(uint(1), uint(1), \"plzzzz\");\n  }\n\n  function testContractInitState() public {\n    PlasmaChainManager plasma = PlasmaChainManager(DeployedAddresses.PlasmaChainManager());\n    Assert.equal(plasma.lastBlockNumber(), uint(0), \"lastBlockNumber should be initiated with 0\");\n    Assert.equal(plasma.txCounter(), uint(0), \"txCounter should be initiated with 0\");\n  }\n}\n"
  },
  {
    "path": "test/integration.js",
    "content": "const http = require(\"http\");\nconst spawn = require('child_process').spawn;\n\nconst PlasmaChainManager = artifacts.require(\"./PlasmaChainManager\");\n\nvar runServer = (contract, operator) => {\n  return new Promise(function(resolve, reject) {\n    var server = spawn('node', ['main.js', '--contract', contract, '--operator', operator]);\n\n    server.stdout.on('data', (data) => {\n      resolve(server);\n    });\n  });\n};\n\nvar request = (method, path, data) => {\n  return new Promise(function(resolve, reject) {\n    var options = {\n      hostname: 'localhost',\n      port: 3001,\n      method: method,\n      path: path,\n    };\n\n    var req = http.request(options, (res) => {\n      var buf = '';\n      res.on('data', (data) => {\n        buf += data;\n      });\n      res.on('end', () => {\n        if (buf) {\n          resolve(JSON.parse(buf));\n        } else {\n          resolve(null);\n        }\n      });\n    });\n\n    if (data) {\n      req.setHeader('Content-Type', 'application/json');\n      req.write(data);\n    }\n\n    req.end();\n  });\n};\n\nvar sleep = (ms) => {\n  return new Promise(function(resolve, reject) {\n    setTimeout(resolve, ms);\n  });\n};\n\ncontract('PlasmaChainManager', function(accounts) {\n  it(\"deposit\", async () => {\n    var plasmaChainManager = await PlasmaChainManager.new(100, 200);\n    var contractAddress = plasmaChainManager.address;\n    var operatorAddress = accounts[0];\n    var server = await runServer(contractAddress, operatorAddress);\n\n    try {\n      await request('POST', '/deposit', JSON.stringify({address: accounts[0], amount: 0.2}));\n      await request('POST', '/mineBlock');\n      var utxo = await request('GET', '/utxo');\n      assert.equal(utxo[0].denom, 200000000000000000);\n    } catch(e) {\n      throw e;\n    } finally {\n      server.kill();\n    }\n  });\n\n  it(\"transact\", async () => {\n    var plasmaChainManager = await PlasmaChainManager.new(100, 200);\n    var contractAddress = plasmaChainManager.address;\n    var operatorAddress = accounts[0];\n    var server = await runServer(contractAddress, operatorAddress);\n\n    try {\n      await request('POST', '/deposit', JSON.stringify({address: accounts[1], amount: 0.5}));\n      await request('POST', '/mineBlock');\n      await request('POST', '/transact', JSON.stringify({from: accounts[1], to: accounts[2], amount: 0.3}));\n      await request('POST', '/mineBlock');\n      var utxo = await request('GET', '/utxo');\n\n      for (i = 0; i < utxo.length; i++) {\n        if (utxo[i].owner === accounts[1]) {\n          assert.equal(utxo[i].denom, 190000000000000000);\n        } else if (utxo[i].owner === accounts[2]) {\n          assert.equal(utxo[i].denom, 300000000000000000);\n        }\n      }\n    } catch(e) {\n      throw e;\n    } finally {\n      server.kill();\n    }\n  });\n\n  it(\"withdraw\", async () => {\n    var plasmaChainManager = await PlasmaChainManager.new(0, 0);\n    var contractAddress = plasmaChainManager.address;\n    var operatorAddress = accounts[0];\n    var server = await runServer(contractAddress, operatorAddress);\n\n    try {\n      await request('POST', '/deposit', JSON.stringify({address: accounts[1], amount: 0.5}));\n      await request('POST', '/mineBlock');\n      var utxo = await request('GET', '/utxo');\n      assert.equal(utxo.length, 1);\n      var withdrawId = await request('POST', '/withdraw/create', JSON.stringify({from: accounts[1], blkNum: utxo[0].blkNum, txIndex: utxo[0].txIndex, oIndex: utxo[0].oIndex}));\n      await sleep(1000);\n      await request('POST', '/withdraw/finalize', JSON.stringify({from: accounts[1]}));\n      await request('POST', '/mineBlock');\n      var utxo2 = await request('GET', '/utxo');\n      assert.equal(utxo2.length, 0);\n    } catch(e) {\n      throw e;\n    } finally {\n      server.kill();\n    }\n  });\n});\n"
  },
  {
    "path": "test/plasma_chain_manager.js",
    "content": "const PlasmaChainManager = artifacts.require(\"./PlasmaChainManager\");\n\ncontract('PlasmaChainManager', function(accounts) {\n  it(\"should assert true\", function(done) {\n    var plasmaChainManager = PlasmaChainManager.deployed();\n    assert.isTrue(true);\n    done();\n  });\n\n  it(\"should deposit successfully\", async () => {\n    const plasmaChainManager = await PlasmaChainManager.deployed();\n    const oldTxCounter = (await plasmaChainManager.txCounter()).toNumber();\n\n    await plasmaChainManager.deposit({value: web3.toWei(1, \"ether\"), from: accounts[0]});\n    const newTxCounter = (await plasmaChainManager.txCounter()).toNumber();\n    assert.equal(newTxCounter, oldTxCounter + 1);\n  });\n});\n"
  },
  {
    "path": "transaction.js",
    "content": "'use strict';\n\nconst createKeccakHash = require('keccak');\nconst RLP = require('rlp');\n\nconst utils = require(\"./utils\");\n\nclass Transaction {\n    constructor(blkNum1, txIndex1, oIndex1, sig1,\n        blkNum2, txIndex2, oIndex2, sig2,\n        newOwner1, denom1, newOwner2, denom2, fee, type) {\n        // first input\n        this.blkNum1 = blkNum1;\n        this.txIndex1 = txIndex1;\n        this.oIndex1 = oIndex1;\n        this.sig1 = sig1;\n\n        // second input\n        this.blkNum2 = blkNum2;\n        this.txIndex2 = txIndex2;\n        this.oIndex2 = oIndex2;\n        this.sig2 = sig2;\n\n        // outputs\n        this.newOwner1 = newOwner1;\n        this.denom1 = denom1;\n        this.newOwner2 = newOwner2;\n        this.denom2 = denom2;\n\n        this.fee = fee;\n        this.type = type;\n    }\n\n    encode(includingSig) {\n        let data = [\n            this.blkNum1, this.txIndex1, this.oIndex1,\n            this.blkNum2, this.txIndex2, this.oIndex2,\n            this.newOwner1, this.denom1, this.newOwner2, this.denom2, this.fee\n        ];\n        if (includingSig) {\n            data.push(this.sig1);\n            data.push(this.sig2);\n        }\n        return RLP.encode(data);\n    }\n\n    toString(includingSig) {\n        return utils.bufferToHex(this.encode(includingSig), false);\n    }\n\n    setSignature(sig) {\n        this.sig1 = sig;\n        if (this.blkNum2 !== 0) {\n            this.sig2 = sig;\n        }\n    }\n}\n\nclass UTXO {\n    constructor(blkNum, txIndex, oIndex, owner, denom) {\n        this.blkNum = blkNum;\n        this.txIndex = txIndex;\n        this.oIndex = oIndex;\n        this.owner = owner;\n        this.denom = denom;\n    }\n}\n\nconst TxType = {\n    NORMAL: 0,\n    DEPOSIT: 1,\n    WITHDRAW: 2,\n    MERGE: 3\n};\n\nlet txPool = [];\nlet utxo = [];\n\nconst createDepositTransactions = (deposits) => {\n    return deposits.map(deposit => {\n        const owner = deposit.from;\n        const amount = parseInt(deposit.amount);\n        return new Transaction(0, 0, 0, 0, 0, 0, 0, 0,\n                               owner, amount, 0, 0, 0, TxType.DEPOSIT);\n    });\n};\n\nconst createWithdrawalTransactions = (withdrawals) => {\n    return withdrawals.map(withdrawal => {\n        const blkNum = parseInt(withdrawal.exitBlockNumber);\n        const txIndex = parseInt(withdrawal.exitTxIndex);\n        const oIndex = parseInt(withdrawal.exitOIndex);\n        return new Transaction(blkNum, txIndex, oIndex, 0, 0, 0, 0, 0,\n                               0, 0, 0, 0, 0, TxType.WITHDRAW);\n    });\n};\n\nconst createMergeTransaction = (owner) => {\n    const indexes = getTwoUTXOsByAddress(owner);\n    if (indexes[0] !== -1 && indexes[1] !== -1) {\n        const utxoA = utxo[indexes[0]];\n        const utxoB = utxo[indexes[1]];\n        return new Transaction(utxoA.blkNum, utxoA.txIndex, utxoA.oIndex, 0,\n                               utxoB.blkNum, utxoB.txIndex, utxoB.oIndex, 0,\n                               owner, utxoA.denom + utxoB.denom, 0, 0, 0,\n                               TxType.MERGE);\n    } else {\n        return null;\n    }\n};\n\nconst createTransaction = async (data, geth) => {\n    let index = getUTXOByAddress(data.from);\n    if (index === -1) {\n        throw 'No asset found';\n    }\n    let blkNum1 = utxo[index].blkNum;\n    let txIndex1 = utxo[index].txIndex;\n    let oIndex1 = utxo[index].oIndex;\n\n    let newOwner1 = data.to;\n    let denom1 = utils.etherToWei(data.amount);\n    let fee = utils.etherToWei(0.01);  // hard-coded fee to 0.01\n    if (utxo[index].denom < denom1 + fee) {\n        throw 'Insufficient funds';\n    }\n    let remain = utxo[index].denom - denom1 - fee;\n    let newOwner2 = (remain > 0) ? data.from : 0;\n    let denom2 = remain;\n\n    let tx = new Transaction(\n        blkNum1, txIndex1, oIndex1, 0, 0, 0, 0, 0,\n        newOwner1, denom1, newOwner2, denom2, fee, TxType.NORMAL);\n    let signature = await geth.signTransaction(tx.toString(false), data.from);\n    tx.setSignature(signature);\n\n    if (await isValidTransaction(tx, geth)) {\n        spendUTXO(tx);\n        txPool.push(tx);\n    } else {\n        throw 'Invalid transaction';\n    }\n    return tx;\n}\n\nconst getUTXOByAddress = (owner, start = 0) => {\n    for (let i = start; i < utxo.length; i++) {\n        if (utxo[i].owner.toLowerCase() === owner.toLowerCase()) {\n            return i;\n        }\n    }\n    return -1;\n};\n\nconst getTwoUTXOsByAddress = (owner) => {\n    let index1 = getUTXOByAddress(owner);\n    let index2 = index1 !== -1 ? getUTXOByAddress(owner, index1 + 1) : -1;\n    return [index1, index2];\n};\n\nconst getUTXOByIndex = (blkNum, txIndex, oIndex) => {\n    for (let i = 0; i < utxo.length; i++) {\n        if (utxo[i].blkNum === blkNum &&\n            utxo[i].txIndex === txIndex &&\n            utxo[i].oIndex === oIndex) {\n            return i;\n        }\n    }\n    return -1;\n};\n\nconst isValidTransaction = async (tx, geth) => {\n    if (tx.type !== TxType.NORMAL) {\n        return true;\n    }\n\n    let denom = 0;\n    if (tx.blkNum1 !== 0) {\n        let message = tx.toString(false);\n        let index = getUTXOByIndex(tx.blkNum1, tx.txIndex1, tx.oIndex1);\n        if (index !== -1 &&\n            await geth.isValidSignature(message, tx.sig1, utxo[index].owner)) {\n            denom += utxo[index].denom;\n        } else {\n            return false;\n        }\n    }\n    if (tx.blkNum2 !== 0) {\n        let message = tx.toString(false);\n        let index = getUTXOByIndex(tx.blkNum2, tx.txIndex2, tx.oIndex2);\n        if (index !== -1 ||\n            await geth.isValidSignature(message, tx.sig2, utxo[index].owner)) {\n            denom += utxo[index].denom;\n        } else {\n            return false;\n        }\n    }\n    return denom === tx.denom1 + tx.denom2 + tx.fee;\n};\n\nconst spendUTXO = (tx) => {\n    if (tx.blkNum1 !== 0) {\n        const index = getUTXOByIndex(tx.blkNum1, tx.txIndex1, tx.oIndex1);\n        if (index !== -1) {\n            utxo.splice(index, 1);\n        }\n    }\n    if (tx.blkNum2 !== 0) {\n        const index = getUTXOByIndex(tx.blkNum2, tx.txIndex2, tx.oIndex2);\n        if (index !== -1) {\n            utxo.splice(index, 1);\n        }\n    }\n};\n\nconst createUTXO = (blockNumber, tx, txIndex) => {\n    if (tx.newOwner1 !== 0 && tx.denom1 !== 0) {\n        utxo.push(new UTXO(blockNumber, txIndex, 0, tx.newOwner1, tx.denom1));\n    }\n    if (tx.newOwner2 !== 0 && tx.denom2 !== 0) {\n        utxo.push(new UTXO(blockNumber, txIndex, 1, tx.newOwner2, tx.denom2));\n    }\n};\n\nconst collectTransactions = async (blockNumber, deposits, withdrawals) => {\n    const txs = [];\n\n    if (deposits.length > 0) {\n        console.log('Deposit transactions found.');\n        console.log(deposits);\n        const depositTxs = await createDepositTransactions(deposits);\n        for (let i = 0; i < depositTxs.length; i++) {\n            const tx = depositTxs[i];\n            createUTXO(blockNumber, tx, txs.length);\n            txs.push(tx.toString(true));\n\n            const mergeTx = await createMergeTransaction(tx.newOwner1);\n            if (mergeTx !== null) {\n                spendUTXO(mergeTx);\n                createUTXO(blockNumber, mergeTx, txs.length);\n                txs.push(mergeTx.toString(true));\n            }\n        }\n    }\n\n    if (withdrawals.length > 0) {\n        console.log('Withdrawals detected.');\n        console.log(withdrawals);\n        const withdrawalTxs = await createWithdrawalTransactions(withdrawals);\n        for (let i = 0; i < withdrawalTxs.length; i++) {\n            const tx = withdrawalTxs[i];\n            spendUTXO(tx);\n            txs.push(tx.toString(true));\n        }\n    }\n\n    for (let i = 0; i < txPool.length; i++) {\n        const tx = txPool[i];\n        createUTXO(blockNumber, tx, txs.length);\n        txs.push(tx.toString(true));\n        txPool.splice(i, 1);\n\n        const mergeTx1 = await createMergeTransaction(tx.newOwner1);\n        if (mergeTx1 !== null) {\n            spendUTXO(mergeTx1);\n            createUTXO(blockNumber, mergeTx1, txs.length);\n            txs.push(mergeTx1.toString(true));\n        }\n        const mergeTx2 = await createMergeTransaction(tx.newOwner2);\n        if (mergeTx2 !== null) {\n            spendUTXO(mergeTx2);\n            createUTXO(blockNumber, mergeTx2, txs.length);\n            txs.push(mergeTx2.toString(true));\n        }\n\n        // Limit transactions per block to power of 2 on purpose for the\n        // convenience of building Merkle tree.\n        if (txs.length >= 256) {\n            break;\n        }\n    }\n\n    // Fill empty string if transactions are less than 256.\n    const len = txs.length;\n    for (let i = len; i < 256; i++) {\n        txs.push(\"\");\n    }\n\n    return txs;\n};\n\nconst getUTXO = () => {\n    return utxo;\n};\n\nconst getPool = () => {\n    return txPool;\n};\n\nmodule.exports = {createTransaction, collectTransactions, getUTXO, getPool};\n"
  },
  {
    "path": "truffle-config.js",
    "content": "module.exports = {\n  // See <http://truffleframework.com/docs/advanced/configuration>\n  // to customize your Truffle configuration!\n};\n"
  },
  {
    "path": "truffle.js",
    "content": "module.exports = {\n    networks: {\n      development: {\n        host: \"localhost\",\n        port: 8545,\n        gas: 6721975,\n        network_id: \"*\", // Match any network id\n      }\n    }\n};\n"
  },
  {
    "path": "utils.js",
    "content": "'use strict';\n\nconst Web3 = require(\"web3\");\n\nconst addHexPrefix = (msg) => {\n    return '0x' + msg;\n};\n\nconst removeHexPrefix = (msg) => {\n    if (Web3.utils.isHexStrict(msg)) {\n        return msg.slice(2);\n    } else {\n        return msg;\n    }\n};\n\nconst bufferToHex = (buf, withPrefix) => {\n    if (withPrefix) {\n        return addHexPrefix(buf.toString('hex'));\n    } else {\n        return buf.toString('hex');\n    }\n};\n\nconst weiToEther = (data) => {\n    return data / 1000000000000000000;\n};\n\nconst etherToWei = (data) => {\n    return data * 1000000000000000000;\n};\n\nmodule.exports = {addHexPrefix, removeHexPrefix, bufferToHex, weiToEther,\n    etherToWei};\n"
  }
]