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

PeerDAS: Add raw functions like eip4844 #282

Merged
merged 1 commit into from
Jan 23, 2025

Conversation

hangleang
Copy link
Contributor

@hangleang hangleang commented Jan 17, 2025

This pull request will add raw functions into DAS trait, like eip4844 in this commit. The idea is to allow easier integration into its domain without needing to transform unit type into underlying field element type (e.g. converting Cell into field elements), which adding more complexity into the integration. The WIP integration into grandine can be found here.

NOTE: the current changes has a few bugs (potential issues have been marked as well), which give the following error when test in the integration:

source slice length (8192) does not match destination slice length (0)

the error is pointing to kzg/src/das.rs, which I'm not quite familiar with the logic here.

UPDATED: the bug has been fixed, after creating the issue :)

cc @ArtiomTr, please help review the changes and give some suggestions

@hangleang hangleang force-pushed the das-integration branch 2 times, most recently from 68e0697 to 25a176c Compare January 17, 2025 11:03
@hangleang hangleang marked this pull request as ready for review January 17, 2025 11:08
Copy link
Contributor

@ArtiomTr ArtiomTr left a comment

Choose a reason for hiding this comment

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

Hello 👋,

kzg/src/das.rs file is for abstract das implementation, with configurable trusted setup size and cell size. So for ethereum-specific parameters it is better to put code into kzg/src/eth/... module, maybe kzg/src/eth/eip_7594?

Probably you can do something like:

// kzg/src/eth/eip_7594.rs file

pub fn recover_cells_and_kzg_proofs_raw::<B: EcBackend>(das: &impl DAS<B>, cell_indices: &[usize], cells: &[[u8; BYTES_PER_CELL]]) -> Result<CellsKzgProofs, String> {
   // ...
}

I believe, then you can use this function like so:

recover_cells_and_kzg_proofs_raw::<BlstBackend>(settings(), cell_indices, cells)

Few things to keep in mind:

  • our implementation saves cells in flat slice, so you probably want to group frs / flatten your input. Because of fixed-size slices, memory layout actually stays the same, so flattening should be zero-cost
  • c-kzg-4844 in recover_cells_and_kzg_proofs function allows to recover only cells, without proofs. Same in compute_cells_and_kzg_proofs, you can select - compute only cells, only proofs, or both. However, rust-eth-kzg doesn't provide such functionality. So we chose to implement as c-kzg does this, but they're not very convenient, as you need to pre-allocate output slices :). So choose what better works for you, just keep in mind that you can skip some computations.

If you need some more help, just ping me 😄

kzg/src/das.rs Outdated
Comment on lines 234 to 235
let mut recovered_cells: Vec<B::Fr> = vec![];
let mut recovered_proofs: Vec<B::G1> = vec![];
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to pre-allocate space for recovered_cells and recovered_proofs. You can look at the examples in test cases:

let mut recv_cells =
vec![B::Fr::default(); eth::CELLS_PER_EXT_BLOB * eth::FIELD_ELEMENTS_PER_CELL];
let mut recv_proofs = vec![B::G1::default(); eth::CELLS_PER_EXT_BLOB];

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, this is already fixed :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ohh, yeah. I already update those with the force-commit

@hangleang
Copy link
Contributor Author

@ArtiomTr you are right, I'll update according to your suggestions

@hangleang
Copy link
Contributor Author

Hello @ArtiomTr

I've been updated to follow your suggestions to move peerdas-specific functions into its own module, outside of das-abstract trait, please kindly review the changes :)

Copy link
Contributor

@ArtiomTr ArtiomTr left a comment

Choose a reason for hiding this comment

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

Looks good, just minor nitpicks 👍

