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

Ack & timeout relay msgs #1810

Merged
merged 2 commits into from
Sep 26, 2024
Merged
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
263 changes: 187 additions & 76 deletions packages/ui/components/ui/tx/actions-views/ibc-relay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,93 +4,104 @@ import {
FungibleTokenPacketData,
IbcRelay,
} from '@penumbra-zone/protobuf/penumbra/core/component/ibc/v1/ibc_pb';
import { MsgRecvPacket } from '@penumbra-zone/protobuf/ibc/core/channel/v1/tx_pb';
import {
MsgAcknowledgement,
MsgRecvPacket,
MsgTimeout,
MsgTimeoutOnClose,
} from '@penumbra-zone/protobuf/ibc/core/channel/v1/tx_pb';
import { MsgUpdateClient } from '@penumbra-zone/protobuf/ibc/core/client/v1/tx_pb';
import { UnimplementedView } from './unimplemented-view.tsx';
import { uint8ArrayToBase64 } from '@penumbra-zone/types/base64';
import { ReactElement } from 'react';
import { Packet } from '@penumbra-zone/protobuf/ibc/core/channel/v1/channel_pb';
import { getUtcTime } from './isc20-withdrawal.tsx';
import { useMemo } from 'react';
import { CircleX } from 'lucide-react';

// Packet data stored as json string encoded into bytes
const parsePacket = ({ packet }: MsgRecvPacket): FungibleTokenPacketData | undefined => {
if (!packet?.data) {
// Attempt to parse data w/ fallbacks if unknown or failures
const ParsedPacketData = ({
data,
dataParser,
}: {
data: Uint8Array;
dataParser?: (arg: Uint8Array) => ReactElement;
}) => {
if (!data.length) {
return undefined;
}

if (!dataParser) {
return <ActionDetails.Row label='Data'>Unknown packet data</ActionDetails.Row>;
}

try {
const dataString = new TextDecoder().decode(packet.data);
return FungibleTokenPacketData.fromJsonString(dataString);
return dataParser(data);
} catch (e) {
return undefined;
return <ActionDetails.Row label='Data'>Error while parsing data</ActionDetails.Row>;
}
};

const MsgResvComponent = ({ packet }: { packet: MsgRecvPacket }) => {
const packetData = useMemo(() => parsePacket(packet), [packet]);
const PacketRows = ({
packet,
dataParser,
}: {
packet: Packet;
dataParser?: (arg: Uint8Array) => ReactElement;
}) => {
Comment on lines +44 to +50
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a number of messages need to display Packet data, abstracted it here

return (
<>
<ParsedPacketData data={packet.data} dataParser={dataParser} />
<ActionDetails.Row label='Sequence'>{Number(packet.sequence)}</ActionDetails.Row>
<ActionDetails.Row label='Source Port'>{packet.sourcePort}</ActionDetails.Row>
<ActionDetails.Row label='Source Channel'>{packet.sourceChannel}</ActionDetails.Row>
<ActionDetails.Row label='Destination Port'>{packet.destinationPort}</ActionDetails.Row>
<ActionDetails.Row label='Destination Channel'>{packet.destinationChannel}</ActionDetails.Row>
{!!packet.timeoutHeight?.revisionHeight && (
<ActionDetails.Row label='Timeout revision height'>
{Number(packet.timeoutHeight.revisionHeight)}
</ActionDetails.Row>
)}
{!!packet.timeoutHeight?.revisionNumber && (
<ActionDetails.Row label='Timeout revision number'>
{Number(packet.timeoutHeight.revisionNumber)}
</ActionDetails.Row>
)}
<ActionDetails.Row label='Timeout timestamp'>
{getUtcTime(packet.timeoutTimestamp)}
</ActionDetails.Row>
</>
);
};

// Packet data stored as json string encoded into bytes
const parseFungibleTokenData = (packetData: Uint8Array) => {
const dataString = new TextDecoder().decode(packetData);
const parsed = FungibleTokenPacketData.fromJsonString(dataString);
return (
<>
<ActionDetails.Row label='Sender'>
<ActionDetails.TruncatedText>{parsed.sender}</ActionDetails.TruncatedText>
</ActionDetails.Row>
<ActionDetails.Row label='Receiver'>
<ActionDetails.TruncatedText>{parsed.receiver}</ActionDetails.TruncatedText>
</ActionDetails.Row>
<ActionDetails.Row label='Denom'>{parsed.denom}</ActionDetails.Row>
<ActionDetails.Row label='Amount'>{parsed.amount}</ActionDetails.Row>
<ActionDetails.Row label='Memo'>{parsed.memo}</ActionDetails.Row>
</>
);
};

const MsgResvComponent = ({ packet }: { packet: MsgRecvPacket }) => {
return (
<ViewBox
label='IBC Relay: Msg Received'
visibleContent={
<ActionDetails>
{packetData === undefined && (
<ActionDetails.Row label='Data'>Unknown packet data</ActionDetails.Row>
)}
{!!packetData?.sender && (
<ActionDetails.Row label='Sender'>
<ActionDetails.TruncatedText>{packetData.sender}</ActionDetails.TruncatedText>
</ActionDetails.Row>
)}
{!!packetData?.receiver && (
<ActionDetails.Row label='Receiver'>
<ActionDetails.TruncatedText>{packetData.receiver}</ActionDetails.TruncatedText>
</ActionDetails.Row>
)}
{!!packetData?.denom && (
<ActionDetails.Row label='Denom'>{packetData.denom}</ActionDetails.Row>
)}
{!!packetData?.amount && (
<ActionDetails.Row label='Amount'>{packetData.amount}</ActionDetails.Row>
)}
{packetData && 'memo' in packetData && (
<ActionDetails.Row label='Memo'>{packetData.memo}</ActionDetails.Row>
)}
{!!packet.packet?.sequence && (
<ActionDetails.Row label='Sequence'>{Number(packet.packet.sequence)}</ActionDetails.Row>
)}
{!!packet.packet?.sourcePort && (
<ActionDetails.Row label='Source Port'>{packet.packet.sourcePort}</ActionDetails.Row>
)}
{!!packet.packet?.sourceChannel && (
<ActionDetails.Row label='Source Channel'>
{packet.packet.sourceChannel}
</ActionDetails.Row>
)}
{!!packet.packet?.destinationPort && (
<ActionDetails.Row label='Destination Port'>
{packet.packet.destinationPort}
</ActionDetails.Row>
)}
{!!packet.packet?.destinationChannel && (
<ActionDetails.Row label='Destination Channel'>
{packet.packet.destinationChannel}
</ActionDetails.Row>
)}
{!!packet.packet?.timeoutHeight?.revisionHeight && (
<ActionDetails.Row label='Timeout revision height'>
{Number(packet.packet.timeoutHeight.revisionHeight)}
</ActionDetails.Row>
)}
{!!packet.packet?.timeoutHeight?.revisionNumber && (
<ActionDetails.Row label='Timeout revision number'>
{Number(packet.packet.timeoutHeight.revisionNumber)}
</ActionDetails.Row>
)}
{!!packet.packet?.timeoutTimestamp && (
<ActionDetails.Row label='Timeout timestamp'>
{getUtcTime(packet.packet.timeoutTimestamp)}
</ActionDetails.Row>
{!!packet.packet && (
<PacketRows packet={packet.packet} dataParser={parseFungibleTokenData} />
)}

<ActionDetails.Row label='Signer'>{packet.signer}</ActionDetails.Row>
{!!packet.proofHeight?.revisionHeight && (
<ActionDetails.Row label='Proof revision height'>
Expand Down Expand Up @@ -127,17 +138,117 @@ const UpdateClientComponent = ({ update }: { update: MsgUpdateClient }) => {
);
};

const MsgTimeoutComponent = ({ timeout }: { timeout: MsgTimeout }) => {
return (
<ViewBox
label='IBC Relay: Msg Timeout'
visibleContent={
<ActionDetails>
{!!timeout.packet && (
<PacketRows packet={timeout.packet} dataParser={parseFungibleTokenData} />
)}
{!!timeout.proofHeight?.revisionHeight && (
<ActionDetails.Row label='Proof revision height'>
{Number(timeout.proofHeight.revisionHeight)}
</ActionDetails.Row>
)}
{!!timeout.proofHeight?.revisionNumber && (
<ActionDetails.Row label='Proof revision number'>
{Number(timeout.proofHeight.revisionNumber)}
</ActionDetails.Row>
)}
<ActionDetails.Row label='Next Sequence Received'>
{Number(timeout.nextSequenceRecv)}
</ActionDetails.Row>
<ActionDetails.Row label='Signer'>{timeout.signer}</ActionDetails.Row>
<ActionDetails.Row label='Proof unreceived'>
<ActionDetails.TruncatedText>
{uint8ArrayToBase64(timeout.proofUnreceived)}
</ActionDetails.TruncatedText>
</ActionDetails.Row>
</ActionDetails>
}
/>
);
};

const MsgAckComponent = ({ ack }: { ack: MsgAcknowledgement }) => {
return (
<ViewBox
label='IBC Relay: Msg Acknowledgement'
visibleContent={
<ActionDetails>
{!!ack.packet && <PacketRows packet={ack.packet} dataParser={parseFungibleTokenData} />}
{!!ack.proofHeight?.revisionHeight && (
<ActionDetails.Row label='Proof revision height'>
{Number(ack.proofHeight.revisionHeight)}
</ActionDetails.Row>
)}
{!!ack.proofHeight?.revisionNumber && (
<ActionDetails.Row label='Proof revision number'>
{Number(ack.proofHeight.revisionNumber)}
</ActionDetails.Row>
)}
<ActionDetails.Row label='Signer'>{ack.signer}</ActionDetails.Row>
<ActionDetails.Row label='Ackknowledgement'>
<ActionDetails.TruncatedText>
{uint8ArrayToBase64(ack.acknowledgement)}
</ActionDetails.TruncatedText>
</ActionDetails.Row>
<ActionDetails.Row label='Proof Acked'>
<ActionDetails.TruncatedText>
{uint8ArrayToBase64(ack.proofAcked)}
</ActionDetails.TruncatedText>
</ActionDetails.Row>
</ActionDetails>
}
/>
);
};

export const IbcRelayComponent = ({ value }: { value: IbcRelay }) => {
if (value.rawAction?.is(MsgRecvPacket.typeName)) {
const packet = new MsgRecvPacket();
value.rawAction.unpackTo(packet);
return <MsgResvComponent packet={packet} />;
}
try {
if (value.rawAction?.is(MsgRecvPacket.typeName)) {
const packet = new MsgRecvPacket();
value.rawAction.unpackTo(packet);
return <MsgResvComponent packet={packet} />;
}

if (value.rawAction?.is(MsgUpdateClient.typeName)) {
const update = new MsgUpdateClient();
value.rawAction.unpackTo(update);
return <UpdateClientComponent update={update} />;
}

if (value.rawAction?.is(MsgTimeout.typeName)) {
const timeout = new MsgTimeout();
value.rawAction.unpackTo(timeout);
return <MsgTimeoutComponent timeout={timeout} />;
}

if (value.rawAction?.is(MsgUpdateClient.typeName)) {
const update = new MsgUpdateClient();
value.rawAction.unpackTo(update);
return <UpdateClientComponent update={update} />;
if (value.rawAction?.is(MsgTimeoutOnClose.typeName)) {
const timeout = new MsgTimeoutOnClose();
value.rawAction.unpackTo(timeout);
return <MsgTimeoutComponent timeout={timeout} />;
}

if (value.rawAction?.is(MsgAcknowledgement.typeName)) {
const ack = new MsgAcknowledgement();
value.rawAction.unpackTo(ack);
return <MsgAckComponent ack={ack} />;
}
} catch (e) {
return (
<ViewBox
label='IBC Relay'
visibleContent={
<div className='flex gap-2 text-sm text-red-400'>
<CircleX className='w-4' />
<span className='mt-1'>Error while parsing details</span>
</div>
}
/>
);
}

return <UnimplementedView label='IBC Relay' />;
Expand Down
Loading