From c7f7df2117b01c47b321a5d6650ed7c1077bd9a7 Mon Sep 17 00:00:00 2001 From: RnkSngh Date: Thu, 21 Mar 2024 02:25:52 -0400 Subject: [PATCH] add UCH logging + new to/from UCHPacket encoding --- contracts/base/GeneralMiddleware.sol | 3 ++ contracts/core/UniversalChannelHandler.sol | 3 ++ contracts/libs/Ibc.sol | 34 ++++++++++++++-------- test/Ibc.t.sol | 29 ++++++++++++++++++ test/universal.channel.t.sol | 10 +++++++ 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/contracts/base/GeneralMiddleware.sol b/contracts/base/GeneralMiddleware.sol index ecdcc74f..c7a72bf0 100644 --- a/contracts/base/GeneralMiddleware.sol +++ b/contracts/base/GeneralMiddleware.sol @@ -24,9 +24,11 @@ contract GeneralMiddleware is IbcMwUser, IbcMiddleware, IbcMwEventsEmitter { */ uint256 public MW_ID; + event UCHPacketSent(address source, bytes32 destination); /** * @param _middleware The middleware contract address this contract sends packets to and receives packets from. */ + constructor(uint256 mwId, address _middleware) IbcMwUser(_middleware) { MW_ID = mwId; } @@ -37,6 +39,7 @@ contract GeneralMiddleware is IbcMwUser, IbcMiddleware, IbcMwEventsEmitter { bytes calldata appData, uint64 timeoutTimestamp ) external override { + emit UCHPacketSent(msg.sender, destPortAddr); _sendPacket(channelId, IbcUtils.toBytes32(msg.sender), destPortAddr, 0, appData, timeoutTimestamp); } diff --git a/contracts/core/UniversalChannelHandler.sol b/contracts/core/UniversalChannelHandler.sol index 2ab9b6b5..55ff58db 100644 --- a/contracts/core/UniversalChannelHandler.sol +++ b/contracts/core/UniversalChannelHandler.sol @@ -22,6 +22,8 @@ contract UniversalChannelHandler is IbcReceiverBase, IbcUniversalChannelMW { // Key: middleware bitmap, Value: middleware address from receiver(chain B)'s perspective mapping(uint256 => address[]) public mwStackAddrs; + event UCHPacketSent(address source, bytes32 destination); + constructor(IbcDispatcher _dispatcher) IbcReceiverBase(_dispatcher) {} /** * @dev Close a universal channel. @@ -55,6 +57,7 @@ contract UniversalChannelHandler is IbcReceiverBase, IbcUniversalChannelMW { bytes memory packetData = IbcUtils.toUniversalPacketBytes( UniversalPacket(IbcUtils.toBytes32(msg.sender), MW_ID, destPortAddr, appData) ); + emit UCHPacketSent(msg.sender, destPortAddr); dispatcher.sendPacket(channelId, packetData, timeoutTimestamp); } diff --git a/contracts/libs/Ibc.sol b/contracts/libs/Ibc.sol index fcffda3b..f04e5b34 100644 --- a/contracts/libs/Ibc.sol +++ b/contracts/libs/Ibc.sol @@ -170,6 +170,27 @@ library IBCErrors { library IbcUtils { error StringTooLong(); + // fromUniversalPacketBytes converts UniversalPacketDataBytes to UniversalPacketData, per how its packed into bytes + function fromUniversalPacketBytes(bytes calldata data) + external + pure + returns (UniversalPacket memory universalPacketData) + { + bytes32 srcPortAddr; + uint256 mwBitmap; + bytes32 destPortAddr; + assembly { + // Keep reusing 0x0 to move from calldata to return vars + calldatacopy(0x0, data.offset, 32) + srcPortAddr := mload(0x0) + calldatacopy(0x0, add(data.offset, 32), 32) + mwBitmap := mload(0x0) + calldatacopy(0x0, add(data.offset, 64), 32) + destPortAddr := mload(0x0) + } + universalPacketData = UniversalPacket(srcPortAddr, uint256(mwBitmap), destPortAddr, data[96:data.length]); + } + /** * 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. @@ -218,19 +239,8 @@ library IbcUtils { } } - // convert params to UniversalPacketBytes with optimal gas cost - function toUniversalPacketBytes(UniversalPacket memory data) internal pure returns (bytes memory packetBytes) { - packetBytes = abi.encode(data); - } - - // fromUniversalPacketBytes converts UniversalPacketDataBytes to UniversalPacketData, per how its packed into bytes - function fromUniversalPacketBytes(bytes memory data) - internal - pure - returns (UniversalPacket memory universalPacketData) - { - universalPacketData = abi.decode(data, (UniversalPacket)); + packetBytes = bytes.concat(data.srcPortAddr, bytes32(data.mwBitmap), data.destPortAddr, data.appData); } // addressToPortId converts an address to a port ID diff --git a/test/Ibc.t.sol b/test/Ibc.t.sol index e97929da..af494b96 100644 --- a/test/Ibc.t.sol +++ b/test/Ibc.t.sol @@ -57,4 +57,33 @@ contract IbcTest is Test { assertFalse(parsederr.success); assertEq(bytes("this is an error message"), parsederr.data); } + + function assert_Equal_Before_and_After_Encoding(address a1, uint256 mwBitmap, address a2, bytes memory appData) + internal + { + bytes memory afterEncoding = IbcUtils.toUniversalPacketBytes( + UniversalPacket(IbcUtils.toBytes32(a1), mwBitmap, IbcUtils.toBytes32(a2), appData) + ); + + UniversalPacket memory afterDecoding = IbcUtils.fromUniversalPacketBytes(afterEncoding); + assertEq(a1, IbcUtils.toAddress(afterDecoding.srcPortAddr)); + assertEq(mwBitmap, afterDecoding.mwBitmap); + assertEq(a2, IbcUtils.toAddress(afterDecoding.destPortAddr)); + assertEq(appData, afterDecoding.appData); + } + + function test_To_From_UniversalPacketBytes() public { + // Standard + assert_Equal_Before_and_After_Encoding(vm.addr(1), uint256(123), vm.addr(2), "helloooo decode this for me "); + // empty string + assert_Equal_Before_and_After_Encoding(vm.addr(1), uint256(123), vm.addr(2), ""); // empty string + + // Really long string: + assert_Equal_Before_and_After_Encoding( + vm.addr(1), + uint256(123), + vm.addr(2), + "daf;lkdsajflkasjdv;lkjzdljga;lkgfjda;iocjvz;lkjval;dsjkf;alkdj;zlkjv;lkjaeg;ijafd;lkjzvc,mnb.kahgd;ajkfaj;dgoij;zlckjv;lzkjv;kaldfjg;alkjgf;lkzvjcx;lkvja;lkjg;aslgjdz;adf;kasjg;lkjwaea;lkjg;io1j;4kjrda;lkfjaleot8ywp89yz;dvhlsdkj" + ); + } } diff --git a/test/universal.channel.t.sol b/test/universal.channel.t.sol index 3731b793..bf3b4ed9 100644 --- a/test/universal.channel.t.sol +++ b/test/universal.channel.t.sol @@ -7,6 +7,7 @@ import {IbcEventsEmitter} from "../contracts/interfaces/IbcDispatcher.sol"; import {IbcReceiver} from "../contracts/interfaces/IbcReceiver.sol"; import "../contracts/core/UniversalChannelHandler.sol"; import "../contracts/examples/Mars.sol"; +import "../contracts/interfaces/IbcMiddleware.sol"; import "../contracts/core/OpLightClient.sol"; import "./Dispatcher.base.t.sol"; import "./VirtualChain.sol"; @@ -91,6 +92,8 @@ struct UcPacket { } contract UniversalChannelPacketTest is Base, IbcMwEventsEmitter { + event UCHPacketSent(address source, bytes32 destination); + VirtualChain eth1; VirtualChain eth2; VirtualChainData v1; @@ -239,6 +242,9 @@ contract UniversalChannelPacketTest is Base, IbcMwEventsEmitter { IbcUtils.toBytes32(address(v1.earth)), mwBitmap, IbcUtils.toBytes32(address(v2.earth)), appData ); packetData = IbcUtils.toUniversalPacketBytes(ucPacket); + // Verify event emitted by UCH + vm.expectEmit(true, true, true, true); + emit UCHPacketSent(address(v1.earth), IbcUtils.toBytes32(address(v2.earth))); // iterate over sending middleware contracts to verify each MW has witnessed the packet for (uint256 i = 0; i < senderMws.length; i++) { @@ -258,6 +264,7 @@ contract UniversalChannelPacketTest is Base, IbcMwEventsEmitter { // Verify event emitted by Dispatcher vm.expectEmit(true, true, true, true); emit SendPacket(address(v1.ucHandler), channelId1, packetData, packetSeq, timeout); + v1.earth.greet(address(v2.earth), channelId1, appData, timeout); // simulate relayer calling dispatcherProxy.recvPacket on chain B @@ -325,6 +332,8 @@ contract UniversalChannelPacketTest is Base, IbcMwEventsEmitter { IbcUtils.toBytes32(address(v1.earth)), mwBitmap, IbcUtils.toBytes32(address(v2.earth)), appData ); packetData = IbcUtils.toUniversalPacketBytes(ucPacket); + vm.expectEmit(true, true, true, true); + emit UCHPacketSent(address(v1.earth), IbcUtils.toBytes32(address(v2.earth))); // iterate over sending middleware contracts to verify each MW has witnessed the packet for (uint256 i = 0; i < senderMws.length; i++) { @@ -341,6 +350,7 @@ contract UniversalChannelPacketTest is Base, IbcMwEventsEmitter { ); } } + // Verify event emitted by Dispatcher vm.expectEmit(true, true, true, true); emit SendPacket(address(v1.ucHandler), channelId1, packetData, packetSeq, timeout);