This repository has been archived by the owner on Oct 22, 2024. It is now read-only.
forked from paritytech/polkadot-sdk
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Full Beefy MMR Relay chain -> Ethereum working with e2e tests (parity…
…tech#343) * fixz * Add subscribe script * Add MMR proof+leaves * add offchain relay workers * decode mmr proofs * update readme * fix config * update README (paritytech#297) * improve get authorities * get more data in script * Add commitment log * tests use incentivized channel id * relayer log more * test query epoch * generate lightclientbridge contract bindings * implement parachain chain for testing * add keypair getter to eth connection * add parachain to relay * update go.mod, go.sum * drop relayer from start-services script (testing) * add lightclientbridge to truffle migration * send initial verification tx to eth contract * BeefyCommitmentInfo type, msg builder methods * add BeefyBlockDelay to config * implement WriteCompleteSignatureCommitment * integrate beefy listener, poll eth blocks + events * add beefy chan, update chain interface for compile * generate validatorregistry contract * generate mmr proof offchain * sync header before polling eth blocks * listener compile * update config * generate mmr proof onchain + verify val registry * implement WriteCompleteSignatureCommitment * add block hash helper function * writer test * update go.mod/go.sum * deploy val registry contract with params * rename parachain to relaychain * integrate latest contracts * rename contract to PolkadotRelayChainBridge * query onchain beefy authorities * in-memory database * refactor substrate listener * refactor ethereum listener/writer * update chain interface * refactor relay.go * bump go.mod, go.sum * remove checks in polkadotrelaychainbridge contract * delete relaychain package * delete relaychain package * fix pending nonce conflict * update tests * implement random seed for completion tx * remove duplicated fields on Beefy/BeefyItem * handle edge case: use intended completion block * fix conditional statment * parachain, relaychain abstractions * remove validatorregistry contract for now * initial ethereum revisons * remove relayconn from chain interface * update mmr -> merkle * update configuration * update parachain tests * fix relaychain init error * update parachain test endpoints * close relaychain lister, database * use database.stop * fetch BLOCK_WAIT_PERIOD directly from contract * remove block number from tx sent log * update beefy naming conventions * normal channels * only process events from our node * move authorities to types file * remove extra param * update contracts to latest * comment out bridge verification checks * clean up * wait until block is finalized to send complete tx * refactor eth writer cron job * update writer test * start relayer with start-service script * ignore relaychain init channels * combine msgs/BEEFY msgs write loops * clean up * minor fixes * update polkadot version * Fix event forwarding Co-authored-by: Denali Marsh <denalimarsh@gmail.com> Co-authored-by: Vincent Geddes <vincent.geddes@hey.com>
- Loading branch information
1 parent
1978dfb
commit 3c0a974
Showing
53 changed files
with
4,158 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
# Local Netlify folder | ||
.netlify | ||
.netlify | ||
|
||
# VSCode Workspace | ||
workspace.code-workspace |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.7.0; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@openzeppelin/contracts/math/SafeMath.sol"; | ||
import "@openzeppelin/contracts/cryptography/ECDSA.sol"; | ||
import "./utils/Bits.sol"; | ||
import "./utils/Bitfield.sol"; | ||
import "./ValidatorRegistry.sol"; | ||
|
||
/** | ||
* @title A entry contract for the Ethereum light client | ||
*/ | ||
contract LightClientBridge { | ||
using SafeMath for uint256; | ||
using Bits for uint256; | ||
using Bitfield for uint256[]; | ||
|
||
/* Events */ | ||
|
||
/** | ||
* @notice Notifies an observer that the prover's attempt at initital | ||
* verification was successful. | ||
* @dev Note that the prover must wait until `n` blocks have been mined | ||
* subsequent to the generation of this event before the 2nd tx can be sent | ||
* @param prover The address of the calling prover | ||
* @param blockNumber The blocknumber in which the initial validation | ||
* succeeded | ||
* @param id An identifier to provide disambiguation | ||
*/ | ||
event InitialVerificationSuccessful( | ||
address prover, | ||
uint256 blockNumber, | ||
uint256 id | ||
); | ||
|
||
/** | ||
* @notice Notifies an observer that the complete verification process has | ||
* finished successfuly and the new payload will be accepted | ||
* @param prover The address of the successful prover | ||
* @param payload the payload which was approved for inclusion | ||
* @param id the identifier used | ||
*/ | ||
event FinalVerificationSuccessful( | ||
address prover, | ||
bytes32 payload, | ||
uint256 id | ||
); | ||
|
||
/* Types */ | ||
|
||
struct ValidationData { | ||
address senderAddress; | ||
bytes32 payload; | ||
uint256[] validatorClaimsBitfield; | ||
uint256 blockNumber; | ||
} | ||
|
||
/* State */ | ||
|
||
ValidatorRegistry public validatorRegistry; | ||
uint256 public currentId; | ||
mapping(uint256 => ValidationData) public validationData; | ||
|
||
/* Constants */ | ||
|
||
uint256 public constant THRESHOLD_NUMERATOR = 2; | ||
uint256 public constant THRESHOLD_DENOMINATOR = 3; | ||
uint256 public constant BLOCK_WAIT_PERIOD = 45; | ||
|
||
/** | ||
* @notice Deploys the LightClientBridge contract | ||
* @dev If the validatorSetRegistry should be initialised with 0 entries, then input | ||
* 0x00 as validatorSetRoot | ||
* @param _validatorRegistry The contract to be used as the validator registry | ||
*/ | ||
constructor(ValidatorRegistry _validatorRegistry) { | ||
validatorRegistry = _validatorRegistry; | ||
currentId = 0; | ||
} | ||
|
||
/* Public Functions */ | ||
/** | ||
* @notice Executed by the prover in order to begin the process of block | ||
* acceptance by the light client | ||
* @param payload contains the payload signed by the validator(s) | ||
* @param validatorClaimsBitfield a bitfield containing a membership status of each | ||
* validator who has claimed to have signed the payload | ||
* @param validatorSignature the signature of one validator | ||
* @param validatorPosition the position of the validator, index starting at 0 | ||
* @param validatorPublicKey the public key of the validator | ||
* @param validatorPublicKeyMerkleProof proof required for validation of the public key in the validator merkle tree | ||
*/ | ||
function newSignatureCommitment( | ||
bytes32 payload, | ||
uint256[] memory validatorClaimsBitfield, | ||
bytes memory validatorSignature, | ||
uint256 validatorPosition, | ||
address validatorPublicKey, | ||
bytes32[] calldata validatorPublicKeyMerkleProof | ||
) public payable { | ||
/** | ||
* @dev Check if validatorPublicKeyMerkleProof is valid based on ValidatorRegistry merkle root | ||
*/ | ||
// require( | ||
// validatorRegistry.checkValidatorInSet( | ||
// validatorPublicKey, | ||
// validatorPosition, | ||
// validatorPublicKeyMerkleProof | ||
// ), | ||
// "Error: Sender must be in validator set at correct position" | ||
// ); | ||
|
||
/** | ||
* @dev Check if validatorSignature is correct, ie. check if it matches | ||
* the signature of senderPublicKey on the payload | ||
*/ | ||
// require( | ||
// ECDSA.recover(payload, validatorSignature) == validatorPublicKey, | ||
// "Error: Invalid Signature" | ||
// ); | ||
|
||
/** | ||
* @dev Check that the bitfield actually contains enough claims to be succesful, ie, > 2/3 | ||
*/ | ||
// require( | ||
// validatorClaimsBitfield.countSetBits() > | ||
// (validatorRegistry.numOfValidators() * THRESHOLD_NUMERATOR) / | ||
// THRESHOLD_DENOMINATOR, | ||
// "Error: Bitfield not enough validators" | ||
// ); | ||
|
||
/** | ||
* @todo Lock up the sender stake as collateral | ||
*/ | ||
// TODO | ||
|
||
// Accept and save the commitment | ||
validationData[currentId] = ValidationData( | ||
msg.sender, | ||
payload, | ||
validatorClaimsBitfield, | ||
block.number | ||
); | ||
|
||
emit InitialVerificationSuccessful(msg.sender, block.number, currentId); | ||
|
||
currentId = currentId.add(1); | ||
} | ||
|
||
/** | ||
* @notice Performs the second step in the validation logic | ||
* @param id an identifying value generated in the previous transaction | ||
* @param payload contains the payload signed by the validator(s) | ||
* @param signatures an array of signatures from the randomly chosen validators | ||
* @param validatorPositions an array of bitfields from the chosen validators | ||
* @param validatorPublicKeys an array of the public key of each signer | ||
* @param validatorPublicKeyMerkleProofs an array of merkle proofs from the chosen validators | ||
*/ | ||
function completeSignatureCommitment( | ||
uint256 id, | ||
bytes32 payload, | ||
bytes[] memory signatures, | ||
uint256[] memory validatorPositions, | ||
address[] memory validatorPublicKeys, | ||
bytes32[][] memory validatorPublicKeyMerkleProofs | ||
) public { | ||
ValidationData storage data = validationData[id]; | ||
|
||
/** | ||
* @dev verify that block wait period has passed | ||
*/ | ||
require( | ||
block.number >= data.blockNumber.add(BLOCK_WAIT_PERIOD), | ||
"Error: Block wait period not over" | ||
); | ||
|
||
/** | ||
* @dev verify that sender is the same as in `newSignatureCommitment` | ||
*/ | ||
require( | ||
msg.sender == data.senderAddress, | ||
"Error: Sender address does not match original validation data" | ||
); | ||
|
||
// uint256 requiredNumOfSignatures = | ||
// (validatorRegistry.numOfValidators() * THRESHOLD_NUMERATOR) / | ||
// THRESHOLD_DENOMINATOR; | ||
|
||
/** | ||
* @dev verify that required number of signatures, positions, public keys and merkle proofs are | ||
* submitted | ||
*/ | ||
// require( | ||
// signatures.length == requiredNumOfSignatures, | ||
// "Error: Number of signatures does not match required" | ||
// ); | ||
// require( | ||
// validatorPositions.length == requiredNumOfSignatures, | ||
// "Error: Number of validator positions does not match required" | ||
// ); | ||
// require( | ||
// validatorPublicKeys.length == requiredNumOfSignatures, | ||
// "Error: Number of validator public keys does not match required" | ||
// ); | ||
// require( | ||
// validatorPublicKeyMerkleProofs.length == requiredNumOfSignatures, | ||
// "Error: Number of validator public keys does not match required" | ||
// ); | ||
|
||
/** | ||
* @dev Generate an array of numbers | ||
*/ | ||
// uint256[] memory randomBitfield = | ||
// Bitfield.randomNBitsFromPrior( | ||
// getSeed(data), | ||
// data.validatorClaimsBitfield, | ||
// requiredNumOfSignatures | ||
// ); | ||
|
||
/** | ||
* @dev For each randomSignature, do: | ||
*/ | ||
// for (uint256 i = 0; i < requiredNumOfSignatures; i++) { | ||
// /** | ||
// * @dev Check if validator in randomBitfield | ||
// */ | ||
// require( | ||
// randomBitfield.isSet(validatorPositions[i]), | ||
// "Error: Validator must be once in bitfield" | ||
// ); | ||
|
||
// /** | ||
// * @dev Remove validator from randomBitfield such that no validator can appear twice in signatures | ||
// */ | ||
// randomBitfield.clear(validatorPositions[i]); | ||
|
||
// /** | ||
// * @dev Check if merkle proof is valid | ||
// */ | ||
// require( | ||
// validatorRegistry.checkValidatorInSet( | ||
// validatorPublicKeys[i], | ||
// validatorPositions[i], | ||
// validatorPublicKeyMerkleProofs[i] | ||
// ), | ||
// "Error: Validator must be in validator set at correct position" | ||
// ); | ||
|
||
// /** | ||
// * @dev Check if signature is correct | ||
// */ | ||
// require( | ||
// ECDSA.recover(payload, signatures[i]) == validatorPublicKeys[i], | ||
// "Error: Invalid Signature" | ||
// ); | ||
// } | ||
|
||
/** | ||
* @todo Release the sender stake as collateral | ||
*/ | ||
// TODO | ||
|
||
/** | ||
* @follow-up Do we need a try-catch block here? | ||
*/ | ||
processPayload(data.payload); | ||
|
||
emit FinalVerificationSuccessful(msg.sender, payload, id); | ||
|
||
/** | ||
* @dev We no longer need the data held in state, so delete it for a gas refund | ||
*/ | ||
delete validationData[id]; | ||
} | ||
|
||
/* Private Functions */ | ||
|
||
/** | ||
* @notice Deterministically generates a seed from the block hash at the block number of creation of the validation | ||
* data plus MAXIMUM_NUM_SIGNERS | ||
* @dev Note that `blockhash(blockNum)` will only work for the 256 most recent blocks. If | ||
* `completeSignatureCommitment` is called too late, a new call to `newSignatureCommitment` is necessary to reset | ||
* validation data's block number | ||
* @param data a storage reference to the validationData struct | ||
* @return onChainRandNums an array storing the random numbers generated inside this function | ||
*/ | ||
function getSeed(ValidationData storage data) | ||
private | ||
view | ||
returns (uint256) | ||
{ | ||
// @note Get payload.blocknumber, add BLOCK_WAIT_PERIOD | ||
uint256 randomSeedBlockNum = data.blockNumber.add(BLOCK_WAIT_PERIOD); | ||
// @note Create a hash seed from the block number | ||
bytes32 randomSeedBlockHash = blockhash(randomSeedBlockNum); | ||
|
||
return uint256(randomSeedBlockHash); | ||
} | ||
|
||
/** | ||
* @notice Perform some operation[s] using the payload | ||
* @param payload The payload variable passed in via the initial function | ||
*/ | ||
function processPayload(bytes32 payload) private { | ||
// Check the payload is newer than the latest | ||
// Check that payload.leaf.block_number is > last_known_block_number; | ||
|
||
//update latestMMRRoot = payload.mmrRoot; | ||
|
||
// if payload is in next epoch, then apply validatorset changes | ||
// if payload is not in current or next epoch, reject | ||
|
||
applyValidatorSetChanges(payload); | ||
} | ||
|
||
/** | ||
* @notice Check if the payload includes a new validator set, | ||
* and if it does then update the new validator set | ||
* @dev This function should call out to the validator registry contract | ||
* @param payload The value to check if changes are required | ||
*/ | ||
function applyValidatorSetChanges(bytes32 payload) private { | ||
// @todo Implement this function | ||
// payload should contain a new root AND a MMR proof to the newest leaf | ||
// check proof is for the newest leaf and is valid | ||
// in the new leaf we should have | ||
/* | ||
MmrLeaf { | ||
block_number: int | ||
parent_hash: frame_system::Module::<T>::leaf_data(), | ||
parachain_heads: Module::<T>::parachain_heads_merkle_root(), | ||
beefy_authority_set: Module::<T>::beefy_authority_set_merkle_root(), | ||
} | ||
*/ | ||
// get beefy_authority_set from newest leaf | ||
// update authority set | ||
// validatorRegistry.updateValidatorSet(beefy_authority_set) | ||
} | ||
} |
Oops, something went wrong.