contract Disputed is
    Relayed,
    EscrowBase,
    DisputedEmitter
{
    mapping(bytes32 => address) internal _disputeInitiators;

    /// @notice Starts a dispute process between seller `_seller` and buyer `_buyer`.
	/// 	Could start only if an arbiter was specified.
	/// @dev Escrow is identified by a message = keccak256(_tradeRecordId, _seller, _buyer)
	/// @dev Seller and buyer shoud sign hash of (message, escrow address, msg.sig, _expireAtBlock, _salt) data
	/// @param _tradeRecordId identifier of escrow record
	/// @param _seller who will mainly deposit to escrow
	/// @param _buyer who will eventually is going to receive payment
	/// @param _expireAtBlock expiry block after which transaction will be invalid
	/// @param _salt random bytes to identify signed data
	/// @param _signature signature of an initiator
	/// @return result code of an operation
    function initiateDispute(
        bytes32 _tradeRecordId,
        address _seller,
        address _buyer,
        uint _expireAtBlock,
        uint _salt,
        bytes _signature
    )
    external
    onlyRelay
    returns (uint)
    {
        bytes32 _escrowHash = _getEscrowHash(_tradeRecordId, _seller, _buyer);
        bytes32 _encodedSaltWithEscrow = _getEncodedSaltWithEscrowHash(_salt, _escrowHash);
        require(_getEscrowExists(_escrowHash), "DISPUTED_ESCROW_SHOULD_EXIST");
        require(!_getEscrowDisputed(_escrowHash), "DISPUTED_ESCROW_SHOULD_NOT_BE_DISPUTED");
        address _arbiter = _getEscrowArbiter(_escrowHash);
        require(_arbiter != address(0), "DISPUTED_ARBITER_SHOULD_NOT_BE_EMPTY");
        require(!_saltWithEscrow2flagMapping[_encodedSaltWithEscrow], "DISPUTED_SUCH_SALT_SHOULD_NOT_EXIST");
        bytes memory _message = abi.encodePacked(
            _escrowHash,
            address(this),
            msg.sig,
            _expireAtBlock,
            _salt
        );
        address _signer = Signatures.getSignerFromSignature(keccak256(_message), _signature);
        require(_signer == _seller || _signer == _buyer, "DISPUTED_SIGNER_SHOULD_BE_BUYER_OR_SELLER");
        require(block.number < _expireAtBlock, "DISPUTED_TX_SHOULD_NOT_BE_EXPIRED");

        _saltWithEscrow2flagMapping[_encodedSaltWithEscrow] = true;
        _disputeInitiators[_escrowHash] = _signer;
        _setEscrowDisputed(_escrowHash, true);
        _emitDisputeRequested(_escrowHash, _arbiter);

        return OK;
    }

    /// @notice Cancels an initiated dispute process
	/// @dev Escrow is identified by a message = keccak256(_tradeRecordId, _seller, _buyer)
	/// @dev Seller and buyer shoud sign hash of (message, escrow address, msg.sig, _expireAtBlock, _salt) data
	/// @param _tradeRecordId identifier of escrow record
	/// @param _seller who will mainly deposit to escrow
	/// @param _buyer who will eventually is going to receive payment
	/// @param _expireAtBlock expiry block after which transaction will be invalid
	/// @param _salt random bytes to identify signed data
	/// @param _signature signature of an initiator
	/// @return result code of an operation
    function cancelDispute(
        bytes32 _tradeRecordId,
        address _seller,
        address _buyer,
        uint _expireAtBlock,
        uint _salt,
        bytes _signature
    )
    external
    onlyRelay
    returns (uint)
    {
        bytes32 _escrowHash = _getEscrowHash(_tradeRecordId, _seller, _buyer);
        bytes32 _encodedSaltWithEscrow = _getEncodedSaltWithEscrowHash(_salt, _escrowHash);
        require(_getEscrowExists(_escrowHash), "DISPUTED_ESCROW_SHOULD_EXIST");
        require(_getEscrowDisputed(_escrowHash), "DISPUTED_ESCROW_SHOULD_BE_DISPUTED");
        require(!_saltWithEscrow2flagMapping[_encodedSaltWithEscrow], "DISPUTED_SUCH_SALT_SHOULD_NOT_EXIST");
        bytes memory _message = abi.encodePacked(
            _escrowHash,
            address(this),
            msg.sig,
            _expireAtBlock,
            _salt
        );
        address _signer = Signatures.getSignerFromSignature(keccak256(_message), _signature);
        require(_signer == _disputeInitiators[_escrowHash], "DISPUTED_SIGNER_SHOULD_BE_DISPUTE_INITIATOR");
        require(block.number < _expireAtBlock, "DISPUTED_TX_SHOULD_NOT_BE_EXPIRED");

        _saltWithEscrow2flagMapping[_encodedSaltWithEscrow] = true;
        _setEscrowDisputed(_escrowHash, false);
        delete _disputeInitiators[_escrowHash];
        _emitDisputeCanceled(_escrowHash);

        return OK;
    }

    /// @notice Transfers disputed value from escrow to the seller `_seller` and the buyer `_buyer` according
	/// 	to provided buyer value `_buyerValue`. The value of escrow - _buyerValue will be transferred to the seller.
	/// @dev Escrow is identified by a message = keccak256(_tradeRecordId, _seller, _buyer)
	/// @dev Arbiter should sign hash of (message, escrow address, msg.sig, _buyerValue, _expireAtBlock) data
	/// @param _tradeRecordId identifier of escrow record
	/// @param _seller who will mainly deposit to escrow
	/// @param _buyer who will eventually is going to receive payment
	/// @param _buyerValue value that will be transferred to the buyer
	/// @param _expireAtBlock expiry block after which transaction will be invalid
	/// @param _arbiterSignature signature of an arbiter
	/// @return result code of an operation
    function releaseDisputedPayment(
        bytes32 _tradeRecordId,
        address _seller,
        address _buyer,
        uint _buyerValue,
        uint _expireAtBlock,
        bytes _arbiterSignature
        ) external returns (uint);

    function getArbiter(bytes32 _tradeRecordId, address _seller, address _buyer) external view returns (address) {
        bytes32 _escrowHash = _getEscrowHash(_tradeRecordId, _seller, _buyer);
        return _getEscrowArbiter(_escrowHash);
    }

    /// @notice Sets a new arbiter `_arbiter`. His address should be approved by both parties.
	/// @dev Escrow is identified by a message = keccak256(_tradeRecordId, _seller, _buyer)
	/// @dev Seller and buyer shoud sign hash of (message, escrow address, msg.sig, _arbiter, _expireAtBlock, _salt) data
	/// @param _tradeRecordId identifier of escrow record
	/// @param _seller who will mainly deposit to escrow
	/// @param _buyer who will eventually is going to receive payment
	/// @param _expireAtBlock expiry block after which transaction will be invalid
	/// @param _salt random bytes to identify signed data
	/// @param _bothSignatures signatures of seller and buyer
    function setArbiter(
        bytes32 _tradeRecordId,
        address _seller,
        address _buyer,
        address _arbiter,
        uint _expireAtBlock,
        uint _salt,
        bytes _bothSignatures
    )
    external
    onlyRelay
    returns (uint)
    {
        bytes32 _escrowHash = _getEscrowHash(_tradeRecordId, _seller, _buyer);
        bytes32 _encodedSaltWithEscrow = _getEncodedSaltWithEscrowHash(_salt, _escrowHash);
        require(_getEscrowExists(_escrowHash), "DISPUTED_ESCROW_SHOULD_EXIST");
        require(!_saltWithEscrow2flagMapping[_encodedSaltWithEscrow], "DISPUTED_SUCH_SALT_SHOULD_NOT_EXIST");
        bytes memory _message = abi.encodePacked(
            _escrowHash,
            address(this),
            msg.sig,
            _arbiter,
            _expireAtBlock,
            _salt
        );
        address[] memory _signers = Signatures.getSignersFromSignatures(keccak256(_message), _bothSignatures);
        require(
            _signers.length == 2 &&
            (
                (_signers[0] == _seller && _signers[1] == _buyer) ||
                (_signers[0] == _buyer && _signers[1] == _seller)
            ),
            "DISPUTED_SIGNERS_SHOULD_BE_BUYER_AND_SELLER");
        require(block.number < _expireAtBlock, "DISPUTED_TX_SHOULD_NOT_BE_EXPIRED");

        _saltWithEscrow2flagMapping[_encodedSaltWithEscrow] = true;
        _setEscrowArbiter(_escrowHash, _arbiter);
        _emitArbiterTransferred(_escrowHash, _arbiter);

        return OK;
    }

    function _setEscrowDisputed(bytes32 _escrowHash, bool _disputeStatus) internal;

    function _setEscrowArbiter(bytes32 _escrowHash, address _arbiter) internal;
}
function initiateDispute(
    bytes32 _tradeRecordId,
    address _seller,
    address _buyer,
    uint _expireAtBlock,
    uint _salt,
    bytes _signature
)
external
onlyRelay
returns (uint)
{
    bytes32 _escrowHash = _getEscrowHash(_tradeRecordId, _seller, _buyer);
    bytes32 _encodedSaltWithEscrow = _getEncodedSaltWithEscrowHash(_salt, _escrowHash);
    require(_getEscrowExists(_escrowHash), "DISPUTED_ESCROW_SHOULD_EXIST");
    require(!_getEscrowDisputed(_escrowHash), "DISPUTED_ESCROW_SHOULD_NOT_BE_DISPUTED");
    address _arbiter = _getEscrowArbiter(_escrowHash);
    require(_arbiter != address(0), "DISPUTED_ARBITER_SHOULD_NOT_BE_EMPTY");
    require(!_saltWithEscrow2flagMapping[_encodedSaltWithEscrow], "DISPUTED_SUCH_SALT_SHOULD_NOT_EXIST");
    bytes memory _message = abi.encodePacked(
        _escrowHash,
        address(this),
        msg.sig,
        _expireAtBlock,
        _salt
    );
    address _signer = Signatures.getSignerFromSignature(keccak256(_message), _signature);
    require(_signer == _seller || _signer == _buyer, "DISPUTED_SIGNER_SHOULD_BE_BUYER_OR_SELLER");
    require(block.number < _expireAtBlock, "DISPUTED_TX_SHOULD_NOT_BE_EXPIRED");

    _saltWithEscrow2flagMapping[_encodedSaltWithEscrow] = true;
    _disputeInitiators[_escrowHash] = _signer;
    _setEscrowDisputed(_escrowHash, true);
    _emitDisputeRequested(_escrowHash, _arbiter);

    return OK;
}

/// @notice Starts a dispute process between seller `_seller` and buyer `_buyer`.
/// 	Could start only if an arbiter was specified.
/// @dev Escrow is identified by a message = keccak256(_tradeRecordId, _seller, _buyer)
/// @dev Seller and buyer shoud sign hash of (message, escrow address, msg.sig, _expireAtBlock, _salt) data
/// @param _tradeRecordId identifier of escrow record
/// @param _seller who will mainly deposit to escrow
/// @param _buyer who will eventually is going to receive payment
/// @param _expireAtBlock expiry block after which transaction will be invalid
/// @param _salt random bytes to identify signed data
/// @param _signature signature of an initiator
/// @return result code of an operation