Skip to content

Latest commit

 

History

History
652 lines (524 loc) · 31.3 KB

lip-0044.md

File metadata and controls

652 lines (524 loc) · 31.3 KB
LIP: 0044
Title: Introduce Validators module
Author: Alessandro Ricottone <[email protected]>
        Andreas Kendziorra <[email protected]>
        Rishi Mittal <[email protected]>
Discussions-To: https://research.lisk.com/t/introduce-validators-module/317
Status: Draft
Type: Standards Track
Created: 2021-08-06
Updated: 2023-02-02
Requires: 0040

Abstract

The Validators module is responsible for validating the eligibility of a validator for generating a block and the block signature. Furthermore, it maintains information about the registered validators in its module store and provides the generator list. In this LIP, we specify the properties of the Validators module, along with their serialization and default values. Furthermore, we specify the state transitions logic defined within this module, i.e. the protocol logic injected during the block processing and the functions that can be called from other modules or off-chain services.

Copyright

This LIP is licensed under the Creative Commons Zero 1.0 Universal.

Motivation

Validators in Lisk PoS and Lisk PoA chains share many common properties, like the generator and BLS keys. It is therefore desirable to handle these properties and their associated logic in a single module, the Validators module.

The Validators module handles parts of the block validation. In particular, it verifies that a validator is eligible for generating a block in a certain block slot and the validity of the block signature. Furthermore, it maintains the generator and BLS keys of all registered validators in its store and exposes functions to register new keys during a validator registration, to update the generator key, and to get the list of current validators (the generator list).

In this LIP we specify the properties, serialization, and initialization of the Validators module, as well as the protocol logic processed during a block processing and the functions exposed to other modules and to off-chain services.

Rationale

Generator Key

To be able to create block signatures, the secret key of the validator account needs to be accessible for a generator node. The most common approach is to store the encrypted passphrase that yields the secret key (or the encrypted secret key) on the node, where the encryption key is derived from a password-based key derivation function. This results in a small security risk: If an attacker is able to get the encrypted passphrase (or encrypted secret key) and the password for the encryption key derivation, the attacker has full control over the validator account. This may be a bigger concern for validators running a Lisk node on a remote data center.

To mitigate this risk, we propose to add an extra key pair to a validator account that is used for creating block signatures. Then, a generator node only requires access to the secret generator key, but not to the secret that is used for signing transactions. To increase security even further, the Validators module allows to update the generator key pair at any time. This is done by calling the setValidatorGeneratorKey function, described below. Note, however, that the current version of the PoS module does not support updating the generator key as otherwise validators could avoid report of misbehavior.

Ensuring Uniqueness of validator BLS Keys

The Validators module store maintains an account for each validator registered in the chain. In particular, it stores the BLS key associated with the validator, used to sign commits. BLS keys have to be unique across the chain, i.e. two validators are not allowed to register the same BLS key. To easily check whether a BLS key has been previously registered, we use a registered BLS keys substore, to store all the registered validator BLS keys. Store keys are set to the BLS key and the corresponding store value to the address of the validator that registered the key. This allows to check for the existence of a certain BLS key in constant time.

Specification

In this section, we specify the substores that are part of the Validators module store and the protocol logic called during the block processing. The Validators module has module name MODULE_NAME_VALIDATORS (see the table below).

Notation and Constants

We define the following constants:

