LIP 0053: Fix verifyValidatorsUpdate function
gkoumout opened this issue · 0 comments
Motivation
In CCU params there is the field activeValidatorsUpdate
which is an object containing 3 items:
blsKeysUpdate
: An array containing the BLS keys of the new active validators (ordered in lexicographic order)bftWeightsUpdate
: An array containing all new BFT weights. This includes the BFT weights of the new validators and the BFT weights of old validators whose weight changed since the last certicificate. If a validator is excluded from the active validator set, then assume their new bft weight is 0.bftWeightsUpdateBitmap
: This bitmap encodes the correspondence between the BFT weights contained in thebftWeightsUpdate
array and the actual validator (identified by their BLS keys) that each weight corresponds to.
The correspondence works as follows (specified in getActiveValidatorsUpdate
function). Let
newBLSKeys
be the keys of the new validators (i.e., the keys of theblsKeysUpdate
array).oldBLSKeys
be the keys of the validators known in the chain until the last certificate.allBLSKeys = newBLSKeys U oldBLSKeys
, i.e., all BLS keys of previous and current validators, sorted in lexicographic order.
Then we have that the bftWeightsUpdateBitmap
contains |allBLSKeys|
bits where the i
th bit (from right to left) corresponds to the i
th entry of allBLSKeys
; if the i
th bit is set to 1 it means that the BFT weight of validator with BLS key allBLSKeys[i]
is included in the BFT weights update array. Therefore the number of 1
s in the bitmap should be equal to the size of bftWeightsUpdate
array.
Validation. Two of the checks performed in verifyValidatorsUpdate
function (part of CCU transaction verification) is that for all new validators:
- The corresponding bitmap value is always set to
1
- The bft weight is positive.
Current Specifications
In the current specifications, the 2 aforementioned checks are performed in the following loop
# All new keys must have a positive BFT weight.
for idx, blsKey in enumerate(allBLSKeys):
if blsKey in blsKeysUpdate:
# Get digit of bitmap at index idx (starting from the right).
digit = (int.from_bytes(bftWeightsUpdateBitmap, 'big') >> idx) & 1
if digit != 1:
raise Exception("New validators must have a BFT weight update.")
if bftWeightsUpdate[idx] == 0:
raise Exception("New validators must have a positive BFT weight.")
Check (1) is done correctly. Check (2) is not. The reason is that we check bftWeightsUpdate[idx]
, where idx
is the index of blsKey
in allBLSKeys
array. So we are mixing up indices of two different arrays. Note that in principle the size of allKeys
might be quite larger than the size of bftWeightUpdate
, in which case even checking bftWeightsUpdate[idx]
would give an error.
Proposed Specifications
We should be able to answer the following question: Which indices of bftWeightsUpdate
array correspond to newBLSkeys? Then for each of those indices i
, we should check that bftWeightsUpdate[i] > 0
.
Here is a proposed way to do that (assuming that check (1) has been already performed successfully):
newValitatorsIndices = [] // indices of bftWeightsUpdate array corresponding to new validators
index = 0
for idx, blsKey in enumerate(allBLSKeys):
# Get digit of bitmap at index idx (starting from the right).
digit = (int.from_bytes(bftWeightsUpdateBitmap, 'big') >> idx) & 1
if digit == 1:
if blsKey in newBLSKeys:
newValitatorsIndices.append(index)
index+=1
for i in newValidatorsIndices:
if bftWeightsUpdate[i] == 0:
raise Exception('New validators must have a positive BFT weight.')
It would be also possible to merge this check with check (1) (but it would make it a bit harder to parse), as follows:
newValitatorsIndices = [] // indices of bftWeightsUpdate array corresponding to new validators
index = 0
for idx, blsKey in enumerate(allBLSKeys):
# Get digit of bitmap at index idx (starting from the right).
digit = (int.from_bytes(bftWeightsUpdateBitmap, 'big') >> idx) & 1
if blsKey in newBLSKeys:
if digit != 1:
raise Exception("New validators must have a BFT weight update.")
newValitatorsIndices.append(index)
if digit == 1:
index+=1
for i in newValidatorsIndices:
if bftWeightsUpdate[i] == 0:
raise Exception('New validators must have a positive BFT weight.')