Comment on lines 36 to 41
let _ = das.recover_cells_and_kzg_proofs(
&mut recovered_cells,
Some(&mut recovered_proofs),
cell_indices,
&cells,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe there should be ?, because you're currently ignoring error, if it occurs in recover_cells_and_kzg_proofs:

Suggested change
let _ = das.recover_cells_and_kzg_proofs(
&mut recovered_cells,
Some(&mut recovered_proofs),
cell_indices,
&cells,
);
das.recover_cells_and_kzg_proofs(
&mut recovered_cells,
Some(&mut recovered_proofs),
cell_indices,
&cells,
)?;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahh! good eyes

Comment on lines 65 to 69
let _ = das.compute_cells_and_kzg_proofs(
Some(&mut recovered_cells),
Some(&mut recovered_proofs),
&blob,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same issue as above, with recover_cells_and_kzg_proofs

Comment on lines 28 to 31
let nested_cells = cfg_iter!(cells)
.map(|bytes| bytes_to_cell::<B>(bytes))
.collect::<Result<Vec<Vec<_>>, _>>()?;
let cells = nested_cells.into_iter().flatten().collect::<Vec<_>>();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure, but looks like it would be better to do flatten first, and then conversion - that way you can avoid heap allocation (collecting each cell to vector), and also flattening will be zero-cost, as memory layout is not changed:

Suggested change
let nested_cells = cfg_iter!(cells)
.map(|bytes| bytes_to_cell::<B>(bytes))
.collect::<Result<Vec<Vec<_>>, _>>()?;
let cells = nested_cells.into_iter().flatten().collect::<Vec<_>>();
let cells = cfg_iter!(cells.flatten()).map(B::Fr::from_bytes).collect::<Result<Vec<_>, String>>()?;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

flatten is not available in the slice array, which should be the better way?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh sorry, I mean as_flattened

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but, even if we do so, the bytes size will not fit into the field element, we need to chunk it first anyway

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, you're right, too rushed with this suggestion. It should be:

Suggested change
let nested_cells = cfg_iter!(cells)
.map(|bytes| bytes_to_cell::<B>(bytes))
.collect::<Result<Vec<Vec<_>>, _>>()?;
let cells = nested_cells.into_iter().flatten().collect::<Vec<_>>();
let cells = cfg_iter!(cells.as_flattened().chunks(BYTES_PER_FIELD_ELEMENT)).map(B::Fr::from_bytes).collect::<Result<Vec<_>, _>>()?;

The point was that we can avoid unnecessary heap allocations, and keep only one

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 could not use cfg_iter on chunks, so remove on cells for now, will improve it later

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah right, chunks is already an iterator. There is par_chunks in rayon, but I believe it wouldn't give much performance improvement

Comment on lines 91 to 94
let nested_cells = cfg_iter!(cells)
.map(|bytes| bytes_to_cell::<B>(bytes))
.collect::<Result<Vec<Vec<_>>, _>>()?;
let cells = nested_cells.into_iter().flatten().collect::<Vec<_>>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as in recover_cells_and_kzg_proofs_raw, heap allocations and flattening could be simplified

Comment on lines 122 to 111
let chunk_size = FIELD_ELEMENTS_PER_CELL;
if bytes.len() % chunk_size != 0 {
return Err(format!("Invalid byte length. got {}", bytes.len(),));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can just check here, if slice size is equal to FIELD_ELEMENTS_PER_EXT_BLOB

Comment on lines 135 to 140
if end > BYTES_PER_CELL {
return Err(format!(
"Invalid cell byte length. Expected {} got {}",
BYTES_PER_CELL, end,
));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this check is unnecessary, as chunks function already returns slices, with size <= chunk_size.

Copy link
Contributor

@ArtiomTr ArtiomTr left a comment

Choose a reason for hiding this comment

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

Perfect 👌

* fix clippy

* add comments after debug

* add placeholder byte into `recovered_cells` and `recovered_proofs`

* move peerdas-specific functions outside of das-abstract trait

* avoid multiple heap allocation

* add cfg_chunks macro
@hangleang
Copy link
Contributor Author

Hello, just added cfg_chunks macro, also because I would like to trigger CI as well, saw one error case in the PR while all success at my fork

@hangleang
Copy link
Contributor Author

@ArtiomTr Can we merge this PR so I can switch to use rust-kzg for peerdas?

@ArtiomTr
Copy link
Contributor

ArtiomTr commented Jan 23, 2025

I approve, but I don't have merge rights to this repo 😄

I believe @sauliusgrigaitis can help

@sauliusgrigaitis sauliusgrigaitis merged commit 0070c96 into grandinetech:main Jan 23, 2025
31 checks passed
@hangleang
Copy link
Contributor Author

Okay, I'll ask him for help

@hangleang
Copy link
Contributor Author

Thanks @sauliusgrigaitis 🙏

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.

3 participants