-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: init for vIBC escrow based on Universal escrows
- Loading branch information
Showing
4 changed files
with
243 additions
and
77 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
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,80 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {IncentivizedMessageEscrow} from "../../IncentivizedMessageEscrow.sol"; | ||
import "../../MessagePayload.sol"; | ||
|
||
/// @notice Scaffolding for Polymer Escrows | ||
abstract contract APolymerEscrow is IncentivizedMessageEscrow { | ||
error NotEnoughGasProvidedForVerification(); | ||
error NonVerifiableMessage(); | ||
error NotImplemented(); | ||
|
||
struct VerifiedMessageHashContext { | ||
bytes32 chainIdentifier; | ||
bytes implementationIdentifier; | ||
} | ||
|
||
mapping(bytes32 => VerifiedMessageHashContext) public isVerifiedMessageHash; | ||
|
||
constructor(address sendLostGasTo) | ||
IncentivizedMessageEscrow(sendLostGasTo) | ||
{} | ||
|
||
function estimateAdditionalCost() external pure returns (address asset, uint256 amount) { | ||
asset = address(0); | ||
amount = 0; | ||
} | ||
|
||
function _uniqueSourceIdentifier() internal view override returns (bytes32 sourceIdentifier) { | ||
return sourceIdentifier = bytes32(block.chainid); | ||
} | ||
|
||
function _proofValidPeriod(bytes32 /* destinationIdentifier */ ) internal pure override returns (uint64) { | ||
return 0; | ||
} | ||
|
||
/** @dev Disable processPacket */ | ||
function processPacket( | ||
bytes calldata, /* messagingProtocolContext */ | ||
bytes calldata, /* rawMessage */ | ||
bytes32 /* feeRecipitent */ | ||
) external payable override { | ||
revert NotImplemented(); | ||
} | ||
|
||
/** @dev Disable reemitAckMessage. Polymer manages the entire flow, so we don't need to worry about expired proofs. */ | ||
function reemitAckMessage( | ||
bytes32 /* sourceIdentifier */, | ||
bytes calldata /* implementationIdentifier */, | ||
bytes calldata /* receiveAckWithContext */ | ||
) external payable override { | ||
revert NotImplemented(); | ||
} | ||
|
||
/** @dev Disable timeoutMessage */ | ||
function timeoutMessage( | ||
bytes32 /* sourceIdentifier */, | ||
bytes calldata /* implementationIdentifier */, | ||
uint256 /* originBlockNumber */, | ||
bytes calldata /* message */ | ||
) external payable override { | ||
revert NotImplemented(); | ||
} | ||
|
||
/// @notice This function is used to allow acks to be executed twice (if the first one ran out of gas) | ||
/// This is not intended to allow processPacket to work. | ||
function _verifyPacket(bytes calldata, /* messagingProtocolContext */ bytes calldata _message) | ||
internal | ||
view | ||
override | ||
returns (bytes32 sourceIdentifier, bytes memory implementationIdentifier, bytes calldata message_) | ||
{ | ||
sourceIdentifier = isVerifiedMessageHash[keccak256(_message)].chainIdentifier; | ||
implementationIdentifier = isVerifiedMessageHash[keccak256(_message)].implementationIdentifier; | ||
|
||
if (sourceIdentifier == bytes32(0)) revert NonVerifiableMessage(); | ||
|
||
message_ = _message; | ||
} | ||
} |
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,155 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {APolymerEscrow} from "./APolymerEscrow.sol"; | ||
import "../../MessagePayload.sol"; | ||
|
||
import {AckPacket} from "vibc-core-smart-contracts/libs/Ibc.sol"; | ||
import { | ||
IbcMwUser, | ||
UniversalPacket, | ||
IbcUniversalPacketSender, | ||
IbcUniversalPacketReceiver | ||
} from "vibc-core-smart-contracts/interfaces/IbcMiddleware.sol"; | ||
import "vibc-core-smart-contracts/interfaces/IbcDispatcher.sol"; | ||
import { | ||
IbcReceiverBase, IbcReceiver | ||
} from "vibc-core-smart-contracts/interfaces/IbcReceiver.sol"; | ||
|
||
/// @notice Polymer implementation of the Generalised Incentives based on vIBC. | ||
contract IncentivizedPolymerEscrow is APolymerEscrow, IbcReceiverBase, IbcReceiver { | ||
|
||
bytes32[] public connectedChannels; | ||
string constant VERSION = '1.0'; | ||
|
||
constructor(address sendLostGasTo, address dispatcher) | ||
APolymerEscrow(sendLostGasTo) | ||
IbcReceiverBase(IbcDispatcher(dispatcher)) | ||
{} | ||
|
||
// IBC callback functions | ||
|
||
function onOpenIbcChannel( | ||
string calldata version, | ||
ChannelOrder /* */, | ||
bool, | ||
string[] calldata, | ||
CounterParty calldata counterparty | ||
) external view onlyIbcDispatcher returns (string memory selectedVersion) { | ||
if (counterparty.channelId == bytes32(0)) { | ||
// ChanOpenInit | ||
require( | ||
keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked(VERSION)), | ||
'Unsupported version' | ||
); | ||
} else { | ||
// ChanOpenTry | ||
require( | ||
keccak256(abi.encodePacked(counterparty.version)) == keccak256(abi.encodePacked(VERSION)), | ||
'Unsupported version' | ||
); | ||
} | ||
return VERSION; | ||
} | ||
|
||
function onConnectIbcChannel( | ||
bytes32 channelId, | ||
bytes32, | ||
string calldata counterpartyVersion | ||
) external onlyIbcDispatcher { | ||
require( | ||
keccak256(abi.encodePacked(counterpartyVersion)) == keccak256(abi.encodePacked(VERSION)), | ||
'Unsupported version' | ||
); | ||
connectedChannels.push(channelId); | ||
} | ||
|
||
function onCloseIbcChannel(bytes32 channelId, string calldata, bytes32) external onlyIbcDispatcher { | ||
// logic to determin if the channel should be closed | ||
bool channelFound = false; | ||
for (uint256 i = 0; i < connectedChannels.length; i++) { | ||
if (connectedChannels[i] == channelId) { | ||
delete connectedChannels[i]; | ||
channelFound = true; | ||
break; | ||
} | ||
} | ||
require(channelFound, 'Channel not found'); | ||
} | ||
|
||
// packet.srcPortAddr is the IncentivizedPolymerEscrow address on the source chain. | ||
// packet.destPortAddr is the address of this contract. | ||
// channelId: the universal channel id from the running chain's perspective, which can be used to identify the counterparty chain. | ||
function onRecvPacket(IbcPacket calldata packet) | ||
external override | ||
onlyIbcDispatcher | ||
returns (AckPacket memory) | ||
{ | ||
uint256 gasLimit = gasleft(); | ||
bytes32 feeRecipitent = bytes32(uint256(uint160(tx.origin))); | ||
|
||
bytes memory sourceImplementationIdentifier = bytes.concat(hex""); // TODO packet.src.portId decode from a mapping | ||
|
||
bytes memory receiveAck = | ||
_handleMessage(packet.src.channelId, sourceImplementationIdentifier, packet.data, feeRecipitent, gasLimit); | ||
|
||
// Send ack: | ||
return AckPacket({success: true, data: receiveAck}); | ||
} | ||
|
||
// The escrow manages acks, so any message can be directly provided to _handleAck. | ||
function onAcknowledgementPacket( | ||
IbcPacket calldata packet, | ||
AckPacket calldata ack | ||
) | ||
external override | ||
onlyIbcDispatcher | ||
{ | ||
uint256 gasLimit = gasleft(); | ||
bytes32 feeRecipitent = bytes32(uint256(uint160(tx.origin))); | ||
|
||
bytes calldata rawMessage = ack.data; | ||
bytes memory destinationImplementationIdentifier = bytes.concat(hex""); // TODO packet.dest.portId , we need to map from channelId. | ||
|
||
isVerifiedMessageHash[keccak256(rawMessage)] = VerifiedMessageHashContext({ | ||
chainIdentifier: packet.src.channelId, | ||
implementationIdentifier: destinationImplementationIdentifier | ||
}); | ||
_handleAck(packet.src.channelId, destinationImplementationIdentifier, rawMessage, feeRecipitent, gasLimit); | ||
} | ||
|
||
// For timeouts, we need to construct the message. | ||
function onTimeoutPacket(IbcPacket calldata packet) external override onlyIbcDispatcher{ | ||
uint256 gasLimit = gasleft(); | ||
bytes32 feeRecipitent = bytes32(uint256(uint160(tx.origin))); | ||
|
||
bytes calldata rawMessage = packet.data; | ||
bytes32 messageIdentifier = bytes32(rawMessage[MESSAGE_IDENTIFIER_START:MESSAGE_IDENTIFIER_END]); | ||
address fromApplication = address(uint160(bytes20(rawMessage[FROM_APPLICATION_START_EVM:FROM_APPLICATION_END]))); | ||
_handleTimeout( | ||
packet.src.channelId, messageIdentifier, fromApplication, rawMessage[CTX0_MESSAGE_START:], feeRecipitent, gasLimit | ||
); | ||
} | ||
|
||
/** | ||
* @param destinationChainIdentifier Universal Channel ID. It's always from the running chain's perspective. | ||
* Each universal channel/channelId represents a directional path from the running chain to a destination chain. | ||
* Universal ChannelIds should _destChainIdToChannelIdd from the Polymer registry. | ||
* Although everyone is free to establish their own channels, they're not "officially" vetted until they're in the Polymer registry. | ||
* @param message packet payload | ||
* @param deadline Packet will timeout after the dest chain's block time in nanoseconds since the epoch passes timeoutTimestamp. | ||
*/ | ||
function _sendPacket( | ||
bytes32 destinationChainIdentifier, | ||
bytes memory /* destinationImplementation */, | ||
bytes memory message, | ||
uint64 deadline | ||
) internal override returns (uint128 costOfsendPacketInNativeToken) { | ||
// If timeoutTimestamp is set to 0, set it to maximum. This does not really apply to Polymer since it is an onRecv implementation | ||
// but it should still conform to the general spec of Generalised Incentives. | ||
uint64 timeoutTimestamp = deadline > 0 ? deadline : type(uint64).max; | ||
|
||
dispatcher.sendPacket(destinationChainIdentifier, message, timeoutTimestamp); | ||
return 0; | ||
} | ||
} |