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

add blobOuter, aggregation & final #153

Merged
merged 1 commit into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions src/blob-inner/blob-outer-processor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/* eslint-disable max-len */
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-continue */
const { Scalar } = require('ffjavascript');
const Constants = require('../constants');

module.exports = class BlobOuter {
/**
* constructor BlobOuterProcessor class
* @param {Object} blobInner - blobInner stark inputs - outputs
* @param {Object} aggBatches - aggregateBatches stark inputs - outputs
*/
constructor(
blobInner,
aggBatches,
) {
// inputs
this.blobInner = blobInner;
this.aggBatches = aggBatches;

// internal use
this.builded = false;

// initialize outputs thta may chnage depending the blobInner and aggBatches
this.newStateRoot = this.blobInner.oldStateRoot;
this.newLocalExitRoot = Constants.ZERO_BYTES32;
this.starkInput = {};
}

/**
* Execute Blob Outer
*/
async execute() {
// build blobData
await this._processBlobOuter();

// compute stark input
await this._computeStarkInput();

this.builded = true;
}

/**
* Process Blob Outer
*/
_processBlobOuter() {
// sanity check between blobInner and aggregation batches
// oldStateRoot
if (this.blobInner.oldStateRoot !== this.aggBatches.oldStateRoot) {
throw new Error('BlobOuter:_processBlobOuter: oldStateRoot mismatch');
}

// forkID
if (this.blobInner.forkID !== this.aggBatches.forkID) {
throw new Error('BlobOuter:_processBlobOuter: oldBlobStateRoot mismatch');
}

// add table to map final outputs
// | isInvalid | isZero(accBatch_blob) | isEqualAccBatch | isTimeOK | isIndexEqual | isRootEqual | **newLocalExitRoot** | **newStateRoot** | **FAIL PROOF** |
// |:---------:|:---------------------:|:---------------:|:--------:|:------------:|:-----------:|:--------------------:|:----------------:|:--------------:|
// | 1 | X | X | X | X | X | blob_ler | blob_SR | 0 |
// | 0 | 1 | X | X | X | X | blob_ler | blob_SR | 0 |
// | 0 | 0 | 0 | X | X | X | X | X | 1 |
// | 0 | 0 | 1 | 0 | X | X | blob_ler | blob_SR | 0 |
// | 0 | 0 | 1 | 1 | 0 | X | blob_ler | blob_SR | 0 |
// | 0 | 0 | 1 | 1 | 1 | 0 | X | X | 1 |
// | 0 | 0 | 1 | 1 | 1 | 1 | batch_SR | batch_ler | 0 |

if (this.blobInner.isInvalid) {
this.newLocalExitRoot = this.blobInner.localExitRootFromBlob;
this.newStateRoot = this.blobInner.oldStateRoot;

return;
}

// isZero(accBatch_blob)
if (Scalar.eq(this.blobInner.newBlobAccInputHash, Constants.ZERO_BYTES32)) {
this.newLocalExitRoot = this.blobInner.localExitRootFromBlob;
this.newStateRoot = this.blobInner.oldStateRoot;

return;
}

if (Scalar.neq(this.blobInner.finalAccBatchHashData, this.aggBatches.newBatchAccInputHash)) {
throw new Error('BlobOuter:_processBlobOuter: accBatchHashData mismatch');
}

if (!Scalar.leq(this.aggBatches.newLastTimestamp, this.blobInner.timestampLimit)) {
this.newLocalExitRoot = this.blobInner.localExitRootFromBlob;
this.newStateRoot = this.blobInner.oldStateRoot;

return;
}

if (Scalar.neq(this.blobInner.lastL1InfoTreeIndex, this.aggBatches.currentL1InfoTreeIndex)) {
this.newLocalExitRoot = this.blobInner.localExitRootFromBlob;
this.newStateRoot = this.blobInner.oldStateRoot;

return;
}

if (Scalar.neq(this.blobInner.lastL1InfoTreeRoot, this.aggBatches.currentL1InfoTreeRoot)) {
throw new Error('BlobOuter:_processBlobOuter: L1InfoTreeRoot mismatch');
}

// set outputs from batch aggregation
this.newLocalExitRoot = this.aggBatches.newLocalExitRoot;
this.newStateRoot = this.aggBatches.newStateRoot;
}

async _computeStarkInput() {
this.starkInput = {
// inputs
oldStateRoot: this.aggBatches.oldStateRoot,
oldBlobStateRoot: this.blobInner.oldBlobStateRoot,
oldBlobAccInputHash: this.blobInner.oldBlobAccInputHash,
oldNumBlob: this.blobInner.oldNumBlob,
chainID: this.aggBatches.chainID,
forkID: this.blobInner.forkID,
// outputs
newStateRoot: this.newStateRoot,
newBlobStateRoot: this.blobInner.newBlobStateRoot,
newBlobAccInputHash: this.blobInner.newBlobAccInputHash,
newNumBlob: this.blobInner.newNumBlob,
newLocalExitRoot: this.newLocalExitRoot,
};
}

/**
* Return stark input
*/
getStarkInput() {
this._isBuilded();

return this.starkInput;
}

/**
* Throw error if blob outer is not already builded
*/
_isNotBuilded() {
if (this.builded) {
throw new Error('BlobProcessor:_isBuilded: already builded');
}
}

/**
* Throw error if blob outer is already builded
*/
_isBuilded() {
if (!this.builded) {
throw new Error('BlobProcessor:_isBuilded: must first be builded');
}
}
};
4 changes: 2 additions & 2 deletions src/blob-inner/blob-processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ module.exports = class BlobProcessor {
);

