Bugfix: Avoid the aggregation of duplicate signatures when creating sync committee contributions
This commit is contained in:
parent
74f03ddadb
commit
29e5700838
|
@ -131,6 +131,7 @@ func computeAggregateSig(votes: seq[TrustedSyncCommitteeMsg],
|
||||||
if vote.subcommitteeIndex != subcommitteeIndex:
|
if vote.subcommitteeIndex != subcommitteeIndex:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not contribution.aggregation_bits[vote.positionInCommittee]:
|
||||||
if not initialized:
|
if not initialized:
|
||||||
initialized = true
|
initialized = true
|
||||||
aggregateSig.init(vote.signature)
|
aggregateSig.init(vote.signature)
|
||||||
|
@ -156,7 +157,8 @@ func produceContribution*(
|
||||||
outContribution.subcommittee_index = subcommitteeIndex.asUInt64
|
outContribution.subcommittee_index = subcommitteeIndex.asUInt64
|
||||||
try:
|
try:
|
||||||
computeAggregateSig(pool.syncMessages[headRoot],
|
computeAggregateSig(pool.syncMessages[headRoot],
|
||||||
subcommitteeIndex, outContribution)
|
subcommitteeIndex,
|
||||||
|
outContribution)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raiseAssert "We have checked for the key upfront"
|
raiseAssert "We have checked for the key upfront"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -853,19 +853,17 @@ proc validateSignedContributionAndProof*(
|
||||||
var
|
var
|
||||||
committeeAggKey {.noInit.}: AggregatePublicKey
|
committeeAggKey {.noInit.}: AggregatePublicKey
|
||||||
initialized = false
|
initialized = false
|
||||||
mixedKeys = 0
|
syncCommitteeSlot = msg.message.contribution.slot + 1
|
||||||
|
|
||||||
for validatorPubKey in dag.syncCommitteeParticipants(
|
for validatorPubKey in dag.syncCommitteeParticipants(
|
||||||
msg.message.contribution.slot + 1,
|
syncCommitteeSlot,
|
||||||
committeeIdx,
|
committeeIdx,
|
||||||
msg.message.contribution.aggregation_bits):
|
msg.message.contribution.aggregation_bits):
|
||||||
let validatorPubKey = validatorPubKey.loadWithCache.get
|
let validatorPubKey = validatorPubKey.loadWithCache.get
|
||||||
if not initialized:
|
if not initialized:
|
||||||
initialized = true
|
initialized = true
|
||||||
committeeAggKey.init(validatorPubKey)
|
committeeAggKey.init(validatorPubKey)
|
||||||
inc mixedKeys
|
|
||||||
else:
|
else:
|
||||||
inc mixedKeys
|
|
||||||
committeeAggKey.aggregate(validatorPubKey)
|
committeeAggKey.aggregate(validatorPubKey)
|
||||||
|
|
||||||
if not initialized:
|
if not initialized:
|
||||||
|
@ -883,10 +881,10 @@ proc validateSignedContributionAndProof*(
|
||||||
epoch, msg.message.contribution.beacon_block_root, fork,
|
epoch, msg.message.contribution.beacon_block_root, fork,
|
||||||
genesisValidatorsRoot, committeeAggKey.finish, cookedSignature.get):
|
genesisValidatorsRoot, committeeAggKey.finish, cookedSignature.get):
|
||||||
debug "failing_sync_contribution",
|
debug "failing_sync_contribution",
|
||||||
slot = msg.message.contribution.slot + 1,
|
blk = msg.message.contribution.beacon_block_root,
|
||||||
|
slot = syncCommitteeSlot,
|
||||||
subnet = committeeIdx,
|
subnet = committeeIdx,
|
||||||
participants = $(msg.message.contribution.aggregation_bits),
|
participants = $(msg.message.contribution.aggregation_bits)
|
||||||
mixedKeys
|
|
||||||
|
|
||||||
return errReject(
|
return errReject(
|
||||||
"SignedContributionAndProof: aggregate signature fails to verify")
|
"SignedContributionAndProof: aggregate signature fails to verify")
|
||||||
|
|
|
@ -188,6 +188,7 @@ template bytes*(x: BitList): auto = seq[byte](x)
|
||||||
template `[]`*(x: BitList, idx: auto): auto = BitSeq(x)[idx]
|
template `[]`*(x: BitList, idx: auto): auto = BitSeq(x)[idx]
|
||||||
template `[]=`*(x: var BitList, idx: auto, val: bool) = BitSeq(x)[idx] = val
|
template `[]=`*(x: var BitList, idx: auto, val: bool) = BitSeq(x)[idx] = val
|
||||||
template `==`*(a, b: BitList): bool = BitSeq(a) == BitSeq(b)
|
template `==`*(a, b: BitList): bool = BitSeq(a) == BitSeq(b)
|
||||||
|
template getBit*(x: var BitList, idx: Natural): bool = getBit(BitSeq(x), idx)
|
||||||
template setBit*(x: var BitList, idx: Natural) = setBit(BitSeq(x), idx)
|
template setBit*(x: var BitList, idx: Natural) = setBit(BitSeq(x), idx)
|
||||||
template clearBit*(x: var BitList, idx: Natural) = clearBit(BitSeq(x), idx)
|
template clearBit*(x: var BitList, idx: Natural) = clearBit(BitSeq(x), idx)
|
||||||
template overlaps*(a, b: BitList): bool = overlaps(BitSeq(a), BitSeq(b))
|
template overlaps*(a, b: BitList): bool = overlaps(BitSeq(a), BitSeq(b))
|
||||||
|
|
|
@ -708,6 +708,7 @@ proc signAndSendContribution(node: BeaconNode,
|
||||||
|
|
||||||
# Failures logged in sendSyncCommitteeContribution
|
# Failures logged in sendSyncCommitteeContribution
|
||||||
discard await node.sendSyncCommitteeContribution(msg[], false)
|
discard await node.sendSyncCommitteeContribution(msg[], false)
|
||||||
|
notice "Contribution sent", contribution = shortLog(msg[])
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
# An error could happen here when the signature task fails - we must
|
# An error could happen here when the signature task fails - we must
|
||||||
# not leak the exception because this is an asyncSpawn task
|
# not leak the exception because this is an asyncSpawn task
|
||||||
|
@ -751,6 +752,7 @@ proc handleSyncCommitteeContributions(node: BeaconNode,
|
||||||
count = selectionProofs.len, time
|
count = selectionProofs.len, time
|
||||||
|
|
||||||
var contributionsSent = 0
|
var contributionsSent = 0
|
||||||
|
|
||||||
time = timeIt:
|
time = timeIt:
|
||||||
for i in 0 ..< selectionProofs.len:
|
for i in 0 ..< selectionProofs.len:
|
||||||
if not selectionProofs[i].completed:
|
if not selectionProofs[i].completed:
|
||||||
|
@ -762,7 +764,10 @@ proc handleSyncCommitteeContributions(node: BeaconNode,
|
||||||
|
|
||||||
var contribution: SyncCommitteeContribution
|
var contribution: SyncCommitteeContribution
|
||||||
let contributionWasProduced = node.syncCommitteeMsgPool[].produceContribution(
|
let contributionWasProduced = node.syncCommitteeMsgPool[].produceContribution(
|
||||||
slot, head.root, candidateAggregators[i].subcommitteeIdx, contribution)
|
slot,
|
||||||
|
head.root,
|
||||||
|
candidateAggregators[i].subcommitteeIdx,
|
||||||
|
contribution)
|
||||||
|
|
||||||
if contributionWasProduced:
|
if contributionWasProduced:
|
||||||
asyncSpawn signAndSendContribution(
|
asyncSpawn signAndSendContribution(
|
||||||
|
@ -770,7 +775,6 @@ proc handleSyncCommitteeContributions(node: BeaconNode,
|
||||||
candidateAggregators[i].validator,
|
candidateAggregators[i].validator,
|
||||||
contribution,
|
contribution,
|
||||||
selectionProof)
|
selectionProof)
|
||||||
notice "Contribution sent", contribution = shortLog(contribution)
|
|
||||||
inc contributionsSent
|
inc contributionsSent
|
||||||
else:
|
else:
|
||||||
debug "Failure to produce contribution",
|
debug "Failure to produce contribution",
|
||||||
|
|
|
@ -207,9 +207,10 @@ suite "Gossip validation - Extra": # Not based on preset config
|
||||||
dag.updateHead(added[], quarantine)
|
dag.updateHead(added[], quarantine)
|
||||||
dag
|
dag
|
||||||
state = assignClone(dag.headState.data.altairData)
|
state = assignClone(dag.headState.data.altairData)
|
||||||
|
slot = state[].data.slot
|
||||||
|
|
||||||
subcommitteeIdx = 0.SyncSubcommitteeIndex
|
subcommitteeIdx = 0.SyncSubcommitteeIndex
|
||||||
syncCommittee = @(dag.syncCommitteeParticipants(state[].data.slot))
|
syncCommittee = @(dag.syncCommitteeParticipants(slot))
|
||||||
subcommittee = toSeq(syncCommittee.syncSubcommittee(subcommitteeIdx))
|
subcommittee = toSeq(syncCommittee.syncSubcommittee(subcommitteeIdx))
|
||||||
|
|
||||||
pubkey = subcommittee[0]
|
pubkey = subcommittee[0]
|
||||||
|
@ -220,13 +221,13 @@ suite "Gossip validation - Extra": # Not based on preset config
|
||||||
validator = AttachedValidator(pubKey: pubkey,
|
validator = AttachedValidator(pubKey: pubkey,
|
||||||
kind: ValidatorKind.Local, data: privateItem, index: some(index))
|
kind: ValidatorKind.Local, data: privateItem, index: some(index))
|
||||||
msg = waitFor signSyncCommitteeMessage(
|
msg = waitFor signSyncCommitteeMessage(
|
||||||
validator, state[].data.slot,
|
validator, slot,
|
||||||
state[].data.fork, state[].data.genesis_validators_root, state[].root)
|
state[].data.fork, state[].data.genesis_validators_root, state[].root)
|
||||||
|
|
||||||
syncCommitteeMsgPool = newClone(SyncCommitteeMsgPool.init())
|
syncCommitteeMsgPool = newClone(SyncCommitteeMsgPool.init())
|
||||||
res = validateSyncCommitteeMessage(
|
res = validateSyncCommitteeMessage(
|
||||||
dag, syncCommitteeMsgPool[], msg, subcommitteeIdx,
|
dag, syncCommitteeMsgPool[], msg, subcommitteeIdx,
|
||||||
state[].data.slot.toBeaconTime(), true)
|
slot.toBeaconTime(), true)
|
||||||
(positions, cookedSig) = res.get()
|
(positions, cookedSig) = res.get()
|
||||||
|
|
||||||
syncCommitteeMsgPool[].addSyncCommitteeMsg(
|
syncCommitteeMsgPool[].addSyncCommitteeMsg(
|
||||||
|
@ -241,7 +242,7 @@ suite "Gossip validation - Extra": # Not based on preset config
|
||||||
contribution = block:
|
contribution = block:
|
||||||
var contribution = (ref SignedContributionAndProof)()
|
var contribution = (ref SignedContributionAndProof)()
|
||||||
check: syncCommitteeMsgPool[].produceContribution(
|
check: syncCommitteeMsgPool[].produceContribution(
|
||||||
state[].data.slot, state[].root, subcommitteeIdx,
|
slot, state[].root, subcommitteeIdx,
|
||||||
contribution.message.contribution)
|
contribution.message.contribution)
|
||||||
syncCommitteeMsgPool[].addSyncContribution(
|
syncCommitteeMsgPool[].addSyncContribution(
|
||||||
contribution[], contribution.message.contribution.signature.load.get)
|
contribution[], contribution.message.contribution.signature.load.get)
|
||||||
|
|
|
@ -81,6 +81,9 @@ suite "Sync committee pool":
|
||||||
pool.addSyncCommitteeMsg(root2Slot, root1, 3, sig3, subcommittee2, [7'u64])
|
pool.addSyncCommitteeMsg(root2Slot, root1, 3, sig3, subcommittee2, [7'u64])
|
||||||
pool.addSyncCommitteeMsg(root2Slot, root2, 4, sig4, subcommittee2, [3'u64])
|
pool.addSyncCommitteeMsg(root2Slot, root2, 4, sig4, subcommittee2, [3'u64])
|
||||||
|
|
||||||
|
# Insert a duplicate message (this should be handled gracefully)
|
||||||
|
pool.addSyncCommitteeMsg(root1Slot, root1, 1, sig1, subcommittee1, [1'u64])
|
||||||
|
|
||||||
# Producing contributions
|
# Producing contributions
|
||||||
#
|
#
|
||||||
block:
|
block:
|
||||||
|
|
Loading…
Reference in New Issue