Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report mvp #66

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/contracts/contracts/AttestationCollector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ contract AttestationCollector is AuthManager, OwnableUpgradeable {
▏*║ EXTERNAL FUNCTIONS ║*▕
\*╚══════════════════════════════════════════════════════════════════════╝*/

function submitAttestation(address _updater, bytes memory _attestation) external {
bytes29 _view = _checkUpdaterAuth(_updater, _attestation);
function submitAttestation(bytes memory _attestation) external {
(address _updater, bytes29 _view) = _checkUpdaterAuth(_attestation);
_storeAttestation(_view);
emit AttestationSubmitted(_updater, _attestation);
}
Expand Down
113 changes: 66 additions & 47 deletions packages/contracts/contracts/Home.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ pragma solidity 0.8.13;
// ============ Internal Imports ============
import { Version0 } from "./Version0.sol";
import { UpdaterStorage } from "./UpdaterStorage.sol";
import { AuthManager } from "./auth/AuthManager.sol";
import { ReportHub } from "./auth/ReportHub.sol";
import { Attestation } from "./libs/Attestation.sol";
import { Report } from "./libs/Report.sol";
import { TypedMemView } from "./libs/TypedMemView.sol";
import { QueueLib } from "./libs/Queue.sol";
import { MerkleLib } from "./libs/Merkle.sol";
import { Header } from "./libs/Header.sol";
Expand All @@ -28,10 +30,12 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol";
* Accepts submissions of fraudulent signatures
* by the Updater and slashes the Updater in this case.
*/
contract Home is Version0, MerkleTreeManager, UpdaterStorage, AuthManager {
contract Home is Version0, MerkleTreeManager, UpdaterStorage, ReportHub {
// ============ Libraries ============

using Attestation for bytes29;
using Report for bytes29;
using TypedMemView for bytes29;
using MerkleLib for MerkleLib.Tree;

using Tips for bytes;
Expand Down Expand Up @@ -92,20 +96,32 @@ contract Home is Version0, MerkleTreeManager, UpdaterStorage, AuthManager {
);

/**
* @notice Emitted when proof of an improper attestation is submitted,
* @notice Emitted when proof of an invalid attestation is submitted,
* which sets the contract to FAILED state
* @param updater Updater who signed improper attestation
* @param updater Updater who signed invalid attestation
* @param attestation Attestation data and signature
*/
event ImproperAttestation(address updater, bytes attestation);
event InvalidAttestation(address updater, bytes attestation);

/**
* @notice Emitted when proof of an invalid fraud report is submitted
* @param watchtower Watchtower who signed invalid fraud report
* @param report Report data and signature
*/
event InvalidReport(address watchtower, bytes report);

/**
* @notice Emitted when the Updater is slashed
* (should be paired with ImproperUpdater or DoubleUpdate event)
* @param updater The address of the updater
* @param reporter The address of the entity that reported the updater misbehavior
* (should be paired with InvalidAttestation event)
* @param updater The address of the updater
* @param reporter The address of the entity that reported the updater misbehavior
* @param watchtower The address of watchtower that signed the fraud report
*/
event UpdaterSlashed(address indexed updater, address indexed reporter);
event UpdaterSlashed(
address indexed updater,
address indexed reporter,
address indexed watchtower
);

/**
* @notice Emitted when the UpdaterManager contract is changed
Expand All @@ -119,10 +135,10 @@ contract Home is Version0, MerkleTreeManager, UpdaterStorage, AuthManager {

// ============ Initializer ============

function initialize(IUpdaterManager _updaterManager) public initializer {
function initialize(IUpdaterManager _updaterManager, address _watchtower) public initializer {
// initialize queue, set Updater Manager, and initialize
_setUpdaterManager(_updaterManager);
__SynapseBase_initialize(updaterManager.updater());
__SynapseBase_initialize(updaterManager.updater(), _watchtower);
state = States.Active;
}

Expand Down Expand Up @@ -238,56 +254,59 @@ contract Home is Version0, MerkleTreeManager, UpdaterStorage, AuthManager {
return _domainHash(localDomain);
}

// ============ Public Functions ============
// ============ Internal Functions ============

/**
* @notice Check if an Attestation is an Improper Attestation;
* if so, slash the Updater and set the contract to FAILED state.
* @notice Check if a reported Attestation is an Invalid Attestation;
* if so, slash the Notary and set the contract to FAILED state.
*
* An Improper Attestation is a (_nonce, _root) update that doesn't correspond with
* An Invalid Attestation is a (_nonce, _root) attestation that doesn't correspond with
* the historical state of Home contract. Either of those needs to be true:
* - _nonce is higher than current nonce (no root exists for this nonce)
* - _root is not equal to the historical root of _nonce
* This would mean that message(s) that were not truly
* dispatched on Home were falsely included in the signed root.
*
* An Improper Attestation will only be accepted as valid by the Replica
* If an Improper Attestation is attempted on Home,
* the Updater will be slashed immediately.
* If an Improper Attestation is submitted to the Replica,
* it should be relayed to the Home contract using this function
* in order to slash the Updater with an Improper Attestation.
* An Invalid Attestation will only be accepted as valid by the Replica
* If an Invalid Attestation is attempted on Home, the Notary will be slashed immediately.
* If an Invalid Attestation is submitted to the Replica, a Guard should generate a Report.
* This Report should be submitted to the Home contract using this function
* in order to slash the Notary with an Invalid Attestation.
*
* @dev Reverts (and doesn't slash updater) if signature is invalid or
* update not current
* @param _updater Updater who signed the attestation
* @param _attestation Attestation data and signature
* @return TRUE if update was an Improper Attestation (implying Updater was slashed)
* @dev Both Notary and Guard signatures
* have been checked at this point (see ReportHub.sol).
*
* @param _guard Guard address
* @param _notary Notary address
* @param _attestationView Memory view over reported Attestation
* @param _report Payload with Report data and signature
* @return TRUE if attestation was an Invalid Attestation (implying Notary was slashed)
*/
function improperAttestation(address _updater, bytes memory _attestation)
public
notFailed
returns (bool)
{
// This will revert if signature is not valid
bytes29 _view = _checkUpdaterAuth(_updater, _attestation);
uint32 _nonce = _view.attestationNonce();
bytes32 _root = _view.attestationRoot();
// Check if nonce is valid, if not => update is fraud
function _handleReport(
address _guard,
address _notary,
bytes29 _attestationView,
bytes memory _report
) internal override notFailed returns (bool) {
// Get merkle state from the attestation
uint32 _nonce = _attestationView.attestationNonce();
bytes32 _root = _attestationView.attestationRoot();
// Check if `_nonce` exists, if not => attestation is fraud
if (_nonce < historicalRoots.length) {
if (_root == historicalRoots[_nonce]) {
// Signed (nonce, root) update is valid
// (nonce, root) corresponds with the historical merkle state of Home.
// Means Notary attestation is valid, while Guard report is invalid.
// TODO: slash Guard for signing an invalid fraud report
emit InvalidReport(_guard, _report);
return false;
}
// Signed root is not the same as the historical one => update is fraud
// `_root` doesn't match historical root for `_nonce` => attestation is fraud
}
_fail();
emit ImproperAttestation(_updater, _attestation);
_fail(_guard);
emit InvalidAttestation(_notary, _attestationView.clone());
return true;
}

// ============ Internal Functions ============

/**
* @notice Set the UpdaterManager
* @param _updaterManager Address of the UpdaterManager
Expand All @@ -300,14 +319,14 @@ contract Home is Version0, MerkleTreeManager, UpdaterStorage, AuthManager {

/**
* @notice Slash the Updater and set contract state to FAILED
* @dev Called when fraud is proven (Improper Update or Double Update)
* @dev Called when fraud is proven (Invalid Attestation)
*/
function _fail() internal {
function _fail(address _watchtower) internal {
// set contract to FAILED
state = States.Failed;
// slash Updater
updaterManager.slashUpdater(payable(msg.sender));
emit UpdaterSlashed(updater, msg.sender);
emit UpdaterSlashed(updater, msg.sender, _watchtower);
}

/**
Expand Down Expand Up @@ -336,8 +355,8 @@ contract Home is Version0, MerkleTreeManager, UpdaterStorage, AuthManager {
return _updater == updater;
}

function _isWatchtower(address) internal pure override returns (bool) {
return false;
function _isWatchtower(address _watchtower) internal view override returns (bool) {
return _watchtower == watchtower;
}

/**
Expand Down
60 changes: 51 additions & 9 deletions packages/contracts/contracts/ReplicaManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ pragma solidity 0.8.13;

// ============ Internal Imports ============
import { UpdaterStorage } from "./UpdaterStorage.sol";
import { AuthManager } from "./auth/AuthManager.sol";
import { ReportHub } from "./auth/ReportHub.sol";
import { Attestation } from "./libs/Attestation.sol";
import { Report } from "./libs/Report.sol";
import { Version0 } from "./Version0.sol";
import { ReplicaLib } from "./libs/Replica.sol";
import { MerkleLib } from "./libs/Merkle.sol";
Expand All @@ -22,14 +23,15 @@ import { TypedMemView } from "./libs/TypedMemView.sol";
* @notice Track root updates on Home,
* prove and dispatch messages to end recipients.
*/
contract ReplicaManager is Version0, UpdaterStorage, AuthManager {
contract ReplicaManager is Version0, UpdaterStorage, ReportHub {
// ============ Libraries ============

using ReplicaLib for ReplicaLib.Replica;
using MerkleLib for MerkleLib.Tree;
using Message for bytes;
using TypedMemView for bytes29;
using Attestation for bytes29;
using Report for bytes29;
using Message for bytes29;
using Header for bytes29;

Expand Down Expand Up @@ -76,6 +78,13 @@ contract ReplicaManager is Version0, UpdaterStorage, AuthManager {
uint256 newConfirmAt
);

event UpdaterBlacklisted(
address indexed updater,
address indexed reporter,
address indexed watchtower,
bytes report
);

// ============ Constructor ============

constructor(uint32 _localDomain) UpdaterStorage(_localDomain) {}
Expand All @@ -93,8 +102,12 @@ contract ReplicaManager is Version0, UpdaterStorage, AuthManager {
* @param _remoteDomain The domain of the Home contract this follows
* @param _updater The EVM id of the updater
*/
function initialize(uint32 _remoteDomain, address _updater) public initializer {
__SynapseBase_initialize(_updater);
function initialize(
uint32 _remoteDomain,
address _updater,
address _watchtower
) public initializer {
__SynapseBase_initialize(_updater, _watchtower);
// set storage variables
entered = 1;
activeReplicas[_remoteDomain] = _createReplica(_remoteDomain);
Expand Down Expand Up @@ -133,11 +146,10 @@ contract ReplicaManager is Version0, UpdaterStorage, AuthManager {
* marks root's allowable confirmation time, and emits an `Update` event.
* @dev Reverts if update doesn't build off latest committedRoot
* or if signature is invalid.
* @param _updater Updater who signer the attestation
* @param _attestation Attestation data and signature
*/
function submitAttestation(address _updater, bytes memory _attestation) external {
bytes29 _view = _checkUpdaterAuth(_updater, _attestation);
function submitAttestation(bytes memory _attestation) external {
(, bytes29 _view) = _checkUpdaterAuth(_attestation);
uint32 remoteDomain = _view.attestationDomain();
require(remoteDomain != localDomain, "Update refers to local chain");
uint32 nonce = _view.attestationNonce();
Expand Down Expand Up @@ -340,8 +352,8 @@ contract ReplicaManager is Version0, UpdaterStorage, AuthManager {
return _updater == updater;
}

function _isWatchtower(address) internal pure override returns (bool) {
return false;
function _isWatchtower(address _watchtower) internal view override returns (bool) {
return _watchtower == watchtower;
}

function _checkForSystemMessage(bytes32 _recipient) internal view returns (address recipient) {
Expand All @@ -359,7 +371,37 @@ contract ReplicaManager is Version0, UpdaterStorage, AuthManager {
}
}

/**
* @notice Applies submitted Report to blacklist reported Notary, and all roots signed by this Notary.
* An honest Notary is incentivized to sign a valid Attestation to collect tips
* from the pending messages, which prevents downtime caused by root blacklisting.
*
* @dev Both Notary and Guard signatures
* have been checked at this point (see ReportHub.sol).
*
* @param _guard Guard address
* @param _notary Notary address
* @param _report Report payload
* @return notaryBlacklisted TRUE if Notary was blacklisted as a result, FALSE if Notary has been blacklisted earlier.
*/
function _handleReport(
address _guard,
address _notary,
bytes29,
bytes memory _report
) internal override returns (bool notaryBlacklisted) {
notaryBlacklisted = _blacklistNotary(_notary);
if (notaryBlacklisted) {
emit UpdaterBlacklisted(_notary, msg.sender, _guard, _report);
}
}

function _storeTips(bytes29 _tips) internal virtual {
// TODO: implement storing & claiming logic
}

function _blacklistNotary(address _notary) internal returns (bool) {
// TODO: implement actual blacklisting
return _notary == updater;
}
}
8 changes: 7 additions & 1 deletion packages/contracts/contracts/UpdaterStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ abstract contract UpdaterStorage is Initializable, OwnableUpgradeable {

// Address of bonded Updater
address public updater;
// Address of bonded Watchtower
address public watchtower;

ISystemMessenger public systemMessenger;

Expand Down Expand Up @@ -67,9 +69,13 @@ abstract contract UpdaterStorage is Initializable, OwnableUpgradeable {

// ============ Initializer ============

function __SynapseBase_initialize(address _updater) internal onlyInitializing {
function __SynapseBase_initialize(address _updater, address _watchtower)
internal
onlyInitializing
{
__Ownable_init();
_setUpdater(_updater);
watchtower = _watchtower;
}

// ============ Modifiers ============
Expand Down
Loading