BLSBaseDSS
BLSBaseDSS
is an abstract contract that provides a base implementation for a DSS(Just like BaseDSS
), And also provides a simple way to integrate BLS signature schemes into your DSS. It includes the hooks which are called from the core contract whenever an operator interacts with core to register/unregister with the DSS or stake/unstake vaults from the DSS.
The internal BLS (Boneh–Lynn–Shacham) implementation allows for efficient signature aggregation and multi-signature verification. By enabling the aggregation of multiple signatures into a single compact signature, the contract reduces on-chain data size and computational overhead, making it ideal for use in threshold signatures and consensus algorithms.
Note: 1. This contract was created with reference to the article Optimized BLS multisignatures on EVM
2. This library operates on the curve BN254
Import the BlsBaseDSS
library
To get started import the BlsBaseDSS
contract and implement that in your contract.
import {BlsBaseDSS} from "./karak-onchain-sdk/BlsBaseDSS.sol";
contract SampleDSS is BlsBaseDSS{
}
Initialize the DSS:
- Call
__BlsBaseDSS_init()
with the core contract address, The desired maximum slashable percentage, The threshold percentage for operators required to accept the signature and Registration hash that the operators will sign for registration. - Sets the core address and registers the DSS with the core.
function initializeDSS(address core, uint256 maxSlashablePercentageWad) external {
// Calls the internal _init function from BaseDSS
__BlsBaseDSS_init(_core, maxSlashablePercentageWad, thresholdPercentage, REGISTRATION_MESSAGE_HASH);
emit DSSInitialized(_core, maxSlashablePercentageWad, thresholdPercentage, REGISTRATION_MESSAGE_HASH);
}
Optional Overloading:
Registration/UnregistrationHook
function registrationHook(address operator, bytes memory data) public override onlyCore {
super.registrationHook(operator, data); // Calls the BaseDSS hook logic
// Add any custom logic here
}
function unregistrationHook(address operator) public override onlyCore {
super.unregistrationHook(operator); // Calls the BaseDSS hook logic
// Add any custom logic here
}
RequestUpdateStakeHook/FinishUpdateStakeHook
function requestUpdateStakeHook(address operator, IBaseDSS.StakeUpdateRequest memory newStake)
public
override
onlyCore
{
super.requestUpdateStakeHook(operator, newStake); // Calls the BaseDSS hook logic
// Add any custom logic here
}
Jailing/Unjailing
function jailOperator(address operator) external {
_jailOperator(operator); // Uses internal function to jail the operator
}
function unjailOperator(address operator) external {
_unjailOperator(operator); // Uses internal function to unjail the operator
}
Using custom pointer for BaseDSSPtr and BaseDSSOpStatePtr
function baseDssStatePtr() internal view override returns (BaseDSSLib.State storage $) {
// return the pointer to the BaseDSSLib.State storage variable.
}
function baseDssOpStatePtr(address operator) internal pure override returns (BaseDSSOperatorLib.State storage $) {
// return the pointer to the BaseDSSOperatorLib.State storage variable.
}
Register an operator
Operators can be registered using the registerOperatorToDSS
function on core, The BLSBaseDSS
contract has implemented the registration hook offered by core, So all calls made to core for registraion end up in the BLSBaseDSS contract's registraionHook function. That function takes in the operator's address and extraData
, which includes the G1 and G2 public keys, message hash, and signature. The operator's public key is then aggregated into a master public key for all operators. The message hash is the message that is signed by the operator before registration, It needs to be passed in the extraData
. We recommed it having as a public variable on the DSS contract, which can be accessed by the operators. The off chain BLS library when provided with the message hash can be used for signing and registration of the operator.
function registrationHook(address operator, bytes memory extraData) external virtual onlyCore
The extraData
param is decoded as follows:
(BN254.G1Point memory g1Pubkey, BN254.G2Point memory g2Pubkey, bytes32 msgHash, BN254.G1Point memory sign) =
abi.decode(extraData, (BN254.G1Point, BN254.G2Point, bytes32, BN254.G1Point));
The SDK has provided a binding in our offchain sdk to convert data in the format that is required for extraData
Unregister operator
The unregistrationHook
also works in the same way registrationHook
function works. Call unregisterOperatorFromDSS
function on core, and it will get routed to the unregistraionHook
function in BLSBaseDSS automatically.
function unregistrationHook(address operator) external virtual onlyCore.
Verify a signature
The verifySignature
function allows you to verify the BLS signature of a message. This ensures that the message was signed with a valid key. Call this function with valid input parameters, And if the signature is incorrect it will panic, Otherwise it runs it with no return value.
function verifySignature(
BN254.G1Point memory g1Key,
BN254.G2Point memory g2Key,
BN254.G1Point memory sign,
bytes32 msgHash
) public view virtual
View registered operators g1 keys
You can retrieve the list of all registered operators’ g1 public keys by calling the allOperatorsG1
function.
function allOperatorsG1(State storage state) internal view returns (BN254.G1Point[] memory)
Check if operator is registered
To check if a specific operator is registered, use the isOperatorRegistered
function.
function isOperatorRegistered(State storage self, address operator) internal view returns (bool)
A reference implementation for using this abstract contract can be found here