diff --git a/app/src/containers/PostList.js b/app/src/containers/PostList.js
index 6de283f..c2de481 100644
--- a/app/src/containers/PostList.js
+++ b/app/src/containers/PostList.js
@@ -5,6 +5,7 @@ import { drizzle } from '../index';
import Post from './Post';
import PlaceholderContainer from './PlaceholderContainer';
+import { determineDBAddress } from '../utils/orbitUtils';
const contract = 'Forum';
const getPostMethod = 'getPost';
@@ -16,7 +17,8 @@ class PostList extends Component {
this.getBlockchainData = this.getBlockchainData.bind(this);
this.state = {
- dataKeys: []
+ dataKeys: [],
+ dbAddresses: []
};
}
@@ -29,20 +31,31 @@ class PostList extends Component {
}
getBlockchainData() {
- const { dataKeys } = this.state;
- const { drizzleStatus, postIDs } = this.props;
+ const { dataKeys, dbAddresses } = this.state;
+ const { drizzleStatus, postIDs, contracts } = this.props;
if (drizzleStatus.initialized) {
const dataKeysShallowCopy = dataKeys.slice();
let fetchingNewData = false;
- postIDs.forEach((postID) => {
+ postIDs.forEach(async (postID) => {
if (!dataKeys[postID]) {
dataKeysShallowCopy[postID] = drizzle.contracts[contract].methods[getPostMethod].cacheCall(
- postID,
+ postID
);
fetchingNewData = true;
}
+ else if (!dbAddresses[postID]){
+ const fetchedPostData = contracts[contract][getPostMethod][dataKeys[postID]];
+ if(fetchedPostData) {
+ const dbAddress = await determineDBAddress('posts', fetchedPostData.value[0]);
+ const dbAddressesShallowCopy = dbAddresses.slice();
+ dbAddressesShallowCopy[postID] = dbAddress;
+ this.setState({
+ dbAddresses: dbAddressesShallowCopy
+ });
+ }
+ }
});
if (fetchingNewData) {
@@ -54,7 +67,7 @@ class PostList extends Component {
}
render() {
- const { dataKeys } = this.state;
+ const { dataKeys, dbAddresses } = this.state;
const { postIDs, contracts, focusOnPost, recentToTheTop } = this.props;
const posts = postIDs.map((postID, index) => {
@@ -62,13 +75,16 @@ class PostList extends Component {
if(dataKeys[postID])
fetchedPostData = contracts[contract][getPostMethod][dataKeys[postID]];
- if(fetchedPostData) {
+ const dbAddress = dbAddresses[postID];
+ if(fetchedPostData && dbAddress) {
+ const userAddress = fetchedPostData.value[0]; //Also works as an Orbit Identity ID
+
const postData = {
- userAddress: fetchedPostData.value[1],
- fullOrbitAddress: `/orbitdb/${fetchedPostData.value[0]}/posts`,
- userName: fetchedPostData.value[2],
- timestamp: fetchedPostData.value[3]*1000,
- topicID: fetchedPostData.value[4]
+ userAddress,
+ fullOrbitAddress: `/orbitdb/${dbAddress}/posts`,
+ userName: fetchedPostData.value[1],
+ timestamp: fetchedPostData.value[2]*1000,
+ topicID: fetchedPostData.value[3]
};
return(
Account address:
{address}
-
- OrbitDB:
- {orbitDBId ? orbitDBId
- :
-
-
- }
-
TopicsDB:
{topicsDBId ? topicsDBId
diff --git a/app/src/containers/TopicContainer.js b/app/src/containers/TopicContainer.js
index 52fc3bf..5c672d0 100644
--- a/app/src/containers/TopicContainer.js
+++ b/app/src/containers/TopicContainer.js
@@ -10,6 +10,7 @@ import NewPost from './NewPost';
import FloatingButton from '../components/FloatingButton';
import { setNavBarTitle } from '../redux/actions/userInterfaceActions.js';
+import { determineDBAddress } from '../utils/orbitUtils';
const contract = 'Forum';
const getTopicMethod = 'getTopic';
@@ -95,16 +96,16 @@ class TopicContainer extends Component {
}
}
- async fetchTopicSubject(orbitDBAddress) {
+ async fetchTopicSubject(userAddress) {
const { topicID } = this.state;
- const { contracts, user, orbitDB, setNavBarTitle } = this.props;
+ const { user, orbitDB, setNavBarTitle } = this.props;
let orbitData;
- if (contracts[contract][getTopicMethod][this.dataKey].value[1]
- === user.address) {
+ if (userAddress === user.address) {
orbitData = orbitDB.topicsDB.get(topicID);
} else {
- const fullAddress = `/orbitdb/${orbitDBAddress}/topics`;
+ const dbAddress = await determineDBAddress('topics', userAddress);
+ const fullAddress = `/orbitdb/${dbAddress}/topics`;
const store = await orbitDB.orbitdb.keyvalue(fullAddress);
await store.load();
@@ -152,7 +153,7 @@ class TopicContainer extends Component {
(
{ this.togglePostingState(); }}
onPostCreated={() => { this.postCreated(); }}
/>
diff --git a/app/src/containers/TopicList.js b/app/src/containers/TopicList.js
index dbab22a..0db0cd8 100644
--- a/app/src/containers/TopicList.js
+++ b/app/src/containers/TopicList.js
@@ -5,6 +5,7 @@ import { drizzle } from '../index';
import Topic from './Topic';
import PlaceholderContainer from './PlaceholderContainer';
+import { determineDBAddress } from '../utils/orbitUtils';
const contract = 'Forum';
const getTopicMethod = 'getTopic';
@@ -16,7 +17,8 @@ class TopicList extends Component {
this.getBlockchainData = this.getBlockchainData.bind(this);
this.state = {
- dataKeys: []
+ dataKeys: [],
+ dbAddresses: []
};
}
@@ -29,19 +31,30 @@ class TopicList extends Component {
}
getBlockchainData() {
- const { dataKeys } = this.state;
- const { drizzleStatus, topicIDs } = this.props;
+ const { dataKeys, dbAddresses } = this.state;
+ const { drizzleStatus, topicIDs, contracts } = this.props;
if (drizzleStatus.initialized) {
const dataKeysShallowCopy = dataKeys.slice();
let fetchingNewData = false;
- topicIDs.forEach((topicID) => {
+ topicIDs.forEach(async (topicID) => {
if (!dataKeys[topicID]) {
dataKeysShallowCopy[topicID] = drizzle.contracts[contract].methods[getTopicMethod]
.cacheCall(topicID);
fetchingNewData = true;
}
+ else if (!dbAddresses[topicID]){
+ const fetchedTopicData = contracts[contract][getTopicMethod][dataKeys[topicID]];
+ if(fetchedTopicData) {
+ const dbAddress = await determineDBAddress('topics', fetchedTopicData.value[0]);
+ const dbAddressesShallowCopy = dbAddresses.slice();
+ dbAddressesShallowCopy[topicID] = dbAddress;
+ this.setState({
+ dbAddresses: dbAddressesShallowCopy
+ });
+ }
+ }
});
if (fetchingNewData) {
@@ -53,7 +66,7 @@ class TopicList extends Component {
}
render() {
- const { dataKeys } = this.state;
+ const { dataKeys, dbAddresses } = this.state;
const { topicIDs, contracts } = this.props;
const topics = topicIDs.map(topicID => {
@@ -61,13 +74,15 @@ class TopicList extends Component {
if(dataKeys[topicID])
fetchedTopicData = contracts[contract][getTopicMethod][dataKeys[topicID]];
- if(fetchedTopicData) {
+ const dbAddress = dbAddresses[topicID];
+ if(fetchedTopicData && dbAddress) {
+ const userAddress = fetchedTopicData.value[0]; //Also works as an Orbit Identity ID
const topicData = {
- userAddress: fetchedTopicData.value[1],
- fullOrbitAddress: `/orbitdb/${fetchedTopicData.value[0]}/topics`,
- userName: fetchedTopicData.value[2],
- timestamp: fetchedTopicData.value[3]*1000,
- numberOfReplies: fetchedTopicData.value[4].length
+ userAddress,
+ fullOrbitAddress: `/orbitdb/${dbAddress}/topics`,
+ userName: fetchedTopicData.value[1],
+ timestamp: fetchedTopicData.value[2]*1000,
+ numberOfReplies: fetchedTopicData.value[3].length
};
return(
state.orbit);
if(!orbit.ready){
- const { orbitdb, topicsDB, postsDB } = yield call(createTempDatabases);
+ const { orbitdb, topicsDB, postsDB } = yield call(createDatabases);
yield put(updateDatabases(DATABASES_CREATED, orbitdb, topicsDB, postsDB ));
- console.debug("Created temporary databases.");
}
}
latestAccount = account;
diff --git a/app/src/redux/sagas/transactionsSaga.js b/app/src/redux/sagas/transactionsSaga.js
index dca9b5c..b69cb72 100644
--- a/app/src/redux/sagas/transactionsSaga.js
+++ b/app/src/redux/sagas/transactionsSaga.js
@@ -10,9 +10,8 @@ const transactionsHistory = Object.create(null);
function* initTransaction(action) {
const dataKey = drizzle.contracts[action.transactionDescriptor.contract]
.methods[action.transactionDescriptor.method].cacheSend(
- ...(action.transactionDescriptor.params),
+ ...(action.transactionDescriptor.params)
);
-
transactionsHistory[dataKey] = action;
transactionsHistory[dataKey].state = 'initialized';
}
diff --git a/app/src/utils/EthereumIdentityProvider.js b/app/src/utils/EthereumIdentityProvider.js
new file mode 100644
index 0000000..734628f
--- /dev/null
+++ b/app/src/utils/EthereumIdentityProvider.js
@@ -0,0 +1,29 @@
+import { web3 } from '../redux/sagas/web3UtilsSaga';
+
+class EthereumIdentityProvider {
+ constructor () {
+ this.web3 = web3;
+ }
+
+ // Returns the type of the identity provider
+ static get type () { return 'ethereum' }
+
+ // Returns the signer's id
+ async getId () {
+ return (await this.web3.eth.getAccounts())[0];
+ }
+
+ // Returns a signature of pubkeysignature
+ async signIdentity (data) {
+ const address = await this.getId();
+ return await this.web3.eth.personal.sign(data,address,""); //Password not required for MetaMask
+ }
+
+ static async verifyIdentity (identity) {
+ // Verify that identity was signed by the ID
+ return web3.eth.accounts.recover(identity.publicKey + identity.signatures.id,
+ identity.signatures.publicKey) === identity.id;
+ }
+}
+
+export default EthereumIdentityProvider;
diff --git a/app/src/utils/orbitUtils.js b/app/src/utils/orbitUtils.js
index 705d46f..91a2ef7 100644
--- a/app/src/utils/orbitUtils.js
+++ b/app/src/utils/orbitUtils.js
@@ -1,12 +1,13 @@
import OrbitDB from 'orbit-db';
-import Keystore from 'orbit-db-keystore';
-import path from 'path';
+import Identities from 'orbit-db-identity-provider';
import IPFS from 'ipfs';
import store from '../redux/store';
-import { DATABASES_LOADED, IPFS_INITIALIZED, updateDatabases } from '../redux/actions/orbitActions';
+import { DATABASES_LOADED, IPFS_INITIALIZED, updateDatabases } from '../redux/actions/orbitActions';
import ipfsOptions from '../config/ipfsOptions';
+import EthereumIdentityProvider from './EthereumIdentityProvider';
function initIPFS() {
+ Identities.addIdentityProvider(EthereumIdentityProvider);
const ipfs = new IPFS(ipfsOptions);
ipfs.on('error', (error) => console.error(`IPFS error: ${error}`));
ipfs.on('ready', async () => {
@@ -21,63 +22,23 @@ function initIPFS() {
});
}
-async function createTempDatabases() {
- console.debug('Creating temporary databases...');
- const ipfs = getIPFS();
- const orbitdb = new OrbitDB(ipfs);
- const topicsDB = await orbitdb.keyvalue('topics');
- const postsDB = await orbitdb.keyvalue('posts');
- return { orbitdb, topicsDB, postsDB };
-}
-
async function createDatabases() {
console.debug('Creating databases...');
- const ipfs = getIPFS();
- const orbitdb = new OrbitDB(ipfs);
- const topicsDB = await orbitdb.keyvalue('topics');
- const postsDB = await orbitdb.keyvalue('posts');
-
- const orbitKey = orbitdb.keystore.getKey(orbitdb.id);
-
- return {
- identityId: 'Tempus',
- identityPublicKey: 'edax',
- identityPrivateKey: 'rerum',
- orbitdb: orbitdb,
- orbitPublicKey: orbitKey.getPublic('hex'),
- orbitPrivateKey: orbitKey.getPrivate('hex'),
- topicsDB: topicsDB.address.root,
- postsDB: postsDB.address.root
- };
+ const databases = await createDBs();
+ console.debug('Databases created successfully.');
+ return databases;
}
-async function loadDatabases(identityId, identityPublicKey, identityPrivateKey,
- orbitId, orbitPublicKey, orbitPrivateKey,
- topicsDBId, postsDBId) {
- const directory = './orbitdb';
- const keystore = Keystore.create(path.join(directory, orbitId, '/keystore'));
-
- keystore._storage.setItem(orbitId, JSON.stringify({
- publicKey: orbitPublicKey,
- privateKey: orbitPrivateKey
- }));
-
- const ipfs = getIPFS();
- const orbitdb = new OrbitDB(ipfs, directory,
- {
- peerId: orbitId, keystore
- });
- const topicsDB = await orbitdb.keyvalue(`/orbitdb/${topicsDBId}/topics`)
- .catch((error) => console.error(`TopicsDB init error: ${error}`));
- const postsDB = await orbitdb.keyvalue(`/orbitdb/${postsDBId}/posts`)
- .catch((error) => console.error(`PostsDB init error: ${error}`));
+async function loadDatabases() {
+ console.debug('Loading databases...');
+ const { orbitdb, topicsDB, postsDB } = await createDBs();
await topicsDB.load().catch((error) => console.error(`TopicsDB loading error: ${error}`));
await postsDB.load().catch((error) => console.error(`PostsDB loading error: ${error}`));
//It is possible that we lack our own data and need to replicate them from somewhere else
topicsDB.events.on('replicate', (address) => {
- console.log(`TopicsDB Replicating (${address}).`);
+ console.log(`TopicsDB replicating (${address}).`);
});
topicsDB.events.on('replicated', (address) => {
console.log(`TopicsDB replicated (${address}).`);
@@ -89,14 +50,26 @@ async function loadDatabases(identityId, identityPublicKey, identityPrivateKey,
console.log(`PostsDB replicated (${address}).`);
});
- console.debug('Orbit databases loaded successfully.');
+ console.debug('Databases loaded successfully.');
store.dispatch(updateDatabases(DATABASES_LOADED, orbitdb, topicsDB, postsDB));
}
+async function determineDBAddress(name, identityId){
+ return (await getOrbitDB().determineAddress(name, 'keyvalue', {
+ accessController: {
+ write: [identityId]}
+ }
+ )).root;
+}
+
function getIPFS() {
return store.getState().orbit.ipfs;
}
+function getOrbitDB() {
+ return store.getState().orbit.orbitdb;
+}
+
async function orbitSagaPut(db, key, value) {
await db.put(key, value).catch((error) => console.error(`Orbit put error: ${error}`));
}
@@ -114,4 +87,24 @@ async function orbitSagaOpen(orbitdb, address) {
return store;
}
-export { initIPFS, createTempDatabases, createDatabases, loadDatabases, orbitSagaPut, orbitSagaOpen };
+async function createDBs(){
+ const ipfs = getIPFS();
+ const identity = await Identities.createIdentity({type: 'ethereum'});
+ const orbitdb = await OrbitDB.createInstance(ipfs, {identity});
+ const topicsDB = await orbitdb.keyvalue('topics')
+ .catch((error) => console.error(`TopicsDB init error: ${error}`));
+ const postsDB = await orbitdb.keyvalue('posts')
+ .catch((error) => console.error(`PostsDB init error: ${error}`));
+
+ return { orbitdb, topicsDB, postsDB };
+}
+
+
+export {
+ initIPFS,
+ createDatabases,
+ loadDatabases,
+ orbitSagaPut,
+ orbitSagaOpen,
+ determineDBAddress
+};
diff --git a/contracts/Forum.sol b/contracts/Forum.sol
index e6dcba2..a49cfb0 100644
--- a/contracts/Forum.sol
+++ b/contracts/Forum.sol
@@ -5,7 +5,6 @@ contract Forum {
//----------------------------------------USER----------------------------------------
struct User {
string username; // TODO: set an upper bound instead of arbitrary string
- OrbitDB orbitdb;
uint[] topicIDs; // IDs of the topics the user created
uint[] postIDs; // IDs of the posts the user created
uint timestamp;
@@ -18,15 +17,10 @@ contract Forum {
event UserSignedUp(string username, address userAddress);
event UsernameUpdated(string newName, string oldName,address userAddress);
- function signUp(string memory username, string memory orbitIdentityId,
- string memory orbitIdentityPublicKey, string memory orbitIdentityPrivateKey,
- string memory orbitId, string memory orbitPublicKey, string memory orbitPrivateKey,
- string memory orbitTopicsDB, string memory orbitPostsDB) public returns (bool) {
+ function signUp(string memory username) public returns (bool) {
require (!hasUserSignedUp(msg.sender), "User has already signed up.");
require(!isUserNameTaken(username), "Username is already taken.");
users[msg.sender] = User(username,
- OrbitDB(orbitIdentityId, orbitIdentityPublicKey, orbitIdentityPrivateKey,
- orbitId, orbitPublicKey, orbitPrivateKey, orbitTopicsDB, orbitPostsDB),
new uint[](0), new uint[](0), block.timestamp, true);
userAddresses[username] = msg.sender;
emit UserSignedUp(username, msg.sender);
@@ -78,82 +72,6 @@ contract Forum {
return users[userAddress].timestamp;
}
- //----------------------------------------OrbitDB----------------------------------------
- // TODO: set upper bounds to strings (instead of being of arbitrary length)
- // TODO: not sure if topicsDB//postsDB are actually needed
- struct OrbitDB {
- string identityId;
- string identityPublicKey;
- string identityPrivateKey;
- string orbitId;
- string orbitPublicKey;
- string orbitPrivateKey;
- string topicsDB;
- string postsDB;
- }
-
- function getOrbitIdentityId(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.identityId;
- }
-
- function getOrbitIdentityPublicKey(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.identityPublicKey;
- }
-
- function getOrbitIdentityPrivateKey(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.identityPrivateKey;
- }
-
-
- function getOrbitDBId(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.orbitId;
- }
-
- function getOrbitPublicKey(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.orbitPublicKey;
- }
-
- //TODO: encrypt using Metamask in the future
- function getOrbitPrivateKey(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.orbitPrivateKey;
- }
-
- function getOrbitTopicsDB(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.topicsDB;
- }
-
- function getOrbitPostsDB(address userAddress) public view returns (string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return users[userAddress].orbitdb.postsDB;
- }
-
- function getOrbitIdentityInfo(address userAddress) public view returns (string memory, string memory, string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return (
- users[userAddress].orbitdb.identityId,
- users[userAddress].orbitdb.identityPublicKey,
- users[userAddress].orbitdb.identityPrivateKey
- );
- }
-
- function getOrbitDBInfo(address userAddress) public view returns (string memory, string memory,
- string memory, string memory, string memory) {
- require (hasUserSignedUp(userAddress), "User hasn't signed up.");
- return (
- users[userAddress].orbitdb.orbitId,
- users[userAddress].orbitdb.orbitPublicKey,
- users[userAddress].orbitdb.orbitPrivateKey,
- users[userAddress].orbitdb.topicsDB,
- users[userAddress].orbitdb.postsDB
- );
- }
//----------------------------------------POSTING----------------------------------------
struct Topic {
@@ -211,10 +129,10 @@ contract Forum {
return numTopics;
}
- function getTopic(uint topicID) public view returns (string memory, address, string memory, uint, uint[] memory) {
+ function getTopic(uint topicID) public view returns (address, string memory, uint, uint[] memory) {
//require(hasUserSignedUp(msg.sender)); needed?
require(topicID