Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds BLS signature verification for provider registration #523

Merged
merged 44 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
4254eaf
feat: adds initial structure for the contract
ckartik Dec 6, 2024
152cdb4
feat: updates tests with bls verfication
ckartik Dec 6, 2024
ca2310b
chore: gets rid of stale todos
ckartik Dec 7, 2024
cf9a264
chore: remove todo
ckartik Dec 7, 2024
1587dd8
feat: adds overrideable BLS key addition
ckartik Dec 7, 2024
34b6a39
feat: adds use of bls sigs in p2p layer
ckartik Dec 9, 2024
b5bc395
feat: adds bls key registration to e2e test
ckartik Dec 9, 2024
a6285fa
chore: updates go mod files
ckartik Dec 9, 2024
132c431
chore: updates ordering of contract functions
ckartik Dec 9, 2024
bb3327d
chore: attempt to sync mod files
ckartik Dec 9, 2024
0ae3946
chore: fixes solidty linter
ckartik Dec 9, 2024
132332e
chore: updates e2e testing
ckartik Dec 9, 2024
8028dea
feat: updates how staking is implemented
ckartik Dec 9, 2024
df498a7
chore: ensures decoding of hex from rpc
ckartik Dec 9, 2024
9708230
chore: use real encoding in stake
ckartik Dec 9, 2024
c827267
chore: fix go mod
ckartik Dec 9, 2024
0eeabbb
chore: update go mod
ckartik Dec 9, 2024
e530c48
chore: fix stake response encoding
ckartik Dec 10, 2024
466d74f
chore: fix e2e test
ckartik Dec 10, 2024
c7d8110
chore: correctly generate message and signature
ckartik Dec 10, 2024
daad545
chore: fixes lint
ckartik Dec 10, 2024
586c66d
chore: correctly format address
ckartik Dec 10, 2024
be870d4
chore: update genesis and runner
ckartik Dec 10, 2024
80485fc
chore: fix location of pre-compile
ckartik Dec 10, 2024
4243cc7
chore: bump geth
ckartik Dec 10, 2024
79a8a80
chore: only set cancun time
ckartik Dec 10, 2024
740722f
chore: correctly configure genesis file
ckartik Dec 10, 2024
d7c6e3c
chore: restrict to only bootnode build
ckartik Dec 10, 2024
f12e4bf
chore: add back signer
ckartik Dec 11, 2024
125e54b
chore: adds back all jobs
ckartik Dec 11, 2024
70f79db
feat: bumps geth and revert genesis change
ckartik Dec 11, 2024
907cf11
chore: don't put any keys in the integeration test cluster
ckartik Dec 11, 2024
9c1586b
feat: adds api call to providers
ckartik Dec 11, 2024
b97b6a8
chore: log ethaddress
ckartik Dec 11, 2024
cce110f
chore: add more logs
ckartik Dec 11, 2024
b9414b0
chore: set eth address
ckartik Dec 11, 2024
06a9cdd
fix: txn opts for BLS registration and errors (#531)
aloknerurkar Dec 12, 2024
1fa0d65
chore: bump abigen
ckartik Dec 12, 2024
704f617
fix: ci runner
mrekucci Dec 12, 2024
d139bad
fix: test
mrekucci Dec 12, 2024
cb68416
chore: test
Dec 13, 2024
48646d7
fix: remove debug
mrekucci Dec 13, 2024
ef22fee
chore: fix add stake test
Dec 13, 2024
53bab5f
chore: fix add stake test
Dec 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
ckartik marked this conversation as resolved.
Show resolved Hide resolved

// 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
Loading