diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol deleted file mode 100644 index d542e7fd0..000000000 --- a/contracts/Migrations.sol +++ /dev/null @@ -1,23 +0,0 @@ -pragma solidity ^0.5.2; - -contract Migrations { - address public owner; - uint256 public last_completed_migration; - - modifier restricted() { - if (msg.sender == owner) _; - } - - constructor() public { - owner = msg.sender; - } - - function setCompleted(uint256 completed) public restricted { - last_completed_migration = completed; - } - - function upgrade(address new_address) public restricted { - Migrations upgraded = Migrations(new_address); - upgraded.setCompleted(last_completed_migration); - } -} diff --git a/contracts/child/misc/Marketplace.sol b/contracts/child/misc/Marketplace.sol deleted file mode 100644 index e27c5e9a0..000000000 --- a/contracts/child/misc/Marketplace.sol +++ /dev/null @@ -1,66 +0,0 @@ -pragma solidity ^0.5.2; - -interface MarketplaceToken { - function transferWithSig( - bytes calldata sig, - uint256 tokenIdOrAmount, - bytes32 data, - uint256 expiration, - address to - ) external returns (address); -} - -contract Marketplace { - struct Order { - address token; - bytes sig; - uint256 tokenIdOrAmount; - } - - function executeOrder( - bytes memory data1, - bytes memory data2, - bytes32 orderId, - uint256 expiration, - address taker - ) public { - Order memory order1 = decode(data1); - Order memory order2 = decode(data2); - - // Transferring order1.token tokens from tradeParticipant1 to address2 - address tradeParticipant1 = MarketplaceToken(order1.token) - .transferWithSig( - order1.sig, - order1.tokenIdOrAmount, - keccak256( - abi.encodePacked(orderId, order2.token, order2.tokenIdOrAmount) - ), - expiration, - taker - ); - - // Transferring token2 from tradeParticipant2 to tradeParticipant1 - address tradeParticipant2 = MarketplaceToken(order2.token) - .transferWithSig( - order2.sig, - order2.tokenIdOrAmount, - keccak256( - abi.encodePacked(orderId, order1.token, order1.tokenIdOrAmount) - ), - expiration, - tradeParticipant1 - ); - require(taker == tradeParticipant2, "Orders are not complimentary"); - } - - function decode(bytes memory data) - internal - pure - returns (Order memory order) - { - (order.token, order.sig, order.tokenIdOrAmount) = abi.decode( - data, - (address, bytes, uint256) - ); - } -} diff --git a/contracts/child/misc/ParentTokenMock.sol b/contracts/child/misc/ParentTokenMock.sol deleted file mode 100644 index ed0a83759..000000000 --- a/contracts/child/misc/ParentTokenMock.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.5.2; - -import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; - -import "./IParentToken.sol"; -// demo token parent contract - -contract ParentTokenMock is IParentToken, Ownable { - mapping(address => bool) isAllowed; - function beforeTransfer(address sender, address to, uint256 value) - external - returns (bool) - { - return isAllowed[sender]; - } - - function updatePermission(address user) public onlyOwner { - require(user != address(0x0)); - isAllowed[user] = !isAllowed[user]; - } -} diff --git a/contracts/common/tokens/ERC20NonTransferable.sol b/contracts/common/tokens/ERC20NonTransferable.sol deleted file mode 100644 index ab5167fe9..000000000 --- a/contracts/common/tokens/ERC20NonTransferable.sol +++ /dev/null @@ -1,13 +0,0 @@ -pragma solidity ^0.5.2; - -import {ERC20} from "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; - -contract ERC20NonTransferable is ERC20 { - function _transfer( - address from, - address to, - uint256 value - ) internal { - revert("Disabled"); - } -} diff --git a/contracts/common/tokens/POLTokenMock.sol b/contracts/common/tokens/POLTokenMock.sol deleted file mode 100644 index c666f98a5..000000000 --- a/contracts/common/tokens/POLTokenMock.sol +++ /dev/null @@ -1,56 +0,0 @@ -pragma solidity ^0.5.2; - -import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; -import "openzeppelin-solidity/contracts/utils/Address.sol"; - - -contract POLTokenMock is ERC20Mintable { - using Address for address; - - // detailed ERC20 - string public name; - string public symbol; - uint8 public decimals = 18; - - constructor(string memory _name, string memory _symbol) public { - name = _name; - symbol = _symbol; - - uint256 value = 10**10 * (10**18); - mint(msg.sender, value); - } - - function safeTransfer(IERC20 token, address to, uint256 value) public { - callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); - } - - function safeTransferFrom(IERC20 token, address from, address to, uint256 value) public { - callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); - } - - /** - * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement - * on the return value: the return value is optional (but if data is returned, it must equal true). - * @param token The token targeted by the call. - * @param data The call data (encoded using abi.encode or one of its variants). - */ - function callOptionalReturn(IERC20 token, bytes memory data) private { - // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since - // we're implementing it ourselves. - - // A Solidity high level call has three parts: - // 1. The target address is checked to verify it contains contract code - // 2. The call itself is made, and success asserted - // 3. The return value is decoded, which in turn checks the size of the returned data. - - require(address(token).isContract()); - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = address(token).call(data); - require(success); - - if (returndata.length > 0) { // Return data is optional - require(abi.decode(returndata, (bool))); - } - } -} diff --git a/contracts/root/predicates/MarketplacePredicate.sol b/contracts/root/predicates/MarketplacePredicate.sol deleted file mode 100644 index d412420a0..000000000 --- a/contracts/root/predicates/MarketplacePredicate.sol +++ /dev/null @@ -1,467 +0,0 @@ -pragma solidity ^0.5.2; - -import {BytesLib} from "../../common/lib/BytesLib.sol"; -import {Common} from "../../common/lib/Common.sol"; -import {ECVerify} from "../../common/lib/ECVerify.sol"; -import {Math} from "openzeppelin-solidity/contracts/math/Math.sol"; -import {RLPEncode} from "../../common/lib/RLPEncode.sol"; -import {RLPReader} from "solidity-rlp/contracts/RLPReader.sol"; -import {SafeMath} from "openzeppelin-solidity/contracts/math/SafeMath.sol"; - -import {IPredicate, PredicateUtils} from "./IPredicate.sol"; -import {IRootChain} from "../IRootChain.sol"; -import {IWithdrawManager} from "../withdrawManager/IWithdrawManager.sol"; -import {Registry} from "../../common/Registry.sol"; -import {TransferWithSigUtils} from "./TransferWithSigUtils.sol"; - -contract MarketplacePredicate is PredicateUtils { - using RLPReader for bytes; - using RLPReader for RLPReader.RLPItem; - using SafeMath for uint256; - - // 0xe660b9e4 = keccak256('executeOrder(bytes,bytes,bytes32,uint256,address)').slice(0, 4) - bytes4 constant EXECUTE_ORDER_FUNC_SIG = 0xe660b9e4; - - Registry public registry; - IRootChain public rootChain; - - struct ExecuteOrderData { - bytes data1; - bytes data2; - bytes32 orderId; - uint256 expiration; - address taker; - } - - struct Order { - address token; - bytes sig; - uint256 amount; - } - - struct ExitTxData { - // token1 and amount1 correspond to what the utxoOwner (tradeParticipant) signed over - uint256 amount1; - uint256 amount2; - address token1; - address token2; - address counterParty; - bytes32 txHash; - uint256 expiration; - } - - struct ReferenceTxData { - uint256 closingBalance; - uint256 age; - address childToken; - address rootToken; - } - - constructor(address _rootChain, address _withdrawManager, address _registry) - public - { - withdrawManager = IWithdrawManager(_withdrawManager); - rootChain = IRootChain(_rootChain); - registry = Registry(_registry); - } - - /** - * @notice Start an exit from in-flight marketplace tx - * @param data RLP encoded array of input utxos - * data[n] ( 1 < n <= 3) is abi encoded as (predicateAddress, RLP encoded reference tx) - * data[n][1] is RLP encoded reference tx that encodes the following fields - * headerNumber Header block number of which the reference tx was a part of - * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root - * blockNumber Block number of which the reference tx is a part of - * blockTime Reference tx block time - * blocktxRoot Transactions root of block - * blockReceiptsRoot Receipts root of block - * receipt Receipt of the reference transaction - * receiptProof Merkle proof of the reference receipt - * branchMask Merkle proof branchMask for the receipt - * logIndex Log Index to read from the receipt - * data[2] is the child token that the user wishes to start an exit for - * @param exitTx Signed (marketplace.executeOrder) exit transaction - */ - function startExit(bytes calldata data, bytes calldata exitTx) - external - payable - isBondProvided - { - RLPReader.RLPItem[] memory referenceTx = data.toRlpItem().toList(); - (address predicate, bytes memory preState) = abi.decode( - referenceTx[0].toBytes(), - (address, bytes) - ); - require( - uint8(registry.predicates(predicate)) != 0, - "Not a valid predicate" - ); - ExitTxData memory exitTxData = processExitTx(exitTx, msg.sender); - require( - exitTxData.expiration > rootChain.getLastChildBlock(), - "The inflight exit is not valid, because the marketplace order has expired" - ); - - // process the first input, which is the proof-of-exitor's funds for token t1 which exitor transferred to counterparty as part of the marketplace tx - ReferenceTxData memory reference1 = processLogTransferReceipt( - predicate, - preState, - msg.sender, - true, /* verifyInclusionInCheckpoint */ - false /* isChallenge */ - ); - - validateTokenBalance( - reference1.childToken, - exitTxData.token1, - reference1.closingBalance, - exitTxData.amount1 - ); - - // process the second input, which is the proof-of-counterparty's funds for token t2 which the counterparty transferred to exitor as part of the marketplace tx - (predicate, preState) = abi.decode( - referenceTx[1].toBytes(), - (address, bytes) - ); - require( - uint8(registry.predicates(predicate)) != 0, - "Not a valid predicate" - ); - ReferenceTxData memory reference2 = processLogTransferReceipt( - predicate, - preState, - exitTxData.counterParty, - true, /*verifyInclusionInCheckpoint*/ - false /* isChallenge */ - ); - validateTokenBalance( - reference2.childToken, - exitTxData.token2, - reference2.closingBalance, - exitTxData.amount2 - ); - - // The last element in referenceTx array refers to the child token being exited - address exitChildToken = address( - RLPReader.toUint(referenceTx[referenceTx.length - 1]) - ); - ReferenceTxData memory reference3; - // referenceTx.length == 4 means the exitor sent along another input UTXO for token t2 - // This will be used to exit with the pre-existing balance for token t2 on the chain - // @todo This part is untested - if (referenceTx.length == 4) { - (predicate, preState) = abi.decode( - referenceTx[3].toBytes(), - (address, bytes) - ); - reference3 = processLogTransferReceipt( - predicate, - preState, - msg.sender, - true, /* verifyInclusionInCheckpoint */ - false /* isChallenge */ - ); - require( - reference2.childToken == reference3.childToken, - "Child token doesnt match" - ); - } - - sendBond(); // send BOND_AMOUNT to withdrawManager - - // uint256 ageOfYoungestInput = ; - // exitId is the age of the youngest input + a reserved last bit - uint256 exitId = Math.max( - Math.max(reference1.age, reference2.age), - reference3.age - ) << - 1; - if (exitChildToken == reference1.childToken) { - withdrawManager.addExitToQueue( - msg.sender, - exitChildToken, - reference1.rootToken, - reference1.closingBalance.sub(exitTxData.amount1), - exitTxData.txHash, - false, /* isRegularExit */ - exitId - ); - } else if (exitChildToken == reference2.childToken) { - withdrawManager.addExitToQueue( - msg.sender, - exitChildToken, - reference2.rootToken, - exitTxData.amount2.add(reference3.closingBalance), - exitTxData.txHash, - false, /* isRegularExit */ - exitId - ); - } - // @todo Support batch - withdrawManager.addInput( - exitId, - reference1.age, /* age of input */ - msg.sender, /* party whom this utxo belongs to */ - reference1.rootToken - ); - withdrawManager.addInput( - exitId, - reference2.age, - exitTxData.counterParty, - reference2.rootToken - ); - // If exitor did not have pre=exiting balance on the chain for token t2 - // In that case, the following input acts as a "dummy" input UTXO to challenge token t2 spends by the exitor - withdrawManager.addInput(exitId, 0, msg.sender, reference3.rootToken); - } - - /** - * @notice Verify the deprecation of a state update - * @param exit ABI encoded PlasmaExit data - * @param inputUtxo ABI encoded Input UTXO data - * @param challengeData RLP encoded data of the challenge reference tx that encodes the following fields - * headerNumber Header block number of which the reference tx was a part of - * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root - * blockNumber Block number of which the reference tx is a part of - * blockTime Reference tx block time - * blocktxRoot Transactions root of block - * blockReceiptsRoot Receipts root of block - * receipt Receipt of the reference transaction - * receiptProof Merkle proof of the reference receipt - * branchMask Merkle proof branchMask for the receipt - * logIndex Log Index to read from the receipt - * tx Challenge transaction - * txProof Merkle proof of the challenge tx - * @return Whether or not the state is deprecated - */ - function verifyDeprecation( - bytes calldata exit, - bytes calldata inputUtxo, - bytes calldata challengeData - ) external view returns (bool) { - PlasmaExit memory _exit = decodeExit(exit); - (uint256 age, address utxoOwner, address predicate, address childToken) = decodeInputUtxo( - inputUtxo - ); - - RLPReader.RLPItem[] memory _challengeData = challengeData - .toRlpItem() - .toList(); - ExitTxData memory challengeTxData = processExitTx( - _challengeData[10].toBytes(), - utxoOwner - ); - - // receipt alone is not enough for a challenge. It is required to check that the challenge tx was included as well - // Challenge will be considered successful if a more recent LogTransfer event is found - // Interestingly, that will be determined by erc20/721 predicate - ReferenceTxData memory referenceTxData = processLogTransferReceipt( - predicate, - challengeData, - utxoOwner, - true, /* verifyInclusionInCheckpoint */ - true /* isChallenge */ - ); - - // this assertion is required only for erc721 because the spend should correspond to the same NFT - if (registry.predicates(predicate) == Registry.Type.ERC721) { - require( - referenceTxData.closingBalance == _exit.receiptAmountOrNFTId && - challengeTxData.amount1 == _exit.receiptAmountOrNFTId, - "LogTransferReceipt, challengeTx NFT and challenged utxo NFT do not match" - ); - } - // assert transferWithSig was still valid when it was included in the child chain - require( - getChildBlockNumberFromAge(referenceTxData.age) <= - challengeTxData.expiration, - "The marketplace order was expired when it was included" - ); - require( - referenceTxData.childToken == childToken && - challengeTxData.token1 == childToken, - "LogTransferReceipt, challengeTx token and challenged utxo token do not match" - ); - require( - challengeTxData.txHash != _exit.txHash, - "Cannot challenge with the exit tx" - ); - require( - referenceTxData.age > age, - "Age of challenge log in the receipt needs to be more recent than Utxo being challenged" - ); - return true; - } - - function getChildBlockNumberFromAge(uint256 age) - internal - pure - returns (uint256) - { - // age is represented as (getExitableAt(createdAt) << 127) | (blockNumber << 32) | branchMask.toRlpItem().toUint(); - return (age << 129) >> 161; - } - - function validateTokenBalance( - address childToken, - address _childToken, - uint256 closingBalance, - uint256 amount - ) internal view { - require(childToken == _childToken, "Child tokens do not match"); - if (registry.isChildTokenErc721(childToken)) { - require( - closingBalance == amount, - "Same erc721 token was not referenced" - ); - } else { - require( - closingBalance >= amount, - "Exiting with more tokens than referenced" - ); - } - } - - function processLogTransferReceipt( - address predicate, - bytes memory preState, - address participant, - bool verifyInclusionInCheckpoint, - bool isChallenge - ) internal view returns (ReferenceTxData memory _referenceTx) { - bytes memory _preState = IPredicate(predicate).interpretStateUpdate( - abi.encode( - preState, - participant, - verifyInclusionInCheckpoint, - isChallenge - ) - ); - ( - _referenceTx.closingBalance, - _referenceTx.age, - _referenceTx.childToken, - _referenceTx.rootToken - ) = abi.decode(_preState, (uint256, uint256, address, address)); - } - - function processExitTx(bytes memory exitTx, address tradeParticipant) - internal - pure - returns (ExitTxData memory txData) - { - RLPReader.RLPItem[] memory txList = exitTx.toRlpItem().toList(); - require(txList.length == 9, "MALFORMED_WITHDRAW_TX"); - address marketplaceContract = RLPReader.toAddress(txList[3]); // "to" field in tx - (bytes4 funcSig, ExecuteOrderData memory executeOrder) = decodeExecuteOrder( - RLPReader.toBytes(txList[5]) - ); - require( - funcSig == EXECUTE_ORDER_FUNC_SIG, - "Not executeOrder transaction" - ); - txData = verifySignatures( - executeOrder, - marketplaceContract, - tradeParticipant - ); - (, txData.txHash) = getAddressFromTx(txList); - } - - function verifySignatures( - ExecuteOrderData memory executeOrder, - address marketplaceContract, - address tradeParticipant - ) internal pure returns (ExitTxData memory) { - Order memory order1 = decodeOrder(executeOrder.data1); - Order memory order2 = decodeOrder(executeOrder.data2); - bytes32 dataHash = TransferWithSigUtils.getTokenTransferOrderHash( - order1.token, // used to evaluate EIP712_DOMAIN_HASH - marketplaceContract, // spender - order1.amount, - keccak256( - abi.encodePacked( - executeOrder.orderId, - order2.token, - order2.amount - ) - ), - executeOrder.expiration - ); - // Cannot check for deactivated sigs here on the root chain - address tradeParticipant1 = ECVerify.ecrecovery(dataHash, order1.sig); - dataHash = TransferWithSigUtils.getTokenTransferOrderHash( - order2.token, // used to evaluate EIP712_DOMAIN_HASH - marketplaceContract, // spender - order2.amount, - keccak256( - abi.encodePacked( - executeOrder.orderId, - order1.token, - order1.amount - ) - ), - executeOrder.expiration - ); - // Cannot check for deactivated sigs here on the root chain - address tradeParticipant2 = ECVerify.ecrecovery(dataHash, order2.sig); - require( - executeOrder.taker == tradeParticipant2, - "Orders are not complimentary" - ); - // token1 and amount1 in ExitTxData should correspond to what the tradeParticipant signed over (spent in the trade) - if (tradeParticipant1 == tradeParticipant) { - return - ExitTxData( - order1.amount, - order2.amount, - order1.token, - order2.token, - tradeParticipant2, - bytes32(0), - executeOrder.expiration - ); - } else if (tradeParticipant2 == tradeParticipant) { - return - ExitTxData( - order2.amount, - order1.amount, - order2.token, - order1.token, - tradeParticipant1, - bytes32(0), - executeOrder.expiration - ); - } - revert("Provided tx doesnt concern the exitor (tradeParticipant)"); - } - - function decodeExecuteOrder(bytes memory orderData) - internal - pure - returns (bytes4 funcSig, ExecuteOrderData memory order) - { - funcSig = BytesLib.toBytes4(BytesLib.slice(orderData, 0, 4)); - // 32 + 32 bytes of some (yet to figure out) offset - order.orderId = bytes32(BytesLib.toUint(orderData, 68)); - order.expiration = BytesLib.toUint(orderData, 100); - order.taker = address(BytesLib.toUint(orderData, 132)); - uint256 length = BytesLib.toUint(orderData, 164); - order.data1 = BytesLib.slice(orderData, 196, length); - uint256 offset = 196 + length; - length = BytesLib.toUint(orderData, offset); - order.data2 = BytesLib.slice(orderData, offset + 32, length); - } - - function decodeOrder(bytes memory data) - internal - pure - returns (Order memory order) - { - (order.token, order.sig, order.amount) = abi.decode( - data, - (address, bytes, uint256) - ); - } -} diff --git a/contracts/root/predicates/TransferWithSigPredicate.sol b/contracts/root/predicates/TransferWithSigPredicate.sol deleted file mode 100644 index 168228b06..000000000 --- a/contracts/root/predicates/TransferWithSigPredicate.sol +++ /dev/null @@ -1,377 +0,0 @@ -pragma solidity ^0.5.2; - -import {Math} from "openzeppelin-solidity/contracts/math/Math.sol"; -import {RLPReader} from "solidity-rlp/contracts/RLPReader.sol"; -import {SafeMath} from "openzeppelin-solidity/contracts/math/SafeMath.sol"; - -import {BytesLib} from "../../common/lib/BytesLib.sol"; -import {ECVerify} from "../../common/lib/ECVerify.sol"; - -import {IPredicate, PredicateUtils} from "./IPredicate.sol"; -import {IRootChain} from "../IRootChain.sol"; -import {IWithdrawManager} from "../withdrawManager/IWithdrawManager.sol"; -import {Registry} from "../../common/Registry.sol"; -import {TransferWithSigUtils} from "./TransferWithSigUtils.sol"; - -contract TransferWithSigPredicate is PredicateUtils { - using RLPReader for bytes; - using RLPReader for RLPReader.RLPItem; - using SafeMath for uint256; - - // 0xe660b9e4 = keccak256('transferWithSig(bytes,uint256,bytes32,uint256,address)').slice(0, 4) - bytes4 constant TRANSFER_WITH_SIG_FUNC_SIG = 0x19d27d9c; - - Registry public registry; - IRootChain public rootChain; - - struct ReferenceTxData { - uint256 closingBalance; - uint256 age; - address childToken; - address rootToken; - } - - struct ExitTxData { - uint256 amountOrToken; - bytes32 txHash; - address childToken; - address signer; - } - - constructor(address _rootChain, address _withdrawManager, address _registry) - public - { - withdrawManager = IWithdrawManager(_withdrawManager); - rootChain = IRootChain(_rootChain); - registry = Registry(_registry); - } - - /** - * @notice Start an exit from in-flight transferWithSig tx while referencing the exitor's pre-existing balance on the chain for the token - * @param data RLP encoded array of 1 input utxo (data format is to keep it consistent with other startExit methods) - * data[0] should be the exitor's proof-of-funds and encodes the following fields - * headerNumber Header block number of which the reference tx was a part of - * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root - * blockNumber Block number of which the reference tx is a part of - * blockTime Reference tx block time - * blocktxRoot Transactions root of block - * blockReceiptsRoot Receipts root of block - * receipt Receipt of the reference transaction - * receiptProof Merkle proof of the reference receipt - * branchMask Merkle proof branchMask for the receipt - * logIndex Log Index to read from the receipt - * @param exitTx ERC20 transfer executed using a transferWithSig - */ - function startExitForOutgoingErc20Transfer( - bytes calldata data, - bytes calldata exitTx - ) external payable isBondProvided { - RLPReader.RLPItem[] memory referenceTx = data.toRlpItem().toList(); - bytes memory preState = referenceTx[0].toBytes(); - (ExitTxData memory exitTxData, uint256 expiration) = processExitTx( - exitTx - ); - require( - expiration > rootChain.getLastChildBlock(), - "The inflight exit is not valid, because the transfer sig has expired" - ); - require( - exitTxData.signer == msg.sender, - "Should be an outgoing transfer" - ); - address erc20Predicate = registry.erc20Predicate(); - ReferenceTxData memory referenceTxData = processLogTransferReceipt( - erc20Predicate, - preState, - msg.sender, - true, /* verifyInclusionInCheckpoint */ - false /* isChallenge */ - ); - require( - exitTxData.childToken == referenceTxData.childToken, - "Reference and exit tx do not correspond to the same child token" - ); - // The closing balance of the referenced tx should be >= exit amount in exitTx - require( - referenceTxData.closingBalance >= exitTxData.amountOrToken, - "Exiting with more tokens than referenced" - ); - - sendBond(); // send BOND_AMOUNT to withdrawManager - - // last bit is to differentiate whether the sender or receiver of the in-flight tx is starting an exit - uint256 exitId = referenceTxData.age << 1; - exitId |= 1; // since msg.sender == exitTxData.signer - withdrawManager.addExitToQueue( - msg.sender, - referenceTxData.childToken, - referenceTxData.rootToken, - referenceTxData.closingBalance.sub(exitTxData.amountOrToken), - exitTxData.txHash, - false, /* isRegularExit */ - exitId - ); - withdrawManager.addInput( - exitId, - referenceTxData.age, - msg.sender, - referenceTxData.rootToken - ); - } - - /** - * @notice Start an exit from in-flight transferWithSig tx while also referencing the exitor's pre-existing balance on the chain for the token - * @param data RLP encoded array of 2 input utxos - * data[0] should be the counterparty's proof-of-funds - * data[1] should be the exitor's proof-of-funds - * data[n] encodes the following fields - * headerNumber Header block number of which the reference tx was a part of - * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root - * blockNumber Block number of which the reference tx is a part of - * blockTime Reference tx block time - * blocktxRoot Transactions root of block - * blockReceiptsRoot Receipts root of block - * receipt Receipt of the reference transaction - * receiptProof Merkle proof of the reference receipt - * branchMask Merkle proof branchMask for the receipt - * logIndex Log Index to read from the receipt - * @param exitTx ERC20 transfer executed using a transferWithSig - */ - function startExitForIncomingErc20Transfer( - bytes calldata data, - bytes calldata exitTx - ) external payable isBondProvided { - RLPReader.RLPItem[] memory referenceTx = data.toRlpItem().toList(); - bytes memory preState = referenceTx[0].toBytes(); - (ExitTxData memory exitTxData, uint256 expiration) = processExitTx( - exitTx - ); - require( - expiration > rootChain.getLastChildBlock(), - "The inflight exit is not valid, because the transfer sig has expired" - ); - require( - exitTxData.signer != msg.sender, - "Should be an incoming transfer" - ); - address erc20Predicate = registry.erc20Predicate(); - // Process the receipt (i.e. proof-of-funds of the counterparty) of the referenced tx - ReferenceTxData memory referenceTxData = processLogTransferReceipt( - erc20Predicate, - preState, - exitTxData.signer, - true, /* verifyInclusionInCheckpoint */ - false /* isChallenge */ - ); - require( - exitTxData.childToken == referenceTxData.childToken, - "Reference and exit tx do not correspond to the same child token" - ); - // The closing balance of the referenced tx should be >= exit amount in exitTx - require( - referenceTxData.closingBalance >= exitTxData.amountOrToken, - "Exiting with more tokens than referenced" - ); - - // referenceTx.length == 2 means the exitor sent along another input UTXO to the exit tx - // This will be used to exit with the exitor's pre-existing balance on the chain for the token - ReferenceTxData memory _referenceTxData; - if (referenceTx.length > 1) { - preState = referenceTx[1].toBytes(); - _referenceTxData = processLogTransferReceipt( - erc20Predicate, - preState, - msg.sender, - true, /* verifyInclusionInCheckpoint */ - false /* isChallenge */ - ); - require( - _referenceTxData.childToken == referenceTxData.childToken, - "child tokens in the referenced txs do not match" - ); - require( - _referenceTxData.rootToken == referenceTxData.rootToken, - "root tokens in the referenced txs do not match" - ); - } - - sendBond(); // send BOND_AMOUNT to withdrawManager - - // last bit is to differentiate whether the sender or receiver of the in-flight tx is starting an exit - uint256 exitId = Math.max(referenceTxData.age, _referenceTxData.age) << - 1; - withdrawManager.addExitToQueue( - msg.sender, - referenceTxData.childToken, - referenceTxData.rootToken, - exitTxData.amountOrToken.add(_referenceTxData.closingBalance), - exitTxData.txHash, - false, /* isRegularExit */ - exitId - ); - withdrawManager.addInput( - exitId, - referenceTxData.age, - exitTxData.signer, - referenceTxData.rootToken - ); - // Even if referenceTx.length == 1, the following input acts as a "dummy" input UTXO to challenge token spends by the exitor - withdrawManager.addInput( - exitId, - _referenceTxData.age, - msg.sender, - referenceTxData.rootToken - ); - } - - /** - * @notice Verify the deprecation of a state update - * @param exit ABI encoded PlasmaExit data - * @param inputUtxo ABI encoded Input UTXO data - * @param challengeData RLP encoded data of the challenge reference tx that encodes the following fields - * headerNumber Header block number of which the reference tx was a part of - * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root - * blockNumber Block number of which the reference tx is a part of - * blockTime Reference tx block time - * blocktxRoot Transactions root of block - * blockReceiptsRoot Receipts root of block - * receipt Receipt of the reference transaction - * receiptProof Merkle proof of the reference receipt - * branchMask Merkle proof branchMask for the receipt - * logIndex Log Index to read from the receipt - * tx Challenge transaction - * txProof Merkle proof of the challenge tx - * @return Whether or not the state is deprecated - */ - function verifyDeprecation( - bytes calldata exit, - bytes calldata inputUtxo, - bytes calldata challengeData - ) external view returns (bool) { - PlasmaExit memory _exit = decodeExit(exit); - (uint256 age, address utxoOwner, address predicate, address childToken) = decodeInputUtxo( - inputUtxo - ); - - RLPReader.RLPItem[] memory _challengeData = challengeData - .toRlpItem() - .toList(); - (ExitTxData memory challengeTxData, uint256 expiration) = processExitTx( - _challengeData[10].toBytes() - ); - require( - challengeTxData.signer == utxoOwner, - "utxoOwner is not transferWithSig signer" - ); - // receipt alone is not enough for a challenge. It is required to check that the challenge tx was included as well - // Challenge will be considered successful if a more recent LogTransfer event is found - // Interestingly, that will be determined by erc20/721 predicate - ReferenceTxData memory referenceTxData = processLogTransferReceipt( - predicate, - challengeData, - utxoOwner, - true, /* verifyInclusionInCheckpoint */ - true /* isChallenge */ - ); - // this assertion is required only for erc721 because the spend should correspond to the same NFT - if (registry.predicates(predicate) == Registry.Type.ERC721) { - require( - referenceTxData.closingBalance == _exit.receiptAmountOrNFTId && - challengeTxData.amountOrToken == _exit.receiptAmountOrNFTId, - "LogTransferReceipt, challengeTx NFT and challenged utxo NFT do not match" - ); - } - // assert transferWithSig was still valid when it was included in the child chain - require( - getChildBlockNumberFromAge(referenceTxData.age) <= expiration, - "The transferWithSig order expired when it was included" - ); - require( - referenceTxData.childToken == childToken && - challengeTxData.childToken == childToken, - "LogTransferReceipt, challengeTx token and challenged utxo token do not match" - ); - require( - challengeTxData.txHash != _exit.txHash, - "Cannot challenge with the exit tx" - ); - require( - referenceTxData.age > age, - "Age of challenge log in the receipt needs to be more recent than Utxo being challenged" - ); - return true; - } - - function getChildBlockNumberFromAge(uint256 age) - internal - pure - returns (uint256) - { - // age is represented as (getExitableAt(createdAt) << 127) | (blockNumber << 32) | branchMask.toRlpItem().toUint(); - return (age << 129) >> 161; - } - - function processLogTransferReceipt( - address predicate, - bytes memory preState, - address participant, - bool verifyInclusionInCheckpoint, - bool isChallenge - ) internal view returns (ReferenceTxData memory _referenceTx) { - bytes memory _preState = IPredicate(predicate).interpretStateUpdate( - abi.encode( - preState, - participant, - verifyInclusionInCheckpoint, - isChallenge - ) - ); - ( - _referenceTx.closingBalance, - _referenceTx.age, - _referenceTx.childToken, - _referenceTx.rootToken - ) = abi.decode(_preState, (uint256, uint256, address, address)); - } - - /** - * @notice Process the challenge transaction - * @param exitTx Challenge transaction - * @return ExitTxData Parsed challenge transaction data - */ - function processExitTx(bytes memory exitTx) - internal - pure - returns (ExitTxData memory txData, uint256 expiration) - { - RLPReader.RLPItem[] memory txList = exitTx.toRlpItem().toList(); - require(txList.length == 9, "MALFORMED_TX"); - txData.childToken = RLPReader.toAddress(txList[3]); // corresponds to "to" field in tx - address spender; - // Signer of this tx is supposed to be the authorized spender - (spender, txData.txHash) = getAddressFromTx(txList); - - bytes memory txPayload = RLPReader.toBytes(txList[5]); - bytes4 funcSig = BytesLib.toBytes4(BytesLib.slice(txPayload, 0, 4)); - require( - funcSig == TRANSFER_WITH_SIG_FUNC_SIG, - "Not transferWithSig transaction" - ); - // 32 bytes offset - txData.amountOrToken = BytesLib.toUint(txPayload, 36); - bytes32 data = bytes32(BytesLib.toUint(txPayload, 68)); - expiration = BytesLib.toUint(txPayload, 100); - // address to = address(BytesLib.toUint(txPayload, 132)); - uint256 siglength = BytesLib.toUint(txPayload, 164); - bytes memory sig = BytesLib.slice(txPayload, 196, siglength); - bytes32 dataHash = TransferWithSigUtils.getTokenTransferOrderHash( - txData.childToken, - spender, - txData.amountOrToken, - data, - expiration - ); - // The signer of the transfer order is the "real signer" of the exit tx - txData.signer = ECVerify.ecrecovery(dataHash, sig); - } -} diff --git a/contracts/root/predicates/TransferWithSigUtils.sol.template b/contracts/root/predicates/TransferWithSigUtils.sol.template deleted file mode 100644 index 4e081b866..000000000 --- a/contracts/root/predicates/TransferWithSigUtils.sol.template +++ /dev/null @@ -1,62 +0,0 @@ -pragma solidity ^0.5.2; - - -library TransferWithSigUtils { - function getTokenTransferOrderHash(address token, address spender, uint256 amount, bytes32 data, uint256 expiration) - public - pure - returns (bytes32 orderHash) - { - orderHash = hashEIP712Message(token, hashTokenTransferOrder(spender, amount, data, expiration)); - } - - function hashTokenTransferOrder(address spender, uint256 amount, bytes32 data, uint256 expiration) - internal - pure - returns (bytes32 result) - { - string memory EIP712_TOKEN_TRANSFER_ORDER_SCHEMA = "TokenTransferOrder(address spender,uint256 tokenIdOrAmount,bytes32 data,uint256 expiration)"; - bytes32 EIP712_TOKEN_TRANSFER_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked(EIP712_TOKEN_TRANSFER_ORDER_SCHEMA)); - bytes32 schemaHash = EIP712_TOKEN_TRANSFER_ORDER_SCHEMA_HASH; - assembly { - // Load free memory pointer - let memPtr := mload(64) - mstore(memPtr, schemaHash) // hash of schema - mstore(add(memPtr, 32), and(spender, 0xffffffffffffffffffffffffffffffffffffffff)) // spender - mstore(add(memPtr, 64), amount) // amount - mstore(add(memPtr, 96), data) // hash of data - mstore(add(memPtr, 128), expiration) // expiration - // Compute hash - result := keccak256(memPtr, 160) - } - } - - function hashEIP712Message(address token, bytes32 hashStruct) - internal - pure - returns (bytes32 result) - { - string memory EIP712_DOMAIN_SCHEMA = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; - bytes32 EIP712_DOMAIN_SCHEMA_HASH = keccak256(abi.encodePacked(EIP712_DOMAIN_SCHEMA)); - string memory EIP712_DOMAIN_NAME = "Matic Network"; - string memory EIP712_DOMAIN_VERSION = "1"; - uint256 EIP712_DOMAIN_CHAINID = {{ borChainId }}; - bytes32 EIP712_DOMAIN_HASH = keccak256(abi.encode( - EIP712_DOMAIN_SCHEMA_HASH, - keccak256(bytes(EIP712_DOMAIN_NAME)), - keccak256(bytes(EIP712_DOMAIN_VERSION)), - EIP712_DOMAIN_CHAINID, - token - )); - bytes32 domainHash = EIP712_DOMAIN_HASH; - assembly { - // Load free memory pointer - let memPtr := mload(64) - mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header - mstore(add(memPtr, 2), domainHash) // EIP712 domain hash - mstore(add(memPtr, 34), hashStruct) // Hash of struct - // Compute hash - result := keccak256(memPtr, 66) - } - } -} diff --git a/contracts/test/MarketplacePredicateTest.sol b/contracts/test/MarketplacePredicateTest.sol deleted file mode 100644 index 770094b24..000000000 --- a/contracts/test/MarketplacePredicateTest.sol +++ /dev/null @@ -1,73 +0,0 @@ -pragma solidity ^0.5.2; - -import {RLPReader} from "solidity-rlp/contracts/RLPReader.sol"; - -import { - MarketplacePredicate -} from "../root/predicates/MarketplacePredicate.sol"; -import {ERC20Predicate} from "../root/predicates/ERC20Predicate.sol"; - -contract MarketplacePredicateTest is MarketplacePredicate { - constructor() - public - MarketplacePredicate(address(0x0), address(0x0), address(0x0)) - {} - - function processLogTransferReceiptTest( - address predicate, - bytes memory data, - address participant - ) public view returns (bytes memory b) { - ReferenceTxData memory _referenceTx = super.processLogTransferReceipt( - predicate, - data, - participant, - false, - false - ); - b = abi.encode( - _referenceTx.closingBalance, - _referenceTx.age, - _referenceTx.childToken, - _referenceTx.rootToken - ); - } - - function processExitTx(bytes memory exitTx) - public - view - returns (bytes memory b) - { - ExitTxData memory txData = super.processExitTx(exitTx, msg.sender); - b = abi.encode( - txData.amount1, - txData.amount2, - txData.token1, - txData.token2, - txData.counterParty - ); - } - - function testGetAddressFromTx(bytes memory exitTx) - public - pure - returns (address signer, bytes32 txHash) - { - RLPReader.RLPItem[] memory txList = exitTx.toRlpItem().toList(); - (signer, txHash) = getAddressFromTx(txList); - } - - function decodeExitTx(bytes memory exitTx) - internal - pure - returns (ExitTxData memory txData) - { - ( - txData.amount1, - txData.amount2, - txData.token1, - txData.token2, - txData.counterParty - ) = abi.decode(exitTx, (uint256, uint256, address, address, address)); - } -}