Skip to content

Commit 722943e

Browse files
wjmelementsrvagg
andauthored
feat: announcePlannedUpgrade (#260)
Reviewer @rvagg Per a discussion with @jennijuju, we want to make the upgrade process more transparent. This changes upgrades to a two step process. First, we announce the planned upgrade. Then, after the designated time, we can complete the upgrade. The delay gives us time to check the pending implementation address. It also gives users notice even if we are not in communication with them. I also fix #259 in this change, because I was already adding tests for `upgradeToAndCall`. I can fix it separately if this change is rejected. After validating that this is a good design that the team likes, I can replicate it for the other proxies in separate PRs. #### Test Plan I tested the new deployment scripts by running them on calibnet. * [announcePlannedUpgrade](https://calibration.filfox.info/en/message/bafy2bzaceb6vpcsy563vhpwig5g6jy7gb6fr2ssxhjgwiagj7k3vbjrg3dg56?t=3) * [upgradeToAndCall](https://calibration.filfox.info/en/message/bafy2bzaceadjmlemjyrv7nmwh5lzavdbntjmywvblrzmtuh3wsyldqlvxbcyo?t=3) #### Changes * announcePlannedUpgrade * update _authorizeUpgrade * new view method for pending upgrade * make gen * add and fix tests covering upgradeToAndCall with migrate * separate announce and upgrade scripts * update implementation-only script --------- Co-authored-by: Rod Vagg <[email protected]>
1 parent 7e78964 commit 722943e

12 files changed

+484
-156
lines changed

service_contracts/abi/FilecoinWarmStorageService.abi.json

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@
7474
"outputs": [],
7575
"stateMutability": "nonpayable"
7676
},
77+
{
78+
"type": "function",
79+
"name": "announcePlannedUpgrade",
80+
"inputs": [
81+
{
82+
"name": "plannedUpgrade",
83+
"type": "tuple",
84+
"internalType": "struct FilecoinWarmStorageService.PlannedUpgrade",
85+
"components": [
86+
{
87+
"name": "nextImplementation",
88+
"type": "address",
89+
"internalType": "address"
90+
},
91+
{
92+
"name": "afterEpoch",
93+
"type": "uint96",
94+
"internalType": "uint96"
95+
}
96+
]
97+
}
98+
],
99+
"outputs": [],
100+
"stateMutability": "nonpayable"
101+
},
77102
{
78103
"type": "function",
79104
"name": "calculateRatesPerEpoch",
@@ -627,19 +652,6 @@
627652
"outputs": [],
628653
"stateMutability": "nonpayable"
629654
},
630-
{
631-
"type": "function",
632-
"name": "serviceCommissionBps",
633-
"inputs": [],
634-
"outputs": [
635-
{
636-
"name": "",
637-
"type": "uint256",
638-
"internalType": "uint256"
639-
}
640-
],
641-
"stateMutability": "view"
642-
},
643655
{
644656
"type": "function",
645657
"name": "serviceProviderRegistry",
@@ -1411,6 +1423,31 @@
14111423
],
14121424
"anonymous": false
14131425
},
1426+
{
1427+
"type": "event",
1428+
"name": "UpgradeAnnounced",
1429+
"inputs": [
1430+
{
1431+
"name": "plannedUpgrade",
1432+
"type": "tuple",
1433+
"indexed": false,
1434+
"internalType": "struct FilecoinWarmStorageService.PlannedUpgrade",
1435+
"components": [
1436+
{
1437+
"name": "nextImplementation",
1438+
"type": "address",
1439+
"internalType": "address"
1440+
},
1441+
{
1442+
"name": "afterEpoch",
1443+
"type": "uint96",
1444+
"internalType": "uint96"
1445+
}
1446+
]
1447+
}
1448+
],
1449+
"anonymous": false
1450+
},
14141451
{
14151452
"type": "event",
14161453
"name": "Upgraded",
@@ -1994,22 +2031,6 @@
19942031
}
19952032
]
19962033
},
1997-
{
1998-
"type": "error",
1999-
"name": "OnlySelf",
2000-
"inputs": [
2001-
{
2002-
"name": "expected",
2003-
"type": "address",
2004-
"internalType": "address"
2005-
},
2006-
{
2007-
"name": "actual",
2008-
"type": "address",
2009-
"internalType": "address"
2010-
}
2011-
]
2012-
},
20132034
{
20142035
"type": "error",
20152036
"name": "OwnableInvalidOwner",

service_contracts/abi/FilecoinWarmStorageServiceStateLibrary.abi.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,30 @@
588588
],
589589
"stateMutability": "view"
590590
},
591+
{
592+
"type": "function",
593+
"name": "nextUpgrade",
594+
"inputs": [
595+
{
596+
"name": "service",
597+
"type": "FilecoinWarmStorageService",
598+
"internalType": "contract FilecoinWarmStorageService"
599+
}
600+
],
601+
"outputs": [
602+
{
603+
"name": "nextImplementation",
604+
"type": "address",
605+
"internalType": "address"
606+
},
607+
{
608+
"name": "afterEpoch",
609+
"type": "uint96",
610+
"internalType": "uint96"
611+
}
612+
],
613+
"stateMutability": "view"
614+
},
591615
{
592616
"type": "function",
593617
"name": "provenPeriods",
@@ -713,6 +737,25 @@
713737
],
714738
"stateMutability": "view"
715739
},
740+
{
741+
"type": "function",
742+
"name": "serviceCommissionBps",
743+
"inputs": [
744+
{
745+
"name": "service",
746+
"type": "FilecoinWarmStorageService",
747+
"internalType": "contract FilecoinWarmStorageService"
748+
}
749+
],
750+
"outputs": [
751+
{
752+
"name": "",
753+
"type": "uint256",
754+
"internalType": "uint256"
755+
}
756+
],
757+
"stateMutability": "view"
758+
},
716759
{
717760
"type": "error",
718761
"name": "ProvingPeriodNotInitialized",

service_contracts/abi/FilecoinWarmStorageServiceStateView.abi.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,24 @@
509509
],
510510
"stateMutability": "view"
511511
},
512+
{
513+
"type": "function",
514+
"name": "nextUpgrade",
515+
"inputs": [],
516+
"outputs": [
517+
{
518+
"name": "nextImplementation",
519+
"type": "address",
520+
"internalType": "address"
521+
},
522+
{
523+
"name": "afterEpoch",
524+
"type": "uint96",
525+
"internalType": "uint96"
526+
}
527+
],
528+
"stateMutability": "view"
529+
},
512530
{
513531
"type": "function",
514532
"name": "provenPeriods",
@@ -622,6 +640,19 @@
622640
],
623641
"stateMutability": "view"
624642
},
643+
{
644+
"type": "function",
645+
"name": "serviceCommissionBps",
646+
"inputs": [],
647+
"outputs": [
648+
{
649+
"name": "",
650+
"type": "uint256",
651+
"internalType": "uint256"
652+
}
653+
],
654+
"stateMutability": "view"
655+
},
625656
{
626657
"type": "error",
627658
"name": "ProvingPeriodNotInitialized",

service_contracts/src/FilecoinWarmStorageService.sol

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ contract FilecoinWarmStorageService is
151151
uint256 epochsPerMonth; // Number of epochs in a month
152152
}
153153