Name Type Value Description
MODULE_NAME_VALIDATORS string "validators" Name of the Validators module.
SUBSTORE_PREFIX_VALIDATORS_KEYS bytes 0x0000 Substore prefix of the validators keys substore.
SUBSTORE_PREFIX_VALIDATOR_PARAMS bytes 0x4000 Substore prefix of the validator params substore.
SUBSTORE_PREFIX_BLS_KEYS bytes 0x8000 Substore prefix of the registered BLS keys substore.
INVALID_BLS_KEY bytes BLS_PUBLIC_KEY_LENGTH bytes all set to 0x00 An invalid BLS key, used as a placeholder before a valid BLS key is registered.
BLOCK_TIME integer 10 (value for the Lisk mainchain) Block time (in seconds) set in the chain configuration.
EVENT_NAME_GENERATOR_KEY_REGISTRATION string "generatorKeyRegistration" Name of the generator key registration event.
EVENT_NAME_BLS_KEY_REGISTRATION string "blsKeyRegistration" Name of the BLS key registration event.
KEY_REG_RESULT_SUCCESS uint32 0 Success result code of key registration events.
KEY_REG_RESULT_NO_VALIDATOR uint32 1 Failure result code of key registration events: address not registered as validator.
KEY_REG_RESULT_ALREADY_VALIDATOR uint32 2 Failure result code of key registration events: address already registered as validator.
KEY_REG_RESULT_DUPLICATE_BLS_KEY uint32 3 Failure result code of key registration events: BLS key already registered in the chain.
KEY_REG_RESULT_INVALID_POP uint32 4 Failure result code of key registration events: invalid proof of possession.
ADDRESS_LENGTH uint32 20 Length in bytes of type Address.
ED25519_PUBLIC_KEY_LENGTH uint32 32 Length in bytes of type PublicKeyEd25519.
BLS_PUBLIC_KEY_LENGTH uint32 48 Length in bytes of type PublicKeyBLS.
BLS_POP_LENGTH uint32 96 Length in bytes of type ProofOfPossession.

Furthermore, we use the symbol // for integer division.

Type Definition

Name Type Validation Description
Address bytes Must be of length ADDRESS_LENGTH. Address of an account.
PublicKeyEd25519 bytes Must be of length ED25519_PUBLIC_KEY_LENGTH. Used for Ed25519 public keys.
PublicKeyBLS bytes Must be of length BLS_PUBLIC_KEY_LENGTH. Used for BLS keys.
ProofOfPossession bytes Must be of length BLS_POP_LENGTH. The proof of possession associated with a BLS key.
ValidatorKeys object Must follow the validatorKeysSchema schema. An object containing the BLS key and generator key associated to a validator.
Validator object Must follow the validatorSchema schema. An object containing the address, keys and BFT weight associated to a validator.
ValidatorParams object Must follow the validatorParamsSchema schema. An object representing the validator parameters.

Validators Module Store

The key-value pairs in the module store are organized in the following substores.

Validators Keys Substore

Substore prefix, Store Key, and Store Value
  • The substore prefix is set to SUBSTORE_PREFIX_VALIDATORS_KEYS.
  • Store keys are set to ADDRESS_LENGTH bytes addresses, representing a user address.
  • Store values are set to validator keys data structures, holding the properties indicated below, serialized using the JSON schema validatorKeysSchema, presented below.
  • Notation: For the rest of this proposal let validatorKeys(address) be an entry in the validators keys substore identified by the store key address, deserialized using validatorKeysSchema schema.
JSON Schema
validatorKeysSchema = {
    "type": "object",
    "required": ["generatorKey", "blsKey"],
    "properties": {
        "generatorKey": {
            "dataType": "bytes",
            "length": ED25519_PUBLIC_KEY_LENGTH,
            "fieldNumber": 1
        },
        "blsKey": {
            "dataType": "bytes",
            "length": BLS_PUBLIC_KEY_LENGTH,
            "fieldNumber": 2
        }
    }
}
Properties

The validator account holds the generator and BLS keys of a registered validator. In this section, we describe the properties of a validator account. These properties are set by the registerValidatorKeys function, called for instance during the processing of the validator registration command or the authority registration command.

  • generatorKey: The public key whose corresponding private key is used to sign blocks generated by the validator.
  • blsKey: The validator BLS key is the public BLS key whose corresponding private key is used to sign certificates.

Validator Params Substore

Substore prefix, Store Key, and Store Value
  • The substore prefix is set to SUBSTORE_PREFIX_VALIDATOR_PARAMS.
  • The store key is set to empty bytes.
  • The store value is the serialization of an object following the JSON schema validatorParamsSchema defined below.
  • Notation: For the rest of this proposal let validatorParamsStore be the entry in the validator params substore, deserialized using validatorParamsSchema schema.
JSON Schema
validatorParamsSchema = {
    "type": "object",
    "required": [
        "precommitThreshold",
        "certificateThreshold",
        "validators"
    ],
    "properties": {
        "precommitThreshold": {
            "dataType": "uint64",
            "fieldNumber": 1
        },
        "certificateThreshold": {
            "dataType": "uint64",
            "fieldNumber": 2
        },
        "validators": {
            "type": "array",
            "fieldNumber": 3,
            "items": {
                ...validatorSchema
            }
        }
    }
}

validatorSchema = {
    "type": "object",
    "required": ["address", "bftWeight", "generatorKey", "blsKey"],
    "properties": {
        "address": {
            "dataType": "bytes",
            "length": ADDRESS_LENGTH,
            "fieldNumber": 1
        },
        "bftWeight": {
            "dataType": "uint64",
            "fieldNumber": 2
        },
        "generatorKey": {
            "dataType": "bytes",
            "length": ED25519_PUBLIC_KEY_LENGTH,
            "fieldNumber": 3
        },
        "blsKey": {
            "dataType": "bytes",
            "length": BLS_PUBLIC_KEY_LENGTH,
            "fieldNumber": 4
        }
    }
}
Properties

This substore stores the current validator parameters as set using the setValidatorParams function:

  • precommitThreshold: This property stores the current precommit threshold, see LIP 0056 for details.
  • certificateThreshold: This property stores the current certificate threshold, see LIP 0061 for details.
  • validators: This property stores an array of objects, each corresponding to a validator with the following properties:
    • address: The address of the validator.
    • bftWeight: The BFT weight of the validator, see LIP 0056 for details.
    • generatorKey: The Ed25519 public key of the validator whose corresponding private key is used to sign blocks generated by the validator.
    • blsKey: The BLS public key of the validator whose corresponding private key is used to sign certificates.

Registered BLS Keys Substore

Substore prefix, Store Key, and Store Value
  • The substore prefix is set to SUBSTORE_PREFIX_BLS_KEYS.
  • Store keys are of type PublicKeyBLS.
  • Store values are set to the addresses of the validators corresponding to the store keys, serialized using the validatorAddressSchema schema presented below.
  • Notation: For the rest of this proposal let registeredBLSKeys(blsKey) be the entry in the registered BLS keys substore identified by the store key blsKey, deserialized using validatorAddressSchema schema.
JSON Schema
validatorAddressSchema = {
    "type": "object",
    "required": ["address"],
    "properties": {
        "address": {
            "dataType": "bytes",
            "length": ADDRESS_LENGTH,
            "fieldNumber": 1
        }
    }
}
Properties

The registered BLS keys substore maintains all registered validator BLS keys, using the BLS key as store key and the address of the validator that registered the BLS key as the corresponding store value. The registered BLS keys substore is initially empty, i.e. it does not contain any key-value pairs.

Events

GeneratorKeyRegistration

This event has name = EVENT_NAME_GENERATOR_KEY_REGISTRATION. This event is emitted when a generator key is registered. The event data contains the generator key generatorKey and the result of the registration result.

Topics
  • address: The address for which the generator key has been registered.
Data
generatorKeyRegDataSchema = {
    "type": "object",
    "required": ["generatorKey", "result"],
    "properties": {
        "generatorKey": {
            "dataType": "bytes",
            "length": ED25519_PUBLIC_KEY_LENGTH,
            "fieldNumber": 1
        },
        "result": {
            "dataType": "uint32",
            "fieldNumber": 2
        }
    }
}

BLSKeyRegistration

This event has name = EVENT_NAME_BLS_KEY_REGISTRATION. This event is emitted when a BLS key is registered. The event data contains the BLS key blsKey and the result of the registration result.

Topics
  • address: The address for which the BLS key has been registered.
Data
blsKeyRegDataSchema = {
    "type": "object",
    "required": ["blsKey", "result"],
    "properties": {
        "blsKey": {
            "dataType": "bytes",
            "length": BLS_PUBLIC_KEY_LENGTH,
            "fieldNumber": 1
        },
        "proofOfPossession": {
            "dataType": "bytes",
            "length": BLS_POP_LENGTH,
            "fieldNumber": 2
        },
        "result": {
            "dataType": "uint32",
            "fieldNumber": 3
        }
    }
}

Commands

The Validators module does not specify any commands.

Protocol Logic for Other Modules

registerValidatorKeys

This function creates a new validator account in the validators keys substore. It is called as part of the validator registration and authority registration commands that are part of the PoS and PoA module. It checks that there is no account already registered for the input validatorAddress, that the input BLS key blsKey has not been registered before in the chain, and that the input proof of possession proofOfPossession for the BLS key is valid. Finally, it creates a new validator account in the validators keys substore and sets its generator key to the input generatorKey.

def registerValidatorKeys(validatorAddress: Address,
        proofOfPossession: ProofOfPossession,
        generatorKey: PublicKeyEd25519,
        blsKey: PublicKeyBLS) -> None:
    if there exists an entry in the validators keys substore with storeKey == validatorAddress:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_GENERATOR_KEY_REGISTRATION,
            data = {
                "generatorKey": generatorKey,
                "result": KEY_REG_RESULT_ALREADY_VALIDATOR
            },
            topics=[validatorAddress]
        )
        raise Exception('This address is already registered as validator.')
    if there exists an entry in the registered BLS keys substore with storeKey == blsKey:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_BLS_KEY_REGISTRATION,
            data = {
                "blsKey": blsKey,
                "proofOfPossession": proofOfPossession,
                "result": KEY_REG_RESULT_DUPLICATE_BLS_KEY
            },
            topics=[validatorAddress]
        )
        raise Exception(f'The BLS key {blsKey.hex()} has already been registered in the chain.')
    if PopVerify(blsKey, proofOfPossession) != VALID:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_BLS_KEY_REGISTRATION,
            data = {
                "blsKey": blsKey,
                "proofOfPossession": proofOfPossession,
                "result": KEY_REG_RESULT_INVALID_POP
            },
            topics=[validatorAddress]
        )
        raise Exception('Invalid proof of possession for the given BLS key.')

    validatorKeys = {
        generatorKey: generatorKey,
        blsKey: blsKey,
    }
    create an entry in the validators keys substore with storeKey = validatorAddress and storeValue = validatorKeys
    create an entry in the registered BLS keys data substore with storeKey = blsKey and storeValue = validatorAddress
    emitEvent(
        module = MODULE_NAME_VALIDATORS,
        name = EVENT_NAME_GENERATOR_KEY_REGISTRATION,
        data = {
            "generatorKey": generatorKey,
            "result": KEY_REG_RESULT_SUCCESS
        },
        topics=[validatorAddress]
    )
    emitEvent(
        module = MODULE_NAME_VALIDATORS,
        name = EVENT_NAME_BLS_KEY_REGISTRATION,
        data = {
            "blsKey": blsKey,
            "proofOfPossession": proofOfPossession,
            "result": KEY_REG_RESULT_SUCCESS
        },
        topics=[validatorAddress]
    )

The function PopVerify is part of the BLS signature scheme.

registerValidatorWithoutBLSKey

This function creates a new validator account in the validators keys substore, where the blsKey is set to INVALID_BLS_KEY. It checks that there is no account already registered for the input validatorAddress. Then, it creates a new validator account in the validators keys substore, with blsKey set to INVALID_BLS_KEY and the generator key set to the input generatorKey.

This function only exist on the mainchain. It is called as part of the genesis block processing of the PoS module for the snapshot block used for the migration from Lisk Core 3 to Lisk Core 4.

def registerValidatorWithoutBLSKey(validatorAddress: Address, generatorKey: PublicKeyEd25519) -> None:
    if there exists an entry in the validators keys substore with storeKey == validatorAddress:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_GENERATOR_KEY_REGISTRATION,
            data = {
                "generatorKey": generatorKey,
                "result": KEY_REG_RESULT_ALREADY_VALIDATOR
            },
            topics=[validatorAddress]
        )
        raise Exception('This address is already registered as validator.')

    validatorKeys = {
        generatorKey: generatorKey,
        blsKey: INVALID_BLS_KEY,
    }
    create an entry in the validators keys substore with storeKey = validatorAddress and storeValue = validatorKeys
    emitEvent(
        module = MODULE_NAME_VALIDATORS,
        name = EVENT_NAME_GENERATOR_KEY_REGISTRATION,
        data = {
            "generatorKey": generatorKey,
            "result": KEY_REG_RESULT_SUCCESS
        },
        topics=[validatorAddress]
    )

getValidatorKeys

This function is used to retrieve information about the validator account corresponding to the input address: Address. It returns validatorKeys(address). If there is no entry corresponding to address, it throws an error.

def getValidatorKeys(address: Address) -> ValidatorKeys:
    if no entry in the validators keys substore exist with storeKey == address:
        raise Exception('No validator account found for the input address.')
    return validatorKeys(address)

