mirror of https://codeberg.org/pzp/pzp-db.git
use Tangle data structure in feed format
This commit is contained in:
parent
6a19cd91a8
commit
babfd5e39f
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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]),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue