Skip to content

Commit

Permalink
Fix timestamp checks & check versions during upgrades (#306)
Browse files Browse the repository at this point in the history
* Improve timestamp checks

* Fix linter

* Upgrade versions

* Refactor

* add check for contracts version in upgrades

---------

Co-authored-by: daveroga <daveroga@gmail.com>
  • Loading branch information
AndriianChestnykh and daveroga authored Oct 29, 2024
1 parent 1eacd84 commit d3ef181
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 44 deletions.
2 changes: 1 addition & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@iden3/contracts",
"description": "Smart Contract library for Solidity",
"version": "2.4.4",
"version": "2.4.5",
"files": [
"**/*.sol",
"/build/contracts/*.json",
Expand Down
58 changes: 27 additions & 31 deletions contracts/state/State.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ contract State is Ownable2StepUpgradeable, IState {
/**
* @dev Version of contract
*/
string public constant VERSION = "2.6.0";
string public constant VERSION = "2.6.1";

// This empty reserved space is put in place to allow future versions
// of the State contract to inherit from other contracts without a risk of
Expand Down Expand Up @@ -439,24 +439,21 @@ contract State is Ownable2StepUpgradeable, IState {
* @param state State of the identity
* @return replacedAt The timestamp when the state of the identity was replaced by another state
*/
function getStateReplacedAt(
uint256 id,
uint256 state
) external view returns (uint256 replacedAt) {
StateCrossChainStorage storage $ = _getStateCrossChainStorage();
replacedAt = $._idToStateReplacedAt[id][state];
if (replacedAt != 0) {
return replacedAt;
}

if (_stateData.stateExists(id, state)) {
replacedAt = _stateData.getStateInfoByIdAndState(id, state).replacedAtTimestamp;
function getStateReplacedAt(uint256 id, uint256 state) external view returns (uint256) {
if (isIdTypeSupported(GenesisUtils.getIdType(id))) {
if (_stateData.stateExists(id, state)) {
return _stateData.getStateInfoByIdAndState(id, state).replacedAtTimestamp;
} else if (GenesisUtils.isGenesisState(id, state)) {
return 0;
}
revert("State entry not found");
} else {
if (GenesisUtils.isGenesisState(id, state)) {
replacedAt = 0;
} else {
revert("State entry not found");
StateCrossChainStorage storage $ = _getStateCrossChainStorage();
uint256 replacedAt = $._idToStateReplacedAt[id][state];
if (replacedAt != 0) {
return replacedAt;
}
revert("Cross-chain state not found");
}
}

Expand All @@ -466,21 +463,20 @@ contract State is Ownable2StepUpgradeable, IState {
* @param root GIST root
* @return replacedAt The timestamp when the GIST root was replaced by another root
*/
function getGistRootReplacedAt(
bytes2 idType,
uint256 root
) external view returns (uint256 replacedAt) {
StateCrossChainStorage storage $ = _getStateCrossChainStorage();
replacedAt = $._rootToGistRootReplacedAt[idType][root];
if (replacedAt != 0) {
return replacedAt;
}

require(isIdTypeSupported(idType), "id type is not supported");
if (!_gistData.rootExists(root)) {
revert("Gist root entry not found");
function getGistRootReplacedAt(bytes2 idType, uint256 root) external view returns (uint256) {
if (isIdTypeSupported(idType)) {
if (_gistData.rootExists(root)) {
return _gistData.getRootInfo(root).replacedAtTimestamp;
}
revert("GIST root entry not found");
} else {
StateCrossChainStorage storage $ = _getStateCrossChainStorage();
uint256 replacedAt = $._rootToGistRootReplacedAt[idType][root];
if (replacedAt != 0) {
return replacedAt;
}
revert("Cross-chain GIST root not found");
}
replacedAt = _gistData.getRootInfo(root).replacedAtTimestamp;
}

/**
Expand Down
8 changes: 8 additions & 0 deletions helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export const contractsInfo = Object.freeze({
},
UNIVERSAL_VERIFIER: {
name: "UniversalVerifier",
version: "1.1.1",
unifiedAddress: "0xfcc86A79fCb057A8e55C6B853dff9479C3cf607c",
create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.UniversalVerifier")),
verificationOpts: {
Expand All @@ -127,6 +128,7 @@ export const contractsInfo = Object.freeze({
},
STATE: {
name: "State",
version: "2.6.1",
unifiedAddress: "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896",
create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.State")),
verificationOpts: {
Expand All @@ -142,6 +144,7 @@ export const contractsInfo = Object.freeze({
},
VALIDATOR_SIG: {
name: "CredentialAtomicQuerySigV2Validator",
version: "2.1.0",
unifiedAddress: "0x59B347f0D3dd4B98cc2E056Ee6C53ABF14F8581b",
create2Calldata: ethers.hexlify(
ethers.toUtf8Bytes("iden3.create2.CredentialAtomicQuerySigV2Validator"),
Expand All @@ -159,6 +162,7 @@ export const contractsInfo = Object.freeze({
},
VALIDATOR_MTP: {
name: "CredentialAtomicQueryMTPV2Validator",
version: "2.1.0",
unifiedAddress: "0x27bDFFCeC5478a648f89764E22fE415486A42Ede",
create2Calldata: ethers.hexlify(
ethers.toUtf8Bytes("iden3.create2.CredentialAtomicQueryMTPV2Validator"),
Expand All @@ -176,6 +180,7 @@ export const contractsInfo = Object.freeze({
},
VALIDATOR_V3: {
name: "CredentialAtomicQueryV3Validator",
version: "2.1.0-beta.1",
unifiedAddress: "0xd179f29d00Cd0E8978eb6eB847CaCF9E2A956336",
create2Calldata: ethers.hexlify(
ethers.toUtf8Bytes("iden3.create2.CredentialAtomicQueryV3Validator"),
Expand All @@ -193,6 +198,7 @@ export const contractsInfo = Object.freeze({
},
IDENTITY_TREE_STORE: {
name: "IdentityTreeStore",
version: "1.1.0",
unifiedAddress: "0x7dF78ED37d0B39Ffb6d4D527Bb1865Bf85B60f81",
create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.IdentityTreeStore")),
verificationOpts: {
Expand All @@ -208,6 +214,7 @@ export const contractsInfo = Object.freeze({
},
VC_PAYMENT: {
name: "VCPayment",
version: "1.0.0",
unifiedAddress: "",
create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.VCPayment")),
verificationOpts: {
Expand All @@ -217,6 +224,7 @@ export const contractsInfo = Object.freeze({
},
MC_PAYMENT: {
name: "MCPayment",
version: "1.0.0",
unifiedAddress: "",
create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.MCPayment")),
verificationOpts: {
Expand Down
12 changes: 12 additions & 0 deletions helpers/helperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ export async function isContract(
return true;
}

export async function checkContractVersion(
contractName: string,
contractAddress: string,
contractVersion: string,
signer?: any,
): Promise<{ upgraded: boolean; currentVersion: string }> {
const contract = await ethers.getContractAt(contractName, contractAddress, signer);
const version = await contract.VERSION();

return { upgraded: contractVersion === version, currentVersion: version };
}

export async function verifyContract(
contractAddress: any,
opts: {
Expand Down
43 changes: 38 additions & 5 deletions scripts/maintenance/multi-chain/checkUnifiedContracts.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
import { getProviders, isContract, Logger } from "../../../helpers/helperUtils";
import { contractsInfo } from "../../../helpers/constants";
import {
checkContractVersion,
getProviders,
getStateContractAddress,
isContract,
Logger,
} from "../../../helpers/helperUtils";
import { contractsInfo, DEFAULT_MNEMONIC } from "../../../helpers/constants";
import { ethers } from "hardhat";

const mnemonicWallet = ethers.Wallet.fromPhrase(DEFAULT_MNEMONIC);

async function main() {
const providers = getProviders();

for (const provider of providers) {
const jsonRpcProvider = new ethers.JsonRpcProvider(provider.rpcUrl);

const contractsNotDeployed: string[] = [];
const contractsNotUpgraded: string[] = [];
for (const property in contractsInfo) {
if (contractsInfo[property].unifiedAddress !== "") {
if (await isContract(contractsInfo[property].unifiedAddress, jsonRpcProvider)) {
if (contractsInfo[property].version) {
const signer = new ethers.Wallet(mnemonicWallet.privateKey, jsonRpcProvider);

let contractAddress = contractsInfo[property].unifiedAddress;

if (property === "STATE") {
contractAddress = getStateContractAddress(
Number((await jsonRpcProvider.getNetwork()).chainId),
);
}
const { upgraded, currentVersion } = await checkContractVersion(
contractsInfo[property].name,
contractAddress,
contractsInfo[property].version,
signer,
);
if (!upgraded) {
contractsNotUpgraded.push(
`${contractsInfo[property].name} (${currentVersion} -> ${contractsInfo[property].version})`,
);
}
}
} else {
contractsNotDeployed.push(property);
contractsNotDeployed.push(contractsInfo[property].name);
}
}
}
if (contractsNotDeployed.length > 0) {
if (contractsNotDeployed.length > 0 || contractsNotUpgraded.length > 0) {
const contractsNotDeployedString = `${contractsNotDeployed.length} contracts are not deployed: ${contractsNotDeployed.join(", ")} `;
const contractsNotUpgradedString = `${contractsNotUpgraded.length} contracts are not upgraded: ${contractsNotUpgraded.join(", ")}`;
Logger.error(
`${provider.network}: ${contractsNotDeployed.length} contracts are not deployed: ${contractsNotDeployed.map((property) => contractsInfo[property].name).join(", ")}`,
`${provider.network}: ${contractsNotDeployed.length > 0 ? contractsNotDeployedString : ""}${contractsNotUpgraded.length > 0 ? contractsNotUpgradedString : ""}`,
);
} else {
Logger.success(`${provider.network}: All contracts are deployed`);
Expand Down
16 changes: 16 additions & 0 deletions scripts/upgrade/state/state-upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import hre, { ethers } from "hardhat";
import { expect } from "chai"; // abi of contract that will be upgraded
import * as stateArtifact from "../../../artifacts/contracts/state/State.sol/State.json";
import {
checkContractVersion,
getConfig,
getStateContractAddress,
removeLocalhostNetworkIgnitionFiles,
Expand Down Expand Up @@ -50,6 +51,21 @@ async function main() {
true,
);

const { upgraded, currentVersion } = await checkContractVersion(
contractsInfo.STATE.name,
stateContractAddress,
contractsInfo.STATE.version,
);

if (upgraded) {
console.log(`Contract is already upgraded to version ${contractsInfo.STATE.version}`);
return;
} else {
console.log(
`Contract is not upgraded and will upgrade version ${currentVersion} to ${contractsInfo.STATE.version}`,
);
}

console.log("Proxy Admin Owner Address: ", await proxyAdminOwnerSigner.getAddress());
console.log("State Owner Address: ", await stateOwnerSigner.getAddress());
if (removePreviousIgnitionFiles) {
Expand Down
19 changes: 19 additions & 0 deletions scripts/upgrade/validators/validators-upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DeployHelper } from "../../../helpers/DeployHelper";
import hre, { ethers } from "hardhat";
import {
checkContractVersion,
getConfig,
removeLocalhostNetworkIgnitionFiles,
verifyContract,
Expand Down Expand Up @@ -48,23 +49,41 @@ async function main() {
validatorContractName: contractsInfo.VALIDATOR_MTP.name,
validatorType: VALIDATOR_TYPES.MTP_V2,
validatorVerification: contractsInfo.VALIDATOR_MTP.verificationOpts,
version: contractsInfo.VALIDATOR_MTP.version,
},
{
validatorContractAddress: contractsInfo.VALIDATOR_SIG.unifiedAddress,
validatorContractName: contractsInfo.VALIDATOR_SIG.name,
validatorType: VALIDATOR_TYPES.SIG_V2,
validatorVerification: contractsInfo.VALIDATOR_SIG.verificationOpts,
version: contractsInfo.VALIDATOR_SIG.version,
},
{
validatorContractAddress: contractsInfo.VALIDATOR_V3.unifiedAddress,
validatorContractName: contractsInfo.VALIDATOR_V3.name,
validatorType: VALIDATOR_TYPES.V3,
validatorVerification: contractsInfo.VALIDATOR_V3.verificationOpts,
version: contractsInfo.VALIDATOR_V3.version,
},
];

const validatorsInfo: any = [];
for (const v of validators) {
const { upgraded, currentVersion } = await checkContractVersion(
v.validatorContractName,
v.validatorContractAddress,
v.version,
);

if (upgraded) {
console.log(`Contract is already upgraded to version ${v.version}`);
continue;
} else {
console.log(
`Contract is not upgraded and will upgrade version ${currentVersion} to ${v.version}`,
);
}

const { validator } = await deployHelper.upgradeValidator(
v.validatorContractAddress as string,
v.validatorContractName,
Expand Down
18 changes: 18 additions & 0 deletions scripts/upgrade/verifiers/universal-verifier-upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
submitZKPResponses_KYCAgeCredential,
} from "./helpers/testVerifier";
import {
checkContractVersion,
getConfig,
getStateContractAddress,
removeLocalhostNetworkIgnitionFiles,
Expand Down Expand Up @@ -53,6 +54,23 @@ async function main() {

console.log(`Starting Universal Verifier Contract Upgrade for ${universalVerifierAddress}`);

const { upgraded, currentVersion } = await checkContractVersion(
contractsInfo.UNIVERSAL_VERIFIER.name,
contractsInfo.UNIVERSAL_VERIFIER.unifiedAddress,
contractsInfo.UNIVERSAL_VERIFIER.version,
);

if (upgraded) {
console.log(
`Contract is already upgraded to version ${contractsInfo.UNIVERSAL_VERIFIER.version}`,
);
return;
} else {
console.log(
`Contract is not upgraded and will upgrade version ${currentVersion} to ${contractsInfo.UNIVERSAL_VERIFIER.version}`,
);
}

if (!ethers.isAddress(config.ledgerAccount)) {
throw new Error("LEDGER_ACCOUNT is not set");
}
Expand Down
Loading

0 comments on commit d3ef181

Please sign in to comment.