setValidatorBLSKey

This function sets the BLS key of a validator account. It checks that there exists a validator account registered for the input validatorAddress, that the input BLS key has not been registered before in the chain, and that the input proof of possession proofOfPossession for the BLS key is valid. Finally, it updates the BLS key of the validator account to the input blsKey.

Note: For blockchains build with the SDK v6, this function should be not be called except from the Legacy module used on the Lisk Mainchain. The reason is that changing BLS keys is not supported by the (interoperability) protocol.

def setValidatorBLSKey(validatorAddress: Address, proofOfPossession: ProofOfPossession, blsKey: PublicKeyBLS) -> None:
    if no entry in the validators keys substore exist with storeKey == validatorAddress:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_BLS_KEY_REGISTRATION,
            data = {
                "blsKey": blsKey,
                "proofOfPossession": proofOfPossession,
                "result": KEY_REG_RESULT_NO_VALIDATOR
            },
            topics=[validatorAddress]
        )
        raise Exception('This address is not registered as validator. Only validators can register a BLS key.')

    if there exists an entry in the registered BLS keys substore with storeKey == blsKey:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_BLS_KEY_REGISTRATION,
            data = {
                "blsKey": blsKey,
                "proofOfPossession": proofOfPossession,
                "result": KEY_REG_RESULT_DUPLICATE_BLS_KEY
            },
            topics=[validatorAddress]
        )
        raise Exception(f'The BLS key {blsKey.hex()} has already been registered in the chain.')

    if PopVerify(blsKey, proofOfPossession) != VALID:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_BLS_KEY_REGISTRATION,
            data = {
                "blsKey": blsKey,
                "proofOfPossession": proofOfPossession,
                "result": KEY_REG_RESULT_INVALID_POP
            },
            topics=[validatorAddress]
        )
        raise Exception('Invalid proof of possession for the given BLS key.')

    validatorKeys(validatorAddress).blsKey = blsKey
    create an entry in the registered BLS keys data substore with storeKey = blsKey and storeValue = validatorAddress
    emitEvent(
        module = MODULE_NAME_VALIDATORS,
        name = EVENT_NAME_BLS_KEY_REGISTRATION,
        data = {
            "blsKey": blsKey,
            "proofOfPossession": proofOfPossession,
            "result": KEY_REG_RESULT_SUCCESS
        },
        topics=[validatorAddress]
    )

setValidatorGeneratorKey

This function sets the generator key of a validator account. It checks that there exists a validator account registered for the input validatorAddress and then updates the generator key to the input generatorKey.

def setValidatorGeneratorKey(validatorAddress: Address, generatorKey: PublicKeyEd25519) -> None:
    if no entry in the validators keys substore exist with storeKey == validatorAddress:
        emitPersistentEvent(
            module = MODULE_NAME_VALIDATORS,
            name = EVENT_NAME_GENERATOR_KEY_REGISTRATION,
            data = {
                "generatorKey": generatorKey,
                "result": KEY_REG_RESULT_NO_VALIDATOR
            },
            topics=[validatorAddress]
        )
        raise Exception('This address is not registered as validator. Only validators can register a generator key.')

    validatorKeys(validatorAddress).generatorKey = generatorKey

    emitEvent(
        module = MODULE_NAME_VALIDATORS,
        name = EVENT_NAME_GENERATOR_KEY_REGISTRATION,
        data = {
            "generatorKey": generatorKey,
            "result": KEY_REG_RESULT_SUCCESS
        },
        topics=[validatorAddress]
    )

getAddressFromBLSKey

This function returns the address that registered the input BLS key blsKey. It checks if an entry with store key equal to blsKey exists in the registered BLS keys substore. If this is the case, it returns the stored address, else it throws an error.

def getAddressFromBLSKey(blsKey: PublicKeyBLS) -> Address:
    if no entry in the registered BLS keys substore exist with storeKey == blsKey:
        raise Exception(f'The BLS key {blsKey.hex()} has not been registered in the chain.')
    return registeredBLSKeys(blsKey)

getValidatorParams

This function returns the value stored in the Validator Params substore.

def getValidatorParams() -> ValidatorParams:
    return validatorParamsStore

getGeneratorsBetweenTimestamps

This function returns the addresses of the generators active between the two input timestamps and the number of block slots assigned to them. The slots corresponding to the input timestamps are NOT counted, only the slots in between. Notice that the input timestamps must be recent enough sucht that the active validator set for the counted slots is the one stored validatorParamsStore.validators. If the input timestamps are older, the result will most likely be incorrect. It’s the calling module’s responsibility to ensure that.

def getGeneratorsBetweenTimestamps(startTimestamp: uint32, endTimestamp: uint32) -> dict[Address, uint32]:
    if endTimestamp < startTimestamp:
        raise Exception('End timestamp cannot be smaller than start timestamp.')

    result = {}

    startSlotNumber = (startTimestamp // BLOCK_TIME) + 1
    endSlotNumber = (endTimestamp // BLOCK_TIME) - 1

    if startSlotNumber > endSlotNumber:
        return result

    totalSlots = endSlotNumber - startSlotNumber + 1
    generatorList = [validator.address for validator in validatorParamsStore.validators]

    # Quick skip to directly assign many block slots to every generator in the list.
    baseSlots = totalSlots // len(generatorList)
    if baseSlots > 0:
        totalSlots -= baseSlots * len(generatorList)
        for generatorAddress in generatorList:
            result[generatorAddress] = baseSlots

    # Assign remaining block slots.
    for slotNumber in range(startSlotNumber, startSlotNumber + totalSlots):
        slotIndex = slotNumber % len(generatorList)
        generatorAddress = generatorList[slotIndex]
        if generatorAddress in result:
            result[generatorAddress] += 1
        else:
            result[generatorAddress] = 1

    return result

setValidatorParams

This function allows to set the precommit threshold, certificate threshold, validators and associated BFT weights to be used from the next height onward. The information is not validated and simply stored in the Validators module. At the end of the function, the information is further forwarded from the application domain to the consensus domain. In the consensus domain the parameters are then verified in the function setBFTParameters, which is called during the "After Application Processing" stage of the genesis block processing or block processing.

Parameters
  • precommitThreshold: The precommit threshold value to be used from the next height onward. The value must be a 64-bit unsigned integer.
  • certificateThreshold: The certificate threshold value to be used from the next height onward. The value must be a 64-bit unsigned integer.
  • validatorList: The validators and their associated BFT weight to be used from the next height onward. The value must be an array of objects with an address property containing the 20-byte address of the validator and a bftWeight property containing the BFT weight of the validator as 64-bit unsigned integer. The order in this array determines the block generation order as the array is forwarded in the same order to the consensus domain. See also the function getGeneratorAtTimestamp in LIP 0058.
Execution
setValidatorParams(precommitThreshold: uint64, certificateThreshold: uint64, validatorList: list[object]):
    params = object of type ValidatorParams
    params.precommitThreshold = precommitThreshold
    params.certificateThreshold = certificateThreshold

    # Obtain validator keys from state store.
    newValidators = []
    for validator in validatorList:
        newValidator = object of type Validator
        newValidator.address =  validator.address
        newValidator.bftWeight = validator.bftWeight
        validatorKeys = getValidatorKeys(validator.address)
        newValidator.blsKey =  validatorKeys.blsKey
        newValidator.generatorKey =  validatorKeys.generatorKey
        newValidators.append(newValidator)
    params.validators = newValidators

    # Write new validator params to state store.
    validatorParamsStore = params
    forward params to consensus domain

Endpoints for Off-Chain Services

getValidatorKeys

This function works exactly as the function getValidatorKeys defined above.

validateBLSKey

This function checks that the input BLS key blsKey has not been registered before in the chain and that the proof of possession proofOfPossession for the BLS key is valid.

def validateBLSKey(proofOfPossession: ProofOfPossession, blsKey: PublicKeyBLS) -> bool:
    if there exists an entry in the registered BLS keys substore with storeKey == blsKey:
        return False

    if PopVerify(blsKey, proofOfPossession) != VALID:
        return False

    return True

Genesis Block Processing

The Validators module does not execute any logic during the genesis block processing.

Backwards Compatibility

This LIP defines a new module and specify its store, which in turn will become part of the state tree and will be authenticated by the state root. As such, it will induce a hardfork.

Reference Implementation

TBA