First commit

This commit is contained in:
Noman 2018-09-06 05:37:37 -04:00
commit 56d6ee6897
No known key found for this signature in database
GPG Key ID: ACD1C4A99857525C
10 changed files with 9087 additions and 0 deletions

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
node_modules/**

14
.eslintrc Normal file
View File

@ -0,0 +1,14 @@
{
"extends": "airbnb",
"globals": {
"artifacts": false,
"document": false,
"window": false
},
"rules": {
"arrow-body-style": [2, "as-needed"],
"arrow-parens": [2, "as-needed"],
"import/prefer-default-export": [2, "never"],
"space-before-function-paren": [2, "never"]
}
}

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
.idea
dist
.env

47
README.md Normal file
View File

@ -0,0 +1,47 @@
## matrix-whisper-bridge
A two-way bridge between the matrix protocol and the whisper protocol
### Development
1. Clone repository and enter project folder
```
git clone ...
cd matrix-whisper-bridge
```
2. Install dependencies
```
npm install
```
3. Install geth
4. Launch lightweight testnet with proper whisper settings for Status chat protocol
```
geth --testnet --light --shh.pow=0.002
```
5. You need to add Status peers so you can join the network.
First connect to the running geth instance
```
geth --testnet attach
```
Then add the peers from this list: https://github.com/status-im/status-go/blob/develop/geth/params/cluster.go
```
var peers = ['enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404','enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404','enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404','enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404','enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.108.68:30304','enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304','enode://436cc6f674928fdc9a9f7990f2944002b685d1c37f025c1be425185b5b1f0900feaf1ccc2a6130268f9901be4a7d252f37302c8335a2c1a62736e9232691cc3a@174.138.105.243:30404','enode://5395aab7833f1ecb671b59bf0521cf20224fe8162fc3d2675de4ee4d5636a75ec32d13268fc184df8d1ddfa803943906882da62a4df42d4fccf6d17808156a87@206.189.243.57:30404','enode://7427dfe38bd4cf7c58bb96417806fab25782ec3e6046a8053370022cbaa281536e8d64ecd1b02e1f8f72768e295d06258ba43d88304db068e6f2417ae8bcb9a6@104.154.88.123:30404','enode://ebefab39b69bbbe64d8cd86be765b3be356d8c4b24660f65d493143a0c44f38c85a257300178f7845592a1b0332811542e9a58281c835babdd7535babb64efc1@35.202.99.224:30404','enode://a6a2a9b3a7cbb0a15da74301537ebba549c990e3325ae78e1272a19a3ace150d03c184b8ac86cc33f1f2f63691e467d49308f02d613277754c4dccd6773b95e8@206.189.108.68:30304','enode://207e53d9bf66be7441e3daba36f53bfbda0b6099dba9a865afc6260a2d253fb8a56a72a48598a4f7ba271792c2e4a8e1a43aaef7f34857f520c8c820f63b44c8@35.224.15.65:30304'];
peers.forEach(function addPeer(peer) { admin.addPeer(peer); });
exit
```

22
app/MatrixRiotBridge.js Normal file
View File

@ -0,0 +1,22 @@
const WhisperUtils = require('../utils/whisperUtils');
class MatrixRiotBridge {
init() {
return Promise.all([
this.connectToWhisper(),
this.connectToMatrix(),
]);
}
connectToWhisper() {
this.whisperUtils = new WhisperUtils();
return this.whisperUtils.init();
}
connectToMatrix() {
this.matrixUtils = {};
return this.matrixUtils;
}
}
module.exports = MatrixRiotBridge;

37
index.js Normal file
View File

@ -0,0 +1,37 @@
//const transit = require('transit-js');
const MatrixRiotBridge = require('./app/MatrixRiotBridge');
const { decodeStatusPayload } = require('./utils/statusUtils');
const CHANNEL = 'noman-test';
const bridge = new MatrixRiotBridge();
bridge.init().then(([whisperUtils]) => {
whisperUtils.getPublicKey().then(publicKey => {
function isMe(sig) {
return publicKey === sig;
}
whisperUtils.send(CHANNEL, 'Bot is alive');
whisperUtils.listen(CHANNEL).then(
subscription => {
subscription.on('data', ({ payload, sig, timestamp, topic }) => {
const [, [message]] = decodeStatusPayload(payload);
const me = isMe(sig);
const name = me ? 'Bot' : sig.slice(0, 10);
console.log('From:', name, 'at', timestamp);
console.log('Topic:', topic);
console.log('Message:', message, '\n');
if (!me) {
whisperUtils.send(CHANNEL, `${message} back`);
}
});
},
);
},
error => console.error(error));
});

8819
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "matrix-whisper-bridge",
"private": true,
"version": "0.1.0",
"description": "A two-way bridge between the matrix protocol and the whisper protocol",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Noman <noman@noman.land>",
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.7.0",
"eslint": "^5.2.0",
"eslint-config-airbnb": "^17.0.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.10.0"
},
"dependencies": {
"npm": "^6.4.1",
"prompt": "^1.0.0",
"transit-js": "^0.8.861",
"web3": "^1.0.0-beta.35",
"ws": "^6.0.0"
}
}

43
utils/statusUtils.js Normal file
View File

@ -0,0 +1,43 @@
const Web3 = require('web3');
const web3 = new Web3();
const { utils: { asciiToHex, hexToAscii, sha3 } } = web3;
function createStatusPayload(
{
tag = '~#c4',
content = 'This is a whisper/slack test!',
messageType = '~:public-group-user-message',
clockValue = (new Date().getTime()) * 100,
contentType = 'text/plain',
timestamp = new Date().getTime(),
} = {},
) {
return asciiToHex(
JSON.stringify([
tag,
[
content,
contentType,
messageType,
clockValue,
timestamp,
],
]),
);
}
function decodeStatusPayload(payload) {
return JSON.parse(hexToAscii(payload));
}
function topicFromChannelName(channelName) {
return sha3(channelName).slice(0, 10);
}
module.exports = {
createStatusPayload,
decodeStatusPayload,
topicFromChannelName,
};

71
utils/whisperUtils.js Normal file
View File

@ -0,0 +1,71 @@
const net = require('net');
const Web3 = require('web3');
const {
createStatusPayload,
topicFromChannelName,
} = require('./statusUtils');
const CHANNEL = 'noman-test';
class WhisperUtils {
init() {
this.web3 = new Web3(new Web3.providers.IpcProvider(
process.env.GETH_IPC_PATH,
net,
));
this.shh = this.web3.shh;
return new Promise((resolve, reject) => {
this.createWhisperIdentity().then(
sig => {
this.sig = sig;
resolve(this);
},
error => reject(error),
);
});
}
createWhisperIdentity() {
return this.shh.newKeyPair();
}
getPublicKey() {
return this.shh.getPublicKey(this.sig);
}
listen(channel) {
const topic = topicFromChannelName(channel);
return new Promise(resolve => {
this.shh.generateSymKeyFromPassword(channel)
.then(symKeyID => {
const subscription = this.shh.subscribe('messages', {
minPow: 0.002,
symKeyID,
topics: [topic],
});
resolve(subscription);
});
});
}
send(channel, message) {
const payload = createStatusPayload({ content: message });
return this.shh.generateSymKeyFromPassword(channel)
.then(symKeyID => this.shh.post({
payload,
powTarget: 0.002,
powTime: 1,
sig: this.sig,
symKeyID,
topic: topicFromChannelName(channel),
ttl: 10,
}));
}
}
module.exports = WhisperUtils;