Browse Source

Merge branch 'peer-db-replication-sagas' into 'implement-ui'

Peer db replication sagas

See merge request ecentrics/apella!1
develop
Apostolos Fanakis 4 years ago
parent
commit
59ba0a7ef3
  1. 9
      packages/concordia-app/package.json
  2. BIN
      packages/concordia-app/src/assets/images/orbitdb_logo.png
  3. 1
      packages/concordia-app/src/assets/images/orbitdb_logo.svg
  4. 2
      packages/concordia-app/src/components/LoadingComponent.jsx
  5. 8
      packages/concordia-app/src/options/breezeOptions.js
  6. 23
      packages/concordia-app/src/orbit/levelUtils.js
  7. 136
      packages/concordia-app/src/orbit/ΕthereumIdentityProvider.js
  8. 8
      packages/concordia-app/src/redux/sagas/orbitSaga.js
  9. 18
      packages/concordia-app/src/redux/sagas/peerDbReplicationSaga.js
  10. 4
      packages/concordia-app/src/utils/orbitUtils.js
  11. 4
      packages/concordia-contracts/package.json
  12. 1380
      yarn.lock

9
packages/concordia-app/package.json

@ -8,8 +8,8 @@
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"postinstall": "patch-package", "postinstall": "patch-package",
"analyze": "source-map-explorer 'build/static/js/*.js'", "analyze": "react-scripts build && source-map-explorer 'build/static/js/*.js' --gzip",
"lint": "yarn run eslint --ext js,jsx . --format table" "lint": "eslint --ext js,jsx . --format table"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
@ -24,18 +24,17 @@
] ]
}, },
"dependencies": { "dependencies": {
"@ezerous/breeze": "~0.3.0", "@ezerous/breeze": "~0.4.0",
"@ezerous/drizzle": "~0.4.0", "@ezerous/drizzle": "~0.4.0",
"@ezerous/eth-identity-provider": "^0.1.0",
"@reduxjs/toolkit": "~1.4.0", "@reduxjs/toolkit": "~1.4.0",
"@welldone-software/why-did-you-render": "^6.0.0-rc.1", "@welldone-software/why-did-you-render": "^6.0.0-rc.1",
"concordia-contracts": "~0.1.0", "concordia-contracts": "~0.1.0",
"i18next": "^19.8.3", "i18next": "^19.8.3",
"i18next-browser-languagedetector": "^6.0.1", "i18next-browser-languagedetector": "^6.0.1",
"i18next-http-backend": "^1.0.21", "i18next-http-backend": "^1.0.21",
"level": "~6.0.1",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"moment": "^2.29.1", "moment": "^2.29.1",
"orbit-db-identity-provider": "~0.3.1",
"prop-types": "~15.7.2", "prop-types": "~15.7.2",
"react": "~16.13.1", "react": "~16.13.1",
"react-dom": "~16.13.1", "react-dom": "~16.13.1",

BIN
packages/concordia-app/src/assets/images/orbitdb_logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

1
packages/concordia-app/src/assets/images/orbitdb_logo.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

2
packages/concordia-app/src/components/LoadingComponent.jsx

@ -9,7 +9,7 @@ import '../assets/css/loading-component.css';
// Images // Images
import ethereumLogo from '../assets/images/ethereum_logo.svg'; import ethereumLogo from '../assets/images/ethereum_logo.svg';
import ipfsLogo from '../assets/images/ipfs_logo.svg'; import ipfsLogo from '../assets/images/ipfs_logo.svg';
import orbitdbLogo from '../assets/images/orbitdb_logo.png'; import orbitdbLogo from '../assets/images/orbitdb_logo.svg';
import appLogo from '../assets/images/app_logo.png'; import appLogo from '../assets/images/app_logo.png';
const LoadingComponent = (props) => { const LoadingComponent = (props) => {

8
packages/concordia-app/src/options/breezeOptions.js

@ -1,4 +1,4 @@
import EthereumIdentityProvider from '../orbit/ΕthereumIdentityProvider'; import { EthereumContractIdentityProvider } from '@ezerous/eth-identity-provider';
const breezeOptions = { const breezeOptions = {
ipfs: { ipfs: {
@ -23,14 +23,14 @@ const breezeOptions = {
}, },
}, },
orbit: { orbit: {
identityProvider: EthereumIdentityProvider, identityProvider: EthereumContractIdentityProvider,
databases: [ databases: [
{ {
name: 'topics', address: 'topics',
type: 'keyvalue', type: 'keyvalue',
}, },
{ {
name: 'posts', address: 'posts',
type: 'keyvalue', type: 'keyvalue',
}, },
], ],

23
packages/concordia-app/src/orbit/levelUtils.js

@ -1,23 +0,0 @@
import level from 'level';
/* Used in development only to store the identity.signatures.publicKey so developers don't have to
repeatedly sign theOrbitDB creation transaction in MetaMask when React development server reloads
the app */
const concordiaDB = level('./concordia/identities');
async function storeIdentitySignaturePubKey(key, signaturePubKey) {
await concordiaDB.put(key, signaturePubKey);
}
// If it exists, it returns the identity.signatures.publicKey for the given key (key is the
// concatenation of identity.publicKey + identity.signatures.id)
async function getIdentitySignaturePubKey(key) {
try {
return await concordiaDB.get(key);
} catch (err) {
if (err && err.notFound) return null; // Not found
throw err;
}
}
export { storeIdentitySignaturePubKey, getIdentitySignaturePubKey };

136
packages/concordia-app/src/orbit/ΕthereumIdentityProvider.js

@ -1,136 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable no-return-await */
import IdentityProvider from 'orbit-db-identity-provider';
import { getIdentitySignaturePubKey, storeIdentitySignaturePubKey } from './levelUtils';
const LOGGING_PREFIX = 'EthereumIdentityProvider: ';
class EthereumIdentityProvider extends IdentityProvider {
constructor(options = {}) {
if (!EthereumIdentityProvider.web3) {
throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because web3 wasn't set. `
+ 'Please use setWeb3(web3) first!');
}
if (!EthereumIdentityProvider.contractAddress) {
throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because contractAddress wasn't set. `
+ 'Please use setContractAddress(contractAddress) first!');
}
super(options);
// Orbit's Identity Id (user's Ethereum address) - Optional (will be grabbed later if omitted)
const { id } = options;
if (id) {
const { userAddress, contractAddress } = EthereumIdentityProvider.splitId(id);
if (EthereumIdentityProvider.web3.utils.isAddress(userAddress)
&& EthereumIdentityProvider.contractAddress === contractAddress) {
this.id = id;
this.userAddress = userAddress;
} else throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because an invalid id was supplied.`);
}
}
static get type() { return 'ethereum'; }
async getId() {
// Id wasn't in the constructor, grab it now
if (!this.id) {
const accounts = await EthereumIdentityProvider.web3.eth.getAccounts();
if (!accounts[0]) {
throw new Error(`${LOGGING_PREFIX}Couldn't create identity, because no web3 accounts were found (
locked Metamask?).`);
}
[this.userAddress] = accounts;
this.id = this.userAddress + EthereumIdentityProvider.contractAddress;
}
return this.id;
}
async signIdentity(data) {
if (process.env.NODE_ENV === 'development') { // Don't sign repeatedly while in development
console.debug(`${LOGGING_PREFIX}Attempting to find stored Orbit identity data...`);
const signaturePubKey = await getIdentitySignaturePubKey(data);
if (signaturePubKey) {
const identityInfo = {
userAddress: this.userAddress,
pubKeySignId: data,
signaturePubKey,
};
if (await EthereumIdentityProvider.verifyIdentityInfo(identityInfo)) {
console.debug(`${LOGGING_PREFIX}Found and verified stored Orbit identity data!`);
return signaturePubKey;
}
console.debug(`${LOGGING_PREFIX}Stored Orbit identity data couldn't be verified.`);
} else console.debug(`${LOGGING_PREFIX}No stored Orbit identity data were found.`);
}
return await this.doSignIdentity(data);
}
// eslint-disable-next-line consistent-return
async doSignIdentity(data) {
try {
const signaturePubKey = await EthereumIdentityProvider.web3.eth.personal.sign(data, this.userAddress, '');
if (process.env.NODE_ENV === 'development') {
storeIdentitySignaturePubKey(data, signaturePubKey)
.then(() => {
console.debug(`${LOGGING_PREFIX}Successfully stored current Orbit identity data.`);
})
.catch(() => {
console.warn(`${LOGGING_PREFIX}Couldn't store current Orbit identity data...`);
});
}
return signaturePubKey; // Password not required for MetaMask
} catch (error) {
if (error.code && error.code === 4001) {
console.debug(`${LOGGING_PREFIX}User denied message signature.`);
return await this.doSignIdentity(data);
}
console.error(`${LOGGING_PREFIX}Failed to sign data.`);
console.error(error);
}
}
static async verifyIdentity(identity) {
const { userAddress } = EthereumIdentityProvider.splitId(identity.id);
// Verify that identity was signed by the ID
return new Promise((resolve) => {
resolve(EthereumIdentityProvider.web3.eth.accounts.recover(identity.publicKey + identity.signatures.id,
identity.signatures.publicKey) === userAddress);
});
}
static async verifyIdentityInfo(identityInfo) {
// Verify that identity was signed by the ID
return new Promise((resolve) => {
resolve(EthereumIdentityProvider.web3.eth.accounts.recover(identityInfo.pubKeySignId,
identityInfo.signaturePubKey) === identityInfo.userAddress);
});
}
// Initialize by supplying a web3 object
static setWeb3(web3) {
EthereumIdentityProvider.web3 = web3;
}
// Initialize by supplying a contract's address (to be used as a point of reference)
static setContractAddress(contractAddress) {
EthereumIdentityProvider.contractAddress = contractAddress;
}
static splitId(id) {
const regex = /(0x.*)(0x.*)/g;
const match = regex.exec(id);
if (match && match.length === 3) {
return { userAddress: match[1], contractAddress: match[2] };
}
throw new Error(`${LOGGING_PREFIX}Invalid id ${id}! Couldn't split it to userAddress, contractAddress.`);
}
}
EthereumIdentityProvider.web3 = {};
EthereumIdentityProvider.contractAddress = {};
export default EthereumIdentityProvider;

8
packages/concordia-app/src/redux/sagas/orbitSaga.js

@ -6,11 +6,11 @@ import { breezeActions } from '@ezerous/breeze';
import { drizzleActions } from '@ezerous/drizzle'; import { drizzleActions } from '@ezerous/drizzle';
import { forumContract } from 'concordia-contracts'; import { forumContract } from 'concordia-contracts';
import EthereumIdentityProvider from '../../orbit/ΕthereumIdentityProvider'; import { EthereumContractIdentityProvider } from '@ezerous/eth-identity-provider';
function* initOrbitDatabases(action) { function* initOrbitDatabases(action) {
const { account, breeze } = action; const { account, breeze } = action;
yield put(breezeActions.orbit.orbitInit(breeze, account + EthereumIdentityProvider.contractAddress)); // same as breeze.initOrbit(account); yield put(breezeActions.orbit.orbitInit(breeze, account + EthereumContractIdentityProvider.contractAddress)); // same as breeze.initOrbit(account);
} }
function* orbitSaga() { function* orbitSaga() {
@ -24,8 +24,8 @@ function* orbitSaga() {
const networkId = yield call([web3.eth.net, web3.eth.net.getId]); const networkId = yield call([web3.eth.net, web3.eth.net.getId]);
const contractAddress = forumContract.networks[networkId].address; const contractAddress = forumContract.networks[networkId].address;
EthereumIdentityProvider.setContractAddress(contractAddress); EthereumContractIdentityProvider.setContractAddress(contractAddress);
EthereumIdentityProvider.setWeb3(web3); EthereumContractIdentityProvider.setWeb3(web3);
yield initOrbitDatabases({ breeze: res[1].breeze, account: res[2].accounts[0] }); yield initOrbitDatabases({ breeze: res[1].breeze, account: res[2].accounts[0] });
} }

18
packages/concordia-app/src/redux/sagas/peerDbReplicationSaga.js

@ -2,12 +2,12 @@ import {
call, put, select, takeEvery, call, put, select, takeEvery,
} from 'redux-saga/effects'; } from 'redux-saga/effects';
import { import {
createOrbitDatabase, addOrbitDB,
ORBIT_DATABASE_READY, ORBIT_DB_READY,
ORBIT_DATABASE_REPLICATED, ORBIT_DB_REPLICATED,
ORBIT_DATABASE_WRITE, ORBIT_DB_WRITE,
} from '@ezerous/breeze/src/orbit/orbitActions'; } from '@ezerous/breeze/src/orbit/orbitActions';
import determineKVAddress from '../../orbit/orbitUtils'; import determineKVAddress from '../../utils/orbitUtils';
import { FETCH_USER_DATABASE, UPDATE_ORBIT_DATA } from '../actions/peerDbReplicationActions'; import { FETCH_USER_DATABASE, UPDATE_ORBIT_DATA } from '../actions/peerDbReplicationActions';
function* fetchUserDb({ orbit, userAddress, dbName }) { function* fetchUserDb({ orbit, userAddress, dbName }) {
@ -15,7 +15,7 @@ function* fetchUserDb({ orbit, userAddress, dbName }) {
orbit, dbName, userAddress, orbit, dbName, userAddress,
}); });
yield put(createOrbitDatabase(orbit, { name: peerDbAddress, type: 'keyvalue' })); yield put(addOrbitDB({ address: peerDbAddress, type: 'keyvalue' }));
} }
function* updateReduxState({ database }) { function* updateReduxState({ database }) {
@ -71,9 +71,9 @@ function* updateReduxState({ database }) {
function* peerDbReplicationSaga() { function* peerDbReplicationSaga() {
yield takeEvery(FETCH_USER_DATABASE, fetchUserDb); yield takeEvery(FETCH_USER_DATABASE, fetchUserDb);
yield takeEvery(ORBIT_DATABASE_REPLICATED, updateReduxState); yield takeEvery(ORBIT_DB_REPLICATED, updateReduxState);
yield takeEvery(ORBIT_DATABASE_READY, updateReduxState); yield takeEvery(ORBIT_DB_READY, updateReduxState);
yield takeEvery(ORBIT_DATABASE_WRITE, updateReduxState); yield takeEvery(ORBIT_DB_WRITE, updateReduxState);
} }
export default peerDbReplicationSaga; export default peerDbReplicationSaga;

4
packages/concordia-app/src/orbit/orbitUtils.js → packages/concordia-app/src/utils/orbitUtils.js

@ -1,5 +1,5 @@
// https://github.com/orbitdb/orbit-db/blob/master/GUIDE.md#address // https://github.com/orbitdb/orbit-db/blob/master/GUIDE.md#address
import EthereumIdentityProvider from './ΕthereumIdentityProvider'; import { EthereumContractIdentityProvider } from '@ezerous/eth-identity-provider';
async function determineDBAddress({ async function determineDBAddress({
orbit, dbName, type, identityId, orbit, dbName, type, identityId,
@ -12,7 +12,7 @@ async function determineDBAddress({
async function determineKVAddress({ orbit, dbName, userAddress }) { async function determineKVAddress({ orbit, dbName, userAddress }) {
return determineDBAddress({ return determineDBAddress({
orbit, dbName, type: 'keyvalue', identityId: userAddress + EthereumIdentityProvider.contractAddress, orbit, dbName, type: 'keyvalue', identityId: userAddress + EthereumContractIdentityProvider.contractAddress,
}); });
} }

4
packages/concordia-contracts/package.json

@ -10,7 +10,7 @@
"_eslint": "yarn eslint . --format table", "_eslint": "yarn eslint . --format table",
"_solhint": "yarn solhint --formatter table contracts/*.sol test/*.sol", "_solhint": "yarn solhint --formatter table contracts/*.sol test/*.sol",
"test": "yarn truffle test", "test": "yarn truffle test",
"migrate": "yarn truffle migrate --network develop" "migrate": "yarn truffle migrate --network develop --reset"
}, },
"dependencies": { "dependencies": {
"@openzeppelin/contracts": "~3.2.0", "@openzeppelin/contracts": "~3.2.0",
@ -23,6 +23,6 @@
"eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.19.0", "eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.2.0",
"solhint": "~3.2.0" "solhint": "~3.3.2"
} }
} }

1380
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save