Skip to content

Commit

Permalink
fix(contracts): fix chunk hash computation (#546)
Browse files Browse the repository at this point in the history
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
  • Loading branch information
zimpha and HAOYUatHZ committed Jun 8, 2023
1 parent 2a745ad commit fb44382
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 40 deletions.
21 changes: 14 additions & 7 deletions contracts/src/L1/rollup/ScrollChain.sol
Original file line number Diff line number Diff line change
Expand Up @@ -402,27 +402,37 @@ contract ScrollChain is OwnableUpgradeable, IScrollChain {
bytes calldata _skippedL1MessageBitmap
) internal view returns (uint256 _totalNumL1MessagesInChunk) {
uint256 chunkPtr;
uint256 startDataPtr;
uint256 dataPtr;
uint256 blockPtr;

assembly {
dataPtr := mload(0x40)
startDataPtr := dataPtr
chunkPtr := add(_chunk, 0x20) // skip chunkLength
blockPtr := add(chunkPtr, 1) // skip numBlocks
}

uint256 _numBlocks = ChunkCodec.validateChunkLength(chunkPtr, _chunk.length);

// concatenate block contexts
uint256 _totalTransactionsInChunk;
for (uint256 i = 0; i < _numBlocks; i++) {
dataPtr = ChunkCodec.copyBlockContext(chunkPtr, dataPtr, i);
uint256 _numTransactionsInBlock = ChunkCodec.numTransactions(blockPtr);
unchecked {
_totalTransactionsInChunk += _numTransactionsInBlock;
blockPtr += ChunkCodec.BLOCK_CONTEXT_LENGTH;
}
}

assembly {
mstore(0x40, add(dataPtr, mul(_totalTransactionsInChunk, 0x20))) // reserve memory for tx hashes
blockPtr := add(chunkPtr, 1) // reset block ptr
}

// concatenate tx hashes
uint256 l2TxPtr = ChunkCodec.l2TxPtr(chunkPtr, _numBlocks);

// avoid stack too deep on forge coverage
uint256 _totalTransactionsInChunk;
while (_numBlocks > 0) {
// concatenate l1 message hashes
uint256 _numL1MessagesInBlock = ChunkCodec.numL1Messages(blockPtr);
Expand All @@ -446,7 +456,6 @@ contract ScrollChain is OwnableUpgradeable, IScrollChain {
}

unchecked {
_totalTransactionsInChunk += _numTransactionsInBlock;
_totalNumL1MessagesInChunk += _numL1MessagesInBlock;
_totalL1MessagesPoppedInBatch += _numL1MessagesInBlock;
_totalL1MessagesPoppedOverall += _numL1MessagesInBlock;
Expand All @@ -467,9 +476,7 @@ contract ScrollChain is OwnableUpgradeable, IScrollChain {

// compute data hash and store to memory
assembly {
let startPtr := mload(0x40)
let dataHash := keccak256(startPtr, sub(dataPtr, startPtr))

let dataHash := keccak256(startDataPtr, sub(dataPtr, startDataPtr))
mstore(memPtr, dataHash)
}

Expand Down
100 changes: 67 additions & 33 deletions contracts/src/test/ScrollChain.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {ScrollChain, IScrollChain} from "../L1/rollup/ScrollChain.sol";
import {MockScrollChain} from "./mocks/MockScrollChain.sol";
import {MockRollupVerifier} from "./mocks/MockRollupVerifier.sol";

// solhint-disable no-inline-assembly

contract ScrollChainTest is DSTestPlus {
// from ScrollChain
event UpdateSequencer(address indexed account, bool status);
Expand Down Expand Up @@ -42,35 +44,6 @@ contract ScrollChainTest is DSTestPlus {
rollup.initialize(address(messageQueue), address(0), 100);
}

/*
function testPublicInputHash() public {
IScrollChain.Batch memory batch;
batch.prevStateRoot = bytes32(0x000000000000000000000000000000000000000000000000000000000000cafe);
batch.newStateRoot = bytes32(0);
batch.withdrawTrieRoot = bytes32(0);
batch
.l2Transactions = hex"0000007402f8710582fd14808506e38dccc9825208944d496ccc28058b1d74b7a19541663e21154f9c848801561db11e24a43380c080a0d890606d7a35b2ab0f9b866d62c092d5b163f3e6a55537ae1485aac08c3f8ff7a023997be2d32f53e146b160fff0ba81e81dbb4491c865ab174d15c5b3d28c41ae";
batch.blocks = new IScrollChain.BlockContext[](1);
batch.blocks[0].blockHash = bytes32(0);
batch.blocks[0].parentHash = bytes32(0);
batch.blocks[0].blockNumber = 51966;
batch.blocks[0].timestamp = 123456789;
batch.blocks[0].baseFee = 0;
batch.blocks[0].gasLimit = 10000000000000000;
batch.blocks[0].numTransactions = 1;
batch.blocks[0].numL1Messages = 0;
(bytes32 hash, , , ) = chain.computePublicInputHash(0, batch);
assertEq(hash, bytes32(0xa9f2ca3175794f91226a410ba1e60fff07a405c957562675c4149b77e659d805));
batch
.l2Transactions = hex"00000064f8628001830f424094000000000000000000000000000000000000bbbb8080820a97a064e07cd8f939e2117724bdcbadc80dda421381cbc2a1f4e0d093d9cc5c5cf68ea03e264227f80852d88743cd9e43998f2746b619180366a87e4531debf9c3fa5dc";
(hash, , , ) = chain.computePublicInputHash(0, batch);
assertEq(hash, bytes32(0x398cb22bbfa1665c1b342b813267538a4c933d7f92d8bd9184aba0dd1122987b));
}
*/

function testCommitBatch() public {
bytes memory batchHeader0 = new bytes(89);

Expand Down Expand Up @@ -260,13 +233,34 @@ contract ScrollChainTest is DSTestPlus {
bytes memory chunk1;

// commit batch1, one chunk with one block, 1 tx, 1 L1 message, no skip
// => payload for data hash of chunk0
// 0000000000000000
// 0000000000000000
// 0000000000000000000000000000000000000000000000000000000000000000
// 0000000000000000
// 0001
// 50c3caa727394b95dc4885b7d25033ed22ac772b985fb274f2a7c0699a11346d
// => data hash for chunk0
// bb88f47194a07d59ed17bc9b2015f83d0afea8f7892d9c5f0b6565563bf06b26
// => data hash for all chunks
// 038433daac85a0b03cd443ed50bc85e832c883061651ae2182b2984751e0b340
// => payload for batch header
// 00
// 0000000000000002
// 0000000000000001
// 0000000000000001
// 038433daac85a0b03cd443ed50bc85e832c883061651ae2182b2984751e0b340
// 119b828c2a2798d2c957228ebeaff7e10bb099ae0d4e224f3eeb779ff61cba61
// 0000000000000000000000000000000000000000000000000000000000000000
// => hash for batch header
// cef70bf80683c4d9b8b2813e90c314e8c56648e231300b8cfed9d666b0caf14e
bytes memory batchHeader1 = new bytes(89 + 32);
assembly {
mstore(add(batchHeader1, 0x20), 0) // version
mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex = 1
mstore(add(batchHeader1, add(0x20, 9)), shl(192, 1)) // l1MessagePopped = 1
mstore(add(batchHeader1, add(0x20, 17)), shl(192, 1)) // totalL1MessagePopped = 1
mstore(add(batchHeader1, add(0x20, 25)), 0xfe21c37fa013c76f86b8593ddf15d685a04b55061d06797597ee866f6ff2edf8) // dataHash
mstore(add(batchHeader1, add(0x20, 25)), 0x038433daac85a0b03cd443ed50bc85e832c883061651ae2182b2984751e0b340) // dataHash
mstore(add(batchHeader1, add(0x20, 57)), batchHash0) // parentBatchHash
mstore(add(batchHeader1, add(0x20, 89)), 0) // bitmap0
}
Expand All @@ -280,9 +274,9 @@ contract ScrollChainTest is DSTestPlus {
chunks[0] = chunk0;
bitmap = new bytes(32);
rollup.commitBatch(0, batchHeader0, chunks, bitmap);
assertGt(uint256(rollup.committedBatches(1)), 0);
assertBoolEq(rollup.isBatchFinalized(1), false);
bytes32 batchHash1 = rollup.committedBatches(1);
assertEq(batchHash1, bytes32(0xcef70bf80683c4d9b8b2813e90c314e8c56648e231300b8cfed9d666b0caf14e));

// finalize batch1
rollup.finalizeBatchWithProof(
Expand All @@ -301,17 +295,57 @@ contract ScrollChainTest is DSTestPlus {

// commit batch2 with two chunks, correctly
// 1. chunk0 has one block, 3 tx, no L1 messages
// => payload for chunk0
// 0000000000000000
// 0000000000000000
// 0000000000000000000000000000000000000000000000000000000000000000
// 0000000000000000
// 0003
// ... (some tx hashes)
// => data hash for chunk0
// 2ac1dad3f3696e5581dfc10f2c7a7a8fc5b344285f7d332c7895a8825fca609a
// 2. chunk1 has three blocks
// 2.1 block0 has 5 tx, 3 L1 messages, no skips
// 2.2 block1 has 10 tx, 5 L1 messages, even is skipped.
// 2.2 block1 has 300 tx, 256 L1 messages, odd position is skipped.
// => payload for chunk1
// 0000000000000000
// 0000000000000000
// 0000000000000000000000000000000000000000000000000000000000000000
// 0000000000000000
// 0005
// 0000000000000000
// 0000000000000000
// 0000000000000000000000000000000000000000000000000000000000000000
// 0000000000000000
// 000a
// 0000000000000000
// 0000000000000000
// 0000000000000000000000000000000000000000000000000000000000000000
// 0000000000000000
// 012c
// ... (some tx hashes)
// => data hash for chunk2
// 5c91563ee8be18cb94accfc83728f883ff5e3aa600fd0799e0a4e39afc7970b9
// => data hash for all chunks
// bf38f308e0a87ed7bf92fa2da038fa1d59a7b9801eb0f6d487f8eef528632145
// => payload for batch header
// 00
// 0000000000000002
// 0000000000000108
// 0000000000000109
// bf38f308e0a87ed7bf92fa2da038fa1d59a7b9801eb0f6d487f8eef528632145
// cef70bf80683c4d9b8b2813e90c314e8c56648e231300b8cfed9d666b0caf14e
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa800000000000000000000000000000000000000000000000000000000000000aa
// => hash for batch header
// 17fe6c12739f3a6261ae6db6486f41758dbd5d0508f19a5ca9ac37df67bbfec2
bytes memory batchHeader2 = new bytes(89 + 32 + 32);
assembly {
mstore(add(batchHeader2, 0x20), 0) // version
mstore(add(batchHeader2, add(0x20, 1)), shl(192, 2)) // batchIndex = 2
mstore(add(batchHeader2, add(0x20, 9)), shl(192, 264)) // l1MessagePopped = 264
mstore(add(batchHeader2, add(0x20, 17)), shl(192, 265)) // totalL1MessagePopped = 265
mstore(add(batchHeader2, add(0x20, 25)), 0xa447b3c80bc6c3ee1aebc1af746c7c6b35a05c4a7af89d2103e9e0788b54375c) // dataHash
mstore(add(batchHeader2, add(0x20, 25)), 0xbf38f308e0a87ed7bf92fa2da038fa1d59a7b9801eb0f6d487f8eef528632145) // dataHash
mstore(add(batchHeader2, add(0x20, 57)), batchHash1) // parentBatchHash
mstore(
add(batchHeader2, add(0x20, 89)),
Expand Down Expand Up @@ -358,9 +392,9 @@ contract ScrollChainTest is DSTestPlus {
}

rollup.commitBatch(0, batchHeader1, chunks, bitmap);
assertGt(uint256(rollup.committedBatches(2)), 0);
assertBoolEq(rollup.isBatchFinalized(2), false);
bytes32 batchHash2 = rollup.committedBatches(2);
assertEq(batchHash2, bytes32(0x17fe6c12739f3a6261ae6db6486f41758dbd5d0508f19a5ca9ac37df67bbfec2));

// verify committed batch correctly
rollup.finalizeBatchWithProof(
Expand Down

0 comments on commit fb44382

Please sign in to comment.