-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add Cell Dissemination via Partial Message Specification #4558
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
base: master
Are you sure you want to change the base?
Conversation
specs/fulu/das-core.md
Outdated
#### `PartialDataColumnSidecar` | ||
|
||
```python | ||
class PartialDataColumnSidecar(Container): | ||
# Only provided if the index can not be inferred from the Gossipsub topic | ||
index: ColumnIndex | None | ||
# Encoded the same as an IHAVE bitmap | ||
cells_present_bitmap: ByteVector[NUMBER_OF_COLUMNS/8] # ceiling if NUMBER_OF_COLUMNS is not divisible by 8 | ||
column: List[Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK] | ||
kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK] | ||
# The following are only provided on eager pushes. | ||
kzg_commitments: None | List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] | ||
signed_block_header: None | SignedBeaconBlockHeader | ||
kzg_commitments_inclusion_proof: None | Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH] | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's three possible ways to go with this:
- Add an Optional SSZ type, which we are currently missing (see https://ethereum-magicians.org/t/eip-6475-ssz-optional/12891)
class PartialDataColumnSidecar(Container):
index: ColumnIndex
cells_bitmap: Bitlist[MAX_BLOB_COMMITMENTS_PER_BLOCK]
cells: List[Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_commitments: Optional[List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]]
signed_block_header: Optional[SignedBeaconBlockHeader]
kzg_commitments_inclusion_proof: Optional[Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH]
- Separate the data part from the kzg commitments, header and proof part
class PartialDataColumnSidecar(Container):
index: ColumnIndex
cells_bitmap: Bitlist[MAX_BLOB_COMMITMENTS_PER_BLOCK]
cells: List[Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
class KZGCommitmentsSidecar(Container):
kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
signed_block_header: SignedBeaconBlockHeader
kzg_commitments_inclusion_proof: Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH]
- Have two different types for a column with and without committments (hopefully with nicer names)
class PartialDataColumnSidecar(Container):
index: ColumnIndex
cells_bitmap: Bitlist[MAX_BLOB_COMMITMENTS_PER_BLOCK]
cells: List[Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
class PartialDataColumnSidecarWithCommitments(Container):
index: ColumnIndex
cells_bitmap: Bitlist[MAX_BLOB_COMMITMENTS_PER_BLOCK]
cells: List[Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
signed_block_header: SignedBeaconBlockHeader
kzg_commitments_inclusion_proof: Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH]
Also:
- changed
ByteVector
toBitlist
(but could also beBitvector
) NUMBER_OF_COLUMNS
isn't the length/max-length of a column, should beMAX_BLOB_COMMITMENTS_PER_BLOCK
- I'd just leave the
column_index
in all the time, 8 bytes doesn't seem worth worrying about imho
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preference for 1, as the having two containers makes things a bit more annoying as you need to then define when one or the other (or both) are transmitted.
I'd just leave the column_index in all the time, 8 bytes doesn't seem worth worrying about imho
The only reason we would need it is when the subnet != column index, right? 8 bytes is small, but a couple bytes here and a couple bytes there and soon we have a whole packet of redundant data. My preference might be to just remove this field and leave it as future work to define how to derive a column index when the subnet count != column count.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 may seem better but the other options move the complexity from the type system (and the requisite push to add a new concept to the ssz spec) to other layers of the stack
that said, we have have Option as a non-canonical type for some time now, and it may be time to go ahead and add formal support for it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including the commitments here is an optimization. We could rely on just receiving them in the block body.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something worth paying attention to here is also how DataColumnSidedcar
evolves in the gloas spec (though these changes will likely pre-date gloas). At the moment it seems like header + inclusion proof will be removed in favor of beacon_block_root
only, requiring the beacon block to be seen in advance (though this still will not contain the kzg commitments), which is somewhat natural with epbs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do the same thing here, requiring the block (or a full sidecar) to have been seen before receiving a partial column (and excluding the commitments, since at this point they're still in the beacon block). Similar to what you did below but with beacon_block_root
instead of the whole signed_beacon_block_header
class PartialDataColumnSidecar(Container):
index: ColumnIndex
cells_present_bitmap: Bitlist[MAX_BLOB_COMMITMENTS_PER_BLOCK]
partial_column: List[Cell, MAX_BLOB_COMMITMENTS_PER_BLOCK]
kzg_proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
beacon_block_root: Root
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I am referring to re: gloas specs btw https://github.com/ethereum/consensus-specs/pull/4527/files
2e1e7f1
to
6113cf2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this mechanistically introduces cell-level deltas, how they precisely integrate into existing PeerDAS processes isn't very clear. How about a section covering the end-to-end behaviour:
- When nodes push
DataColumnSidecar
s - When nodes send partial IWANTs
- When nodes send partial IHAVEs
- When nodes send
PartialDataColumnSidecar
s - Dependency on
getBlobsV3
specs/fulu/p2p-interface.md
Outdated
To address this tradeoff a Client may choose to eagerly push some (or all) of | ||
the cells it has. Clients SHOULD only do this when they are reasonably confident | ||
that a peer does not have the provided cells. For example, a proposer including | ||
private blobs SHOULD eagerly push the cells corresponding to the private blobs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless we have a backpressure mechanism where a peer can ask an aggressively pushy peer to back off, and downscore if they don't follow through, this can be gamed to spam the peer under the pretence of "noble cause."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how this is worse than the current status quo. Currently we always eagerly push a whole column unless we receive an IDONTWANT.
This is the same except an IDONTWANT is replaced by an updated partial IHAVE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a subtle difference, though. Before, our eager push was guaranteed to be useful if the peer was lacking any blob. Now, we can push data that's knowingly useless (e.g. an attacker always pushes the cells they know their peer already has, confidently learnt from EL tx announcements from the same IP). At 72 blobs, for a supernode that's only lacking 1 blob, this path exposes it to receiving 18MiB x D of knowingly redundant data, when all it needed was 256KiB (1 row x 128 columns x 2KiB cell size).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explanation. You're saying that the status quo at least guarantees some useful information, even if it's a single cell, while partial messages can be used to only transmit redundant information.
I'm not yet convinced this is an interesting attack though. The cost to the attacker is 1:1 relative to the cost of the receiver. There are easier ways to transmit useless information to the peer at a 1:1 cost. Even in your example of 72 blobs with one missing blob, status quo would still receive (18MiB - 256KiB) x D redundant information.
specs/fulu/p2p-interface.md
Outdated
``` | ||
|
||
For example, if the peer wanted cells at positions 0,3,9, the corresponding | ||
IWANT bitmap would be: `0000 1001 000 0010`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's n
in the example?
000 -> 0000 , and also the byte order looks wrong, shouldn't we have MSB-first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the 000 -> 0000 correction.
n can be anything from 9 to 16 in this case, but I should just say 12 to be clear in the example.
The byte order looks wrong, shouldn't we have MSB-first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, still it might worth clarifying in what byte order it goes on wire.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually isn't that defined by the SSZ encoding for Bitlist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point (but I don't know :-) )
8be8bff
to
829184f
Compare
829184f
to
8c70c5b
Compare
8c70c5b
to
c88bab2
Compare
In draft until the libp2p/specs work is merged and we have multiple implementations of this. Also requires TODOs to be addressed.
This PR specifies how consensus clients use Gossipsub's Partial Messages to disseminate cells rather than only full columns.
More context in this ethresearch post.