feat: 🏗️ Setup smart contracts structure for messages Substrate->Eth (#8)

This PR:
- Sets up an interface for message passing from a Substrate chain to the
Ethereum.
- Adds a diagram for understanding how messages are validated.
This commit is contained in:
Facundo Farall 2025-03-19 10:23:20 -03:00 committed by GitHub
parent 7a4d441fd9
commit 70a034fab7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 140 additions and 0 deletions

1
.gitignore vendored
View file

@ -16,6 +16,7 @@ Cargo.lock
# IDE directories
.vscode/
.idea/
.zed/
# cspell
cspell.json

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

View file

@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
interface IBeefyMessageVerifierErrors {}
interface IBeefyMessageVerifierEvents {}
/// @dev An BEEFY MMRLeaf without the `leaf_extra field.
/// Reference: https://github.com/paritytech/polkadot-sdk/blob/1abdb7f39cada4840d795b9b721631a3cfe53174/substrate/primitives/consensus/beefy/src/mmr.rs#L53
struct BeefyMMRLeafPartial {
uint8 version;
uint32 parentNumber;
bytes32 parentHash;
uint64 nextAuthoritySetID;
uint32 nextAuthoritySetLen;
bytes32 nextAuthoritySetRoot;
}
/// @dev Parameters used to verify a proof generated by https://github.com/paritytech/polkadot-sdk/blob/1abdb7f39cada4840d795b9b721631a3cfe53174/substrate/utils/binary-merkle-tree/src/lib.rs#L144
struct SubstrateBinaryMerkleProof {
uint256 position;
uint256 width;
bytes32[] proof;
}
/// @dev Parameters used to verify a that a BEEFY MMRLeaf is part of the latest BEEFY MMR Root.
/// Reference: https://github.com/Snowfork/snowbridge/blob/069177d6c6d44c5a1478ae2f14051c0b3bd7a69a/contracts/src/BeefyClient.sol#L399
struct BeefyMMRProof {
bytes32[] proof;
uint256 leafProofOrder;
}
interface IBeefyMessageVerifier is IBeefyMessageVerifierErrors, IBeefyMessageVerifierEvents {
/**
* @notice Verifies a message passed through the "Extra Leaf Data" field of a BEEFY MMRLeaf.
* @dev The message has to be a part of the committed messages in a BEEFY MMRLeaf, that
* is part of the latest BEEFY MMR Root that the validators have signed and finalised.
* @dev To prove that the message is part of the committed messages, three proofs are required:
* 1. A proof that the message is part of a message commitment in the BEEFY MMRLeaf.
* 2. A proof that the message commitment is part of the BEEFY MMRLeaf.
* 3. A proof that the BEEFY MMRLeaf is part of the latest BEEFY MMR Root.
* @dev For more a diagram on these three layers of proofs, see:
* https://github.com/Moonsong-Labs/datahaven/blob/main/contracts/resources/MessageProofs.png
* @dev For more details on BEEFY, see: https://wiki.polkadot.network/docs/learn-consensus#bridging-beefy
* @dev For more details on the MMRLeaf specification, see: https://spec.polkadot.network/sect-finality#defn-beefy-payload
* @dev For more details on the verification of the MMRLeaf, see Snowbridge's docs: https://docs.snowbridge.network/architecture/verification/polkadot
* @param message The message to verify.
* @param messageProof The proof that the message is part of a message commitment in the BEEFY MMRLeaf.
* @param messageId The ID that identifies the kind of message being passed through the "Extra Leaf Data" field of the BEEFY MMRLeaf.
* @param messageCommitmentProof The proof that the message commitment is part of the BEEFY MMRLeaf.
* @param partialBeefyLeaf The partial BEEFY leaf (without the `leaf_extra` field which contains the root of the message commitments).
* @param beefyLeafProof The proof that the BEEFY MMRLeaf is part of the latest BEEFY MMR Root.
* @return isValid Whether the message is valid.
*/
function verifyBeefyMessage(
bytes calldata message,
bytes calldata messageProof,
bytes32 messageId,
SubstrateBinaryMerkleProof calldata messageCommitmentProof,
BeefyMMRLeafPartial calldata partialBeefyLeaf,
BeefyMMRProof calldata beefyLeafProof
) external view returns (bool);
}

View file

@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import "../interfaces/IBeefyMessageVerifier.sol";
contract ServiceMessageDispatcher is IBeefyMessageVerifier, Initializable, OwnableUpgradeable {
bytes32 public constant VALIDATOR_POINTS_MESSAGE_ID = keccak256("VALIDATOR_POINTS");
bytes32 public constant SLASH_MESSAGE_ID = keccak256("SLASH");
/// @notice A mapping of message IDs to external verifiers.
/// @dev The external verifier is a contract that implements the `IBeefyMessageVerifier` interface.
/// @dev This provides a way to delegate the verification of unknown message IDs to an external contract,
/// thus extending the functionality of this contract without the need to modify the contract.
mapping(bytes32 => address) public messageIdToExternalVerifier;
function initialize() public initializer {
__Ownable_init();
}
function verifyValidatorPointsMessage(
bytes calldata, // message,
bytes calldata, // messageProof,
SubstrateBinaryMerkleProof calldata, // messageCommitmentProof,
BeefyMMRLeafPartial calldata, // partialBeefyLeaf,
BeefyMMRProof calldata // beefyLeafProof
) public view returns (bool) {
// TODO: Implement the logic to verify the validator points message
return true;
}
function verifySlashMessage(
bytes calldata, // message,
bytes calldata, // messageProof,
SubstrateBinaryMerkleProof calldata, // messageCommitmentProof,
BeefyMMRLeafPartial calldata, // partialBeefyLeaf,
BeefyMMRProof calldata // beefyLeafProof
) public view returns (bool) {
// TODO: Implement the logic to verify the slash message
return true;
}
function verifyBeefyMessage(
bytes calldata message,
bytes calldata messageProof,
bytes32 messageId,
SubstrateBinaryMerkleProof calldata messageCommitmentProof,
BeefyMMRLeafPartial calldata partialBeefyLeaf,
BeefyMMRProof calldata beefyLeafProof
) external view override returns (bool) {
// For known message IDs, we can verify it here.
if (messageId == VALIDATOR_POINTS_MESSAGE_ID) {
return verifyValidatorPointsMessage(
message, messageProof, messageCommitmentProof, partialBeefyLeaf, beefyLeafProof
);
} else if (messageId == SLASH_MESSAGE_ID) {
return verifySlashMessage(
message, messageProof, messageCommitmentProof, partialBeefyLeaf, beefyLeafProof
);
} else if (messageIdToExternalVerifier[messageId] != address(0)) {
// For unknown message IDs, we delegate the verification to an external verifier, if there is one registered.
return IBeefyMessageVerifier(messageIdToExternalVerifier[messageId]).verifyBeefyMessage(
message,
messageProof,
messageId,
messageCommitmentProof,
partialBeefyLeaf,
beefyLeafProof
);
}
// Unknown message IDs are not supported.
return false;
}
}