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

DA compression for fuel-vm types #670

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft

Conversation

Dentosal
Copy link
Member

@Dentosal Dentosal commented Feb 1, 2024

See correspoing fuel-core PR as well: FuelLabs/fuel-core#1609

This PR adds Compact derive macro and fuel-compression machinery, that are used to convert Transaction and contained types into CompactTransaction and Compact*. The compact types are equivalent to the original types, except that malleable fields are removed, and often-repeated types like AssetId and Address are converted to three-byte keys to a separate database, called temporal storage. See the contained fuel-compression/README.md for an extended description.

@Dentosal Dentosal added fuel-types Related to the `fuel-types` crate. mainnet labels Feb 1, 2024
@Dentosal Dentosal self-assigned this Feb 1, 2024
@xgreenx xgreenx requested a review from a team February 19, 2024 18:11
Copy link
Collaborator

@xgreenx xgreenx left a comment

Choose a reason for hiding this comment

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

Thank you nice PR!=) It is cool that Compact can be derived almost for all types.

I feel like we put too many details about the implementation of the algorithm and how keys are created, and it seems we can simplify that part a lot to be agnostic. But the main with compaction looks good; I will review it one more time, more precisely when PR is in final shape.

BTW, compilation fails for me:

image

let start: Key<T> = self.start_keys.value();
let end: Key<T> = self.safe_keys_start.value();
// Check if the value is in the possibly-overwritable range
if !key.is_between(start, end) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if the key is not in the range but at the beginning of the whole range, for example, zero? You will overflow the end and may also overwrite it.

{
type Compact = ArrayWrapper<S, T::Compact>;

fn count(&self) -> CountPerTable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to have a separate implementation for bytes later to be more performance?

Copy link
Member Author

Choose a reason for hiding this comment

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

This fully monomorphizes. Zero-cost abstraction.

if v == needle {
return Some(key);
}
key = key.next();
Copy link
Collaborator

Choose a reason for hiding this comment

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

How do we know that it will not overflow?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's allowed to overflow. Wrapping around is the expected behavior.

}

tables!(
AssetId: [u8; 32],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need to add ContractId as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep.

pub sender: Address,
/// The receiver on the `Fuel` chain.
#[cfg_attr(feature = "da-compression", da_compress(registry = "Address"))]
pub recipient: Address,
pub amount: Word,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can skip amount since it is restorable informaiton

Copy link
Member Author

Choose a reason for hiding this comment

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

Because we can find get from L1 message?

pub recipient: Address,
pub amount: Word,
pub nonce: Nonce,
#[derivative(Debug(format_with = "fmt_as_field"))]
pub witness_index: Specification::Witness,
#[derivative(Debug(format_with = "fmt_as_field"))]
#[cfg_attr(feature = "da-compression", da_compress(skip))]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm, I think we need to keep the used gas price. It is true that we should be able to calculate it based on the other field of the transaction, but it requires VM initialization for fraud proofs wit right consensus parameters and maybe it is better to start without it for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok sure. We can keep it for now.

pub enum Output {
Coin {
#[cfg_attr(feature = "da-compression", da_compress(registry = "Address"))]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think it would be better to support the actual path to the table's type instead of using a string. Instead of registry = "Address" -> registry = ::fuel_compression::tables::Address or simply ::fuel_compression::tables::Address

Copy link
Member Author

Choose a reason for hiding this comment

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

Proc macros had some limits around this, but maybe they have been lifted? I'll take a look.

@@ -20,6 +20,7 @@ use rand::{
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "da-compression", derive(fuel_compression::Compact))]
#[derive(fuel_types::canonical::Deserialize, fuel_types::canonical::Serialize)]
pub struct UtxoId {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I remember correctly, we agreed on replacing UtxoId with TxPointer. Do you plan to do that during this PR or not?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not planning to do that here, since that's separate from the BiggerType -> Key separation I'm truing to achieve in the PR. Maybe I'll add it here if I can refactor like this #670 (comment)

{
/// Reverse lookup.
/// TODO: possibly add a lookup table for this, if deemed necessary
pub fn lookup_value(&self, needle: &T::Type) -> Option<Key<T>> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we put too much business logic here with overflowing and counters. I feel like it should be part of the fuel-core while fuel-compression is responsible only for iterating over the fields and calling next_key::<Table>(value). The type that implements next_key may be decided in which way he wants to do that.

The count function also sounds like something related to the exact algorithm implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree; if I can get the logic separated it would be really nice. I'll look into it.

Copy link
Contributor

@bvrooman bvrooman left a comment

Choose a reason for hiding this comment

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

Reviewed 6/41 so far, will continue review at the next chance I get

fuel-compression/src/compaction.rs Outdated Show resolved Hide resolved
fuel-compression/src/compaction.rs Outdated Show resolved Hide resolved
Comment on lines +191 to +193
for item in self.iter() {
count += item.count();
}
Copy link
Contributor

@bvrooman bvrooman Mar 4, 2024

Choose a reason for hiding this comment

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

Suggested change
for item in self.iter() {
count += item.count();
}
count = self.iter().fold(count, |acc, &item| acc + item.count());

Copy link
Member Author

Choose a reason for hiding this comment

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

I find the plain loop way easier to read.

Dentosal and others added 2 commits March 6, 2024 10:05
Co-authored-by: Brandon Vrooman <brandon.vrooman@fuel.sh>
Co-authored-by: Brandon Vrooman <brandon.vrooman@fuel.sh>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fuel-types Related to the `fuel-types` crate. mainnet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants