Skip to content

Commit

Permalink
Merge pull request #16 from wormhole-foundation/v1.1-update
Browse files Browse the repository at this point in the history
update improvements
  • Loading branch information
barnjamin authored Nov 8, 2023
2 parents 06bd1e5 + f3dbb4e commit d3fcaaf
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 163 deletions.
50 changes: 2 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,17 +228,13 @@ interface IWormholeReceiver {
*
* NOTE: This function should be restricted such that only the Wormhole Relayer contract can call it.
*
* We also recommend that this function:
* - Stores all received `deliveryHash`s in a mapping `(bytes32 => bool)`, and
* on every call, checks that deliveryHash has not already been stored in the
* map (This is to prevent other users maliciously trying to relay the same message)
* - Checks that `sourceChain` and `sourceAddress` are indeed who
* We also recommend that this function checks that `sourceChain` and `sourceAddress` are indeed who
* you expect to have requested the calling of `send` on the source chain
*
* The invocation of this function corresponding to the `send` request will have msg.value equal
* to the receiverValue specified in the send request.
*
* If the invocation of this function reverts or exceeds the gas limit
* If the invocation of this function reverts or exceeds the gas limit
* specified by the send requester, this delivery will result in a `ReceiverFailure`.
*
* @param payload - an arbitrary message which was included in the delivery by the
Expand Down Expand Up @@ -276,48 +272,6 @@ So, in receiveWormholeMessages, we want to:
To provide certainty about the validity of the payload, we must restrict the msg.sender of this function to only be the Wormhole Relayer contract. Otherwise, anyone could call this receiveWormholeMessages endpoint with fake greetings, source chains, and source senders.

### One more step

You should store each delivery hash in a mapping from delivery hashes to booleans, to prevent duplicate processing of deliveries! This also gives you a way of tracking the completion of sent deliveries

```solidity
event GreetingReceived(string greeting, uint16 senderChain, address sender);
string public latestGreeting;
mapping(bytes32 => bool) public seenDeliveryVaaHashes;
/**
* @notice Endpoint that the Wormhole Relayer contract will call
* to deliver the greeting
*/
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory, // additionalVaas
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 // deliveryHash
) public payable override {
require(msg.sender == address(wormholeRelayer), "Only relayer allowed");
// Ensure no duplicate deliveries
require(!seenDeliveryVaaHashes[deliveryHash], "Message already processed");
seenDeliveryVaaHashes[deliveryHash] = true;
// Parse the payload and do the corresponding actions!
(string memory greeting, address sender) = abi.decode(payload, (string, address));
latestGreeting = greeting;
emit GreetingReceived(
latestGreeting,
sourceChain,
fromWormholeFormat(sourceAddress)
);
}
```

And voila, we have a full contract that can be deployed to many EVM chains, and in totality would form a full cross-chain application powered by Wormhole!

Users with any wallet can request greetings to be emitted on any chain that is part of the system.
Expand Down
28 changes: 4 additions & 24 deletions beyond-hello-wormhole.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ HelloWormhole is a great example application, but has much room for improvement.

Topics covered:

- Protections
- Restricting the sender
- Preventing duplicate deliveries
- Restricting the sender
- Refunds
- Chained Deliveries
- Delivering existing VAAs

## Protections

### Problem 1 - The greetings can come from anyone
### Issue: The greetings can come from anyone

A user doesn’t have to go through the HelloWormhole contract to request a greeting - they can call `wormholeRelayer.sendPayloadToEvm{value: cost}(…)` themselves!

Expand Down Expand Up @@ -49,26 +47,9 @@ Often, it is desirable that all of the requests go through your own source contr
}
```

### Problem 2 - The greetings can be relayed multiple times
### Example Solution for Problem 1

As mentioned in the first article, without having the mapping of delivery hashes to boolean, anyone can fetch the delivery VAA corresponding to a sent greeting, and have it delivered again to the target HelloWormhole contract! This causes another `GreetingReceived` event to be emitted from the same `senderChain` and `sender`, even though the sender only intended on sending this greeting once.

**Solution:** In our implementation of receiveWormholeMessages, we store each delivery hash in a mapping from delivery hashes to booleans, to indicate that the delivery has already been processed. Then, at the beginning we can check to see if the delivery has already been processed, and revert if it has.

```solidity
mapping(bytes32 => bool) public seenDeliveryVaaHashes;
modifier replayProtect(bytes32 deliveryHash) {
require(!seenDeliveryVaaHashes[deliveryHash], "Message already processed");
seenDeliveryVaaHashes[deliveryHash] = true;
_;
}
```

### Example Solution for Problems 1 and 2

We provide a base class in the [Wormhole Solidity SDK](https://github.com/wormhole-foundation/wormhole-solidity-sdk) that includes the modifiers shown above, makes it easy to add these functionalities as such
We provide a base class in the [Wormhole Solidity SDK](https://github.com/wormhole-foundation/wormhole-solidity-sdk) that includes the modifier shown above, makes it easy to add these functionalities as such

```solidity
function receiveWormholeMessages(
Expand All @@ -83,7 +64,6 @@ We provide a base class in the [Wormhole Solidity SDK](https://github.com/wormho
override
onlyWormholeRelayer
isRegisteredSender(sourceChain, sourceAddress)
replayProtect(deliveryHash)
{
latestGreeting = abi.decode(payload, (string));
Expand Down
50 changes: 21 additions & 29 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"clean": "rm -rf ts-scripts/ethers-contracts/; rm -rf out/;"
},
"dependencies": {
"@certusone/wormhole-sdk": "^0.9.20",
"@certusone/wormhole-sdk": "^0.10.5",
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@typechain/ethers-v5": "^11.0.0",
"ethers": "^5",
Expand Down
32 changes: 19 additions & 13 deletions src/HelloWormhole.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@ contract HelloWormhole is IWormholeReceiver {
wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
}

function quoteCrossChainGreeting(uint16 targetChain) public view returns (uint256 cost) {
(cost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, 0, GAS_LIMIT);
function quoteCrossChainGreeting(
uint16 targetChain
) public view returns (uint256 cost) {
(cost, ) = wormholeRelayer.quoteEVMDeliveryPrice(
targetChain,
0,
GAS_LIMIT
);
}

function sendCrossChainGreeting(uint16 targetChain, address targetAddress, string memory greeting) public payable {
function sendCrossChainGreeting(
uint16 targetChain,
address targetAddress,
string memory greeting
) public payable {
uint256 cost = quoteCrossChainGreeting(targetChain);
require(msg.value == cost);
wormholeRelayer.sendPayloadToEvm{value: cost}(
Expand All @@ -33,25 +43,21 @@ contract HelloWormhole is IWormholeReceiver {
);
}

mapping(bytes32 => bool) public seenDeliveryVaaHashes;

function receiveWormholeMessages(
bytes memory payload,
bytes[] memory, // additionalVaas
bytes32, // address that called 'sendPayloadToEvm' (HelloWormhole contract address)
uint16 sourceChain,
bytes32 deliveryHash // this can be stored in a mapping deliveryHash => bool to prevent duplicate deliveries
bytes32 // unique identifier of delivery
) public payable override {
require(msg.sender == address(wormholeRelayer), "Only relayer allowed");

// Ensure no duplicate deliveries
require(!seenDeliveryVaaHashes[deliveryHash], "Message already processed");
seenDeliveryVaaHashes[deliveryHash] = true;

// Parse the payload and do the corresponding actions!
(string memory greeting, address sender) = abi.decode(payload, (string, address));
(string memory greeting, address sender) = abi.decode(
payload,
(string, address)
);
latestGreeting = greeting;
emit GreetingReceived(latestGreeting, sourceChain, sender);
}

}
}
Loading

0 comments on commit d3fcaaf

Please sign in to comment.