Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #366 from maticnetwork/typed-transactions
Browse files Browse the repository at this point in the history
Typed transactions
  • Loading branch information
jdkanani authored Sep 17, 2021
2 parents aac51fb + a03c4dc commit 17e3087
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 77 deletions.
4 changes: 1 addition & 3 deletions contracts/child/ChildERC721.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
pragma solidity ^0.5.2;

import {
ERC721Full
} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import {ERC721Full} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";

import "./ChildToken.sol";
import "./misc/IParentToken.sol";
Expand Down
170 changes: 170 additions & 0 deletions contracts/common/lib/ExitPayloadReader.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
pragma solidity 0.5.17;

import {RLPReader} from "solidity-rlp/contracts/RLPReader.sol";
import {BytesLib} from "./BytesLib.sol";

library ExitPayloadReader {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;

uint8 constant WORD_SIZE = 32;

struct ExitPayload {
RLPReader.RLPItem[] data;
}

struct Receipt {
RLPReader.RLPItem[] data;
bytes raw;
uint256 logIndex;
}

struct Log {
RLPReader.RLPItem data;
RLPReader.RLPItem[] list;
}

struct LogTopics {
RLPReader.RLPItem[] data;
}

function toExitPayload(bytes memory data)
internal
pure
returns (ExitPayload memory)
{
RLPReader.RLPItem[] memory payloadData = data
.toRlpItem()
.toList();

return ExitPayload(payloadData);
}

function copy(uint src, uint dest, uint len) private pure {
if (len == 0) return;

// copy as many word sizes as possible
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}

src += WORD_SIZE;
dest += WORD_SIZE;
}

// left over bytes. Mask is used to remove unwanted bytes from the word
uint mask = 256 ** (WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}
}

function getHeaderNumber(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[0].toUint();
}

function getBlockProof(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[1].toBytes();
}

function getBlockNumber(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[2].toUint();
}

function getBlockTime(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[3].toUint();
}

function getTxRoot(ExitPayload memory payload) internal pure returns(bytes32) {
return bytes32(payload.data[4].toUint());
}

function getReceiptRoot(ExitPayload memory payload) internal pure returns(bytes32) {
return bytes32(payload.data[5].toUint());
}

function getReceipt(ExitPayload memory payload) internal pure returns(Receipt memory receipt) {
receipt.raw = payload.data[6].toBytes();
RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();

if (receiptItem.isList()) {
// legacy tx
receipt.data = receiptItem.toList();
} else {
// pop first byte before parsting receipt
bytes memory typedBytes = receipt.raw;
bytes memory result = new bytes(typedBytes.length - 1);
uint256 srcPtr;
uint256 destPtr;
assembly {
srcPtr := add(33, typedBytes)
destPtr := add(0x20, result)
}

copy(srcPtr, destPtr, result.length);
receipt.data = result.toRlpItem().toList();
}

receipt.logIndex = getReceiptLogIndex(payload);
return receipt;
}

function getReceiptProof(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[7].toBytes();
}

function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[8].toBytes();
}

function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[8].toUint();
}

function getReceiptLogIndex(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[9].toUint();
}

function getTx(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[10].toBytes();
}

function getTxProof(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[11].toBytes();
}

// Receipt methods
function toBytes(Receipt memory receipt) internal pure returns(bytes memory) {
return receipt.raw;
}

function getLog(Receipt memory receipt) internal pure returns(Log memory) {
RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
return Log(logData, logData.toList());
}

// Log methods
function getEmitter(Log memory log) internal pure returns(address) {
return RLPReader.toAddress(log.list[0]);
}

function getTopics(Log memory log) internal pure returns(LogTopics memory) {
return LogTopics(log.list[1].toList());
}

function getData(Log memory log) internal pure returns(bytes memory) {
return log.list[2].toBytes();
}

function toRlpBytes(Log memory log) internal pure returns(bytes memory) {
return log.data.toRlpBytes();
}

