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

Feature/vc improvement #133

Merged
merged 7 commits into from
Feb 1, 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
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ module.exports.MTBridge = require('./src/mt-bridge');
module.exports.mtBridgeUtils = require('./src/mt-bridge-utils');
module.exports.Database = require('./src/database');
module.exports.l1InfoTreeUtils = require('./src/l1-info-tree-utils');
module.exports.VirtualCountersManager = require('./src/virtual-counters-manager');
module.exports.blockUtils = require('./src/block-utils');
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"@ethereumjs/block": "^3.6.2",
"@ethereumjs/tx": "^3.4.0",
"@polygon-hermez/common": "2.6.4",
"@polygon-hermez/vm": "5.7.42",
"@polygon-hermez/vm": "6.0.11",
"ethereumjs-util": "^7.1.4",
"ethers": "^5.5.4",
"ffjavascript": "^0.2.55",
Expand Down
9 changes: 5 additions & 4 deletions src/processor-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,14 +374,15 @@ async function computeL2TxHash(tx, returnEncoded = false) {
}
// Add value
hash += `${formatL2TxHashParam(tx.value, 32)}`;
let { data } = tx;
// Compute data length
if (tx.data.startsWith('0x')) {
tx.data = tx.data.slice(2);
if (data.startsWith('0x')) {
data = data.slice(2);
}
const dataLength = Math.ceil(tx.data.length / 2);
const dataLength = Math.ceil(data.length / 2);
hash += `${formatL2TxHashParam(dataLength.toString(16), 3)}`;
if (dataLength > 0) {
hash += `${formatL2TxHashParam(tx.data, dataLength)}`;
hash += `${formatL2TxHashParam(data, dataLength)}`;
}
// Add chainID
if (typeof tx.chainID !== 'undefined') {
Expand Down
85 changes: 77 additions & 8 deletions src/processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ const { Block } = require('@ethereumjs/block');
const {
Address, BN, toBuffer, bufferToInt,
} = require('ethereumjs-util');

const { cloneDeep } = require('lodash');
const { Scalar } = require('ffjavascript');
const SMT = require('./smt');
const TmpSmtDB = require('./tmp-smt-db');
const Constants = require('./constants');
const stateUtils = require('./state-utils');
const smtUtils = require('./smt-utils');
const VirtualCountersManager = require('./virtual-counters-manager');

const { getCurrentDB } = require('./smt-utils');
const { calculateAccInputHash, calculateSnarkInput, calculateBatchHashData } = require('./contract-utils');
Expand Down Expand Up @@ -74,6 +75,7 @@ module.exports = class Processor {
vm,
options,
extraData,
smtLevels,
ignasirv marked this conversation as resolved.
Show resolved Hide resolved
) {
this.db = db;
this.newNumBatch = numBatch;
Expand All @@ -83,7 +85,7 @@ module.exports = class Processor {
this.F = poseidon.F;
this.tmpSmtDB = new TmpSmtDB(db);
this.smt = new SMT(this.tmpSmtDB, poseidon, poseidon.F);

this.smt.setMaxLevel(smtLevels);
this.rawTxs = [];
this.decodedTxs = [];
this.builded = false;
Expand All @@ -107,6 +109,8 @@ module.exports = class Processor {
this.l1InfoTree = {};

this.vm = vm;
this.oldVm = cloneDeep(vm);
this.oldVm.stateManager = vm.stateManager.copy();
this.evmSteps = [];
this.updatedAccounts = {};
this.isLegacyTx = false;
Expand All @@ -117,6 +121,7 @@ module.exports = class Processor {
this.txIndex = 0;
this.logIndex = 0;
this.blockInfoRoot = [this.F.zero, this.F.zero, this.F.zero, this.F.zero];
this.vcm = new VirtualCountersManager(options.vcmConfig);
}

/**
Expand Down Expand Up @@ -157,6 +162,11 @@ module.exports = class Processor {
await this._computeStarkInput();

this.builded = true;

// Check virtual counters
const virtualCounters = this.vcm.getCurrentSpentCounters();

return { virtualCounters };
}

/**
Expand Down Expand Up @@ -343,14 +353,43 @@ module.exports = class Processor {
* B: ENOUGH UPFRONT TX COST
* Process transaction will perform the following operations
* from: increase nonce
* from: substract total tx cost
* from: subtract total tx cost
* from: refund unused gas
* to: increase balance
* update state
* finally pay all the fees to the sequencer address
*/
async _processTx() {
this.vcm.setSMTLevels((2 ** this.smt.maxLevel + 50000).toString(2).length);
// Compute init processing counters
this.vcm.computeFunctionCounters('batchProcessing', { batchL2DataLength: (this.rawTxs.join('').length - this.rawTxs.length * 2) / 2 });
// Compute rlp parsing counters
for (let i = 0; i < this.decodedTxs.length; i++) {
if (this.decodedTxs[i].tx.type === Constants.TX_CHANGE_L2_BLOCK) {
this.vcm.computeFunctionCounters('decodeChangeL2BlockTx');
} else {
const txDataLen = this.decodedTxs[i].tx.data ? (this.decodedTxs[i].tx.data.length - 2) / 2 : 0;
this.vcm.computeFunctionCounters('rlpParsing', {
txRLPLength: (this.rawTxs[i].length - 2) / 2,
txDataLen,
v: Scalar.e(this.decodedTxs[i].tx.v),
r: Scalar.e(this.decodedTxs[i].tx.r),
s: Scalar.e(this.decodedTxs[i].tx.s),
gasPriceLen: (this.decodedTxs[i].tx.gasPrice.length - 2) / 2,
gasLimitLen: (this.decodedTxs[i].tx.gasLimit.length - 2) / 2,
valueLen: (this.decodedTxs[i].tx.value.length - 2) / 2,
chainIdLen: typeof this.decodedTxs[i].tx.chainID === 'undefined' ? 0 : (ethers.utils.hexlify(this.decodedTxs[i].tx.chainID).length - 2) / 2,
nonceLen: (this.decodedTxs[i].tx.nonce.length - 2) / 2,
});
}
}
for (let i = 0; i < this.decodedTxs.length; i++) {
/**
* Set vcm poseidon levels. Maximum (theoretical) levels that can be added in a tx is 50k poseidons.
* We count how much can the smt increase in a tx and compute the virtual poseidons with this worst case scenario. This is a tx full of SSTORE (20000 gas), max gas in a tx is 30M so we can do 1.5k sstore in a tx. Assuming tree already has 0 leafs, increases to 2**11. 11*1.5k = 22.5k * 2 (go up and down in the tree) = 45k poseidon, we round up to 50k for safety reasons. This happens in case the tree levels are greater than 32, in case are lower, we will put the levels to 32
*/
const maxLevelPerTx = (2 ** this.smt.maxLevel + 50000).toString(2).length < 32 ? 32 : (2 ** this.smt.maxLevel + 50000).toString(2).length;
this.vcm.setSMTLevels(maxLevelPerTx);
const currentDecodedTx = this.decodedTxs[i];

// skip verification first tx is a changeL2Block
Expand Down Expand Up @@ -419,8 +458,8 @@ module.exports = class Processor {
}
const v = this.isLegacyTx ? currentTx.v : Number(currentTx.v) - 27 + currentTx.chainID * 2 + 35;

const bytecodeLength = await stateUtils.getContractBytecodeLength(currentTx.from, this.smt, this.currentStateRoot);
if (bytecodeLength > 0) {
const bytecodeLen = await stateUtils.getContractBytecodeLength(currentTx.from, this.smt, this.currentStateRoot);
if (bytecodeLen > 0) {
currentDecodedTx.isInvalid = true;
currentDecodedTx.reason = 'TX INVALID: EIP3607 Do not allow transactions for which tx.sender has any code deployed';
continue;
Expand Down Expand Up @@ -461,8 +500,25 @@ module.exports = class Processor {

const evmBlock = Block.fromBlockData(blockData, { common: evmTx.common });
try {
const txResult = await this.vm.runTx({ tx: evmTx, block: evmBlock, effectivePercentage: currentTx.effectivePercentage });
this.evmSteps.push(txResult.execResult.evmSteps);
// init custom counters snapshot
this.vcm.initCustomSnapshot(i);
const txResult = await this.vm.runTx({
tx: evmTx, block: evmBlock, effectivePercentage: currentTx.effectivePercentage, vcm: this.vcm,
});
// Compute counters
let bytecodeLength;
let isDeploy = false;
if (typeof currentDecodedTx.tx.to === 'undefined' || currentDecodedTx.tx.to.length <= 2) {
bytecodeLength = txResult.execResult.returnValue.length;
isDeploy = true;
} else {
bytecodeLength = await stateUtils.getContractBytecodeLength(currentDecodedTx.tx.to, this.smt, this.currentStateRoot);
}
this.vcm.computeFunctionCounters('processTx', { bytecodeLength, isDeploy });
this.evmSteps.push({
steps: txResult.execResult.evmSteps,
counters: this.vcm.computeCustomSnapshotConsumption(i),
});

currentDecodedTx.receipt = txResult.receipt;
currentDecodedTx.createdAddress = txResult.createdAddress;
Expand Down Expand Up @@ -621,8 +677,16 @@ module.exports = class Processor {
await this.consolidateBlock();
}
}
/**
* We check how much the smt has increased. In case it has increased more than expected, it means we may have a pow attack so we have to recompute counters cost with this new smt levels
* We re run the batch with a new maxLevel value at the smt
*/
if (this.smt.maxLevel > maxLevelPerTx) {
console.log('WARNING: smt levels increased more than expected, re running batch with new smt levels');
this._rollbackBatch();
i = -1;
}
}

await this.consolidateBlock();
}

Expand Down Expand Up @@ -708,6 +772,9 @@ module.exports = class Processor {
}

async _processChangeL2BlockTx(tx) {
// Reduce counters
this.vcm.computeFunctionCounters('processChangeL2Block');

// write old blockhash (oldStateRoot) on storage
// Get old blockNumber
const oldBlockNumber = await stateUtils.getContractStorage(
Expand Down Expand Up @@ -867,6 +934,8 @@ module.exports = class Processor {

_rollbackBatch() {
this.currentStateRoot = this.oldStateRoot;
this.vm = cloneDeep(this.oldVm);
this.vm.stateManager = this.oldVm.stateManager.copy();
this.updatedAccounts = {};
}

Expand Down
24 changes: 19 additions & 5 deletions src/smt.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ class SMT {
this.hash = hash;
this.F = F;
this.empty = [F.zero, F.zero, F.zero, F.zero];
// set init smt level to 0
this.maxLevel = 0;
}

/**
* Set current smt level
* @param {Number} level max smt level
*/
setMaxLevel(level) {
this.maxLevel = level;
}

/**
Expand All @@ -31,12 +41,12 @@ class SMT {
* {Array[Field]} key modified,
* {Array[Array[Field]]} siblings: array of siblings,
* {Array[Field]} insKey: inserted key,
* {Scalar} insValue: insefted value,
* {Scalar} insValue: inserted value,
* {Bool} isOld0: is new insert or delete,
* {Scalar} oldValue: old leaf value,
* {Scalar} newValue: new leaf value,
* {String} mode: action performed by the insertion,
* {Number} proofHashCounter: counter of hashs must be done to proof this operation
* {Number} proofHashCounter: counter of hash must be done to proof this operation
*/
async set(oldRoot, key, value) {
const self = this;
Expand Down Expand Up @@ -101,7 +111,6 @@ class SMT {

level -= 1;
accKey.pop();

// calculate hash to validate oldRoot
proofHashCounter = nodeIsZero(oldRoot, F) ? 0 : (siblings.slice(0, level + 1).length + (((foundVal ?? 0n) === 0n) ? 0 : 2));

Expand Down Expand Up @@ -270,6 +279,11 @@ class SMT {
}
}

// Update max level in case last insertion increased smt size
if (this.maxLevel < siblings.length) {
this.maxLevel = siblings.length;
}

return {
oldRoot,
newRoot,
Expand All @@ -290,7 +304,7 @@ class SMT {
/**
* Get value merkle-tree
* @param {Array[Field]} root - merkle-tree root
* @param {Array[Field]} key - path to retoreve the value
* @param {Array[Field]} key - path to retrieve the value
* @returns {Object} Information about the value to retrieve
* {Array[Field]} root: merkle-tree root,
* {Array[Field]} key: key to look for,
Expand All @@ -299,7 +313,7 @@ class SMT {
* {Bool} isOld0: is new insert or delete,
* {Array[Field]} insKey: key found,
* {Scalar} insValue: value found,
* {Number} proofHashCounter: counter of hashs must be done to proof this operation
* {Number} proofHashCounter: counter of hash must be done to proof this operation
*/
async get(root, key) {
const self = this;
Expand Down
Loading
Loading