Skip to content

Commit

Permalink
Merge pull request #3 from aragon/f/OS-588_abstract-pluginRepo-plugin…
Browse files Browse the repository at this point in the history
…Setup-ids

F/ OS-588 Sbstract plugin repo  & plugin setup ids
  • Loading branch information
Rekard0 authored Sep 11, 2023
2 parents 60f3eb2 + e0fd41d commit 91d9e53
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 18 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/subgraph-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Subgraph Tests
on:
push:
paths:
- 'subgraph/**'
- '.github/workflows/subgraph-*.yml'

env:
working-directory: subgraph

jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{env.working-directory}}
steps:
- uses: actions/checkout@v2
- name: Install node
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install dependencies
run: yarn install --pure-lockfile
- name: Run Tests
run: yarn run test
2 changes: 2 additions & 0 deletions subgraph/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added `getPluginRepoId`, `getPluginSetupId`, `getPluginInstallationId`, `getPluginPreparationId`, `getPluginReleaseId`, `getPluginVersionId`, and `getPluginPermissionId`.

### Removed
5 changes: 0 additions & 5 deletions subgraph/src/ids.ts

This file was deleted.

13 changes: 13 additions & 0 deletions subgraph/src/ids/dao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {Address} from '@graphprotocol/graph-ts';

/**
* Generates the DAO's ID using its address in hexadecimal format.
*
* @param dao - The address of the DAO.
* @returns A hexadecimal string representation of the provided DAO address.
*/
export function getDaoId(dao: Address): string {
return dao.toHexString();
}

// TODO: this is not complete, it will be done in it's own task.
137 changes: 137 additions & 0 deletions subgraph/src/ids/pluginRepo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import {
Address,
ByteArray,
Bytes,
crypto,
ethereum
} from '@graphprotocol/graph-ts';
import {PERMISSION_OPERATIONS} from '../utils/constants';

/**
* Generates the plugin repository's ID using its address in hexadecimal format.
*
* @param pluginRepo - The address of the plugin repository.
* @returns A hexadecimal string representation of the provided address.
*/
export function getPluginRepoId(pluginRepo: Address): string {
return pluginRepo.toHexString();
}

/**
* Generates the plugin setup's ID using its address in hexadecimal format.
*
* @param pluginSetup - The address of the plugin setup.
* @returns A hexadecimal string representation of the provided address.
*/
export function getPluginSetupId(pluginSetup: Address): string {
return pluginSetup.toHexString();
}

/**
* Generates an installation ID for a plugin based on a DAO and plugin's address.
*
* @param dao - The address of the DAO.
* @param plugin - The address of the plugin.
* @returns A unique ID based on the DAO and plugin's address or null if the encoding fails.
*/
export function getPluginInstallationId(
dao: Address,
plugin: Address
): string | null {
let installationIdTuple = new ethereum.Tuple();
installationIdTuple.push(ethereum.Value.fromAddress(dao));
installationIdTuple.push(ethereum.Value.fromAddress(plugin));

let installationIdTupleEncoded = ethereum.encode(
ethereum.Value.fromTuple(installationIdTuple)
);

if (installationIdTupleEncoded) {
return Bytes.fromHexString(
crypto
.keccak256(
ByteArray.fromHexString(installationIdTupleEncoded.toHexString())
)
.toHexString()
).toHexString();
}
return null;
}

/**
* Generates a preparation ID by merging plugin installation ID and plugin setup ID.
*
* @param pluginInstallationId - The installation ID of the plugin.
* @param pluginSetupId - The preparedSetupId of the plugin emitted from `PluginSetupProcessor`.
* Refer to the [PluginSetupProcessor contract](https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/plugin/setup/PluginSetupProcessorHelpers.sol) for more details.
* @returns A concatenated ID string for plugin preparation.
*/
export function getPluginPreparationId(
pluginInstallationId: string,
prepareSetupId: Bytes
): string {
const ids = [pluginInstallationId, prepareSetupId.toHexString()];
return ids.join('_');
}

/**
* Generates a unique ID for a plugin's release based on its repository and release number.
*
* @param pluginRepo - The address of the plugin repository.
* @param release - The number corresponding to the plugin's release.
* @returns An ID string for the plugin release.
*/
export function getPluginReleaseId(pluginRepo: Address, release: i32): string {
const ids = [getPluginRepoId(pluginRepo), release.toString()];
return ids.join('_');
}

/**
* Generates a unique ID for a plugin's version using its repository, release number, and build number.
*
* @param pluginRepo - The address of the plugin repository.
* @param release - The release number of the plugin.
* @param build - The build number for the specific version of the plugin.
* @returns A unique ID string for the plugin version.
*/
export function getPluginVersionId(
pluginRepo: Address,
release: i32,
build: i32
): string {
const ids = [
getPluginRepoId(pluginRepo),
release.toString(),
build.toString()
];
return ids.join('_');
}

/**
* Generates a unique permission ID for a plugin based on multiple attributes including operation, addresses, and existing permission ID.
*
* @param pluginPreparationId - The ID from plugin preparation.
* @param operation - The numerical code for the operation type.
* @param where - The address specifying the location of the permission.
* @param who - The address specifying the entity of the permission.
* @param permissionId - An existing permission ID.
* @returns A concatenated unique ID string for the plugin permission.
*/
export function getPluginPermissionId(
pluginPreparationId: string,
operation: i32,
where: Address,
who: Address,
permissionId: Bytes
): string {
const operationId = PERMISSION_OPERATIONS.get(operation);
const ids = [
pluginPreparationId,
operationId,
where.toHexString(),
who.toHexString(),
permissionId.toHexString()
];

return ids.join('_');
}
3 changes: 2 additions & 1 deletion subgraph/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './ids';
export * from './ids/dao';
export * from './ids/pluginRepo';
4 changes: 4 additions & 0 deletions subgraph/src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const PERMISSION_OPERATIONS = new Map<number, string>()
.set(0, 'Grant')
.set(1, 'Revoke')
.set(2, 'GrantWithCondition');
6 changes: 6 additions & 0 deletions subgraph/tests/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export const ADDRESS_ONE = '0x0000000000000000000000000000000000000001';
export const ADDRESS_TWO = '0x0000000000000000000000000000000000000002';

export const DUMMY_BYTES32_HEX =
'0x0000000000000000000000000000000000000000000000000000000000000000';
export const DUMMY_INSTALLATION_ID =
'0x000f1e7f040a60eb32b1b2348cf41924f85e221648a4d0a5ba66d2dea1122ee6';
17 changes: 5 additions & 12 deletions subgraph/tests/ids.test.ts → subgraph/tests/ids/dao.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import {Address} from '@graphprotocol/graph-ts';
import {getDaoId} from '../src';
import {getDaoId} from '../../src';
import {
afterAll,
assert,
beforeAll,
describe,
test,
test
} from 'matchstick-as/assembly/index';
import {ADDRESS_ONE} from './constants';
import {ADDRESS_ONE} from '../constants';

// Tests structure (matchstick-as >=0.5.0)
// https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0

describe('Describe id tests', () => {
beforeAll(() => {
// before all tests
});

afterAll(() => {
// after all tests
});
test('getDaoId', () => {
describe('DAO ID generation', () => {
test('`getDaoId` should return the hexadecimal representation of the provided address', () => {
const address = Address.fromString(ADDRESS_ONE);
assert.stringEquals(getDaoId(address), ADDRESS_ONE);
});
Expand Down
158 changes: 158 additions & 0 deletions subgraph/tests/ids/pluginRepo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import {Address, Bytes, crypto} from '@graphprotocol/graph-ts';
import {assert, describe, log, test} from 'matchstick-as/assembly/index';
import {
getPluginRepoId,
getPluginSetupId,
getPluginInstallationId,
getPluginPreparationId,
getPluginReleaseId,
getPluginVersionId,
getPluginPermissionId
} from '../../src';
import {
ADDRESS_ONE,
ADDRESS_TWO,
DUMMY_BYTES32_HEX,
DUMMY_INSTALLATION_ID
} from '../constants';
import {PERMISSION_OPERATIONS} from '../../src/utils/constants';

describe('PluginRepo ID generation', () => {
test('`getPluginRepoId` should return the hexadecimal representation of the provided address', () => {
// Constants
const PLUGIN_REPO_ADDRESS = Address.fromString(ADDRESS_ONE);

assert.stringEquals(getPluginRepoId(PLUGIN_REPO_ADDRESS), ADDRESS_ONE);
});

test('`getPluginSetupId` should return the hexadecimal representation of the provided address', () => {
// Constants
const PLUGIN_SETUP_ADDRESS = Address.fromString(ADDRESS_ONE);

assert.stringEquals(getPluginSetupId(PLUGIN_SETUP_ADDRESS), ADDRESS_ONE);
});

test('`getPluginInstallationId` should return a unique ID based on the DAO and plugins address', () => {
// Constants
const DAO_ADDRESS = Address.fromString(ADDRESS_ONE);
const PLUGIN_ADDRESS = Address.fromString(ADDRESS_TWO);

// Generate the pluginInstallationId.
const pluginInstallationId = getPluginInstallationId(
DAO_ADDRESS,
PLUGIN_ADDRESS
);

if (pluginInstallationId) {
const expectedEncodedBytes = Bytes.fromHexString(
'0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'
);

const expectedId = Bytes.fromHexString(
crypto.keccak256(expectedEncodedBytes).toHexString()
).toHexString();

assert.stringEquals(pluginInstallationId, expectedId);

// The test is done.
return;
}

// Test should fail if `pluginInstallationId` is null.
assert.assertTrue(false);
});

test('`getPluginPreparationId` should return a concatenated ID string for plugin preparation', () => {
// Constants
const DAO_ADDRESS = Address.fromString(ADDRESS_ONE);
const PLUGIN_ADDRESS = Address.fromString(ADDRESS_TWO);

// Generate the pluginInstallationId.
const pluginInstallationId = getPluginInstallationId(
DAO_ADDRESS,
PLUGIN_ADDRESS
);

if (pluginInstallationId) {
const pluginSetupId = Bytes.fromHexString(DUMMY_BYTES32_HEX);

// Generate the pluginPreparationId.
const pluginPreparationId = getPluginPreparationId(
pluginInstallationId,
pluginSetupId
);

// Expected result
const expectedPreparationId = `${pluginInstallationId}_${DUMMY_BYTES32_HEX}`;

assert.stringEquals(pluginPreparationId, expectedPreparationId);

// test is done.
return;
}

// Test should fail if `pluginInstallationId` is null.
assert.assertTrue(false);
});

test('`getPluginReleaseId` should return an ID string for the plugin release.', () => {
// Constants
const PLUGIN_REPO_ADDRESS = Address.fromString(ADDRESS_ONE);
const PLUGIN_RELEASE: i32 = 1;

// Generate the pluginReleaseId.
const pluginReleaseId = getPluginReleaseId(
PLUGIN_REPO_ADDRESS,
PLUGIN_RELEASE
);

const expectedId = `${ADDRESS_ONE}_${PLUGIN_RELEASE}`;

assert.stringEquals(pluginReleaseId, expectedId);
});

test('`getPluginVersionId` should return an ID string for the plugin version.', () => {
// Constants
const PLUGIN_REPO_ADDRESS = Address.fromString(ADDRESS_ONE);
const PLUGIN_RELEASE: i32 = 1;
const PLUGIN_BUILD: i32 = 2;

// Generate the pluginVersionId.
const pluginVersionId = getPluginVersionId(
PLUGIN_REPO_ADDRESS,
PLUGIN_RELEASE,
PLUGIN_BUILD
);

const expectedId = `${ADDRESS_ONE}_${PLUGIN_RELEASE}_${PLUGIN_BUILD}`;

assert.stringEquals(pluginVersionId, expectedId);
});

test('`getPluginPermissionId` should return a concatenated unique ID string for the plugin permission', () => {
// Constants
const OPERATION: i32 = 1;
const WHERE_ADDRESS = Address.fromString(ADDRESS_ONE);
const WHO_ADDRESS = Address.fromString(ADDRESS_TWO);
const PERMISSION_ID = Bytes.fromHexString(DUMMY_BYTES32_HEX);
const PLUGIN_PREPARATION_ID = getPluginPreparationId(
DUMMY_BYTES32_HEX,
Bytes.fromHexString(DUMMY_BYTES32_HEX)
);

// Generate the pluginPermissionId
const pluginPermissionId = getPluginPermissionId(
PLUGIN_PREPARATION_ID,
OPERATION,
WHERE_ADDRESS,
WHO_ADDRESS,
PERMISSION_ID
);

const expectedId = `${PLUGIN_PREPARATION_ID}_${PERMISSION_OPERATIONS.get(
OPERATION
)}_${WHERE_ADDRESS.toHexString()}_${WHO_ADDRESS.toHexString()}_${PERMISSION_ID.toHexString()}`;

assert.stringEquals(pluginPermissionId, expectedId);
});
});

0 comments on commit 91d9e53

Please sign in to comment.