154+
// Used for announcing upgrades, packed into one slot
155+
struct PlannedUpgrade {
156+
// Address of the new implementation contract
157+
address nextImplementation;
158+
// Upgrade will not occur until at least this epoch
159+
uint96 afterEpoch;
160+
}
161+
154162
// =========================================================================
155163
// Constants
156164

@@ -229,7 +237,7 @@ contract FilecoinWarmStorageService is
229237
uint256 private challengeWindowSize;
230238

231239
// Commission rate
232-
uint256 public serviceCommissionBps;
240+
uint256 private serviceCommissionBps;
233241

234242
// Track which proving periods have valid proofs with bitmap
235243
mapping(uint256 dataSetId => mapping(uint256 periodId => uint256)) private provenPeriods;
@@ -266,6 +274,10 @@ contract FilecoinWarmStorageService is
266274
// The address allowed to terminate CDN services
267275
address private filBeamControllerAddress;
268276

277+
PlannedUpgrade private nextUpgrade;
278+
279+
event UpgradeAnnounced(PlannedUpgrade plannedUpgrade);
280+
269281
// =========================================================================
270282

271283
// Modifier to ensure only the PDP verifier contract can call certain functions
@@ -374,7 +386,19 @@ contract FilecoinWarmStorageService is
374386
serviceCommissionBps = 0; // 0%
375387
}
376388

377-
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
389+
function announcePlannedUpgrade(PlannedUpgrade calldata plannedUpgrade) external onlyOwner {
390+
require(plannedUpgrade.nextImplementation.code.length > 3000);
391+
require(plannedUpgrade.afterEpoch > block.number);
392+
nextUpgrade = plannedUpgrade;
393+
emit UpgradeAnnounced(plannedUpgrade);
394+
}
395+
396+
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {
397+
// zero address already checked by ERC1967Utils._setImplementation
398+
require(newImplementation == nextUpgrade.nextImplementation);
399+
require(block.number >= nextUpgrade.afterEpoch);
400+
delete nextUpgrade;
401+
}
378402

