use Tangle data structure in feed format

This commit is contained in:
Andre Staltz 2023-04-14 16:54:41 +03:00
parent 6a19cd91a8
commit babfd5e39f
12 changed files with 177 additions and 303 deletions

View File

@ -5,6 +5,7 @@
const stringify = require('fast-json-stable-stringify') const stringify = require('fast-json-stable-stringify')
const ed25519 = require('ssb-keys/sodium') const ed25519 = require('ssb-keys/sodium')
const base58 = require('bs58') const base58 = require('bs58')
const union = require('set.prototype.union')
const { stripAuthor } = require('./strip') const { stripAuthor } = require('./strip')
const { getMsgId, getMsgHash } = require('./get-msg-id') const { getMsgId, getMsgHash } = require('./get-msg-id')
const representContent = require('./represent-content') const representContent = require('./represent-content')
@ -15,6 +16,7 @@ const {
validateBatch, validateBatch,
validateMsgHash, validateMsgHash,
} = require('./validation') } = require('./validation')
const Tangle = require('../tangle')
/** /**
* @typedef {Iterator<Msg> & {values: () => Iterator<Msg>}} MsgIter * @typedef {Iterator<Msg> & {values: () => Iterator<Msg>}} MsgIter
@ -51,7 +53,7 @@ const {
* @property {string} type * @property {string} type
* @property {number} when * @property {number} when
* @property {Keys} keys * @property {Keys} keys
* @property {Record<string, MsgIter>} tangles * @property {Record<string, Tangle>} tangles
*/ */
/** /**
@ -93,122 +95,6 @@ function toPlaintextBuffer(opts) {
return Buffer.from(stringify(opts.content), 'utf8') return Buffer.from(stringify(opts.content), 'utf8')
} }
function calculateDepth(existing, tangleId = null) {
let max = -1
for (const msg of existing.values()) {
const depth = msg.metadata.tangles[tangleId]?.depth ?? 0
if (depth > max) {
max = depth
}
}
return max + 1
}
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 determineTips(existing, tangleId = null) {
const tips = new Set()
for (const msg of existing.values()) {
tips.add(getMsgHash(msg))
}
for (const msg of existing.values()) {
const prev = msg.metadata.tangles[tangleId]?.prev ?? []
for (const p of prev) {
tips.delete(p)
}
}
return tips
}
function calculatePrev(existing, depth, lipmaaDepth, tangleId = null) {
const prev = []
const tips = determineTips(existing, tangleId)
for (const msg of existing.values()) {
const msgDepth = msg.metadata.tangles[tangleId]?.depth ?? 0
const msgHash = getMsgHash(msg)
if (
msgDepth === depth - 1 ||
msgDepth === lipmaaDepth ||
tips.has(msgHash)
) {
prev.push(msgHash)
}
}
return prev
}
/**
* @param {MsgIter} existing
* @param {string} tangleId
* @returns
*/
function prevalidateExisting(existing, tangleId) {
if (!existing?.[Symbol.iterator]) {
// prettier-ignore
return new Error(`existing must be an iterator, but got ${typeof existing}`)
}
if (typeof existing?.values !== 'function') {
// prettier-ignore
return new Error(`existing must be a Map, Set, or Array, but got ${existing}`)
}
if (!tangleId) {
// prettier-ignore
return new Error(`tangleId must be a string, but got ${typeof tangleId}`)
}
let isEmpty = true
let hasDepthZeroMsg = false
for (const p of existing.values()) {
isEmpty = false
if (!p.metadata) {
// prettier-ignore
return new Error(`existing must contain messages, but got ${typeof p}`)
}
if (!p.metadata.tangles[tangleId] && getMsgHash(p) === tangleId) {
if (hasDepthZeroMsg) {
// prettier-ignore
return new Error(`existing must contain only 1 message with depth 0`)
} else {
hasDepthZeroMsg = true
}
} else if (!p.metadata.tangles[tangleId]) {
// prettier-ignore
return new Error(`existing must refer to the tangleId ${tangleId}`)
}
}
if (!isEmpty && !hasDepthZeroMsg) {
// prettier-ignore
return new Error(`opts.existing must contain the message with depth 0`)
}
}
/** /**
* @param {CreateOpts} opts * @param {CreateOpts} opts
* @returns {Msg} * @returns {Msg}
@ -224,12 +110,11 @@ function create(opts) {
if (opts.tangles) { if (opts.tangles) {
for (const rootId in opts.tangles) { for (const rootId in opts.tangles) {
if ((err = validateMsgHash(rootId))) throw err if ((err = validateMsgHash(rootId))) throw err
const existing = opts.tangles[rootId] const tangle = opts.tangles[rootId]
if ((err = prevalidateExisting(existing, rootId))) throw err const depth = tangle.getMaxDepth() + 1
const tips = tangle.getTips()
const depth = calculateDepth(existing, rootId) const lipmaaSet = tangle.getLipmaaSet(depth)
const lipmaaDepth = lipmaa(depth + 1) - 1 const prev = [...union(lipmaaSet, tips)]
const prev = calculatePrev(existing, depth, lipmaaDepth, rootId)
tangles[rootId] = { depth, prev } tangles[rootId] = { depth, prev }
} }
} else { } else {

View File

@ -1,6 +1,7 @@
const base58 = require('bs58') const base58 = require('bs58')
const ed25519 = require('ssb-keys/sodium') const ed25519 = require('ssb-keys/sodium')
const stringify = require('fast-json-stable-stringify') const stringify = require('fast-json-stable-stringify')
const Tangle = require('../tangle')
function validateShape(msg) { function validateShape(msg) {
if (!msg || typeof msg !== 'object') { if (!msg || typeof msg !== 'object') {
@ -73,13 +74,23 @@ function validateSignature(msg) {
} }
} }
function validateTangle(msg, existingMsgs, tangleId) { /**
const tangle = msg.metadata.tangles[tangleId] *
if (!tangle?.prev || !Array.isArray(tangle.prev)) { * @param {any} msg
* @param {Tangle} tangle
* @param {*} tangleId
* @returns
*/
function validateTangle(msg, tangle, tangleId) {
if (!msg.metadata.tangles[tangleId]) {
return new Error('invalid message: must have metadata.tangles.' + tangleId)
}
const { depth, prev } = msg.metadata.tangles[tangleId]
if (!prev || !Array.isArray(prev)) {
// prettier-ignore // prettier-ignore
return new Error('invalid message: prev must be an array, on feed: ' + msg.metadata.who); return new Error('invalid message: prev must be an array, on feed: ' + msg.metadata.who);
} }
for (const p of tangle.prev) { for (const p of prev) {
if (typeof p !== 'string') { if (typeof p !== 'string') {
// prettier-ignore // prettier-ignore
return new Error('invalid message: prev must contain strings but found ' + p + ', on feed: ' + msg.metadata.who); return new Error('invalid message: prev must contain strings but found ' + p + ', on feed: ' + msg.metadata.who);
@ -89,18 +100,13 @@ function validateTangle(msg, existingMsgs, tangleId) {
return new Error('invalid message: prev must not contain URIs, on feed: ' + msg.metadata.who); return new Error('invalid message: prev must not contain URIs, on feed: ' + msg.metadata.who);
} }
if (!existingMsgs.has(p)) { if (!tangle.has(p)) {
// prettier-ignore // prettier-ignore
return new Error('invalid message: prev ' + p + ' is not locally known, on feed: ' + msg.metadata.who); return new Error('invalid message: prev ' + p + ' is not locally known, on feed: ' + msg.metadata.who);
} }
const existingMsg = existingMsgs.get(p) const prevDepth = tangle.getDepth(p)
if (existingMsg.metadata.type !== msg.metadata.type) { if (prevDepth >= depth) {
// prettier-ignore
return new Error('invalid message: prev ' + p + ' is not from the same type, on feed: ' + msg.metadata.who);
}
const existingDepth = existingMsg.metadata.tangles[tangleId]?.depth ?? 0
if (existingDepth >= tangle.depth) {
// prettier-ignore // prettier-ignore
return new Error('invalid message: depth of prev ' + p + ' is not lower, on feed: ' + msg.metadata.who); return new Error('invalid message: depth of prev ' + p + ' is not lower, on feed: ' + msg.metadata.who);
} }
@ -158,7 +164,7 @@ function validateContent(msg) {
// FIXME: validateDepth should be +1 of the max of prev depth // FIXME: validateDepth should be +1 of the max of prev depth
function validateSync(msg, existingMsgs, msgHash, rootHash) { function validateSync(msg, tangle, msgHash, rootHash) {
let err let err
if ((err = validateShape(msg))) return err if ((err = validateShape(msg))) return err
if ((err = validateWho(msg))) return err if ((err = validateWho(msg))) return err
@ -166,15 +172,15 @@ function validateSync(msg, existingMsgs, msgHash, rootHash) {
if (msgHash === rootHash) { if (msgHash === rootHash) {
if ((err = validateTangleRoot(msg))) return err if ((err = validateTangleRoot(msg))) return err
} else { } else {
if ((err = validateTangle(msg, existingMsgs, rootHash))) return err if ((err = validateTangle(msg, tangle, rootHash))) return err
} }
if ((err = validateContent(msg))) return err if ((err = validateContent(msg))) return err
if ((err = validateSignature(msg))) return err if ((err = validateSignature(msg))) return err
} }
function validate(msg, existingMsgs, msgHash, rootHash, cb) { function validate(msg, tangle, msgHash, rootHash, cb) {
let err let err
if ((err = validateSync(msg, existingMsgs, msgHash, rootHash))) { if ((err = validateSync(msg, tangle, msgHash, rootHash))) {
return cb(err) return cb(err)
} }
cb() cb()

View File

@ -4,6 +4,7 @@ const AAOL = require('async-append-only-log')
const promisify = require('promisify-4loc') const promisify = require('promisify-4loc')
const Obz = require('obz') const Obz = require('obz')
const FeedV1 = require('./feed-v1') const FeedV1 = require('./feed-v1')
const Tangle = require('./tangle')
const { ReadyGate, isEmptyObject } = require('./utils') const { ReadyGate, isEmptyObject } = require('./utils')
const { decrypt } = require('./encryption') const { decrypt } = require('./encryption')
@ -149,10 +150,10 @@ exports.init = function initDB(peer, config) {
function add(msg, tangleRootHash, cb) { function add(msg, tangleRootHash, cb) {
// TODO: optimize this. This may be slow if you're adding many msgs in a // TODO: optimize this. This may be slow if you're adding many msgs in a
// row, because it creates a new Map() each time. // row, because it creates a new Map() each time.
const tangleMsgs = populateTangle(tangleRootHash) const tangle = new Tangle(tangleRootHash, records())
const msgHash = FeedV1.getMsgHash(msg) const msgHash = FeedV1.getMsgHash(msg)
FeedV1.validate(msg, tangleMsgs, msgHash, tangleRootHash, validationCB) FeedV1.validate(msg, tangle, msgHash, tangleRootHash, validationCB)
function validationCB(err) { function validationCB(err) {
// prettier-ignore // prettier-ignore
@ -180,20 +181,10 @@ exports.init = function initDB(peer, config) {
return null return null
} }
function populateTangle(tangleId) {
const map = new Map()
for (const rec of records()) {
if (rec.hash === tangleId || rec.msg.metadata.tangles?.[tangleId]) {
map.set(rec.hash, rec.msg)
}
}
return map
}
function populateTangles(tangleIds) { function populateTangles(tangleIds) {
const tangles = {} const tangles = {}
for (const tangleId of tangleIds) { for (const tangleId of tangleIds) {
tangles[tangleId] ??= populateTangle(tangleId) tangles[tangleId] ??= new Tangle(tangleId, records())
} }
return tangles return tangles
} }
@ -325,5 +316,8 @@ exports.init = function initDB(peer, config) {
// internal // internal
findEncryptionFormatFor, findEncryptionFormatFor,
// mockable by tests
_getLog: () => log,
} }
} }

View File

@ -40,6 +40,11 @@ function compareMsgHashes(a, b) {
} }
class Tangle { class Tangle {
/**
* @type {string}
*/
#rootHash
/** /**
* @type {Set<string>} * @type {Set<string>}
*/ */
@ -70,23 +75,28 @@ class Tangle {
* @param {string} rootHash * @param {string} rootHash
* @param {Iterable<Rec>} recordsIter * @param {Iterable<Rec>} recordsIter
*/ */
constructor(rootHash, recordsIter) { constructor(rootHash, recordsIter = []) {
this.#rootHash = rootHash
this.#maxDepth = 0 this.#maxDepth = 0
for (const rec of recordsIter) { for (const rec of recordsIter) {
const msgHash = rec.hash this.add(rec.hash, rec.msg)
const tangles = rec.msg.metadata.tangles }
if (msgHash === rootHash) { }
add(msgHash, msg) {
const tangles = msg.metadata.tangles
if (msgHash === this.#rootHash) {
this.#tips.add(msgHash) this.#tips.add(msgHash)
this.#perDepth.set(0, [msgHash]) this.#perDepth.set(0, [msgHash])
this.#depth.set(msgHash, 0) this.#depth.set(msgHash, 0)
} else if (tangles[rootHash]) { } else if (tangles[this.#rootHash]) {
this.#tips.add(msgHash) this.#tips.add(msgHash)
const prev = tangles[rootHash].prev const prev = tangles[this.#rootHash].prev
for (const p of prev) { for (const p of prev) {
this.#tips.delete(p) this.#tips.delete(p)
} }
this.#prev.set(msgHash, prev) this.#prev.set(msgHash, prev)
const depth = tangles[rootHash].depth const depth = tangles[this.#rootHash].depth
if (depth > this.#maxDepth) this.#maxDepth = depth if (depth > this.#maxDepth) this.#maxDepth = depth
this.#depth.set(msgHash, depth) this.#depth.set(msgHash, depth)
const atDepth = this.#perDepth.get(depth) ?? [] const atDepth = this.#perDepth.get(depth) ?? []
@ -95,7 +105,6 @@ class Tangle {
this.#perDepth.set(depth, atDepth) this.#perDepth.set(depth, atDepth)
} }
} }
}
/** /**
* @param {number} depth * @param {number} depth

View File

@ -26,6 +26,7 @@
"obz": "^1.1.0", "obz": "^1.1.0",
"promisify-4loc": "^1.0.0", "promisify-4loc": "^1.0.0",
"push-stream": "^11.2.0", "push-stream": "^11.2.0",
"set.prototype.union": "^1.0.2",
"ssb-uri2": "^2.4.1" "ssb-uri2": "^2.4.1"
}, },
"devDependencies": { "devDependencies": {
@ -44,7 +45,7 @@
"tape": "^5.6.3" "tape": "^5.6.3"
}, },
"scripts": { "scripts": {
"test": "tape test/*.js | tap-arc --bail", "test": "tape test/*.test.js | tap-arc --bail",
"format-code": "prettier --write \"*.js\" \"(test|compat|indexes|operators)/*.js\"", "format-code": "prettier --write \"*.js\" \"(test|compat|indexes|operators)/*.js\"",
"format-code-staged": "pretty-quick --staged --pattern \"*.js\" --pattern \"(test|compat|indexes|operators)/*.js\"", "format-code-staged": "pretty-quick --staged --pattern \"*.js\" --pattern \"(test|compat|indexes|operators)/*.js\"",
"coverage": "c8 --reporter=lcov npm run test" "coverage": "c8 --reporter=lcov npm run test"

View File

@ -5,6 +5,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack') const SecretStack = require('secret-stack')
const caps = require('ssb-caps') const caps = require('ssb-caps')
const FeedV1 = require('../lib/feed-v1') const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const p = require('util').promisify const p = require('util').promisify
const { generateKeypair } = require('./util') const { generateKeypair } = require('./util')
@ -32,7 +33,7 @@ test('add()', async (t) => {
type: 'post', type: 'post',
content: { text: 'This is the first post!' }, content: { text: 'This is the first post!' },
tangles: { tangles: {
[rootHash]: new Map([[FeedV1.getMsgHash(rootMsg), rootMsg]]), [rootHash]: new Tangle(rootHash, [recRoot]),
}, },
}) })

View File

@ -6,6 +6,7 @@ const SecretStack = require('secret-stack')
const caps = require('ssb-caps') const caps = require('ssb-caps')
const p = require('util').promisify const p = require('util').promisify
const FeedV1 = require('../lib/feed-v1') const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util') const { generateKeypair } = require('./util')
const DIR = path.join(os.tmpdir(), 'ppppp-db-create') const DIR = path.join(os.tmpdir(), 'ppppp-db-create')
@ -72,9 +73,9 @@ test('add() forked then create() merged', async (t) => {
type: 'post', type: 'post',
content: { text: '3rd post forked from 1st' }, content: { text: '3rd post forked from 1st' },
tangles: { tangles: {
[rootHash]: new Map([ [rootHash]: new Tangle(rootHash, [
[rootHash, rootMsg], { hash: rootHash, msg: rootMsg },
[rec1.hash, rec1.msg], rec1,
]), ]),
}, },
}) })

View File

@ -1,5 +1,6 @@
const tape = require('tape') const tape = require('tape')
const FeedV1 = require('../lib/feed-v1') const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util') const { generateKeypair } = require('./util')
let rootMsg = null let rootMsg = null
@ -30,7 +31,7 @@ tape('FeedV1.create()', (t) => {
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: new Map([[rootHash, rootMsg]]), [rootHash]: new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }]),
}, },
when, when,
}) })
@ -71,9 +72,9 @@ tape('FeedV1.create()', (t) => {
content: content2, content: content2,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: new Map([ [rootHash]: new Tangle(rootHash, [
[rootHash, rootMsg], { hash: rootHash, msg: rootMsg },
[msgHash1, msg1], { hash: msgHash1, msg: msg1 },
]), ]),
}, },
when: when + 1, when: when + 1,
@ -111,14 +112,14 @@ tape('FeedV1.create()', (t) => {
tape('create() handles DAG tips correctly', (t) => { tape('create() handles DAG tips correctly', (t) => {
const keys = generateKeypair('alice') const keys = generateKeypair('alice')
const when = 1652037377204 const when = 1652037377204
const existing = new Map([[rootHash, rootMsg]]) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys, keys,
content: { text: '1' }, content: { text: '1' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 1, when: when + 1,
}) })
@ -129,14 +130,14 @@ tape('create() handles DAG tips correctly', (t) => {
'msg1.prev is root' 'msg1.prev is root'
) )
existing.set(msgHash1, msg1) tangle.add(msgHash1, msg1)
const msg2A = FeedV1.create({ const msg2A = FeedV1.create({
keys, keys,
content: { text: '2A' }, content: { text: '2A' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 2, when: when + 2,
}) })
@ -151,9 +152,8 @@ tape('create() handles DAG tips correctly', (t) => {
content: { text: '2B' }, content: { text: '2B' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
existing,
when: when + 2, when: when + 2,
}) })
const msgHash2B = FeedV1.getMsgHash(msg2B) const msgHash2B = FeedV1.getMsgHash(msg2B)
@ -163,14 +163,14 @@ tape('create() handles DAG tips correctly', (t) => {
'msg2B.prev is msg1' 'msg2B.prev is msg1'
) )
existing.set(msgHash2B, msg2B) tangle.add(msgHash2B, msg2B)
const msg3 = FeedV1.create({ const msg3 = FeedV1.create({
keys, keys,
content: { text: '3' }, content: { text: '3' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 3, when: when + 3,
}) })
@ -180,10 +180,10 @@ tape('create() handles DAG tips correctly', (t) => {
[rootHash, msgHash2B], [rootHash, msgHash2B],
'msg3.prev is root(lipmaa),msg2B(previous)' 'msg3.prev is root(lipmaa),msg2B(previous)'
) )
existing.set(msgHash3, msg3) tangle.add(msgHash3, msg3)
const msgHash2A = FeedV1.getMsgHash(msg2A) const msgHash2A = FeedV1.getMsgHash(msg2A)
existing.set(msgHash2A, msg2A) tangle.add(msgHash2A, msg2A)
t.pass('msg2A comes into awareness') t.pass('msg2A comes into awareness')
const msg4 = FeedV1.create({ const msg4 = FeedV1.create({
@ -191,7 +191,7 @@ tape('create() handles DAG tips correctly', (t) => {
content: { text: '4' }, content: { text: '4' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 4, when: when + 4,
}) })

View File

@ -1,6 +1,7 @@
const tape = require('tape') const tape = require('tape')
const base58 = require('bs58') const base58 = require('bs58')
const FeedV1 = require('../lib/feed-v1') const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util') const { generateKeypair } = require('./util')
tape('invalid msg with non-array prev', (t) => { tape('invalid msg with non-array prev', (t) => {
@ -9,21 +10,21 @@ tape('invalid msg with non-array prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
const existing = new Map([[rootHash, rootMsg]]) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg = FeedV1.create({ const msg = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030001000, when: 1652030001000,
}) })
msg.metadata.tangles[rootHash].prev = null msg.metadata.tangles[rootHash].prev = null
const msgHash = FeedV1.getMsgHash(msg) const msgHash = FeedV1.getMsgHash(msg)
FeedV1.validate(msg, existing, msgHash, rootHash, (err) => { FeedV1.validate(msg, tangle, msgHash, rootHash, (err) => {
t.ok(err, 'invalid 2nd msg throws') t.ok(err, 'invalid 2nd msg throws')
t.match(err.message, /prev must be an array/, 'invalid 2nd msg description') t.match(err.message, /prev must be an array/, 'invalid 2nd msg description')
t.end() t.end()
@ -36,26 +37,26 @@ tape('invalid msg with bad prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
const existing = new Map([[rootHash, rootMsg]]) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030001000, when: 1652030001000,
}) })
const msgHash1 = FeedV1.getMsgHash(msg1) const msgHash1 = FeedV1.getMsgHash(msg1)
existing.set(msgHash1, msg1) tangle.add(msgHash1, msg1)
const msg2 = FeedV1.create({ const msg2 = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030002000, when: 1652030002000,
}) })
@ -63,7 +64,7 @@ tape('invalid msg with bad prev', (t) => {
msg2.metadata.tangles[rootHash].prev = [1234] msg2.metadata.tangles[rootHash].prev = [1234]
const msgHash2 = FeedV1.getMsgHash(msg2) const msgHash2 = FeedV1.getMsgHash(msg2)
FeedV1.validate(msg2, existing, msgHash2, rootHash, (err) => { FeedV1.validate(msg2, tangle, msgHash2, rootHash, (err) => {
t.ok(err, 'invalid 2nd msg throws') t.ok(err, 'invalid 2nd msg throws')
t.match( t.match(
err.message, err.message,
@ -80,26 +81,26 @@ tape('invalid msg with URI in prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
const existing = new Map([[rootHash, rootMsg]]) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030001000, when: 1652030001000,
}) })
const msgHash1 = FeedV1.getMsgHash(msg1) const msgHash1 = FeedV1.getMsgHash(msg1)
existing.set(msgHash1, msg1) tangle.add(msgHash1, msg1)
const msg2 = FeedV1.create({ const msg2 = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030002000, when: 1652030002000,
}) })
@ -109,7 +110,7 @@ tape('invalid msg with URI in prev', (t) => {
msg2.metadata.tangles[rootHash].depth = 1 msg2.metadata.tangles[rootHash].depth = 1
msg2.metadata.tangles[rootHash].prev = [fakeMsgKey1] msg2.metadata.tangles[rootHash].prev = [fakeMsgKey1]
FeedV1.validate(msg2, existing, msgHash2, rootHash, (err) => { FeedV1.validate(msg2, tangle, msgHash2, rootHash, (err) => {
t.ok(err, 'invalid 2nd msg throws') t.ok(err, 'invalid 2nd msg throws')
t.match( t.match(
err.message, err.message,
@ -126,26 +127,26 @@ tape('invalid msg with unknown prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
const existing = new Map([[rootHash, rootMsg]]) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030001000, when: 1652030001000,
}) })
const msgHash1 = FeedV1.getMsgHash(msg1) const msgHash1 = FeedV1.getMsgHash(msg1)
existing.set(msgHash1, msg1) tangle.add(msgHash1, msg1)
const unknownMsg = FeedV1.create({ const unknownMsg = FeedV1.create({
keys, keys,
content: { text: 'Alien' }, content: { text: 'Alien' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030001000, when: 1652030001000,
}) })
@ -156,13 +157,16 @@ tape('invalid msg with unknown prev', (t) => {
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: new Map([[rootHash, rootMsg], [unknownMsgHash, unknownMsg]]), [rootHash]: new Tangle(rootHash, [
{ hash: rootHash, msg: rootMsg },
{ hash: unknownMsgHash, msg: unknownMsg },
]),
}, },
when: 1652030002000, when: 1652030002000,
}) })
const msgHash2 = FeedV1.getMsgHash(msg2) const msgHash2 = FeedV1.getMsgHash(msg2)
FeedV1.validate(msg2, existing, msgHash2, rootHash, (err) => { FeedV1.validate(msg2, tangle, msgHash2, rootHash, (err) => {
t.ok(err, 'invalid 2nd msg throws') t.ok(err, 'invalid 2nd msg throws')
t.match( t.match(
err.message, err.message,

View File

@ -1,28 +1,28 @@
const tape = require('tape') const tape = require('tape')
const FeedV1 = require('../lib/feed-v1') const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util') const { generateKeypair } = require('./util')
tape('lipmaa prevs', (t) => { tape('lipmaa prevs', (t) => {
const keys = generateKeypair('alice') const keys = generateKeypair('alice')
const content = { text: 'Hello world!' } const content = { text: 'Hello world!' }
const when = 1652037377204 const when = 1652037377204
const existing = new Map()
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
existing.set(rootHash, rootMsg) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys, keys,
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 1, when: when + 1,
}) })
const msgHash1 = FeedV1.getMsgHash(msg1) const msgHash1 = FeedV1.getMsgHash(msg1)
existing.set(msgHash1, msg1) tangle.add(msgHash1, msg1)
t.equals(msg1.metadata.tangles[rootHash].depth, 1, 'msg1 depth') t.equals(msg1.metadata.tangles[rootHash].depth, 1, 'msg1 depth')
t.deepEquals(msg1.metadata.tangles[rootHash].prev, [rootHash], 'msg1 prev') t.deepEquals(msg1.metadata.tangles[rootHash].prev, [rootHash], 'msg1 prev')
@ -31,30 +31,26 @@ tape('lipmaa prevs', (t) => {
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 2, when: when + 2,
}) })
const msgHash2 = FeedV1.getMsgHash(msg2) const msgHash2 = FeedV1.getMsgHash(msg2)
existing.set(msgHash2, msg2) tangle.add(msgHash2, msg2)
t.equals(msg2.metadata.tangles[rootHash].depth, 2, 'msg2 depth') t.equals(msg2.metadata.tangles[rootHash].depth, 2, 'msg2 depth')
t.deepEquals( t.deepEquals(msg2.metadata.tangles[rootHash].prev, [msgHash1], 'msg2 prev')
msg2.metadata.tangles[rootHash].prev,
[msgHash1],
'msg2 prev'
)
const msg3 = FeedV1.create({ const msg3 = FeedV1.create({
keys, keys,
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 3, when: when + 3,
}) })
const msgHash3 = FeedV1.getMsgHash(msg3) const msgHash3 = FeedV1.getMsgHash(msg3)
existing.set(msgHash3, msg3) tangle.add(msgHash3, msg3)
t.equals(msg3.metadata.tangles[rootHash].depth, 3, 'msg3 depth') t.equals(msg3.metadata.tangles[rootHash].depth, 3, 'msg3 depth')
t.deepEquals( t.deepEquals(
msg3.metadata.tangles[rootHash].prev, msg3.metadata.tangles[rootHash].prev,
@ -67,66 +63,54 @@ tape('lipmaa prevs', (t) => {
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 4, when: when + 4,
}) })
const msgHash4 = FeedV1.getMsgHash(msg4) const msgHash4 = FeedV1.getMsgHash(msg4)
existing.set(msgHash4, msg4) tangle.add(msgHash4, msg4)
t.equals(msg4.metadata.tangles[rootHash].depth, 4, 'msg4 depth') t.equals(msg4.metadata.tangles[rootHash].depth, 4, 'msg4 depth')
t.deepEquals( t.deepEquals(msg4.metadata.tangles[rootHash].prev, [msgHash3], 'msg4 prev')
msg4.metadata.tangles[rootHash].prev,
[msgHash3],
'msg4 prev'
)
const msg5 = FeedV1.create({ const msg5 = FeedV1.create({
keys, keys,
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 5, when: when + 5,
}) })
const msgHash5 = FeedV1.getMsgHash(msg5) const msgHash5 = FeedV1.getMsgHash(msg5)
existing.set(msgHash5, msg5) tangle.add(msgHash5, msg5)
t.equals(msg5.metadata.tangles[rootHash].depth, 5, 'msg5 depth') t.equals(msg5.metadata.tangles[rootHash].depth, 5, 'msg5 depth')
t.deepEquals( t.deepEquals(msg5.metadata.tangles[rootHash].prev, [msgHash4], 'msg5 prev')
msg5.metadata.tangles[rootHash].prev,
[msgHash4],
'msg5 prev'
)
const msg6 = FeedV1.create({ const msg6 = FeedV1.create({
keys, keys,
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 6, when: when + 6,
}) })
const msgHash6 = FeedV1.getMsgHash(msg6) const msgHash6 = FeedV1.getMsgHash(msg6)
existing.set(msgHash6, msg6) tangle.add(msgHash6, msg6)
t.equals(msg6.metadata.tangles[rootHash].depth, 6, 'msg6 depth') t.equals(msg6.metadata.tangles[rootHash].depth, 6, 'msg6 depth')
t.deepEquals( t.deepEquals(msg6.metadata.tangles[rootHash].prev, [msgHash5], 'msg6 prev')
msg6.metadata.tangles[rootHash].prev,
[msgHash5],
'msg6 prev'
)
const msg7 = FeedV1.create({ const msg7 = FeedV1.create({
keys, keys,
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: when + 7, when: when + 7,
}) })
const msgHash7 = FeedV1.getMsgHash(msg7) const msgHash7 = FeedV1.getMsgHash(msg7)
existing.set(msgHash7, msg7) tangle.add(msgHash7, msg7)
t.equals(msg7.metadata.tangles[rootHash].depth, 7, 'msg7 depth') t.equals(msg7.metadata.tangles[rootHash].depth, 7, 'msg7 depth')
t.deepEquals( t.deepEquals(
msg7.metadata.tangles[rootHash].prev, msg7.metadata.tangles[rootHash].prev,
@ -134,6 +118,5 @@ tape('lipmaa prevs', (t) => {
'msg7 prev (has lipmaa!)' 'msg7 prev (has lipmaa!)'
) )
t.end() t.end()
}) })

View File

@ -1,27 +1,26 @@
const tape = require('tape') const tape = require('tape')
const FeedV1 = require('../lib/feed-v1') const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util') const { generateKeypair } = require('./util')
tape('simple multi-author tangle', (t) => { tape('simple multi-author tangle', (t) => {
const keysA = generateKeypair('alice') const keysA = generateKeypair('alice')
const keysB = generateKeypair('bob') const keysB = generateKeypair('bob')
const existingA = new Map()
const existingB = new Map()
const rootMsgA = FeedV1.createRoot(keysA, 'post') const rootMsgA = FeedV1.createRoot(keysA, 'post')
const rootHashA = FeedV1.getMsgHash(rootMsgA) const rootHashA = FeedV1.getMsgHash(rootMsgA)
existingA.set(rootHashA, rootMsgA) const tangleA = new Tangle(rootHashA, [{ hash: rootHashA, msg: rootMsgA }])
const rootMsgB = FeedV1.createRoot(keysB, 'post') const rootMsgB = FeedV1.createRoot(keysB, 'post')
const rootHashB = FeedV1.getMsgHash(rootMsgB) const rootHashB = FeedV1.getMsgHash(rootMsgB)
existingB.set(rootHashB, rootMsgB) const tangleB = new Tangle(rootHashB, [{ hash: rootHashB, msg: rootMsgB }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys: keysA, keys: keysA,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHashA]: existingA, [rootHashA]: tangleA,
}, },
when: 1652030001000, when: 1652030001000,
}) })
@ -36,10 +35,9 @@ tape('simple multi-author tangle', (t) => {
keys: keysB, keys: keysB,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
existing: new Map(),
tangles: { tangles: {
[rootHashB]: existingB, [rootHashB]: tangleB,
[msgHash1]: new Map([[msgHash1, msg1]]), [msgHash1]: new Tangle(msgHash1, [{ hash: msgHash1, msg: msg1 }]),
}, },
when: 1652030002000, when: 1652030002000,
}) })
@ -72,48 +70,47 @@ tape('lipmaa in multi-author tangle', (t) => {
const content = { text: 'Hello world!' } const content = { text: 'Hello world!' }
const when = 1652037377204 const when = 1652037377204
const existingA = new Map()
const existingB = new Map()
const tangleExisting = new Map()
const rootMsgA = FeedV1.createRoot(keysA, 'post') const rootMsgA = FeedV1.createRoot(keysA, 'post')
const rootHashA = FeedV1.getMsgHash(rootMsgA) const rootHashA = FeedV1.getMsgHash(rootMsgA)
existingA.set(rootHashA, rootMsgA) const tangleA = new Tangle(rootHashA, [{ hash: rootHashA, msg: rootMsgA }])
const rootMsgB = FeedV1.createRoot(keysB, 'post') const rootMsgB = FeedV1.createRoot(keysB, 'post')
const rootHashB = FeedV1.getMsgHash(rootMsgB) const rootHashB = FeedV1.getMsgHash(rootMsgB)
existingB.set(rootHashB, rootMsgB) const tangleB = new Tangle(rootHashB, [{ hash: rootHashB, msg: rootMsgB }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys: keysA, keys: keysA,
content, content,
type: 'post', type: 'post',
tangles: { tangles: {
[rootHashA]: existingA, [rootHashA]: tangleA,
}, },
when: when + 1, when: when + 1,
}) })
const msgHash1 = FeedV1.getMsgHash(msg1) const msgHash1 = FeedV1.getMsgHash(msg1)
existingA.set(msgHash1, msg1) tangleA.add(msgHash1, msg1)
tangleExisting.set(msgHash1, msg1) const tangleThread = new Tangle(msgHash1, [{ hash: msgHash1, msg: msg1 }])
t.deepEquals(Object.keys(msg1.metadata.tangles),[rootHashA], 'A:msg1 has only feed tangle') t.deepEquals(
Object.keys(msg1.metadata.tangles),
[rootHashA],
'A:msg1 has only feed tangle'
)
const msg2 = FeedV1.create({ const msg2 = FeedV1.create({
keys: keysB, keys: keysB,
content, content,
type: 'post', type: 'post',
existing: existingB,
tangles: { tangles: {
[rootHashB]: existingB, [rootHashB]: tangleB,
[msgHash1]: tangleExisting, [msgHash1]: tangleThread,
}, },
when: when + 2, when: when + 2,
}) })
const msgHash2 = FeedV1.getMsgHash(msg2) const msgHash2 = FeedV1.getMsgHash(msg2)
existingB.set(msgHash2, msg2) tangleB.add(msgHash2, msg2)
tangleExisting.set(msgHash2, msg2) tangleThread.add(msgHash2, msg2)
t.deepEquals( t.deepEquals(
msg2.metadata.tangles[msgHash1].prev, msg2.metadata.tangles[msgHash1].prev,
@ -125,16 +122,15 @@ tape('lipmaa in multi-author tangle', (t) => {
keys: keysB, keys: keysB,
content, content,
type: 'post', type: 'post',
existing: existingB,
tangles: { tangles: {
[rootHashB]: existingB, [rootHashB]: tangleB,
[msgHash1]: tangleExisting, [msgHash1]: tangleThread,
}, },
when: when + 3, when: when + 3,
}) })
const msgHash3 = FeedV1.getMsgHash(msg3) const msgHash3 = FeedV1.getMsgHash(msg3)
existingB.set(msgHash3, msg3) tangleB.add(msgHash3, msg3)
tangleExisting.set(msgHash3, msg3) tangleThread.add(msgHash3, msg3)
t.deepEquals( t.deepEquals(
msg3.metadata.tangles[msgHash1].prev, msg3.metadata.tangles[msgHash1].prev,
@ -146,16 +142,15 @@ tape('lipmaa in multi-author tangle', (t) => {
keys: keysA, keys: keysA,
content, content,
type: 'post', type: 'post',
existing: existingA,
tangles: { tangles: {
[rootHashA]: existingA, [rootHashA]: tangleA,
[msgHash1]: tangleExisting, [msgHash1]: tangleThread,
}, },
when: when + 4, when: when + 4,
}) })
const msgHash4 = FeedV1.getMsgHash(msg4) const msgHash4 = FeedV1.getMsgHash(msg4)
existingB.set(msgHash4, msg4) tangleB.add(msgHash4, msg4)
tangleExisting.set(msgHash4, msg4) tangleThread.add(msgHash4, msg4)
t.deepEquals( t.deepEquals(
msg4.metadata.tangles[msgHash1].prev, msg4.metadata.tangles[msgHash1].prev,

View File

@ -1,17 +1,17 @@
const tape = require('tape') const tape = require('tape')
const base58 = require('bs58') const base58 = require('bs58')
const FeedV1 = require('../lib/feed-v1') const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util') const { generateKeypair } = require('./util')
tape('validate root msg', (t) => { tape('validate root msg', (t) => {
const keys = generateKeypair('alice') const keys = generateKeypair('alice')
const existing = new Map()
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
existing.set(rootHash, rootMsg) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
FeedV1.validate(rootMsg, existing, rootHash, rootHash, (err) => { FeedV1.validate(rootMsg, tangle, rootHash, rootHash, (err) => {
if (err) console.log(err) if (err) console.log(err)
t.error(err, 'valid root msg') t.error(err, 'valid root msg')
t.end() t.end()
@ -20,25 +20,24 @@ tape('validate root msg', (t) => {
tape('validate 2nd msg with existing root', (t) => { tape('validate 2nd msg with existing root', (t) => {
const keys = generateKeypair('alice') const keys = generateKeypair('alice')
const existing = new Map()
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
existing.set(rootHash, rootMsg) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030001000, when: 1652030001000,
}) })
const msgHash1 = FeedV1.getMsgHash(msg1) const msgHash1 = FeedV1.getMsgHash(msg1)
existing.set(msgHash1, msg1) tangle.add(msgHash1, msg1)
FeedV1.validate(msg1, existing, msgHash1, rootHash, (err) => { FeedV1.validate(msg1, tangle, msgHash1, rootHash, (err) => {
if (err) console.log(err) if (err) console.log(err)
t.error(err, 'valid 2nd msg') t.error(err, 'valid 2nd msg')
t.end() t.end()
@ -48,18 +47,16 @@ tape('validate 2nd msg with existing root', (t) => {
tape('validate 2nd forked msg', (t) => { tape('validate 2nd forked msg', (t) => {
const keys = generateKeypair('alice') const keys = generateKeypair('alice')
const existing = new Map()
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
existing.set(rootHash, rootMsg) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1A = FeedV1.create({ const msg1A = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
existing: new Map(), existing: new Map(),
when: 1652030001000, when: 1652030001000,
@ -71,15 +68,15 @@ tape('validate 2nd forked msg', (t) => {
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030002000, when: 1652030002000,
}) })
const msgHash1B = FeedV1.getMsgHash(msg1B) const msgHash1B = FeedV1.getMsgHash(msg1B)
existing.set(msgHash1A, msg1A) tangle.add(msgHash1A, msg1A)
existing.set(msgHash1B, msg1B) tangle.add(msgHash1B, msg1B)
FeedV1.validate(msg1B, existing, msgHash1B, rootHash, (err) => { FeedV1.validate(msg1B, tangle, msgHash1B, rootHash, (err) => {
if (err) console.log(err) if (err) console.log(err)
t.error(err, 'valid 2nd forked msg') t.error(err, 'valid 2nd forked msg')
t.end() t.end()
@ -89,18 +86,16 @@ tape('validate 2nd forked msg', (t) => {
tape('invalid msg with unknown previous', (t) => { tape('invalid msg with unknown previous', (t) => {
const keys = generateKeypair('alice') const keys = generateKeypair('alice')
const existing = new Map()
const rootMsg = FeedV1.createRoot(keys, 'post') const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg) const rootHash = FeedV1.getMsgHash(rootMsg)
existing.set(rootHash, rootMsg) const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const msg1 = FeedV1.create({ const msg1 = FeedV1.create({
keys, keys,
content: { text: 'Hello world!' }, content: { text: 'Hello world!' },
type: 'post', type: 'post',
tangles: { tangles: {
[rootHash]: existing, [rootHash]: tangle,
}, },
when: 1652030001000, when: 1652030001000,
}) })
@ -110,7 +105,7 @@ tape('invalid msg with unknown previous', (t) => {
msg1.metadata.tangles[rootHash].prev = [fakeMsgHash] msg1.metadata.tangles[rootHash].prev = [fakeMsgHash]
FeedV1.validate(msg1, existing, msgHash1, rootHash, (err) => { FeedV1.validate(msg1, tangle, msgHash1, rootHash, (err) => {
t.ok(err, 'invalid 2nd msg throws') t.ok(err, 'invalid 2nd msg throws')
t.match( t.match(
err.message, err.message,