Skip to content

Commit

Permalink
fix: refactor config (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
simboonlong authored Dec 16, 2021
1 parent e292fc6 commit 85d3cd1
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 200 deletions.
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,30 +447,29 @@ rm ./examples/sample-key
open-attestation deploy document-store "My Name" --network ropsten --key 0000000000000000000000000000000000000000000000000000000000000003
```

### Configuration file
### Config (Create configuration file)

This command will generate a config file with sandbox DNS, document store and token registry.

Please note that a wallet.json file in ropsten network must be provided in order for this command to work.

Please also note that there must have funds in the Ropsten wallet, as we require to pay some gas fees to generate the config file.
> Please note that a wallet.json file in ropsten network with sufficient **funds** must be provided in order for this command to work.

You will need:

- `--output-dir` option specify which folder the config file will be created in.
- `--encrypted-wallet-path` option indicates a path to an [encrypted wallet](https://docs.ethers.io/v5/api/signer/#Wallet-encrypt).
- `--config-template-path` option to provide a path to a config file.
- `--config-type` option specify which default template to use to create the config file, i.e. `tradetrust`.
- `--config-template-path` option to provide a path to reference a config template file.
- `--config-type` option to specify which config template to reference to create the config file, i.e. `tradetrust`.

There are 2 ways of using this command to generate a config file, both in which, will return a new config file with sandbox DNS, updated document store and updated token registry.

#### Method 1: Using config-type option (recommended)

This method will generate the most basic config file with a sandbox DNS, document store and token registry.
This method will generate the most basic config file with a sandbox DNS, document store and token registry. The reference config templates are [here](https://github.com/Open-Attestation/open-attestation-cli/tree/master/src/implementations/config/templates).

Step 1: Generate a wallet.json file

```
// If you already have a wallet.json that is in ropsten network and it does have some funds in it, you can skip this step.
// If you already have a wallet.json that is in ropsten network with sufficient funds, you can skip this step.
open-attestation wallet create --output-file wallet.json --fund ropsten
```

Expand All @@ -487,7 +486,7 @@ This method will generate a copy of your existing config file with the updated s
Step 1: Generate a wallet.json file

```
// If you already have a wallet.json that is in ropsten network and it does have some funds in it, you can skip this step.
// If you already have a wallet.json that is in ropsten network with sufficient funds, you can skip this step.
open-attestation wallet create --output-file wallet.json --fund ropsten
```

Expand Down
6 changes: 3 additions & 3 deletions src/implementations/config/__tests__/input-config-file.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"type": "DNS-TXT",
"location": "demo-tradetrust.openattestation.com"
},
"name": "DEMO STORE",
"name": "DEMO TOKEN REGISTRY",
"tokenRegistry": "0x13249BA1Ec6B957Eb35D34D7b9fE5D91dF225B5B"
}
]
Expand All @@ -27,7 +27,7 @@
"defaults": {
"issuers": [
{
"name": "Demo Issuer",
"name": "DEMO DOCUMENT STORE",
"documentStore": "0x8bA63EAB43342AAc3AdBB4B827b68Cf4aAE5Caca",
"identityProof": {
"type": "DNS-TXT",
Expand All @@ -44,7 +44,7 @@
"issuers": [
{
"id": "did:ethr:0xaCc51f664D647C9928196c4e33D46fd98FDaA91D",
"name": "Demo Issuer",
"name": "DEMO ISSUER",
"revocation": {
"type": "NONE"
},
Expand Down
204 changes: 133 additions & 71 deletions src/implementations/config/create.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,160 @@
import { Issuer } from "@govtechsg/open-attestation/dist/types/__generated__/schema.2.0";
import { Issuer, RevocationType } from "@govtechsg/open-attestation/dist/types/__generated__/schema.2.0";
import fs from "fs";
import path from "path";
import { info } from "signale";
import { info, success } from "signale";
import { highlight } from "../../utils";
import { readFile } from "../../implementations/utils/disk";
import { handler as createTemporaryDns } from "../../commands/dns/txt-record/create";
import { CreateConfigCommand } from "../../commands/config/config.type";
import { ConfigFile, TypesOfForms } from "./types";
import { ConfigFile, Form } from "./types";
import { Wallet } from "ethers";
import { getDocumentStoreOrTokenRegistryAddress, updateConfigFile, updateForms, getConfigFile } from "./helpers";
import tradetrustConfig from "./templates/tradetrust.json";
import { deployDocumentStore } from "../../implementations/deploy/document-store";
import { deployTokenRegistry } from "../../implementations/deploy/token-registry";

const sandboxEndpointUrl = "https://sandbox.fyntech.io";
const SANDBOX_ENDPOINT_URL = "https://sandbox.fyntech.io";

const getConfigFile = async (configType: string, configTemplatePath: string): Promise<ConfigFile> => {
switch (configType) {
case "tradetrust":
return tradetrustConfig as ConfigFile;
default:
return JSON.parse(await readFile(configTemplatePath));
}
};

const getTokenRegistryAddress = async (encryptedWalletPath: string): Promise<string> => {
info(`Enter password to continue deployment of Token Registry`);
const tokenRegistry = await deployTokenRegistry({
encryptedWalletPath,
network: "ropsten",
gasPriceScale: 1,
dryRun: false,
registryName: "Token Registry",
registrySymbol: "TR",
});
const { contractAddress } = tokenRegistry;
success(`Token registry deployed, address: ${highlight(contractAddress)}`);
return contractAddress;
};

const getDocumentStoreAddress = async (encryptedWalletPath: string): Promise<string> => {
info(`Enter password to continue deployment of Document Store`);
const documentStore = await deployDocumentStore({
encryptedWalletPath,
network: "ropsten",
gasPriceScale: 1,
dryRun: false,
storeName: "Document Store",
});
const { contractAddress } = documentStore;
success(`Document store deployed, address: ${highlight(contractAddress)}`);
return contractAddress;
};

const validate = (forms: Form[]): boolean => {
const isValidForm = forms.some((form: Form) => {
const isValidFormType = form.type === "TRANSFERABLE_RECORD" && "VERIFIABLE_DOCUMENT";
const isValidIdentityProofType = form.defaults.issuers.some(
(issuer: any) => issuer.identityProof?.type === "DNS-TXT" && "DNS-DID" && "DID"
);

return isValidFormType && isValidIdentityProofType;
});

return isValidForm;
};

export const create = async ({
encryptedWalletPath,
outputDir,
configType,
configTemplatePath,
}: CreateConfigCommand): Promise<string | undefined> => {
}: CreateConfigCommand): Promise<string> => {
const wallet = await readFile(encryptedWalletPath);
const walletObject = JSON.parse(wallet) as Wallet;

info(`Wallet detected at ${encryptedWalletPath}`);

const configFile: ConfigFile = await getConfigFile(configType, configTemplatePath);

const configFile = await getConfigFile(configType, configTemplatePath);
const { forms } = configFile;

const getContractAddress = async (
typesOfForms: TypesOfForms[],
formType: "VERIFIABLE_DOCUMENT" | "TRANSFERABLE_RECORD",
identityProofType: "DNS-DID" | "DNS-TXT"
): Promise<string> => {
const isValidForm = typesOfForms.some(
(item: TypesOfForms) =>
item.type === formType && (<any>Object).values(item.identityProofTypes).includes(identityProofType)
);
if (!validate(forms)) {
throw new Error("Invalid form detected in config file, please update the form before proceeding.");
}

if (!isValidForm)
throw new Error("Invalid form detected in config file, please update the form before proceeding.");

switch (true) {
case formType === "TRANSFERABLE_RECORD":
case formType === "VERIFIABLE_DOCUMENT" && identityProofType === "DNS-TXT":
return await getDocumentStoreOrTokenRegistryAddress(encryptedWalletPath, formType);

case formType === "VERIFIABLE_DOCUMENT" && identityProofType === "DNS-DID":
info(`Creating temporary DNS for DID`);
return (
(await createTemporaryDns({
networkId: 3,
publicKey: `did:ethr:0x${walletObject.address}#controller`,
sandboxEndpoint: sandboxEndpointUrl,
})) || ""
);

default:
throw new Error("Invalid form detected in config file, please update the form before proceeding.");
}
};
const hasTransferableRecord = forms.some((form) => form.type === "TRANSFERABLE_RECORD");
const hasDocumentStore = forms.some((form) => form.type === "VERIFIABLE_DOCUMENT");
const hasDid = forms.some((form) =>
form.defaults.issuers.some((issuer) => issuer.identityProof?.type.includes("DID"))
);

// loop through the form template to check the type of forms
const typesOfForms: TypesOfForms[] = forms.map((form) => {
const identityProofTypes = form.defaults.issuers.map((issuer: Issuer) => issuer.identityProof?.type);
return {
type: form.type,
identityProofTypes: identityProofTypes,
};
});
let tokenRegistryAddress = "";
let documentStoreAddress = "";
let dnsNameTransferableRecord: string | undefined = "";
let dnsNameVerifiable: string | undefined = "";
let dnsNameDid: string | undefined = "";

// generate doc store, token registry and DNS based on the form type in the form template
const documentStoreAddress = await getContractAddress(typesOfForms, "VERIFIABLE_DOCUMENT", "DNS-TXT");
const verifiableDocumentDnsTxtName = documentStoreAddress
? await createTemporaryDns({ networkId: 3, address: documentStoreAddress, sandboxEndpoint: sandboxEndpointUrl })
: "";
const verifiableDocumentDnsDidName = await getContractAddress(typesOfForms, "VERIFIABLE_DOCUMENT", "DNS-DID");
const tokenRegistryAddress = await getContractAddress(typesOfForms, "TRANSFERABLE_RECORD", "DNS-TXT");
const tokenRegistryDnsName = tokenRegistryAddress
? await createTemporaryDns({ networkId: 3, address: tokenRegistryAddress, sandboxEndpoint: sandboxEndpointUrl })
: "";

const updatedForms = updateForms(
forms,
documentStoreAddress,
tokenRegistryAddress,
walletObject,
verifiableDocumentDnsTxtName || "",
verifiableDocumentDnsDidName,
tokenRegistryDnsName || ""
);
if (hasTransferableRecord) {
tokenRegistryAddress = await getTokenRegistryAddress(encryptedWalletPath);
dnsNameTransferableRecord = await createTemporaryDns({
networkId: 3,
address: tokenRegistryAddress,
sandboxEndpoint: SANDBOX_ENDPOINT_URL,
});
}

const updatedConfigFile = updateConfigFile(configFile, wallet, updatedForms);
if (hasDocumentStore) {
documentStoreAddress = await getDocumentStoreAddress(encryptedWalletPath);
dnsNameVerifiable = await createTemporaryDns({
networkId: 3,
address: documentStoreAddress,
sandboxEndpoint: SANDBOX_ENDPOINT_URL,
});
}

const configFileName = "config.json";
const outputPath = path.join(outputDir, configFileName);
if (hasDid) {
// DID no need deploy any
dnsNameDid = await createTemporaryDns({
networkId: 3,
publicKey: `did:ethr:0x${walletObject.address}#controller`,
sandboxEndpoint: SANDBOX_ENDPOINT_URL,
});
}

const updatedForms = forms.map((form) => {
if (form.type === "VERIFIABLE_DOCUMENT") {
const updatedIssuers = form.defaults.issuers.map((issuer: Issuer) => {
if (issuer.identityProof?.type === "DNS-TXT") {
issuer.documentStore = documentStoreAddress;
if (issuer.identityProof?.location) issuer.identityProof.location = dnsNameVerifiable;
} else if (issuer.identityProof?.type.includes("DID")) {
issuer.id = `did:ethr:0x${walletObject.address}`;
if (issuer.revocation?.type) issuer.revocation.type = "NONE" as RevocationType;
if (issuer.identityProof?.location) issuer.identityProof.location = dnsNameDid;
if (issuer.identityProof?.key) issuer.identityProof.key = `did:ethr:0x${walletObject.address}#controller`;
}
return issuer;
});
form.defaults.issuers = updatedIssuers;
}
if (form.type === "TRANSFERABLE_RECORD") {
const updatedIssuers = form.defaults.issuers.map((issuer: Issuer) => {
issuer.tokenRegistry = tokenRegistryAddress;
if (issuer.identityProof?.location) issuer.identityProof.location = dnsNameTransferableRecord;
return issuer;
});
form.defaults.issuers = updatedIssuers;
}
return form;
});

const updatedConfigFile = {
...configFile,
wallet: { ...configFile.wallet, encryptedJson: wallet },
forms: updatedForms,
};
const outputPath = path.join(outputDir, "config.json");
fs.writeFileSync(outputPath, JSON.stringify(updatedConfigFile, null, 2));

return outputPath;
};
Loading

0 comments on commit 85d3cd1

Please sign in to comment.