You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
4.2 KiB
161 lines
4.2 KiB
4 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
pragma solidity >=0.6.11;
|
||
|
|
||
|
import "@openzeppelin/contracts/cryptography/ECDSA.sol";
|
||
4 years ago
|
import "@summa-tx/memview-sol/contracts/TypedMemView.sol";
|
||
|
|
||
|
library Message {
|
||
|
using TypedMemView for bytes;
|
||
|
using TypedMemView for bytes29;
|
||
|
|
||
4 years ago
|
uint256 constant PREFIX_LENGTH = 76;
|
||
|
|
||
4 years ago
|
function formatMessage(
|
||
|
uint32 _origin,
|
||
|
bytes32 _sender,
|
||
|
uint32 _sequence,
|
||
|
uint32 _destination,
|
||
|
bytes32 _recipient,
|
||
|
bytes memory _body
|
||
|
) internal pure returns (bytes memory) {
|
||
|
return
|
||
|
abi.encodePacked(
|
||
|
_origin,
|
||
|
_sender,
|
||
|
_sequence,
|
||
|
_destination,
|
||
|
_recipient,
|
||
|
_body
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function messageHash(
|
||
|
uint32 _origin,
|
||
|
bytes32 _sender,
|
||
|
uint32 _sequence,
|
||
|
uint32 _destination,
|
||
|
bytes32 _recipient,
|
||
|
bytes memory _body
|
||
|
) internal pure returns (bytes32) {
|
||
|
return
|
||
|
keccak256(
|
||
|
formatMessage(
|
||
|
_origin,
|
||
|
_sender,
|
||
|
_sequence,
|
||
|
_destination,
|
||
|
_recipient,
|
||
|
_body
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function origin(bytes29 _message) internal pure returns (uint32) {
|
||
|
return uint32(_message.indexUint(0, 4));
|
||
|
}
|
||
|
|
||
|
function sender(bytes29 _message) internal pure returns (bytes32) {
|
||
|
return _message.index(4, 32);
|
||
|
}
|
||
|
|
||
|
function sequence(bytes29 _message) internal pure returns (uint32) {
|
||
4 years ago
|
return uint32(_message.indexUint(36, 4));
|
||
4 years ago
|
}
|
||
|
|
||
|
function destination(bytes29 _message) internal pure returns (uint32) {
|
||
4 years ago
|
return uint32(_message.indexUint(40, 4));
|
||
4 years ago
|
}
|
||
|
|
||
|
function recipient(bytes29 _message) internal pure returns (bytes32) {
|
||
4 years ago
|
return _message.index(44, 32);
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
function recipientAddress(bytes29 _message)
|
||
|
internal
|
||
|
pure
|
||
|
returns (address)
|
||
|
{
|
||
|
return address(uint160(uint256(recipient(_message))));
|
||
|
}
|
||
|
|
||
4 years ago
|
function body(bytes29 _message) internal pure returns (bytes29) {
|
||
4 years ago
|
return _message.slice(PREFIX_LENGTH, _message.len() - PREFIX_LENGTH, 0);
|
||
4 years ago
|
}
|
||
|
}
|
||
4 years ago
|
|
||
|
abstract contract Common {
|
||
|
enum States {ACTIVE, FAILED}
|
||
|
|
||
4 years ago
|
uint32 public immutable originDomain;
|
||
4 years ago
|
bytes32 public immutable DOMAIN_HASH;
|
||
|
|
||
4 years ago
|
address public updater;
|
||
4 years ago
|
States public state;
|
||
4 years ago
|
bytes32 public current;
|
||
4 years ago
|
|
||
|
event Update(
|
||
4 years ago
|
uint32 indexed _originDomain,
|
||
4 years ago
|
bytes32 indexed _oldRoot,
|
||
|
bytes32 indexed _newRoot,
|
||
|
bytes signature
|
||
|
);
|
||
|
event DoubleUpdate(
|
||
4 years ago
|
bytes32 _oldRoot,
|
||
4 years ago
|
bytes32[2] _newRoot,
|
||
|
bytes _signature,
|
||
|
bytes _signature2
|
||
|
);
|
||
|
|
||
4 years ago
|
constructor(
|
||
4 years ago
|
uint32 _originDomain,
|
||
4 years ago
|
address _updater,
|
||
|
bytes32 _current
|
||
|
) {
|
||
4 years ago
|
originDomain = _originDomain;
|
||
4 years ago
|
updater = _updater;
|
||
4 years ago
|
current = _current;
|
||
4 years ago
|
DOMAIN_HASH = keccak256(abi.encodePacked(_originDomain, "OPTICS"));
|
||
4 years ago
|
state = States.ACTIVE;
|
||
|
}
|
||
|
|
||
|
function fail() internal virtual;
|
||
|
|
||
|
function _setFailed() internal {
|
||
|
state = States.FAILED;
|
||
|
}
|
||
|
|
||
|
modifier notFailed() {
|
||
4 years ago
|
require(state != States.FAILED, "failed state");
|
||
4 years ago
|
_;
|
||
|
}
|
||
|
|
||
|
function checkSig(
|
||
|
bytes32 _oldRoot,
|
||
4 years ago
|
bytes32 _newRoot,
|
||
4 years ago
|
bytes memory _signature
|
||
|
) internal view returns (bool) {
|
||
|
bytes32 _digest =
|
||
|
keccak256(abi.encodePacked(DOMAIN_HASH, _oldRoot, _newRoot));
|
||
|
_digest = ECDSA.toEthSignedMessageHash(_digest);
|
||
|
return ECDSA.recover(_digest, _signature) == updater;
|
||
|
}
|
||
|
|
||
4 years ago
|
// Checks that updater signed both updates and that
|
||
|
// the two updates are not equal (i.e. conflicting)
|
||
4 years ago
|
function doubleUpdate(
|
||
4 years ago
|
bytes32 _oldRoot,
|
||
4 years ago
|
bytes32[2] calldata _newRoot,
|
||
4 years ago
|
bytes calldata _signature,
|
||
|
bytes calldata _signature2
|
||
|
) external notFailed {
|
||
|
if (
|
||
4 years ago
|
Common.checkSig(_oldRoot, _newRoot[0], _signature) &&
|
||
|
Common.checkSig(_oldRoot, _newRoot[1], _signature2) &&
|
||
|
_newRoot[0] != _newRoot[1]
|
||
4 years ago
|
) {
|
||
|
fail();
|
||
|
emit DoubleUpdate(_oldRoot, _newRoot, _signature, _signature2);
|
||
|
}
|
||
|
}
|
||
|
}
|