Skip to content
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

initial rough commit of a work/attestation pool #46

Merged
merged 3 commits into from
Dec 26, 2018

Conversation

tersec
Copy link
Contributor

@tersec tersec commented Dec 21, 2018

Tests will come later, but built to be testable, a separate abstract data type which can be plugged in.

Data structures aren't great, but they're internal and subject to change later.

Copy link
Member

@arnetheduck arnetheduck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without updating the bitfield, this branch isn't that useful - also, pushed some bit setters in #47 that might help set the right bits, while we wait for the spec to settle.. later, we'll probably move to nim-ranges and the bit array type there


# TODO: What's the best way to union participation_bitfield as seq[byte]?
# Obvious methods, but a reusable abstraction probably exists.
# result.participation_bitfield =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, without updating this, it won't pass validation.. easiest union is just a for loop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, absolutely, both points. It's just that said for loop ideally doesn't belong here, ideally. It's what obvious methods refers to. I'll implement it and per your other comment, move to nim-ranges later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something in the style of toOpenArray(cast[ptr byte](foo), 0, foo.len - 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arnetheduck added one for-loop approach to do this. I'd prefer something more functional or at least immutable, but doing that, retaining compositionality, and not creating extra memory allocations requires more machinery than seems warranted for a hopefully temporary hack.

@mratsim will address that in an upcoming commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we can stay away from cast, that's generally a bonus

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm leaving this as a for loop + nim-ranges TODO for now. Open to alternatives.

result.attestations = initTable[AttestationData, seq[Attestation]]()

func getLookupKey(attestationData: AttestationData): array[0..31, byte] =
hash_tree_root(attestationData)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's hash_tree_final after #47 is merged, which gives you an eth2digest..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I'll wait until #47 is merged and use hash_tree_final.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another todo: garbage collection when enough slot time has passed

Noted.

signatures.add(perShardAttestation.aggregate_signature)
combine(signatures)

func getAggregatedAttestion*(pool: AttestationPool, shard: uint64) : Attestation =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might make more sense to just aggregate directly in the pool, dropping any attestations that are already part of the key - not sure if there's ever interest to keep non-aggregated attestations around.. since you can't use the same pubkey twice, my guess would be that the easiest thing for clients to do is to publish attestations with only one signature and have everyone do the aggregation locally (instead of trying to publish aggregate sigs) - later this can be refined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this design too, but that's what the comment

  #
  # Per Danny as of 2018-12-21:
  # Yeah, you can do any linear combination of signatures. but you have to
  # remember the linear combination of pubkeys that constructed
  # if you have two instances of a signature from pubkey p, then you need 2*p
  # in the group pubkey because the attestation bitfield is only 1 bit per
  # pubkey right now, attestations do not support this it could be extended to
  # support N overlaps up to N times per pubkey if we had N bits per validator
  # instead of 1
  # We are shying away from this for the time being. If there end up being
  # substantial difficulties in network layer aggregation, then adding bits to
  # aid in supporting overlaps is one potential solution

in the beginning effectively is supposed to address. The code I had and threw away did something more like what you describe -- aggregate incrementally, track who's included, all that.

Currently, I guess we wouldn't see the same pubkey twice, with just gossip pub/sub broadcasting, but that's not specified one way or the other in the protocol and explicitly still up in the air. So I'm wary of designing the attestation/work pool to rely on that kind of quirk too tightly.

But maybe it's safe enough.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, on the gossip network I'd expect only single-signature attestations to travel (for now) - so it probably makes sense here that the code checks any incoming attetation for overlap and that's it - if you save every attestation and try to find the best coverage every time a new one comes in, that's a lot of work as well..

point is, I'm a validator, I know which block I'm responsible for, so I start collecting data (shard state, beacon state, pow state), and when the moment is right, I publish an attestation to the latest data that I know, signed only by me, without considering the attestation pool at all.. that's the simplest correct strategy - later maybe we can play with others.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, wonder what the economy of that is - if everyone drops attestations that cover more than the client already has, it means you're aligned to send attestations with few signatures in the hope of getting them accepted in the pool - the counterbalance is that when you're a proposer, you want to have as many attestations as possible.. hm.

anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this seems reasonable, and works better in other ways. Done in new branch.

The cryptoeconomic/game theory/etc would need to get better thought out, but that's not my initial goal.

Copy link
Contributor

@mratsim mratsim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type and/or init looks wrong

# It would be better to combine these incrementally, pending above.
# This back-loads the work.
AttestationPool* = object
attestations: Table[uint64, Table[array[32, byte], seq[Attestation]]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my need this can just be Table[AttesterIdx, ref seq[AttestationData]].

THe uint64 should be aliased to make the purpose clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When consolidating/combining incrementally, this particular organization ends up making less sense regardless.

# per shard

proc init*(T: type AttestationPool): T =
result.attestations = initTable[AttestationData, seq[Attestation]]()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent with type declaration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, fixed in new branch, and added smoke test to keep it working. Also simplified type at cost of retrieval, but good tradeoff right now.

func getLookupKey(attestationData: AttestationData): array[0..31, byte] =
hash_tree_root(attestationData)

proc addAttestation*(pool: var AttestationPool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be called add to avoid attestationPool.addAttestation and be consistent with nim seqs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


# TODO: What's the best way to union participation_bitfield as seq[byte]?
# Obvious methods, but a reusable abstraction probably exists.
# result.participation_bitfield =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something in the style of toOpenArray(cast[ptr byte](foo), 0, foo.len - 1)

@arnetheduck
Copy link
Member

another todo: garbage collection when enough slot time has passed

@mratsim mratsim merged commit 6864451 into master Dec 26, 2018
@tersec tersec deleted the InitialAttestationPool branch December 28, 2018 17:10
etan-status pushed a commit that referenced this pull request May 12, 2022
* Move initAPI to newDispatcher() call.
etan-status pushed a commit that referenced this pull request May 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants