diff --git a/packages/vda-common/CHANGELOG.md b/packages/vda-common/CHANGELOG.md index cd2da59e..72bbeb09 100644 --- a/packages/vda-common/CHANGELOG.md +++ b/packages/vda-common/CHANGELOG.md @@ -1,3 +1,8 @@ +2024-03-22 (v3.0.2) +------------------- + +- Update the abi & address of `StorageNodeRegistry` contract + 2024-03-15 (v3.0.1) ------------------- diff --git a/packages/vda-common/package.json b/packages/vda-common/package.json index a522f2b0..474bcf49 100644 --- a/packages/vda-common/package.json +++ b/packages/vda-common/package.json @@ -1,6 +1,6 @@ { "name": "@verida/vda-common", - "version": "3.0.1", + "version": "3.0.2", "description": "Common utils & contract addresses for Verida", "main": "dist/index.js", "author": "Alex J", diff --git a/packages/vda-common/src/abi/StorageNodeRegistry.json b/packages/vda-common/src/abi/StorageNodeRegistry.json index bc02223c..c3c017f8 100644 --- a/packages/vda-common/src/abi/StorageNodeRegistry.json +++ b/packages/vda-common/src/abi/StorageNodeRegistry.json @@ -131,6 +131,22 @@ } ] }, + { + "type": "function", + "name": "acceptOwnership", + "constant": false, + "payable": false, + "inputs": [], + "outputs": [] + }, + { + "type": "function", + "name": "cancelTransferOwnership", + "constant": false, + "payable": false, + "inputs": [], + "outputs": [] + }, { "type": "function", "name": "owner", @@ -145,6 +161,20 @@ } ] }, + { + "type": "function", + "name": "pendingOwner", + "constant": true, + "stateMutability": "view", + "payable": false, + "inputs": [], + "outputs": [ + { + "type": "address", + "name": "pendingOwner_" + } + ] + }, { "type": "function", "name": "transferOwnership", @@ -1436,6 +1466,14 @@ { "type": "string", "name": "countryCode" + }, + { + "type": "uint256", + "name": "pageSize" + }, + { + "type": "uint256", + "name": "pageNumber" } ], "outputs": [ @@ -1517,6 +1555,14 @@ { "type": "uint8", "name": "status" + }, + { + "type": "uint256", + "name": "pageSize" + }, + { + "type": "uint256", + "name": "pageNumber" } ], "outputs": [ @@ -1594,6 +1640,14 @@ { "type": "string", "name": "regionCode" + }, + { + "type": "uint256", + "name": "pageSize" + }, + { + "type": "uint256", + "name": "pageNumber" } ], "outputs": [ @@ -1675,6 +1729,14 @@ { "type": "uint8", "name": "status" + }, + { + "type": "uint256", + "name": "pageSize" + }, + { + "type": "uint256", + "name": "pageNumber" } ], "outputs": [ @@ -1752,6 +1814,14 @@ { "type": "uint8", "name": "status" + }, + { + "type": "uint256", + "name": "pageSize" + }, + { + "type": "uint256", + "name": "pageNumber" } ], "outputs": [ diff --git a/packages/vda-common/src/contract.ts b/packages/vda-common/src/contract.ts index 459a30ed..b26fb30e 100644 --- a/packages/vda-common/src/contract.ts +++ b/packages/vda-common/src/contract.ts @@ -63,9 +63,9 @@ export const CONTRACT_ADDRESS : Record { const countries = [...new Set(counrtryList)]; for (let i = 0; i < countries.length; i++) { - const result = await blockchainApi.getNodesByCountryCode(countries[i]); + const result = await blockchainApi.getNodesByCountryCode(countries[i], 10, 1); assert.ok(result.length > 0, "Return 1 or more nodes for registered country code"); } }) it('getNodesByCountryCodeAndStatus',async () => { const removingCountryCode = (DID_NODE_MAP.get(REMOVE_START_DIDS[0].address)).countryCode; - const result = await blockchainApi.getNodesByCountryCode(removingCountryCode, EnumStatus.removing); + const result = await blockchainApi.getNodesByCountryCode(removingCountryCode, 10, 1, EnumStatus.removing); assert.ok(result.length > 0, "Return 1 or more removing nodes for registered country code"); }) @@ -239,25 +239,25 @@ describe("vda-node-client", () => { const regions = [...new Set(regionList)]; for (let i = 0; i < regions.length; i++) { - const result = await blockchainApi.getNodesByRegionCode(regions[i]); + const result = await blockchainApi.getNodesByRegionCode(regions[i], 10, 1); assert.ok(result.length > 0, "Return 1 or more nodes for registered region code"); } }) it('getNodesByRegionCodeAndStatus',async () => { const removingRegioinCode = (DID_NODE_MAP.get(REMOVE_START_DIDS[0].address)).regionCode; - const result = await blockchainApi.getNodesByRegionCode(removingRegioinCode, EnumStatus.removing); + const result = await blockchainApi.getNodesByRegionCode(removingRegioinCode, 10, 1, EnumStatus.removing); assert.ok(result.length > 0, "Return 1 or more removing nodes for registered country code"); }) it("getNodesByStatus", async () => { - let result = await blockchainApi.getNodesByStatus(EnumStatus.active); + let result = await blockchainApi.getNodesByStatus(EnumStatus.active, 10, 1); assert.ok(result.length > 0, "Return 1 or more active nodes"); - result = await blockchainApi.getNodesByStatus(EnumStatus.removing); + result = await blockchainApi.getNodesByStatus(EnumStatus.removing, 10, 1); assert.ok(result.length > 0, "Return 1 or more pending removal nodes"); - result = await blockchainApi.getNodesByStatus(EnumStatus.removed); + result = await blockchainApi.getNodesByStatus(EnumStatus.removed, 10, 1); assert.ok(result.length >= 0, "Return 0 or more active nodes"); }) }) diff --git a/packages/vda-node-manager/CHANGELOG.md b/packages/vda-node-manager/CHANGELOG.md index e412bc05..cb8a78b6 100644 --- a/packages/vda-node-manager/CHANGELOG.md +++ b/packages/vda-node-manager/CHANGELOG.md @@ -1,3 +1,8 @@ +2024-03-22 (v3.0.2) +------------------- + +- Update the functions for `StorageNodeRegitry` contract + 2024-03-15 (v3.0.0) ------------------- diff --git a/packages/vda-node-manager/package.json b/packages/vda-node-manager/package.json index 4bbc4b37..2f363899 100644 --- a/packages/vda-node-manager/package.json +++ b/packages/vda-node-manager/package.json @@ -1,6 +1,6 @@ { "name": "@verida/vda-node-manager", - "version": "3.0.1", + "version": "3.0.2", "description": "Client to manage storage nodes from the blockchain", "main": "build/index.js", "types": "build/index.d.ts", diff --git a/packages/vda-node-manager/src/blockchain/ownerApi.ts b/packages/vda-node-manager/src/blockchain/ownerApi.ts index cf4a4a6b..7c2fe54a 100644 --- a/packages/vda-node-manager/src/blockchain/ownerApi.ts +++ b/packages/vda-node-manager/src/blockchain/ownerApi.ts @@ -326,4 +326,20 @@ export class VeridaNodeOwnerApi extends VeridaNodeManager { } } + + /** + * Start 2-step ownership transfer + * @param to New owenr address + */ + public async transferOwnership(to: string) { + if (this.readOnly || !this.config.signKey) { + throw new Error(`Unable to submit to blockchain. In read only mode.`) + } + + const response = await this.vdaWeb3Client!.transferOwnership(to); + + if (response.success !== true) { + throw new Error(`Failed to transfer ownership: ${response.reason}`); + } + } } \ No newline at end of file diff --git a/packages/vda-node-manager/src/blockchain/userApi.ts b/packages/vda-node-manager/src/blockchain/userApi.ts index 113df5cf..93e0495d 100644 --- a/packages/vda-node-manager/src/blockchain/userApi.ts +++ b/packages/vda-node-manager/src/blockchain/userApi.ts @@ -667,16 +667,19 @@ export class VeridaNodeManager { /** * Return an array of `Storagenode` structs for country code * @param countryCode Unique two-character string code + * @param pageSize Number of maximum elements of returned + * @param pageNumber Page index. Index starts from 1 + * @param status Status of nodes to be returned * @returns An array of `Storagenode` structs */ - public async getNodesByCountryCode(countryCode: string, status?: EnumStatus) { + public async getNodesByCountryCode(countryCode: string, pageSize: BigNumberish, pageNumber: BigNumberish, status?: EnumStatus) { let response; try { if (this.vdaWeb3Client) { if (status === undefined) { - response = await this.vdaWeb3Client.getNodesByCountryCode(countryCode); + response = await this.vdaWeb3Client.getNodesByCountryCode(countryCode, pageSize, pageNumber); } else { - response = await this.vdaWeb3Client.getNodesByCountryCodeAndStatus(countryCode, status); + response = await this.vdaWeb3Client.getNodesByCountryCodeAndStatus(countryCode, status, pageSize, pageNumber); } if (response.success !== true) { @@ -688,9 +691,9 @@ export class VeridaNodeManager { ); } else { if (status === undefined) { - response = await this.contract!.callStatic.getNodesByCountryCode(countryCode); + response = await this.contract!.callStatic.getNodesByCountryCode(countryCode, pageSize, pageNumber); } else { - response = await this.contract!.callStatic.getNodesByCountryCodeAndStatus(countryCode, status); + response = await this.contract!.callStatic.getNodesByCountryCodeAndStatus(countryCode, status, pageSize, pageNumber); } return await Promise.all( @@ -706,16 +709,19 @@ export class VeridaNodeManager { /** * Return an array of `Storagenode` structs for region code * @param regionCode Unique region string code + * @param pageSize Number of maximum elements of returned + * @param pageNumber Page index. Index starts from 1 + * @param status Status of nodes to be returned * @returns An array of `Storagenode` structs */ - public async getNodesByRegionCode(regionCode: string, status?: EnumStatus) { + public async getNodesByRegionCode(regionCode: string, pageSize: BigNumberish, pageNumber: BigNumberish, status?: EnumStatus) { let response; try { if (this.vdaWeb3Client) { if (status === undefined) { - response = await this.vdaWeb3Client.getNodesByRegionCode(regionCode); + response = await this.vdaWeb3Client.getNodesByRegionCode(regionCode, pageSize, pageNumber); } else { - response = await this.vdaWeb3Client.getNodesByRegionCodeAndStatus(regionCode, status); + response = await this.vdaWeb3Client.getNodesByRegionCodeAndStatus(regionCode, status, pageSize, pageNumber); } if (response.success !== true) { @@ -727,9 +733,9 @@ export class VeridaNodeManager { ); } else { if (status === undefined) { - response = await this.contract!.callStatic.getNodesByRegionCode(regionCode); + response = await this.contract!.callStatic.getNodesByRegionCode(regionCode, pageSize, pageNumber); } else { - response = await this.contract!.callStatic.getNodesByRegionCodeAndStatus(regionCode, status); + response = await this.contract!.callStatic.getNodesByRegionCodeAndStatus(regionCode, status, pageSize, pageNumber); } return await Promise.all( @@ -745,13 +751,15 @@ export class VeridaNodeManager { /** * Return an array of `Storagenode` structs for specified status * @param status Status of targeting storage nodes + * @param pageSize Number of maximum elements of returned + * @param pageNumber Page index. Index starts from 1 * @returns An array of `Storagenode` structs */ - public async getNodesByStatus(status: EnumStatus) { + public async getNodesByStatus(status: EnumStatus, pageSize: BigNumberish, pageNumber: BigNumberish) { let response; try { if (this.vdaWeb3Client) { - response = await this.vdaWeb3Client.getNodesByStatus(status); + response = await this.vdaWeb3Client.getNodesByStatus(status, pageSize, pageNumber); if (response.success !== true) { throw new Error(response.reason); @@ -761,7 +769,7 @@ export class VeridaNodeManager { response.data.map(async (item: any) => await this.standardizeNode(item)) ); } else { - response = await this.contract!.callStatic.getNodesByStatus(status); + response = await this.contract!.callStatic.getNodesByStatus(status, pageSize, pageNumber); return await Promise.all( response.map(async (item: any) => await this.standardizeNode(item)) @@ -1199,4 +1207,19 @@ export class VeridaNodeManager { throw new Error(`Failed to check withdrawal enabled (${message})`); } } + + /** + * Accept 2-step ownership transfer + */ + public async acceptOwnership() { + if (this.readOnly || !this.config.signKey) { + throw new Error(`Unable to submit to blockchain. In read only mode.`) + } + + const response = await this.vdaWeb3Client!.acceptOwnership(); + + if (response.success !== true) { + throw new Error(`Failed to accept the ownership: ${response.reason}`); + } + } } \ No newline at end of file diff --git a/packages/vda-node-manager/test/user_read.test.ts b/packages/vda-node-manager/test/user_read.test.ts index 3d789ea7..bf0c2542 100644 --- a/packages/vda-node-manager/test/user_read.test.ts +++ b/packages/vda-node-manager/test/user_read.test.ts @@ -288,10 +288,35 @@ describe('vda-node-manager read only tests', () => { for (let i = 0; i < countryCodes.length; i++) { const countryNodes = nodes.filter(item => item.countryCode === countryCodes[i]); - const result = await blockchainApi.getNodesByCountryCode(countryCodes[i]); + const result = await blockchainApi.getNodesByCountryCode(countryCodes[i], 100, 1); assert.ok(countryNodes.length === result.length, "Get nodes by country code"); } + + // Pagination check + // Failed for invalid page number + try { + await blockchainApi.getNodesByCountryCode(countryCodes[0], 10, 0); + } catch(err) { + assert.ok(err.message.match('Failed to get nodes by country'), 'Invalid page number'); + } + + // Failed for invalid page size + try { + await blockchainApi.getNodesByCountryCode(countryCodes[0], 0, 1); + } catch(err) { + assert.ok(err.message.match('Failed to get nodes by country'), 'Invalid page number'); + } + + try { + await blockchainApi.getNodesByCountryCode(countryCodes[0], 101, 1); + } catch(err) { + assert.ok(err.message.match('Failed to get nodes by country'), 'Invalid page number'); + } + + // Success + const result = await blockchainApi.getNodesByCountryCode(countryCodes[0], 1, 2); + assert.ok(result.length === 1, "Get nodes by country code"); }) it("Get nodes by country code and status",async () => { @@ -300,7 +325,7 @@ describe('vda-node-manager read only tests', () => { // Get `active` nodes for (let i = 0; i < countryCodes.length; i++) { - let result = await blockchainApi.getNodesByCountryCode(countryCodes[i], EnumStatus.active); + let result = await blockchainApi.getNodesByCountryCode(countryCodes[i], 100, 1, EnumStatus.active); assert.ok(result.length > 0, "Get 'active' nodes by country code"); } @@ -308,8 +333,12 @@ describe('vda-node-manager read only tests', () => { const did = REMOVE_START_DIDS[0]; const removedNode = DID_NODE_MAP.get(did.address); const removedCountryCode = removedNode.countryCode; - const result = await blockchainApi.getNodesByCountryCode(removedCountryCode, EnumStatus.removing); + let result = await blockchainApi.getNodesByCountryCode(removedCountryCode, 100, 1, EnumStatus.removing); assert.ok(result.length > 0, "Get 'removing' nodes by country code"); + + // Pagination check + result = await blockchainApi.getNodesByCountryCode(countryCodes[0], 1, 2, EnumStatus.active); + assert.ok(result.length === 1, "Get 'active' nodes by country code"); }) it("Get nodes by region code", async () => { @@ -318,10 +347,29 @@ describe('vda-node-manager read only tests', () => { for (let i = 0; i < regionCodes.length; i++) { const regionNodes = nodes.filter(item => item.regionCode === regionCodes[i]); - const result = await blockchainApi.getNodesByRegionCode(regionCodes[i]); + const result = await blockchainApi.getNodesByRegionCode(regionCodes[i], 100, 1); assert.ok(regionNodes.length === result.length, "Get nodes by region code"); } + + // Pagination check + // Failed for invalid page number + try { + await blockchainApi.getNodesByRegionCode(regionCodes[0], 10, 0); + } catch(err) { + assert.ok(err.message.match('Failed to get nodes by region'), 'Invalid page number'); + } + + // Failed for invalid page size + try { + await blockchainApi.getNodesByRegionCode(regionCodes[0], 0, 1); + } catch(err) { + assert.ok(err.message.match('Failed to get nodes by region'), 'Invalid page number'); + } + + // Success + const result = await blockchainApi.getNodesByRegionCode(regionCodes[0], 1, 2); + assert.ok(result.length === 1, "Get nodes by country code"); }) it("Get nodes by region code and status",async () => { @@ -330,7 +378,7 @@ describe('vda-node-manager read only tests', () => { const regionCodes = [...new Set(regionArr)] for (let i = 0; i < regionCodes.length; i++) { - const result = await blockchainApi.getNodesByRegionCode(regionCodes[i], EnumStatus.active); + const result = await blockchainApi.getNodesByRegionCode(regionCodes[i], 100, 1, EnumStatus.active); assert.ok(result.length > 0, "Get 'active' nodes by region code"); } @@ -338,21 +386,25 @@ describe('vda-node-manager read only tests', () => { const did = REMOVE_START_DIDS[0]; const removedNode = DID_NODE_MAP.get(did.address); const removedregionCode = removedNode.regionCode; - const result = await blockchainApi.getNodesByRegionCode(removedregionCode, EnumStatus.removing); + let result = await blockchainApi.getNodesByRegionCode(removedregionCode, 100, 1, EnumStatus.removing); assert.ok(result.length > 0, "Get 'removing' nodes by region code and status"); + + // Check pagination + result = await blockchainApi.getNodesByRegionCode(regionCodes[0], 1, 2, EnumStatus.active); + assert.ok(result.length === 1, "Get 'active' nodes by region code"); }) it("Get nodes by status", async () => { // Get `active` nodes - let result = await blockchainApi.getNodesByStatus(EnumStatus.active); + let result = await blockchainApi.getNodesByStatus(EnumStatus.active, 100, 1); assert.ok(result.length >= REGISTERED_DIDS.length, "Get active nodes"); // Get `pending removal` nodes - result = await blockchainApi.getNodesByStatus(EnumStatus.removing); + result = await blockchainApi.getNodesByStatus(EnumStatus.removing, 100, 1); assert.ok(result.length > 0, "Get pending removal nodes"); // Get `removed` nodes - result = await blockchainApi.getNodesByStatus(EnumStatus.removed); + result = await blockchainApi.getNodesByStatus(EnumStatus.removed, 100, 1); assert.ok(result.length >= 0, "Get removed nodes"); }) }) diff --git a/packages/vda-node-manager/test/user_write.test.ts b/packages/vda-node-manager/test/user_write.test.ts index 268c4a31..dd6d98be 100644 --- a/packages/vda-node-manager/test/user_write.test.ts +++ b/packages/vda-node-manager/test/user_write.test.ts @@ -528,7 +528,7 @@ describe('vda-node-manager read and write tests', () => { for (let i = 0; i < countryCodes.length; i++) { const countryNodes = nodes.filter(item => item.countryCode === countryCodes[i]); - const result = await blockchainApi.getNodesByCountryCode(countryCodes[i]); + const result = await blockchainApi.getNodesByCountryCode(countryCodes[i], 100, 1); assert.ok(result.length >= countryNodes.length, "Get nodes by country code"); } @@ -537,7 +537,7 @@ describe('vda-node-manager read and write tests', () => { it("Get nodes by country code and status",async () => { // Get `active` nodes const countryCode = newNode.countryCode; - let result = await blockchainApi.getNodesByCountryCode(countryCode, EnumStatus.active); + let result = await blockchainApi.getNodesByCountryCode(countryCode, 100, 1, EnumStatus.active); let filterdNodes = result.filter(item => item.name === newNode.name); assert.ok(compareNodeData(newNode, filterdNodes[0]), "Get nodes by country code and status"); @@ -545,7 +545,7 @@ describe('vda-node-manager read and write tests', () => { const did = REMOVE_START_DIDS[0]; const removedNode = DID_NODE_MAP.get(did.address); const removedCountryCode = removedNode.countryCode; - result = await blockchainApi.getNodesByCountryCode(removedCountryCode, EnumStatus.removing); + result = await blockchainApi.getNodesByCountryCode(removedCountryCode, 100, 1, EnumStatus.removing); filterdNodes = result.filter(item => item.name === removedNode.name); assert.ok(compareNodeData(removedNode, filterdNodes[0]), "Get nodes by country code and status"); }) @@ -556,7 +556,7 @@ describe('vda-node-manager read and write tests', () => { for (let i = 0; i < regionCodes.length; i++) { const regionNodes = nodes.filter(item => item.regionCode === regionCodes[i]); - const result = await blockchainApi.getNodesByRegionCode(regionCodes[i]); + const result = await blockchainApi.getNodesByRegionCode(regionCodes[i], 100, 1); assert.ok(result.length >= regionNodes.length , "Get nodes by region code"); } @@ -565,7 +565,7 @@ describe('vda-node-manager read and write tests', () => { it("Get nodes by region code and status",async () => { // Get `active` nodes const regionCode = newNode.regionCode; - let result = await blockchainApi.getNodesByRegionCode(regionCode, EnumStatus.active); + let result = await blockchainApi.getNodesByRegionCode(regionCode, 100, 1, EnumStatus.active); let filterdNodes = result.filter(item => item.name === newNode.name); assert.ok(compareNodeData(newNode, filterdNodes[0]), "Get nodes by region code and status"); @@ -573,22 +573,22 @@ describe('vda-node-manager read and write tests', () => { const did = REMOVE_START_DIDS[0]; const removedNode = DID_NODE_MAP.get(did.address); const removedregionCode = removedNode.regionCode; - result = await blockchainApi.getNodesByRegionCode(removedregionCode, EnumStatus.removing); + result = await blockchainApi.getNodesByRegionCode(removedregionCode, 100, 1, EnumStatus.removing); filterdNodes = result.filter(item => item.name === removedNode.name); assert.ok(compareNodeData(removedNode, filterdNodes[0]), "Get nodes by region code and status"); }) it("Get nodes by status", async () => { // Get `active` nodes - let result = await blockchainApi.getNodesByStatus(EnumStatus.active); + let result = await blockchainApi.getNodesByStatus(EnumStatus.active, 100, 1); assert.ok(result.length >= REGISTERED_DIDS.length, "Get active nodes"); // Get `pending removal` nodes - result = await blockchainApi.getNodesByStatus(EnumStatus.removing); + result = await blockchainApi.getNodesByStatus(EnumStatus.removing, 100, 1); assert.ok(result.length > 0, "Get pending removal nodes"); // Get `removed` nodes - result = await blockchainApi.getNodesByStatus(EnumStatus.removed); + result = await blockchainApi.getNodesByStatus(EnumStatus.removed, 100, 1); assert.ok(result.length >= 0, "Get removed nodes"); }) })