1340 attempted to fix an issue with deserializing ValidatorSet data without the total_voting_power field being present in the serialized message.
The field is actually redundant, its value is computable as the sum of voting powers of the validators listed in the message. If the field is not present in the deserialized JSON or protobuf, its value should be recomputed. There is also an issue of trust with impications on protocol security: if we accept the total voting power value without checking it against the sum of listed validators' powers, this means validator::Set cannot be assumed correct by construction.
In the serialization bolted onto the tendermint-proto types generated for ValidatorSet, we skipped the field to match the serialization schema of misbehavior evidence, where it is not present in at least one place, in #1292.
Unfortunately, validator::Set has also had Serialize and Deserialize derived, the total_voting_power field included with no fallback, and this stricter deserialization schema has probably been in use by parties unknown. That serialization is simply derived, with no validations.
Definition of "done"
The following needs to be implemented and backed by tests:
In tendermint, the Deserialize impl for validator::Set can tolerate the absence of the total_voting_power field by computing it from validator data.
If the the total_voting_power field is present in the deserialized structure, its value is validated against the computed sum of validators' voting powers.
A special case can be made for the value of 0, which is easy to fill by default in Go and seems to occur in the wild. This is also the default deserialization for the missing field in protobuf. It should be treated like omission of the field.
In tendermint-proto, the deserialization for the generated ValidatorSet structs accepts a present total_voting_power field and ~applies the same validation as described above~ represents its value in the struct field. On serialization, the field is omitted to maintain backward compatibility; alternatively (in a technically breaking change), the field is included, but serializations of compound types with validator set fields may bypass this serialization by e.g. using a custom helper.
The sum computation is protected against integer overflows.
Validator set serializations where the total voting power exceeds the protocol-defined maximum limit (u64::MAX / 8) are rejected as invalid.
1340 attempted to fix an issue with deserializing
ValidatorSet
data without thetotal_voting_power
field being present in the serialized message.The field is actually redundant, its value is computable as the sum of voting powers of the validators listed in the message. If the field is not present in the deserialized JSON or protobuf, its value should be recomputed. There is also an issue of trust with impications on protocol security: if we accept the total voting power value without checking it against the sum of listed validators' powers, this means
validator::Set
cannot be assumed correct by construction.In the serialization bolted onto the tendermint-proto types generated for
ValidatorSet
, we skipped the field to match the serialization schema of misbehavior evidence, where it is not present in at least one place, in #1292.Unfortunately,
validator::Set
has also hadSerialize
andDeserialize
derived, thetotal_voting_power
field included with no fallback, and this stricter deserialization schema has probably been in use by parties unknown. That serialization is simply derived, with no validations.Definition of "done"
The following needs to be implemented and backed by tests:
tendermint
, theDeserialize
impl forvalidator::Set
can tolerate the absence of thetotal_voting_power
field by computing it from validator data.total_voting_power
field is present in the deserialized structure, its value is validated against the computed sum of validators' voting powers.tendermint-proto
, the deserialization for the generatedValidatorSet
structs accepts a presenttotal_voting_power
field and ~applies the same validation as described above~ represents its value in the struct field. On serialization, the field is omitted to maintain backward compatibility; alternatively (in a technically breaking change), the field is included, but serializations of compound types with validator set fields may bypass this serialization by e.g. using a custom helper.u64::MAX / 8
) are rejected as invalid.Originally posted by @mzabaluev in https://github.com/informalsystems/tendermint-rs/issues/1340#issuecomment-1708508934