Introduce a certificate generation mechanism

Hello everyone,

I want to propose another LIP for the roadmap objective “Update Lisk-BFT for interoperability”. The LIP introduces commit messages, aggregate commits and certificates. It further defines the certificate generation process.

I’m looking forward to your feedback.

Here is the complete LIP draft:

LIP: <LIP number>
Title: Introduce a certificate generation mechanism
Author: Jan Hackfeld <jan.hackfeld@lightcurve.io>
Type: Standards Track
Created: <YYYY-MM-DD>
Updated: <YYYY-MM-DD>

Abstract

This LIP defines the schema of certificates, how unsigned certificates can be computed from blocks and how they are signed using BLS signatures. We further specify commit messages, which are messages containing BLS signatures of certificates. We describe how validators create unsigned certificates for blocks they consider final and share the signatures of such certificates by gossiping commit messages via the P2P network. The certificate signatures shared via commit messages are subsequently aggregated and included in blocks. From the aggregate certificate signatures included in blocks, any node in the blockchain network can create signed certificates which can be used in cross-chain update transactions to facilitate interoperability.

Copyright

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

Motivation

The interoperability solution for the Lisk ecosystem, which is based on the paradigm of cross-chain certification, requires the consensus algorithm to generate certificates which can be used in cross-chain update transactions. The certificates should attest all information required for the cross-chain update including the state root, which allows authenticating cross-chain messages, and the validators hash, which authenticates changes of the validators and therefore signers of future certificates.

Rationale

In this section, we explain the main aspects and design choices for the specifications.

Certificates

Certificates are the key object for transferring information about the state of one chain to another chain. Every certificate is derived from a finalized block and contains the minimal subset of the block header properties required for interoperability. In this section, we explain why each of the certificate properties is required. For context, see the Introduce cross-chain update transactions LIP on how the properties are used in the validation and processing of cross-chain update transactions.

  • blockID: This property uniquely identifies which block a certificate is derived from.
  • height: This property is used to check that certificates are submitted sequentially, i.e., in increasing order of height, when included in cross-chain updates.
  • timestamp: This property is used to check the liveness requirement as part of the cross-chain update validation.
    The liveness check prevents that certificates that are older than 30 days can be submitted.
  • stateRoot: The state root is used to authenticate cross-chain messages.
  • validatorsHash: The validators hash is used to authenticate changes of the validators and the threshold required for certificate signatures.
  • aggregationBits and signature: The value of aggregationBits is a bitmap defining which validators signed the certificate and the signature property contains the corresponding aggregate BLS signature.
    These properties are used to validate that indeed the required threshold of validators signed the certificate.

Single Commits

Single commits are objects used to share BLS signatures of certificates via the P2P layer. Instead of including single commits on-chain as part of a block, we propose to share them off-chain via the P2P layer for the following main reasons:

  • Including non-aggregated BLS signatures by every validator for every block would significantly increase the block size.
  • Validating additional BLS signatures included in blocks slows down the block processing.
  • The single commits can be broadcast directly as soon as a block is considered final, allowing for faster sharing of certificate signatures as they do not first have to be included in a block.

Note that it is always possible to generate certificates only from on-chain data (block headers) and the BLS signatures shared as part of the single commit messages are not required for it. This means that data availability is not an issue for this solution.

It further is important that the protocol provides enough incentives for validators to create single commits and to share them via the P2P layer. To ensure such incentives for a DPoS blockchain, we propose to add an additional condition for unlocking votes in a separate LIP. For delegates and users to be able to unlock their tokens used for voting, this LIP will require that certificates are regularly created and therefore a sufficient threshold of active delegates has to regularly create single commits so these can be aggregated for the certificate. For a blockchain using Proof-of-Authority, the validators have their reputation at stake and we believe that this is sufficient incentive for them to participate in the certificate creation, which is required for interoperability.

Certificate Threshold

