Skip to content

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