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

Raunak/upgradeable dispatcher #56

Merged
merged 21 commits into from
Mar 21, 2024
Merged
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "lib/base64"]
path = lib/base64
url = https://github.com/Brechtpd/base64.git
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable
128 changes: 76 additions & 52 deletions contracts/core/Dispatcher.sol

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
pragma solidity ^0.8.0;

import {L1Header, ProofVerifier, OpL2StateProof, Ics23Proof} from "../interfaces/ProofVerifier.sol";
import {ConsensusStateManager} from "../interfaces/ConsensusStateManager.sol";
import {LightClient} from "../interfaces/LightClient.sol";
import {L1Block} from "optimism/L2/L1Block.sol";

// OptimisticConsensusStateManager manages the appHash at different
// OptimisticLightClient manages the appHash at different
// heights and track the fraud proof end time for them.
contract OptimisticConsensusStateManager is ConsensusStateManager {
contract OptimisticLightClient is LightClient {
// consensusStates maps from the height to the appHash.
mapping(uint256 => uint256) public consensusStates;

Expand Down
111 changes: 111 additions & 0 deletions contracts/interfaces/IDispatcher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IbcDispatcher, IbcEventsEmitter} from "./IbcDispatcher.sol";

import {L1Header, OpL2StateProof, Ics23Proof} from "./ProofVerifier.sol";
import {IbcChannelReceiver, IbcPacketReceiver} from "./IbcReceiver.sol";
import {
Channel,
CounterParty,
ChannelOrder,
IbcPacket,
ChannelState,
AckPacket,
IBCErrors,
IbcUtils,
Ibc
} from "../libs/Ibc.sol";

interface IDispatcher is IbcDispatcher, IbcEventsEmitter {
function setPortPrefix(string calldata _portPrefix) external;

function updateClientWithOptimisticConsensusState(
L1Header calldata l1header,
OpL2StateProof calldata proof,
uint256 height,
uint256 appHash
) external returns (uint256 fraudProofEndTime, bool ended);

/**
* This function is called by a 'relayer' on behalf of a dApp. The dApp should implement IbcChannelHandler's
* onChanOpenInit. If the callback succeeds, the dApp should return the selected version and the emitted event
* will be relayed to the IBC/VIBC hub chain.
*/
function channelOpenInit(
IbcChannelReceiver portAddress,
string calldata version,
ChannelOrder ordering,
bool feeEnabled,
string[] calldata connectionHops,
string calldata counterpartyPortId
) external;

/**
* This function is called by a 'relayer' on behalf of a dApp. The dApp should implement IbcChannelHandler's
* onChanOpenTry. If the callback succeeds, the dApp should return the selected version and the emitted event
* will be relayed to the IBC/VIBC hub chain.
*/
function channelOpenTry(
IbcChannelReceiver portAddress,
CounterParty calldata local,
ChannelOrder ordering,
bool feeEnabled,
string[] calldata connectionHops,
CounterParty calldata counterparty,
Ics23Proof calldata proof
) external;

/**
* This func is called by a 'relayer' after the IBC/VIBC hub chain has processed the ChannelOpenTry event.
* The dApp should implement the onChannelConnect method to handle the third channel handshake method: ChanOpenAck
*/
function channelOpenAck(
IbcChannelReceiver portAddress,
CounterParty calldata local,
string[] calldata connectionHops,
ChannelOrder ordering,
bool feeEnabled,
CounterParty calldata counterparty,
Ics23Proof calldata proof
) external;

/**
* This func is called by a 'relayer' after the IBC/VIBC hub chain has processed the ChannelOpenTry event.
* The dApp should implement the onChannelConnect method to handle the last channel handshake method:
* ChannelOpenConfirm
*/
function channelOpenConfirm(
IbcChannelReceiver portAddress,
CounterParty calldata local,
string[] calldata connectionHops,
ChannelOrder ordering,
bool feeEnabled,
CounterParty calldata counterparty,
Ics23Proof calldata proof
) external;

function closeIbcChannel(bytes32 channelId) external;

function sendPacket(bytes32 channelId, bytes calldata packet, uint64 timeoutTimestamp) external;

function acknowledgement(
IbcPacketReceiver receiver,
IbcPacket calldata packet,
bytes calldata ack,
Ics23Proof calldata proof
) external;

function timeout(IbcPacketReceiver receiver, IbcPacket calldata packet, Ics23Proof calldata proof) external;

function recvPacket(IbcPacketReceiver receiver, IbcPacket calldata packet, Ics23Proof calldata proof) external;

function getOptimisticConsensusState(uint256 height)
external
view
returns (uint256 appHash, uint256 fraudProofEndTime, bool ended);

function portIdAddressMatch(address addr, string calldata portId) external view returns (bool isMatch);

function getChannel(address portAddress, bytes32 channelId) external view returns (Channel memory channel);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;

import {Ics23Proof, L1Header, OpL2StateProof} from "./ProofVerifier.sol";

interface ConsensusStateManager {
interface LightClient {
/**
* addOpConsensusState adds an appHash to internal store and
* returns the fraud proof end time, and a bool flag indicating if
Expand Down
48 changes: 48 additions & 0 deletions contracts/libs/Ibc.sol
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,54 @@ library IBCErrors {
library IbcUtils {
error StringTooLong();

/**
* Convert a non-0x-prefixed hex string to an address
* @param hexStr hex string to convert to address. Note that the hex string must not include a 0x prefix.
* hexStr is case-insensitive.
*/
function hexStrToAddress(string memory hexStr) public pure returns (address addr) {
if (bytes(hexStr).length != 40) {
revert IBCErrors.invalidHexStringLength();
}

bytes memory strBytes = bytes(hexStr);
bytes memory addrBytes = new bytes(20);

for (uint256 i = 0; i < 20; i++) {
uint8 high = uint8(strBytes[i * 2]);
uint8 low = uint8(strBytes[1 + i * 2]);
// Convert to lowercase if the character is in uppercase
if (high >= 65 && high <= 90) {
high += 32;
}
if (low >= 65 && low <= 90) {
low += 32;
}
uint8 digit = (high - (high >= 97 ? 87 : 48)) * 16 + (low - (low >= 97 ? 87 : 48));
addrBytes[i] = bytes1(digit);
}

assembly {
addr := mload(add(addrBytes, 20))
}

return addr;
}

// For XXXX => vIBC direction, SC needs to verify the proof of membership of TRY_PENDING
// For vIBC initiated channel, SC doesn't need to verify any proof, and these should be all empty
function isChannelOpenTry(CounterParty calldata counterparty) public pure returns (bool open) {
if (counterparty.channelId == bytes32(0) && bytes(counterparty.version).length == 0) {
return false;
// ChanOpenInit with unknow conterparty
} else if (counterparty.channelId != bytes32(0) && bytes(counterparty.version).length != 0) {
// this is the ChanOpenTry; counterparty must not be zero-value
return true;
} else {
revert IBCErrors.invalidCounterParty();
}
}

// convert params to UniversalPacketBytes with optimal gas cost

function toUniversalPacketBytes(UniversalPacket memory data) internal pure returns (bytes memory packetBytes) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {ConsensusStateManager} from "../interfaces/ConsensusStateManager.sol";
import {L1Header, OpL2StateProof, Ics23Proof} from "../interfaces/ProofVerifier.sol";
import {LightClient, L1Header, OpL2StateProof, Ics23Proof} from "../interfaces/LightClient.sol";

/**
* @title DummyConsensusStateManager
* @title DummyLightClient
* @dev This contract is a dummy implementation of a consensus state manager.
* It should only be used for testing purposes.
* The logic for checking if the proof length is greater than zero is naive.
*/
contract DummyConsensusStateManager is ConsensusStateManager {
contract DummyLightClient is LightClient {
error InvalidDummyMembershipProof();
error InvalidDummyNonMembershipProof();

Expand Down
7 changes: 7 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,12 @@ fs_permissions = [{ access = 'read', path = './test/payload'}]
[fmt]
wrap_comments = true
number_underscore = "thousands"
ignore = ["lib/*"]

[rpc_endpoints]
sepolia = "${SEPOLIA_RPC_URL}"

[etherscan]
sepolia = { key = "${ETHERSCAN_API_KEY}" }

# See more config options https://book.getfoundry.sh/reference/config.html
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts-upgradeable
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@openzeppelin/=lib/openzeppelin-contracts/
@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/
@lazyledger/protobuf3-solidity-lib/=lib/protobuf3-solidity-lib/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
Expand Down
46 changes: 29 additions & 17 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ pragma solidity ^0.8.0;

import "forge-std/Script.sol";
import "../contracts/utils/DummyProofVerifier.sol";
import "../contracts/utils/DummyConsensusStateManager.sol";
import "../contracts/utils/DummyLightClient.sol";
import "../contracts/core/Dispatcher.sol";
import "../contracts/examples/Mars.sol";
import {IDispatcher} from "../contracts/core/Dispatcher.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "../contracts/core/OpProofVerifier.sol";
import "../contracts/core/OpConsensusStateManager.sol";
import "../contracts/core/OpLightClient.sol";
import "../contracts/core/UniversalChannelHandler.sol";
import "../contracts/examples/Earth.sol";

Expand All @@ -31,7 +33,7 @@ contract Deploy is Script {

console.log("Deploying dummy contracts to %s...", chain);

address stateManager = deployDummyConsensusStateManager();
address stateManager = deployDummyLightClient();
address dispatcher = deployDispatcher(portPrefix, stateManager);
deployMars(dispatcher);

Expand All @@ -44,7 +46,7 @@ contract Deploy is Script {

address l1BlockProvider = vm.envOr("L1_BLOCK_PROVIDER", 0x4200000000000000000000000000000000000015);
uint32 fraudProofWindowSecs = 0;
address opStateManager = deployOpConsensusStateManager(fraudProofWindowSecs, proofVerifierAddr, l1BlockProvider);
address opStateManager = deployOpLightClient(fraudProofWindowSecs, proofVerifierAddr, l1BlockProvider);

dispatcher = deployDispatcher(portPrefix, opStateManager);
deployMars(dispatcher);
Expand All @@ -53,9 +55,9 @@ contract Deploy is Script {
deployEarth(universalChannelHandler);
}

function deployDummyConsensusStateManager() public broadcast returns (address addr_) {
DummyConsensusStateManager manager = new DummyConsensusStateManager{salt: _implSalt()}();
console.log("DummyConsensusStateManager deployed at %s", address(manager));
function deployDummyLightClient() public broadcast returns (address addr_) {
DummyLightClient manager = new DummyLightClient{salt: _implSalt()}();
console.log("DummyLightClient deployed at %s", address(manager));
return address(manager);
}

Expand All @@ -64,9 +66,19 @@ contract Deploy is Script {
broadcast
returns (address addr_)
{
Dispatcher dispatcher = new Dispatcher{salt: _implSalt()}(portPrefix, DummyConsensusStateManager(stateManager_));
console.log("Dispatcher deployed at %s", address(dispatcher));
return address(dispatcher);
Dispatcher impl = new Dispatcher{salt: _implSalt()}();
IDispatcher proxy = IDispatcher(
address(
new ERC1967Proxy{salt: _implSalt()}(
address(impl),
abi.encodeWithSelector(Dispatcher.initialize.selector, portPrefix, DummyLightClient(stateManager_))
)
)
);

console.log("Dispatcher imnplementation at %s", address(impl));
console.log("Dispatcher proxy at %s", address(proxy));
return address(proxy);
}

function deployMars(address dispatcher) public broadcast returns (address addr_) {
Expand All @@ -81,15 +93,15 @@ contract Deploy is Script {
return address(verifier);
}

function deployOpConsensusStateManager(
uint32 fraudProofWindowSecs,
address proofVerifierAddr,
address l1BlockProvider
) public broadcast returns (address addr_) {
OptimisticConsensusStateManager manager = new OptimisticConsensusStateManager{salt: _implSalt()}(
function deployOpLightClient(uint32 fraudProofWindowSecs, address proofVerifierAddr, address l1BlockProvider)
public
broadcast
returns (address addr_)
{
OptimisticLightClient manager = new OptimisticLightClient{salt: _implSalt()}(
fraudProofWindowSecs, ProofVerifier(proofVerifierAddr), L1Block(l1BlockProvider)
);
console.log("OptimisticConsensusStateManager deployed at %s", address(manager));
console.log("OptimisticLightClient deployed at %s", address(manager));
return address(manager);
}

Expand Down
Loading