/// <summary> /// Check if ``indexed_attestation`` has valid indices and signature. /// </summary> public bool IsValidIndexedAttestation(BeaconState state, IndexedAttestation indexedAttestation, Domain domain) { MiscellaneousParameters miscellaneousParameters = _miscellaneousParameterOptions.CurrentValue; IReadOnlyList<ValidatorIndex> attestingIndices = indexedAttestation.AttestingIndices; // Verify max number of indices if ((ulong) attestingIndices.Count > miscellaneousParameters.MaximumValidatorsPerCommittee) { if (_logger.IsWarn()) Log.InvalidIndexedAttestationTooMany(_logger, indexedAttestation.Data.Index, indexedAttestation.Data.Slot, attestingIndices.Count, miscellaneousParameters.MaximumValidatorsPerCommittee, null); return false; } // Verify indices are sorted if (attestingIndices.Count() > 1) { for (int index = 0; index < attestingIndices.Count() - 1; index++) { if (!(attestingIndices[index] < attestingIndices[index + 1])) { if (_logger.IsWarn()) Log.InvalidIndexedAttestationNotSorted(_logger, indexedAttestation.Data.Index, indexedAttestation.Data.Slot, 0, index, null); return false; } } } // Verify aggregate signature IEnumerable<BlsPublicKey> publicKeys = attestingIndices.Select(x => state.Validators[(int) (ulong) x].PublicKey); BlsPublicKey aggregatePublicKey = _cryptographyService.BlsAggregatePublicKeys(publicKeys); Hash32 messageHash = _cryptographyService.HashTreeRoot(indexedAttestation.Data); BlsSignature signature = indexedAttestation.Signature; bool isValid = _cryptographyService.BlsVerify(aggregatePublicKey, messageHash, signature, domain); if (!isValid) { if (_logger.IsWarn()) Log.InvalidIndexedAttestationSignature(_logger, indexedAttestation.Data.Index, indexedAttestation.Data.Slot, null); return false; } return true; }
/// <summary> /// Check if ``indexed_attestation`` has valid indices and signature. /// </summary> public bool IsValidIndexedAttestation(BeaconState state, IndexedAttestation indexedAttestation, Domain domain) { MiscellaneousParameters miscellaneousParameters = _miscellaneousParameterOptions.CurrentValue; IList <ValidatorIndex> bit0Indices = indexedAttestation.CustodyBit0Indices; IList <ValidatorIndex> bit1Indices = indexedAttestation.CustodyBit1Indices; // Verify no index has custody bit equal to 1 [to be removed in phase 1] if (bit1Indices.Count != 0) // [to be removed in phase 1] { if (_logger.IsWarn()) { _logger.LogWarning(Event.InvalidIndexedAttestation, "Invalid indexed attestion from committee {CommitteeIndex} for slot {Slot}, because it has {BitIndicesCount} bit 1 indices.", indexedAttestation.Data.Index, indexedAttestation.Data.Slot, bit1Indices.Count()); } return(false); //[to be removed in phase 1] } // Verify max number of indices int totalIndices = bit0Indices.Count + bit1Indices.Count; if ((ulong)totalIndices > miscellaneousParameters.MaximumValidatorsPerCommittee) { if (_logger.IsWarn()) { _logger.LogWarning(Event.InvalidIndexedAttestation, "Invalid indexed attestion from committee {CommitteeIndex} for slot {Slot}, because it has total indices {TotalIndices}, more than the maximum validators per committe {MaximumValidatorsPerCommittee}.", indexedAttestation.Data.Index, indexedAttestation.Data.Slot, totalIndices, miscellaneousParameters.MaximumValidatorsPerCommittee); } return(false); } // Verify index sets are disjoint IEnumerable <ValidatorIndex> intersect = bit0Indices.Intersect(bit1Indices); if (intersect.Count() != 0) { if (_logger.IsWarn()) { _logger.LogWarning(Event.InvalidIndexedAttestation, "Invalid indexed attestion from committee {CommitteeIndex} for slot {Slot}, because it has {IntersectingValidatorCount} validator indexes in common between custody bit 0 and custody bit 1.", indexedAttestation.Data.Index, indexedAttestation.Data.Slot, intersect.Count()); } return(false); } // Verify indices are sorted if (bit0Indices.Count() > 1) { for (int index = 0; index < bit0Indices.Count() - 1; index++) { if (!(bit0Indices[index] < bit0Indices[index + 1])) { if (_logger.IsWarn()) { _logger.LogWarning(Event.InvalidIndexedAttestation, "Invalid indexed attestion from committee {CommitteeIndex} for slot {Slot}, because custody bit 0 index {IndexNumber} is not sorted.", indexedAttestation.Data.Index, indexedAttestation.Data.Slot, index); } return(false); } } } if (bit1Indices.Count() > 1) { for (int index = 0; index < bit1Indices.Count() - 1; index++) { if (!(bit1Indices[index] < bit1Indices[index + 1])) { if (_logger.IsWarn()) { _logger.LogWarning(Event.InvalidIndexedAttestation, "Invalid indexed attestion from committee {CommitteeIndex} for slot {Slot}, because custody bit 1 index {IndexNumber} is not sorted.", indexedAttestation.Data.Index, indexedAttestation.Data.Slot, index); } return(false); } } } // Verify aggregate signature IEnumerable <BlsPublicKey> bit0PublicKeys = bit0Indices.Select(x => state.Validators[(int)(ulong)x].PublicKey); BlsPublicKey bit0AggregatePublicKey = _cryptographyService.BlsAggregatePublicKeys(bit0PublicKeys); IEnumerable <BlsPublicKey> bit1PublicKeys = bit1Indices.Select(x => state.Validators[(int)(ulong)x].PublicKey); BlsPublicKey bit1AggregatePublicKey = _cryptographyService.BlsAggregatePublicKeys(bit1PublicKeys); BlsPublicKey[] publicKeys = new[] { bit0AggregatePublicKey, bit1AggregatePublicKey }; AttestationDataAndCustodyBit attestationDataAndCustodyBit0 = new AttestationDataAndCustodyBit(indexedAttestation.Data, false); Hash32 messageHashBit0 = attestationDataAndCustodyBit0.HashTreeRoot(); AttestationDataAndCustodyBit attestationDataAndCustodyBit1 = new AttestationDataAndCustodyBit(indexedAttestation.Data, true); Hash32 messageHashBit1 = attestationDataAndCustodyBit1.HashTreeRoot(); Hash32[] messageHashes = new[] { messageHashBit0, messageHashBit1 }; BlsSignature signature = indexedAttestation.Signature; bool isValid = _cryptographyService.BlsVerifyMultiple(publicKeys, messageHashes, signature, domain); if (!isValid) { if (_logger.IsWarn()) { _logger.LogWarning(Event.InvalidIndexedAttestation, "Invalid indexed attestion from committee {CommitteeIndex} for slot {Slot}, because the aggregate signature does not match.", indexedAttestation.Data.Index, indexedAttestation.Data.Slot); } return(false); } return(true); }