The commit messages can be viewed as a third round of BFT consensus votes for blocks, after prevotes and precommits. For proceeding from the prevote to the precommit phase for a block, the sum of finality weights of validators prevoting for the block has to be at least a certain value called prevote threshold. Similarly, for proceeding to the commit phase for a block, the phase introduced by this LIP where validators generate commit messages, the sum of finality weights of validators precommitting for the block or a descendant of it has to be at least a certain value called precommit threshold, see the Weighted Lisk-BFT consensus LIP for details. Note that if the sum of finality weights is at least the precommit threshold, then the respective block is considered final and will not be reverted.

During the commit phase an additional threshold is used, called certificate threshold. It is used in the following parts of the protocol:

  • Commit messages are aggregated to aggregate commits, which are then included in blocks. The sum of finality weights of the validators signing an aggregate commit has to be at least the certificate threshold value. Here the validators are those that are active at the height of the certificate and the finality weights are the corresponding weights at that height.
  • When submitting a certificate via a cross-chain update transaction, the weights of all signers have to be above the certificate threshold value that is currently known to the other chain. See the Introduce cross-chain update transactions LIP for details.

In the specifications below, we propose to use the same values for the certificate threshold as for the precommit threshold, both for Lisk DPoS and Lisk PoA. This means that the same threshold is required for finality as for cross-chain certification. In general, for a height h, the certificate threshold could be chosen within the following range

floor(1/3*aggregateFinalityWeight(h))+1 <= certificateThreshold(h) <= aggregateFinalityWeight(h),

where aggregateFinalityWeight(h) is the sum of finality weights at height h and certificateThreshold(h) is the certificate threshold at height h (see specification below for details). This allows for a sidechain to choose different trade-offs between satisfying certification liveness (generating certificates with enough signatures) and state transition validity (certificates attest a valid sidechain history):

  • A sidechain prioritizing certification liveness can choose a certificate threshold of floor(1/3* aggregateFinalityWeight(h))+1. This ensures that as long as floor(2/3*aggregateFinalityWeight(h))+1 of the sidechain validators in terms of finality weight are always honest, no invalid state transition will be certified. On the other hand, more than half of the sidechain validators could go permanently offline (and be replaced by other delegates in a Lisk DPoS chain, for instance) without endangering certification liveness.
  • A sidechain prioritizing state transition validity may even choose a threshold higher than the precommit threshold if they view the danger of invalid state transitions being certified as higher than contradicting blocks to be finalized. Such a chain may accept that the connection to the Lisk Mainchain is terminated in case of a certification liveness failure and would have to be re-established afterwards.

Aggregate Commits

Aggregate commits are created from single commits by aggregating the BLS signatures. The aggregate commits are then added to blocks in order to ensure that certificates can be obtained from on-chain data and they also enable adding the additional unlocking condition for DPoS blockchains. Aggregate commits are basically the same as certificates with the properties blockID, timestamp, stateRoot and validatorsHash removed as these properties are already included in previous blocks and are thus redundant.

Chain of Trust

We say that the chain of trust property is satisfied for a sequence of of certificates c1, …, ck with c1.height < c2.height < … < ck.height, if the following holds for any two consecutive certificates ci and ci+1 for i in {1, …, k-1}: If v1,…,vn are the validators authenticated by ci, w1,…,wn are the associated finality weights and t is the threshold authenticated by ci, then the aggregate signature of ci+1 must be valid with respect to the validators v1,…,vn with finality weights w1,…,wn and threshold t.

Let a1, …, ak be the sequence of all aggregate commits included in a blockchain. Every aggregate commit ai uniquely corresponds to one certificate ci. We then call c1, …, ck the certificates generated by the blockchain. We further say that the blockchain satisfies the chain of trust property, if the sequence of certificates c1, …, ck satisfies the chain of trust property.

Intuitively, the chain of trust property means that for any validator change a subset of the previous validators with total finality weights above the given threshold value has to sign a certificate. The chain of trust would, for example, be broken if all validators could change without signing a certificate authenticating this change, as certificates by the new validators would then not be accepted by other chains as these are not aware of the change. An example of a sequence of three certificates satisfying the chain of trust is shown in Figure 1. If in that example Certificate 3, which authenticates the transition from the validator set A, B, E, F back to A, B, C, D, is never generated, then the chain of trust property does not hold. In particular, if Certificate 2 is submitted to the Lisk Mainchain, then no further certificates could be submitted to the Lisk Mainchain.

Figure 1: Example of a sequence of three certificates satisfying the chain of trust.

For maintaining interoperability via certification, it is therefore crucial that the chain of trust is always maintained. Every block and also any certificate derived from a block has a validatorsHash property. This property is computed from the BLS keys of the active validators, their finality weights and the certificate threshold after the block is applied, see the New Block Header LIP for details. As the active validators are the same within one round, only blocks which are the last block of a round may have a different validatorsHash property than their parent block. This means that the chain of trust property is satisfied if the certificate generation mechanism ensures that a certificate is generated for all blocks that are the last block of a round because this ensures that there is a certificate authenticating any validator transition that happened in the chain.

The certificate generation specified in this LIP therefore generates an aggregate commit for all blocks that are the last block of a round and included in the chain. From the on-chain data the corresponding certificates can be computed and the chain of trust property is therefore satisfied. If possible, also aggregate commits for blocks that are not at the end of the round are created and added to blocks so that a certificate is created as soon as a block is finalized and it is not required to wait for the end of a round.

Specification

Terminology

  • active validator: A validator that can propose blocks and contribute to block finality by casting consensus votes for blocks. Active validators are defined per round and may change from round to round.
  • finality weight: The weight with which a validator contributes to finalizing blocks and signing certificates.
  • single commit: A message containing a BLS signature of a certificate, which corresponds to a block in the current chain. The single commit message signals that the signing validator considers the corresponding block final.
  • aggregate commit: A message containing an aggregate BLS signature of a certificate, which corresponds to a block in the current chain. It attests that all signing validators consider the corresponding block final.
  • certificate: Object that authenticates the relevant information for cross-chain communication. It uniquely corresponds to a block header of a chain and contains a subset of properties of that block header. The aggregate BLS signature provided in a certificate attests the finality of the block. Single commits and aggregate commits are messages for communicating certificate signatures.

Constants

Name Value Description
BLOCK_TIME configurable per chain
default: 10 seconds
Length of a block slot.
MESSAGE_TAG_CERTIFICATE "LSK_CE_" Message tag prepended when signing a certificate object, see LIP 0037.
COMMIT_RANGE_STORED 100 The commit messages at heights {chainMaxHeightFinalized - COMMIT_RANGE_STORED, ..., chainMaxHeightFinalized} are always stored. For smaller heights only the commits for the last block of a round are stored.

Basic Variables and Functions

Name Type Description
lastHeightRound(h) uint32 For a height h of round r as input, this function returns the height of the last block of round r. In particular, if h is the last height of a round, we have h=lastHeightRound(h).
numActiveValidators(h) uint32 Number of different active validators in the round associated to the block at height h.
validatorFinalityWeight(h, v) uint64 Finality weight of validator v at height h. This weight represents how much a validator contributes to finalizing blocks and signing certificates. It is used when computing the aggregate weight of prevotes and precommits for blocks and checking if the aggregate weight of certificate signers is sufficient.
aggregateFinalityWeight(h) uint64 Sum of finality weights of all active validators at height h.
blockPrecommitWeight[h] uint64 Aggregate weight of precommits for the block in the current chain at height h.
precommitThreshold(h) uint64 Precommit weight threshold required for considering a block final. The value can be configured, but for all heights h the following has to hold: floor(1/3*aggregateFinalityWeight(h))+1 <= precommitThreshold(h) <= aggregateFinalityWeight(h).
certificateThreshold(h) uint64 Weight threshold required for certification. This threshold is authenticated by the validatorsHash property and required by other chains for certification. The value can be configured, but for all heights h the following has to hold: floor(1/3*aggregateFinalityWeight(h))+1 <= certificateThreshold(h) <= aggregateFinalityWeight(h).
chainMaxHeightPrecommited uint32 The maximum height h such that the following holds: blockPrecommitWeights[h]>=precommitThreshold(h).
chainMaxHeightFinalized uint32 The height until which the current chain is considered final and will not be reverted. It is the maximum value of chainMaxHeightPrecommited computed so far. We therefore have chainMaxHeightPrecommited <= chainMaxHeightFinalized.
certifiedHeight uint32 The largest height for which the current chain contains an aggregate commit and therefore a certificate. By the specification of aggregate commit validity we have certifiedHeight <= chainMaxHeightPrecommited.

Except for lastHeightRound(h), numActiveValidators(h), certificateThreshold(h) and certifiedHeight, the above variables and functions were already introduced in the Weighted Lisk-BFT consensus LIP.

Lisk DPoS configuration

This section gives the specific values for the functions for Lisk DPoS.

Name Value Description
NUM_ACTIVE_DELEGATES configurable per chain
Lisk Mainnet: 101
The number of active delegates in a Lisk DPoS chain.
certificateThreshold(h) floor(2/3*NUM_ACTIVE_DELEGATES)+1 Note that this value is equal to precommitThreshold(h) for a DPoS chain.

Lisk PoA configuration

This section gives the specific values for the functions for Lisk PoA, see the section “Lisk PoA configuration” in the [Weighted Lisk-BFT consensus LIP][Weighted Lisk-BFT consensus LIP] for the notation below.

Name Value Description
certificateThreshold(h) chainProp[h].threshold Note that this value is equal to precommitThreshold(h) for a PoA chain.

Certified Height as Part of State

The certifiedHeight value is stored as uint32 in the state store. As part of the processing of a genesis block b, it is initialized with b.header.height. Afterwards, it is updated as part of the processing of the aggregate commits contained in a block header, which is described in the section Block Processing below.

Certificate

Certificates are the key object for transferring information about the state of one chain to another chain. Every certificate references a finalized block via the blockID property and contains a subset of the properties of that block, namely those properties important for interoperability.

Schema

certificateSchema = {
  "type": "object",
  "properties": {
    "blockID": {
      "dataType": "bytes",
      "fieldNumber": 1
    },
    "height": {
      "dataType": "uint32",
      "fieldNumber": 2
    },
    "timestamp": {
      "dataType": "uint32",
      "fieldNumber": 3
    },
    "stateRoot": {
      "dataType": "bytes",
      "fieldNumber": 4
    },
    "validatorsHash": {
      "dataType": "bytes",
      "fieldNumber": 5
    },
    "aggregationBits": {
      "dataType": "bytes",
      "fieldNumber": 6
    },
    "signature": {
      "dataType": "bytes",
      "fieldNumber": 7
    },
  },
  "required": [
    "blockID",
    "height",
    "timestamp",
    "stateRoot",
    "validatorsHash",
  ]
}

Creation

A certificate is always created from a finalized block in the current chain. A certificate c is computed from a block b in the following canonical way:

computeCertificateFromBlock(b):
  c = certificate object following certificateSchema
  c.blockID = block ID of b
  c.height = b.header.height
  c.timestamp = b.header.timestamp
  c.stateRoot = b.header.stateRoot
  c.validatorsHash = b.header.validatorsHash
  return c

Note that the two properties aggregationBits and signature are not required in the schema and are not set in the above function.

Signature Computation and Validation

In this section we describe how a signature of a certificate is computed and how single and aggregate signatures of certificates are validated. Let the functions signBLS(), verifyBLS() and verifyWeightedAggSig() be as defined in the LIP 0038.

The following function computes a certificate signature. The inputs are the following:

  • sk is the BLS secret key for signing,
  • networkIdentifier is the network identifier of the chain that the certificate corresponds to,
  • c is a certificate object following the schema certificateSchema defined above.
signCertificate(sk, networkIdentifier, c):
  remove the aggregationBits and signature property from c
  message = serialization of c according to LIP 0027
  tag = MESSAGE_TAG_CERTIFICATE
  return signBLS(sk, tag, networkIdentifier, message)

The following function validates that the BLS signature provided as input is a valid signature of the certificate with respect to the given public key. The inputs are the following:

  • pk is the BLS public key used for validating the signature,
  • sig is the BLS signature,
  • networkIdentifier is the network identifier of the chain that the certificate corresponds to,
  • c is a certificate object following the schema certificateSchema defined above.
verifySingleCertificateSignature(pk, sig, networkIdentifier, c):
  remove the aggregationBits and signature property from c
  message = serialization of c according to LIP 0027
  tag = MESSAGE_TAG_CERTIFICATE
  return verifyBLS(pk, tag, networkIdentifier, message, sig)

The following function validates the aggregate BLS signature which is provided as part of the certificate object. The inputs are the following:

  • keysList is an array of BLS public keys,
  • weights is an array of weights corresponding to the BLS public keys, i.e., weights[i] is the finality weight of the validator with public key keysList[i],
  • threshold is the required threshold value for the signatures,
  • networkIdentifier is the network identifier of the chain that the certificate corresponds to,
  • c is a certificate object following the schema certificateSchema defined above.
verifyAggregateCertificateSignature(keysList, weights, threshold, networkIdentifier, c):
   aggregateSignature = c.signature
   aggregationBits = c.aggregationBits
   remove the aggregationBits and signature property from c
   message = serialization of c according to LIP 0027
   tag = MESSAGE_TAG_CERTIFICATE
   return verifyWeightedAggSig(keysList, aggregationBits, aggregateSignature, tag, networkIdentifier, weights, threshold, message)

Single Commits

If for validator v a block b or a descendant of it obtains precommits of at least the precommit threshold value, then validator v creates and gossips a commit message if the validator is active at height b.height. This commit message contains a certificate signature by validator v of the certificate computed from the block b. These commit messages are collected by all nodes, aggregated and then can be added to a block by any block proposer as part of an aggregate commit defined below. In this section, we define the schema, creation and validity for single commit messages and the peer-to-peer gossip mechanism.

Schema

singleCommitSchema = {
  "type": "object",
  "properties": {
    "blockID": {
      "dataType": "bytes",
      "fieldNumber": 1
    },
    "height": {
      "dataType": "uint32",
      "fieldNumber": 2
    },
    "validatorAddress": {
      "dataType": "bytes",
      "fieldNumber": 3
    },
    "certificateSignature": {
      "dataType": "bytes",
      "fieldNumber": 4
    },
  },
  "required": [
    "blockID",
    "height",
    "validatorAddress",
    "certificateSignature",
  ]
}

Creation

The following function creates a single commit by a validator for a block. The inputs are the following:

  • b is a block,
  • v is a validator,
  • networkIdentifier is the network identifier of the chain that the block corresponds to.
createSingleCommit(b, v, networkIdentifier):
  m = single commit object following singleCommitSchema
  m.blockID = block ID of b
  m.height = b.header.height
  m.validatorAddress = address of v
  (sk,pk) = BLS key pair of validator v
  c = computeCertificateFromBlock(b)
  m.certificateSignature = signCertificate(sk, networkIdentifier, c)
  return m

If for a validator v, after applying a block the value of chainMaxHeightPrecommited increases from h1 to h2, then the following commit messages are created by the validator v and gossiped as described further below:

  • For every height h in {h1+1, h1+2, ..., h2} which is the height of a last block of a round, validator v creates a commit message for the block b at height h in its chain if validator v was active at height h.
  • If h2 is not the height of a last block of a round, v creates a commit message for the block b at height h2 in its chain if validator v was active at height h.

