diff --git a/golos-lib-js/src/auth/messages.js b/golos-lib-js/src/auth/messages.js
index 98ee5e7..f6ab15a 100644
--- a/golos-lib-js/src/auth/messages.js
+++ b/golos-lib-js/src/auth/messages.js
@@ -6,6 +6,7 @@ import truncate from 'lodash/truncate';
import {Aes, PrivateKey, PublicKey} from './ecc'
import {ops} from './serializer'
import golosApi from '../api'
+import auth from '.'
import {fitImageToSize} from '../utils';
import { promisify, } from '../promisify'
const {isInteger} = Number
@@ -32,6 +33,8 @@ export const MAX_IMAGE_QUOTE_LENGTH = 2000;
const toPrivateObj = o => (o ? o.d ? o : PrivateKey.fromWif(o) : o/*null or undefined*/)
const toPublicObj = o => (o ? o.Q ? o : PublicKey.fromString(o) : o/*null or undefined*/)
+export const emptyPublicKey = 'GLS1111111111111111111111111111111114T1Anm'
+
function validateAppVersion(app, version) {
assert(typeof app === 'string' && app.length >= 1 && app.length <= 16,
'message.app should be a string, >= 1, <= 16');
@@ -243,6 +246,26 @@ function msgFromHex(hex, lengthPrefixed = false) {
return msgFromBuf(buf, lengthPrefixed)
}
+const parseMsg = (message, rawMsg, raw_messages = false) => {
+ message.raw_message = rawMsg
+ if (!raw_messages) {
+ let msg = JSON.parse(message.raw_message)
+ msg.type = msg.type || 'text'
+ validateBody(msg.body)
+ if (msg.type === 'image')
+ validateImageMsg(msg)
+ validateAppVersion(msg.app, msg.version)
+ validateMsgWithQuote(msg)
+ message.message = msg
+ }
+}
+
+const warnDeprecation = (oldMethod, newMethod) => {
+ console.warn(`golos.messages.${oldMethod} deprecated. ` +
+ `It is not async, not supports groups, etc. ` +
+ `Will be removed in future. Migrate to golos.messages.${newMethod}.`)
+}
+
/**
Decodes messages of format used by golos.messages.encode(), which are length-prefixed, and also messages sent by another way (not length-prefixed).
Also, parses (JSON) and validates each message (app, version...). (Invalid messages are also added to result, it is need to mark them as read. To change it, use on_error
).
@@ -260,6 +283,8 @@ function msgFromHex(hex, lengthPrefixed = false) {
@return {array} - result array of message_objects. Each object has "message" and "raw_message" fields. If message is invalid, it has only "raw_message" field. And if message cannot be decoded at all, it hasn't any of these fields.
*/
export function decode(private_memo_key, second_user_public_memo_key, message_objects, before_decode = undefined, for_each = undefined, on_error = undefined, begin_idx = undefined, end_idx = undefined, raw_messages = false) {
+ warnDeprecation('decode', 'decodeMsgs')
+
assert(private_memo_key, 'private_memo_key is required');
assert(second_user_public_memo_key, 'second_user_public_memo_key is required');
assert(message_objects, 'message_objects is required');
@@ -300,17 +325,7 @@ export function decode(private_memo_key, second_user_public_memo_key, message_ob
const mbuf = ByteBuffer.fromBinary(decrypted.toString('binary'), ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN)
decrypted = msgFromBuf(mbuf, true)
- message_object.raw_message = decrypted;
- if (!raw_messages) {
- let msg = JSON.parse(message_object.raw_message);
- msg.type = msg.type || 'text';
- validateBody(msg.body);
- if (msg.type === 'image')
- validateImageMsg(msg);
- validateAppVersion(msg.app, msg.version);
- validateMsgWithQuote(msg);
- message_object.message = msg;
- }
+ parseMsg(message_object, decrypted, raw_messages)
} catch (exception) {
if (processOnError(exception))
return true;
@@ -327,11 +342,20 @@ export function decode(private_memo_key, second_user_public_memo_key, message_ob
return results;
}
-export async function decodeMsgs({ messages, for_each, on_error, raw_messages,
+export async function decodeMsgs({ msgs, before_decode, for_each, on_error,
+ raw_messages,
+ private_memo, // chats
+ api, login, // groups
begin_idx, end_idx,
}) {
+ let private_key = private_memo && toPrivateObj(private_memo)
+ const myPublic = private_key.toPublic().toString()
+ let shareds = {}
+
let results = []
- forEachMessage(messages, begin_idx, end_idx, (message, i) => {
+ let entriesDec = {}
+
+ forEachMessage(msgs, begin_idx, end_idx, (message, i) => {
// Return true if for_each should not be called
let processOnError = (exception) => {
if (on_error) {
@@ -340,30 +364,49 @@ export async function decodeMsgs({ messages, for_each, on_error, raw_messages,
}
return true
}
+ console.error('golos.messages.decodeMsgs', i, exception)
return false
}
try {
- let rawMsg = msgFromHex(message.encrypted_message)
- const isEncrypted = rawMsg.startsWith('{"t":"em"')
+ if (before_decode && before_decode(message, i, results)) {
+ return true
+ }
+
+ if (!message.group) {
+ const pubKey = message.from_memo_key === myPublic ?
+ message.to_memo_key : message.from_memo_key
+ // Most "heavy" line (in private chats)
+ if (!shareds[pubKey]) {
+ console.error(pubKey)
+ console.error(message.from_memo_key)
+ console.error(message.to_memo_key)
+ shareds[pubKey] = private_key.get_shared_secret(toPublicObj(pubKey))
+ }
- if (isEncrypted) {
- if (!message.decrypted || message.decrypt_date !== message.receive_date) {
+ let decrypted = Aes.decrypt(shareds[pubKey], null,
+ message.nonce.toString(),
+ Buffer.from(message.encrypted_message, 'hex'),
+ message.checksum)
+ const mbuf = ByteBuffer.fromBinary(decrypted.toString('binary'), ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN)
+ decrypted = msgFromBuf(mbuf, true)
+
+ parseMsg(message, decrypted, raw_messages)
+ } else {
+ let rawMsg = msgFromHex(message.encrypted_message)
+ const isEncrypted = rawMsg.startsWith('{"t":"em"')
+
+ if (isEncrypted) {
+ if (!message.decrypted || message.decrypt_date !== message.receive_date) {
+ entriesDec[i] = { group: message.group,
+ encrypted_message: message.encrypted_message, }
+ return true
+ }
+ rawMsg = msgFromHex(message.decrypted)
}
- rawMsg = msgFromHex(message.decrypted)
- }
- message.raw_message = rawMsg
- if (!raw_messages) {
- let msg = JSON.parse(message.raw_message)
- msg.type = msg.type || 'text'
- validateBody(msg.body)
- if (msg.type === 'image')
- validateImageMsg(msg)
- validateAppVersion(msg.app, msg.version)
- validateMsgWithQuote(msg)
- message.message = msg
+ parseMsg(message, rawMsg, raw_messages)
}
} catch (exception) {
if (processOnError(exception))
@@ -380,6 +423,39 @@ export async function decodeMsgs({ messages, for_each, on_error, raw_messages,
return true
})
+
+ const entries = Object.values(entriesDec)
+ if (entries.length) {
+ if (!api) {
+ api = golosApi
+ }
+ const { dgp, account, keys, sessionName } = login
+ const decRes = await auth.withNodeLogin({ account, keys, sessionName, dgp,
+ call: async (loginData) => {
+ const decRes = await api.decryptMessagesAsync({
+ entries
+ })
+ return decRes
+ }
+ })
+ const idxs = Object.keys(entriesDec)
+ if (decRes.status !== 'ok') {
+
+ } else {
+ for (let i = 0; i < entries.length; ++i) {
+ const dec = decRes.results[i]
+ if (dec.body) {
+ const msg = results[idxs[i]]
+ dec.decrypted = dec.body // TODO: but `decrypted` should be hex
+ parseMsg(msg, dec.decrypted, raw_messages)
+ // for_each
+ } else {
+ // on_error, for_each if not handled
+ }
+ }
+ }
+ }
+
return results
}
@@ -392,6 +468,8 @@ export async function decodeMsgs({ messages, for_each, on_error, raw_messages,
@return {object} - Object with fields: nonce, checksum and message.
*/
export function encode(from_private_memo_key, to_public_memo_key, message, nonce = undefined) {
+ warnDeprecation('encode', 'encodeMsg')
+
assert(from_private_memo_key, 'from_private_memo_key is required');
assert(to_public_memo_key, 'to_public_memo_key is required');
assert(message, 'message is required');
@@ -415,14 +493,50 @@ export function encode(from_private_memo_key, to_public_memo_key, message, nonce
};
}
-export async function encodeMsg({ group, message, nonce, api }) {
+export async function encodeMsg({ group,
+ private_memo, to_public_memo,
+ msg, nonce,
+ api
+}) {
+ assert(msg, 'msg is required')
+
const msgToBuffer = (msg) => {
const mbuf = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN)
- mbuf.writeUTF8String(JSON.stringify(msg));
+ if (group) {
+ mbuf.writeUTF8String(JSON.stringify(msg))
+ } else {
+ mbuf.writeVString(JSON.stringify(msg))
+ }
msg = new Buffer(mbuf.copy(0, mbuf.offset).toBinary(), 'binary')
return msg
}
+ if (!group) {
+ assert(private_memo, 'private_memo is required in private chats')
+ assert(to_public_memo, 'to_public_memo is required in private chats')
+
+ const fromKey = toPrivateObj(private_memo)
+ const toKey = toPublicObj(to_public_memo)
+
+ msg = msgToBuffer(msg)
+
+ let data = Aes.encrypt(fromKey,
+ toKey,
+ msg,
+ nonce)
+
+ return {
+ nonce: data.nonce.toString(),
+ encrypted_message: data.message.toString('hex'),
+ checksum: data.checksum,
+ from_memo_key: fromKey.toPublic().toString(),
+ to_memo_key: to_public_memo,
+ }
+ }
+
+ assert(!private_memo, 'private_memo is for private messages, not groups')
+ assert(!to_public_memo, 'to_public_memo is for private messages, not groups')
+
if (group.is_encrypted) {
if (!api) {
api = golosApi
@@ -448,6 +562,8 @@ export async function encodeMsg({ group, message, nonce, api }) {
nonce: Aes.uniqueNonce().toString(),
encrypted_message,
checksum: 0,
+ from_memo_key: emptyPublicKey,
+ to_memo_key: emptyPublicKey,
}
}
diff --git a/golos-lib-js/test/messages.test.js b/golos-lib-js/test/messages.test.js
index 0c8e12a..fbc2a1a 100644
--- a/golos-lib-js/test/messages.test.js
+++ b/golos-lib-js/test/messages.test.js
@@ -1,6 +1,6 @@
import { assert } from 'chai';
import cloneDeep from 'lodash/cloneDeep';
-import { encode, decode, newTextMsg, newImageMsgAsync, makeQuoteMsg,
+import { encode, encodeMsg, decode, decodeMsgs, newTextMsg, newImageMsgAsync, makeQuoteMsg,
DEFAULT_APP, DEFAULT_VERSION, MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT,
MAX_TEXT_QUOTE_LENGTH, MAX_IMAGE_QUOTE_LENGTH } from '../src/auth/messages';
import th from './test_helper';
@@ -22,17 +22,19 @@ describe('golos.messages: encode()', function() {
await importNativeLib();
});
- it('input arguments', function() {
- assert.throws(() => encode(), 'from_private_memo_key is required');
- assert.throws(() => encode(alice.memo), 'to_public_memo_key is required');
- assert.throws(() => encode(alice.memo, bob.memo_pub), 'message is required');
+ it('input arguments', async function() {
+ await assert.isRejected(encodeMsg({}), 'msg is required');
+ await assert.isRejected(encodeMsg({ msg: 1 }), 'private_memo is required in private chats');
+ await assert.isRejected(encodeMsg({ msg: 1, private_memo: alice.memo }),
+ 'to_public_memo is required in private chats');
- assert.throws(() => encode(1, alice.memo, bob.memo_pub));
+ await assert.isRejected(encodeMsg({ msg: 1, private_memo: 1, to_public_memo: alice.memo }))
})
- it('normal case', function() {
+ it('normal case', async function() {
var msg = newTextMsg('Привет');
- var res = encode(alice.memo, bob.memo_pub, msg);
+ var res = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
assert.isString(res.nonce);
assert.isNotEmpty(res.nonce);
@@ -43,7 +45,7 @@ describe('golos.messages: encode()', function() {
assert.isNotEmpty(res.encrypted_message);
})
- it('cyrillic, emoji, etc', function() {
+ it('cyrillic, emoji, etc', async function() {
var veryLong = 'Очень';
for (let i = 0; i < 100; ++i) {
veryLong += ' длинный текст. Длинный текст.\nОчень';
@@ -63,7 +65,8 @@ describe('golos.messages: encode()', function() {
veryLong,
]) {
var msg = newTextMsg(str);
- var enc = encode(alice.memo, bob.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
msg.type = 'text';
var res = decode(bob.memo, alice.memo_pub, [enc]);
@@ -72,13 +75,17 @@ describe('golos.messages: encode()', function() {
}
})
- it('alice -> alice, bob', function() {
+ it('alice -> alice, bob', async function() {
var msg = newTextMsg('Привет');
- var enc = encode(alice.memo, bob.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
msg.type = 'text';
- var res = decode(alice.memo, bob.memo_pub, [enc]);
+ var res = await decodeMsgs({ private_memo: alice.memo,
+ msgs: [enc] });
assert.lengthOf(res, 1);
+ console.error(res[0].raw_message)
+ console.error(res[0].message)
assert.deepStrictEqual(res[0].message, msg);
var res = decode(bob.memo, alice.memo_pub, [enc]);
@@ -86,9 +93,10 @@ describe('golos.messages: encode()', function() {
assert.deepStrictEqual(res[0].message, msg);
})
- it('alice -> alice', function() {
+ it('alice -> alice', async function() {
var msg = newTextMsg('Привет');
- var enc = encode(alice.memo, alice.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: alice.memo_pub, msg })
msg.type = 'text';
var res = decode(alice.memo, alice.memo_pub, [enc]);
@@ -104,9 +112,10 @@ describe('golos.messages: encode()', function() {
assert.isNull(res[0].message);
})
- it('edit case', function() {
+ it('edit case', async function() {
var msg = newTextMsg('Привет');
- var enc = encode(alice.memo, bob.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
msg.type = 'text';
var res = decode(bob.memo, alice.memo_pub, [enc]);
@@ -114,7 +123,8 @@ describe('golos.messages: encode()', function() {
assert.deepStrictEqual(res[0].message, msg);
var msg = newTextMsg('Приветик');
- var enc = encode(alice.memo, bob.memo_pub, msg, enc.nonce);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg, nonce: enc.nonce })
msg.type = 'text';
var res2 = decode(bob.memo, alice.memo_pub, [enc]);
@@ -128,11 +138,13 @@ describe('golos.messages: encode()', function() {
describe('golos.messages: decode()', function() {
before(async function () {
var msg = newTextMsg('Привет');
- var msgEnc = encode(alice.memo, bob.memo_pub, msg);
+ var msgEnc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
msg.type = 'text'; // for comparison
var msg2 = await newImageMsgAsync(correctImageURL);
- var msgEnc2 = encode(alice.memo, bob.memo_pub, msg2);
+ var msgEnc2 = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: msg2 })
this._msgs = [
msg,
@@ -143,8 +155,10 @@ describe('golos.messages: decode()', function() {
this._msgObjs = [
msgEnc,
msgEnc2,
- encode(alice.memo, alice.memo_pub, {}),
- encode(alice.memo, bob.memo_pub, {}),
+ await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: alice.memo_pub, msg: {} }),
+ await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: {} }),
];
this._decoded = decode(bob.memo, alice.memo_pub, this._msgObjs);
@@ -163,8 +177,7 @@ describe('golos.messages: decode()', function() {
assert.throws(() => decode(alice.memo, null), 'second_user_public_memo_key is required');
assert.throws(() => decode(alice.memo, bob.memo_pub), 'message_objects is required');
- assert.throws(() => decode('wrong key', bob.memo_pub, []), 'Non-base58 character');
- assert.throws(() => decode(alice.memo, 'wrong key', []));
+ assert.throws(() => decode('wrong key', bob.memo_pub, [{}]), 'Non-base58 character');
})
it('validation', async function() {
@@ -178,7 +191,8 @@ describe('golos.messages: decode()', function() {
// non-decodable
{
- let msg = encode(alice.memo, alice.memo_pub, normalText);
+ let msg = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: alice.memo_pub, msg: normalText })
messages.push({
nonce: msg.nonce,
checksum: 1,
@@ -191,16 +205,20 @@ describe('golos.messages: decode()', function() {
return data; // as it is
});
- messages.push(encode(alice.memo, bob.memo_pub, 'не json'));
+ messages.push(await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: 'не json' }));
JSON.stringify.restore();
}
// JSON, but not object
- messages.push(encode(alice.memo, bob.memo_pub, 'Привет'));
+ messages.push(await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: 'Привет' }))
// no body
- messages.push(encode(alice.memo, bob.memo_pub, {}));
+ messages.push(await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: {} }));
// no app, version
- messages.push(encode(alice.memo, bob.memo_pub, {body: 'Привет'}));
+ messages.push(await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: {body: 'Привет'} }));
// normal text msgs
for (let i = 0; i < 50 - 5; ++i) {
@@ -308,25 +326,26 @@ describe('golos.messages: decode()', function() {
// normal
messages.push(normalImageEnc);
- var addMsg = ((breaker) => {
+ var addMsg = async (breaker) => {
var msg = Object.assign({}, normalImage);
breaker(msg);
- msg = encode(alice.memo, bob.memo_pub, msg);
+ msg = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
messages.push(msg);
- });
+ };
// no body
- addMsg(msg => delete msg.body);
+ await addMsg(msg => delete msg.body);
// no app
- addMsg(msg => delete msg.app);
+ await addMsg(msg => delete msg.app);
// previewWidth, previewHeight problems
- addMsg(msg => delete msg.previewWidth);
- addMsg(msg => msg.previewWidth = '12px');
- addMsg(msg => msg.previewWidth = 0);
- addMsg(msg => msg.previewWidth = MAX_PREVIEW_WIDTH + 1);
- addMsg(msg => delete msg.previewHeight);
- addMsg(msg => msg.previewHeight = '12px');
- addMsg(msg => msg.previewHeight = 0);
- addMsg(msg => msg.previewHeight = MAX_PREVIEW_HEIGHT + 1);
+ await addMsg(msg => delete msg.previewWidth);
+ await addMsg(msg => msg.previewWidth = '12px');
+ await addMsg(msg => msg.previewWidth = 0);
+ await addMsg(msg => msg.previewWidth = MAX_PREVIEW_WIDTH + 1);
+ await addMsg(msg => delete msg.previewHeight);
+ await addMsg(msg => msg.previewHeight = '12px');
+ await addMsg(msg => msg.previewHeight = 0);
+ await addMsg(msg => msg.previewHeight = MAX_PREVIEW_HEIGHT + 1);
var on_error = sandbox.spy();
var res = decode(bob.memo, alice.memo_pub, messages,
@@ -574,9 +593,10 @@ describe('golos.messages: decode()', function() {
})
describe('golos.messages: decode() replies', function() {
- it('message', function() {
+ it('message', async function() {
var msg = newTextMsg('Привет');
- var enc = encode(alice.memo, bob.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
const orig = Object.assign({
from: 'alice',
}, enc);
@@ -585,7 +605,8 @@ describe('golos.messages: decode() replies', function() {
var msg2 = newTextMsg('Hi');
msg2 = makeQuoteMsg(msg2, origDecoded[0]);
- var enc2 = encode(alice.memo, bob.memo_pub, msg2);
+ var enc2 = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: msg2 })
const reply = Object.assign({
from: 'bob',
}, enc2);
@@ -598,9 +619,10 @@ describe('golos.messages: decode() replies', function() {
assert.strictEqual(bothDecoded[1].message.quote.type, bothDecoded[0].message.type);
})
- it('too long message', function() {
+ it('too long message', async function() {
var msg = newTextMsg('a'.repeat(MAX_TEXT_QUOTE_LENGTH + 1));
- var enc = encode(alice.memo, bob.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
const orig = Object.assign({
from: 'alice',
}, enc);
@@ -611,7 +633,8 @@ describe('golos.messages: decode() replies', function() {
msg2 = makeQuoteMsg(msg2, origDecoded[0]);
// keep its original length to make message invalid
msg2.quote.body = 'a'.repeat(MAX_TEXT_QUOTE_LENGTH + 1);
- var enc2 = encode(alice.memo, bob.memo_pub, msg2);
+ var enc2 = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: msg2 })
const reply = Object.assign({
from: 'bob',
}, enc2);
@@ -626,7 +649,8 @@ describe('golos.messages: decode() replies', function() {
assert.isTrue(correctImageURL.length > MAX_TEXT_QUOTE_LENGTH, 'too short correctImageURL for this test');
var msg = await newImageMsgAsync(correctImageURL);
- var enc = encode(alice.memo, bob.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
const orig = Object.assign({
from: 'alice',
}, enc);
@@ -635,7 +659,8 @@ describe('golos.messages: decode() replies', function() {
var msg2 = newTextMsg('Hi');
msg2 = makeQuoteMsg(msg2, origDecoded[0]);
- var enc2 = encode(alice.memo, bob.memo_pub, msg2);
+ var enc2 = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: msg2 })
const reply = Object.assign({
from: 'bob',
}, enc2);
@@ -657,7 +682,8 @@ describe('golos.messages: decode() replies', function() {
previewWidth: MAX_PREVIEW_WIDTH,
previewHeight: MAX_PREVIEW_HEIGHT,
};
- var enc = encode(alice.memo, bob.memo_pub, msg);
+ var enc = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg })
const orig = Object.assign({
from: 'alice',
}, enc);
@@ -666,7 +692,8 @@ describe('golos.messages: decode() replies', function() {
var msg2 = newTextMsg('Hi');
msg2 = makeQuoteMsg(msg2, origDecoded[0]);
- var enc2 = encode(alice.memo, bob.memo_pub, msg2);
+ var enc2 = await encodeMsg({ private_memo: alice.memo,
+ to_public_memo: bob.memo_pub, msg: msg2 })
const reply = Object.assign({
from: 'bob',
}, enc2);