forked from trent_larson/crowd-funder-for-time-pwa
First iteration of account creation. More refactoring to come
This commit is contained in:
65
src/libs/crypto/index.ts
Normal file
65
src/libs/crypto/index.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup";
|
||||
import { getRandomBytesSync } from "ethereum-cryptography/random";
|
||||
import { entropyToMnemonic } from "ethereum-cryptography/bip39";
|
||||
import { wordlist } from "ethereum-cryptography/bip39/wordlists/english";
|
||||
import { HDNode } from "@ethersproject/hdnode";
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {string} address
|
||||
* @param {string} publicHex
|
||||
* @param {string} privateHex
|
||||
* @param {string} derivationPath
|
||||
* @return {*} {Omit<IIdentifier, 'provider'>}
|
||||
*/
|
||||
export const newIdentifier = (
|
||||
address: string,
|
||||
publicHex: string,
|
||||
privateHex: string,
|
||||
derivationPath: string
|
||||
): Omit<IIdentifier, keyof "provider"> => {
|
||||
return {
|
||||
did: DEFAULT_DID_PROVIDER_NAME + ":" + address,
|
||||
keys: [
|
||||
{
|
||||
kid: publicHex,
|
||||
kms: "local",
|
||||
meta: { derivationPath: derivationPath },
|
||||
privateKeyHex: privateHex,
|
||||
publicKeyHex: publicHex,
|
||||
type: "Secp256k1",
|
||||
},
|
||||
],
|
||||
provider: DEFAULT_DID_PROVIDER_NAME,
|
||||
services: [],
|
||||
};
|
||||
};
|
||||
|
||||
export const deriveAddress = (
|
||||
mnemonic: string
|
||||
): [string, string, string, string] => {
|
||||
const UPORT_ROOT_DERIVATION_PATH = "m/7696500'/0'/0'/0'";
|
||||
mnemonic = mnemonic.trim().toLowerCase();
|
||||
|
||||
const hdnode: HDNode = HDNode.fromMnemonic(mnemonic);
|
||||
const rootNode: HDNode = hdnode.derivePath(UPORT_ROOT_DERIVATION_PATH);
|
||||
const privateHex = rootNode.privateKey.substring(2); // original starts with '0x'
|
||||
const publicHex = rootNode.publicKey.substring(2); // original starts with '0x'
|
||||
const address = rootNode.address;
|
||||
|
||||
return [address, privateHex, publicHex, UPORT_ROOT_DERIVATION_PATH];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @return {*} {string}
|
||||
*/
|
||||
export const createIdentifier = (): string => {
|
||||
const entropy: Uint8Array = getRandomBytesSync(32);
|
||||
const mnemonic = entropyToMnemonic(entropy, wordlist);
|
||||
|
||||
return mnemonic;
|
||||
};
|
||||
100
src/libs/veramo/appSlice.ts
Normal file
100
src/libs/veramo/appSlice.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/* import * as R from "ramda";
|
||||
import { configureStore, createSlice } from "@reduxjs/toolkit";
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
|
||||
import { Contact } from "../entity/contact";
|
||||
import { Settings } from "../entity/settings";
|
||||
import * as utility from "../utility/utility";
|
||||
|
||||
const MAX_LOG_LENGTH = 2000000;
|
||||
|
||||
export const DEFAULT_ENDORSER_API_SERVER = "https://endorser.ch:3000";
|
||||
export const DEFAULT_ENDORSER_VIEW_SERVER = "https://endorser.ch";
|
||||
export const LOCAL_ENDORSER_API_SERVER = "http://127.0.0.1:3000";
|
||||
export const LOCAL_ENDORSER_VIEW_SERVER = "http://127.0.0.1:3001";
|
||||
export const TEST_ENDORSER_API_SERVER = "https://test.endorser.ch:8000";
|
||||
export const TEST_ENDORSER_VIEW_SERVER = "https://test.endorser.ch:8080";
|
||||
|
||||
// for contents set in reducers
|
||||
interface Payload<T> {
|
||||
type: string;
|
||||
payload: T;
|
||||
}
|
||||
|
||||
interface LogMsg {
|
||||
log: boolean;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
export const appSlice = createSlice({
|
||||
name: "app",
|
||||
initialState: {
|
||||
// This is nullable because it is cached state from the DB...
|
||||
// it'll be null if we haven't even loaded from the DB yet.
|
||||
settings: null as Settings,
|
||||
|
||||
// This is nullable because it is cached state from the DB...
|
||||
// it'll be null if we haven't even loaded from the DB yet.
|
||||
identifiers: null as Array<IIdentifier> | null,
|
||||
|
||||
// This is nullable because it is cached state from the DB...
|
||||
// it'll be null if we haven't even loaded from the DB yet.
|
||||
contacts: null as Array<Contact> | null,
|
||||
|
||||
viewServer: DEFAULT_ENDORSER_VIEW_SERVER,
|
||||
|
||||
logMessage: "",
|
||||
|
||||
advancedMode: false,
|
||||
testMode: false,
|
||||
},
|
||||
reducers: {
|
||||
addIdentifier: (state, contents: Payload<IIdentifier>) => {
|
||||
state.identifiers = state.identifiers.concat([contents.payload]);
|
||||
},
|
||||
addLog: (state, contents: Payload<LogMsg>) => {
|
||||
if (state.logMessage.length > MAX_LOG_LENGTH) {
|
||||
state.logMessage =
|
||||
"<truncated>\n..." +
|
||||
state.logMessage.substring(
|
||||
state.logMessage.length - MAX_LOG_LENGTH / 2
|
||||
);
|
||||
}
|
||||
if (contents.payload.log) {
|
||||
console.log(contents.payload.msg);
|
||||
state.logMessage += "\n" + contents.payload.msg;
|
||||
}
|
||||
},
|
||||
setAdvancedMode: (state, contents: Payload<boolean>) => {
|
||||
state.advancedMode = contents.payload;
|
||||
},
|
||||
setContacts: (state, contents: Payload<Array<Contact>>) => {
|
||||
state.contacts = contents.payload;
|
||||
},
|
||||
setContact: (state, contents: Payload<Contact>) => {
|
||||
const index = R.findIndex(
|
||||
(c) => c.did === contents.payload.did,
|
||||
state.contacts
|
||||
);
|
||||
state.contacts[index] = contents.payload;
|
||||
},
|
||||
setHomeScreen: (state, contents: Payload<string>) => {
|
||||
state.settings.homeScreen = contents.payload;
|
||||
},
|
||||
setIdentifiers: (state, contents: Payload<Array<IIdentifier>>) => {
|
||||
state.identifiers = contents.payload;
|
||||
},
|
||||
setSettings: (state, contents: Payload<Settings>) => {
|
||||
state.settings = contents.payload;
|
||||
},
|
||||
setTestMode: (state, contents: Payload<boolean>) => {
|
||||
state.testMode = contents.payload;
|
||||
},
|
||||
setViewServer: (state, contents: Payload<string>) => {
|
||||
state.viewServer = contents.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const appStore = configureStore({ reducer: appSlice.reducer });
|
||||
*/
|
||||
151
src/libs/veramo/setup.ts
Normal file
151
src/libs/veramo/setup.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
// Created from the setup in https://veramo.io/docs/guides/react_native
|
||||
|
||||
// Core interfaces
|
||||
/* import {
|
||||
createAgent,
|
||||
IDIDManager,
|
||||
IResolver,
|
||||
IDataStore,
|
||||
IKeyManager,
|
||||
} from "@veramo/core";
|
||||
*/
|
||||
// Core identity manager plugin
|
||||
//import { DIDManager } from "@veramo/did-manager";
|
||||
|
||||
// Ethr did identity provider
|
||||
//import { EthrDIDProvider } from "@veramo/did-provider-ethr";
|
||||
|
||||
// Core key manager plugin
|
||||
//import { KeyManager } from "@veramo/key-manager";
|
||||
|
||||
// Custom key management system for RN
|
||||
//import { KeyManagementSystem } from '@veramo/kms-local-react-native'
|
||||
|
||||
// Custom resolver
|
||||
// Custom resolvers
|
||||
//import { DIDResolverPlugin } from "@veramo/did-resolver";
|
||||
/* import { Resolver } from "did-resolver";
|
||||
import { getResolver as ethrDidResolver } from "ethr-did-resolver";
|
||||
import { getResolver as webDidResolver } from "web-did-resolver";
|
||||
*/
|
||||
// for VCs and VPs https://veramo.io/docs/api/credential-w3c
|
||||
//import { CredentialIssuer } from '@veramo/credential-w3c'
|
||||
|
||||
// Storage plugin using TypeOrm
|
||||
/* import {
|
||||
Entities,
|
||||
KeyStore,
|
||||
DIDStore,
|
||||
IDataStoreORM,
|
||||
} from "@veramo/data-store";
|
||||
*/
|
||||
// TypeORM is installed with @veramo/typeorm
|
||||
//import { createConnection } from 'typeorm'
|
||||
|
||||
//import * as R from "ramda";
|
||||
|
||||
/*
|
||||
import { Contact } from '../entity/contact'
|
||||
import { Settings } from '../entity/settings'
|
||||
import { PrivateData } from '../entity/privateData'
|
||||
|
||||
import { Initial1616938713828 } from '../migration/1616938713828-initial'
|
||||
import { SettingsContacts1616967972293 } from '../migration/1616967972293-settings-contacts'
|
||||
import { EncryptedSeed1637856484788 } from '../migration/1637856484788-EncryptedSeed'
|
||||
import { HomeScreenConfig1639947962124 } from '../migration/1639947962124-HomeScreenConfig'
|
||||
import { HandlePublicKeys1652142819353 } from '../migration/1652142819353-HandlePublicKeys'
|
||||
import { LastClaimsSeen1656811846836 } from '../migration/1656811846836-LastClaimsSeen'
|
||||
import { ContactRegistered1662256903367 }from '../migration/1662256903367-ContactRegistered'
|
||||
import { PrivateData1663080623479 } from '../migration/1663080623479-PrivateData'
|
||||
|
||||
const ALL_ENTITIES = Entities.concat([Contact, Settings, PrivateData])
|
||||
|
||||
// Create react native DB connection configured by ormconfig.js
|
||||
|
||||
export const dbConnection = createConnection({
|
||||
database: 'endorser-mobile.sqlite',
|
||||
entities: ALL_ENTITIES,
|
||||
location: 'default',
|
||||
logging: ['error', 'info', 'warn'],
|
||||
migrations: [ Initial1616938713828, SettingsContacts1616967972293, EncryptedSeed1637856484788, HomeScreenConfig1639947962124, HandlePublicKeys1652142819353, LastClaimsSeen1656811846836, ContactRegistered1662256903367, PrivateData1663080623479 ],
|
||||
migrationsRun: true,
|
||||
type: 'react-native',
|
||||
})
|
||||
*/
|
||||
function didProviderName(netName: string) {
|
||||
return "did:ethr" + (netName === "mainnet" ? "" : ":" + netName);
|
||||
}
|
||||
|
||||
//const NETWORK_NAMES = ["mainnet", "rinkeby"];
|
||||
|
||||
const DEFAULT_DID_PROVIDER_NETWORK_NAME = "mainnet";
|
||||
|
||||
export const DEFAULT_DID_PROVIDER_NAME = didProviderName(
|
||||
DEFAULT_DID_PROVIDER_NETWORK_NAME
|
||||
);
|
||||
|
||||
export const HANDY_APP = false;
|
||||
|
||||
// this is used as the object in RegisterAction claims
|
||||
export const SERVICE_ID = "endorser.ch";
|
||||
|
||||
//const INFURA_PROJECT_ID = "INFURA_PROJECT_ID";
|
||||
/*
|
||||
const providers = {}
|
||||
NETWORK_NAMES.forEach((networkName) => {
|
||||
providers[didProviderName(networkName)] = new EthrDIDProvider({
|
||||
defaultKms: 'local',
|
||||
network: networkName,
|
||||
rpcUrl: 'https://' + networkName + '.infura.io/v3/' + INFURA_PROJECT_ID,
|
||||
gas: 1000001,
|
||||
ttl: 60 * 60 * 24 * 30 * 12 + 1,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const didManager = new DIDManager({
|
||||
store: new DIDStore(dbConnection),
|
||||
defaultProvider: DEFAULT_DID_PROVIDER_NAME,
|
||||
providers: providers,
|
||||
})
|
||||
*/
|
||||
|
||||
/* const basicDidResolvers = NETWORK_NAMES.map((networkName) => [
|
||||
networkName,
|
||||
new Resolver({
|
||||
ethr: ethrDidResolver({
|
||||
networks: [
|
||||
{
|
||||
name: networkName,
|
||||
rpcUrl:
|
||||
"https://" + networkName + ".infura.io/v3/" + INFURA_PROJECT_ID,
|
||||
},
|
||||
],
|
||||
}).ethr,
|
||||
web: webDidResolver().web,
|
||||
}),
|
||||
]);
|
||||
|
||||
const basicResolverMap = R.fromPairs(basicDidResolvers)
|
||||
|
||||
export const DEFAULT_BASIC_RESOLVER = basicResolverMap[DEFAULT_DID_PROVIDER_NETWORK_NAME]
|
||||
|
||||
const agentDidResolvers = NETWORK_NAMES.map((networkName) => {
|
||||
return new DIDResolverPlugin({
|
||||
resolver: basicResolverMap[networkName],
|
||||
})
|
||||
})
|
||||
|
||||
let allPlugins = [
|
||||
new CredentialIssuer(),
|
||||
new KeyManager({
|
||||
store: new KeyStore(dbConnection),
|
||||
kms: {
|
||||
local: new KeyManagementSystem(),
|
||||
},
|
||||
}),
|
||||
didManager,
|
||||
].concat(agentDidResolvers)
|
||||
*/
|
||||
|
||||
//export const agent = createAgent<IDIDManager & IKeyManager & IDataStore & IDataStoreORM & IResolver>({ plugins: allPlugins })
|
||||
@@ -167,77 +167,62 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
import { getRandomBytesSync } from "ethereum-cryptography/random";
|
||||
import { entropyToMnemonic } from "ethereum-cryptography/bip39";
|
||||
import { wordlist } from "ethereum-cryptography/bip39/wordlists/english";
|
||||
import { HDNode } from "@ethersproject/hdnode";
|
||||
import { useAppStore } from "../store/app";
|
||||
import { useAccountStore } from "../store/account";
|
||||
import { createIdentifier, deriveAddress, newIdentifier } from "../libs/crypto";
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
import * as R from "ramda";
|
||||
import R from "ramda";
|
||||
|
||||
@Options({
|
||||
components: {},
|
||||
})
|
||||
export default class AccountViewView extends Vue {
|
||||
created() {
|
||||
console.log("Component has been created!");
|
||||
const entropy = getRandomBytesSync(32);
|
||||
const mnemonic = entropyToMnemonic(entropy, wordlist);
|
||||
const mnemonicPassword = "HardCodedPassword";
|
||||
console.log(entropy, mnemonic);
|
||||
this.importAndStoreIdentifier(mnemonic, mnemonicPassword, false, []);
|
||||
}
|
||||
importAndStoreIdentifier(
|
||||
mnemonic: string,
|
||||
mnemonicPassword: string,
|
||||
toLowercase: boolean,
|
||||
previousIdentifiers: Array<IIdentifier>
|
||||
) {
|
||||
const UPORT_ROOT_DERIVATION_PATH = "m/7696500'/0'/0'/0'";
|
||||
mnemonic = mnemonic.trim().toLowerCase();
|
||||
console.log(mnemonic);
|
||||
const hdnode: HDNode = HDNode.fromMnemonic(mnemonic);
|
||||
const rootNode: HDNode = hdnode.derivePath(UPORT_ROOT_DERIVATION_PATH);
|
||||
const privateHex = rootNode.privateKey.substring(2); // original starts with '0x'
|
||||
const publicHex = rootNode.publicKey.substring(2); // original starts with '0x'
|
||||
let address = rootNode.address;
|
||||
const previousIdentifiers: Array<IIdentifier> = [];
|
||||
const toLowercase = true;
|
||||
const appStore = useAppStore();
|
||||
const accountStore = useAccountStore();
|
||||
|
||||
console.log(address, privateHex, publicHex);
|
||||
if (appStore._condition == "uninitialized") {
|
||||
const mnemonic = createIdentifier();
|
||||
|
||||
const prevIds = previousIdentifiers || [];
|
||||
if (toLowercase) {
|
||||
const foundEqual = R.find(
|
||||
(id: IIdentifier) => id.did.split(":")[2] === address,
|
||||
prevIds
|
||||
);
|
||||
if (foundEqual) {
|
||||
// They're trying to create a lowercase version of one that exists in normal case.
|
||||
// (We really should notify the user.)
|
||||
// appStore.dispatch(appSlice.actions.addLog({log: true, msg: "Will create a normal-case version of the DID since a regular version exists."}))
|
||||
const [address, privateHex, publicHex, UPORT_ROOT_DERIVATION_PATH] =
|
||||
deriveAddress(mnemonic);
|
||||
//appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... derived keys and address..."}))
|
||||
const prevIds = previousIdentifiers || [];
|
||||
let addr = address;
|
||||
if (toLowercase) {
|
||||
const foundEqual = R.find(
|
||||
(id: IIdentifier) => id.did.split(":")[2] === address,
|
||||
prevIds
|
||||
);
|
||||
if (foundEqual) {
|
||||
// appStore.dispatch(appSlice.actions.addLog({log: true, msg: "Will create a normal-case version of the DID since a regular version exists."}))
|
||||
} else {
|
||||
addr = address.toLowerCase();
|
||||
}
|
||||
} else {
|
||||
address = address.toLowerCase();
|
||||
// They're not trying to convert to lowercase.
|
||||
const foundLower = R.find(
|
||||
(id: IIdentifier) => id.did.split(":")[2] === address.toLowerCase(),
|
||||
prevIds
|
||||
);
|
||||
if (foundLower) {
|
||||
// appStore.dispatch(appSlice.actions.addLog({log: true, msg: "Will create a lowercase version of the DID since a lowercase version exists."}))
|
||||
addr = address.toLowerCase();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// They're not trying to convert to lowercase.
|
||||
const foundLower = R.find(
|
||||
(id: IIdentifier) => id.did.split(":")[2] === address.toLowerCase(),
|
||||
prevIds
|
||||
|
||||
const newId = newIdentifier(
|
||||
addr,
|
||||
publicHex,
|
||||
privateHex,
|
||||
UPORT_ROOT_DERIVATION_PATH
|
||||
);
|
||||
if (foundLower) {
|
||||
// They're trying to create a normal case version of one that exists in lowercase.
|
||||
// (We really should notify the user.)
|
||||
//appStore.dispatch(appSlice.actions.addLog({log: true, msg: "Will create a lowercase version of the DID since a lowercase version exists."}))
|
||||
address = address.toLowerCase();
|
||||
}
|
||||
//appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... created new ID..."}))
|
||||
accountStore.account = JSON.stringify(newId);
|
||||
//appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... stored new ID..."}))
|
||||
}
|
||||
//appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... derived keys and address..."}))
|
||||
|
||||
//const newId = newIdentifier(address, publicHex, privateHex, UPORT_ROOT_DERIVATION_PATH)
|
||||
//appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... created new ID..."}))
|
||||
|
||||
// awaiting because otherwise the UI may not see that a mnemonic was created
|
||||
//const savedId = await storeIdentifier(newId, mnemonic, mnemonicPassword)
|
||||
//appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... stored new ID..."}))
|
||||
//return savedId;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user