Commit 29f92bed authored by Karl Floersch's avatar Karl Floersch

Finish custom encoding

parent d0831b40
......@@ -35,6 +35,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
uint256 constant public BATCH_CONTEXT_SIZE = 16;
uint256 constant public BATCH_CONTEXT_LENGTH_POS = 12;
uint256 constant public BATCH_CONTEXT_START_POS = 15;
uint256 constant public TX_DATA_HEADER_SIZE = 15;
/*************
......@@ -234,106 +235,87 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
override
public
{
bytes32 firstCalldataWord;
uint40 _shouldStartAtBatch;
uint24 _totalElementsToAppend;
uint24 _contextsLength;
assembly {
// First 5 bytes after MethodId is _shouldStartAtBatch
_shouldStartAtBatch := shr(216, calldataload(4))
// Next 3 bytes is _totalElementsToAppend
_totalElementsToAppend := shr(232, calldataload(9))
// And the last 3 bytes is the _contextsLength
_contextsLength := shr(232, calldataload(12))
}
console.log("_shouldStartAtBatch");
console.log(_shouldStartAtBatch);
console.log("_totalElementsToAppend");
console.log(_totalElementsToAppend);
// numBatchContexts header always starts at byte 12
// 4[method_id] + 5[_shouldStartAtBatch] + 3[_totalElementsToAppend]
uint numBatchContexts;
assembly {
// 3 byte numSubsequentQueueTransactions
numBatchContexts := shr(232, calldataload(BATCH_CONTEXT_LENGTH_POS))
require(
_shouldStartAtBatch == getTotalBatches(),
"Actual batch start index does not match expected start index."
);
require(
msg.sender == sequencer,
"Function can only be called by the Sequencer."
);
require(
_contextsLength > 0,
"Must provide at least one batch context."
);
require(
_totalElementsToAppend > 0,
"Must append at least one element."
);
bytes32[] memory leaves = new bytes32[](_totalElementsToAppend);
uint32 transactionIndex = 0;
uint32 numSequencerTransactionsProcessed = 0;
uint32 nextSequencerTransactionPosition = uint32(BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * _contextsLength);
(, uint32 nextQueueIndex) = _getLatestBatchContext();
for (uint32 i = 0; i < _contextsLength; i++) {
BatchContext memory context = _getBatchContext(i);
_validateBatchContext(context, nextQueueIndex);
for (uint32 i = 0; i < context.numSequencedTransactions; i++) {
bytes memory txData = _getTransactionData(nextSequencerTransactionPosition);
leaves[transactionIndex] = _hashTransactionChainElement(
TransactionChainElement({
isSequenced: true,
queueIndex: 0,
timestamp: context.timestamp,
blockNumber: context.blockNumber,
txData: txData
})
);
nextSequencerTransactionPosition += uint32(TX_DATA_HEADER_SIZE + txData.length);
numSequencerTransactionsProcessed++;
transactionIndex++;
}
for (uint32 i = 0; i < context.numSubsequentQueueTransactions; i++) {
leaves[transactionIndex] = _getQueueLeafHash(nextQueueIndex);
nextQueueIndex++;
transactionIndex++;
}
}
console.log("numBatchContexts");
// Grab a batch context
BatchContext memory batchCtx = _getBatchContext(0);
// Grab transaction data
bytes memory transaction = _getTransactionData(0);
console.logBytes(transaction);
// require(
// _shouldStartAtBatch == getTotalBatches(),
// "Actual batch start index does not match expected start index."
// );
// require(
// msg.sender == sequencer,
// "Function can only be called by the Sequencer."
// );
// require(
// _contexts.length > 0,
// "Must provide at least one batch context."
// );
// require(
// _totalElementsToAppend > 0,
// "Must append at least one element."
// );
// bytes32[] memory leaves = new bytes32[](_totalElementsToAppend);
// uint32 transactionIndex = 0;
// uint32 numSequencerTransactionsProcessed = 0;
// (, uint32 nextQueueIndex) = _getLatestBatchContext();
// for (uint32 i = 0; i < _contexts.length; i++) {
// BatchContext memory context = _contexts[i];
// _validateBatchContext(context, nextQueueIndex);
// for (uint32 i = 0; i < context.numSequencedTransactions; i++) {
// leaves[transactionIndex] = _hashTransactionChainElement(
// TransactionChainElement({
// isSequenced: true,
// queueIndex: 0,
// timestamp: context.timestamp,
// blockNumber: context.blockNumber,
// txData: _transactions[numSequencerTransactionsProcessed]
// })
// );
// numSequencerTransactionsProcessed++;
// transactionIndex++;
// }
// for (uint32 i = 0; i < context.numSubsequentQueueTransactions; i++) {
// leaves[transactionIndex] = _getQueueLeafHash(nextQueueIndex);
// nextQueueIndex++;
// transactionIndex++;
// }
// }
// require(
// numSequencerTransactionsProcessed == _transactions.length,
// "Not all sequencer transactions were processed."
// );
// require(
// transactionIndex == _totalElementsToAppend,
// "Actual transaction index does not match expected total elements to append."
// );
// uint256 numQueuedTransactions = _totalElementsToAppend - numSequencerTransactionsProcessed;
// _appendBatch(
// Lib_MerkleRoot.getMerkleRoot(leaves),
// _totalElementsToAppend,
// numQueuedTransactions
// );
// emit SequencerBatchAppended(
// nextQueueIndex - numQueuedTransactions,
// numQueuedTransactions
// );
require(
transactionIndex == _totalElementsToAppend,
"Actual transaction index does not match expected total elements to append."
);
uint256 numQueuedTransactions = _totalElementsToAppend - numSequencerTransactionsProcessed;
_appendBatch(
Lib_MerkleRoot.getMerkleRoot(leaves),
_totalElementsToAppend,
numQueuedTransactions
);
emit SequencerBatchAppended(
nextQueueIndex - numQueuedTransactions,
numQueuedTransactions
);
}
......@@ -341,18 +323,23 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
* Internal Functions *
**********************/
/**
* Returns the BatchContext located at a particular index.
* @param _index The index of the BatchContext
* @return _context The BatchContext at the specified index.
*/
function _getBatchContext(
uint _numProcessed
uint _index
)
internal
view
returns (
BatchContext memory context
BatchContext memory _context
)
{
// Batch contexts always start at byte 12:
// 4[method_id] + 5[_shouldStartAtBatch] + 3[_totalElementsToAppend] + 3[numBatchContexts]
uint contextPosition = 15 + _numProcessed * BATCH_CONTEXT_SIZE;
uint contextPosition = 15 + _index * BATCH_CONTEXT_SIZE;
uint numSequencedTransactions;
uint numSubsequentQueueTransactions;
uint ctxTimestamp;
......@@ -367,52 +354,41 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
// 5 byte blockNumber
ctxBlockNumber := shr(216, calldataload(add(contextPosition, 11)))
}
console.log("RETURNING BATCH:");
console.log("numSequencedTransactions");
console.log(numSequencedTransactions);
console.log("numSubsequentQueueTransactions");
console.log(numSubsequentQueueTransactions);
console.log("timestamp");
console.log(ctxTimestamp);
console.log("blockNumber");
console.log(ctxBlockNumber);
return BatchContext({
numSequencedTransactions: numSequencedTransactions,
numSubsequentQueueTransactions: numSubsequentQueueTransactions,
timestamp: ctxTimestamp,
blockNumber: ctxBlockNumber,
index: _numProcessed
blockNumber: ctxBlockNumber
});
}
/**
* Returns the transaction data located at a particular start position in calldata.
* @param _startPosition Start position in calldata (represented in bytes).
* @return _transactionData The transaction data for this particular element.
*/
function _getTransactionData(
uint _numProcessed
uint _startPosition
)
internal
view
returns (
bytes memory transactionData
bytes memory _transactionData
)
{
uint numBatchContexts;
assembly {
numBatchContexts := shr(232, calldataload(BATCH_CONTEXT_LENGTH_POS))
}
uint startPosition = BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * numBatchContexts;
uint transactionSize;
assembly {
// 3 byte transactionSize
transactionSize := shr(232, calldataload(startPosition))
transactionSize := shr(232, calldataload(_startPosition))
// Initialize transactionData at the free memory pointer 0x40
transactionData := mload(0x40)
// Initialize _transactionData at the free memory pointer 0x40
_transactionData := mload(0x40)
// Store the length as the first word to conform with `bytes memory`
mstore(transactionData, transactionSize)
mstore(_transactionData, transactionSize)
// Store the rest of the transaction
calldatacopy(add(transactionData, 32), add(startPosition, 3), transactionSize)
calldatacopy(add(_transactionData, 32), add(_startPosition, 3), transactionSize)
}
return transactionData;
return _transactionData;
}
/**
......
......@@ -46,7 +46,6 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
uint256 numSubsequentQueueTransactions;
uint256 timestamp;
uint256 blockNumber;
uint256 index;
}
struct TransactionChainElement {
......@@ -96,17 +95,17 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
uint256 _numQueuedTransactions
) external;
// /**
// * Allows the sequencer to append a batch of transactions.
// * @param _transactions Array of raw transaction data.
// * @param _contexts Array of batch contexts.
// * @param _shouldStartAtBatch Specific batch we expect to start appending to.
// * @param _totalElementsToAppend Total number of batch elements we expect to append.
// */
function appendSequencerBatch(
// uint256 _shouldStartAtBatch,
// uint _totalElementsToAppend,
// BatchContext[] memory _contexts,
// bytes[] memory _transactions
/**
* Allows the sequencer to append a batch of transactions.
* param _shouldStartAtBatch Specific batch we expect to start appending to.
* param _totalElementsToAppend Total number of batch elements we expect to append.
* param _contexts Array of batch contexts.
* param _transactionDataFields Array of raw transaction data.
*/
function appendSequencerBatch( // USES CUSTOM ENCODING FOR EFFICIENCY PURPOSES
// uint40 _shouldStartAtBatch,
// uint24 _totalElementsToAppend,
// BatchContext[] _contexts,
// bytes[] _transactionDataFields
) external;
}
......@@ -111,20 +111,16 @@ const encodeAppendSequencerBatch = (
): string => {
let encoding: string
const encodedShouldStartAtBatch = remove0x(BigNumber.from(b.shouldStartAtBatch).toHexString()).padStart(10, '0')
console.log('encodedShouldStartAtBatch', encodedShouldStartAtBatch)
const encodedTotalElementsToAppend = remove0x(BigNumber.from(b.totalElementsToAppend).toHexString()).padStart(6, '0')
console.log('encodedTotalElementsToAppend', encodedTotalElementsToAppend)
const encodedContextsHeader = remove0x(BigNumber.from(b.contexts.length).toHexString()).padStart(6, '0')
const encodedContexts = encodedContextsHeader + b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), '')
console.log('encodedContexts', encodedContexts)
const encodedTransactionData = b.transactions.reduce((acc, cur) => {
if (cur.length % 2 !== 0) throw new Error('Unexpected uneven hex string value!')
const encodedTxDataHeader = remove0x(BigNumber.from(remove0x(cur).length/2).toHexString()).padStart(6, '0')
return acc + encodedTxDataHeader + remove0x(cur)
}, '')
console.log('encodedTransactionData', encodedTransactionData)
return (
encodedShouldStartAtBatch +
encodedTotalElementsToAppend +
......@@ -139,7 +135,6 @@ const appendSequencerBatch = async (
): Promise<TransactionResponse> => {
const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2,10)
const calldata = encodeAppendSequencerBatch(batch)
console.log('Generated batch calldata:', calldata)
return OVM_CanonicalTransactionChain.signer.sendTransaction({
to: OVM_CanonicalTransactionChain.address,
data:'0x' + methodId + calldata,
......@@ -493,30 +488,11 @@ describe('OVM_CanonicalTransactionChain', () => {
)
})
it.only('should revert if expected start does not match current total batches', async () => {
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = (await getNextBlockNumber(ethers.provider)) + 100
const batch: AppendSequencerBatchParams = {
shouldStartAtBatch: 99,
totalElementsToAppend: 88,
contexts: [
{
numSequencedTransactions: 69,
numSubsequentQueueTransactions: 42,
timestamp,
blockNumber,
},
],
transactions: ['0x1234'],
}
const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, batch)
})
it('should revert if expected start does not match current total batches', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
['0x1234'],
[
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0,
......@@ -524,19 +500,19 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0,
},
],
1234,
1
)
).to.be.revertedWith(
shouldStartAtBatch: 1234,
totalElementsToAppend: 1
}
)).to.be.revertedWith(
'Actual batch start index does not match expected start index.'
)
})
it('should revert if not called by the sequencer', async () => {
await expect(
OVM_CanonicalTransactionChain.connect(signer).appendSequencerBatch(
['0x1234'],
[
appendSequencerBatch(OVM_CanonicalTransactionChain.connect(signer), {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0,
......@@ -544,34 +520,37 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0,
},
],
0,
1
)
).to.be.revertedWith('Function can only be called by the Sequencer.')
shouldStartAtBatch: 0,
totalElementsToAppend: 1
}
)).to.be.revertedWith('Function can only be called by the Sequencer.')
})
it('should revert if no contexts are provided', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(['0x1234'], [], 0, 1)
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [],
shouldStartAtBatch: 0,
totalElementsToAppend: 1
})
).to.be.revertedWith('Must provide at least one batch context.')
})
it('should revert if total elements to append is zero', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
['0x1234'],
[
{
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [{
numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0,
timestamp: 0,
blockNumber: 0,
},
],
0,
0
)
).to.be.revertedWith('Must append at least one element.')
}],
shouldStartAtBatch: 0,
totalElementsToAppend: 0
}
)).to.be.revertedWith('Must append at least one element.')
})
for (const size of ELEMENT_TEST_SIZES) {
......@@ -590,9 +569,10 @@ describe('OVM_CanonicalTransactionChain', () => {
)
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
['0x1234'],
[
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0,
......@@ -600,9 +580,9 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0,
},
],
0,
1
)
shouldStartAtBatch: 0,
totalElementsToAppend: 1
})
).to.be.revertedWith(
'Older queue batches must be processed before a new sequencer batch.'
)
......@@ -612,9 +592,9 @@ describe('OVM_CanonicalTransactionChain', () => {
const timestamp = (await getEthTime(ethers.provider)) + 1000
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
['0x1234'],
[
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0,
......@@ -622,8 +602,9 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0,
},
],
0,
1
shouldStartAtBatch: 0,
totalElementsToAppend: 1
}
)
).to.be.revertedWith('Sequencer transactions timestamp too high.')
})
......@@ -633,9 +614,9 @@ describe('OVM_CanonicalTransactionChain', () => {
const blockNumber = (await getNextBlockNumber(ethers.provider)) + 100
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
['0x1234'],
[
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0,
......@@ -643,8 +624,9 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: blockNumber,
},
],
0,
1
shouldStartAtBatch: 0,
totalElementsToAppend: 1
}
)
).to.be.revertedWith('Sequencer transactions blockNumber too high.')
})
......@@ -674,12 +656,12 @@ describe('OVM_CanonicalTransactionChain', () => {
it('should append the given number of transactions', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions,
contexts,
0,
size
)
shouldStartAtBatch: 0,
totalElementsToAppend: size
})
)
.to.emit(OVM_CanonicalTransactionChain, 'SequencerBatchAppended')
.withArgs(0, 0)
......@@ -722,11 +704,12 @@ describe('OVM_CanonicalTransactionChain', () => {
it('should append the batch', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions,
contexts,
0,
size * 2
shouldStartAtBatch: 0,
totalElementsToAppend: size * 2
}
)
)
.to.emit(OVM_CanonicalTransactionChain, 'SequencerBatchAppended')
......@@ -762,12 +745,12 @@ describe('OVM_CanonicalTransactionChain', () => {
it('should append the batch', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions,
contexts,
0,
size + spacing
)
shouldStartAtBatch: 0,
totalElementsToAppend: size + spacing
})
)
.to.emit(OVM_CanonicalTransactionChain, 'SequencerBatchAppended')
.withArgs(0, spacing)
......@@ -802,9 +785,14 @@ describe('OVM_CanonicalTransactionChain', () => {
return '0x' + '12' + '34'.repeat(idx)
})
await OVM_CanonicalTransactionChain.connect(
await appendSequencerBatch(OVM_CanonicalTransactionChain.connect(
sequencer
).appendSequencerBatch(transactions, contexts, 0, size)
), {
transactions,
contexts,
shouldStartAtBatch: 0,
totalElementsToAppend: size
})
})
it(`should return ${size}`, async () => {
......
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