whisper: JavaScript tests to cover updated Whisper API
This commit is contained in:
parent
92afd0d47e
commit
8876fd04d3
|
@ -14,6 +14,7 @@
|
||||||
*/**/*tx_database*
|
*/**/*tx_database*
|
||||||
*/**/*dapps*
|
*/**/*dapps*
|
||||||
vendor/github.com/ethereum/go-ethereum/vendor
|
vendor/github.com/ethereum/go-ethereum/vendor
|
||||||
|
/node_modules/
|
||||||
|
|
||||||
#*
|
#*
|
||||||
.#*
|
.#*
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -53,7 +53,9 @@ ci:
|
||||||
build/env.sh go test -v -cover ./extkeys
|
build/env.sh go test -v -cover ./extkeys
|
||||||
|
|
||||||
generate:
|
generate:
|
||||||
|
cp ./node_modules/web3/dist/web3.js ./static/scripts/web3.js
|
||||||
build/env.sh go generate ./static
|
build/env.sh go generate ./static
|
||||||
|
rm ./static/scripts/web3.js
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@build/env.sh echo "mode: set" > coverage-all.out
|
@build/env.sh echo "mode: set" > coverage-all.out
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "status-go",
|
||||||
|
"version": "0.9.6",
|
||||||
|
"description": "JavaScript tests for RPC API (Whisper/5, Swarm)",
|
||||||
|
"main": "index.js",
|
||||||
|
"dependencies": {
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"chai": "^3.5.0",
|
||||||
|
"mocha": "^3.3.0",
|
||||||
|
"requirejs": "^2.3.3",
|
||||||
|
"web3": "github:farazdagi/web3.js#geth/1.6.1-unstable"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha --bail --slow 1000 --full-trace static/tests"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/farazdagi/status-go.git"
|
||||||
|
},
|
||||||
|
"author": "Victor Farazdagi",
|
||||||
|
"license": "ISC",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/farazdagi/status-go/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/farazdagi/status-go#readme"
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -1,87 +0,0 @@
|
||||||
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>web3.js sample</title>
|
|
||||||
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/jquery-3.1.1.js"
|
|
||||||
integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script type="text/javascript" src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
|
||||||
<script type="text/javascript" src="./bignumber.js"></script>
|
|
||||||
<script type="text/javascript" src="./web3.js"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
var web3 = new Web3();
|
|
||||||
var shh = web3.shh;
|
|
||||||
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8645'));
|
|
||||||
|
|
||||||
var identity = '0x04eedbaafd6adf4a9233a13e7b1c3c14461fffeba2e9054b8d456ce5f6ebeafadcbf3dce3716253fbc391277fa5a086b60b283daf61fb5b1f26895f456c2f31ae3';
|
|
||||||
if (!web3.shh.hasIdentity(identity)) {
|
|
||||||
throw 'idenitity "0x04eedbaafd6adf4a9233a13e7b1c3c14461fffeba2e9054b8d456ce5f6ebeafadcbf3dce3716253fbc391277fa5a086b60b283daf61fb5b1f26895f456c2f31ae3" not found in whisper';
|
|
||||||
}
|
|
||||||
|
|
||||||
var topic = 'example3';
|
|
||||||
var payload = 'test message 3 (K1 -> "", signed broadcast)';
|
|
||||||
|
|
||||||
// generate symmetric key (if doesn't already exist)
|
|
||||||
if (!shh.hasSymKey(topic)) {
|
|
||||||
shh.addSymKey(topic, "0xdeadbeef"); // alternatively: shh.generateSymKey("example3");
|
|
||||||
// to delete key, rely on: shh.deleteSymKey(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start watching for messages
|
|
||||||
var filter = shh.filter({
|
|
||||||
from: identity,
|
|
||||||
topics: [web3.fromAscii(topic)],
|
|
||||||
keyname: topic // you can use some other name for key too
|
|
||||||
});
|
|
||||||
filter.watch(function(error, result){
|
|
||||||
if (!error) {
|
|
||||||
console.log("Message received1: ", result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
var message = {
|
|
||||||
from: identity,
|
|
||||||
topics: [web3.fromAscii(topic)],
|
|
||||||
payload: payload,
|
|
||||||
ttl: 20,
|
|
||||||
keyname: topic
|
|
||||||
};
|
|
||||||
var err = shh.post(message)
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
}, 3000)
|
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<!-- Static navbar -->
|
|
||||||
<nav class="navbar navbar-default navbar-static-top">
|
|
||||||
<div class="container">
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="jumbotron">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,392 +0,0 @@
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Whisper Notification Server Test</title>
|
|
||||||
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/jquery-3.1.1.js"
|
|
||||||
integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script type="text/javascript" src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
|
||||||
<script type="text/javascript" src="./../scripts/bignumber.js"></script>
|
|
||||||
<script type="text/javascript" src="./../scripts/web3.js"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
var protocolKey = '0x040edb0d71a3dbe928e154fcb696ffbda359b153a90efc2b46f0043ce9f5dbe55b77b9328fd841a1db5273758624afadd5b39638d4c35b36b3a96e1a586c1b4c2a';
|
|
||||||
var discoverServerTopic = '0x268302f3'; // DISCOVER_NOTIFICATION_SERVER
|
|
||||||
var proposeServerTopic = '0x08e3d8c0'; // PROPOSE_NOTIFICATION_SERVER
|
|
||||||
var acceptServerTopic = '0x04f7dea6'; // ACCEPT_NOTIFICATION_SERVER
|
|
||||||
var ackClientSubscriptionTopic = '0x93dafe28'; // ACK_NOTIFICATION_SERVER_SUBSCRIPTION
|
|
||||||
var sendNotificationTopic = '0x69915296'; // SEND_NOTIFICATION
|
|
||||||
var newChatSessionTopic = '0x509579a2'; // NEW_CHAT_SESSION
|
|
||||||
var ackNewChatSessionTopic = '0xd012aae8'; // ACK_NEW_CHAT_SESSION
|
|
||||||
var newDeviceRegistrationTopic = '0x14621a51'; // NEW_DEVICE_REGISTRATION
|
|
||||||
var ackDeviceRegistrationTopic = '0x424358d6'; // ACK_DEVICE_REGISTRATION
|
|
||||||
var checkClientSessionTopic = '0x8745d931'; // CHECK_CLIENT_SESSION
|
|
||||||
var confirmClientSessionTopic = '0xd3202c5f'; // CONFIRM_CLIENT_SESSION
|
|
||||||
var dropClientSessionTopic = '0x3a6656bb'; // DROP_CLIENT_SESSION
|
|
||||||
|
|
||||||
// this will be used both by Device A and Device B
|
|
||||||
var registerDevice = function (web3, identity, chatId, chatKey, deviceId) {
|
|
||||||
console.log('chat session key: ', chatKey);
|
|
||||||
|
|
||||||
// make sure that chat key is loaded
|
|
||||||
var keyname = chatId + 'chatkey'; // there might be many chat keys
|
|
||||||
web3.shh.deleteSymKey(keyname);
|
|
||||||
web3.shh.addSymKey(keyname, chatKey);
|
|
||||||
|
|
||||||
// before sending request, let's start waiting for response
|
|
||||||
var filter = web3.shh.filter({
|
|
||||||
to: identity,
|
|
||||||
topics: [ackDeviceRegistrationTopic]
|
|
||||||
});
|
|
||||||
filter.watch(function (error, result) {
|
|
||||||
if (!error) {
|
|
||||||
// response will be in JSON, e.g. {"server": "0xdeadbeef"}
|
|
||||||
var payload = JSON.parse(web3.toAscii(result.payload));
|
|
||||||
console.log("Device Registration ACK received: ", result, payload);
|
|
||||||
|
|
||||||
// no need to watch for the filter any more
|
|
||||||
filter.stopWatching();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity,
|
|
||||||
topics: [newDeviceRegistrationTopic],
|
|
||||||
payload: '{"device": "' + deviceId + '"}',
|
|
||||||
ttl: 20,
|
|
||||||
keyname: keyname
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var startDeviceA = function () {
|
|
||||||
var web3 = new Web3();
|
|
||||||
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8645'));
|
|
||||||
|
|
||||||
var sendDiscoveryRequest = function (identity) {
|
|
||||||
// notification server discovery request is a signed (sent from us),
|
|
||||||
// encrypted with Notification Protocol Asymmetric (Public) Key
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity,
|
|
||||||
to: protocolKey,
|
|
||||||
topics: [discoverServerTopic],
|
|
||||||
ttl: 20
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var sendAcceptServerRequest = function (identity, serverId) {
|
|
||||||
// whenever we are ready to accept server, having a given serverId, we need
|
|
||||||
// to notify it by sending signed (from us) and encrypted (using protocol key)
|
|
||||||
// acceptance message.
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity, // it is absolutely important to identify the client, or your acceptance will be dropped
|
|
||||||
to: protocolKey,
|
|
||||||
topics: [acceptServerTopic],
|
|
||||||
payload: '{"server": "' + serverId + '"}',
|
|
||||||
ttl: 20
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var watchProposeServerResponses = function (identity) {
|
|
||||||
// some notification servers will be able to serve, so they will send encrypted (to you)
|
|
||||||
// message, with a PROPOSE_NOTIFICATION_SERVER topic (for which we wait)
|
|
||||||
var filter = web3.shh.filter({
|
|
||||||
to: identity, // wait for anon. messages to ourselves
|
|
||||||
topics: [proposeServerTopic]
|
|
||||||
});
|
|
||||||
filter.watch(function (error, result) {
|
|
||||||
if (!error) {
|
|
||||||
console.log("Server proposal received: ", result);
|
|
||||||
// response will be in JSON, e.g. {"server": "0x81f34abd0df038e01a8f9c04bee7ce92925b7240e334dc8f2400dea7a2a6d829678be8b40e1d9b9988e25960552eafe2df7f928188e4143ba657a699519c438d"}
|
|
||||||
// which will give you serverId
|
|
||||||
var payload = JSON.parse(web3.toAscii(result.payload));
|
|
||||||
console.log(payload);
|
|
||||||
|
|
||||||
// no need to watch for the filter any more
|
|
||||||
filter.stopWatching();
|
|
||||||
|
|
||||||
// accept (in FIFO order) the server
|
|
||||||
// we need to make sure that only a single server is selected,
|
|
||||||
// as you will receive multiple proposals for different servers,
|
|
||||||
// and may accept more that one of those proposals (which will
|
|
||||||
// result in duplicate notifications)
|
|
||||||
sendAcceptServerRequest(identity, payload.server);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var shareChatKey = function (chatId, chatKey) {
|
|
||||||
console.log('chat session key: ', chatKey)
|
|
||||||
// pre-defined test identity (it gets injected automatically by statusd)
|
|
||||||
var deviceBIdentity = '0x04eedbaafd6adf4a9233a13e7b1c3c14461fffeba2e9054b8d456ce5f6ebeafadcbf3dce3716253fbc391277fa5a086b60b283daf61fb5b1f26895f456c2f31ae3';
|
|
||||||
|
|
||||||
// it is up to you how you share secret among participants, here is sample
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity,
|
|
||||||
to: deviceBIdentity,
|
|
||||||
topics: ["chatKeySharing"],
|
|
||||||
payload: '{"chat": "' + chatId + '", "key": "' + chatKey + '"}',
|
|
||||||
ttl: 20
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var removeClientSession = function () {
|
|
||||||
var checkClientSession = function (callback) {
|
|
||||||
// before sending request, let's start waiting for response
|
|
||||||
var filter = web3.shh.filter({
|
|
||||||
to: identity,
|
|
||||||
topics: [confirmClientSessionTopic]
|
|
||||||
});
|
|
||||||
filter.watch(function (error, result) {
|
|
||||||
if (!error) {
|
|
||||||
// response will be in JSON, e.g. {"server": "0xdeadbeef", "key": "0xdeadbeef"}
|
|
||||||
var payload = JSON.parse(web3.toAscii(result.payload));
|
|
||||||
console.log("Client session confirmation received: ", result, payload);
|
|
||||||
|
|
||||||
// no need to watch for the filter any more
|
|
||||||
filter.stopWatching();
|
|
||||||
|
|
||||||
callback(payload)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// send enquiry to all servers, to learn whether we have a client session with any of them
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity,
|
|
||||||
to: protocolKey,
|
|
||||||
topics: [checkClientSessionTopic],
|
|
||||||
ttl: 20
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkClientSession(function (payload) {
|
|
||||||
console.log("time to cleanup: ", payload.server);
|
|
||||||
console.log("session key: ", payload.key);
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
// notify server that you want to unsubscribe
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity,
|
|
||||||
to: protocolKey,
|
|
||||||
topics: [dropClientSessionTopic],
|
|
||||||
ttl: 20
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
}, 5000); // let's all other concurrent tasks to wrap up
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var createChatSession = function (subscriptionKey) {
|
|
||||||
var chatId = '0xdeadbeef';
|
|
||||||
|
|
||||||
// subscriptionKey is key shared by server that allows us to communicate with server privately
|
|
||||||
var keyname = 'SUBSCRIPTION_KEY'; // you might want to be tad more creative
|
|
||||||
web3.shh.deleteSymKey(keyname);
|
|
||||||
web3.shh.addSymKey(keyname, subscriptionKey);
|
|
||||||
|
|
||||||
console.log("subscription key: ", subscriptionKey);
|
|
||||||
|
|
||||||
// before sending new chat request, let's start waiting for response
|
|
||||||
var filter = web3.shh.filter({
|
|
||||||
to: identity,
|
|
||||||
topics: [ackNewChatSessionTopic]
|
|
||||||
});
|
|
||||||
filter.watch(function (error, result) {
|
|
||||||
if (!error) {
|
|
||||||
console.log("Chat Creation ACK received: ", result);
|
|
||||||
// response will be in JSON, e.g. {"server": "0xdeadbeef", "key": "0xdeadbeef"}
|
|
||||||
// which will give you serverId
|
|
||||||
var payload = JSON.parse(web3.toAscii(result.payload));
|
|
||||||
|
|
||||||
// no need to watch for the filter any more
|
|
||||||
filter.stopWatching();
|
|
||||||
|
|
||||||
// ok, at this point we have Chat Session SymKey, and we can:
|
|
||||||
// 1. register our device with that chat
|
|
||||||
// 2. share that key with others, so that they can register themselves
|
|
||||||
// 3. use chat key to trigger notifications
|
|
||||||
|
|
||||||
// this obtained from https://status-sandbox-c1b34.firebaseapp.com/
|
|
||||||
var deviceId = 'ca5pRJc6L8s:APA91bHpYFtpxvXx6uOayGmnNVnktA4PEEZdquCCt3fWR5ldLzSy1A37Tsbzk5Gavlmk1d_fvHRVnK7xPAhFFl-erF7O87DnIEstW6DEyhyiKZYA4dXFh6uy323f9A3uw5hEtT_kQVhT';
|
|
||||||
|
|
||||||
registerDevice(web3, identity, chatId, payload.key, deviceId); // weird signature because we reuse method for Device B
|
|
||||||
shareChatKey(chatId, payload.key);
|
|
||||||
|
|
||||||
// now do a cleanup
|
|
||||||
removeClientSession();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity,
|
|
||||||
topics: [newChatSessionTopic],
|
|
||||||
payload: '{"chat": "' + chatId + '"}', // globally unique chat Id
|
|
||||||
ttl: 20,
|
|
||||||
keyname: keyname
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var watchServerAckResponses = function (identity) {
|
|
||||||
// if server we accepted is ok, it will send encrypted (to you)
|
|
||||||
// message, with a ACK_NOTIFICATION_SERVER_SUBSCRIPTION topic (for which we wait)
|
|
||||||
// This message completes the subscription process. At this point you should
|
|
||||||
// have topic and symkey necessary to manage your subscription.
|
|
||||||
var filter = web3.shh.filter({
|
|
||||||
to: identity, // wait for anon. messages to ourselves
|
|
||||||
topics: [ackClientSubscriptionTopic]
|
|
||||||
});
|
|
||||||
filter.watch(function (error, result) {
|
|
||||||
if (!error) {
|
|
||||||
console.log("Server ACK received: ", result);
|
|
||||||
// response will be in JSON, e.g. {"server": "0xdeadbeef", "key": "0xdeadbeef"}
|
|
||||||
// which will give you serverId
|
|
||||||
var payload = JSON.parse(web3.toAscii(result.payload));
|
|
||||||
console.log(payload);
|
|
||||||
|
|
||||||
// no need to watch for the filter any more
|
|
||||||
filter.stopWatching();
|
|
||||||
|
|
||||||
// this concludes discovery, and we can use obtained key to invoke chat sessions
|
|
||||||
createChatSession(payload.key)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var identity = web3.shh.newIdentity();
|
|
||||||
console.log("identity used: ", identity)
|
|
||||||
|
|
||||||
// check identity
|
|
||||||
if (!web3.shh.hasIdentity(identity)) {
|
|
||||||
throw 'idenitity "' + identity + '" not found in whisper';
|
|
||||||
}
|
|
||||||
|
|
||||||
watchProposeServerResponses(identity);
|
|
||||||
watchServerAckResponses(identity);
|
|
||||||
|
|
||||||
// start discovery protocol, by sending discovery request
|
|
||||||
sendDiscoveryRequest(identity);
|
|
||||||
};
|
|
||||||
|
|
||||||
var startDeviceB = function () {
|
|
||||||
var web3 = new Web3();
|
|
||||||
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8745'));
|
|
||||||
|
|
||||||
// pre-defined test identity (it gets injected automatically by statusd)
|
|
||||||
var identity = '0x04eedbaafd6adf4a9233a13e7b1c3c14461fffeba2e9054b8d456ce5f6ebeafadcbf3dce3716253fbc391277fa5a086b60b283daf61fb5b1f26895f456c2f31ae3';
|
|
||||||
if (!web3.shh.hasIdentity(identity)) {
|
|
||||||
throw 'idenitity "' + identity + '" not found in whisper';
|
|
||||||
}
|
|
||||||
|
|
||||||
// for for key sharing, it is up to you how you implement it (which topic to use etc)
|
|
||||||
var filter = web3.shh.filter({
|
|
||||||
to: identity, // wait for anon. messages to ourselves
|
|
||||||
topics: ['chatKeySharing']
|
|
||||||
});
|
|
||||||
filter.watch(function (error, result) {
|
|
||||||
if (!error) {
|
|
||||||
console.log("Chat key received: ", result);
|
|
||||||
// response will be in JSON, e.g. {chat: "0xdeadbeef", key: "0x04e68e37433baf55ddc2fe9f7533e4e722bcdad4239c98df92f3522907ced72d"}
|
|
||||||
var payload = JSON.parse(web3.toAscii(result.payload));
|
|
||||||
console.log(payload);
|
|
||||||
|
|
||||||
// no need to watch for the filter any more
|
|
||||||
filter.stopWatching();
|
|
||||||
|
|
||||||
// let's save incoming key
|
|
||||||
var keyname = payload.chat + '-chatkey';
|
|
||||||
web3.shh.deleteSymKey(keyname);
|
|
||||||
web3.shh.addSymKey(keyname, payload.key);
|
|
||||||
|
|
||||||
// now register ourselves
|
|
||||||
var deviceId = 'some-device-id'; // you obtain this from FCM
|
|
||||||
registerDevice(web3, identity, payload.chat, payload.key, deviceId);
|
|
||||||
|
|
||||||
// finally, trigger the notifications on all registered device (except for yourself)
|
|
||||||
// at this point it is really trivial, use the key + specific topic:
|
|
||||||
var err = web3.shh.post({
|
|
||||||
from: identity,
|
|
||||||
topics: [sendNotificationTopic],
|
|
||||||
payload: '{' // see https://firebase.google.com/docs/cloud-messaging/http-server-ref
|
|
||||||
+ '"notification": {'
|
|
||||||
+ '"title": "status.im notification",'
|
|
||||||
+ '"body": "Hello this is test notification!",'
|
|
||||||
+ '"icon": "https://status.im/img/logo.png",'
|
|
||||||
+ '"click_action": "https://status.im"'
|
|
||||||
+ '},'
|
|
||||||
+ '"to": "{{ ID }}"' // this get replaced by device id your've registered
|
|
||||||
+ '}',
|
|
||||||
ttl: 20,
|
|
||||||
keyname: keyname
|
|
||||||
});
|
|
||||||
if (err !== null) {
|
|
||||||
console.log("message NOT sent")
|
|
||||||
} else {
|
|
||||||
console.log("message sent OK")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log("device b started")
|
|
||||||
};
|
|
||||||
|
|
||||||
startDeviceA();
|
|
||||||
startDeviceB();
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<!-- Static navbar -->
|
|
||||||
<nav class="navbar navbar-default navbar-static-top">
|
|
||||||
<div class="container">
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="jumbotron">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -0,0 +1 @@
|
||||||
|
AAAAHO5M3eI:APA91bFutoRd69Nq8-AsZJbwM8MFyH6vVUtkJLlrKXD0EMZ61vQxHA0FhUvJhOArmq-LBTEclB85WgKNYD-RSYKZ7pXKn8VKvFYBqoto6nL15cNlfpx4wCBJkHERlo7lLZx9-g6iQDks
|
|
@ -0,0 +1 @@
|
||||||
|
77d185965daa460ee7a8cb44f6001bb9884a04ed27a49ba6ea0f81cd4e5ac40b
|
|
@ -0,0 +1 @@
|
||||||
|
asdfasdf
|
|
@ -0,0 +1,2 @@
|
||||||
|
- see `make generate` which outputs web3.js into this directory
|
||||||
|
- in future, some static JavaScript files may be added to this folder
|
File diff suppressed because it is too large
Load Diff
16320
static/scripts/web3.js
16320
static/scripts/web3.js
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,754 @@
|
||||||
|
var chai = require("chai");
|
||||||
|
var expect = chai.expect;
|
||||||
|
var assert = chai.assert;
|
||||||
|
var Web3 = require('web3');
|
||||||
|
|
||||||
|
describe('Whisper Tests', function () {
|
||||||
|
var node1 = new Web3();
|
||||||
|
var node2 = new Web3();
|
||||||
|
var web3 = node1;
|
||||||
|
node1.setProvider(new web3.providers.HttpProvider('http://localhost:8645'));
|
||||||
|
node2.setProvider(new web3.providers.HttpProvider('http://localhost:8745'));
|
||||||
|
|
||||||
|
console.log('Node is expected: statusd --datadir app1 --http --httpport 8645 wnode');
|
||||||
|
console.log('Node is expected: statusd --datadir app2 --http --httpport 8745 wnode');
|
||||||
|
console.log('Node is expected: statusd --datadir wnode1 wnode --notify --injectaccounts=false --identity ./static/keys/wnodekey --firebaseauth ./static/keys/firebaseauthkey');
|
||||||
|
|
||||||
|
// some common vars
|
||||||
|
var topic1 = '0xdeadbeef'; // each topic 4 bytes, as hex
|
||||||
|
var topic2 = '0xbeefdead'; // each topic 4 bytes, as hex
|
||||||
|
var topic3 = '0xbebebebe'; // each topic 4 bytes, as hex
|
||||||
|
var topic4 = '0xdadadada'; // each topic 4 bytes, as hex
|
||||||
|
var identity1 = '0x04eedbaafd6adf4a9233a13e7b1c3c14461fffeba2e9054b8d456ce5f6ebeafadcbf3dce3716253fbc391277fa5a086b60b283daf61fb5b1f26895f456c2f31ae3';
|
||||||
|
var identity2 = '0x0490161b00f2c47542d28c2e8908e77159b1720dccceb6393d7c001850122efc3b1709bcea490fd8f5634ba1a145aa0722d86b9330b0e39a8d493cb981fd459da2';
|
||||||
|
|
||||||
|
// watchFilter makes sure that we halt the filter on first message received
|
||||||
|
var watchFilter = function (filter, done) {
|
||||||
|
var messageReceived = false;
|
||||||
|
filter.watch(function (error, message) {
|
||||||
|
if (messageReceived) return; // avoid double calling
|
||||||
|
messageReceived = true; // no need to watch for the filter any more
|
||||||
|
filter.stopWatching();
|
||||||
|
done(error, message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// makeTopic generates random topic (4 bytes, in hex)
|
||||||
|
var makeTopic = function () {
|
||||||
|
var min = 1;
|
||||||
|
var max = Math.pow(16, 8);
|
||||||
|
var randInt = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
return web3.toHex(randInt);
|
||||||
|
};
|
||||||
|
|
||||||
|
context('shh/5 API verification', function () {
|
||||||
|
it('statusd node is running', function () {
|
||||||
|
var web3 = new Web3();
|
||||||
|
var provider = new web3.providers.HttpProvider('http://localhost:8645');
|
||||||
|
var result = provider.send({});
|
||||||
|
assert.equal(typeof result, 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.version()', function () {
|
||||||
|
var version = node1.shh.version();
|
||||||
|
assert.equal(version, '0x5', 'Whisper version does not match');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.info()', function () {
|
||||||
|
var info = node1.shh.info();
|
||||||
|
if (info == "") {
|
||||||
|
throw new Error('no Whisper info provided')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
context('symmetric key management', function () {
|
||||||
|
var keyId = ''; // symmetric key ID (to be populated)
|
||||||
|
var keyVal = ''; // symmetric key value (to be populated)
|
||||||
|
|
||||||
|
it('shh.generateSymmetricKey()', function () {
|
||||||
|
keyId = node1.shh.generateSymmetricKey();
|
||||||
|
assert.lengthOf(keyId, 64, 'invalid keyId length');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.getSymmetricKey(keyId)', function () {
|
||||||
|
keyVal = node1.shh.getSymmetricKey(keyId);
|
||||||
|
assert.lengthOf(keyVal, 66, 'invalid key value length'); // 2 bytes for "0x"
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.hasSymmetricKey(keyId)', function () {
|
||||||
|
expect(node1.shh.hasSymmetricKey(keyId)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.deleteSymmetricKey(keyId)', function () {
|
||||||
|
expect(node1.shh.hasSymmetricKey(keyId)).to.equal(true);
|
||||||
|
node1.shh.deleteSymmetricKey(keyId);
|
||||||
|
expect(node1.shh.hasSymmetricKey(keyId)).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.addSymmetricKeyDirect(keyVal)', function () {
|
||||||
|
keyIdOriginal = keyId;
|
||||||
|
keyId = node1.shh.addSymmetricKeyDirect(keyVal);
|
||||||
|
assert.notEqual(keyId, keyIdOriginal);
|
||||||
|
assert.lengthOf(keyId, 64, 'invalid keyId length');
|
||||||
|
expect(node1.shh.hasSymmetricKey(keyId)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.addSymmetricKeyFromPassword(password)', function () {
|
||||||
|
var password = 'foobar';
|
||||||
|
var keyId = node1.shh.addSymmetricKeyFromPassword(password);
|
||||||
|
var keyVal = node1.shh.getSymmetricKey(keyId);
|
||||||
|
|
||||||
|
assert.lengthOf(keyId, 64, 'invalid keyId length');
|
||||||
|
expect(node1.shh.hasSymmetricKey(keyId)).to.equal(true);
|
||||||
|
assert.equal(keyVal, '0xa582720d74d463589df14c11538189a1c07778c47e86f70bab7b5ba27e2de3cc');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('assymmetric key management', function () {
|
||||||
|
var keyId = ''; // to be populated
|
||||||
|
var pubKey = ''; // to be populated
|
||||||
|
|
||||||
|
it('shh.newKeyPair()', function () {
|
||||||
|
keyId = node1.shh.newKeyPair();
|
||||||
|
assert.lengthOf(keyId, 64);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.hasKeyPair(id)', function () {
|
||||||
|
expect(node1.shh.hasKeyPair(keyId)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.getPublicKey(id)', function () {
|
||||||
|
pubKey = node1.shh.getPublicKey(keyId);
|
||||||
|
assert.lengthOf(pubKey, 132);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.hasKeyPair(pubKey)', function () {
|
||||||
|
expect(node1.shh.hasKeyPair(pubKey)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.getPrivateKey(id)', function () {
|
||||||
|
var prvkey = node1.shh.getPrivateKey(keyId);
|
||||||
|
assert.lengthOf(prvkey, 66);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.deleteKeyPair(id)', function () {
|
||||||
|
expect(node1.shh.hasKeyPair(pubKey)).to.equal(true);
|
||||||
|
expect(node1.shh.hasKeyPair(keyId)).to.equal(true);
|
||||||
|
node1.shh.deleteKeyPair(keyId);
|
||||||
|
expect(node1.shh.hasKeyPair(pubKey)).to.equal(false);
|
||||||
|
expect(node1.shh.hasKeyPair(keyId)).to.equal(false);
|
||||||
|
|
||||||
|
// re-create
|
||||||
|
keyId = node1.shh.newKeyPair();
|
||||||
|
assert.lengthOf(keyId, 64);
|
||||||
|
pubKey = node1.shh.getPublicKey(keyId);
|
||||||
|
assert.lengthOf(pubKey, 132);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.deleteKeyPair(pubKey)', function () {
|
||||||
|
expect(node1.shh.hasKeyPair(pubKey)).to.equal(true);
|
||||||
|
expect(node1.shh.hasKeyPair(keyId)).to.equal(true);
|
||||||
|
node1.shh.deleteKeyPair(pubKey);
|
||||||
|
expect(node1.shh.hasKeyPair(pubKey)).to.equal(false);
|
||||||
|
expect(node1.shh.hasKeyPair(keyId)).to.equal(false);
|
||||||
|
|
||||||
|
// re-create
|
||||||
|
keyId = node1.shh.newKeyPair();
|
||||||
|
assert.lengthOf(keyId, 64);
|
||||||
|
pubKey = node1.shh.getPublicKey(keyId);
|
||||||
|
assert.lengthOf(pubKey, 132);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('subscribe and manually get messages', function () {
|
||||||
|
// NOTE: you can still use shh.filter to poll for messages automatically, see other examples
|
||||||
|
|
||||||
|
var filterid1 = ''; // sym filter, to be populated
|
||||||
|
var filterid2 = ''; // asym filter, to be populated
|
||||||
|
var keyId = ''; // symkey, to be populated
|
||||||
|
var uniqueTopic = makeTopic();
|
||||||
|
|
||||||
|
var payloadBeforeSymFilter = 'sent before filter was active (symmetric)';
|
||||||
|
var payloadAfterSymFilter = 'sent after filter was active (symmetric)';
|
||||||
|
var payloadBeforeAsymFilter = 'sent before filter was active (asymmetric)';
|
||||||
|
var payloadAfterAsymFilter = 'sent after filter was active (asymmetric)';
|
||||||
|
|
||||||
|
it('shh.subscribe(filterParams) - symmetric filter', function () {
|
||||||
|
keyId = node1.shh.generateSymmetricKey();
|
||||||
|
assert.lengthOf(keyId, 64);
|
||||||
|
|
||||||
|
// send message, which will be floating around *before* filter is even created
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
key: keyId,
|
||||||
|
topic: uniqueTopic,
|
||||||
|
payload: payloadBeforeSymFilter
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
|
||||||
|
// symmetric filter
|
||||||
|
filterid1 = node1.shh.subscribe({
|
||||||
|
type: "sym",
|
||||||
|
key: keyId,
|
||||||
|
sig: identity1,
|
||||||
|
topics: [topic1, topic2, uniqueTopic]
|
||||||
|
});
|
||||||
|
assert.lengthOf(filterid1, 64);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.subscribe(filterParams) - asymmetric filter', function () {
|
||||||
|
// send message, which will be floating around *before* filter is even created
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
key: identity2,
|
||||||
|
topic: uniqueTopic,
|
||||||
|
payload: payloadBeforeAsymFilter
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
|
||||||
|
// asymmetric filter
|
||||||
|
filterid2 = node1.shh.subscribe({
|
||||||
|
type: "asym",
|
||||||
|
key: identity2,
|
||||||
|
sig: identity1,
|
||||||
|
topics: [topic1, topic2, uniqueTopic]
|
||||||
|
});
|
||||||
|
assert.lengthOf(filterid1, 64);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.getMessages(filterID) - symmetric filter', function () {
|
||||||
|
// let's try to capture message that was there *before* filter is created
|
||||||
|
var messages = node1.shh.getMessages(filterid1);
|
||||||
|
assert.typeOf(messages, 'array');
|
||||||
|
assert.lengthOf(messages, 1);
|
||||||
|
assert.equal(web3.toAscii(messages[0].payload), payloadBeforeSymFilter);
|
||||||
|
|
||||||
|
// send message, after the filter has been already installed
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
key: keyId,
|
||||||
|
topic: uniqueTopic,
|
||||||
|
payload: payloadAfterSymFilter
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.getMessages(filterID) - asymmetric filter', function () {
|
||||||
|
// let's try to capture message that was there *before* filter is created
|
||||||
|
var messages = node1.shh.getMessages(filterid2);
|
||||||
|
assert.typeOf(messages, 'array');
|
||||||
|
assert.lengthOf(messages, 1);
|
||||||
|
assert.equal(web3.toAscii(messages[0].payload), payloadBeforeAsymFilter);
|
||||||
|
|
||||||
|
// send message, after the filter has been already installed
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
key: identity2,
|
||||||
|
topic: uniqueTopic,
|
||||||
|
payload: payloadAfterAsymFilter
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.getSubscriptionMessages(filterID) - symmetric filter', function (done) {
|
||||||
|
// allow some time for message to propagate
|
||||||
|
setTimeout(function () {
|
||||||
|
// now let's try to capture new messages from our last capture
|
||||||
|
var messages = node1.shh.getSubscriptionMessages(filterid1);
|
||||||
|
assert.typeOf(messages, 'array');
|
||||||
|
assert.lengthOf(messages, 1);
|
||||||
|
assert.equal(web3.toAscii(messages[0].payload), payloadAfterSymFilter);
|
||||||
|
|
||||||
|
// no more messages should be returned
|
||||||
|
messages = node1.shh.getSubscriptionMessages(filterid1);
|
||||||
|
assert.typeOf(messages, 'array');
|
||||||
|
assert.lengthOf(messages, 0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.getSubscriptionMessages(filterID) - asymmetric filter', function () {
|
||||||
|
// allow some time for message to propagate
|
||||||
|
setTimeout(function () {
|
||||||
|
// now let's try to capture new messages from our last capture
|
||||||
|
var messages = node1.shh.getSubscriptionMessages(filterid2);
|
||||||
|
assert.typeOf(messages, 'array');
|
||||||
|
assert.lengthOf(messages, 1);
|
||||||
|
assert.equal(web3.toAscii(messages[0].payload), payloadAfterAsymFilter);
|
||||||
|
|
||||||
|
// no more messages should be returned
|
||||||
|
messages = node1.shh.getSubscriptionMessages(filterid2);
|
||||||
|
assert.typeOf(messages, 'array');
|
||||||
|
assert.lengthOf(messages, 0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shh.unsubscribe(filterID)', function () {
|
||||||
|
node1.shh.unsubscribe(filterid1);
|
||||||
|
node1.shh.unsubscribe(filterid2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('symmetrically encrypted messages send/recieve', function () {
|
||||||
|
this.timeout(0);
|
||||||
|
|
||||||
|
var keyId = ''; // symmetric key ID (to be populated)
|
||||||
|
var keyVal = ''; // symmetric key value (to be populated)
|
||||||
|
var payload = 'here come the dragons';
|
||||||
|
|
||||||
|
it('default test identity is present', function () {
|
||||||
|
if (!node1.shh.hasKeyPair(identity1)) {
|
||||||
|
throw new Error('identity not found in whisper: ' + identity1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ensure symkey exists', function () {
|
||||||
|
keyId = node1.shh.generateSymmetricKey();
|
||||||
|
assert.lengthOf(keyId, 64);
|
||||||
|
expect(node1.shh.hasSymmetricKey(keyId)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('read the generated symkey', function () {
|
||||||
|
keyVal = node1.shh.getSymmetricKey(keyId);
|
||||||
|
assert.lengthOf(keyVal, 66); // 2 bytes for "0x"
|
||||||
|
});
|
||||||
|
|
||||||
|
it('send/receive symmetrically encrypted message', function (done) {
|
||||||
|
// start watching for messages
|
||||||
|
watchFilter(node1.shh.filter({
|
||||||
|
type: "sym",
|
||||||
|
key: keyId,
|
||||||
|
sig: identity1,
|
||||||
|
topics: [topic1, topic2]
|
||||||
|
}), function (err, message) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// send message
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
key: keyId,
|
||||||
|
sig: identity1,
|
||||||
|
topic: topic1,
|
||||||
|
payload: web3.fromAscii(payload),
|
||||||
|
ttl: 20,
|
||||||
|
powTime: 2,
|
||||||
|
powTarget: 0.001
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('send the minimal symmetric message possible', function (done) {
|
||||||
|
var uniqueTopic = makeTopic();
|
||||||
|
|
||||||
|
// start watching for messages
|
||||||
|
watchFilter(node1.shh.filter({
|
||||||
|
type: "sym",
|
||||||
|
key: keyId,
|
||||||
|
topics: [uniqueTopic]
|
||||||
|
}), function (err, message) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// send message
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
key: keyId,
|
||||||
|
topic: uniqueTopic
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('message travelling from one node to another', function () {
|
||||||
|
this.timeout(0);
|
||||||
|
|
||||||
|
var keyId1 = ''; // symmetric key ID on node 1 (to be populated)
|
||||||
|
var keyId2 = ''; // symmetric key ID on node 2 (to be populated)
|
||||||
|
|
||||||
|
it('statusd node1 is running', function () {
|
||||||
|
var web3 = new Web3();
|
||||||
|
var provider = new web3.providers.HttpProvider('http://localhost:8645');
|
||||||
|
var result = provider.send({});
|
||||||
|
assert.equal(typeof result, 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('statusd node2 is running', function () {
|
||||||
|
var web3 = new Web3();
|
||||||
|
var provider = new web3.providers.HttpProvider('http://localhost:8745');
|
||||||
|
var result = provider.send({});
|
||||||
|
assert.equal(typeof result, 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test identities injected', function () {
|
||||||
|
if (!node1.shh.hasKeyPair(identity1)) {
|
||||||
|
throw new Error('identity not found in whisper (node1): ' + identity1);
|
||||||
|
}
|
||||||
|
if (!node1.shh.hasKeyPair(identity2)) {
|
||||||
|
throw new Error('identity not found in whisper (node1): ' + identity2);
|
||||||
|
}
|
||||||
|
if (!node2.shh.hasKeyPair(identity1)) {
|
||||||
|
throw new Error('identity not found in whisper (node2): ' + identity1);
|
||||||
|
}
|
||||||
|
if (!node2.shh.hasKeyPair(identity2)) {
|
||||||
|
throw new Error('identity not found in whisper (node2): ' + identity2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ensure symkey exists', function () {
|
||||||
|
keyId1 = node1.shh.generateSymmetricKey();
|
||||||
|
assert.lengthOf(keyId1, 64);
|
||||||
|
expect(node1.shh.hasSymmetricKey(keyId1)).to.equal(true);
|
||||||
|
|
||||||
|
// obtain key value
|
||||||
|
var keyVal = node1.shh.getSymmetricKey(keyId1);
|
||||||
|
assert.lengthOf(keyVal, 66); // 2 bytes of "0x"
|
||||||
|
|
||||||
|
// share the value with the node2
|
||||||
|
keyId2 = node2.shh.addSymmetricKeyDirect(keyVal);
|
||||||
|
assert.lengthOf(keyId2, 64);
|
||||||
|
expect(node2.shh.hasSymmetricKey(keyId2)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('send symmetrically encrypted, signed message (node1 -> node2)', function (done) {
|
||||||
|
var payload = 'send symmetrically encrypted, signed message (node1 -> node2)';
|
||||||
|
var topic = makeTopic();
|
||||||
|
// start watching for messages
|
||||||
|
watchFilter(node2.shh.filter({
|
||||||
|
type: "sym",
|
||||||
|
sig: identity1,
|
||||||
|
key: keyId2,
|
||||||
|
topics: [topic]
|
||||||
|
}), function (err, message) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// send message
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
sig: identity1,
|
||||||
|
key: keyId1,
|
||||||
|
topic: topic,
|
||||||
|
payload: payload,
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('send asymmetrically encrypted, signed message (node1.id1 -> node2.id2)', function (done) {
|
||||||
|
var payload = 'send asymmetrically encrypted, signed message (node1.id1 -> node2.id2)';
|
||||||
|
var topic = makeTopic();
|
||||||
|
// start watching for messages
|
||||||
|
watchFilter(node2.shh.filter({
|
||||||
|
type: "asym",
|
||||||
|
sig: identity1,
|
||||||
|
key: identity2
|
||||||
|
}), function (err, message) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// send message
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
sig: identity1,
|
||||||
|
key: identity2,
|
||||||
|
topic: topic,
|
||||||
|
payload: payload,
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('push notifications', function () {
|
||||||
|
this.timeout(5000);
|
||||||
|
var discoveryPubKey = '0x040edb0d71a3dbe928e154fcb696ffbda359b153a90efc2b46f0043ce9f5dbe55b77b9328fd841a1db5273758624afadd5b39638d4c35b36b3a96e1a586c1b4c2a';
|
||||||
|
var discoverServerTopic = '0x268302f3'; // DISCOVER_NOTIFICATION_SERVER
|
||||||
|
var proposeServerTopic = '0x08e3d8c0'; // PROPOSE_NOTIFICATION_SERVER
|
||||||
|
var acceptServerTopic = '0x04f7dea6'; // ACCEPT_NOTIFICATION_SERVER
|
||||||
|
var ackClientSubscriptionTopic = '0x93dafe28'; // ACK_NOTIFICATION_SERVER_SUBSCRIPTION
|
||||||
|
var sendNotificationTopic = '0x69915296'; // SEND_NOTIFICATION
|
||||||
|
var newChatSessionTopic = '0x509579a2'; // NEW_CHAT_SESSION
|
||||||
|
var ackNewChatSessionTopic = '0xd012aae8'; // ACK_NEW_CHAT_SESSION
|
||||||
|
var newDeviceRegistrationTopic = '0x14621a51'; // NEW_DEVICE_REGISTRATION
|
||||||
|
var ackDeviceRegistrationTopic = '0x424358d6'; // ACK_DEVICE_REGISTRATION
|
||||||
|
var checkClientSessionTopic = '0x8745d931'; // CHECK_CLIENT_SESSION
|
||||||
|
var confirmClientSessionTopic = '0xd3202c5f'; // CONFIRM_CLIENT_SESSION
|
||||||
|
var dropClientSessionTopic = '0x3a6656bb'; // DROP_CLIENT_SESSION
|
||||||
|
|
||||||
|
// ensures that message had payload (which is HEX-encoded JSON)
|
||||||
|
var extractPayload = function (message) {
|
||||||
|
expect(message).to.have.property('payload');
|
||||||
|
return JSON.parse(web3.toAscii(message.payload));
|
||||||
|
};
|
||||||
|
|
||||||
|
var identity1 = ''; // pub key of device 1
|
||||||
|
var identity2 = ''; // pub key of device 2
|
||||||
|
var chatKeySharingTopic = makeTopic(); // topic used by device1 to send chat key to device 2
|
||||||
|
|
||||||
|
context('prepare devices', function () {
|
||||||
|
it('create key pair to be used as main identity on device1', function () {
|
||||||
|
var keyId = node1.shh.newKeyPair();
|
||||||
|
assert.lengthOf(keyId, 64);
|
||||||
|
|
||||||
|
identity1 = node1.shh.getPublicKey(keyId);
|
||||||
|
assert.lengthOf(identity1, 132);
|
||||||
|
|
||||||
|
expect(node1.shh.hasKeyPair(identity1)).to.equal(true);
|
||||||
|
expect(node1.shh.hasKeyPair(identity2)).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create key pair to be used as main identity on device2', function () {
|
||||||
|
var keyId = node2.shh.newKeyPair();
|
||||||
|
assert.lengthOf(keyId, 64);
|
||||||
|
|
||||||
|
identity2 = node2.shh.getPublicKey(keyId);
|
||||||
|
assert.lengthOf(identity1, 132);
|
||||||
|
|
||||||
|
expect(node2.shh.hasKeyPair(identity1)).to.equal(false);
|
||||||
|
expect(node2.shh.hasKeyPair(identity2)).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('run device1', function () {
|
||||||
|
var serverId = ''; // accepted/selected server id
|
||||||
|
var subscriptionKeyId = ''; // symkey provided by server, and used to configure client-server subscription
|
||||||
|
var chatKeyId = ''; // symkey provided by server, and shared among clients so that they can trigger notifications
|
||||||
|
var appChatId = ''; // chat id that identifies device1-device2 interaction session on RN app level
|
||||||
|
|
||||||
|
|
||||||
|
it('start discovery by sending discovery request', function () {
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
sig: identity1,
|
||||||
|
key: discoveryPubKey,
|
||||||
|
topic: discoverServerTopic,
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('watch for server proposals', function (done) {
|
||||||
|
watchFilter(node1.shh.filter({
|
||||||
|
type: "asym",
|
||||||
|
sig: discoveryPubKey,
|
||||||
|
key: identity1,
|
||||||
|
topics: [proposeServerTopic]
|
||||||
|
}), function (err, message) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
// process payload
|
||||||
|
var payload = extractPayload(message);
|
||||||
|
expect(payload).to.have.property('server');
|
||||||
|
serverId = payload.server;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('client accepts server', function () {
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
sig: identity1,
|
||||||
|
key: discoveryPubKey,
|
||||||
|
topic: acceptServerTopic,
|
||||||
|
payload: '{"server": "' + serverId + '"}',
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('watch for server ACK response and save provided subscription key', function (done) {
|
||||||
|
watchFilter(node1.shh.filter({
|
||||||
|
type: "asym",
|
||||||
|
key: identity1,
|
||||||
|
topics: [ackClientSubscriptionTopic]
|
||||||
|
}), function (err, message) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
// process payload
|
||||||
|
var payload = extractPayload(message);
|
||||||
|
expect(payload).to.have.property('server');
|
||||||
|
expect(payload).to.have.property('key');
|
||||||
|
|
||||||
|
// save subscription key
|
||||||
|
subscriptionKeyId = node1.shh.addSymmetricKeyDirect(payload.key);
|
||||||
|
assert.lengthOf(subscriptionKeyId, 64);
|
||||||
|
expect(node1.shh.hasSymmetricKey(subscriptionKeyId)).to.equal(true);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create chat session', function () {
|
||||||
|
appChatId = makeTopic(); // globally unique chat id
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
sig: identity1,
|
||||||
|
key: subscriptionKeyId,
|
||||||
|
topic: newChatSessionTopic,
|
||||||
|
payload: '{"chat": "' + appChatId + '"}',
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('watch for server to respond with chat key', function (done) {
|
||||||
|
watchFilter(node1.shh.filter({
|
||||||
|
type: "asym",
|
||||||
|
key: identity1,
|
||||||
|
topics: [ackNewChatSessionTopic]
|
||||||
|
}), function (err, message) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
// process payload
|
||||||
|
var payload = extractPayload(message);
|
||||||
|
expect(payload).to.have.property('server');
|
||||||
|
expect(payload).to.have.property('key');
|
||||||
|
|
||||||
|
// save subscription key
|
||||||
|
chatKeyId = node1.shh.addSymmetricKeyDirect(payload.key);
|
||||||
|
assert.lengthOf(chatKeyId, 64);
|
||||||
|
expect(node1.shh.hasSymmetricKey(chatKeyId)).to.equal(true);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('register device with a given chat', function (done) {
|
||||||
|
// this obtained from https://status-sandbox-c1b34.firebaseapp.com/
|
||||||
|
var deviceId = 'ca5pRJc6L8s:APA91bHpYFtpxvXx6uOayGmnNVnktA4PEEZdquCCt3fWR5ldLzSy1A37Tsbzk5Gavlmk1d_fvHRVnK7xPAhFFl-erF7O87DnIEstW6DEyhyiKZYA4dXFh6uy323f9A3uw5hEtT_kQVhT';
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
sig: identity1,
|
||||||
|
key: chatKeyId,
|
||||||
|
topic: newDeviceRegistrationTopic,
|
||||||
|
payload: '{"device": "' + deviceId + '"}',
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
|
||||||
|
// watch for server server ACK
|
||||||
|
watchFilter(node1.shh.filter({
|
||||||
|
type: "asym",
|
||||||
|
key: identity1,
|
||||||
|
topics: [ackDeviceRegistrationTopic]
|
||||||
|
}), function (err, message) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
// process payload
|
||||||
|
var payload = extractPayload(message);
|
||||||
|
expect(payload).to.have.property('server');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('share chat key, so that another device can send us notifications', function () {
|
||||||
|
var chatKey = node1.shh.getSymmetricKey(chatKeyId);
|
||||||
|
assert.lengthOf(chatKey, 66);
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
sig: identity1,
|
||||||
|
key: identity2,
|
||||||
|
topic: chatKeySharingTopic,
|
||||||
|
payload: '{"chat": "' + appChatId + '", "key": "' + chatKey + '"}',
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('run device2', function () {
|
||||||
|
var chatKeyId = '';
|
||||||
|
|
||||||
|
it('watch for device1 to send us chat key', function (done) {
|
||||||
|
watchFilter(node2.shh.filter({
|
||||||
|
type: "asym",
|
||||||
|
key: identity2,
|
||||||
|
topics: [chatKeySharingTopic]
|
||||||
|
}), function (err, message) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
// process payload
|
||||||
|
var payload = extractPayload(message);
|
||||||
|
expect(payload).to.have.property('chat');
|
||||||
|
expect(payload).to.have.property('key');
|
||||||
|
|
||||||
|
// persist chat key
|
||||||
|
chatKeyId = node2.shh.addSymmetricKeyDirect(payload.key);
|
||||||
|
assert.lengthOf(chatKeyId, 64);
|
||||||
|
expect(node2.shh.hasSymmetricKey(chatKeyId)).to.equal(true);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('trigger notification (from device2, on device1)', function () {
|
||||||
|
var message = {
|
||||||
|
type: "sym",
|
||||||
|
sig: identity2,
|
||||||
|
key: chatKeyId,
|
||||||
|
topic: sendNotificationTopic,
|
||||||
|
payload: '{' // see https://firebase.google.com/docs/cloud-messaging/http-server-ref
|
||||||
|
+ '"notification": {'
|
||||||
|
+ '"title": "status.im notification",'
|
||||||
|
+ '"body": "Hello this is test notification!",'
|
||||||
|
+ '"icon": "https://status.im/img/logo.png",'
|
||||||
|
+ '"click_action": "https://status.im"'
|
||||||
|
+ '},'
|
||||||
|
+ '"to": "{{ ID }}"' // this get replaced by device id your've registered
|
||||||
|
+ '}',
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node2.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('misc methods and cleanup', function () {
|
||||||
|
|
||||||
|
it('check client session', function (done) {
|
||||||
|
// request status
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
sig: identity1,
|
||||||
|
key: discoveryPubKey,
|
||||||
|
topic: checkClientSessionTopic,
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
|
||||||
|
// process server's response
|
||||||
|
watchFilter(node1.shh.filter({
|
||||||
|
type: "asym",
|
||||||
|
key: identity1,
|
||||||
|
topics: [confirmClientSessionTopic]
|
||||||
|
}), function (err, message) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
// process payload
|
||||||
|
var payload = extractPayload(message);
|
||||||
|
expect(payload).to.have.property('server');
|
||||||
|
expect(payload).to.have.property('key');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('remove client session', function () {
|
||||||
|
var message = {
|
||||||
|
type: "asym",
|
||||||
|
sig: identity1,
|
||||||
|
key: discoveryPubKey,
|
||||||
|
topic: dropClientSessionTopic,
|
||||||
|
ttl: 20
|
||||||
|
};
|
||||||
|
expect(node1.shh.post(message)).to.equal(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue