文件 1 的 1:ToshimonDojoRootTunnel.sol
pragma solidity 0.7.3;
library RLPReader {
uint8 constant STRING_SHORT_START = 0x80;
uint8 constant STRING_LONG_START = 0xb8;
uint8 constant LIST_SHORT_START = 0xc0;
uint8 constant LIST_LONG_START = 0xf8;
uint8 constant WORD_SIZE = 32;
struct RLPItem {
uint256 len;
uint256 memPtr;
}
function toRlpItem(bytes memory item)
internal
pure
returns (RLPItem memory)
{
require(item.length > 0, "RLPReader: INVALID_BYTES_LENGTH");
uint256 memPtr;
assembly {
memPtr := add(item, 0x20)
}
return RLPItem(item.length, memPtr);
}
function toList(RLPItem memory item)
internal
pure
returns (RLPItem[] memory)
{
require(isList(item), "RLPReader: ITEM_NOT_LIST");
uint256 items = numItems(item);
RLPItem[] memory result = new RLPItem[](items);
uint256 listLength = _itemLength(item.memPtr);
require(listLength == item.len, "RLPReader: LIST_DECODED_LENGTH_MISMATCH");
uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
uint256 dataLen;
for (uint256 i = 0; i < items; i++) {
dataLen = _itemLength(memPtr);
result[i] = RLPItem(dataLen, memPtr);
memPtr = memPtr + dataLen;
}
return result;
}
function isList(RLPItem memory item) internal pure returns (bool) {
uint8 byte0;
uint256 memPtr = item.memPtr;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < LIST_SHORT_START) return false;
return true;
}
function toRlpBytes(RLPItem memory item)
internal
pure
returns (bytes memory)
{
bytes memory result = new bytes(item.len);
uint256 ptr;
assembly {
ptr := add(0x20, result)
}
copy(item.memPtr, ptr, item.len);
return result;
}
function toAddress(RLPItem memory item) internal pure returns (address) {
require(!isList(item), "RLPReader: DECODING_LIST_AS_ADDRESS");
require(item.len == 21, "RLPReader: INVALID_ADDRESS_LENGTH");
return address(toUint(item));
}
function toUint(RLPItem memory item) internal pure returns (uint256) {
require(!isList(item), "RLPReader: DECODING_LIST_AS_UINT");
require(item.len <= 33, "RLPReader: INVALID_UINT_LENGTH");
uint256 itemLength = _itemLength(item.memPtr);
require(itemLength == item.len, "RLPReader: UINT_DECODED_LENGTH_MISMATCH");
uint256 offset = _payloadOffset(item.memPtr);
uint256 len = item.len - offset;
uint256 result;
uint256 memPtr = item.memPtr + offset;
assembly {
result := mload(memPtr)
if lt(len, 32) {
result := div(result, exp(256, sub(32, len)))
}
}
return result;
}
function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
uint256 itemLength = _itemLength(item.memPtr);
require(itemLength == item.len, "RLPReader: UINT_STRICT_DECODED_LENGTH_MISMATCH");
require(item.len == 33, "RLPReader: INVALID_UINT_STRICT_LENGTH");
uint256 result;
uint256 memPtr = item.memPtr + 1;
assembly {
result := mload(memPtr)
}
return result;
}
function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
uint256 listLength = _itemLength(item.memPtr);
require(listLength == item.len, "RLPReader: BYTES_DECODED_LENGTH_MISMATCH");
uint256 offset = _payloadOffset(item.memPtr);
uint256 len = item.len - offset;
bytes memory result = new bytes(len);
uint256 destPtr;
assembly {
destPtr := add(0x20, result)
}
copy(item.memPtr + offset, destPtr, len);
return result;
}
function numItems(RLPItem memory item) private pure returns (uint256) {
uint256 count = 0;
uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
uint256 endPtr = item.memPtr + item.len;
while (currPtr < endPtr) {
currPtr = currPtr + _itemLength(currPtr);
require(currPtr <= endPtr, "RLPReader: NUM_ITEMS_DECODED_LENGTH_MISMATCH");
count++;
}
return count;
}
function _itemLength(uint256 memPtr) private pure returns (uint256) {
uint256 itemLen;
uint256 byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START) itemLen = 1;
else if (byte0 < STRING_LONG_START)
itemLen = byte0 - STRING_SHORT_START + 1;
else if (byte0 < LIST_SHORT_START) {
assembly {
let byteLen := sub(byte0, 0xb7)
memPtr := add(memPtr, 1)
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen)))
itemLen := add(dataLen, add(byteLen, 1))
}
} else if (byte0 < LIST_LONG_START) {
itemLen = byte0 - LIST_SHORT_START + 1;
} else {
assembly {
let byteLen := sub(byte0, 0xf7)
memPtr := add(memPtr, 1)
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen)))
itemLen := add(dataLen, add(byteLen, 1))
}
}
return itemLen;
}
function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
uint256 byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START) return 0;
else if (
byte0 < STRING_LONG_START ||
(byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)
) return 1;
else if (byte0 < LIST_SHORT_START)
return byte0 - (STRING_LONG_START - 1) + 1;
else return byte0 - (LIST_LONG_START - 1) + 1;
}
function copy(
uint256 src,
uint256 dest,
uint256 len
) private pure {
if (len == 0) return;
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
}
uint256 mask = 256**(WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
}
pragma solidity 0.7.3;
library MerklePatriciaProof {
function verify(
bytes memory value,
bytes memory encodedPath,
bytes memory rlpParentNodes,
bytes32 root
) internal pure returns (bool) {
RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);
bytes memory currentNode;
RLPReader.RLPItem[] memory currentNodeList;
bytes32 nodeKey = root;
uint256 pathPtr = 0;
bytes memory path = _getNibbleArray(encodedPath);
if (path.length == 0) {
return false;
}
for (uint256 i = 0; i < parentNodes.length; i++) {
if (pathPtr > path.length) {
return false;
}
currentNode = RLPReader.toRlpBytes(parentNodes[i]);
if (nodeKey != keccak256(currentNode)) {
return false;
}
currentNodeList = RLPReader.toList(parentNodes[i]);
if (currentNodeList.length == 17) {
if (pathPtr == path.length) {
if (
keccak256(RLPReader.toBytes(currentNodeList[16])) ==
keccak256(value)
) {
return true;
} else {
return false;
}
}
uint8 nextPathNibble = uint8(path[pathPtr]);
if (nextPathNibble > 16) {
return false;
}
nodeKey = bytes32(
RLPReader.toUintStrict(currentNodeList[nextPathNibble])
);
pathPtr += 1;
} else if (currentNodeList.length == 2) {
uint256 traversed = _nibblesToTraverse(
RLPReader.toBytes(currentNodeList[0]),
path,
pathPtr
);
if (pathPtr + traversed == path.length) {
if (
keccak256(RLPReader.toBytes(currentNodeList[1])) ==
keccak256(value)
) {
return true;
} else {
return false;
}
}
if (traversed == 0) {
return false;
}
pathPtr += traversed;
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
} else {
return false;
}
}
}
function _nibblesToTraverse(
bytes memory encodedPartialPath,
bytes memory path,
uint256 pathPtr
) private pure returns (uint256) {
uint256 len = 0;
bytes memory partialPath = _getNibbleArray(encodedPartialPath);
bytes memory slicedPath = new bytes(partialPath.length);
for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
bytes1 pathNibble = path[i];
slicedPath[i - pathPtr] = pathNibble;
}
if (keccak256(partialPath) == keccak256(slicedPath)) {
len = partialPath.length;
} else {
len = 0;
}
return len;
}
function _getNibbleArray(bytes memory b)
internal
pure
returns (bytes memory)
{
bytes memory nibbles = "";
if (b.length > 0) {
uint8 offset;
uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
if (hpNibble == 1 || hpNibble == 3) {
nibbles = new bytes(b.length * 2 - 1);
bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
nibbles[0] = oddNibble;
offset = 1;
} else {
nibbles = new bytes(b.length * 2 - 2);
offset = 0;
}
for (uint256 i = offset; i < nibbles.length; i++) {
nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
}
}
return nibbles;
}
function _getNthNibbleOfBytes(uint256 n, bytes memory str)
private
pure
returns (bytes1)
{
return
bytes1(
n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10
);
}
}
pragma solidity 0.7.3;
library Merkle {
function checkMembership(
bytes32 leaf,
uint256 index,
bytes32 rootHash,
bytes memory proof
) internal pure returns (bool) {
require(proof.length % 32 == 0, "Invalid proof length");
uint256 proofHeight = proof.length / 32;
require(index < 2 ** proofHeight, "Leaf index is too big");
bytes32 proofElement;
bytes32 computedHash = leaf;
for (uint256 i = 32; i <= proof.length; i += 32) {
assembly {
proofElement := mload(add(proof, i))
}
if (index % 2 == 0) {
computedHash = keccak256(
abi.encodePacked(computedHash, proofElement)
);
} else {
computedHash = keccak256(
abi.encodePacked(proofElement, computedHash)
);
}
index = index / 2;
}
return computedHash == rootHash;
}
}
pragma solidity 0.7.3;
interface IFxStateSender {
function sendMessageToChild(address _receiver, bytes calldata _data) external;
}
contract ICheckpointManager {
struct HeaderBlock {
bytes32 root;
uint256 start;
uint256 end;
uint256 createdAt;
address proposer;
}
mapping(uint256 => HeaderBlock) public headerBlocks;
}
abstract contract FxBaseRootTunnel {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
using Merkle for bytes32;
bytes32 public constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;
IFxStateSender public fxRoot;
ICheckpointManager public checkpointManager;
address public fxChildTunnel;
mapping(bytes32 => bool) public processedExits;
constructor(address _checkpointManager, address _fxRoot) {
checkpointManager = ICheckpointManager(_checkpointManager);
fxRoot = IFxStateSender(_fxRoot);
}
function setFxChildTunnel(address _fxChildTunnel) public {
require(fxChildTunnel == address(0x0), "FxBaseRootTunnel: CHILD_TUNNEL_ALREADY_SET");
fxChildTunnel = _fxChildTunnel;
}
function _sendMessageToChild(bytes memory message) internal {
fxRoot.sendMessageToChild(fxChildTunnel, message);
}
function _validateAndExtractMessage(bytes memory inputData) internal returns (bytes memory) {
RLPReader.RLPItem[] memory inputDataRLPList = inputData
.toRlpItem()
.toList();
bytes32 exitHash = keccak256(
abi.encodePacked(
inputDataRLPList[2].toUint(),
MerklePatriciaProof._getNibbleArray(inputDataRLPList[8].toBytes()),
inputDataRLPList[9].toUint()
)
);
require(
processedExits[exitHash] == false,
"FxRootTunnel: EXIT_ALREADY_PROCESSED"
);
processedExits[exitHash] = true;
RLPReader.RLPItem[] memory receiptRLPList = inputDataRLPList[6]
.toBytes()
.toRlpItem()
.toList();
RLPReader.RLPItem memory logRLP = receiptRLPList[3]
.toList()[
inputDataRLPList[9].toUint()
];
RLPReader.RLPItem[] memory logRLPList = logRLP.toList();
require(fxChildTunnel == RLPReader.toAddress(logRLPList[0]), "FxRootTunnel: INVALID_FX_CHILD_TUNNEL");
require(
MerklePatriciaProof.verify(
inputDataRLPList[6].toBytes(),
inputDataRLPList[8].toBytes(),
inputDataRLPList[7].toBytes(),
bytes32(inputDataRLPList[5].toUint())
),
"FxRootTunnel: INVALID_RECEIPT_PROOF"
);
_checkBlockMembershipInCheckpoint(
inputDataRLPList[2].toUint(),
inputDataRLPList[3].toUint(),
bytes32(inputDataRLPList[4].toUint()),
bytes32(inputDataRLPList[5].toUint()),
inputDataRLPList[0].toUint(),
inputDataRLPList[1].toBytes()
);
RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList();
require(
bytes32(logTopicRLPList[0].toUint()) == SEND_MESSAGE_EVENT_SIG,
"FxRootTunnel: INVALID_SIGNATURE"
);
bytes memory receivedData = logRLPList[2].toBytes();
(bytes memory message) = abi.decode(receivedData, (bytes));
return message;
}
function _checkBlockMembershipInCheckpoint(
uint256 blockNumber,
uint256 blockTime,
bytes32 txRoot,
bytes32 receiptRoot,
uint256 headerNumber,
bytes memory blockProof
) private view returns (uint256) {
(
bytes32 headerRoot,
uint256 startBlock,
,
uint256 createdAt,
) = checkpointManager.headerBlocks(headerNumber);
require(
keccak256(
abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)
)
.checkMembership(
blockNumber-startBlock,
headerRoot,
blockProof
),
"FxRootTunnel: INVALID_HEADER"
);
return createdAt;
}
function receiveMessage(bytes memory inputData) public virtual {
bytes memory message = _validateAndExtractMessage(inputData);
_processMessageFromChild(message);
}
function _processMessageFromChild(bytes memory message) virtual internal;
}
pragma solidity 0.7.3;
interface ToshimonMinter {
function safeTransferFrom(
address _from,
address _to,
uint256 _id,
uint256 _amount,
bytes calldata _data
) external;
function safeBatchTransferFrom(
address _from,
address _to,
uint256[] calldata _ids,
uint256[] calldata _amounts,
bytes calldata _data
) external;
function setApprovalForAll(address _operator, bool _approved) external;
function isApprovedForAll(address _owner, address _operator)
external
view
returns (bool isOperator);
function balanceOf(address _owner, uint256 _id)
external
view
returns (uint256);
function totalSupply(uint256 _id) external view returns (uint256);
function tokenMaxSupply(uint256 _id) external view returns (uint256);
function burn(
address _account,
uint256 _id,
uint256 _amount
) external;
function mint(
address _to,
uint256 _id,
uint256 _quantity,
bytes memory _data
) external;
function mintBatch(address user, uint256[] calldata ids, uint256[] calldata amounts)
external;
}
contract ToshimonDojoRootTunnel is FxBaseRootTunnel {
bytes public latestData;
ToshimonMinter public toshimonMinter;
uint256 public lastPackOpening;
event PacksOpening(uint256 packOpeningId);
constructor(address _checkpointManager, address _fxRoot) FxBaseRootTunnel(_checkpointManager, _fxRoot) {
lastPackOpening = 0;
toshimonMinter = ToshimonMinter(0xd2d2a84f0eB587F70E181A0C4B252c2c053f80cB);
}
function _processMessageFromChild(bytes memory data) internal override {
}
function getLastPackOpening() public view returns(uint256) {
lastPackOpening;
}
function sendMessageToChild(uint256 quantity) public returns(uint256){
require(
toshimonMinter.balanceOf(msg.sender, 0) >= quantity,
"You need more packs frend"
);
toshimonMinter.burn(msg.sender, 0, quantity);
_sendMessageToChild(abi.encode(quantity, msg.sender));
}
}