diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 3b1c21fd..9ec23f77 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -7,7 +7,7 @@ runs: id: nodejs uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 20.x cache: npm - name: Cache node modules diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 2d9f754e..8a41c85b 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -33,7 +33,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: - node-version: '16.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Publish npm package diff --git a/abi/ContentAsset.json b/abi/ContentAsset.json index 29ef3bdb..df9ed74a 100644 --- a/abi/ContentAsset.json +++ b/abi/ContentAsset.json @@ -344,6 +344,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "clearOldCommitsMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "contentAssetStorage", @@ -604,6 +617,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_flag", + "type": "bool" + } + ], + "name": "setOldMetadataClearingFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "unfinalizedStateStorage", diff --git a/contracts/v1/assets/ContentAsset.sol b/contracts/v1/assets/ContentAsset.sol index c7952e98..407fbb30 100644 --- a/contracts/v1/assets/ContentAsset.sol +++ b/contracts/v1/assets/ContentAsset.sol @@ -53,6 +53,8 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { ServiceAgreementV1 public serviceAgreementV1; UnfinalizedStateStorage public unfinalizedStateStorage; + bool private _isOldMetadataClearingDisabled = false; + // solhint-disable-next-line no-empty-blocks constructor(address hubAddress) HubDependent(hubAddress) {} @@ -255,6 +257,7 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { uint256 startTime; uint16 epochsNumber; + uint16 currentEpoch; uint128 epochLength; (startTime, epochsNumber, epochLength, , ) = sasProxy.getAgreementData(agreementId); @@ -262,6 +265,8 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { revert ContentAssetErrors.AssetExpired(tokenId); } + currentEpoch = uint16((block.timestamp - startTime) / epochLength); + bytes32 unfinalizedState = uss.getUnfinalizedState(tokenId); uint256 unfinalizedStateIndex = cas.getAssertionIdsLength(tokenId); @@ -279,12 +284,17 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { } uint96 updateTokenAmount = sasProxy.getAgreementUpdateTokenAmount(agreementId); + if (sasProxy.agreementV1Exists(agreementId)) { sasProxy.deleteServiceAgreementV1U1Object(agreementId); } else { sasProxy.setAgreementUpdateTokenAmount(agreementId, 0); } + sasProxy.deleteCommitsCount(keccak256(abi.encodePacked(agreementId, currentEpoch, unfinalizedStateIndex))); + sasProxy.deleteUpdateCommitsDeadline(keccak256(abi.encodePacked(agreementId, unfinalizedStateIndex))); + sasProxy.setV1U1AgreementEpochSubmissionHead(agreementId, currentEpoch, unfinalizedStateIndex, 0); + sasProxy.transferV1U1AgreementTokens(msg.sender, updateTokenAmount); uss.deleteIssuer(tokenId); @@ -298,6 +308,48 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { ); } + function clearOldCommitsMetadata(uint256 tokenId) external onlyAssetOwner(tokenId) { + if (_isOldMetadataClearingDisabled) { + revert("Function is disabled"); + } + + ContentAssetStorage cas = contentAssetStorage; + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + address contentAssetStorageAddress = address(cas); + + bytes memory keyword = abi.encodePacked(contentAssetStorageAddress, cas.getAssertionIdByIndex(tokenId, 0)); + + bytes32 agreementId = hashingProxy.callHashFunction( + HASH_FUNCTION_ID, + abi.encodePacked(contentAssetStorageAddress, tokenId, keyword) + ); + + uint256 unfinalizedStateIndex = cas.getAssertionIdsLength(tokenId); + + if ( + block.timestamp <= + sasProxy.getUpdateCommitsDeadline(keccak256(abi.encodePacked(agreementId, unfinalizedStateIndex))) + ) { + revert ContentAssetErrors.PendingUpdateFinalization( + contentAssetStorageAddress, + tokenId, + unfinalizedStateIndex + ); + } + + uint256 startTime; + uint16 currentEpoch; + uint128 epochLength; + (startTime, , epochLength, , ) = sasProxy.getAgreementData(agreementId); + + currentEpoch = uint16((block.timestamp - startTime) / epochLength); + + sasProxy.deleteCommitsCount(keccak256(abi.encodePacked(agreementId, currentEpoch, unfinalizedStateIndex))); + sasProxy.deleteUpdateCommitsDeadline(keccak256(abi.encodePacked(agreementId, unfinalizedStateIndex))); + sasProxy.setV1U1AgreementEpochSubmissionHead(agreementId, currentEpoch, unfinalizedStateIndex, 0); + } + function extendAssetStoringPeriod( uint256 tokenId, uint16 epochsNumber, @@ -401,6 +453,10 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { emit AssetUpdatePaymentIncreased(contentAssetStorageAddress, tokenId, tokenAmount); } + function setOldMetadataClearingFlag(bool _flag) external onlyHubOwner { + _isOldMetadataClearingDisabled = _flag; + } + function _createAsset( bytes32 assertionId, uint128 size, diff --git a/deployments/hardhat_contracts.json b/deployments/hardhat_contracts.json new file mode 100644 index 00000000..a21ea58d --- /dev/null +++ b/deployments/hardhat_contracts.json @@ -0,0 +1,175 @@ +{ + "contracts": { + "Hub": { + "evmAddress": "0xc9a1c15d6B5DB1DffC067CA23B68C8aa99D042C1", + "version": "2.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 2195, + "deploymentTimestamp": 1711097519019, + "deployed": true + }, + "HubController": { + "evmAddress": "0xe47ED094d87828E31B5D955D089be2A44d5D9Bb9", + "version": "1.0.2", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 2196, + "deploymentTimestamp": 1711097519601, + "deployed": true + }, + "Token": { + "evmAddress": "0x5Ab2232a0c296f0ae6D73140bFeFc4316970224F", + "version": null, + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 2198, + "deploymentTimestamp": 1711097519887, + "deployed": true + }, + "ProfileStorage": { + "evmAddress": "0xebDCd07Bf0ee267f13C0A1e553972C0EE11d3361", + "version": "1.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3202, + "deploymentTimestamp": 1711097543655, + "deployed": true + }, + "ShardingTableStorage": { + "evmAddress": "0x10672236862DB3D90A145185a0832a22F142183B", + "version": "2.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3205, + "deploymentTimestamp": 1711097544237, + "deployed": true + }, + "StakingStorage": { + "evmAddress": "0x464247623b6A12D61E69Bee136a0c5E75A45Fea9", + "version": "1.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3207, + "deploymentTimestamp": 1711097544963, + "deployed": true + }, + "ShardingTable": { + "evmAddress": "0x5418515e944cb8C55fbb6C875FE1d9b1B4ECC09D", + "version": "2.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3210, + "deploymentTimestamp": 1711097546425, + "deployed": true + }, + "IdentityStorage": { + "evmAddress": "0x407FB1A641098BDf7a9Ef5a84e910a1F996c700f", + "version": "2.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3213, + "deploymentTimestamp": 1711097547404, + "deployed": true + }, + "ParametersStorage": { + "evmAddress": "0xBa01DbbfCEe0c4577c674Ea443e14560C4a67Db4", + "version": "1.1.1", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3215, + "deploymentTimestamp": 1711097547965, + "deployed": true + }, + "ServiceAgreementStorageV1": { + "evmAddress": "0xBc3Bf6C2c8f783Cd6A992941477c0aE89111cf2c", + "version": "1.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3220, + "deploymentTimestamp": 1711097548957, + "deployed": true + }, + "ServiceAgreementStorageV1U1": { + "evmAddress": "0x84BE2c6E16D95511158E848FD664e65AC76029F4", + "version": "1.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3223, + "deploymentTimestamp": 1711097549539, + "deployed": true + }, + "ServiceAgreementStorageProxy": { + "evmAddress": "0xB1F9Fc38c52E32468a0b025a33831b457CE97F39", + "version": "1.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3226, + "deploymentTimestamp": 1711097550182, + "deployed": true + }, + "NodeOperatorFeeChangesStorage": { + "evmAddress": "0x783e40fBae3E5C427102b496cf8621Fc5dD32F80", + "version": "2.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3229, + "deploymentTimestamp": 1711097550767, + "deployed": true + }, + "Staking": { + "evmAddress": "0x3b63985303D5636F6b60315B45531d176Af2c399", + "version": "2.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3231, + "deploymentTimestamp": 1711097551349, + "deployed": true + }, + "Identity": { + "evmAddress": "0xa84E50408f9dC576309102da03Ed8D6A82b7869B", + "version": "1.1.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3234, + "deploymentTimestamp": 1711097553480, + "deployed": true + }, + "HashingProxy": { + "evmAddress": "0xfa994A0DF5E4139e78bEdBA699005a21A2990e4d", + "version": "1.0.1", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3238, + "deploymentTimestamp": 1711097554089, + "deployed": true + }, + "SHA256": { + "evmAddress": "0xfa4d1950831e0aE5bEc5663419B5A7Bf50F7175B", + "version": null, + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3240, + "deploymentTimestamp": 1711097554248, + "deployed": true + }, + "WhitelistStorage": { + "evmAddress": "0x91416df12C887453E85267487D340f690C00f973", + "version": "1.0.0", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3243, + "deploymentTimestamp": 1711097555331, + "deployed": true + }, + "Profile": { + "evmAddress": "0xC531715A0aEe9FD389e453bB364F05704EDEC0b0", + "version": "1.1.1", + "gitBranch": "fix/cancel-update", + "gitCommitHash": "3810c7ac6e4864d57c04a811ead4a2cfde8b21bc", + "deploymentBlock": 3245, + "deploymentTimestamp": 1711097555900, + "deployed": true + } + } +} diff --git a/package-lock.json b/package-lock.json index 155cf2e1..ef7b9d6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dkg-evm-module", - "version": "4.2.4", + "version": "4.2.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dkg-evm-module", - "version": "4.2.4", + "version": "4.2.5", "license": "Apache-2.0", "dependencies": { "@openzeppelin/contracts": "^4.9.3", diff --git a/package.json b/package.json index 546ba85f..5de257c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dkg-evm-module", - "version": "4.2.4", + "version": "4.2.5", "description": "Smart contracts for OriginTrail V6", "main": "index.ts", "files": [ diff --git a/test/v1/unit/ContentAsset.test.ts b/test/v1/unit/ContentAsset.test.ts index cbd04e26..6a9ff203 100644 --- a/test/v1/unit/ContentAsset.test.ts +++ b/test/v1/unit/ContentAsset.test.ts @@ -313,11 +313,34 @@ describe('@v1 @unit ContentAsset contract', function () { it('Cancel asset state update after failed update commit phase, expect previous state to be active', async () => { const tokenId = await createAsset(); await updateAsset(tokenId); + + const keyword = hre.ethers.utils.solidityPack( + ['address', 'bytes32'], + [ContentAssetStorage.address, assetInputStruct.assertionId], + ); + + const agreementId = hre.ethers.utils.soliditySha256( + ['address', 'uint256', 'bytes'], + [ContentAssetStorage.address, tokenId, keyword], + ); + const epochStateId = hre.ethers.utils.solidityKeccak256(['bytes32', 'uint16', 'uint256'], [agreementId, 0, 1]); + const ServiceAgreementStorageProxy = await hre.ethers.getContract( + 'ServiceAgreementStorageProxy', + ); + + await ServiceAgreementStorageProxy.incrementCommitsCount(epochStateId); + await time.increase(await ParametersStorage.updateCommitWindowDuration()); await expect(ContentAsset.cancelAssetStateUpdate(tokenId)) .to.emit(ContentAsset, 'AssetStateUpdateCanceled') .withArgs(ContentAssetStorage.address, tokenId, 1, assetUpdateArgs.tokenAmount); + const commitCount = await ServiceAgreementStorageProxy.getCommitsCount(epochStateId); + const stateId = hre.ethers.utils.soliditySha256(['bytes32', 'uint256'], [agreementId, 1]); + const updateCommitDeadline = await ServiceAgreementStorageProxy.getUpdateCommitsDeadline(stateId); + + await expect(commitCount).to.be.equal(0); + await expect(updateCommitDeadline).to.be.equal(0); }); it('Cancel asset state update using non-owner account, expect to be reverted', async () => { diff --git a/test/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts index 8644c6b4..1cafca84 100644 --- a/test/v2/unit/CommitManagerV2.test.ts +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -21,7 +21,7 @@ import { } from '../../../typechain'; import { ContentAssetStructs } from '../../../typechain/contracts/v1/assets/ContentAsset'; import { ServiceAgreementStructsV1 } from '../../../typechain/contracts/v1/CommitManagerV1'; -import { ServiceAgreementStructsV2 } from '../../../typechain/contracts/v2/CommitManagerV2'; +import { ServiceAgreementStructsV2 } from '../../../typechain/contracts/v2/CommitManagerV1.sol/CommitManagerV2'; type CommitManagerV2Fixture = { accounts: SignerWithAddress[]; diff --git a/test/v2/unit/CommitManagerV2U1.test.ts b/test/v2/unit/CommitManagerV2U1.test.ts index f18000d3..804325af 100644 --- a/test/v2/unit/CommitManagerV2U1.test.ts +++ b/test/v2/unit/CommitManagerV2U1.test.ts @@ -22,7 +22,7 @@ import { } from '../../../typechain'; import { ContentAssetStructs } from '../../../typechain/contracts/v1/assets/ContentAsset'; import { ServiceAgreementStructsV1 } from '../../../typechain/contracts/v1/CommitManagerV1U1'; -import { ServiceAgreementStructsV2 } from '../../../typechain/contracts/v2/CommitManagerV2U1'; +import { ServiceAgreementStructsV2 } from '../../../typechain/contracts/v2/CommitManagerV1U1.sol/CommitManagerV2U1'; const UINT256_MAX_BN = BigNumber.from(2).pow(256).sub(1); const UINT64_MAX_BN = BigNumber.from(2).pow(64).sub(1);