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

Add owner to Replica #32

Merged
merged 10 commits into from
Jan 29, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion rust/optics-core/src/test_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub mod output_functions {
)
.unwrap();
tree.push_leaf(
"0x7d2185e5a65904eeb35980b8e335f72d31feccfdc12b9bc0f6cbe32073ea7fba"
"0x5068ac60cb6f9c5202bbe8e7a1babdd972133ea3ad37d7e0e753c7e4ddd7ffbd"
.parse()
.unwrap(),
TREE_DEPTH,
Expand Down
38 changes: 13 additions & 25 deletions solidity/optics-core/contracts/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ abstract contract Common is Initializable {
bytes signature2
);

/**
* @notice Emitted when Updater is rotated
* @param updater The address of the new updater
*/
event NewUpdater(address updater);

// ============ Modifiers ============

/**
Expand Down Expand Up @@ -123,8 +129,7 @@ abstract contract Common is Initializable {
if (
Common._isUpdaterSignature(_oldRoot, _newRoot[0], _signature) &&
Common._isUpdaterSignature(_oldRoot, _newRoot[1], _signature2) &&
_newRoot[0] != _newRoot[1] &&
!Common._isBenignDoubleUpdate(_oldRoot)
_newRoot[0] != _newRoot[1]
) {
_fail();
emit DoubleUpdate(_oldRoot, _newRoot, _signature, _signature2);
Expand Down Expand Up @@ -188,28 +193,11 @@ abstract contract Common is Initializable {
}

/**
* @notice Checks that a root is in a whitelist for which double updates
* are not enforced.
* @param _oldRoot Old merkle root
* @return TRUE iff the provided root is in the whitelist
**/
function _isBenignDoubleUpdate(bytes32 _oldRoot)
internal
view
returns (bool)
{
// The Polygon home temporarily forked from its replicas at this
// root due to a chain reorg.
// Polygon update txHash:
// 0x9cbe36b8d5365df013f138421b99283c014bbeee08f9c3b1f19ae428511e94ba
// Ethereum update txHash:
// 0xe8df2fc845356c1c75206982f8b707e8e5354c72a2ed250fcf839cbbf11101f9
// The fork was benign, as all roots were commitments to the same set
// of messages. The fork was resolved and therefore the system should
// not halt when presented with any double update that builds off of
// this root.
return
_oldRoot ==
0xde6e3d4540f861d08dfe4ac16334792de2fb44aa7bcd5b657238410791c67a81;
* @notice Set the Updater
* @param _updater Address of the Updater
*/
function _setUpdater(address _updater) internal {
updater = _updater;
emit NewUpdater(_updater);
}
}
16 changes: 1 addition & 15 deletions solidity/optics-core/contracts/Home.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,6 @@ contract Home is
*/
event UpdaterSlashed(address indexed updater, address indexed reporter);

/**
* @notice Emitted when Updater is rotated by the UpdaterManager
* @param updater The address of the new updater
*/
event NewUpdater(address updater);

/**
* @notice Emitted when the UpdaterManager contract is changed
* @param updaterManager The address of the new updaterManager
Expand Down Expand Up @@ -213,6 +207,7 @@ contract Home is
) external notFailed {
// check that the update is not fraudulent;
// if fraud is detected, Updater is slashed & Home is set to FAILED state
// Opportunity for gas savings, we iterate through the queue twice.
if (improperUpdate(_committedRoot, _newRoot, _signature)) return;
// clear all of the intermediate roots contained in this update from the queue
while (true) {
Expand Down Expand Up @@ -315,15 +310,6 @@ contract Home is
emit NewUpdaterManager(address(_updaterManager));
}

/**
* @notice Set the Updater
* @param _updater Address of the Updater
*/
function _setUpdater(address _updater) internal {
updater = _updater;
emit NewUpdater(_updater);
}

/**
* @notice Slash the Updater and set contract state to FAILED
* @dev Called when fraud is proven (Improper Update or Double Update)
Expand Down
52 changes: 50 additions & 2 deletions solidity/optics-core/contracts/Replica.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ contract Replica is Version0, Common {
mapping(bytes32 => uint256) public confirmAt;
// Mapping of message leaves to MessageStatus
mapping(bytes32 => MessageStatus) public messages;
// address responsible for Updater rotation
address private _owner;
asaj marked this conversation as resolved.
Show resolved Hide resolved

// ============ Upgrade Gap ============

Expand All @@ -63,6 +65,11 @@ contract Replica is Version0, Common {

// ============ Events ============

event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);

/**
* @notice Emitted when message is processed
* @param messageHash Hash of message that failed to process
Expand Down Expand Up @@ -103,10 +110,33 @@ contract Replica is Version0, Common {
committedRoot = _committedRoot;
confirmAt[_committedRoot] = 1;
optimisticSeconds = _optimisticSeconds;
transferOwnership(msg.sender);
}

// ============ Modifiers ============

/**
* @notice Ensures that function is called by the owner
* @dev NOTE THAT WHEN OWNER IS THE NULL ADDRESS ANYONE CAN CALL ONLYOWNER
* FUNCTIONS. This is to allow the owner to be set post-facto. As such,
* renouncing ownership to the null address is unsafe and disabled.
*/
modifier onlyOwner() {
bool ok = msg.sender == owner() || owner() == address(0);
require(ok, "!owner");
_;
}

// ============ External Functions ============

/**
* @notice Set a new Updater
* @param _updater the new Updater
*/
function setUpdater(address _updater) external onlyOwner {
_setUpdater(_updater);
}

/**
* @notice Called by external agent. Submits the signed update's new root,
* marks root's allowable confirmation time, and emits an `Update` event.
Expand Down Expand Up @@ -155,6 +185,26 @@ contract Replica is Version0, Common {
process(_message);
}

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

/**
* @notice Returns the address of the current owner.
*/
// THIS SHOULD NOT BE VIRTUAL!
asaj marked this conversation as resolved.
Show resolved Hide resolved
function owner() public view virtual returns (address) {
return _owner;
}

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "!newOwner");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}

/**
* @notice Given formatted message, attempts to dispatch
* message payload to end recipient.
Expand Down Expand Up @@ -229,8 +279,6 @@ contract Replica is Version0, Common {
entered = 1;
}

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

/**
* @notice Check that the root has been submitted
* and that the optimistic timeout period has expired,
Expand Down
4 changes: 0 additions & 4 deletions solidity/optics-core/contracts/test/TestReplica.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ contract TestReplica is Replica {
_setFailed();
}

function setUpdater(address _updater) external {
updater = _updater;
}

function setRemoteDomain(uint32 _remoteDomain) external {
remoteDomain = _remoteDomain;
}
Expand Down
8 changes: 8 additions & 0 deletions typescript/optics-deploy/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,14 @@ export async function relinquish(deploy: CoreDeploy) {
`${deploy.chain.name}: Dispatched relinquish upgradeBeaconController`,
);

Object.entries(deploy.contracts.replicas).forEach(async ([domain, replica]) => {
await replica.proxy.transferOwnership(govRouter, deploy.overrides);
log(
isTestDeploy,
`${deploy.chain.name}: Dispatched relinquish Replica for domain ${domain}`,
);
});

let tx = await deploy.contracts.home!.proxy.transferOwnership(
govRouter,
deploy.overrides,
Expand Down
2 changes: 1 addition & 1 deletion typescript/optics-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"dependencies": {
"@optics-xyz/deploy": "^0.0.3",
"@optics-xyz/multi-provider": "^0.0.4",
"@optics-xyz/ts-interface": "^1.0.9",
"@optics-xyz/ts-interface": "1.1.0",
"@types/node": "^15.14.7",
"dotenv": "^10.0.0",
"ethers": "^5.4.7"
Expand Down
25 changes: 0 additions & 25 deletions typescript/optics-tests/test/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,31 +94,6 @@ describe('Common', async () => {
expect(state).to.equal(OpticsState.ACTIVE);
});

it('Does not fail contract on whitelisted double update proof', async () => {
const oldRoot = ethers.utils.hexlify(
'0xde6e3d4540f861d08dfe4ac16334792de2fb44aa7bcd5b657238410791c67a81');
const newRoot = ethers.utils.formatBytes32String('new root 1');
const newRoot2 = ethers.utils.formatBytes32String('new root 2');

const { signature } = await updater.signUpdate(oldRoot, newRoot);
const { signature: signature2 } = await updater.signUpdate(
oldRoot,
newRoot2,
);

await common.doubleUpdate(
oldRoot,
[newRoot, newRoot2],
signature,
signature2
);

// State should not be failed because double update proof is whitelisted
const state = await common.state();
expect(state).not.to.equal(OpticsState.FAILED);
expect(state).to.equal(OpticsState.ACTIVE);
});

it('Checks Rust-produced SignedUpdate', async () => {
// Compare Rust output in json file to solidity output
for (let testCase of signedUpdateTestCases) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ describe('GovernanceRouter', async () => {
// dispatch call on local governorRouter
let tx = await governorRouter.callRemote(nonGovernorDomain, [call]);
let receipt = await tx.wait(0);
console.log('events', receipt.events)
asaj marked this conversation as resolved.
Show resolved Hide resolved
let leaf = receipt.events?.[0].topics[1];

expect(leaf).to.equal(helpers.proof.leaf);
Expand Down
6 changes: 6 additions & 0 deletions typescript/optics-tests/test/replica.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ describe('Replica', async () => {
).to.be.revertedWith('Initializable: contract is already initialized');
});

it('Owner can rotate updater', async () => {
asaj marked this conversation as resolved.
Show resolved Hide resolved
const newUpdater = fakeUpdater.address
await replica.setUpdater(newUpdater);
expect(await replica.updater()).to.equal(newUpdater);
});

it('Halts on fail', async () => {
await replica.setFailed();
expect(await replica.state()).to.equal(OpticsState.FAILED);
Expand Down
4 changes: 4 additions & 0 deletions typescript/typechain/optics-core/Common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ interface CommonInterface extends ethers.utils.Interface {

events: {
"DoubleUpdate(bytes32,bytes32[2],bytes,bytes)": EventFragment;
"NewUpdater(address)": EventFragment;
"Update(uint32,bytes32,bytes32,bytes)": EventFragment;
};

getEvent(nameOrSignatureOrTopic: "DoubleUpdate"): EventFragment;
getEvent(nameOrSignatureOrTopic: "NewUpdater"): EventFragment;
getEvent(nameOrSignatureOrTopic: "Update"): EventFragment;
}

Expand Down Expand Up @@ -193,6 +195,8 @@ export class Common extends BaseContract {
}
>;

NewUpdater(updater?: null): TypedEventFilter<[string], { updater: string }>;

Update(
homeDomain?: BigNumberish | null,
oldRoot?: BytesLike | null,
Expand Down
Loading