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

Synclayer Batch Aggregation #470

Open
wants to merge 18 commits into
base: dev
Choose a base branch
from
Open

Conversation

vladenysiuk
Copy link
Contributor

What ❔

Implement batch forwarding from hyperchains to lower layer and compression for pubdata.

Why ❔

To allow deployment of hyperchains on top of synclayer and compress reused storage slots.


interface IBatchAggregator {
function commitBatch(
bytes calldata _totalL2ToL1PubdataAndStateDiffs,
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need to send both compressed diffs and their uncompressed version? I think we should decide on what we want. I would rather send only uncompressed ones

// Add batch to BatchAggregator
IBatchAggregator(BATCH_AGGREGATOR_ADDRESS).commitBatch(
_newBatch.pubdataCommitments,
0,
Copy link
Collaborator

Choose a reason for hiding this comment

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

just in case, s.chainId is available in storage


numInitialWritesProcessed++;
require(derivedKey == compressedStateDiffs.readBytes32(stateDiffPtr), "iw: initial key mismatch");
if (keyStatus[chainId][derivedKey] == false) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I understand it is draft, just in case better to rename to isKeyTouched

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

bytes memory compressedStateDiff = new bytes(touchedKeys[chainId].length * COMPRESSED_STATE_DIFF_SIZE);
uint256 stateDiffPtr = 0;
for (uint256 keyIndex = 0; keyIndex < touchedKeys[chainId].length; keyIndex += 1) {
bytes32 derivedKey = touchedKeys[chainId][keyIndex];
Copy link
Collaborator

Choose a reason for hiding this comment

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

repeated writes should be encoded as pair (enum_index, value)

system-contracts/contracts/BatchAggregator.sol Outdated Show resolved Hide resolved
system-contracts/contracts/BatchAggregator.sol Outdated Show resolved Hide resolved
uint256 compressedStateDiffSize = 0;
initialWritesPtr = 0;
for(uint256 i = 0;i<numberOfInitialWrites*STATE_DIFF_ENTRY_SIZE;i+=STATE_DIFF_ENTRY_SIZE){
bytes memory stateDiff = initialWrites[chainId][initialWritesPtr];
Copy link
Collaborator

Choose a reason for hiding this comment

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

just in case, does your code work fine with the same key used for both initial write and repeated write? I.e. in the first batch it is an initial write and it is a repeated write in another 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 break it down into two different write instances. That is done because I haven't found a way to derive last enumIndex, and thus know enumIndex for initial writes.

function addRepeatedWrite(uint256 chainId, bytes calldata stateDiff, bytes calldata compressedStateDiff) internal{
uint64 enumIndex = stateDiff.readUint64(84);
if (slotStatus[chainId][enumIndex]==false){
uncompressedWrites[chainId][enumIndex] = stateDiff;
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we store the entire state diff? not too bad, but I think storing just initialValue,finalValue,enumIndex will be much easier to read

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, done


function compressValue(uint256 initialValue, uint256 finalValue) internal returns (bytes memory compressedDiff){
uint8 transform = bytesLength(finalValue);
uint8 add = bytesLength(finalValue-initialValue);
Copy link
Collaborator

Choose a reason for hiding this comment

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

this will panic if finalValue < initialValue. please use unchecked

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

function compressValue(uint256 initialValue, uint256 finalValue) internal returns (bytes memory compressedDiff){
uint8 transform = bytesLength(finalValue);
uint8 add = bytesLength(finalValue-initialValue);
uint8 sub = bytesLength(initialValue-finalValue);
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

uint8 transform = bytesLength(finalValue);
uint8 add = bytesLength(finalValue-initialValue);
uint8 sub = bytesLength(initialValue-finalValue);
uint8 opt = (transform<add?transform:add);
Copy link
Collaborator

Choose a reason for hiding this comment

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

please use extended variable names opt -> optimal

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

import {UnsafeBytesCalldata} from "./libraries/UnsafeBytesCalldata.sol";
import {ICompressor, OPERATION_BITMASK, LENGTH_BITS_OFFSET, MAX_ENUMERATION_INDEX_SIZE} from "./interfaces/ICompressor.sol";

uint256 constant BYTE_BITMASK = (1<<8)-1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

type(uint8).max

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

value = initialValue-finalValue;
}
else{
require(false, "Optimal operation is not transform, add or sub");
Copy link
Collaborator

Choose a reason for hiding this comment

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

there are four types of compression:

    /// 0 -> Nothing (32 bytes)
    /// 1 -> Add
    /// 2 -> Subtract
    /// 3 -> Transform (< 32 bytes)

I am not sure this will work when the size of the change is 32 bytes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can't be 32 bytes. At most 2^32/2 = 2^31, because of add or subtract

Copy link
Collaborator

@StanislavBreadless StanislavBreadless May 28, 2024

Choose a reason for hiding this comment

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

32 is bytes, not bits, i.e. 2^256 / 2 = 2^255

uint256 messageSliceStart = calldataPtr;
uint32 numberOfMessages = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4]));
calldataPtr += 4;
bytes32 reconstructedChainedMessagesHash;
Copy link
Collaborator

Choose a reason for hiding this comment

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

not used

stateDiffPtr += 32;
uint8 metadata = uint8(bytes1(_compressedStateDiffs[stateDiffPtr]));
stateDiffPtr++;
uint8 operation = metadata & OPERATION_BITMASK;
Copy link
Collaborator

Choose a reason for hiding this comment

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

unused vars

function addInitialWrite(uint256 chainId, bytes32 derivedKey, uint64 enumIndex, uint256 initialValue, uint256 finalValue) internal{
bytes memory slotData = new bytes(STATE_DIFF_AGGREGATION_INFO_SIZE);
assembly{
mstore(add(slotData,0),derivedKey)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would really suggest to store those as separate mappings to increase readability.

Or even, better, a single mapping, but with a struct inside. There is little benefit in using bytes here

}
uint8 diffPtr = optimal;
for(uint8 i = 0;i<optimal;i+=1){
compressedDiff[diffPtr] = bytes1(uint8(value & type(uint8).max));
Copy link
Collaborator

Choose a reason for hiding this comment

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

this value doesnt change

@vladenysiuk vladenysiuk marked this pull request as ready for review May 31, 2024 13:22
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.

2 participants