Commit 2775f256 authored by Karl Floersch's avatar Karl Floersch Committed by Kelvin Fichter

feat: further improve batch submission efficiency

test(contracts): Add a test for block hash in logs
parent 7aec0958
...@@ -362,14 +362,6 @@ contract CanonicalTransactionChain is ICanonicalTransactionChain, Lib_AddressRes ...@@ -362,14 +362,6 @@ contract CanonicalTransactionChain is ICanonicalTransactionChain, Lib_AddressRes
IChainStorageContainer queueRef = queue(); IChainStorageContainer queueRef = queue();
uint40 queueLength = _getQueueLength(queueRef); uint40 queueLength = _getQueueLength(queueRef);
// Reserve some memory to save gas on hashing later on. This is a relatively safe estimate
// for the average transaction size that will prevent having to resize this chunk of memory
// later on. Saves gas.
bytes memory hashMemory = new bytes((msg.data.length / totalElementsToAppend) * 2);
// Initialize the array of canonical chain leaves that we will append.
bytes32[] memory leaves = new bytes32[](totalElementsToAppend);
// Each leaf index corresponds to a tx, either sequenced or enqueued. // Each leaf index corresponds to a tx, either sequenced or enqueued.
uint32 leafIndex = 0; uint32 leafIndex = 0;
...@@ -388,39 +380,10 @@ contract CanonicalTransactionChain is ICanonicalTransactionChain, Lib_AddressRes ...@@ -388,39 +380,10 @@ contract CanonicalTransactionChain is ICanonicalTransactionChain, Lib_AddressRes
curContext = nextContext; curContext = nextContext;
// Process sequencer transactions first. // Process sequencer transactions first.
for (uint32 j = 0; j < curContext.numSequencedTransactions; j++) { numSequencerTransactions += uint32(curContext.numSequencedTransactions);
uint256 txDataLength;
assembly {
txDataLength := shr(232, calldataload(nextTransactionPtr))
}
require(
txDataLength <= MAX_ROLLUP_TX_SIZE,
"Transaction data size exceeds maximum for rollup transaction."
);
leaves[leafIndex] = _getSequencerLeafHash(
curContext,
nextTransactionPtr,
txDataLength,
hashMemory
);
nextTransactionPtr += uint40(TX_DATA_HEADER_SIZE + txDataLength);
numSequencerTransactions++;
leafIndex++;
}
// Now process any subsequent queue transactions. // Now process any subsequent queue transactions.
for (uint32 j = 0; j < curContext.numSubsequentQueueTransactions; j++) { nextQueueIndex += uint40(curContext.numSubsequentQueueTransactions);
require(
nextQueueIndex < queueLength,
"Not enough queued transactions to append."
);
leaves[leafIndex] = _getQueueLeafHash(nextQueueIndex);
nextQueueIndex++;
leafIndex++;
}
} }
// Generate the required metadata that we need to append this batch // Generate the required metadata that we need to append this batch
...@@ -447,11 +410,9 @@ contract CanonicalTransactionChain is ICanonicalTransactionChain, Lib_AddressRes ...@@ -447,11 +410,9 @@ contract CanonicalTransactionChain is ICanonicalTransactionChain, Lib_AddressRes
blockNumber = lastElement.blockNumber; blockNumber = lastElement.blockNumber;
} }
// For efficiency reasons getMerkleRoot modifies the `leaves` argument in place // Cache the previous blockhash to ensure all transaction data can be retrieved efficiently.
// while calculating the root hash therefore any arguments passed to it must not
// be used again afterwards
_appendBatch( _appendBatch(
Lib_MerkleTree.getMerkleRoot(leaves), blockhash(block.number-1),
totalElementsToAppend, totalElementsToAppend,
numQueuedTransactions, numQueuedTransactions,
blockTimestamp, blockTimestamp,
......
...@@ -620,86 +620,35 @@ describe('CanonicalTransactionChain', () => { ...@@ -620,86 +620,35 @@ describe('CanonicalTransactionChain', () => {
).to.be.revertedWith('Function can only be called by the Sequencer.') ).to.be.revertedWith('Function can only be called by the Sequencer.')
}) })
it('should revert when trying to input more data than the max data size', async () => { it('should emit the previous blockhash in the TransactionBatchAppended event', async () => {
const MAX_ROLLUP_TX_SIZE =
await CanonicalTransactionChain.MAX_ROLLUP_TX_SIZE()
const data = '0x' + '12'.repeat(MAX_ROLLUP_TX_SIZE + 1)
const timestamp = await getEthTime(ethers.provider) const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 const currentBlockHash = await (
await ethers.provider.getBlock('latest')
).hash
const blockNumber = await getNextBlockNumber(ethers.provider)
const res = await appendSequencerBatch(CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
const receipt = await res.wait()
await expect( // Because the res value is returned by a sendTransaction type, we need to manually
appendSequencerBatch(CanonicalTransactionChain, { // decode the logs.
transactions: [data], const eventArgs = ethers.utils.defaultAbiCoder.decode(
contexts: [ ['uint256', 'bytes32', 'uint256', 'uint256', 'bytes'],
{ receipt.logs[0].data
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Transaction data size exceeds maximum for rollup transaction.'
) )
})
describe('Sad path cases', () => {
const target = NON_ZERO_ADDRESS
const gasLimit = 500_000
const data = '0x' + '12'.repeat(1234)
describe('when the sequencer attempts to add more queue transactions than exist', () => {
it('reverts when there are zero transactions in the queue', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect( await expect(eventArgs[0]).to.eq(currentBlockHash)
appendSequencerBatch(CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 1,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Not enough queued transactions to append.')
})
it('reverts when there are insufficient (but nonzero) transactions in the queue', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
const numEnqueues = 7
for (let i = 0; i < numEnqueues; i++) {
await CanonicalTransactionChain.enqueue(target, gasLimit, data)
}
await expect(
appendSequencerBatch(CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: numEnqueues + 1,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: numEnqueues + 1,
})
).to.be.revertedWith('Not enough queued transactions to append.')
})
})
}) })
for (const size of ELEMENT_TEST_SIZES) { for (const size of ELEMENT_TEST_SIZES) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment