Skip to content

Commit

Permalink
feat: add axelar command id docs (#1074)
Browse files Browse the repository at this point in the history
  • Loading branch information
Olanetsoft authored Jul 29, 2024
1 parent 37fd754 commit c382cda
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 2 deletions.
11 changes: 9 additions & 2 deletions src/layouts/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ export const getNavigation = (section) => {
title: "Monitor Transaction State",
href: "/dev/general-message-passing/monitoring",
},
{
title: "Axelar CommandID",
href: "/dev/general-message-passing/axelar-commandid",
},
{
title: "Axelar Executable",
href: "/dev/general-message-passing/executable"
href: "/dev/general-message-passing/executable",
},
{
title: "Verify GMP Transaction",
Expand Down Expand Up @@ -454,7 +458,10 @@ export const getNavigation = (section) => {
{
title: "Static configs",
children: [
{ title: "Static configs", href: "/resources/static-configs/static-configs" }
{
title: "Static configs",
href: "/resources/static-configs/static-configs",
},
],
},
{
Expand Down
128 changes: 128 additions & 0 deletions src/pages/dev/general-message-passing/axelar-commandid.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# The Axelar `CommandID`

Ensuring the authenticity and integrity of messages is crucial in cross-chain communication. The `CommandID` in the [Axelar network](https://www.axelar.network/) plays a significant role in this process, acting as a unique identifier for interchain messages.

This guide explains what an Axelar `CommandID` is, how it is generated, and its importance in the [Solidity Axelar GMP SDK](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity).

## What is the Axelar `CommandID`?

Axelar `CommandID` is a unique identifier assigned to each message or command that facilitates interchain communication. It ensures that each message can be tracked, verified, and validated across different blockchain networks, preventing issues such as replay attacks or message tampering.

## How is an Axelar `CommandID` generated?

Generating an Axelar `CommandID` involves combining specific attributes of the message, such as the `sourceChain`, `messageId`, and other relevant data, and then hashing this combination to produce a unique identifier. This process ensures that each `CommandID` is unique for the message it represents.

### Solidity Implementation

In the provided Solidity contract, the `messageToCommandId()` function generates the `CommandID`.

Here's how it works:

```solidity
/**
* @notice Compute the commandId for a message.
* @param sourceChain The name of the source chain as registered on Axelar.
* @param messageId The unique message id for the message.
* @return The commandId for the message.
*/
function messageToCommandId(string calldata sourceChain, string calldata messageId) public pure returns (bytes32) {
// Axelar doesn't allow `sourceChain` to contain '_', hence this encoding is unambiguous
return keccak256(bytes(string.concat(sourceChain, '_', messageId)));
}
```

This function takes two parameters: `sourceChain` and `messageId`. It concatenates these parameters with an underscore (`_`) separator, ensuring the combination is unique and unambiguous. The concatenated string is then hashed using `keccak256` to produce the `CommandID`. You can view the source code [here](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/1db893b373ec9fcd7cf838ebea4b3a7a24585791/contracts/gateway/BaseAmplifierGateway.sol#L103C14-L103C32).

### Go implementation

The `CommandID` is generated similarly by hashing relevant data in the Go implementation.

Here's an example function for generating a `CommandID`:

```go
// NewCommandID generates a new CommandID using the provided data and chain ID
func NewCommandID(data []byte, chainID sdk.Int) []byte {
return crypto.Keccak256(data, chainID.Bytes())
}
```

This function combines the provided data and chain ID, then hashes the combination using `Keccak256()` to produce the `CommandID`. The source code for this can be found [here](https://github.com/axelarnetwork/axelar-core/blob/main/x/evm/types/command.go).

### Example commands

Here are a few examples of command creation in the provided Go code:

- Burn Token:

```go
// NewBurnTokenCommand creates a command to burn tokens with the given burner's information
func NewBurnTokenCommand(chainID sdk.Int, keyID multisig.KeyID, height int64, burnerInfo BurnerInfo, isTokenExternal bool) Command {
heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, uint64(height))

burnTokenMaxGasCost := burnInternalTokenMaxGasCost
if isTokenExternal {
burnTokenMaxGasCost = burnExternalTokenMaxGasCost
}

return Command{
ID: NewCommandID(append(burnerInfo.Salt.Bytes(), heightBytes...), chainID),
Type: COMMAND_TYPE_BURN_TOKEN,
Params: createBurnTokenParams(burnerInfo.Symbol, common.Hash(burnerInfo.Salt)),
KeyID: keyID,
MaxGasCost: uint32(burnTokenMaxGasCost),
}
}
```

- Deploy Token:

```go
// NewDeployTokenCommand creates a command to deploy a token
func NewDeployTokenCommand(chainID sdk.Int, keyID multisig.KeyID, asset string, tokenDetails TokenDetails, address Address, dailyMintLimit sdk.Uint) Command {
return Command{
ID: NewCommandID([]byte(fmt.Sprintf("%s_%s", asset, tokenDetails.Symbol)), chainID),
Type: COMMAND_TYPE_DEPLOY_TOKEN,
Params: createDeployTokenParams(tokenDetails.TokenName, tokenDetails.Symbol, tokenDetails.Decimals, tokenDetails.Capacity, address, dailyMintLimit),
KeyID: keyID,
MaxGasCost: deployTokenMaxGasCost,
}
}
```

## Importance of the `CommandID` for message verification

The `CommandID` is key for verifying the authenticity and integrity of interchain messages. When a message is received on the destination chain, the `CommandID` allows the system to:

1. **Verify Message Approval:**
The `isMessageApproved()` function checks if a message has been approved by comparing the stored message hash with the calculated hash using the `CommandID`.

```solidity
function isMessageApproved(
string calldata sourceChain,
string calldata messageId,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash
) external view override returns (bool) {
bytes32 commandId = messageToCommandId(sourceChain, messageId);
return _isMessageApproved(commandId, sourceChain, sourceAddress, contractAddress, payloadHash);
}
```

2. **Prevent Replay Attacks:**
The `validateMessage()` function ensures that a message can only be executed once by marking it as executed after validation.

```solidity
function validateMessage(
string calldata sourceChain,
string calldata messageId,
string calldata sourceAddress,
bytes32 payloadHash
) external override returns (bool valid) {
bytes32 commandId = messageToCommandId(sourceChain, messageId);
valid = _validateMessage(commandId, sourceChain, sourceAddress, payloadHash);
}
```

Refer to more detailed information on how Axelar verifies GMP transactions [here](https://docs.axelar.dev/dev/general-message-passing/verify-gmp-tx).

0 comments on commit c382cda

Please sign in to comment.