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

docs: current wire formats #521

Merged
merged 1 commit into from
Sep 21, 2024

Conversation

evan-gray
Copy link
Contributor

This PR explicitly documents the wire format spec implemented across the EVM, SVM, and TS code.

EVM

NttManager

/// @dev Message emitted and received by the nttManager contract.
/// The wire format is as follows:
/// - id - 32 bytes
/// - sender - 32 bytes
/// - payloadLength - 2 bytes
/// - payload - `payloadLength` bytes
struct NttManagerMessage {
/// @notice unique message identifier
/// @dev This is incrementally assigned on EVM chains, but this is not
/// guaranteed on other runtimes.
bytes32 id;
/// @notice original message sender address.
bytes32 sender;
/// @notice payload that corresponds to the type.
bytes payload;
}

/// @dev Prefix for all NativeTokenTransfer payloads
/// This is 0x99'N''T''T'
bytes4 constant NTT_PREFIX = 0x994E5454;

/// @dev Native Token Transfer payload.
/// The wire format is as follows:
/// - NTT_PREFIX - 4 bytes
/// - numDecimals - 1 byte
/// - amount - 8 bytes
/// - sourceToken - 32 bytes
/// - to - 32 bytes
/// - toChain - 2 bytes
struct NativeTokenTransfer {
/// @notice Amount being transferred (big-endian u64 and u8 for decimals)
TrimmedAmount amount;
/// @notice Source chain token address.
bytes32 sourceToken;
/// @notice Address of the recipient.
bytes32 to;
/// @notice Chain ID of the recipient
uint16 toChain;
}

Transceiver

/// @dev Message emitted by Transceiver implementations.
/// Each message includes an Transceiver-specified 4-byte prefix.
/// The wire format is as follows:
/// - prefix - 4 bytes
/// - sourceNttManagerAddress - 32 bytes
/// - recipientNttManagerAddress - 32 bytes
/// - nttManagerPayloadLength - 2 bytes
/// - nttManagerPayload - `nttManagerPayloadLength` bytes
/// - transceiverPayloadLength - 2 bytes
/// - transceiverPayload - `transceiverPayloadLength` bytes
struct TransceiverMessage {
/// @notice Address of the NttManager contract that emitted this message.
bytes32 sourceNttManagerAddress;
/// @notice Address of the NttManager contract that receives this message.
bytes32 recipientNttManagerAddress;
/// @notice Payload provided to the Transceiver contract by the NttManager contract.
bytes nttManagerPayload;
/// @notice Optional payload that the transceiver can encode and use for its own message passing purposes.
bytes transceiverPayload;
}

Wormhole Transceiver

/// @dev Prefix for all TransceiverMessage payloads
/// This is 0x99'E''W''H'
/// @notice Magic string (constant value set by messaging provider) that idenfies the payload as an transceiver-emitted payload.
/// Note that this is not a security critical field. It's meant to be used by messaging providers to identify which messages are Transceiver-related.
bytes4 constant WH_TRANSCEIVER_PAYLOAD_PREFIX = 0x9945FF10;
/// @dev Prefix for all Wormhole transceiver initialisation payloads
/// This is bytes4(keccak256("WormholeTransceiverInit"))
bytes4 constant WH_TRANSCEIVER_INIT_PREFIX = 0x9c23bd3b;
/// @dev Prefix for all Wormhole peer registration payloads
/// This is bytes4(keccak256("WormholePeerRegistration"))
bytes4 constant WH_PEER_REGISTRATION_PREFIX = 0x18fc67c2;

function encodeTransceiverInit(
TransceiverInit memory init
) public pure returns (bytes memory) {
return abi.encodePacked(
init.transceiverIdentifier,
init.nttManagerAddress,
init.nttManagerMode,
init.tokenAddress,
init.tokenDecimals
);
}

function encodeTransceiverRegistration(
TransceiverRegistration memory registration
) public pure returns (bytes memory) {
return abi.encodePacked(
registration.transceiverIdentifier,
registration.transceiverChainId,
registration.transceiverAddress
);
}

SVM

NttManager

id.write(writer)?;
writer.write_all(sender)?;
let len: u16 = u16::try_from(payload.written_size()).expect("u16 overflow");
len.write(writer)?;

const PREFIX: [u8; 4] = [0x99, 0x4E, 0x54, 0x54];

Self::PREFIX.write(writer)?;
amount.write(writer)?;
source_token.write(writer)?;
to.write(writer)?;
to_chain.write(writer)?;

Transceiver