// invalidate blob
if (this.inInvalid === true) {
if (this.isInvalid === true) {
this.finalAccBatchHashData = Constants.ZERO_BYTES32;
} else {
// compute finalAccBatchHashData
Expand All @@ -357,7 +357,7 @@ module.exports = class BlobProcessor {
this.finalAccBatchHashData,
await computeBatchL2HashData(batchData),
this.sequencerAddress,
this.forcedHashData,
(this.blobType === blobConstants.BLOB_TYPE.FORCED) ? this.forcedHashData : Constants.ZERO_BYTES32,
);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ module.exports.DB_GLOBAL_EXIT_ROOT = ethers.utils.id(('ZKEVM_DB_GLOBAL_EXIT_ROOT
module.exports.DB_ADDRESS_STORAGE = ethers.utils.id(('ZKEVM_DB_ADDRESS_STORAGE'));
module.exports.DB_TOUCHED_ACCOUNTS = ethers.utils.id(('ZKEVM_DB_TOUCHED_ACCOUNTS'));
module.exports.DB_STARK_INPUT = ethers.utils.id(('ZKEVM_DB_STARK_INPUT'));
module.exports.DB_AGG_BATCHES = ethers.utils.id(('ZKEVM_DB_AGG_BATCHES'));
module.exports.DB_STARK_BLOB_INNER = ethers.utils.id(('ZKEVM_DB_BLOB_INNER'));
// blob DB keys
module.exports.DB_OUTER_STATE_ROOT = ethers.utils.id(('DB_OUTER_STATE_ROOT'));
module.exports.DB_BLOB_STATE_ROOT = ethers.utils.id(('DB_BLOB_STATE_ROOT'));
module.exports.DB_BLOB_ACC_INPUT_HASH = ethers.utils.id(('DB_BLOB_ACC_INPUT_HASH'));
module.exports.DB_LAST_NUM_BLOB = ethers.utils.id(('DB_LAST_NUM_BLOB'));
module.exports.DB_OUTER_LOCAL_EXIT_ROOT = ethers.utils.id(('DB_OUTER_LOCAL_EXIT_ROOT'));
module.exports.DB_STARK_BLOB_OUTER = ethers.utils.id(('DB_STARK_BLOB_OUTER'));

// Default values and global constants
module.exports.DEFAULT_MAX_TX = 1000;
Expand Down
100 changes: 63 additions & 37 deletions src/contract-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,88 @@ const { Scalar } = require('ffjavascript');
const { sha256Snark, padZeros } = require('./utils');

/**
* Compute input for SNARK circuit: sha256(aggrAddress, oldStateRoot, oldAccInputHash, oldNumBatch, chainID, forkID, newStateRoot, newAccInputHash, newLocalExitRoot, newNumBatch) % FrSNARK
* @param {String} oldStateRoot - Current state Root
* @param {String} newStateRoot - New State root once the batch is processed
* @param {String} oldBatchAccInputHash - initial accumulateInputHash
* @param {String} newBatchAccInputHash - final accumulateInputHash
* @param {String} newLocalExitRoot - New local exit root once the all batches is processed
* @param {Number} oldNumBatch - initial batch number
* @param {Number} newNumBatch - final batch number
* @param {Number} chainID - L2 chainID
* @param {String} aggregatorAddress - Aggregator Ethereum address in hex string
* @param {Number} forkID - L2 rom fork identifier
* Compute input for SNARK circuit: sha256(
* initStateRoot, initBlobStateRoot, initBlobAccInputHash, initNumBlob, chainId, forkID
* finalStateRoot, finalBlobStateRoot, finalBlobAccInputHash, finalNumBlob, finalLocalExitRoot
* aggregatorAddress
* ) % FrSNARK
* @param {String} initStateRoot - old state root in hex encoding
* @param {String} initBlobStateRoot - old blob state root in hex encoding
* @param {String} initBlobAccInputHash - old blob account input hash in hex encoding
* @param {Number} initNumBlob - old number of blobs
* @param {Number} chainId - chain id
* @param {Number} forkID - fork id
* @param {String} initStateRoot - new state root in hex encoding
* @param {String} initBlobStateRoot - new blob state root in hex encoding
* @param {String} initBlobAccInputHash - new blob account input hash in hex encoding
* @param {Number} initNumBlob - new number of blobs
* @param {String} initLocalExitRoot - new local exit root in hex encoding
* @param {String} aggregatorAddress - aggregator address in hex encoding
* @returns {String} - input snark in hex encoding
*/
async function calculateSnarkInput(
oldStateRoot,
newStateRoot,
newLocalExitRoot,
oldBatchAccInputHash,
newBatchAccInputHash,
chainID,
aggregatorAddress,
initStateRoot,
initBlobStateRoot,
initBlobAccInputHash,
initNumBlob,
chainId,
forkID,
finalStateRoot,
finalBlobStateRoot,
finalBlobAccInputHash,
finalNumBlob,
finalLocalExitRoot,
aggregatorAddress,
) {
// 20 bytes aggregator address
const strAggregatorAddress = padZeros((Scalar.fromString(aggregatorAddress, 16)).toString(16), 40);
// 32 bytes each field element for initStateRoot
const strInitStateRoot = padZeros((Scalar.fromString(initStateRoot, 16)).toString(16), 64);

// 32 bytes each field element for initBlobStateRoot
const strInitBlobStateRoot = padZeros((Scalar.fromString(initBlobStateRoot, 16)).toString(16), 64);

// 32 bytes each field element for oldStateRoot
const strOldStateRoot = padZeros((Scalar.fromString(oldStateRoot, 16)).toString(16), 64);
// 32 bytes each field element for initBlobAccInputHash
const strInitBlobAccInputHash = padZeros((Scalar.fromString(initBlobAccInputHash, 16)).toString(16), 64);

// 32 bytes each field element for oldStateRoot
const strOldBatchAccInputHash = padZeros((Scalar.fromString(oldBatchAccInputHash, 16)).toString(16), 64);
// 8 bytes for initNumBlob
const strInitNumBlob = padZeros(Scalar.e(initNumBlob).toString(16), 16);

// 8 bytes for chainID
const strChainID = padZeros(Scalar.e(chainID).toString(16), 16);
const strChainID = padZeros(Scalar.e(chainId).toString(16), 16);

// 8 bytes for forkID
const strForkID = padZeros(Scalar.e(forkID).toString(16), 16);

// 32 bytes each field element for oldStateRoot
const strNewStateRoot = padZeros((Scalar.fromString(newStateRoot, 16)).toString(16), 64);
// 32 bytes each field element for finalStateRoot
const strFinalStateRoot = padZeros((Scalar.fromString(finalStateRoot, 16)).toString(16), 64);

// 32 bytes each field element for oldStateRoot
const strNewBatchAccInputHash = padZeros((Scalar.fromString(newBatchAccInputHash, 16)).toString(16), 64);
// 32 bytes each field element for finalBlobStateRoot
const strFinalBlobStateRoot = padZeros((Scalar.fromString(finalBlobStateRoot, 16)).toString(16), 64);

// 32 bytes each field element for oldStateRoot
const strNewLocalExitRoot = padZeros((Scalar.fromString(newLocalExitRoot, 16)).toString(16), 64);
// 32 bytes each field element for finalBlobAccInputHash
const strFinalBlobAccInputHash = padZeros((Scalar.fromString(finalBlobAccInputHash, 16)).toString(16), 64);

// 8 bytes for finalNumBlob
const strFinalNumBlob = padZeros(Scalar.e(finalNumBlob).toString(16), 16);

// 32 bytes each field element for finalLocalExitRoot
const strFinalLocalExitRoot = padZeros((Scalar.fromString(finalLocalExitRoot, 16)).toString(16), 64);

// 20 bytes aggregator address
const strAggregatorAddress = padZeros((Scalar.fromString(aggregatorAddress, 16)).toString(16), 40);

// build final bytes sha256
const finalStr = strAggregatorAddress
.concat(strOldStateRoot)
.concat(strOldBatchAccInputHash)
const finalStr = strInitStateRoot
.concat(strInitBlobStateRoot)
.concat(strInitBlobAccInputHash)
.concat(strInitNumBlob)
.concat(strChainID)
.concat(strForkID)
.concat(strNewStateRoot)
.concat(strNewBatchAccInputHash)
.concat(strNewLocalExitRoot);
.concat(strFinalStateRoot)
.concat(strFinalBlobStateRoot)
.concat(strFinalBlobAccInputHash)
.concat(strFinalNumBlob)
.concat(strFinalLocalExitRoot)
.concat(strAggregatorAddress);

return sha256Snark(finalStr);
}
Expand Down
6 changes: 3 additions & 3 deletions src/processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ module.exports = class Processor {
// Read Local exit root
await this._readLocalExitRoot();

// Calculate stark and snark input
// Calculate stark
await this._computeStarkInput();

this.builded = true;
Expand Down Expand Up @@ -977,7 +977,7 @@ module.exports = class Processor {
const newStateRoot = smtUtils.h4toString(this.currentStateRoot);
const oldBatchAccInputHash = smtUtils.h4toString(this.oldBatchAccInputHash);
const newLocalExitRoot = smtUtils.h4toString(this.newLocalExitRoot);
const newTimestamp = this.newLastTimestamp.toString();
const newLastTimestamp = this.newLastTimestamp.toString();
this.batchHashData = await computeBatchL2HashData(
this.getBatchL2Data(),
);
Expand All @@ -997,7 +997,7 @@ module.exports = class Processor {
oldBatchAccInputHash,
newBatchAccInputHash, // output
newLocalExitRoot, // output
newTimestamp, // output
newLastTimestamp, // output
chainID: this.chainID,
forkID: this.forkID,
forcedHashData: this.forcedHashData,
Expand Down
Loading