Tangle class belongs to FeedV1

This commit is contained in:
Andre Staltz 2023-04-17 16:25:43 +03:00
parent 0917361305
commit d2023b5bc0
13 changed files with 105 additions and 87 deletions

View File

@ -16,8 +16,14 @@ const {
validateBatch,
validateMsgHash,
} = require('./validation')
const { isEmptyObject } = require('../utils')
const Tangle = require('../tangle')
const Tangle = require('./tangle')
function isEmptyObject(obj) {
for (const _key in obj) {
return false
}
return true
}
/**
* @typedef {Iterator<Msg> & {values: () => Iterator<Msg>}} MsgIter
@ -215,6 +221,7 @@ module.exports = {
stripAuthor,
toPlaintextBuffer,
fromPlaintextBuffer,
Tangle,
validate,
validateBatch,
}

View File

@ -1,9 +1,5 @@
/**
* @typedef {import("./plugin").Rec} Rec
*/
/**
* @typedef {import("./feed-v1").Msg} Msg
* @typedef {import("./index").Msg} Msg
*/
function lipmaa(n) {
@ -80,16 +76,12 @@ class Tangle {
#maxDepth
/**
*
* @param {string} rootHash
* @param {Iterable<Rec>} recordsIter
* @param {Iterable<Msg>} msgsIter
*/
constructor(rootHash, recordsIter = []) {
constructor(rootHash) {
this.#rootHash = rootHash
this.#maxDepth = 0
for (const rec of recordsIter) {
this.add(rec.hash, rec.msg)
}
}
add(msgHash, msg) {
@ -184,7 +176,7 @@ class Tangle {
return { type, who }
}
#shortestPathToRoot(msgHash) {
shortestPathToRoot(msgHash) {
const path = []
let current = msgHash
while (true) {
@ -207,16 +199,6 @@ class Tangle {
return path
}
getDeletablesAndErasables(msgHash) {
const erasables = this.#shortestPathToRoot(msgHash)
const sorted = this.topoSort()
const index = sorted.indexOf(msgHash)
const deletables = sorted.filter(
(msgHash, i) => i < index && !erasables.includes(msgHash)
)
return { deletables, erasables }
}
getMaxDepth() {
return this.#maxDepth
}

View File

@ -1,7 +1,7 @@
const base58 = require('bs58')
const ed25519 = require('ssb-keys/sodium')
const stringify = require('fast-json-stable-stringify')
const Tangle = require('../tangle')
const Tangle = require('./tangle')
function validateShape(msg) {
if (!msg || typeof msg !== 'object') {

View File

@ -4,7 +4,6 @@ const AAOL = require('async-append-only-log')
const promisify = require('promisify-4loc')
const Obz = require('obz')
const FeedV1 = require('./feed-v1')
const Tangle = require('./tangle')
const { ReadyGate } = require('./utils')
const { decrypt } = require('./encryption')
@ -41,6 +40,29 @@ const { decrypt } = require('./encryption')
* @typedef {RecPresent | RecDeleted} Rec
*/
class DBTangle extends FeedV1.Tangle {
/**
* @param {string} rootHash
* @param {Iterable<Rec>} recordsIter
*/
constructor(rootHash, recordsIter) {
super(rootHash)
for (const rec of recordsIter) {
this.add(rec.hash, rec.msg)
}
}
getDeletablesAndErasables(msgHash) {
const erasables = this.shortestPathToRoot(msgHash)
const sorted = this.topoSort()
const index = sorted.indexOf(msgHash)
const deletables = sorted.filter(
(msgHash, i) => i < index && !erasables.includes(msgHash)
)
return { deletables, erasables }
}
}
exports.name = 'db'
exports.init = function initDB(peer, config) {
@ -150,7 +172,7 @@ exports.init = function initDB(peer, config) {
function populateTangles(tangleIds) {
const tangles = {}
for (const tangleId of tangleIds) {
tangles[tangleId] ??= new Tangle(tangleId, records())
tangles[tangleId] ??= new DBTangle(tangleId, records())
}
return tangles
}
@ -162,8 +184,8 @@ exports.init = function initDB(peer, config) {
function add(msg, tangleRootHash, cb) {
// TODO: optimize this. This may be slow if you're adding many msgs in a
// row, because it creates a new Map() each time.
const tangle = new Tangle(tangleRootHash, records())
// row, because it creates a new Map() each time. Perhaps with QuickLRU
const tangle = new DBTangle(tangleRootHash, records())
const msgHash = FeedV1.getMsgHash(msg)
FeedV1.validate(msg, tangle, msgHash, tangleRootHash, validationCB)
@ -293,7 +315,7 @@ exports.init = function initDB(peer, config) {
}
function getTangle(tangleId) {
return new Tangle(tangleId, records())
return new DBTangle(tangleId, records())
}
function* msgs() {

View File

@ -18,11 +18,4 @@ class ReadyGate {
}
}
function isEmptyObject(obj) {
for (const _key in obj) {
return false
}
return true
}
module.exports = { ReadyGate, isEmptyObject }
module.exports = { ReadyGate }

View File

@ -5,7 +5,6 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack')
const caps = require('ssb-caps')
const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const p = require('util').promisify
const { generateKeypair } = require('./util')
@ -26,6 +25,8 @@ test('add()', async (t) => {
const recRoot = await p(peer.db.add)(rootMsg, rootHash)
t.equals(recRoot.msg.metadata.when, 0, 'root msg added')
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(recRoot.hash, recRoot.msg)
const inputMsg = FeedV1.create({
keys,
@ -33,7 +34,7 @@ test('add()', async (t) => {
type: 'post',
content: { text: 'This is the first post!' },
tangles: {
[rootHash]: new Tangle(rootHash, [recRoot]),
[rootHash]: tangle,
},
})

View File

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

View File

@ -1,6 +1,5 @@
const tape = require('tape')
const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util')
let rootMsg = null
@ -26,12 +25,15 @@ tape('FeedV1.create()', (t) => {
const content = { text: 'Hello world!' }
const when = 1652037377204
const tangle1 = new FeedV1.Tangle(rootHash)
tangle1.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,
content,
type: 'post',
tangles: {
[rootHash]: new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }]),
[rootHash]: tangle1,
},
when,
})
@ -65,6 +67,10 @@ tape('FeedV1.create()', (t) => {
'getMsgId'
)
const tangle2 = new FeedV1.Tangle(rootHash)
tangle2.add(rootHash, rootMsg)
tangle2.add(msgHash1, msg1)
const content2 = { text: 'Ola mundo!' }
const msg2 = FeedV1.create({
@ -72,10 +78,7 @@ tape('FeedV1.create()', (t) => {
content: content2,
type: 'post',
tangles: {
[rootHash]: new Tangle(rootHash, [
{ hash: rootHash, msg: rootMsg },
{ hash: msgHash1, msg: msg1 },
]),
[rootHash]: tangle2,
},
when: when + 1,
})
@ -112,7 +115,8 @@ tape('FeedV1.create()', (t) => {
tape('create() handles DAG tips correctly', (t) => {
const keys = generateKeypair('alice')
const when = 1652037377204
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,

View File

@ -1,7 +1,6 @@
const tape = require('tape')
const base58 = require('bs58')
const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util')
tape('invalid msg with non-array prev', (t) => {
@ -10,7 +9,8 @@ tape('invalid msg with non-array prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg = FeedV1.create({
keys,
@ -37,7 +37,8 @@ tape('invalid msg with bad prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,
@ -81,7 +82,8 @@ tape('invalid msg with URI in prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,
@ -127,7 +129,8 @@ tape('invalid msg with unknown prev', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,
@ -152,15 +155,16 @@ tape('invalid msg with unknown prev', (t) => {
})
const unknownMsgHash = FeedV1.getMsgHash(unknownMsg)
const tangle2 = new FeedV1.Tangle(rootHash)
tangle2.add(rootHash, rootMsg)
tangle2.add(unknownMsgHash, unknownMsg)
const msg2 = FeedV1.create({
keys,
content: { text: 'Hello world!' },
type: 'post',
tangles: {
[rootHash]: new Tangle(rootHash, [
{ hash: rootHash, msg: rootMsg },
{ hash: unknownMsgHash, msg: unknownMsg },
]),
[rootHash]: tangle2
},
when: 1652030002000,
})
@ -183,7 +187,8 @@ tape('invalid feed msg with a different who', (t) => {
const rootMsg = FeedV1.createRoot(keysA, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const feedTangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const feedTangle = new FeedV1.Tangle(rootHash)
feedTangle.add(rootHash, rootMsg)
const msg = FeedV1.create({
keys: keysB,
@ -207,7 +212,8 @@ tape('invalid feed msg with a different type', (t) => {
const rootMsg = FeedV1.createRoot(keysA, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const feedTangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const feedTangle = new FeedV1.Tangle(rootHash)
feedTangle.add(rootHash, rootMsg)
const msg = FeedV1.create({
keys: keysA,

View File

@ -1,6 +1,5 @@
const tape = require('tape')
const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util')
tape('lipmaa prevs', (t) => {
@ -10,7 +9,8 @@ tape('lipmaa prevs', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,

View File

@ -1,6 +1,5 @@
const tape = require('tape')
const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util')
tape('simple multi-author tangle', (t) => {
@ -9,11 +8,13 @@ tape('simple multi-author tangle', (t) => {
const rootMsgA = FeedV1.createRoot(keysA, 'post')
const rootHashA = FeedV1.getMsgHash(rootMsgA)
const tangleA = new Tangle(rootHashA, [{ hash: rootHashA, msg: rootMsgA }])
const tangleA = new FeedV1.Tangle(rootHashA)
tangleA.add(rootHashA, rootMsgA)
const rootMsgB = FeedV1.createRoot(keysB, 'post')
const rootHashB = FeedV1.getMsgHash(rootMsgB)
const tangleB = new Tangle(rootHashB, [{ hash: rootHashB, msg: rootMsgB }])
const tangleB = new FeedV1.Tangle(rootHashB)
tangleB.add(rootHashB, rootMsgB)
const msg1 = FeedV1.create({
keys: keysA,
@ -31,13 +32,16 @@ tape('simple multi-author tangle', (t) => {
'msg1 has only feed tangle'
)
const tangleX = new FeedV1.Tangle(msgHash1)
tangleX.add(msgHash1, msg1)
const msg2 = FeedV1.create({
keys: keysB,
content: { text: 'Hello world!' },
type: 'post',
tangles: {
[rootHashB]: tangleB,
[msgHash1]: new Tangle(msgHash1, [{ hash: msgHash1, msg: msg1 }]),
[msgHash1]: tangleX,
},
when: 1652030002000,
})
@ -73,11 +77,13 @@ tape('lipmaa in multi-author tangle', (t) => {
const rootMsgA = FeedV1.createRoot(keysA, 'post')
const rootHashA = FeedV1.getMsgHash(rootMsgA)
const tangleA = new Tangle(rootHashA, [{ hash: rootHashA, msg: rootMsgA }])
const tangleA = new FeedV1.Tangle(rootHashA)
tangleA.add(rootHashA, rootMsgA)
const rootMsgB = FeedV1.createRoot(keysB, 'post')
const rootHashB = FeedV1.getMsgHash(rootMsgB)
const tangleB = new Tangle(rootHashB, [{ hash: rootHashB, msg: rootMsgB }])
const tangleB = new FeedV1.Tangle(rootHashB)
tangleB.add(rootHashB, rootMsgB)
const msg1 = FeedV1.create({
keys: keysA,
@ -90,7 +96,8 @@ tape('lipmaa in multi-author tangle', (t) => {
})
const msgHash1 = FeedV1.getMsgHash(msg1)
tangleA.add(msgHash1, msg1)
const tangleThread = new Tangle(msgHash1, [{ hash: msgHash1, msg: msg1 }])
const tangleThread = new FeedV1.Tangle(msgHash1)
tangleThread.add(msgHash1, msg1)
t.deepEquals(
Object.keys(msg1.metadata.tangles),

View File

@ -1,7 +1,6 @@
const tape = require('tape')
const base58 = require('bs58')
const FeedV1 = require('../lib/feed-v1')
const Tangle = require('../lib/tangle')
const { generateKeypair } = require('./util')
tape('validate root msg', (t) => {
@ -9,7 +8,8 @@ tape('validate root msg', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
FeedV1.validate(rootMsg, tangle, rootHash, rootHash, (err) => {
if (err) console.log(err)
@ -23,7 +23,8 @@ tape('validate 2nd msg with existing root', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,
@ -49,7 +50,8 @@ tape('validate 2nd forked msg', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1A = FeedV1.create({
keys,
@ -88,7 +90,8 @@ tape('invalid msg with unknown previous', (t) => {
const rootMsg = FeedV1.createRoot(keys, 'post')
const rootHash = FeedV1.getMsgHash(rootMsg)
const tangle = new Tangle(rootHash, [{ hash: rootHash, msg: rootMsg }])
const tangle = new FeedV1.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = FeedV1.create({
keys,

View File

@ -6,13 +6,13 @@ const SecretStack = require('secret-stack')
const caps = require('ssb-caps')
const p = require('util').promisify
const { generateKeypair } = require('./util')
const Tangle = require('../lib/tangle')
const DIR = path.join(os.tmpdir(), 'ppppp-db-tangle')
rimraf.sync(DIR)
let peer
let rootPost, reply1Lo, reply1Hi, reply2A, reply3Lo, reply3Hi
let tangle
test('setup', async (t) => {
const keysA = generateKeypair('alice')
const keysB = generateKeypair('bob')
@ -81,11 +81,11 @@ test('setup', async (t) => {
])
reply3Lo = reply3B.localeCompare(reply3C) < 0 ? reply3B : reply3C
reply3Hi = reply3B.localeCompare(reply3C) < 0 ? reply3C : reply3B
tangle = peer.db.getTangle(rootPost)
})
test('Tangle.has', (t) => {
const tangle = new Tangle(rootPost, peer.db.records())
t.true(tangle.has(rootPost), 'has rootPost')
t.true(tangle.has(reply1Lo), 'has reply1Lo')
t.true(tangle.has(reply1Hi), 'has reply1Hi')
@ -97,7 +97,6 @@ test('Tangle.has', (t) => {
})
test('Tangle.getDepth', t=> {
const tangle = new Tangle(rootPost, peer.db.records())
t.equals(tangle.getDepth(rootPost), 0, 'depth of rootPost is 0')
t.equals(tangle.getDepth(reply1Lo), 1, 'depth of reply1Lo is 1')
t.equals(tangle.getDepth(reply1Hi), 1, 'depth of reply1Hi is 1')
@ -108,13 +107,11 @@ test('Tangle.getDepth', t=> {
})
test('Tangle.getMaxDepth', t => {
const tangle = new Tangle(rootPost, peer.db.records())
t.equals(tangle.getMaxDepth(), 3, 'max depth is 3')
t.end()
})
test('Tangle.topoSort', (t) => {
const tangle = new Tangle(rootPost, peer.db.records())
const sorted = tangle.topoSort()
t.deepEquals(sorted, [
@ -129,7 +126,6 @@ test('Tangle.topoSort', (t) => {
})
test('Tangle.getTips', (t) => {
const tangle = new Tangle(rootPost, peer.db.records())
const tips = tangle.getTips()
t.equals(tips.size, 2, 'there are 2 tips')
@ -139,7 +135,6 @@ test('Tangle.getTips', (t) => {
})
test('Tangle.getLipmaaSet', (t) => {
const tangle = new Tangle(rootPost, peer.db.records())
t.equals(tangle.getLipmaaSet(0).size, 0, 'lipmaa 0 (empty)')
t.equals(tangle.getLipmaaSet(1).size, 1, 'lipmaa 1 (-1)')
@ -162,7 +157,6 @@ test('Tangle.getLipmaaSet', (t) => {
})
test('Tangle.getDeletablesAndErasables basic', (t) => {
const tangle = new Tangle(rootPost, peer.db.records())
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply2A)
t.deepEquals(deletables, [reply1Hi], 'deletables')
@ -171,7 +165,6 @@ test('Tangle.getDeletablesAndErasables basic', (t) => {
})
test('Tangle.getDeletablesAndErasables with lipmaa', (t) => {
const tangle = new Tangle(rootPost, peer.db.records())
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply3Lo)
t.deepEquals(deletables, [reply1Lo, reply1Hi, reply2A], 'deletables')