Skip to content

Commit 21ceeb4

Browse files
committed
Per-contract panic buttons that can be disabled
We now allow to specify a panic button per contract. Such individual panic buttons can be disabled what makes the operator contract permanently approved. There is a default panic button value that is assigned to each new operator contract. The only role which can disable panic button for operator contract is governance. We don't want the operator contract upgrader to have a power of permanently approving operator contracts if this action can not be later undone.
1 parent 3f2166e commit 21ceeb4

3 files changed

Lines changed: 249 additions & 45 deletions

File tree

solidity/contracts/Registry.sol

Lines changed: 88 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,32 @@ pragma solidity 0.5.17;
88
contract Registry {
99
enum ContractStatus {New, Approved, Disabled}
1010

11-
// Governance role is to enable recovery from key compromise by rekeying other roles.
11+
// Governance role is to enable recovery from key compromise by rekeying
12+
// other roles. Also, it can disable operator contract panic buttons
13+
// permanently.
1214
address internal governance;
1315

1416
// Registry Keeper maintains approved operator contracts. Each operator
1517
// contract must be approved before it can be authorized by a staker or
1618
// used by a service contract.
1719
address internal registryKeeper;
1820

19-
// The Panic Button can disable malicious or malfunctioning contracts
20-
// that have been previously approved by the Registry Keeper.
21-
address internal panicButton;
21+
// Each operator contract has a Panic Button which can disable malicious
22+
// or malfunctioning contract that have been previously approved by the
23+
// Registry Keeper.
24+
//
25+
// New operator contract added to the registry has a default panic button
26+
// value assigned (defaultPanicButton). Panic button for each operator
27+
// contract can be later updated by Governance to individual value.
28+
//
29+
// It is possible to disable panic button for individual contract by
30+
// setting the panic button to zero address. In such case, operator contract
31+
// can not be disabled and is permanently approved in the registry.
32+
mapping(address => address) public panicButtons;
33+
34+
// Default panic button for each new operator contract added to the
35+
// registry. Can be later updated for each contract.
36+
address internal defaultPanicButton;
2237

2338
// Each service contract has a Operator Contract Upgrader whose purpose
2439
// is to manage operator contracts for that specific service contract.
@@ -34,7 +49,12 @@ contract Registry {
3449

3550
event GovernanceUpdated();
3651
event RegistryKeeperUpdated();
37-
event PanicButtonUpdated();
52+
event DefaultPanicButtonUpdated();
53+
event OperatorContractPanicButtonDisabled(address operatorContract);
54+
event OperatorContractPanicButtonUpdated(
55+
address operatorContract,
56+
address panicButton
57+
);
3858
event OperatorContractUpgraderUpdated(
3959
address serviceContract,
4060
address upgrader
@@ -50,15 +70,33 @@ contract Registry {
5070
_;
5171
}
5272

53-
modifier onlyPanicButton() {
73+
modifier onlyPanicButton(address _operatorContract) {
74+
address panicButton = panicButtons[_operatorContract];
75+
require(panicButton != address(0), "Panic button disabled");
5476
require(panicButton == msg.sender, "Not authorized");
5577
_;
5678
}
5779

80+
modifier onlyForNewContract(address _operatorContract) {
81+
require(
82+
isNewOperatorContract(_operatorContract),
83+
"Not a new operator contract"
84+
);
85+
_;
86+
}
87+
88+
modifier onlyForApprovedContract(address _operatorContract) {
89+
require(
90+
isApprovedOperatorContract(_operatorContract),
91+
"Not an approved operator contract"
92+
);
93+
_;
94+
}
95+
5896
constructor() public {
5997
governance = msg.sender;
6098
registryKeeper = msg.sender;
61-
panicButton = msg.sender;
99+
defaultPanicButton = msg.sender;
62100
}
63101

64102
function setGovernance(address _governance) public onlyGovernance {
@@ -71,9 +109,45 @@ contract Registry {
71109
emit RegistryKeeperUpdated();
72110
}
73111

74-
function setPanicButton(address _panicButton) public onlyGovernance {
75-
panicButton = _panicButton;
76-
emit PanicButtonUpdated();
112+
function setDefaultPanicButton(address _panicButton) public onlyGovernance {
113+
defaultPanicButton = _panicButton;
114+
emit DefaultPanicButtonUpdated();
115+
}
116+
117+
function setOperatorContractPanicButton(
118+
address _operatorContract,
119+
address _panicButton
120+
) public onlyForApprovedContract(_operatorContract) onlyGovernance {
121+
require(
122+
panicButtons[_operatorContract] != address(0),
123+
"Disabled panic button cannot be updated"
124+
);
125+
require(
126+
_panicButton != address(0),
127+
"Panic button must be non-zero address"
128+
);
129+
130+
panicButtons[_operatorContract] = _panicButton;
131+
132+
emit OperatorContractPanicButtonUpdated(
133+
_operatorContract,
134+
_panicButton
135+
);
136+
}
137+
138+
function disableOperatorContractPanicButton(address _operatorContract)
139+
public
140+
onlyForApprovedContract(_operatorContract)
141+
onlyGovernance
142+
{
143+
require(
144+
panicButtons[_operatorContract] != address(0),
145+
"Panic button already disabled"
146+
);
147+
148+
panicButtons[_operatorContract] = address(0);
149+
150+
emit OperatorContractPanicButtonDisabled(_operatorContract);
77151
}
78152

79153
function setOperatorContractUpgrader(
@@ -89,26 +163,19 @@ contract Registry {
89163

90164
function approveOperatorContract(address operatorContract)
91165
public
166+
onlyForNewContract(operatorContract)
92167
onlyRegistryKeeper
93168
{
94-
require(
95-
isNewOperatorContract(operatorContract),
96-
"Only new operator contracts can be approved"
97-
);
98-
99169
operatorContracts[operatorContract] = ContractStatus.Approved;
170+
panicButtons[operatorContract] = defaultPanicButton;
100171
emit OperatorContractApproved(operatorContract);
101172
}
102173

103174
function disableOperatorContract(address operatorContract)
104175
public
105-
onlyPanicButton
176+
onlyForApprovedContract(operatorContract)
177+
onlyPanicButton(operatorContract)
106178
{
107-
require(
108-
isApprovedOperatorContract(operatorContract),
109-
"Only approved operator contracts can be disabled"
110-
);
111-
112179
operatorContracts[operatorContract] = ContractStatus.Disabled;
113180
emit OperatorContractDisabled(operatorContract);
114181
}

solidity/contracts/stubs/RegistryStub.sol

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ pragma solidity 0.5.17;
22

33
import "../Registry.sol";
44

5-
contract RegistryStub is Registry {
65

6+
contract RegistryStub is Registry {
77
function getGovernance() public view returns (address) {
88
return governance;
99
}
@@ -12,7 +12,15 @@ contract RegistryStub is Registry {
1212
return registryKeeper;
1313
}
1414

15-
function getPanicButton() public view returns (address) {
16-
return panicButton;
15+
function getDefaultPanicButton() public view returns (address) {
16+
return defaultPanicButton;
17+
}
18+
19+
function getPanicButtonForContract(address operatorContract)
20+
public
21+
view
22+
returns (address)
23+
{
24+
return panicButtons[operatorContract];
1725
}
1826
}

0 commit comments

Comments
 (0)