Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions contracts/Registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ contract Registry is AccessControlEnumerable {
function setPackageStatus(uint256 packageIdx, uint8 flags) external onlyRole(SET_STATUS_ROLE) {
Package storage package = packages[packageIdx];
package.flags = flags;

// If banned flag is setted, the name should be freed from the mapping
if(((flags >> 3) & uint8(1)) == 1) {
bytes32 nameHash = keccak256(abi.encodePacked(package.name));
packageIdxByName[nameHash] = 0;
}

emit UpdateStatus(packageIdx, flags);
}

Expand Down
1 change: 0 additions & 1 deletion contracts/Repo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ contract Repo is Initializable, AccessControlEnumerableUpgradeable {

struct Version {
string version;
// TODO: is there any difference between using bytes and string here?
string contentURI;
}

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"name": "dappnode-package-manager",
"scripts": {
"build": "npx hardhat compile",
"test": "npx hardhat test"
"test": "npx hardhat test",
"deploy:registry:hardhat": "npx hardhat run scripts/deploy-registry.ts --network hardhat",
"deploy:registry:xDai": "npx hardhat run scripts/deploy-registry.ts --network xDAI"
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
Expand Down
31 changes: 31 additions & 0 deletions scripts/deploy-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {ethers} from "hardhat";
import {Registry} from "../typechain-types/Registry";

async function main() {
const registryName = "dnp.dappnode";

/*
Deploy Registry contract
*/
console.log("\n#######################");
console.log("##### Deployment Registry Contract #####");
console.log("#######################");
console.log("registryName:", registryName);

const Registry = await ethers.getContractFactory("Registry");
const registry = (await Registry.deploy(registryName)) as Registry;
await registry.deployed();

console.log("#######################\n");
console.log("Dappnode Registry Contract deployed to:", registry.address);

console.log("\n#######################");
console.log("##### Checks #####");
console.log("#######################");
console.log("registryName:", await registry.registryName());
}

main().catch((e) => {
console.error(e);
process.exit(1);
});
32 changes: 0 additions & 32 deletions scripts/deploy.js

This file was deleted.

134 changes: 114 additions & 20 deletions test/registry.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
import {expect} from "chai";
import {ethers} from "hardhat";
import {Event} from "ethers";
import {Registry} from "../typechain-types/Registry";
import {Repo} from "../typechain-types/Repo";
import {Registry, PackageStruct} from "../typechain-types/Registry";
import {Repo, VersionStruct} from "../typechain-types/Repo";

interface RepoPackage {
name: string;
dev: string;
flags: number;
}

describe("Registry", function () {
it("dnp.dappnode registry publish one package", async function () {
const [owner, addr1] = await ethers.getSigners();

const registryName = "dnp.dappnode";

const newVersion1: RepoVersion = {
const newVersion1: VersionStruct = {
version: "0.1.0",
contentURI: "/ipfs/Qmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
};

const newVersion2: RepoVersion = {
const newVersion2: VersionStruct = {
version: "0.2.0-beta.0",
contentURI: "/ipfs/Qmbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
};

const newPackage = {
const newPackage: RepoPackage = {
name: "gnosis",
dev: addr1.address,
flags: 0,
Expand All @@ -36,11 +42,14 @@ describe("Registry", function () {
// Publish new repo from admin account
const {repo: newRepoAddress} = await publishRepoVersion(registry, newPackage, newVersion1);

// Assert registry packages
await assertPackages(registry, [{flags: newPackage.flags, repo: newRepoAddress, name: newPackage.name}])

// Connect to deployed repo
const repoWithAdmin = (await ethers.getContractAt("Repo", newRepoAddress, owner)) as Repo;
const repoWithDev = (await ethers.getContractAt("Repo", newRepoAddress, addr1)) as Repo;

// Assert that there are two version in the Repo contract
// Assert that there are a version in the Repo contract
await assertRepoVersions(repoWithDev, [newVersion1]);

// Ensure it's already initialized
Expand Down Expand Up @@ -102,7 +111,7 @@ describe("Registry", function () {
// Test that non-auth users can NOT publish packages
const registryUser = (await ethers.getContractAt("Registry", registryAdmin.address, addr1)) as Registry;
await expect(publishRepoVersion(registryUser, newPackage, newVersion1)).to.be.revertedWith(
"Initializable: contract is already initialized"
"NO_ADD_PACKAGE_ROLE"
);

// Allow anyone to publish package
Expand All @@ -111,29 +120,91 @@ describe("Registry", function () {
// Test that non-auth users DO can publish packages
const {repo: newRepoAddress} = await publishRepoVersion(registryUser, newPackage, newVersion1);

// Assert registry packages
await assertPackages(registryUser, [{flags: newPackage.flags, repo: newRepoAddress, name: newPackage.name}])

// Connect to deployed repo
const repoUser = (await ethers.getContractAt("Repo", newRepoAddress, addr1)) as Repo;

// Assert that there are two version in the Repo contract
await assertRepoVersions(repoUser, [newVersion1]);
});
});

interface RepoVersion {
version: string;
contentURI: string;
}
it("public.dappnode registry publish one package and set flags", async function () {
const [owner, addr1] = await ethers.getSigners();

interface RepoPackage {
name: string;
dev: string;
flags: number;
}
const registryName = "dnp.dappnode";

const newVersion1: VersionStruct = {
version: "0.1.0",
contentURI: "/ipfs/Qmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
};

const newPackage: RepoPackage = {
name: "gnosis",
dev: addr1.address,
flags: 0,
};

const Registry = await ethers.getContractFactory("Registry");
const registry = (await Registry.deploy(registryName)) as Registry;

await registry.deployed();

expect(await registry.registryName()).to.equal(registryName, "Wrong registryName");

// Publish new repo from admin account
const {repo: newRepoAddress} = await publishRepoVersion(registry, newPackage, newVersion1);

// Assert registry packages
await assertPackages(registry, [{flags: newPackage.flags, repo: newRepoAddress, name: newPackage.name}])

// Connect to deployed repo
const repoWithDev = (await ethers.getContractAt("Repo", newRepoAddress, addr1)) as Repo;

// Assert that there are a version in the Repo contract
await assertRepoVersions(repoWithDev, [newVersion1]);

// Set flags using the following:
// Bitfield with status flags, TBD
// 0 - visible
// 1 - active
// 2 - validated
// 3 - banned
const nameHash = ethers.utils.solidityKeccak256(["string"], [newPackage.name]);
const packageIdx = 1;
expect(await registry.getPackageIdx(newPackage.name)).to.be.equal(packageIdx);
expect(await registry.packageIdxByName(nameHash)).to.be.equal(packageIdx);

// Calculate flag value for visible, active and validated
const flagValue = calculateFlagValue(true, true, true, false);

// Set package flags
await registry.setPackageStatus(packageIdx, flagValue);

// Assert registry packages
await assertPackages(registry, [{flags: flagValue, repo: newRepoAddress, name: newPackage.name}])
expect(await registry.getPackageIdx(newPackage.name)).to.be.equal(packageIdx);

// Calculate flag value banned
const bannedFlag = calculateFlagValue(false, false, false, true);

// Set package flags
await registry.setPackageStatus(packageIdx, bannedFlag);

// Assert registry packages
await assertPackages(registry, [{flags: bannedFlag, repo: newRepoAddress, name: newPackage.name}])

// Package should have been removed from the packageIdxByName mapping
await expect(registry.getPackageIdx(newPackage.name)).to.be.revertedWith("REGISTRY_INEXISTENT_NAME");
expect(await registry.packageIdxByName(nameHash)).to.be.equal(0);
});
});

/**
* Call newPackageWithVersion and assert event is correct
*/
async function publishRepoVersion(registry: Registry, pkg: RepoPackage, version: RepoVersion): Promise<{repo: string}> {
async function publishRepoVersion(registry: Registry, pkg: RepoPackage, version: VersionStruct): Promise<{repo: string}> {
const newPackageWithVersionTx = await registry.newPackageWithVersion(
pkg.name,
pkg.dev,
Expand All @@ -160,11 +231,11 @@ async function publishRepoVersion(registry: Registry, pkg: RepoPackage, version:
return {repo};
}

async function assertRepoVersions(repo: Repo, expectedVersions: RepoVersion[]) {
async function assertRepoVersions(repo: Repo, expectedVersions: VersionStruct[]) {
const versionCountBN = await repo.getVersionsCount();
const versionCount = versionCountBN.toNumber();

const versions: RepoVersion[] = [];
const versions: VersionStruct[] = [];

for (let i = 1; i < versionCount + 1; i++) {
const version = await repo.getByVersionId(i);
Expand All @@ -177,10 +248,33 @@ async function assertRepoVersions(repo: Repo, expectedVersions: RepoVersion[]) {
expect(versions).to.deep.equal(expectedVersions, "Wrong versions in repo");
}

async function assertPackages(registry: Registry, expectedPackages: PackageStruct[]) {
const packageCountBN = await registry.getPackageCount();
const packageCount = packageCountBN.toNumber();

const packages: PackageStruct[] = [];

for (let i = 1; i < packageCount + 1; i++) {
const currentPackage = await registry.packages(i) as PackageStruct;
packages.push({
flags: currentPackage.flags,
repo: currentPackage.repo,
name: currentPackage.name,
});
}

expect(packages).to.deep.equal(expectedPackages, "Wrong versions in repo");
}

function getEvent(events: Event[] = [], eventName: string): Event {
const event = events.find((event) => event.event === eventName);
if (!event) {
throw Error(`No event found for ${eventName}`);
}
return event;
}

function calculateFlagValue(visible: Boolean, active: Boolean,validated: Boolean, banned: Boolean): number {
const value = Number(visible) + Number(active)*2 + Number(validated)*4 + Number(banned)*8;
return value;
}