diff --git a/app/package.json b/app/package.json
index cb0aa4c..fa888c5 100644
--- a/app/package.json
+++ b/app/package.json
@@ -11,7 +11,6 @@
"drizzle": "1.4.0",
"history": "4.9.0",
"ipfs": "0.35.0",
- "level": "5.0.1",
"lodash.isequal": "4.5.0",
"orbit-db": "0.21.0-rc.1",
"orbit-db-keystore": "0.2.1",
diff --git a/app/src/containers/LoadingContainer.js b/app/src/containers/LoadingContainer.js
index c9cddbe..eb7bcf2 100644
--- a/app/src/containers/LoadingContainer.js
+++ b/app/src/containers/LoadingContainer.js
@@ -17,9 +17,8 @@ class LoadingContainer extends Component {
This browser has no connection to the Ethereum network.
Please make sure that:
- - You use MetaMask or a dedicated Ethereum browser
- - They are pointed to the correct network
- - Your account is unlocked and the app has the rights to access it
+ - MetaMask is unlocked and pointed to the correct network
+ - The app has been granted the right to connect to your account
@@ -34,6 +33,7 @@ class LoadingContainer extends Component {
We can't find any Ethereum accounts!
+
Please make sure that MetaMask is unlocked.
@@ -76,6 +76,7 @@ class LoadingContainer extends Component {
Preparing OrbitDB...
+
Please sign the transaction in MetaMask to create the databases.
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 c7e8373..b69cb72 100644
--- a/app/src/redux/sagas/transactionsSaga.js
+++ b/app/src/redux/sagas/transactionsSaga.js
@@ -4,17 +4,14 @@ import { drizzle } from '../../index';
import { orbitSagaPut } from '../../utils/orbitUtils';
import { WEB3_UTILS_SAGA_INITIALIZED } from '../actions/web3UtilsActions';
import { CONTRACT_EVENT_FIRED } from './eventSaga';
-import { getCurrentAccount } from './web3UtilsSaga';
const transactionsHistory = Object.create(null);
function* initTransaction(action) {
- action.transactionDescriptor.params.from = getCurrentAccount();
const dataKey = drizzle.contracts[action.transactionDescriptor.contract]
.methods[action.transactionDescriptor.method].cacheSend(
...(action.transactionDescriptor.params)
);
-
transactionsHistory[dataKey] = action;
transactionsHistory[dataKey].state = 'initialized';
}
diff --git a/app/src/utils/levelUtils.js b/app/src/utils/levelUtils.js
deleted file mode 100644
index b36df30..0000000
--- a/app/src/utils/levelUtils.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import level from 'level'
-
-const db = level('./orbitdb/identity/identitykeys');
-
-async function getPrivateKey(id){
- try{
- const keyPair = await db.get(id);
- return JSON.parse(keyPair).privateKey;
- } catch (err) {
- if (err && err.notFound)
- console.error("LevelDB: Private Key not found!");
- throw err;
- }
-}
-
-async function setKeyPair(id, publicKey, privateKey){
- await db.put(id,JSON.stringify({publicKey: publicKey, privateKey: privateKey}));
-}
-
-export { getPrivateKey, setKeyPair }
diff --git a/app/src/utils/orbitUtils.js b/app/src/utils/orbitUtils.js
index 8b78736..91a2ef7 100644
--- a/app/src/utils/orbitUtils.js
+++ b/app/src/utils/orbitUtils.js
@@ -2,10 +2,9 @@ import OrbitDB from 'orbit-db';
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';
-import { getPrivateKey, setKeyPair } from './levelUtils';
function initIPFS() {
Identities.addIdentityProvider(EthereumIdentityProvider);
@@ -23,63 +22,17 @@ function initIPFS() {
});
}
-async function createTempDatabases() {
- console.debug('Creating temporary databases...');
- const ipfs = getIPFS();
- const identity = await Identities.createIdentity({type: 'ethereum'});
- const orbitdb = await OrbitDB.createInstance(ipfs, {identity});
- console.dir(orbitdb)
- 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 identity = await Identities.createIdentity({type: 'ethereum'});
- const orbitdb = await OrbitDB.createInstance(ipfs, {identity});
- const options = {
- // Give write access to ourselves
- accessController: {
- write: ['*']
- }
- };
- const topicsDB = await orbitdb.keyvalue('topics', options);
- const postsDB = await orbitdb.keyvalue('posts', options);
- const privateKey = await getPrivateKey(identity.id);
-
- return {
- identityId: identity.id,
- identityPublicKey: identity.publicKey,
- identityPrivateKey: privateKey,
- orbitdb: orbitdb,
- orbitPublicKey: "eeeee",
- orbitPrivateKey: "fffffff",
- 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) {
+async function loadDatabases() {
console.debug('Loading databases...');
- const ipfs = getIPFS();
- await setKeyPair(identityId, identityPublicKey, identityPrivateKey);
- const identity = await Identities.createIdentity({type: 'ethereum' });
- const orbitdb = await OrbitDB.createInstance(ipfs, {identity});
-
- console.dir(orbitdb)
+ const { orbitdb, topicsDB, postsDB } = await createDBs();
- const topicsDB = await orbitdb.keyvalue('topics')
- .catch((error) => console.error(`TopicsDB init error: ${error}`));
-
- console.dir(topicsDB)
-
- const postsDB = await orbitdb.keyvalue('posts')
- .catch((error) => console.error(`PostsDB init error: ${error}`));
- console.dir(topicsDB)
await topicsDB.load().catch((error) => console.error(`TopicsDB loading error: ${error}`));
await postsDB.load().catch((error) => console.error(`PostsDB loading error: ${error}`));
@@ -101,10 +54,22 @@ async function loadDatabases(identityId, identityPublicKey, identityPrivateKey,
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}`));
}
@@ -122,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