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

feat(core): adds eip4844 transactions support to eth-signer and eth-sender #976

Closed

Conversation

montekki
Copy link
Member

@montekki montekki commented Jan 31, 2024

What ❔

Adds the support of EIP4844 transactions to the eth-signer and eth-sender components.

EIP4844 introduces blob transactions that expand the already existing EIP1559 transactions by adding the following information. The main purpose is sending the blobs of data to the network that will only temporarily remain available instead of forever staying in chain history. A single transaction may include multiple blobs.

  • A new blob gas price field is added to the transaction introducing which introduces a new blob gas market to the protocol
  • The versioned hashes of the blobs (these first two fields go into tx and remain on-chain forever)
  • A "sidecar" addition info that includes the blobs themselves, the commitments and proofs of these blobs. This information is only temporarily kept around by CL.

The above mentioned EIP describes signing and network transmission format of these transactions which this PR implements.

Checklist

  • PR title corresponds to the body of PR (we generate changelog entries from PRs).
  • Tests for the changes have been added / updated.
  • Documentation comments have been added / updated.
  • Code has been formatted via zk fmt and zk lint.
  • Spellcheck has been run via zk spellcheck.
  • Linkcheck has been run via zk linkcheck.

@montekki
Copy link
Member Author

@koloz193 the testcases in the EIP4844 page are not populated, we need to come up with a strategy to unit test this + integration test

@koloz193
Copy link
Contributor

@koloz193 the testcases in the EIP4844 page are not populated, we need to come up with a strategy to unit test this + integration test

agreed, for integration tests we can assume that theyll all test using blobs if thats what the server is configured to use. maybe in the long run we may want a new CI step that runs all the tests where pubdata is used as calldata and where pubdata is used as blobs

@montekki montekki marked this pull request as ready for review February 6, 2024 07:58
core/lib/dal/src/models/storage_eth_tx.rs Show resolved Hide resolved
core/lib/eth_client/src/clients/http/signing.rs Outdated Show resolved Hide resolved
core/lib/eth_client/src/types.rs Outdated Show resolved Hide resolved
core/lib/eth_client/src/types.rs Show resolved Hide resolved
core/lib/eth_client/src/types.rs Show resolved Hide resolved
core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs Outdated Show resolved Hide resolved
core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs Outdated Show resolved Hide resolved
core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs Outdated Show resolved Hide resolved
f.value = Some(value);
f.gas_price = Some(gas_price)
}),
None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe consider having a separate method (sign_prepared_tx_with_blobs?)

this way you'll not have to add these None,None in all these places?

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 would be a bit less typesafe but a bit more pretty i suppose. i don't have a strong opinion on it

Copy link
Member

Choose a reason for hiding this comment

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

I think this problem would also be solved if we would introduce a custom options type.

}
}

pub fn raw_tx(&self, blob_tx_sidecar: Option<&EthTxBlobSidecar>) -> RawTransactionBytes {
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you explain what this method does? Also I'm a little bit surprised, that we are adding this 'blob tx sidecar' from the side (as it seems to really modify the interface).
Maybe have 2 methods instead? (raw_tx) & raw_tx_with_blob ?

(the second one could also check - if we can - that the transaction is really a 'blob type' transaction? )

Copy link
Member Author

Choose a reason for hiding this comment

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

Before this patch the SignedCallResult contained a public field raw_tx which is essentially a transaction encoded into a byte form. We only supported 1559 transactions and so it made sense to be as it is. Important thing: for 1559 the signed transaction blob prefixed with a byte 0x02 to indicate the tx type is the network form of the transaction, end of story. With the instroduction of 4844 transactions that changes; the signed transaction blob (transaction + signature) + a byte prefix for type is no longer the complete network form for them, there is a new thing ("sidecar") that contains blobs, proofs and commitments + a new way it is all encoded together with the transaction (params + signature).

We want an interface that would leave no space for errors like: you wanted a 4844 tx, but actually went ahead and sent raw_tx field which in itself is not enough for it. All possible solutions to this are not ideal: you may misuse the current interface; you may misuse the interface proposed by you; currently we have no typesafe way of telling one tx from another.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should blob sidecar be a part of the new and raw_tx will return the correct value depends on it?

Copy link
Member Author

Choose a reason for hiding this comment

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

the only place where we call this new method is in sign_prepared_tx_for_addr and so we would need to pass the optional sidecar also there although it would be never used there. Which is probably even less ergonomic

Copy link
Member

Choose a reason for hiding this comment

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

@montekki great explanation, and I think it would make an even greater doc comment for future readers; could you please add one?

Copy link
Collaborator

Choose a reason for hiding this comment

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

+1 - let's add this to the comment

core/lib/eth_client/src/clients/mock.rs Outdated Show resolved Hide resolved
}
}

pub fn raw_tx(&self, blob_tx_sidecar: Option<&EthTxBlobSidecar>) -> RawTransactionBytes {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should blob sidecar be a part of the new and raw_tx will return the correct value depends on it?

// itself and the sidecar containing the blobs.
async fn test_generating_signed_raw_transaction_with_4844_sidecar_two_blobs() {
let private_key =
H256::from_str("27593fea79697e947890ecbecce7901b0008345e5d7259710d0dd5e500d040be")
Copy link
Contributor

Choose a reason for hiding this comment

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

i hope it's newly generated key.

Copy link
Member Author

Choose a reason for hiding this comment

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

ETH_SENDER_SENDER_OPERATOR_PRIVATE_KEY="0x27593fea79697e947890ecbecce7901b0008345e5d7259710d0dd5e500d040be"


self.rlp_append_access_list(&mut stream);

stream.append(&self.max_fee_per_blob_gas.unwrap());
Copy link
Contributor

Choose a reason for hiding this comment

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

should we at least verify that it's the correct tx_type?

core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs Outdated Show resolved Hide resolved
Copy link
Member

@popzxc popzxc left a comment

Choose a reason for hiding this comment

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

Didn't thoroughly check everything, a few surface comments

}
}

pub fn raw_tx(&self, blob_tx_sidecar: Option<&EthTxBlobSidecar>) -> RawTransactionBytes {
Copy link
Member

Choose a reason for hiding this comment

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

@montekki great explanation, and I think it would make an even greater doc comment for future readers; could you please add one?

Comment on lines +5 to +13
/// A forward-compatible `enum` describing a EIP4844 sidecar
///
/// This enum in `bincode`-encoded form is stored in the database
/// alongside all other transaction-related fields for EIP4844 transactions
/// in `eth_txs` and `eth_tx_history` tables.
#[derive(Clone, Deserialize, Serialize)]
pub enum EthTxBlobSidecar {
EthTxBlobSidecarV1(EthTxBlobSidecarV1),
}
Copy link
Member

Choose a reason for hiding this comment

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

Nice! Could we add some snapshot tests to make sure that we don't break bincode serialization by accident? I'd expect something similar to what we have for prover jobs serialization.

core/lib/eth_client/src/clients/mock.rs Outdated Show resolved Hide resolved
f.value = Some(value);
f.gas_price = Some(gas_price)
}),
None,
Copy link
Member

Choose a reason for hiding this comment

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

I think this problem would also be solved if we would introduce a custom options type.

Comment on lines +25 to +28
// A `EIP_4844_TX_TYPE` transaction blob sidecar.
//
// Format a `bincode`-encoded `EthTxBlobSidecar` enum.
pub blob_sidecar: Option<Vec<u8>>,
Copy link
Member

Choose a reason for hiding this comment

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

Maybe a stupid question, but still. If I'm not mistaken, blobs are taken as whole, even if you use a fraction of it. So we may assume that at times, the blob won't be fully utilized (e.g. each time when pubdata wasn't the criterion to seal the batch), and there will be an "empty" reminder.

Do we want to use any kind of compression when we store/load data? Given that the state size is a problem already, probably we would like to optimize on this front whenever possible.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes well my current idea here is that as you see Vec<u8> is used for all fields while from the protocol level we need three arrays: [u8; 32] [u8; 48] and [u8; 32*4096]. Two reasons for this:

  1. serde does not implement traits for such const-size arrays
  2. we actually have to store actual data with variable lengths for blob instead of a const array.

So as such there will always be a layer of conversion between a blob expanded to const size and Vec<u8> that is stored in DB and back. We just haven't figured out the ergonomics with @koloz193 but that is the current idea and things will remain as they are now. If we should use some compression for it is another topic; however the versioned blob struct should make it possible to do so in the future if necessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

we can definitely compress the data at the db level. there are a few things that happen before we store the blob:

  1. we take the raw pubdata and split it into chunks of size 4096*31 lets call these zksync_blobs
  2. for each zksync_blob we then convert it into an ethereum_blob
  3. from the ethereum_blob we compute the other kzg values needed

while the zksync_blob might have trailing zeroes, im not sure if/how many trailing zeroes are in the ethereum_blob. for this we'd want to check if we want to store the zksync_blob or the ethereum_blob. this would introduce a crypto dependency to be able to convert zksync_blob -> ethereum_blob

@montekki
Copy link
Member Author

Closing in favor of #1254

@montekki montekki closed this Feb 27, 2024
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

5 participants