-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #50 from siburu/channel-upgrade
Support for Channel Upgrade
- Loading branch information
Showing
24 changed files
with
1,826 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
build/ | ||
cache/ | ||
node_modules/ | ||
out/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.20; | ||
|
||
import {IIBCContractUpgradableModule, IIBCContractUpgradableModuleErrors} from "./IIBCContractUpgradableModule.sol"; | ||
|
||
import {IBCChannelUpgradableModuleBase} from "@hyperledger-labs/yui-ibc-solidity/contracts/apps/commons/IBCChannelUpgradableModule.sol"; | ||
import {Channel, UpgradeFields} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/Channel.sol"; | ||
import {IIBCHandler} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/25-handler/IIBCHandler.sol"; | ||
|
||
abstract contract IBCContractUpgradableModuleBase is | ||
IBCChannelUpgradableModuleBase, | ||
IIBCContractUpgradableModule, | ||
IIBCContractUpgradableModuleErrors | ||
{ | ||
// ------------------- Storage ------------------- // | ||
|
||
// NOTE: A module should set an initial appVersion struct in contract constructor or initializer | ||
mapping(string appVersion => AppInfo) internal appInfos; | ||
|
||
// ------------------- Modifiers ------------------- // | ||
|
||
modifier onlyContractUpgrader() { | ||
address msgSender = _msgSender(); | ||
if (!_isContractUpgrader(msgSender)) { | ||
revert IBCContractUpgradableModuleUnauthorizedUpgrader(msgSender); | ||
} | ||
_; | ||
} | ||
|
||
// ------------------- Functions ------------------- // | ||
|
||
/** | ||
* @dev See {IIBCContractUpgradableModule-getAppinfoproposal} | ||
*/ | ||
function getAppInfoProposal(string calldata version) | ||
external | ||
view | ||
override(IIBCContractUpgradableModule) | ||
returns (AppInfo memory) | ||
{ | ||
return appInfos[version]; | ||
} | ||
|
||
/** | ||
* @dev See {IIBCContractUpgradableModule-proposeAppVersion} | ||
*/ | ||
function proposeAppVersion(string calldata version, AppInfo calldata appInfo_) | ||
external | ||
override(IIBCContractUpgradableModule) | ||
onlyContractUpgrader | ||
{ | ||
if (appInfo_.implementation == address(0)) { | ||
revert IBCContractUpgradableModuleAppInfoProposedWithZeroImpl(); | ||
} | ||
|
||
AppInfo storage appInfo = appInfos[version]; | ||
if (appInfo.implementation != address(0)) { | ||
revert IBCContractUpgradableModuleAppInfoIsAlreadySet(); | ||
} | ||
|
||
appInfos[version] = appInfo_; | ||
} | ||
|
||
/** | ||
* @dev See {IERC165-supportsInterface}. | ||
*/ | ||
function supportsInterface(bytes4 interfaceId) | ||
public | ||
view | ||
virtual | ||
override(IBCChannelUpgradableModuleBase) | ||
returns (bool) | ||
{ | ||
return super.supportsInterface(interfaceId) || | ||
interfaceId == type(IIBCContractUpgradableModule).interfaceId; | ||
} | ||
|
||
/** | ||
* @dev See {IIBCModuleUpgrade-onChanUpgradeInit} | ||
*/ | ||
function onChanUpgradeInit( | ||
string calldata portId, | ||
string calldata channelId, | ||
uint64 upgradeSequence, | ||
UpgradeFields.Data calldata proposedUpgradeFields | ||
) | ||
public | ||
view | ||
virtual | ||
override(IBCChannelUpgradableModuleBase) | ||
onlyIBC | ||
returns (string memory version) | ||
{ | ||
version = super.onChanUpgradeInit(portId, channelId, upgradeSequence, proposedUpgradeFields); | ||
|
||
(Channel.Data memory channel, bool found) = IIBCHandler(ibcAddress()).getChannel(portId, channelId); | ||
if (!found) { | ||
revert IBCContractUpgradableModuleChannelNotFound(portId, channelId); | ||
} | ||
|
||
_prepareContractUpgrade(channel.version, version); | ||
} | ||
|
||
/** | ||
* @dev See {IIBCModuleUpgrade-onChanUpgradeTry} | ||
*/ | ||
function onChanUpgradeTry( | ||
string calldata portId, | ||
string calldata channelId, | ||
uint64 upgradeSequence, | ||
UpgradeFields.Data calldata proposedUpgradeFields | ||
) | ||
public | ||
view | ||
virtual | ||
override(IBCChannelUpgradableModuleBase) | ||
onlyIBC | ||
returns (string memory version) | ||
{ | ||
version = super.onChanUpgradeTry(portId, channelId, upgradeSequence, proposedUpgradeFields); | ||
|
||
(Channel.Data memory channel, bool found) = IIBCHandler(ibcAddress()).getChannel(portId, channelId); | ||
if (!found) { | ||
revert IBCContractUpgradableModuleChannelNotFound(portId, channelId); | ||
} | ||
|
||
_prepareContractUpgrade(channel.version, version); | ||
} | ||
|
||
/** | ||
* @dev See {IIBCModuleUpgrade-onChanUpgradeOpen} | ||
*/ | ||
function onChanUpgradeOpen( | ||
string calldata portId, | ||
string calldata channelId, | ||
uint64 upgradeSequence | ||
) | ||
public | ||
virtual | ||
override(IBCChannelUpgradableModuleBase) | ||
onlyIBC | ||
{ | ||
super.onChanUpgradeOpen(portId, channelId, upgradeSequence); | ||
|
||
(Channel.Data memory channel, bool found) = IIBCHandler(ibcAddress()).getChannel(portId, channelId); | ||
if (!found) { | ||
revert IBCContractUpgradableModuleChannelNotFound(portId, channelId); | ||
} | ||
|
||
_upgradeContract(channel.version); | ||
} | ||
|
||
// ------------------- Internal Functions ------------------- // | ||
|
||
function _isContractUpgrader(address msgSender) internal view virtual returns (bool); | ||
|
||
function _doUpgradeContract(address implementation, bytes memory initialCalldata) internal virtual; | ||
|
||
// ------------------- Private Functions ------------------- // | ||
|
||
function _prepareContractUpgrade(string memory version, string memory newVersion) private view { | ||
if (!_compareString(version, newVersion)) { | ||
AppInfo storage appInfo = appInfos[newVersion]; | ||
if (appInfo.implementation == address(0)) { | ||
revert IBCContractUpgradableModuleAppInfoNotProposedYet(); | ||
} | ||
if (appInfo.consumed) { | ||
revert IBCContractUpgradableModuleAlreadyConsumedAppInfo(); | ||
} | ||
} | ||
} | ||
|
||
function _compareString(string memory a, string memory b) private pure returns (bool) { | ||
if (bytes(a).length != bytes(b).length) { | ||
return false; | ||
} | ||
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)); | ||
} | ||
|
||
function _upgradeContract(string memory newVersion) private { | ||
AppInfo storage appInfo = appInfos[newVersion]; | ||
|
||
if (appInfo.implementation != address(0) && !appInfo.consumed) { | ||
appInfo.consumed = true; | ||
_doUpgradeContract(appInfo.implementation, appInfo.initialCalldata); | ||
delete appInfo.initialCalldata; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.20; | ||
|
||
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; | ||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
import {IBCContractUpgradableModuleBase} from "./IBCContractUpgradableModule.sol"; | ||
|
||
import {IBCMockApp} from "@hyperledger-labs/yui-ibc-solidity/contracts/apps/mock/IBCMockApp.sol"; | ||
import {IBCAppBase} from "@hyperledger-labs/yui-ibc-solidity/contracts/apps/commons/IBCAppBase.sol"; | ||
import {IBCChannelUpgradableModuleBase} from "@hyperledger-labs/yui-ibc-solidity/contracts/apps/commons/IBCChannelUpgradableModule.sol"; | ||
import {IIBCHandler} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/25-handler/IIBCHandler.sol"; | ||
|
||
contract IBCContractUpgradableUUPSMockApp is | ||
UUPSUpgradeable, | ||
IBCMockApp, | ||
IBCContractUpgradableModuleBase | ||
{ | ||
address immutable _self; // implementation's address, which is set by the constructor | ||
address immutable _deployer; // contract deployer, which is set by the constructor | ||
constructor(IIBCHandler ibcHandler_) IBCMockApp(ibcHandler_) { | ||
_self = address(this); | ||
_deployer = msg.sender; | ||
} | ||
|
||
// ------------------- Functions ------------------- // | ||
|
||
function self() external view returns(address) { | ||
return _self; | ||
} | ||
|
||
/** | ||
* @dev See {Ownable-owner}. | ||
*/ | ||
function owner() public view virtual override(Ownable) returns (address) { | ||
return _deployer; | ||
} | ||
|
||
/** | ||
* @dev See {IERC165-supportsInterface}. | ||
*/ | ||
function supportsInterface(bytes4 interfaceId) | ||
public | ||
view | ||
virtual | ||
override(IBCAppBase, IBCContractUpgradableModuleBase) | ||
returns (bool) | ||
{ | ||
return super.supportsInterface(interfaceId) || | ||
interfaceId == type(UUPSUpgradeable).interfaceId; | ||
} | ||
|
||
// ------------------- Internal Functions ------------------- // | ||
|
||
function __IBCContractUpgradableUUPSMockApp_init(string memory initialVersion) internal initializer { | ||
__UUPSUpgradeable_init(); | ||
|
||
AppInfo storage appInfo = appInfos[initialVersion]; | ||
appInfo.implementation = _self; | ||
appInfo.consumed = true; | ||
} | ||
|
||
/** | ||
* @dev See{IBCChannelUpgradableModuleBase-_isAuthorizedUpgrader} | ||
*/ | ||
function _isAuthorizedUpgrader(string calldata, string calldata, address msgSender) | ||
internal | ||
view | ||
virtual | ||
override(IBCChannelUpgradableModuleBase) | ||
returns (bool) | ||
{ | ||
return _isContractUpgrader(msgSender); | ||
} | ||
|
||
/** | ||
* @dev See{IBCContractUpgradableModuleBase-_isContractUpgrader} | ||
*/ | ||
function _isContractUpgrader(address msgSender) | ||
internal | ||
view | ||
virtual | ||
override(IBCContractUpgradableModuleBase) | ||
returns (bool) | ||
{ | ||
return msgSender == owner() || msgSender == address(this); | ||
} | ||
|
||
/** | ||
* @dev See{IBCContractUpgradableModuleBase-_upgradeContract} | ||
*/ | ||
function _doUpgradeContract(address implementation, bytes memory initialCalldata) | ||
internal | ||
virtual | ||
override(IBCContractUpgradableModuleBase) | ||
{ | ||
// if there is no implementation update, nothing happens here | ||
if (ERC1967Utils.getImplementation() != implementation) { | ||
ERC1967Utils.upgradeToAndCall(implementation, initialCalldata); | ||
} | ||
} | ||
|
||
/** | ||
* @dev See {UUPSupgradeable-_authorizeupgrade} | ||
*/ | ||
function _authorizeUpgrade(address msgSender) internal view virtual override(UUPSUpgradeable) { | ||
if (!_isContractUpgrader(msgSender)) { | ||
revert IBCContractUpgradableModuleUnauthorizedUpgrader(msgSender); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.20; | ||
|
||
interface IIBCContractUpgradableModuleErrors { | ||
// ------------------- Errors ------------------- // | ||
|
||
error IBCContractUpgradableModuleUnauthorizedUpgrader(address msgSender); | ||
error IBCContractUpgradableModuleAppInfoProposedWithZeroImpl(); | ||
error IBCContractUpgradableModuleAppInfoNotProposedYet(); | ||
error IBCContractUpgradableModuleAppInfoIsAlreadySet(); | ||
error IBCContractUpgradableModuleChannelNotFound(string portId, string channelId); | ||
error IBCContractUpgradableModuleAlreadyConsumedAppInfo(); | ||
} | ||
|
||
interface IIBCContractUpgradableModule { | ||
// ------------------- Data Structures ------------------- // | ||
|
||
/** | ||
* @dev Proposed AppInfo data | ||
* @param implemantation the new implementation address | ||
* @param initialCalldata the first function call's calldata | ||
* @param consumed the flag that specifies if this implementation is already deployed | ||
*/ | ||
struct AppInfo { | ||
address implementation; | ||
bytes initialCalldata; | ||
bool consumed; | ||
} | ||
|
||
// ------------------- Functions ------------------- // | ||
|
||
/** | ||
* @dev Returns the proposed AppInfo for the given version | ||
*/ | ||
function getAppInfoProposal(string calldata version) external view returns (AppInfo memory); | ||
|
||
/** | ||
* @dev Propose an Appinfo for the given version | ||
* @notice This function is only callable by an authorized upgrader. | ||
* To upgrade the IBC module contract along with a channel upgrade, the upgrader must | ||
* call this function before calling `channelUpgradeInit` or `channelUpgradeTry` of the IBC handler. | ||
*/ | ||
function proposeAppVersion(string calldata version, AppInfo calldata appInfo) external; | ||
|
||
} | ||
|
File renamed without changes.
Oops, something went wrong.