// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IVerifier} from "../interfaces/IVerifier.sol";
import {IWormhole} from "../interfaces/IWormhole.sol";
import {IHandler} from "../interfaces/IHandler.sol";
import {MessageLib} from "../shared/message/MessageLib.sol";
import {TokenMessageLib} from "../shared/message/TokenMessageLib.sol";
import {ExtendedMessageLib} from "../shared/message/ExtendedMessageLib.sol";
import {calculateReplayHash} from "../shared/message/BaseMessage.sol";
import {UsesCore} from "../shared/UsesCore.sol";
import {NonReentrant} from "../shared/NonReentrant.sol";

/// @title Inbox
/// @author The Warp Team <contact@warp.inc>
/// @notice This contract implements a generic cross-chain Inbox.
///
/// @dev The Inbox contract handles receiving messages from an off-chain
///      transport agent then delivering them to the correct handler
///      application.
///
/// @dev The Inbox is a verification-agnostic message deliverer. It allows
///      messages to be verified by any abstract Verifier contract, or the
///      Wormhole core contract.
///
/// @dev The Inbox checks that the message is addressed to this chain, that the
///      message has not been delivered already, that it was committed to by a
///      known Outbox, and that the message is valid according to the Verifier.
///      If all checks pass, the message is delivered to the Handler.
///
/// @dev When delivering the message to the Handler, the Inbox passes relevant
///      information including:
///      - The message itself.
///      - The address of the Verifier that validated the message.
///      - The address of the sender of the message.
///      - The chain id of the sender of the message.
abstract contract Inbox is UsesCore, NonReentrant {
    using MessageLib for MessageLib.Message;
    using TokenMessageLib for TokenMessageLib.TokenMessage;
    using ExtendedMessageLib for ExtendedMessageLib.ExtendedMessage;

    /// @notice Emitted when a message is received and delivered to a handler.
    /// @dev Topic0
    ///      0xb1f9608fc9a04b1041cff0ff42f903f47c60650e2cf5a50591327ce831ea07a5
    /// @param index The index of the message in the Outbox on the emitting
    ///        chain.
    /// @param emitterChainId The Wormhole chain id from which the message was
    ///        emitted.
    /// @param emitterAddress The Wormhole-formatted address of the contract
    ///        that emitted the message, presumably an Outbox.
    /// @param handler The address of the handler that processed the message.
    /// @param verifier The address of the verifier that validated the message.
    event Received(
        uint64 indexed index,
        uint16 indexed emitterChainId,
        bytes32 emitterAddress,
        address indexed handler,
        address verifier
    );

    /// @notice Emitted when a message is received and delivered to
    ///         a handler with extra un-verified data.
    /// @dev Identical to Received, but with a different topic0.
    /// @dev Topic0
    ///      0xa11231c10ad1681023b505b6836eace2b100967a3047848374598fe02df8a4ae
    /// @param index The index of the message in the Outbox on the emitting
    ///        chain.
    /// @param emitterChainId The Wormhole chain id from which the message was
    ///        emitted.
    ///        that emitted this message, presumably an Outbox.
    /// @param emitterAddress The Wormhole-formatted address of the contract.
    /// @param handler The address of the handler that processed the message.
    /// @param verifier The address of the verifier that validated the message.
    event ReceivedExt(
        uint64 indexed index,
        uint16 indexed emitterChainId,
        bytes32 emitterAddress,
        address indexed handler,
        address verifier
    );

    /// @notice Emitted when a message with tokensis received and delivered to
    ///         a handler.
    /// @dev Identical to Received, but with a different topic0.
    /// @dev Topic0
    ///      0x6a7d9c2416abb712bc96f9eff261297cfccc5781844a77ad2791826e4ba5161d
    /// @param index The index of the message in the Outbox on the emitting
    ///        chain.
    /// @param emitterChainId The Wormhole chain id from which the message was
    ///        emitted.
    /// @param emitterAddress The Wormhole-formatted address of the contract
    ///        that emitted the message, presumably an Outbox.
    /// @param handler The address of the handler that processed the message.
    /// @param verifier The address of the verifier that validated the message.
    event ReceivedWithTokens(
        uint64 indexed index,
        uint16 indexed emitterChainId,
        bytes32 emitterAddress,
        address indexed handler,
        address verifier
    );

    /// @notice Core rejected signed VAA.
    /// @dev Selector 0x8ee2e336.
    /// @param reason The reason the VAA was rejected, provided by the Wormhole
    ///        core contract.
    error InvalidVaa(string reason);

    /// @notice Message is not addressed to this chain.
    /// @dev Selector 0xf31052b3.
    /// @param _chainId The chain id to which the message ought to be delivered.
    error WrongTargetChain(uint16 _chainId);

    /// @notice Message has been delivered already, and cannot be delivered
    ///         again.
    /// @dev Selector 0xbf51ac51.
    error DisallowedDelivery();

    /// @notice Handler does not allow this messenger.
    /// @dev Selector 0xbe18d4f5.
    error DisallowedMessenger(uint16 chainId, bytes32 outbox);

    /// @notice Handler does not allow this verifier.
    /// @dev Selector 0x266fa8b5.
    error DisallowedVerifier(address verifier);

    /// @notice Chain has forked and message receipt is currently
    ///         disabled. This condition may be temporary, should
    ///         the guardian set restart service on this chain by
    ///         updating the stored EVM chain ID in Wormhole core.
    /// @dev Selector 0xf22c96c1.
    error Forked();

    /// @notice Receive a message, and validate it via a specified verifier.
    ///         If the witness is valid, deliver the message to the handler.
    /// @param verifier The address of the Verifier contract which will be used
    ///        to authenticate the message.
    /// @param witnessedStatement The witnessed statement to verify and deliver.
    ///        The format of this statement is Verifier-specific.
    /// @custom:reverts as verify
    /// @custom:reverts as deliver
    /// @custom:emits as deliver
    function recv(address verifier, bytes memory witnessedStatement) external nonReentrant {
        IVerifier.VerifiedStatement memory statement = verify(verifier, witnessedStatement);
        deliver(verifier, statement.emitterChainId, statement.emitterAddress, statement.message);
    }

    /// @notice Receive a message to be delivered with un-verified extra data.
    ///         Validate the message via the specified verifier.
    ///         While the message and witness are verified, the extra data is
    ///         not. If the witness is valid, deliver the message and the
    ///         un-verified extra data to the handler.
    ///         Applications MUST perform additional verification as necessary
    ///         before trusting the extra data.
    /// @dev Security notice: `recvExt` is not reentrance protected.
    ///      This choice was made to enable the delivery of
    ///      additional messages contained in `unverifiedRelayerData.`
    ///      App devs are responsible for ensuring that their `handleExt`
    ///      function is secure even if re-entered by this `Messenger` contract.
    /// @param verifier The address of the Verifier contract which will be used
    ///        to authenticate the message.
    /// @param witnessedStatement The witnessed statement to verify and deliver.
    ///        The format of this statement is Verifier-specific.
    /// @param unverifiedRelayerData The un-verified extra data to deliver.
    ///        This data is submitted by the message relayer,
    ///        and may contain any un-trusted data.
    ///        Applications may leverage this field to extend any extra
    ///        functionalitythat requires data not available at the time of
    ///        sending a message, such as (for example) additional signed
    ///        Wormhole VAAs.
    /// @custom:reverts as verify
    /// @custom:reverts as deliverExt
    /// @custom:emits as deliverExt
    function recvExt(address verifier, bytes memory witnessedStatement, bytes memory unverifiedRelayerData) external {
        IVerifier.VerifiedStatement memory statement = verify(verifier, witnessedStatement);
        deliverExt(
            verifier, statement.emitterChainId, statement.emitterAddress, statement.message, unverifiedRelayerData
        );
    }

    /// @notice Receive a message to be delivered with tokens.
    ///         Validate the message via the specified verifier.
    ///         If the witness is valid, deliver the message and
    ///         the token transfer data to the handler.
    ///         Applications MUST submit the transfer to the specified bridge
    ///         and verify that it results in a transfer of the expected
    ///         type and amount of tokens.
    /// @dev Security notice: `recvWithTokens` is not reentrance protected.
    ///      This choice was made to enable the delivery of
    ///      additional messages contained in `transfer` data
    ///      (e.g. a message delivering a Burn-and-Mint token built on this
    ///      contract).
    ///      App devs are responsible for ensuring that their `handleWithTokens`
    ///      function is secure even if re-entered by this `Messenger` contract.
    /// @param verifier The address of the Verifier contract which will be used
    ///        to authenticate the message.
    /// @param witnessedStatement The witnessed statement to verify and deliver.
    ///        The format of this statement is Verifier-specific.
    /// @param transfer The transfer to deliver. This is an opaque array of
    ///        bytes that contains bridge-specific asset transfer data. The
    ///        transfer is not verified.
    /// @custom:reverts as verify
    /// @custom:reverts as deliverWithTokens
    /// @custom:emits as deliverWithTokens
    function recvWithTokens(address verifier, bytes memory witnessedStatement, bytes memory transfer) external {
        IVerifier.VerifiedStatement memory statement = verify(verifier, witnessedStatement);
        deliverWithTokens(verifier, statement.emitterChainId, statement.emitterAddress, statement.message, transfer);
    }

    /// @notice Verify a witnessed statement using the specified verifier.
    /// @dev Verification is outsourced to a Verifier contract, which is
    ///      responsible for validating the witness and parsing the statement.
    ///      This contract is specified by the Relayer when delivering the
    ///      message via the recv function. The address of the Verifier is later
    ///      passed to the handler via the IHandler.acceptableVerifier function.
    /// @return statement - the parsed statement (iff the witness is valid)
    function verify(address verifier, bytes memory witnessedStatement)
        public
        view
        returns (IVerifier.VerifiedStatement memory statement)
    {
        if (verifier == address(core())) {
            statement = verifyVaa(witnessedStatement);
        } else {
            statement = IVerifier(verifier).verify(witnessedStatement);
        }
    }

    /// @dev This function is called by recv after verify. It implements the
    ///      logic for delivering a message to a handler.
    /// @param emitterChainId The chain id of the remote Outbox.
    /// @param emitterAddress The address of the remote Outbox (using Wormhole
    ///        VAA addressing) which emitted the message.
    /// @param encodedMessage The encoded message to deliver.
    /// @custom:reverts as MessageLib.decode
    /// @custom:reverts as extractEvmTarget
    /// @custom:reverts as checkPreconditions
    /// @custom:reverts as checkPreflights
    /// @custom:emits Received(emitterChainId, index, handler, verifier)
    function deliver(address verifier, uint16 emitterChainId, bytes32 emitterAddress, bytes memory encodedMessage)
        internal
    {
        // Decode
        MessageLib.Message memory message = MessageLib.decode(encodedMessage);
        uint64 index = message.index;
        IHandler handler = message.extractEvmTarget();

        // pre-checks
        checkPreconditions(message.targetChain);
        bytes32 replayHash = calculateReplayHash(emitterChainId, emitterAddress, index);
        checkPreflights(handler, verifier, emitterChainId, emitterAddress, replayHash);

        // emit
        emit Received(index, emitterChainId, emitterAddress, address(handler), verifier);

        // deliver
        handler.handle(emitterChainId, message.sender, replayHash, message.contents);
    }

    /// @dev This function is called by recvExt after verify. It
    ///      implements the logic for delivering a message to a handler
    ///      along with some extra un-trusted data.
    /// @dev Security notice: the handler MUST auth the verifier address and
    ///      MUST auth the (emitter chain, sender).
    /// @dev Security notice: the handler MUST auth the caller of handle.
    /// @param verifier The address of the Verifier contract which authenticated
    ///        the message.
    /// @param emitterChainId The chain id of the remote Outbox.
    /// @param emitterAddress The address of the remote Outbox (using Wormhole
    ///        VAA addressing) which emitted the message.
    /// @param encodedMessage The encoded message to deliver.
    /// @param unverifiedRelayerData The un-verified extra data to deliver.
    ///        This data is submitted by the message relayer,
    ///        and may contain any un-trusted data.
    ///        Applications may leverage this field to extend any extra
    ///        functionality that requires data not available at the time of
    ///        sending a message, such as (for example) additional signed
    ///        Wormhole VAAs.
    /// @custom:reverts as ExtendedMessageLib.decode
    /// @custom:reverts as extractEvmTarget
    /// @custom:reverts as checkPreconditions
    /// @custom:reverts as checkPreflights
    /// @custom:emits ReceivedExt(emitterChainId, index, handler, emitterAddress, verifier)
    function deliverExt(
        address verifier,
        uint16 emitterChainId,
        bytes32 emitterAddress,
        bytes memory encodedMessage,
        bytes memory unverifiedRelayerData
    ) internal {
        // Decode
        ExtendedMessageLib.ExtendedMessage memory message = ExtendedMessageLib.decode(encodedMessage);
        uint64 index = message.index;
        IHandler handler = message.extractEvmTarget();

        // pre-checks
        checkPreconditions(message.targetChain);
        bytes32 replayHash = calculateReplayHash(emitterChainId, emitterAddress, index);
        checkPreflights(handler, verifier, emitterChainId, emitterAddress, replayHash);

        // emit
        emit ReceivedExt(index, emitterChainId, emitterAddress, address(handler), verifier);

        // deliver
        handler.handleExt(
            emitterChainId, message.sender, replayHash, message.contents, message.relaySignal, unverifiedRelayerData
        );
    }

    /// @dev This function is called by recvWithTokens after verify. It
    ///      implements the logic for delivering a message with value to a
    ///      handler.
    /// @param verifier The address of the Verifier contract which authenticated
    ///        the message.
    /// @param emitterChainId The chain id of the remote Outbox.
    /// @param emitterAddress The address of the remote Outbox (using Wormhole
    ///        VAA addressing) which emitted the message.
    /// @param encodedMessage The encoded message to deliver.
    /// @param transfer The transfer to deliver. This is an opaque array of
    ///        bytes that contains bridge-specific asset transfer data. The
    ///        transfer is not verified.
    /// @custom:reverts as TokenMessageLib.decode
    /// @custom:reverts as extractEvmTarget
    /// @custom:reverts as checkPreconditions
    /// @custom:reverts as checkPreflights
    /// @custom:emits ReceivedWithTokens(emitterChainId, index, handler, emitterAddress, verifier)
    function deliverWithTokens(
        address verifier,
        uint16 emitterChainId,
        bytes32 emitterAddress,
        bytes memory encodedMessage,
        bytes memory transfer
    ) internal {
        // Decode
        TokenMessageLib.TokenMessage memory message = TokenMessageLib.decode(encodedMessage);
        uint64 index = message.index;
        IHandler handler = message.extractEvmTarget();

        // pre-checks
        checkPreconditions(message.targetChain);
        bytes32 replayHash = calculateReplayHash(emitterChainId, emitterAddress, index);
        checkPreflights(handler, verifier, emitterChainId, emitterAddress, replayHash);

        // emit
        emit ReceivedWithTokens(index, emitterChainId, emitterAddress, address(handler), verifier);

        // deliver
        handler.handleWithTokens(
            emitterChainId,
            message.sender,
            replayHash,
            message.contents,
            message.bridge,
            message.assetIdentifier,
            message.amount,
            transfer
        );
    }

    /// @notice Revert if the message is not addressed to this chain.
    /// @dev Ensures that messages are only delivered to the chain to which they
    ///      are addressed.
    /// @param _chainId The chain id to which the mesage is address, as provided
    ///        in the message itself, and authenticated by some Verifier.
    /// @custom:reverts WrongTargetChain(_chainId) if the message is not
    ///                 addressed to this chain.
    function checkTargetChain(uint16 _chainId) internal view {
        if (core().chainId() != _chainId) revert WrongTargetChain(_chainId);
    }

    /// @notice Revert if the core has detected a chain fork.
    /// @dev Core uses a stored EVM chain ID and compares it to the
    ///      current EVM chain ID. If they differ, the chain has forked.
    ///      To prevent uncertainty during forks, the Inbox stops receiving
    ///      messages until the guardian restarts the service by updating
    ///      the stored EVM chain ID in Wormhole core.
    /// @custom:reverts If the chain has forked.
    function checkNotForked() internal view {
        if (core().isFork()) revert Forked();
    }

    /// @notice Check preconditions for a message. The preconditions are
    ///         - This chain is not known to be forked.
    ///         - The target chain is this chain.
    /// @dev Simple wrapper for above check____ functions.
    /// @param targetChain The chain id to which the mesage is addressed, as
    ///        provided in the message itself, and authenticated by some
    ///        Verifier.
    /// @custom:reverts as checkNotForked
    /// @custom:reverts as checkTargetChain
    function checkPreconditions(uint16 targetChain) internal view {
        checkNotForked();
        checkTargetChain(targetChain);
    }

    /// @notice Check preflights for a message. The Preflight checks are
    ///        - The handler accepts the outbox
    ///        - The handler accepts the verifier
    /// @dev Preflights are distinct from preconditions because they are
    ///      not required for the message to be valid, but rather are
    ///      required for the message to be delivered to the handler.
    /// @dev Preflights provide a mechanism for an application to configure
    ///      its trust model in any way it pleases.
    /// @param handler The handler contract.
    /// @param verifier The verifier contract address that was used  to verify
    ///        the message.
    /// @param emitterChain The Wormhole chain ID of the emitter.
    /// @param emitterAddress The address of the emitter on that chain.
    /// @param replayHash A protocol-specific unique identifier for the message
    /// @custom:reverts DisallowedMessenger(emitterChain, emitterAddress) If the
    ///                 handler does not accept the outbox.
    /// @custom:reverts DisallowedVerifier(verifier) If the handler does not
    ///                 accept the verifier.
    /// @custom:reverts DisallowedDelivery() If the handler does not accept the delivery due to message having been processed already.
    function checkPreflights(
        IHandler handler,
        address verifier,
        uint16 emitterChain,
        bytes32 emitterAddress,
        bytes32 replayHash
    ) internal view {
        if (!handler.acceptableMessenger(emitterChain, emitterAddress)) {
            revert DisallowedMessenger(emitterChain, emitterAddress);
        }
        if (!handler.acceptableVerifier(verifier)) revert DisallowedVerifier(verifier);
        if (!handler.acceptableDelivery(replayHash)) revert DisallowedDelivery();
    }

    /// @notice Verify a Wormhole core witnessed statement using
    ///         parseAndVerifyVM
    /// @dev This is a special-case Verifier for Wormhole core contracts. It
    ///      essentially is a local shim to make the core conform to the
    ///      Verifier interface. It is used by the recvVaa function to verify
    ///      input that specifies the core as the Verifier.
    /// @dev MUST revert if the VM is not valid.
    /// @param vaa The puported VAA to verify and parse.
    /// @return statement - the statement parsed from the VM (iff the Wormhole
    ///         witness is valid).
    /// @custom:reverts InvalidVaa(reason) if the VM is not valid.
    function verifyVaa(bytes memory vaa) internal view returns (IVerifier.VerifiedStatement memory statement) {
        // call Wormhole core parseAndVerifyVM
        IWormhole.VM memory vm;
        bool result;
        string memory reason;
        (vm, result, reason) = core().parseAndVerifyVM(vaa);
        // revert if VM is not valid
        if (!result) revert InvalidVaa(reason);
        // transform VM to VerifiedStatement and return
        statement.emitterChainId = vm.emitterChainId;
        statement.emitterAddress = vm.emitterAddress;
        statement.message = vm.payload;
    }
}
[{"inputs":[{"internalType":"address","name":"attestor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ATTESTOR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core","type":"address"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"deploy","outputs":[],"stateMutability":"nonpayable","type":"function"}]