Skip to content

Commit

Permalink
feat: adds BLS signature verification for provider registration (#523)
Browse files Browse the repository at this point in the history
* feat: adds initial structure for the contract

* feat: updates tests with bls verfication

* chore: gets rid of stale todos

* chore: remove todo

* feat: adds overrideable BLS key addition

* feat: adds use of bls sigs in p2p layer

* feat: adds bls key registration to e2e test

* chore: updates go mod files

* chore: updates ordering of contract functions

* chore: attempt to sync mod files

* chore: fixes solidty linter

* chore: updates e2e testing

* feat: updates how staking is implemented

* chore: ensures decoding of hex from rpc

* chore: use real encoding in stake

* chore: fix go mod

* chore: update go mod

* chore: fix stake response encoding

* chore: fix e2e test

* chore: correctly generate message and signature

* chore: fixes lint

* chore: correctly format address

* chore: update genesis and runner

* chore: fix location of pre-compile

* chore: bump geth

* chore: only set cancun time

* chore: correctly configure genesis file

* chore: restrict to only bootnode build

* chore: add back signer

* chore: adds back all jobs

* feat: bumps geth and revert genesis change

* chore: don't put any keys in the integeration test cluster

* feat: adds api call to providers

* chore: log ethaddress

* chore: add more logs

* chore: set eth address

* fix: txn opts for BLS registration and errors (#531)

* chore: add errors to verification

* chore: add errors to verification

* chore: add errors to verification

---------

Co-authored-by: Alok <[email protected]>

* chore: bump abigen

* fix: ci runner

* fix: test

* chore: test

* fix: remove debug

* chore: fix add stake test

* chore: fix add stake test

---------

Co-authored-by: aloknerurkar <[email protected]>
Co-authored-by: Alok <[email protected]>
Co-authored-by: mrekucci <[email protected]>
  • Loading branch information
4 people authored Dec 13, 2024
1 parent 7c51c71 commit 33c187a
Show file tree
Hide file tree
Showing 21 changed files with 1,262 additions and 494 deletions.
140 changes: 122 additions & 18 deletions contracts-abi/abi/ProviderRegistry.abi
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "addVerifiedBLSKey",
"inputs": [
{
"name": "blsPublicKey",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "bidderSlashedAmount",
Expand Down Expand Up @@ -104,11 +122,6 @@
"name": "provider",
"type": "address",
"internalType": "address"
},
{
"name": "blsPublicKeys",
"type": "bytes[]",
"internalType": "bytes[]"
}
],
"outputs": [],
Expand Down Expand Up @@ -305,6 +318,24 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "overrideAddBLSKey",
"inputs": [
{
"name": "provider",
"type": "address",
"internalType": "address"
},
{
"name": "blsPublicKey",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "owner",
Expand Down Expand Up @@ -446,13 +477,7 @@
{
"type": "function",
"name": "registerAndStake",
"inputs": [
{
"name": "blsPublicKeys",
"type": "bytes[]",
"internalType": "bytes[]"
}
],
"inputs": [],
"outputs": [],
"stateMutability": "payable"
},
Expand Down Expand Up @@ -621,6 +646,35 @@
"outputs": [],
"stateMutability": "payable"
},
{
"type": "function",
"name": "verifySignature",
"inputs": [
{
"name": "pubKey",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "message",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "signature",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "withdraw",
Expand Down Expand Up @@ -667,6 +721,25 @@
],
"stateMutability": "view"
},
{
"type": "event",
"name": "BLSKeyAdded",
"inputs": [
{
"name": "provider",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "blsPublicKey",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
}
],
"anonymous": false
},
{
"type": "event",
"name": "BidderWithdrawSlashedAmount",
Expand Down Expand Up @@ -918,12 +991,6 @@
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "blsPublicKeys",
"type": "bytes[]",
"indexed": false,
"internalType": "bytes[]"
}
],
"anonymous": false
Expand Down Expand Up @@ -1040,6 +1107,11 @@
"name": "AtLeastOneBLSKeyRequired",
"inputs": []
},
{
"type": "error",
"name": "BLSSignatureInvalid",
"inputs": []
},
{
"type": "error",
"name": "BidderAmountIsZero",
Expand Down Expand Up @@ -1295,11 +1367,43 @@
}
]
},
{
"type": "error",
"name": "PublicKeyLengthInvalid",
"inputs": [
{
"name": "exp",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "got",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "ReentrancyGuardReentrantCall",
"inputs": []
},
{
"type": "error",
"name": "SignatureLengthInvalid",
"inputs": [
{
"name": "exp",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "got",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"type": "error",
"name": "StakeTransferFailed",
Expand Down
287 changes: 252 additions & 35 deletions contracts-abi/clients/ProviderRegistry/ProviderRegistry.go

Large diffs are not rendered by default.

95 changes: 79 additions & 16 deletions contracts/contracts/core/ProviderRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ contract ProviderRegistry is
emit FeePayoutPeriodBlocksUpdated(_feePayoutPeriodBlocks);
}

function overrideAddBLSKey(address provider, bytes calldata blsPublicKey) external onlyOwner {
require(providerRegistered[provider], ProviderNotRegistered(provider));
eoaToBlsPubkeys[provider].push(blsPublicKey);
blockBuilderBLSKeyToAddress[blsPublicKey] = provider;
}

/// @dev Requests unstake of the staked amount.
function unstake() external whenNotPaused {
require(providerStakes[msg.sender] != 0, NoStakeToWithdraw(msg.sender));
Expand Down Expand Up @@ -228,6 +234,35 @@ contract ProviderRegistry is
emit BidderWithdrawSlashedAmount(msg.sender, amount);
}

/**
* @dev Adds a verified BLS key to the provider's account.
* @param blsPublicKey The BLS public key to be added.
* @param signature The signature (96 bytes) used for verification.
*/
function addVerifiedBLSKey(
bytes calldata blsPublicKey,
bytes calldata signature
) external {
address provider = msg.sender;

require(providerRegistered[provider], ProviderNotRegistered(provider));
require(blsPublicKey.length == 48, PublicKeyLengthInvalid(48, blsPublicKey.length));
require(signature.length == 96, SignatureLengthInvalid(96, signature.length));


bytes32 message = keccak256(abi.encodePacked(provider));

// Verify the BLS signature
bool isValid = verifySignature(blsPublicKey, message, signature);
require(isValid, BLSSignatureInvalid());

// Add the BLS public key to the provider's account
eoaToBlsPubkeys[provider].push(blsPublicKey);
blockBuilderBLSKeyToAddress[blsPublicKey] = provider;

emit BLSKeyAdded(provider, blsPublicKey);
}

/**
* @dev Manually withdraws accumulated penalty fees to the recipient
* to cover the edge case that oracle doesn't slash/reward, and funds still need to be withdrawn.
Expand Down Expand Up @@ -272,20 +307,18 @@ contract ProviderRegistry is

/**
* @dev Register and stake function for providers.
* @param blsPublicKeys The BLS public keys of the provider.
* The validity of this key must be verified manually off-chain.
*/
function registerAndStake(bytes[] calldata blsPublicKeys) public payable whenNotPaused {
_registerAndStake(msg.sender, blsPublicKeys);
function registerAndStake() public payable whenNotPaused {
_registerAndStake(msg.sender);
}

/**
* @dev Register and stake on behalf of a provider.
* @param provider Address of the provider.
* @param blsPublicKeys BLS public keys of the provider.
*/
function delegateRegisterAndStake(address provider, bytes[] calldata blsPublicKeys) public payable whenNotPaused onlyOwner {
_registerAndStake(provider, blsPublicKeys);
function delegateRegisterAndStake(address provider) public payable whenNotPaused onlyOwner {
_registerAndStake(provider);
}

/// @dev Ensure the provider's balance is greater than minStake and no pending withdrawal
Expand All @@ -295,27 +328,57 @@ contract ProviderRegistry is
require(withdrawalRequests[provider] == 0, PendingWithdrawalRequest(provider));
}


/**
* @dev Verifies a BLS signature using the precompile
* @param pubKey The public key (48 bytes G1 point)
* @param message The message hash (32 bytes)
* @param signature The signature (96 bytes G2 point)
* @return success True if verification succeeded
*/
function verifySignature(
bytes calldata pubKey,
bytes32 message,
bytes calldata signature
) public view returns (bool) {
// Input validation
require(pubKey.length == 48, "Public key must be 48 bytes");
require(signature.length == 96, "Signature must be 96 bytes");

// Concatenate inputs in required format:
// [pubkey (48 bytes) | message (32 bytes) | signature (96 bytes)]
bytes memory input = bytes.concat(
pubKey,
message,
signature
);

// Call precompile
(bool success, bytes memory result) = address(0xf0).staticcall(input);

// Check if call was successful
if (!success) {
return false;
}

// If we got a result back and it's not empty, verification succeeded
return result.length > 0;
}

function _stake(address provider) internal {
require(providerRegistered[provider], ProviderNotRegistered(provider));
require(withdrawalRequests[provider] == 0, PendingWithdrawalRequest(provider));
providerStakes[provider] += msg.value;
emit FundsDeposited(provider, msg.value);
}

function _registerAndStake(address provider, bytes[] calldata blsPublicKeys) internal {
function _registerAndStake(address provider) internal {
require(!providerRegistered[provider], ProviderAlreadyRegistered(provider));
require(msg.value >= minStake, InsufficientStake(msg.value, minStake));
require(blsPublicKeys.length != 0, AtLeastOneBLSKeyRequired());
uint256 numKeys = blsPublicKeys.length;
for (uint256 i = 0; i < numKeys; ++i) {
bytes memory key = blsPublicKeys[i];
require(key.length == 48, InvalidBLSPublicKeyLength(key.length, 48));
blockBuilderBLSKeyToAddress[key] = provider;
}
eoaToBlsPubkeys[provider] = blsPublicKeys;

providerStakes[provider] = msg.value;
providerRegistered[provider] = true;
emit ProviderRegistered(provider, msg.value, blsPublicKeys);
emit ProviderRegistered(provider, msg.value);
}

// solhint-disable-next-line no-empty-blocks
Expand Down
16 changes: 13 additions & 3 deletions contracts/contracts/interfaces/IProviderRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.26;
interface IProviderRegistry {

/// @dev Event emitted when a provider is registered
event ProviderRegistered(address indexed provider, uint256 stakedAmount, bytes[] blsPublicKeys);
event ProviderRegistered(address indexed provider, uint256 stakedAmount);

/// @dev Event emitted when funds are deposited
event FundsDeposited(address indexed provider, uint256 amount);
Expand Down Expand Up @@ -36,6 +36,9 @@ interface IProviderRegistry {
/// @dev Event emitted when the fee percent is updated
event FeePercentUpdated(uint256 indexed newFeePercent);

/// @dev Event emitted when a BLS key is added
event BLSKeyAdded(address indexed provider, bytes blsPublicKey);

/// @dev Event emitted when there are insufficient funds to slash
event InsufficientFundsToSlash(
address indexed provider,
Expand Down Expand Up @@ -67,8 +70,11 @@ interface IProviderRegistry {
error PendingWithdrawalRequest(address sender);
error BidderAmountIsZero(address sender);
error BidderWithdrawalTransferFailed(address sender, uint256 amount);

function registerAndStake(bytes[] calldata blsPublicKeys) external payable;
error PublicKeyLengthInvalid(uint256 exp, uint256 got);
error SignatureLengthInvalid(uint256 exp, uint256 got);
error BLSSignatureInvalid();

function registerAndStake() external payable;

function stake() external payable;

Expand All @@ -78,7 +84,11 @@ interface IProviderRegistry {
address payable bidder,
uint256 residualBidPercentAfterDecay
) external;

function addVerifiedBLSKey(bytes calldata blsPublicKey, bytes calldata signature) external;

function overrideAddBLSKey(address provider, bytes calldata blsPublicKey) external;

function isProviderValid(address committerAddress) external view;

function getEoaFromBLSKey(bytes calldata blsKey) external view returns (address);
Expand Down
Loading

0 comments on commit 33c187a

Please sign in to comment.