E::PREFIX.write(writer)?;
source_ntt_manager.write(writer)?;
recipient_ntt_manager.write(writer)?;
let len: u16 = u16::try_from(ntt_manager_payload.written_size()).expect("u16 overflow");
len.write(writer)?;
// TODO: review this in wormhole-io. The written_size logic is error prone. Instead,
// a better API would be
// foo.write_with_prefix_be::<u16>(writer)
// which writes the length as a big endian u16.
ntt_manager_payload.write(writer)?;
let len: u16 = u16::try_from(transceiver_payload.len()).expect("u16 overflow");
len.write(writer)?;
writer.write_all(transceiver_payload)?;

Wormhole Transceiver

impl Transceiver for WormholeTransceiver {
const PREFIX: [u8; 4] = [0x99, 0x45, 0xFF, 0x10];
}
impl WormholeTransceiver {
pub const INFO_PREFIX: [u8; 4] = [0x9c, 0x23, 0xbd, 0x3b];
pub const PEER_INFO_PREFIX: [u8; 4] = [0x18, 0xfc, 0x67, 0xc2];
}

impl Transceiver for WormholeTransceiver {
const PREFIX: [u8; 4] = [0x99, 0x45, 0xFF, 0x10];
}
impl WormholeTransceiver {
pub const INFO_PREFIX: [u8; 4] = [0x9c, 0x23, 0xbd, 0x3b];
pub const PEER_INFO_PREFIX: [u8; 4] = [0x18, 0xfc, 0x67, 0xc2];
}

WormholeTransceiver::PEER_INFO_PREFIX.write(writer)?;
self.chain_id.write(writer)?;
self.transceiver_address.write(writer)

TS

NttManager

export const nttManagerMessageLayout = <
const P extends CustomizableBytes = undefined
>(
customPayload?: P
) =>
[
{ name: "id", binary: "bytes", size: 32 },
{ name: "sender", ...layoutItems.universalAddressItem },
customizableBytes({ name: "payload", lengthSize: 2 }, customPayload),
] as const satisfies Layout;

export const nativeTokenTransferLayout = [
prefixItem([0x99, 0x4e, 0x54, 0x54]),
{ name: "trimmedAmount", ...trimmedAmountItem },
{ name: "sourceToken", ...layoutItems.universalAddressItem },
{ name: "recipientAddress", ...layoutItems.universalAddressItem },
{ name: "recipientChain", ...layoutItems.chainItem() },
] as const satisfies Layout;

Transceiver

export const transceiverMessageLayout = <
const MP extends CustomizableBytes = undefined,
const TP extends CustomizableBytes = undefined
>(
prefix: Prefix,
nttManagerPayload?: MP,
transceiverPayload?: TP
) =>
[
prefixItem(prefix),
{ name: "sourceNttManager", ...layoutItems.universalAddressItem },
{ name: "recipientNttManager", ...layoutItems.universalAddressItem },
customizableBytes(
{ name: "nttManagerPayload", lengthSize: 2 },
nttManagerPayload
),
customizableBytes(
{ name: "transceiverPayload", lengthSize: 2 },
transceiverPayload
),
] as const satisfies Layout;

Wormhole Transceiver

export const wormholeTransceiverMessageLayout = <
MP extends CustomizableBytes = undefined
>(
nttManagerPayload?: MP
) =>
transceiverMessageLayout(
[0x99, 0x45, 0xff, 0x10],
nttManagerPayload,
new Uint8Array(0)
);

export const transceiverInfo = [
prefixItem([0x9c, 0x23, 0xbd, 0x3b]),
{ name: "managerAddress", ...layoutItems.universalAddressItem },
{ name: "mode", binary: "uint", size: 1 },
{ name: "token", ...layoutItems.universalAddressItem },
{ name: "decimals", binary: "uint", size: 1 },
] as const satisfies Layout;

export type TransceiverRegistration = LayoutToType<
typeof transceiverRegistration
>;
export const transceiverRegistration = [
prefixItem([0x18, 0xfc, 0x67, 0xc2]),
{ name: "chain", ...layoutItems.chainItem() },
{ name: "transceiver", ...layoutItems.universalAddressItem },
] as const satisfies Layout;

nvsriram
nvsriram previously approved these changes Sep 20, 2024
bruce-riley
bruce-riley previously approved these changes Sep 21, 2024
Copy link

@bruce-riley bruce-riley left a comment

Choose a reason for hiding this comment

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

Looks good to me, just a couple of nits.

README.md Outdated Show resolved Hide resolved
docs/NttManager.md Outdated Show resolved Hide resolved
docs/Transceiver.md Outdated Show resolved Hide resolved
@evan-gray evan-gray merged commit 00f83aa into wormhole-foundation:main Sep 21, 2024
9 checks passed
@evan-gray evan-gray deleted the spec branch September 21, 2024 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants