-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Invalid empty attestation signatures consensus split #1713
Comments
There are going to be places in the protocol in the future where there will be a single aggregate signature, and not a list of aggregates; for example even in phase 1 there's the light client aggregate. And so in such a situation, the degenerate case is not going to be an empty list of attestations as it is elsewhere, it will be a single aggregate signature that is empty. To avoid requiring higher-layer extra logic around that case, it seems to me reasonable to make zero a valid signature for an empty list of pubkeys and messages. This case would get actually triggered when eg. somehow no one from the light client committee shows up to get included in a block. We could add a separate rule banning empty attestations from phase 0 if desired; seems pointless to allow them. |
But leaving them in does not break anything, and introducing breaking changes at this point should imo only be done to fix serious issues. Preventing 0 attestations barely affects the scope of behavior and attack surface. A rational actor would find more valuable attestations anyway, and not want to pack the block overly full, increasing the size of the block and time to compute. As for the argument of “packing” the block with crap, the proposer could easily just put duplicate attestations (of high participation and thus more computational load) or just put a bunch of single bit attestations. |
I am not concerned about the spam as much, but about it being a weird edge case that already has clients to disagree now, and likely in the future if we leave it as-is. Being more explicit about validity conditions would be good, especially because the BLS spec itself has its own requirements (the |
That's fair. Especially with compression, an empty attestation is close to zero overhead anyway. So I'd be happy with specifying that BLS aggregate signatures with 0 pubkeys are in general allowed. |
Giving some feedback as per @protolambda's request :)
This also seems reasonable to me. I assume the intention is that only
Same for me.
Agreed. |
This would make sense and I would agree with this, being explicit here is a definite plus. I would be ok with verifying that a signature was |
There are 2 layers where invalid signatures are handled:
Currently due to testing, fuzzing (and invalid signatures in deposits?) we have a wrapper to handle valid signatures at a low-level (i.e. point on the G2 curve) and invalid signatures but that we accept because of protocol or test/fuzzing requirements. We may add in the future another case with lazy loaded signatures (i.e. we stored it in a database on shutdown and reload it in the future). type
BlsValueType* = enum
Real
OpaqueBlob
BlsValue*[N: static int, T] = object
# N = 48 for public keys and N = 96 for Signature
case kind*: BlsValueType
of Real:
blsValue*: T
of OpaqueBlob:
blob*: array[N, byte] In the low-level layer, it is very easy to check if a signature is valid or not: it's not a point on the curve and the underlying library will not parse it. For example At the high-level layer, there is a huge potential for divergence and as @nisdas said, being explicit is important as are test cases.
|
Have we reached a conclusion yet? From my point of view I think we have two options which I prefer option 1: Option 1Request a change to the BLS specs such that if n = 0 i.e. aggregate_public_key = 0
for key in public_keys:
aggregate_public_key += key Rather than specifically handling the If this were the case we would simply have
Option 2Leave BLS spec as is and handle in the Ethereum layer the case where |
I am opposed to Option 1 as it requires changes in the BLS spec (big or not, it's friction), and it doesn't solve for the real issue: clients have to get this right and not rely on libraries to work-around. If one library accepts 0 pubkeys, the other doesn't, and yet another is somewhere in between, then clients have to track those edge cases to stay in consensus. And properly implement work arounds. Option 2 is better, as it does not require anything from the BLS spec, and is super straigtforward to implement and test:
And better (IMHO) would be an Option 3, to remove one branch of decision making:
|
From this option all logic would now be in the BLS layer and none in the Ethereum layer which is appealing. However, as you mentioned you would like public keys I like this option mostly as I think the BLS spec is not implementation friendly in its current form and I would like to change it anyway.
I do see the appeal of option 3 over option 2 in avoiding changes to BLS spec. And in that |
Do you anticipate that it would be easy enough to get this included in the spec ? I would prefer to have everything under our control if possible,and the best way to do that would be to handle this in the application layer. I would prefer option 3 as it is mostly straightforward to implement and I do not see any valid case for having an attestation with 0 participants. Its effectively just junk in the block that client's will have to process and verify. |
I'm confident at this stage if we wished to change the BLS spec it would get done. Though we are looking to freeze phase 0 and BLS spec is a bottleneck so maybe it's best to go with option 3 which as you say will also give us more control. Additionally, if we go with option 3 and handle this case without interacting with BLS then it will be irrelevant if BLS makes any changes in this regard. |
Ok, I'm making a PR for option 3 (empty set of participants is invalid as attestation to go onchain). If we need it to behave differently for some unforeseen phase 1+ case, we can simply start allowing the type of attestation we need, with backwards compatibility in regards to attestations already on-chain. Also, we indeed avoid being affected by BLS changes and library issues in any way, which is great. |
What about the issue brought up by vitalik early in the thread? We know of cases where empty signatures will have to be included on chain. In such a case, I suppose we have to be explicit -- e.g. EDIT: The fix in #1780 is specific to this one issue. If we go Option 3, we'll need to address each type of potentially empty aggregate specifically. This is probably fine |
Handle empty aggregation bits as discussed in #1713
Copy-paste the discussion on #1799 https://github.com/ethereum/eth2.0-specs/pull/1799/files#r424421674
So far, we have two non-signature cases in phase 1:
My 2 centsOptional 3 is good for attestation aggregation, however, for phase 1 cases, IMO I'm inclined to apply option 2 for these cases.
def optional_fast_aggregate_verify(pubkeys, message, signature) -> bool:
if len(pubkeys) == 0:
return signature == NO_SIGNATURE
else:
return bls.FastAggregateVerify(pubkeys, message, signature) def optional_aggregate_verify(pubkeys, messages, signature) -> bool:
if len(pubkeys) == 0:
return signature == NO_SIGNATURE
else:
return bls.AggregateVerify(pubkeys, messages, signature) So it would be crystal clear that |
Yes, being explicit like that is great, and avoids weird edge cases between implementations in the BLS spec, as readers have many different assumptions about 0 pubkeys, and the BLS spec does not cover it. With phase0 I prefer option 3, as it is easier to open it back up, than restrict it (since existing signatures still would need to be valid). For phase 1 that approach looks good. |
* dup the draft 5 to allow having both at the same time * Move to a stashe folder * implement expand_message_xmd * implement the new hashToField * And done? (besides tests) * Add Eth2 suite * Change pseudo_random_bytes to uniform_bytes - https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/pull/263/files * For now use Py-ECC as reference (cfrg/draft-irtf-cfrg-hash-to-curve#265) * Add expandMessage official test suite + fix termination detection cfrg/draft-irtf-cfrg-hash-to-curve#266 * Introduce official vectors in H2C * Fix hash2curve sign0 * Pass all H2C test vectors current and futures * Setup eth v12 test vectors * Pass tests and properly handle aggregation with zero signature - ethereum/consensus-specs#1713 * Introduce a switch to toggle v0.11 or v0.12 signatures * Fix domain sepratation tag for v0.11.x * Re-skip the buggy v0.11.x aggregation tests * change default to 0.11.x
@protolambda I think it's okay to close this issue? |
@hwwhww yes, this was resolved. There are still surprises with the infinity-point pubkey/signature for implementers though, especially when changing to new BLS libraries (BLST). @paulhauner was looking into this. If you've any edge cases or things that should be documented/tested, please make a new issue and we'll look into it. |
Some context:
The scope of the bug:
Precondition: n >= 1, otherwise return INVALID.
(Thanks @hwwhww for retrieving this from the specs)empty_aggregation_bits
test case that described it as valid: https://github.com/ethereum/eth2.0-spec-tests/tree/v0.11.0/tests/minimal/phase0/operations/attestation/pyspec_tests/empty_aggregation_bits0xc00000....
signature is valid for no pubkeys.0x000...
is not.0xc000...
is.SignatureSet
, and then verifies it as a whole. This would allow the0xc000
signature to pass without pubkeys I think. But not other invalid signatures.Action points:
Labeling it as a bug, although the spec is not necessarily wrong, it is definitely a case that should get attention to avoid clients splitting chains in the future.
@djrtwo @CarlBeek
The text was updated successfully, but these errors were encountered: