Skip to content

Commit

Permalink
commitBatch and receiveBatch optimizations (#40)
Browse files Browse the repository at this point in the history
* fixes

* fix

* Fix BridgeStorage and Gateway hardhat tests

* Fix forge tests

* Fix

* Fix

* comments fix

---------

Co-authored-by: filip <[email protected]>
  • Loading branch information
goran-ethernal and grujicf authored Oct 11, 2024
1 parent 0ca2c99 commit 8158051
Show file tree
Hide file tree
Showing 12 changed files with 349 additions and 412 deletions.
40 changes: 13 additions & 27 deletions contracts/blade/BridgeStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,19 @@ contract BridgeStorage is ValidatorSetStorage {
/**
* @notice commits new batch
* @param batch new batch
* @param signature aggregated signature of validators that signed the new batch
* @param bitmap bitmap of which validators signed the message
*/
function commitBatch(
BridgeMessageBatch calldata batch,
uint256[2] calldata signature,
bytes calldata bitmap
) external onlySystemCall {
function commitBatch(SignedBridgeMessageBatch calldata batch) external onlySystemCall {
_verifyBatch(batch);

bytes memory hash = abi.encode(
keccak256(abi.encode(batch.messages, batch.sourceChainId, batch.destinationChainId))
keccak256(
abi.encode(batch.rootHash, batch.startId, batch.endId, batch.sourceChainId, batch.destinationChainId)
)
);
verifySignature(bls.hashToPoint(DOMAIN_BRIDGE, hash), signature, bitmap);

SignedBridgeMessageBatch storage signedBatch = batches[batchCounter];
signedBatch.batch = batch;
signedBatch.signature = signature;
signedBatch.bitmap = bitmap;
verifySignature(bls.hashToPoint(DOMAIN_BRIDGE, hash), batch.signature, batch.bitmap);

batches[batchCounter] = batch;

emit NewBatch(batchCounter);

Expand All @@ -77,23 +71,15 @@ contract BridgeStorage is ValidatorSetStorage {
* @notice Internal function that verifies the batch
* @param batch batch to verify
*/
function _verifyBatch(BridgeMessageBatch calldata batch) private {
require(batch.messages.length > 0, "EMPTY_BATCH");
require(lastCommitted[batch.sourceChainId] + 1 == batch.messages[0].id, "INVALID_LAST_COMMITTED");

for (uint256 i = 0; i < batch.messages.length; ) {
BridgeMessage memory message = batch.messages[i];
require(message.sourceChainId == batch.sourceChainId, "INVALID_SOURCE_CHAIN_ID");
require(message.destinationChainId == batch.destinationChainId, "INVALID_DESTINATION_CHAIN_ID");
unchecked {
++i;
}
}
function _verifyBatch(SignedBridgeMessageBatch calldata batch) private {
require(batch.rootHash != bytes32(0), "EMPTY_BATCH");

if (batch.sourceChainId == block.chainid) {
lastCommittedInternal[batch.destinationChainId] = batch.messages[batch.messages.length - 1].id;
require(lastCommittedInternal[batch.destinationChainId] + 1 == batch.startId, "INVALID_LAST_COMMITTED");
lastCommittedInternal[batch.destinationChainId] = batch.endId;
} else {
lastCommitted[batch.sourceChainId] = batch.messages[batch.messages.length - 1].id;
require(lastCommitted[batch.sourceChainId] + 1 == batch.startId, "INVALID_LAST_COMMITTED");
lastCommitted[batch.sourceChainId] = batch.endId;
}
}

Expand Down
48 changes: 36 additions & 12 deletions contracts/blade/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.19;

import "./ValidatorSetStorage.sol";
import "../interfaces/IGateway.sol";
import "../lib/Merkle.sol";

contract Gateway is ValidatorSetStorage, IGateway {
uint256 public constant MAX_LENGTH = 2048;
Expand Down Expand Up @@ -54,25 +55,32 @@ contract Gateway is ValidatorSetStorage, IGateway {
/**
* @notice receives the batch of messages and executes them
* @param batch batch of messages
* @param signature the aggregated signature submitted by the proposer
* @param bitmap bitmap of which validators signed the message
*/
// slither-disable-next-line protected-vars
function receiveBatch(
BridgeMessageBatch calldata batch,
BridgeMessage[] calldata batch,
uint256[2] calldata signature,
bytes calldata bitmap
) external {
_verifyBatch(batch);

bytes memory hash = abi.encode(
keccak256(abi.encode(batch.messages, batch.sourceChainId, batch.destinationChainId))
keccak256(
abi.encode(
calculateMerkleRoot(batch),
batch[0].id,
batch[batch.length - 1].id,
batch[0].sourceChainId,
batch[0].destinationChainId
)
)
);

verifySignature(bls.hashToPoint(DOMAIN_BRIDGE, hash), signature, bitmap);

uint256 length = batch.messages.length;
uint256 length = batch.length;
for (uint256 i = 0; i < length; ) {
_executeBridgeMessage(batch.messages[i]);
_executeBridgeMessage(batch[i]);

unchecked {
++i;
Expand All @@ -84,14 +92,15 @@ contract Gateway is ValidatorSetStorage, IGateway {
* @notice Internal function that verifies the batch
* @param batch batch to verify
*/
function _verifyBatch(BridgeMessageBatch calldata batch) private view {
require(batch.messages.length > 0, "EMPTY_BATCH");
function _verifyBatch(BridgeMessage[] calldata batch) private view {
require(batch.length > 0, "EMPTY_BATCH");

uint256 destinationChainId = block.chainid;
require(batch.destinationChainId == destinationChainId, "INVALID_DESTINATION_CHAIN_ID");
for (uint256 i = 0; i < batch.messages.length; ) {
BridgeMessage memory message = batch.messages[i];
require(message.sourceChainId == batch.sourceChainId, "INVALID_SOURCE_CHAIN_ID");
uint256 sourceChainId = batch[0].sourceChainId;

for (uint256 i = 0; i < batch.length; ) {
BridgeMessage memory message = batch[i];
require(message.sourceChainId == sourceChainId, "INVALID_SOURCE_CHAIN_ID");
require(message.destinationChainId == destinationChainId, "INVALID_DESTINATION_CHAIN_ID");
unchecked {
++i;
Expand Down Expand Up @@ -127,6 +136,21 @@ contract Gateway is ValidatorSetStorage, IGateway {
emit BridgeMessageResult(message.id, success, message.sourceChainId, message.destinationChainId, returnData);
}

// Function to calculate Merkle Root from an array of BridgeMessages
function calculateMerkleRoot(BridgeMessage[] memory messages) internal pure returns (bytes32) {
require(messages.length > 0, "No messages provided");

// Convert the BridgeMessages to their keccak256 hashes (this will be the actual leaves)
bytes32[] memory leaves = new bytes32[](messages.length);

for (uint256 i = 0; i < messages.length; i++) {
leaves[i] = keccak256(abi.encode(messages[i]));
}

// Pass the leaves to compute the Merkle root
return Merkle.computeMerkleRoot(leaves);
}

// slither-disable-next-line unused-state,naming-convention
uint256[50] private __gap;
}
25 changes: 11 additions & 14 deletions contracts/interfaces/blade/IValidatorSetStorage.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
pragma solidity ^0.8.19;

bytes32 constant DOMAIN_VALIDATOR_SET = keccak256("DOMAIN_VALIDATOR_SET");

Expand Down Expand Up @@ -34,23 +34,20 @@ struct BridgeMessage {
}

/**
* @param messages list of all messages in batch
* @param sourceChainId id of chain which is source of batch
* @param destinationChainId id of chain which is destination of batch
*/
struct BridgeMessageBatch {
BridgeMessage[] messages;
uint256 sourceChainId;
uint256 destinationChainId;
}

/**
* @param batch batch that is signed
* @param rootHash root hash of the batch
* @param startId start id of the batch
* @param endId end id of the batch
* @param sourceChainId id of source chain
* @param destinationChainId id of destination chain
* @param signature aggregated signature of validators that signed the batch
* @param bitmap bitmap of which validators signed the message
*/
struct SignedBridgeMessageBatch {
BridgeMessageBatch batch;
bytes32 rootHash;
uint256 startId;
uint256 endId;
uint256 sourceChainId;
uint256 destinationChainId;
uint256[2] signature;
bytes bitmap;
}
Expand Down
37 changes: 37 additions & 0 deletions contracts/lib/Merkle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/**
* @title Merkle tree Lib
* @notice merkle tree helper functions
*/
library Merkle {
/**
* @notice helper function to compute the Merkle Root from an array of already hashed leaves
* @param leaves hashed leaves of the merkle tree
*/
function computeMerkleRoot(bytes32[] memory leaves) internal pure returns (bytes32) {
if (leaves.length == 1) {
return leaves[0]; // If there's only one leaf, it is the root
}

// Continue hashing pairs of nodes until the root is obtained
while (leaves.length > 1) {
uint256 nextLevelSize = (leaves.length + 1) / 2;
bytes32[] memory nextLevel = new bytes32[](nextLevelSize);

for (uint256 i = 0; i < leaves.length / 2; i++) {
nextLevel[i] = keccak256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]));
}

// If the number of leaves is odd, carry forward the last leaf
if (leaves.length % 2 == 1) {
nextLevel[nextLevel.length - 1] = leaves[leaves.length - 1];
}

leaves = nextLevel;
}

return leaves[0];
}
}
14 changes: 8 additions & 6 deletions docs/blade/BridgeStorage.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function batchCounter() external view returns (uint256)
### batches

```solidity
function batches(uint256) external view returns (struct BridgeMessageBatch batch, bytes bitmap)
function batches(uint256) external view returns (bytes32 rootHash, uint256 startId, uint256 endId, uint256 sourceChainId, uint256 destinationChainId, bytes bitmap)
```


Expand All @@ -166,7 +166,11 @@ function batches(uint256) external view returns (struct BridgeMessageBatch batch

| Name | Type | Description |
|---|---|---|
| batch | BridgeMessageBatch | undefined |
| rootHash | bytes32 | undefined |
| startId | uint256 | undefined |
| endId | uint256 | undefined |
| sourceChainId | uint256 | undefined |
| destinationChainId | uint256 | undefined |
| bitmap | bytes | undefined |

### bls
Expand Down Expand Up @@ -206,7 +210,7 @@ function bn256G2() external view returns (contract IBN256G2)
### commitBatch

```solidity
function commitBatch(BridgeMessageBatch batch, uint256[2] signature, bytes bitmap) external nonpayable
function commitBatch(SignedBridgeMessageBatch batch) external nonpayable
```


Expand All @@ -217,9 +221,7 @@ function commitBatch(BridgeMessageBatch batch, uint256[2] signature, bytes bitma

| Name | Type | Description |
|---|---|---|
| batch | BridgeMessageBatch | undefined |
| signature | uint256[2] | undefined |
| bitmap | bytes | undefined |
| batch | SignedBridgeMessageBatch | undefined |

### commitValidatorSet

Expand Down
4 changes: 2 additions & 2 deletions docs/blade/Gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ function processedEvents(uint256) external view returns (bool)
### receiveBatch

```solidity
function receiveBatch(BridgeMessageBatch batch, uint256[2] signature, bytes bitmap) external nonpayable
function receiveBatch(BridgeMessage[] batch, uint256[2] signature, bytes bitmap) external nonpayable
```


Expand All @@ -326,7 +326,7 @@ function receiveBatch(BridgeMessageBatch batch, uint256[2] signature, bytes bitm

| Name | Type | Description |
|---|---|---|
| batch | BridgeMessageBatch | undefined |
| batch | BridgeMessage[] | undefined |
| signature | uint256[2] | undefined |
| bitmap | bytes | undefined |

Expand Down
12 changes: 12 additions & 0 deletions docs/lib/Merkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Merkle











Loading

0 comments on commit 8158051

Please sign in to comment.