UNPKG

eslint-plugin-o1js

Version:

o1js rules for ESLint

179 lines 8.13 kB
"use strict"; const types_1 = require("../types"); const ast_utils_1 = require("../utils/ast-utils"); const selectors_1 = require("../utils/selectors"); // A map containing all Smart Contracts that we can immediately derive state count from const knownContractState = new Map(); // A map containing all Smart Contracts that need their state to be derived from other Smart Contracts const unknownContractState = new Map(); const rule = { meta: { messages: { noGreaterStorageLimitInCircuit: `A Smart Contract can only have ${types_1.MAX_CONTRACT_STATES} allowed storage fields.`, }, schema: [], type: 'problem', docs: { description: `A Smart Contract can only have ${types_1.MAX_CONTRACT_STATES} allowed storage fields.`, recommended: 'error', url: '', }, }, create(context) { return { 'Program:exit': function () { let derivedUnknownState = true; // Continue to derive unknown states if we can succesfully derive a previous unknown state. while (derivedUnknownState) { derivedUnknownState = false; // Assume we can't derive an unknown state to break out of the while unknownContractState.forEach((stateInfo, className) => { stateInfo = stateInfo.map((state) => { var _a; if (state.kind === 'UnknownStateInfo') { const { dependsOn, type, node } = state; const contractState = (_a = knownContractState.get(dependsOn)) !== null && _a !== void 0 ? _a : []; const contractStateCount = calculateContractState(contractState); // Succesfully derived an unknown state, continue looping in the parent while loop if (contractStateCount > 0) { derivedUnknownState = true; return { kind: 'KnownStateInfo', size: contractStateCount, type, node, }; } } return state; }); const unknownRemaining = stateInfo.filter((state) => { return state.kind === 'UnknownStateInfo'; }).length; if (unknownRemaining === 0) { unknownContractState.delete(className); knownContractState.set(className, stateInfo); } }); } for (const [, contractState] of knownContractState) { const stateDecoratorInfo = getStateDecoratorInfo(contractState); const contractStateCount = calculateContractState(contractState); if (stateDecoratorInfo && !stateDecoratorInfo.reported) { if (contractStateCount > types_1.MAX_CONTRACT_STATES) { context.report({ messageId: `noGreaterStorageLimitInCircuit`, loc: stateDecoratorInfo.node.loc, }); stateDecoratorInfo.reported = true; // Set reported to true to avoid reporting same error in multiple files } } } }, [selectors_1.SMART_CONTRACT_DEFINITION]: function (smartContractNode) { findKnownAndUnknownStates(smartContractNode); }, [selectors_1.CIRCUIT_VALUE_DEFINITION]: function (circuitValueNode) { findKnownAndUnknownStates(circuitValueNode); }, }; }, }; function calculateContractState(contractState) { return contractState.reduce((acc, state) => { if (state.type.kind === 'arrayProp') { return acc + state.size * state.type.arrayPropLength; } else { return acc + state.size; } }, 0); } function getStateDecoratorInfo(stateInfo) { return stateInfo.find((state) => { return state.type.kind === 'state'; }); } function findKnownAndUnknownStates(smartContractNode) { var _a, _b, _c, _d; const stateInfo = []; const classBody = (_a = (0, ast_utils_1.getClassBodyStatements)(smartContractNode)) !== null && _a !== void 0 ? _a : []; let isAllPrimitiveState = true; for (const classStatement of classBody) { // Get the kind of decorator (`prop`, `state` or `arrayProp`) as well as the TS decorator node const decorator = (0, ast_utils_1.getValidDecorator)(classStatement); if (!decorator) { continue; } // Get the user specified type from the decorator node (e.g `Field`, `Group` or a CircuitValue) const o1DecoratorType = (_b = (0, ast_utils_1.getFirstDecoratorValue)(decorator.decorator)) !== null && _b !== void 0 ? _b : (0, ast_utils_1.getPropertyType)(classStatement); if (!o1DecoratorType) { continue; } // If the decorator type is a o1js primitive, get it's value const primitive = (0, types_1.geto1jsPrimitive)(o1DecoratorType); if (!primitive) { isAllPrimitiveState = false; } if (decorator.kind === 'prop' || decorator.kind === 'state') { if (primitive) { const { size } = types_1.o1jsPrimitiveSizeInfo[primitive]; stateInfo.push({ kind: 'KnownStateInfo', type: { kind: decorator.kind }, node: smartContractNode, reported: false, size, }); } else { stateInfo.push({ kind: 'UnknownStateInfo', type: { kind: decorator.kind, }, node: smartContractNode, dependsOn: o1DecoratorType, }); } } else if (decorator.kind === 'arrayProp') { const arrayPropLength = (_c = (0, ast_utils_1.getSecondDecoratorValue)(decorator.decorator)) !== null && _c !== void 0 ? _c : 0; if (primitive) { const { size } = types_1.o1jsPrimitiveSizeInfo[primitive]; stateInfo.push({ kind: 'KnownStateInfo', type: { kind: decorator.kind, arrayPropLength, }, node: smartContractNode, reported: false, size, }); } else { stateInfo.push({ kind: 'UnknownStateInfo', type: { kind: decorator.kind, arrayPropLength, }, node: smartContractNode, dependsOn: o1DecoratorType, }); } } } // If all decorators are a o1js primitive, we can calculate the Smart Contract state count and // insert it into the `knownContractStateMap`. If the user specifies a CircuitValue, we store the state info // in `unknownContractState` to derive it's state count later. const className = (_d = (0, ast_utils_1.getClassName)(smartContractNode)) !== null && _d !== void 0 ? _d : ''; if (isAllPrimitiveState) { knownContractState.set(className, stateInfo); } else { unknownContractState.set(className, stateInfo); } } module.exports = rule; //# sourceMappingURL=no-greater-storage-limit-in-circuit.js.map