erigon-pulse/cmd/devnet/contracts/rootreceiver.sol
Mark Holt c51573f333
Bor eth event flow (#8068)
Implemented polygon->eth flow
2023-08-25 12:19:39 +01:00

154 lines
6.0 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RLPReader} from "lib/rlpreader.sol";
import {MerklePatriciaProof} from "lib/merklepatriciaproof.sol";
import {Merkle} from "lib/Merkle.sol";
import "lib/exitpayloadreader.sol";
contract ICheckpointManager {
struct HeaderBlock {
bytes32 root;
uint256 start;
uint256 end;
uint256 createdAt;
address proposer;
}
/**
* @notice mapping of checkpoint header numbers to block details
* @dev These checkpoints are submited by plasma contracts
*/
mapping(uint256 => HeaderBlock) public headerBlocks;
}
contract RootReceiver {
using RLPReader for RLPReader.RLPItem;
using Merkle for bytes32;
using ExitPayloadReader for bytes;
using ExitPayloadReader for ExitPayloadReader.ExitPayload;
using ExitPayloadReader for ExitPayloadReader.Log;
using ExitPayloadReader for ExitPayloadReader.LogTopics;
using ExitPayloadReader for ExitPayloadReader.Receipt;
// keccak256(MessageSent(bytes))
bytes32 public constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;
// root chain manager
ICheckpointManager public checkpointManager;
// storage to avoid duplicate exits
mapping(bytes32 => bool) public processedExits;
mapping(address => uint) public senders;
event received(address _source, uint256 _amount);
constructor(address _checkpointManager) {
checkpointManager = ICheckpointManager(_checkpointManager);
}
function _validateAndExtractMessage(bytes memory inputData) internal returns (address, bytes memory) {
ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();
bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
uint256 blockNumber = payload.getBlockNumber();
// checking if exit has already been processed
// unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
bytes32 exitHash = keccak256(
abi.encodePacked(
blockNumber,
// first 2 nibbles are dropped while generating nibble array
// this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
// so converting to nibble array and then hashing it
MerklePatriciaProof._getNibbleArray(branchMaskBytes),
payload.getReceiptLogIndex()
)
);
require(processedExits[exitHash] == false, "FxRootTunnel: EXIT_ALREADY_PROCESSED");
processedExits[exitHash] = true;
ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
ExitPayloadReader.Log memory log = receipt.getLog();
// check child tunnel
//require(fxChildTunnel == log.getEmitter(), "FxRootTunnel: INVALID_FX_CHILD_TUNNEL");
bytes32 receiptRoot = payload.getReceiptRoot();
// verify receipt inclusion
require(
MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot),
"FxRootTunnel: INVALID_RECEIPT_PROOF"
);
// verify checkpoint inclusion
_checkBlockMembershipInCheckpoint(
blockNumber,
payload.getBlockTime(),
payload.getTxRoot(),
receiptRoot,
payload.getHeaderNumber(),
payload.getBlockProof()
);
ExitPayloadReader.LogTopics memory topics = log.getTopics();
require(
bytes32(topics.getField(0).toUint()) == SEND_MESSAGE_EVENT_SIG, // topic0 is event sig
"FxRootTunnel: INVALID_SIGNATURE"
);
// received message data
bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message
return (log.getEmitter(), message);
}
function _checkBlockMembershipInCheckpoint(
uint256 blockNumber,
uint256 blockTime,
bytes32 txRoot,
bytes32 receiptRoot,
uint256 headerNumber,
bytes memory blockProof
) private view {
(bytes32 headerRoot, uint256 startBlock, , , ) = checkpointManager.headerBlocks(headerNumber);
require(
keccak256(abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)).checkMembership(
blockNumber - startBlock,
headerRoot,
blockProof
),
"FxRootTunnel: INVALID_HEADER"
);
}
/**
* @notice receive message from L2 to L1, validated by proof
* @dev This function verifies if the transaction actually happened on child chain
*
* @param inputData RLP encoded data of the reference tx containing following list of fields
* 0 - headerNumber - Checkpoint header block number containing the reference tx
* 1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
* 2 - blockNumber - Block number containing the reference tx on child chain
* 3 - blockTime - Reference tx block time
* 4 - txRoot - Transactions root of block
* 5 - receiptRoot - Receipts root of block
* 6 - receipt - Receipt of the reference transaction
* 7 - receiptProof - Merkle proof of the reference receipt
* 8 - branchMask - 32 bits denoting the path of receipt in merkle tree
* 9 - receiptLogIndex - Log Index to read from the receipt
*/
function receiveMessage(bytes memory inputData) public virtual {
(address sender, bytes memory message) = _validateAndExtractMessage(inputData);
_processMessageFromChild(sender, message);
}
function _processMessageFromChild(address /*sender*/, bytes memory data) internal {
(address receiver, address from, uint amount) = abi.decode(data, (address, address, uint));
require(receiver == address(this), "Invalid receiver");
uint total = senders[from];
senders[from] = total + amount;
emit received(from, amount);
}
}