// LogTopics methods
function getField(LogTopics memory topics, uint256 index) internal pure returns(RLPReader.RLPItem memory) {
return topics.data[index];
}
}
2 changes: 1 addition & 1 deletion contracts/common/lib/Merkle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ library Merkle {
uint256 index,
bytes32 rootHash,
bytes memory proof
) public pure returns (bool) {
) internal pure returns (bool) {
require(proof.length % 32 == 0, "Invalid proof length");
uint256 proofHeight = proof.length / 32;
// Proof of size n means, height of the tree is n+1.
Expand Down
12 changes: 3 additions & 9 deletions contracts/common/tokens/ERC721PlasmaMintable.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
pragma solidity ^0.5.2;

import {
ERC721Mintable
} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Mintable.sol";
import {
ERC721MetadataMintable
} from "openzeppelin-solidity/contracts/token/ERC721/ERC721MetadataMintable.sol";
import {
ERC721Metadata
} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Metadata.sol";
import {ERC721Mintable} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Mintable.sol";
import {ERC721MetadataMintable} from "openzeppelin-solidity/contracts/token/ERC721/ERC721MetadataMintable.sol";
import {ERC721Metadata} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Metadata.sol";

contract ERC721PlasmaMintable is ERC721Mintable, ERC721MetadataMintable {
constructor(string memory name, string memory symbol)
Expand Down
4 changes: 1 addition & 3 deletions contracts/common/tokens/RootERC721.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
pragma solidity ^0.5.2;

import {
ERC721Full
} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import {ERC721Full} from "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";

contract RootERC721 is ERC721Full {
constructor(string memory name, string memory symbol)
Expand Down
40 changes: 19 additions & 21 deletions contracts/root/predicates/ERC20PredicateBurnOnly.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,58 @@ 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 {ExitPayloadReader} from "../../common/lib/ExitPayloadReader.sol";
import {IErcPredicate} from "./IPredicate.sol";
import {Registry} from "../../common/Registry.sol";
import {
WithdrawManagerHeader
} from "../withdrawManager/WithdrawManagerStorage.sol";
import {WithdrawManagerHeader} from "../withdrawManager/WithdrawManagerStorage.sol";

contract ERC20PredicateBurnOnly is IErcPredicate {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
using SafeMath for uint256;

using ExitPayloadReader for bytes;
using ExitPayloadReader for ExitPayloadReader.ExitPayload;
using ExitPayloadReader for ExitPayloadReader.Receipt;
using ExitPayloadReader for ExitPayloadReader.Log;
using ExitPayloadReader for ExitPayloadReader.LogTopics;

// keccak256('Withdraw(address,address,uint256,uint256,uint256)')
bytes32 constant WITHDRAW_EVENT_SIG = 0xebff2602b3f468259e1e99f613fed6691f3a6526effe6ef3e768ba7ae7a36c4f;

Registry registry;

constructor(
address _withdrawManager,
address _depositManager,
address _registry
address _depositManager
) public IErcPredicate(_withdrawManager, _depositManager) {
registry = Registry(_registry);
}

function startExitWithBurntTokens(bytes calldata data) external {
RLPReader.RLPItem[] memory referenceTxData = data.toRlpItem().toList();
bytes memory receipt = referenceTxData[6].toBytes();
RLPReader.RLPItem[] memory inputItems = receipt.toRlpItem().toList();
uint256 logIndex = referenceTxData[9].toUint();
ExitPayloadReader.ExitPayload memory payload = data.toExitPayload();
ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
uint256 logIndex = payload.getReceiptLogIndex();
require(logIndex < MAX_LOGS, "Supporting a max of 10 logs");
uint256 age = withdrawManager.verifyInclusion(
data,
0, /* offset */
false /* verifyTxInclusion */
);
inputItems = inputItems[3].toList()[logIndex].toList(); // select log based on given logIndex
ExitPayloadReader.Log memory log = receipt.getLog();

// "address" (contract address that emitted the log) field in the receipt
address childToken = RLPReader.toAddress(inputItems[0]);
bytes memory logData = inputItems[2].toBytes();
inputItems = inputItems[1].toList(); // topics
address childToken = log.getEmitter();
ExitPayloadReader.LogTopics memory topics = log.getTopics();
// now, inputItems[i] refers to i-th (0-based) topic in the topics array
// event Withdraw(address indexed token, address indexed from, uint256 amountOrTokenId, uint256 input1, uint256 output1)
require(
bytes32(inputItems[0].toUint()) == WITHDRAW_EVENT_SIG,
bytes32(topics.getField(0).toUint()) == WITHDRAW_EVENT_SIG,
"Not a withdraw event signature"
);
address rootToken = address(RLPReader.toUint(inputItems[1]));
require(
msg.sender == address(inputItems[2].toUint()), // from
msg.sender == address(topics.getField(2).toUint()), // from
"Withdrawer and burn exit tx do not match"
);
uint256 exitAmount = BytesLib.toUint(logData, 0); // amountOrTokenId
address rootToken = address(topics.getField(1).toUint());
uint256 exitAmount = BytesLib.toUint(log.getData(), 0); // amountOrTokenId
withdrawManager.addExitToQueue(
msg.sender,
childToken,
Expand Down
33 changes: 19 additions & 14 deletions contracts/root/predicates/ERC721PredicateBurnOnly.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import {SafeMath} from "openzeppelin-solidity/contracts/math/SafeMath.sol";
import {BytesLib} from "../../common/lib/BytesLib.sol";
import {Common} from "../../common/lib/Common.sol";
import {RLPEncode} from "../../common/lib/RLPEncode.sol";

import {ExitPayloadReader} from "../../common/lib/ExitPayloadReader.sol";
import {IErcPredicate} from "./IPredicate.sol";

contract ERC721PredicateBurnOnly is IErcPredicate {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
using SafeMath for uint256;

using ExitPayloadReader for bytes;
using ExitPayloadReader for ExitPayloadReader.ExitPayload;
using ExitPayloadReader for ExitPayloadReader.Receipt;
using ExitPayloadReader for ExitPayloadReader.Log;
using ExitPayloadReader for ExitPayloadReader.LogTopics;

// keccak256('Withdraw(address,address,uint256)')
bytes32 constant WITHDRAW_EVENT_SIG = 0x9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb;

Expand All @@ -37,35 +43,34 @@ contract ERC721PredicateBurnOnly is IErcPredicate {
public
returns (bytes memory)
{
RLPReader.RLPItem[] memory referenceTxData = data.toRlpItem().toList();
bytes memory receipt = referenceTxData[6].toBytes();
RLPReader.RLPItem[] memory inputItems = receipt.toRlpItem().toList();
uint256 age = withdrawManager.verifyInclusion(
data,
0, /* offset */
false /* verifyTxInclusion */
);
uint256 logIndex = referenceTxData[9].toUint();

ExitPayloadReader.ExitPayload memory payload = data.toExitPayload();
ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
uint256 logIndex = payload.getReceiptLogIndex();
require(logIndex < MAX_LOGS, "Supporting a max of 10 logs");
inputItems = inputItems[3].toList()[logIndex].toList(); // select log based on given logIndex
ExitPayloadReader.Log memory log = receipt.getLog();

// "address" (contract address that emitted the log) field in the receipt
address childToken = RLPReader.toAddress(inputItems[0]);
bytes memory logData = inputItems[2].toBytes();
inputItems = inputItems[1].toList(); // topics
address childToken = log.getEmitter();
ExitPayloadReader.LogTopics memory topics = log.getTopics();
// now, inputItems[i] refers to i-th (0-based) topic in the topics array
// event Withdraw(address indexed token, address indexed from, uint256 amountOrTokenId, uint256 input1, uint256 output1)
require(
bytes32(inputItems[0].toUint()) == WITHDRAW_EVENT_SIG,
bytes32(topics.getField(0).toUint()) == WITHDRAW_EVENT_SIG,
"Not a withdraw event signature"
);
address rootToken = address(RLPReader.toUint(inputItems[1]));
require(
msg.sender == address(inputItems[2].toUint()), // from
msg.sender == address(topics.getField(2).toUint()), // from
"Withdrawer and burn exit tx do not match"
);
uint256 tokenId = BytesLib.toUint(logData, 0);
uint256 exitId = age << 1; // last bit is reserved for housekeeping in erc20Predicate
address rootToken = address(topics.getField(1).toUint());
uint256 tokenId = BytesLib.toUint(log.getData(), 0);
uint256 exitId = age << 1;
withdrawManager.addExitToQueue(
msg.sender,
childToken,
Expand Down
4 changes: 1 addition & 3 deletions contracts/root/predicates/IPredicate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {RLPEncode} from "../../common/lib/RLPEncode.sol";

import {IWithdrawManager} from "../withdrawManager/IWithdrawManager.sol";
import {IDepositManager} from "../depositManager/IDepositManager.sol";
import {
ExitsDataStructure
} from "../withdrawManager/WithdrawManagerStorage.sol";
import {ExitsDataStructure} from "../withdrawManager/WithdrawManagerStorage.sol";
import {ChainIdMixin} from "../../common/mixin/ChainIdMixin.sol";

interface IPredicate {
Expand Down
Loading

0 comments on commit 17e3087

Please sign in to comment.