From 1f53758fe0555f917da587d27ed1e5d81e652386 Mon Sep 17 00:00:00 2001 From: Andre Staltz Date: Tue, 4 Apr 2023 13:44:33 +0300 Subject: [PATCH] add lipmaa prev --- lib/encryption.js | 5 +- lib/feed-v1/index.js | 89 +++++++++++++++++++++++++------ lib/plugin.js | 48 +++++++++++------ test/add.test.js | 3 +- test/create.test.js | 11 +--- test/feed-v1-encode.test.js | 12 +++-- test/feed-v1-invalid-prev.test.js | 70 +++++++++--------------- test/feed-v1-invalid-type.test.js | 15 ++++-- test/feed-v1-lipmaa.test.js | 86 +++++++++++++++++++++++++++++ test/feed-v1-validate.test.js | 44 +++++++++------ 10 files changed, 266 insertions(+), 117 deletions(-) create mode 100644 test/feed-v1-lipmaa.test.js diff --git a/lib/encryption.js b/lib/encryption.js index 7de3ee0..94be0a0 100644 --- a/lib/encryption.js +++ b/lib/encryption.js @@ -17,8 +17,7 @@ function ciphertextStrToBuffer(str) { */ function decrypt(rec, peer, config) { const msgEncrypted = rec.msg - const { metadata, content } = msgEncrypted - const { who, prev } = metadata + const { content } = msgEncrypted if (typeof content !== 'string') return rec const encryptionFormat = peer.db.findEncryptionFormatFor(content) @@ -26,7 +25,7 @@ function decrypt(rec, peer, config) { // Decrypt const ciphertextBuf = ciphertextStrToBuffer(content) - const opts = { keys: config.keys, author: who, previous: prev } + const opts = { keys: config.keys } const plaintextBuf = encryptionFormat.decrypt(ciphertextBuf, opts) if (!plaintextBuf) return rec diff --git a/lib/feed-v1/index.js b/lib/feed-v1/index.js index 34b6154..760e783 100644 --- a/lib/feed-v1/index.js +++ b/lib/feed-v1/index.js @@ -5,7 +5,7 @@ const stringify = require('fast-json-stable-stringify') const ed25519 = require('ssb-keys/sodium') const base58 = require('bs58') -const { stripAuthor, stripMsgKey } = require('./strip') +const { stripAuthor } = require('./strip') const { getMsgId, getMsgHash } = require('./get-msg-id') const representContent = require('./represent-content') const { @@ -31,6 +31,18 @@ const { * @property {string} sig */ +/** + * @typedef {Object} CreateOpts + * @property {*} content + * @property {string} type + * @property {number} when + * @property {Object} keys + * @property {string} keys.id + * @property {string} keys.private + * @property {Iterator & {values: () => Iterator}} existing + * @property {Iterator & {values: () => Iterator}} tips + */ + /** * @param {Msg} msg */ @@ -62,47 +74,90 @@ function toPlaintextBuffer(opts) { return Buffer.from(stringify(opts.content), 'utf8') } -function calculateDepth(prev) { - let max = -1; - for (const p of prev) { +function calculateDepth(tips) { + let max = -1 + for (const p of tips.values()) { if (p.metadata.depth > max) { - max = p.metadata.depth; + max = p.metadata.depth } } return max + 1 } -function summarizePrev(prev) { - return Array.from(prev).map(getMsgHash) +function lipmaa(n) { + let m = 1 + let po3 = 3 + let u = n + + // find k such that (3^k - 1)/2 >= n + while (m < n) { + po3 *= 3 + m = (po3 - 1) / 2 + } + + // find longest possible backjump + po3 /= 3 + if (m !== n) { + while (u !== 0) { + m = (po3 - 1) / 2 + po3 /= 3 + u %= m + } + + if (m !== po3) { + po3 = m + } + } + + return n - po3 } -function prevalidatePrev(prev) { - if (prev && !prev[Symbol.iterator]) { - // prettier-ignore - throw new Error('opts.prev must be an iterator, but got ' + typeof prev) +function calculatePrev(existing, depth, lipmaaDepth) { + const prev = [] + for (const msg of existing.values()) { + const msgDepth = msg.metadata.depth + if (msgDepth === lipmaaDepth || msgDepth === depth - 1) { + prev.push(getMsgHash(msg)) + } } - for (const p of prev) { + return prev +} + +function prevalidatePrevious(prev, name) { + if (!prev?.[Symbol.iterator]) { + // prettier-ignore + throw new Error(`opts.${name} must be an iterator, but got ${typeof prev}`) + } + if (typeof prev?.values !== 'function') { + // prettier-ignore + throw new Error(`opts.${name} must be a Map, Set, or Array, but got ${prev}`) + } + for (const p of prev.values()) { if (!p.metadata) { - throw new Error('opts.prev must contain messages, but got ' + typeof p) + throw new Error(`opts.${name} must contain messages, but got ${typeof p}`) } } } + /** - * @param {*} opts + * @param {CreateOpts} opts * @returns {Msg} */ function create(opts) { let err if ((err = validateType(opts.type))) throw err - prevalidatePrev(opts.prev) + prevalidatePrevious(opts.existing, 'existing') + prevalidatePrevious(opts.tips, 'tips') const [proof, size] = representContent(opts.content) - const depth = calculateDepth(opts.prev) + const depth = calculateDepth(opts.tips) + const lipmaaDepth = lipmaa(depth + 1) - 1 + const prev = calculatePrev(opts.existing, depth, lipmaaDepth) const msg = { content: opts.content, metadata: { depth, - prev: summarizePrev(opts.prev), + prev, proof, size, type: opts.type, diff --git a/lib/plugin.js b/lib/plugin.js index 01e42e8..f2b0362 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -49,8 +49,8 @@ exports.init = function initDB(peer, config) { const onRecordAdded = Obz() const msgsPerFeed = { - _mapAll: new Map(), // who => Set - _mapTips: new Map(), // who => Set + _mapAll: new Map(), // who => Set + _mapTips: new Map(), // who => Set _byHash: new Map(), // msgId => Msg // TODO: optimize space usage of this?? update(msg, msgId) { const msgHash = FeedV1.getMsgHash(msgId ?? msg) @@ -58,28 +58,37 @@ exports.init = function initDB(peer, config) { const setAll = this._mapAll.get(feedId) ?? new Set() const setTips = this._mapTips.get(feedId) ?? new Set() for (const p of msg.metadata.prev) { - const prevMsg = this._byHash.get(p) - setTips.delete(prevMsg) + setTips.delete(p) } - setAll.add(msg) - setTips.add(msg) + setAll.add(msgHash) + setTips.add(msgHash) this._mapTips.set(feedId, setTips) this._mapAll.set(feedId, setAll) this._byHash.set(msgHash, msg) }, - getAll() { - return this._byHash + getAll(feedId) { + const map = new Map() + for (const msgHash of this._mapAll.get(feedId) ?? []) { + const msg = this._byHash.get(msgHash) + if (msg) map.set(msgHash, msg) + } + return map }, getTips(feedId) { - return this._mapTips.get(feedId) ?? [] + const map = new Map() + for (const msgHash of this._mapTips.get(feedId) ?? []) { + const msg = this._byHash.get(msgHash) + if (msg) map.set(msgHash, msg) + } + return map }, deleteMsg(msg) { const feedId = FeedV1.getFeedId(msg) const msgHash = FeedV1.getMsgHash(msg) const setAll = this._mapAll.get(feedId) - setAll.delete(msg) + setAll.delete(msgHash) const setTips = this._mapTips.get(feedId) - setTips.delete(msg) + setTips.delete(msgHash) this._byHash.delete(msgHash) }, } @@ -187,6 +196,8 @@ exports.init = function initDB(peer, config) { function add(msg, cb) { const feedId = FeedV1.getFeedId(msg) + // TODO: optimize this. This may be slow if you're adding many msgs in a + // row, because `getAll()` creates a new Map() each time. const existingMsgs = msgsPerFeed.getAll(feedId) FeedV1.validate(msg, existingMsgs, validationCB) @@ -223,19 +234,26 @@ exports.init = function initDB(peer, config) { // Create full opts: let tempMsg try { - tempMsg = FeedV1.create({ when: Date.now(), ...opts, prev: [], keys }) + tempMsg = FeedV1.create({ + when: Date.now(), + ...opts, + existing: [], + tips: [], + keys, + }) } catch (err) { return cb(new Error('create() failed', { cause: err })) } const feedId = FeedV1.getFeedId(tempMsg) - const prev = msgsPerFeed.getTips(feedId) - const fullOpts = { when: Date.now(), ...opts, prev, keys } + const existing = msgsPerFeed.getAll(feedId) + const tips = msgsPerFeed.getTips(feedId) + const fullOpts = { when: Date.now(), ...opts, existing, tips, keys } // If opts ask for encryption, encrypt and put ciphertext in opts.content const recps = fullOpts.content.recps if (Array.isArray(recps) && recps.length > 0) { const plaintext = FeedV1.toPlaintextBuffer(fullOpts) - const encryptOpts = { ...fullOpts, keys, recps, prev } + const encryptOpts = { ...fullOpts, recps } let ciphertextBuf try { ciphertextBuf = encryptionFormat.encrypt(plaintext, encryptOpts) diff --git a/test/add.test.js b/test/add.test.js index c36f9e3..1a04e5e 100644 --- a/test/add.test.js +++ b/test/add.test.js @@ -25,7 +25,8 @@ test('add()', async (t) => { when: 1514517067954, type: 'post', content: { text: 'This is the first post!' }, - prev: [], + existing: [], + tips: [], }) const rec = await p(peer.db.add)(inputMsg) diff --git a/test/create.test.js b/test/create.test.js index ed05cd2..0c04d0b 100644 --- a/test/create.test.js +++ b/test/create.test.js @@ -48,7 +48,8 @@ test('add() forked then create() merged', async (t) => { when: Date.now(), type: 'post', content: { text: '3rd post forked from 1st' }, - prev: [rec1.msg], + existing: [rec1.msg], + tips: [rec1.msg], }) const rec3 = await p(peer.db.add)(msg3) @@ -64,14 +65,6 @@ test('add() forked then create() merged', async (t) => { [msgHash2, msgHash3], 'msg4 prev is msg2 and msg3' ) - const msgHash4 = FeedV1.getMsgHash(rec4.msg) - - const rec5 = await p(peer.db.create)({ - type: 'post', - content: { text: 'I am 5th post' }, - }) - t.ok(rec5, '5th post created') - t.deepEquals(rec5.msg.metadata.prev, [msgHash4], 'msg5 prev is msg4') }) test('create() encrypted with box', async (t) => { diff --git a/test/feed-v1-encode.test.js b/test/feed-v1-encode.test.js index 18d74ac..065a2b8 100644 --- a/test/feed-v1-encode.test.js +++ b/test/feed-v1-encode.test.js @@ -11,7 +11,8 @@ tape('encode/decode works', (t) => { keys, content, type: 'post', - prev: [], + existing: [], + tips: [], when, }) t.deepEquals( @@ -34,12 +35,12 @@ tape('encode/decode works', (t) => { console.log(msg1) - const msgHash = '9cYegpVpddoMSdvSf53dTH' + const msgHash1 = '9cYegpVpddoMSdvSf53dTH' t.equals( FeedV1.getMsgId(msg1), 'ppppp:message/v1/4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW/post/' + - msgHash, + msgHash1, 'getMsgId' ) @@ -49,7 +50,8 @@ tape('encode/decode works', (t) => { keys, content: content2, type: 'post', - prev: [msg1], + existing: new Map([[msgHash1, msg1]]), + tips: new Map([[msgHash1, msg1]]), when: when + 1, }) t.deepEquals( @@ -64,7 +66,7 @@ tape('encode/decode works', (t) => { ) t.equals(msg2.metadata.type, 'post', 'metadata.type') t.equals(msg2.metadata.depth, 1, 'metadata.depth') - t.deepEquals(msg2.metadata.prev, [msgHash], 'metadata.prev') + t.deepEquals(msg2.metadata.prev, [msgHash1], 'metadata.prev') t.deepEquals(msg2.metadata.proof, 'XuZEzH1Dhy1yuRMcviBBcN', 'metadata.proof') t.deepEquals(msg2.metadata.size, 21, 'metadata.size') t.equals(typeof msg2.metadata.when, 'number', 'metadata.when') diff --git a/test/feed-v1-invalid-prev.test.js b/test/feed-v1-invalid-prev.test.js index e9106e0..04d2d2d 100644 --- a/test/feed-v1-invalid-prev.test.js +++ b/test/feed-v1-invalid-prev.test.js @@ -10,7 +10,8 @@ tape('invalid 1st msg with non-empty prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [{ metadata: { depth: 10 }, sig: 'fake' }], + existing: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), + tips: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), when: 1652030001000, }) @@ -32,7 +33,8 @@ tape('invalid 1st msg with non-array prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) msg.metadata.prev = null @@ -51,7 +53,8 @@ tape('invalid msg with non-array prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) const msgHash1 = FeedV1.getMsgHash(msg1) @@ -60,7 +63,8 @@ tape('invalid msg with non-array prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [{ metadata: { depth: 10 }, sig: 'fake' }], + existing: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), + tips: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), when: 1652030002000, }) msg2.metadata.prev = null @@ -85,16 +89,18 @@ tape('invalid msg with bad prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) -const msgHash1 = FeedV1.getMsgHash(msg1) + const msgHash1 = FeedV1.getMsgHash(msg1) const msg2 = FeedV1.create({ keys, content: { text: 'Hello world!' }, type: 'post', - prev: [{ metadata: { depth: 10 }, sig: 'fake' }], + existing: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), + tips: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), when: 1652030002000, }) msg2.metadata.prev = [1234] @@ -119,7 +125,8 @@ tape('invalid msg with URI in prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) const msgHash1 = FeedV1.getMsgHash(msg1) @@ -128,7 +135,8 @@ tape('invalid msg with URI in prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [{ metadata: { depth: 10 }, sig: 'fake' }], + existing: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), + tips: new Map([['1234', { metadata: { depth: 10 }, sig: 'fake' }]]), when: 1652030002000, }) const randBuf = Buffer.alloc(16).fill(16) @@ -155,7 +163,8 @@ tape('invalid msg with unknown prev', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) const msgHash1 = FeedV1.getMsgHash(msg1) @@ -164,15 +173,18 @@ tape('invalid msg with unknown prev', (t) => { keys, content: { text: 'Alien' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) + const unknownMsgHash = FeedV1.getMsgHash(unknownMsg) const msg2 = FeedV1.create({ keys, content: { text: 'Hello world!' }, type: 'post', - prev: [unknownMsg], + existing: new Map([[unknownMsgHash, unknownMsg]]), + tips: new Map([[unknownMsgHash, unknownMsg]]), when: 1652030002000, }) @@ -188,37 +200,3 @@ tape('invalid msg with unknown prev', (t) => { t.end() }) }) - -tape('invalid msg with unknown prev in a Set', (t) => { - const keys = generateKeypair('alice') - - const msg1 = FeedV1.create({ - keys, - content: { text: 'Hello world!' }, - type: 'post', - prev: [], - when: 1652030001000, - }) - - const msg2 = FeedV1.create({ - keys, - content: { text: 'Hello world!' }, - type: 'post', - prev: [{ metadata: { depth: 10 }, sig: 'fake' }], - when: 1652030002000, - }) - const fakeMsgKey1 = base58.encode(Buffer.alloc(16).fill(42)) - msg2.metadata.prev = [fakeMsgKey1] - - const existing = new Set([msg1]) - - FeedV1.validate(msg2, existing, (err) => { - t.ok(err, 'invalid 2nd msg throws') - t.match( - err.message, - /prev .+ is not locally known/, - 'invalid 2nd msg description' - ) - t.end() - }) -}) diff --git a/test/feed-v1-invalid-type.test.js b/test/feed-v1-invalid-type.test.js index 5585e5e..648f41c 100644 --- a/test/feed-v1-invalid-type.test.js +++ b/test/feed-v1-invalid-type.test.js @@ -12,7 +12,8 @@ tape('invalid type not a string', (t) => { content: { text: 'Hello world!' }, when: 1652037377204, type: 123, - prev: [], + existing: new Map(), + tips: new Map(), }) }, /type is not a string/, @@ -31,7 +32,8 @@ tape('invalid type with "/" character', (t) => { content: { text: 'Hello world!' }, when: 1652037377204, type: 'group/init', - prev: [], + existing: new Map(), + tips: new Map(), }) }, /invalid type/, @@ -50,7 +52,8 @@ tape('invalid type with "*" character', (t) => { content: { text: 'Hello world!' }, when: 1652037377204, type: 'star*', - prev: [], + existing: new Map(), + tips: new Map(), }) }, /invalid type/, @@ -69,7 +72,8 @@ tape('invalid type too short', (t) => { content: { text: 'Hello world!' }, when: 1652037377204, type: 'xy', - prev: [], + existing: new Map(), + tips: new Map(), }) }, /shorter than 3/, @@ -88,7 +92,8 @@ tape('invalid type too long', (t) => { content: { text: 'Hello world!' }, when: 1652037377204, type: 'a'.repeat(120), - prev: [], + existing: new Map(), + tips: new Map(), }) }, /100\+ characters long/, diff --git a/test/feed-v1-lipmaa.test.js b/test/feed-v1-lipmaa.test.js new file mode 100644 index 0000000..9b9787b --- /dev/null +++ b/test/feed-v1-lipmaa.test.js @@ -0,0 +1,86 @@ +const tape = require('tape') +const FeedV1 = require('../lib/feed-v1') +const { generateKeypair } = require('./util') + +tape('lipmaa prevs', (t) => { + const keys = generateKeypair('alice') + const content = { text: 'Hello world!' } + const when = 1652037377204 + const existing = new Map() + const tips = new Map() + + const msg1 = FeedV1.create({ + keys, + content, + type: 'post', + existing: new Map(), + tips: new Map(), + when: when + 1, + }) + const msgHash1 = FeedV1.getMsgHash(msg1) + existing.set(msgHash1, msg1) + tips.set(msgHash1, msg1) + t.deepEquals(msg1.metadata.prev, [], 'msg1.prev is empty') + + const msg2 = FeedV1.create({ + keys, + content, + type: 'post', + existing, + tips, + when: when + 2, + }) + const msgHash2 = FeedV1.getMsgHash(msg2) + existing.set(msgHash2, msg2) + tips.set(msgHash2, msg2) + tips.delete(msgHash1) + t.deepEquals(msg2.metadata.prev, [msgHash1], 'msg2.prev is msg1') + + const msg3 = FeedV1.create({ + keys, + content, + type: 'post', + existing, + tips, + when: when + 3, + }) + const msgHash3 = FeedV1.getMsgHash(msg3) + existing.set(msgHash3, msg3) + tips.set(msgHash3, msg3) + tips.delete(msgHash2) + t.deepEquals(msg3.metadata.prev, [msgHash2], 'msg3.prev is msg2') + + const msg4 = FeedV1.create({ + keys, + content, + type: 'post', + existing, + tips, + when: when + 4, + }) + const msgHash4 = FeedV1.getMsgHash(msg4) + existing.set(msgHash4, msg4) + tips.set(msgHash4, msg4) + tips.delete(msgHash3) + t.deepEquals( + msg4.metadata.prev, + [msgHash1, msgHash3], + 'msg4.prev is msg1 and msg3' + ) + + const msg5 = FeedV1.create({ + keys, + content, + type: 'post', + existing, + tips, + when: when + 5, + }) + const msgHash5 = FeedV1.getMsgHash(msg5) + existing.set(msgHash5, msg5) + tips.set(msgHash5, msg5) + tips.delete(msgHash4) + t.deepEquals(msg5.metadata.prev, [msgHash4], 'msg5.prev is msg4') + + t.end() +}) diff --git a/test/feed-v1-validate.test.js b/test/feed-v1-validate.test.js index d0dc47c..9e56285 100644 --- a/test/feed-v1-validate.test.js +++ b/test/feed-v1-validate.test.js @@ -10,7 +10,8 @@ tape('validate 1st msg', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) @@ -28,7 +29,8 @@ tape('validate 2nd msg with existing nativeMsg', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) const msgHash1 = FeedV1.getMsgHash(msg1) @@ -37,7 +39,8 @@ tape('validate 2nd msg with existing nativeMsg', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [msg1], + existing: new Map([[msgHash1, msg1]]), + tips: new Map([[msgHash1, msg1]]), when: 1652030002000, }) @@ -58,16 +61,18 @@ tape('validate 2nd msg with existing msgId', (t) => { content: { text: 'Hello world!' }, type: 'post', prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) - const msgKey1 = FeedV1.getMsgId(msg1) -const msgHash1 = FeedV1.getMsgHash(msg1) + const msgHash1 = FeedV1.getMsgHash(msg1) const msg2 = FeedV1.create({ keys, content: { text: 'Hello world!' }, type: 'post', - prev: [msg1], + existing: new Map([[msgHash1, msg1]]), + tips: new Map([[msgHash1, msg1]]), when: 1652030002000, }) @@ -87,22 +92,24 @@ tape('validate 2nd msg with existing KVT', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) -const msgHash1 = FeedV1.getMsgHash(msg1) + const msgHash1 = FeedV1.getMsgHash(msg1) const msg2 = FeedV1.create({ keys, content: { text: 'Hello world!' }, type: 'post', - prev: [msg1], + existing: new Map([[msgHash1, msg1]]), + tips: new Map([[msgHash1, msg1]]), when: 1652030002000, }) const existing = new Map() existing.set(msgHash1, msg1) - FeedV1.validate(msg2,existing, (err) => { + FeedV1.validate(msg2, existing, (err) => { if (err) console.log(err) t.error(err, 'valid 2nd msg') t.end() @@ -116,7 +123,8 @@ tape('validate 2nd forked msg', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) const msgHash1 = FeedV1.getMsgHash(msg1) @@ -125,7 +133,8 @@ tape('validate 2nd forked msg', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [msg1], + existing: new Map([[msgHash1, msg1]]), + tips: new Map([[msgHash1, msg1]]), when: 1652030002000, }) const msgHash2A = FeedV1.getMsgHash(msg2A) @@ -134,7 +143,8 @@ tape('validate 2nd forked msg', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [msg1], + existing: new Map([[msgHash1, msg1]]), + tips: new Map([[msgHash1, msg1]]), when: 1652030003000, }) @@ -155,10 +165,11 @@ tape('invalid msg with unknown previous', (t) => { keys, content: { text: 'Hello world!' }, type: 'post', - prev: [], + existing: new Map(), + tips: new Map(), when: 1652030001000, }) -const msgHash1 = FeedV1.getMsgHash(msg1) + const msgHash1 = FeedV1.getMsgHash(msg1) const fakeMsgKey1 = base58.encode(Buffer.alloc(16).fill(42)) @@ -166,7 +177,8 @@ const msgHash1 = FeedV1.getMsgHash(msg1) keys, content: { text: 'Hello world!' }, type: 'post', - prev: [msg1], + existing: new Map([[msgHash1, msg1]]), + tips: new Map([[msgHash1, msg1]]), when: 1652030002000, }) msg2.metadata.prev = [fakeMsgKey1]