P2P Gossip

Every node needs to store single commit messages for the P2P gossip mechanism and to allow for the creation of aggregate commits from single commits. The data structures storing single commits are not part of the blockchain state and can be cleared when restarting the node. For the gossip mechanism it is further important to know which commit messages have been gossiped already. The specifications below therefore assume that single commits are stored in two separate data structures, but in general a different mechanism for classification such as an additional flag can also be used.

We assume that single commits are stored in the following two data structures:

  • nonGossipedCommits: This data structure stores newly received or newly created single commit messages until they are gossiped.
    The commit messages are organized by height.
  • gossipedCommits: This data structure contains commit messages that have been gossiped already.
    The commit messages are also organized by height.

Every new incoming single commit message m is processed as follows:

  1. Discard m if it is already contained in nonGossipedCommits or gossipedCommits, i.e., if there is a message m2 in nonGossipedCommits or gossipedCommits with m2.validatorAddress=m.validatorAddress and m2.blockID=m.blockID.
  2. Discard m if m.height <= certifiedHeight.
  3. Discard m if m.height is not in {chainMaxHeightPrecommited - COMMIT_RANGE_STORED, ..., chainMaxHeightPrecommited} and m.height is not the height of an end-of-round block.
  4. Discard m if m.blockID is not the ID of the block b in the current chain at height m.height.
  5. Discard m if the validator given by m.validatorAddress is not active at height m.height.
  6. Let b be the block in the current chain at height m.height, c = computeCertificateFromBlock(b), pk be the BLS public key of the validator given by m.validatorAddress and networkIdentifier be the network identifier of the current chain. Check that verifySingleCertificateSignature(pk, m.certificateSignature, networkIdentifier, c) returns VALID.
  7. If all validations above pass, m is added to nonGossipedCommits. If steps 1 through 4 above pass, but step 5 or 6 fail, the corresponding peer receives a ban score of 100 (see LIP 0004).

Let h be the height of the current tip of the chain. Commit messages in nonGossipedCommits are gossiped every BLOCK_TIME/2 seconds as follows:

  1. Cleanup the data structures nonGossipedCommits and gossipedCommits.
    1. Remove any single commit message m from nonGossipedCommits and gossipedCommits with m.height <= certifiedHeight.
    2. For every commit message m in nonGossipedCommits or gossipedCommits one of the following two conditions has to hold, otherwise it is discarded.
      • The value of m.height is in {chainMaxHeightPrecommited - COMMIT_RANGE_STORED, …, chainMaxHeightPrecommited}.
      • The value of m.height is the height of a last block in a round.
  2. Choose up to 2*numActiveValidators(h) commit messages as follows:
    1. Select any message in nonGossipedCommits or gossipedCommits with m.height < chainMaxHeightPrecommited - COMMIT_RANGE_STORED choosing messages with smaller height first.
    2. Select all newly created commit messages in nonGossipedCommits (created by a forging validator node itself) choosing the ones with the largest height first.
    3. Select among the received commit messages in nonGossipedCommits (created by other nodes) the ones with the largest height first.
  3. Gossip an array of up to 2*numActiveValidators(h) commit messages to 16 randomly chosen connected peers with at least 8 of them being outgoing peers (same parameters as block propagation).
  4. Move any gossiped commit message included in nonGossipedCommits to gossipedCommits.

Aggregate Commits

From multiple single commit messages an aggregate commit message can be computed by aggregating the BLS signatures. Subsequently, an aggregate commit can be included in a block header. In this section we describe the schema of an aggregate commit, how it is computed from single commits, how a block forger chooses an aggregate commit to include in a block and how an aggregate commit is processed as part of the block processing.

Schema