379403
/**
380404
* @notice Sets new proving period parameters
@@ -398,9 +422,7 @@ contract FilecoinWarmStorageService is
398422
* Only callable during proxy upgrade process
399423
* @param _viewContract Address of the view contract (optional, can be address(0))
400424
*/
401-
function migrate(address _viewContract) public onlyProxy reinitializer(4) {
402-
require(msg.sender == address(this), Errors.OnlySelf(address(this), msg.sender));
403-
425+
function migrate(address _viewContract) public onlyProxy onlyOwner reinitializer(4) {
404426
// Set view contract if provided
405427
if (_viewContract != address(0)) {
406428
viewContractAddress = _viewContract;

service_contracts/src/FilecoinWarmStorageServiceStateView.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ contract FilecoinWarmStorageServiceStateView is IPDPProvingSchedule {
131131
return service.nextPDPChallengeWindowStart(setId);
132132
}
133133

134+
function nextUpgrade() external view returns (address nextImplementation, uint96 afterEpoch) {
135+
return service.nextUpgrade();
136+
}
137+
134138
function provenPeriods(uint256 dataSetId, uint256 periodId) external view returns (bool) {
135139
return service.provenPeriods(dataSetId, periodId);
136140
}
@@ -150,4 +154,8 @@ contract FilecoinWarmStorageServiceStateView is IPDPProvingSchedule {
150154
function railToDataSet(uint256 railId) external view returns (uint256) {
151155
return service.railToDataSet(railId);
152156
}
157+
158+
function serviceCommissionBps() external view returns (uint256) {
159+
return service.serviceCommissionBps();
160+
}
153161
}

service_contracts/src/lib/FilecoinWarmStorageServiceLayout.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ bytes32 constant APPROVED_PROVIDERS_SLOT = bytes32(uint256(15));
2424
bytes32 constant APPROVED_PROVIDER_IDS_SLOT = bytes32(uint256(16));
2525
bytes32 constant VIEW_CONTRACT_ADDRESS_SLOT = bytes32(uint256(17));
2626
bytes32 constant FIL_BEAM_CONTROLLER_ADDRESS_SLOT = bytes32(uint256(18));
27+
bytes32 constant NEXT_UPGRADE_SLOT = bytes32(uint256(19));

service_contracts/src/lib/FilecoinWarmStorageServiceStateInternalLibrary.sol

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ library FilecoinWarmStorageServiceStateInternalLibrary {
203203
initChallengeWindowStart = block.number + maxProvingPeriod - challengeWindowSize;
204204
}
205205

206+
function serviceCommissionBps(FilecoinWarmStorageService service) internal view returns (uint256) {
207+
return uint256(service.extsload(StorageLayout.SERVICE_COMMISSION_BPS_SLOT));
208+
}
209+
206210
/**
207211
* @notice Returns the start of the next challenge window for a data set
208212
* @param service The service contract
@@ -490,4 +494,20 @@ library FilecoinWarmStorageServiceStateInternalLibrary {
490494
function filBeamControllerAddress(FilecoinWarmStorageService service) internal view returns (address) {
491495
return address(uint160(uint256(service.extsload(StorageLayout.FIL_BEAM_CONTROLLER_ADDRESS_SLOT))));
492496
}
497+
498+
/**
499+
* @notice Get information about the next contract upgrade
500+
* @param service The service contract
501+
* @return nextImplementation The next code for the contract
502+
* @return afterEpoch The earliest the upgrade may complete
503+
*/
504+
function nextUpgrade(FilecoinWarmStorageService service)
505+
internal
506+
view
507+
returns (address nextImplementation, uint96 afterEpoch)
508+
{
509+
bytes32 upgradeInfo = service.extsload(StorageLayout.NEXT_UPGRADE_SLOT);
510+
nextImplementation = address(uint160(uint256(upgradeInfo)));
511+
afterEpoch = uint96(uint256(upgradeInfo) >> 160);
512+
}
493513
}

service_contracts/src/lib/FilecoinWarmStorageServiceStateLibrary.sol

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ library FilecoinWarmStorageServiceStateLibrary {
199199
initChallengeWindowStart = block.number + maxProvingPeriod - challengeWindowSize;
200200
}
201201

202+
function serviceCommissionBps(FilecoinWarmStorageService service) public view returns (uint256) {
203+
return uint256(service.extsload(StorageLayout.SERVICE_COMMISSION_BPS_SLOT));
204+
}
205+
202206
/**
203207
* @notice Returns the start of the next challenge window for a data set
204208
* @param service The service contract
@@ -486,4 +490,20 @@ library FilecoinWarmStorageServiceStateLibrary {
486490
function filBeamControllerAddress(FilecoinWarmStorageService service) public view returns (address) {
487491
return address(uint160(uint256(service.extsload(StorageLayout.FIL_BEAM_CONTROLLER_ADDRESS_SLOT))));
488492
}
493+
494+
/**
495+
* @notice Get information about the next contract upgrade
496+
* @param service The service contract
497+
* @return nextImplementation The next code for the contract
498+
* @return afterEpoch The earliest the upgrade may complete
499+
*/
500+
function nextUpgrade(FilecoinWarmStorageService service)
501+
public
502+
view
503+
returns (address nextImplementation, uint96 afterEpoch)
504+
{
505+
bytes32 upgradeInfo = service.extsload(StorageLayout.NEXT_UPGRADE_SLOT);
506+
nextImplementation = address(uint160(uint256(upgradeInfo)));
507+
afterEpoch = uint96(uint256(upgradeInfo) >> 160);
508+
}
489509
}

0 commit comments

Comments
 (0)