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

Implement caching for parameters and identity storage contracts #2834

Merged
merged 11 commits into from
Dec 25, 2023
15 changes: 15 additions & 0 deletions src/constants/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ export const CONTRACTS = {
HUB_CONTRACT: 'HubContract',
COMMIT_MANAGER_V1_U1_CONTRACT: 'CommitManagerV1U1Contract',
SERVICE_AGREEMENT_V1_CONTRACT: 'ServiceAgreementV1Contract',
PARAMETERS_STORAGE_CONTRACT: 'ParametersStorageContract',
};

export const CONTRACT_EVENTS = {
Expand All @@ -563,6 +564,7 @@ export const CONTRACT_EVENTS = {
PROFILE: ['AskUpdated'],
COMMIT_MANAGER_V1: ['StateFinalized'],
SERVICE_AGREEMENT_V1: ['ServiceAgreementV1Extended', 'ServiceAgreementV1Terminated'],
PARAMETERS_STORAGE: ['ParameterChanged'],
};

export const NODE_ENVIRONMENTS = {
Expand All @@ -586,3 +588,16 @@ export const BLOCK_TIME_MILLIS = {
};

export const TRANSACTION_CONFIRMATIONS = 1;

export const CACHED_FUNCTIONS = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also please move the cached values from the scoring function ?

ParametersStorage: [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make it an object, with parameterName as key and type as value, then we don't need to iterate through all parameters every time.

If we remove types as I suggested in the comment above, we can just make it a Set with constant O(1) lookup

'r0',
'r1',
'r2',
'finalizationCommitsNumber',
'updateCommitWindowDuration',
'commitWindowDurationPerc',
'proofWindowDurationPerc',
'epochLength',
],
};
7 changes: 7 additions & 0 deletions src/modules/blockchain/blockchain-module-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ class BlockchainModuleManager extends BaseModuleManager {
]);
}

cacheParameter(blockchain, parameterName, parameterValue) {
return this.callImplementationFunction(blockchain, 'cacheParameter', [
parameterName,
parameterValue,
]);
}

getPrivateKey(blockchain) {
return this.callImplementationFunction(blockchain, 'getPrivateKey');
}
Expand Down
60 changes: 39 additions & 21 deletions src/modules/blockchain/implementation/web3-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
HTTP_RPC_PROVIDER_PRIORITY,
FALLBACK_PROVIDER_QUORUM,
RPC_PROVIDER_STALL_TIMEOUT,
CACHED_FUNCTIONS,
} from '../../../constants/constants.js';

const require = createRequire(import.meta.url);
Expand Down Expand Up @@ -51,6 +52,7 @@ const ABIs = {
const SCORING_FUNCTIONS = {
1: 'Log2PLDSF',
};
const resultCache = {};

class Web3Service {
async initialize(config, logger) {
Expand Down Expand Up @@ -269,6 +271,10 @@ class Web3Service {
}
}

cacheParameter(parameterName, parameterValue) {
resultCache[parameterName] = parameterValue;
}

initializeContract(contractName, contractAddress) {
if (ABIs[contractName] != null) {
this[`${contractName}Contract`] = new ethers.Contract(
Expand Down Expand Up @@ -406,30 +412,42 @@ class Web3Service {

async callContractFunction(contractInstance, functionName, args) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're using this function for identityId, old logic for getIdentityId function should be removed and we can add it to cached parameters since it's immutable

let result;
while (result === undefined) {
try {
// eslint-disable-next-line no-await-in-loop
result = await contractInstance[functionName](...args);
} catch (error) {
const decodedErrorData = this._decodeErrorData(error, contractInstance.interface);

const functionFragment = contractInstance.interface.getFunction(
error.transaction.data.slice(0, 10),
);
const inputs = functionFragment.inputs
.map((input, i) => {
const argName = input.name;
const argValue = this._formatArgument(args[i]);
return `${argName}=${argValue}`;
})
.join(', ');
if (
CACHED_FUNCTIONS.ParametersStorage.includes(functionName) &&
resultCache[functionName] !== undefined
) {
result = resultCache[functionName];
} else {
while (result === undefined) {
try {
// eslint-disable-next-line no-await-in-loop
result = await contractInstance[functionName](...args);
if (CACHED_FUNCTIONS.ParametersStorage.includes(functionName)) {
resultCache[functionName] = result;
}
} catch (error) {
const decodedErrorData = this._decodeErrorData(
error,
contractInstance.interface,
);

throw new Error(
`Call ${functionName}(${inputs}) failed, reason: ${decodedErrorData}`,
);
const functionFragment = contractInstance.interface.getFunction(
error.transaction.data.slice(0, 10),
);
const inputs = functionFragment.inputs
.map((input, i) => {
const argName = input.name;
const argValue = this._formatArgument(args[i]);
return `${argName}=${argValue}`;
})
.join(', ');

throw new Error(
`Call ${functionName}(${inputs}) failed, reason: ${decodedErrorData}`,
);
}
}
}

return result;
}

Expand Down
17 changes: 17 additions & 0 deletions src/service/blockchain-event-listener-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ class BlockchainEventListenerService {
currentBlock,
CONTRACT_EVENTS.SERVICE_AGREEMENT_V1,
),
this.getContractEvents(
blockchainId,
CONTRACTS.PARAMETERS_STORAGE_CONTRACT,
currentBlock,
CONTRACT_EVENTS.PARAMETERS_STORAGE,
),
];

if (!devEnvironment) {
Expand Down Expand Up @@ -227,6 +233,17 @@ class BlockchainEventListenerService {
}
}

async handleParameterChangedEvents(blockEvents) {
for (const event of blockEvents) {
const { parameterName, parameterValue } = JSON.parse(event.data);
this.blockchainModuleManager.cacheParameter(
event.blockchainId,
parameterName,
parameterValue,
);
}
}

handleNewContractEvents(blockEvents) {
for (const event of blockEvents) {
const { contractName, newContractAddress } = JSON.parse(event.data);
Expand Down