aggregateCommitSchema = {
  "type": "object",
  "properties": {
    "height": {
      "dataType": "uint32",
      "fieldNumber": 1
    },
    "aggregationBits": {
      "dataType": "bytes",
      "fieldNumber": 2
    },
    "certificateSignature": {
      "dataType": "bytes",
      "fieldNumber": 3
    },
  },
  "required": [
    "height",
    "aggregationBits",
    "certificateSignature",
  ]
}

Block Creation

In this section, we define two functions that help validators compute an appropriate aggregate commit that can be added to the block.

The following function creates an aggregate commit from the provided single commits. The input of the function is the following:

  • singleCommits: This is a set of single commit messages, all for the same block.

The function then returns an aggregate commit object following the schema aggregateCommitSchema.

aggregateSingleCommits(singleCommits):
  h = m.height for first single commit in singleCommits
  validatorKeys = list of BLS public keys of validators active at height h, ordered lexicographically
  pubKeySignaturePairs=[]
  for all m in singleCommits:
    pk = BLS public key of m.validatorAdress
    add (pk, m.certificateSignature) to pubKeySignaturePairs
  (bitmap, aggSig) = createAggSig(validatorKeys, pubKeySignaturePairs)
  aggregateCommit = object following aggregateCommitSchema
  aggregateCommit.height = h
  aggregateCommit.aggregationBits = bitmap
  aggregateCommit.certificateSignature = aggSig
  return aggregateCommit

When creating a block, the validator node creating it uses the following function to select an aggregate commit to add to the block.

selectAggregateCommit():
  nextHeight = min(lastHeightRound(certifiedHeight+1), chainMaxHeightPrecommited)
  while nextHeight > certifiedHeight:
     singleCommits = commits in nonGossipedCommits or gossipedCommits with height equal to nextHeight
     nextValidators = set of validator addresses appearing in the single commit messages in singleCommits
     aggregateFinalityWeight = 0
     for v in nextValidators:
       aggregateFinalityWeight += validatorFinalityWeight(nextHeight, v)
     if aggregateFinalityWeight >= certificationThreshold(nextHeight):
        return aggregateSingleCommits(singleCommits)
     else:
        nextHeight -= 1
  return object following aggregateCommitSchema with default values as defined in LIP 0027

Block Processing

Validation of Aggregate Commit

Apart from obeying the schema, an aggregate commit included in a block b needs to pass the following validation:

validateAggregateCommit(b):
  m = aggregate commit contained in b
  if m only contains default values:
     return True
  // Check that the height of the aggregate commit is at most chainMaxHeightPrecommited
  previousMaxHeightPrecommited = value of chainMaxHeightPrecommited before processing b
  if m.height > previousMaxHeightPrecommited:
    return False
  // Check that there is always an aggregate commit for end of round blocks
  if m.height > lastHeightRound(certifiedHeight+1):
    return False
  // Check the aggregate signature with respect to the finality weights
  // and certificate threshold
  b1 = block at height m.height
  c = computeCertificateFromBlock(b1)
  c.aggregationBits = m.aggregationBits
  c.signature = m.certificateSignature
  networkIdentifier = network identifier of the chain
  validatorKeys = array of BLS public keys of validators active at height m.height, ordered lexicographically
  weights = array of validator finality weights at height m.height such that weights[i]
            is the finality weight of the validator with the BLS key validatorKeys[i]
  threshold = certificationThreshold(m.height)
  return verifyAggregateCertificateSignature(validatorKeys, weights, threshold, networkIdentifier, c)

If the validation above fails, the peer sending the respective block receives a ban score of 100, see LIP 0004.

Processing of Aggregate Commit

An aggregate commit m included in a block b is processed by setting the certifiedHeight property of the chain to m.height.

Block Deletion

If a block b is deleted and it contains an non-empty aggregate commit m, then this is added to the aggregateCommits data structure.

Backwards Compatibility

This LIP implies a hard fork of the protocol because of the following changes:

  • The block processing includes the validation and processing of aggregate commits included in a block.
  • An endpoint for gossiping single commit messages is added to the P2P layer.
1 Like