rename type=>domain, group=>identity

This commit is contained in:
Andre Staltz 2023-06-25 11:07:22 +03:00
parent 08d3450f20
commit d97d29fca0
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
31 changed files with 829 additions and 710 deletions

View File

@ -1,6 +1,6 @@
const base58 = require('bs58') const base58 = require('bs58')
const b4a = require('b4a') const b4a = require('b4a')
const MsgV2 = require('./msg-v2') const MsgV3 = require('./msg-v3')
/** /**
* @typedef {import('./index').Rec} Rec * @typedef {import('./index').Rec} Rec
@ -48,7 +48,7 @@ function decrypt(rec, peer, config) {
if (!plaintextBuf) return rec if (!plaintextBuf) return rec
// Reconstruct KVT in JS encoding // Reconstruct KVT in JS encoding
const msgDecrypted = MsgV2.fromPlaintextBuffer(plaintextBuf, msgEncrypted) const msgDecrypted = MsgV3.fromPlaintextBuffer(plaintextBuf, msgEncrypted)
return { return {
hash: rec.hash, hash: rec.hash,

View File

@ -5,12 +5,12 @@ const promisify = require('promisify-4loc')
const b4a = require('b4a') const b4a = require('b4a')
const base58 = require('bs58') const base58 = require('bs58')
const Obz = require('obz') const Obz = require('obz')
const MsgV2 = require('./msg-v2') const MsgV2 = require('./msg-v3')
const { ReadyGate } = require('./utils') const { ReadyGate } = require('./utils')
const { decrypt } = require('./encryption') const { decrypt } = require('./encryption')
/** /**
* @typedef {import('./msg-v2').Msg} Msg * @typedef {import('./msg-v3').Msg} Msg
*/ */
/** /**
@ -198,13 +198,13 @@ exports.init = function initDB(peer, config) {
const tangle = new DBTangle(tangleRootHash, records()) const tangle = new DBTangle(tangleRootHash, records())
const pubkeys = new Set() const pubkeys = new Set()
if (msg.metadata.group) { if (msg.metadata.identity) {
const groupTangle = new DBTangle(msg.metadata.group, records()) const identityTangle = new DBTangle(msg.metadata.identity, records())
if (!groupTangle.has(msg.metadata.group)) { if (!identityTangle.has(msg.metadata.identity)) {
// prettier-ignore // prettier-ignore
return cb(new Error('add() failed because the group tangle is unknown')) return cb(new Error('add() failed because the identity tangle is unknown'))
} }
for (const msgHash of groupTangle.topoSort()) { for (const msgHash of identityTangle.topoSort()) {
const msg = get(msgHash) const msg = get(msgHash)
if (!msg?.data?.add) continue if (!msg?.data?.add) continue
pubkeys.add(msg.data.add) pubkeys.add(msg.data.add)
@ -225,12 +225,12 @@ exports.init = function initDB(peer, config) {
function initializeFeed(opts, cb) { function initializeFeed(opts, cb) {
const keypair = opts.keypair ?? config.keypair const keypair = opts.keypair ?? config.keypair
const { group, type } = opts const { identity, domain } = opts
const feedRootHash = getFeedId(group, type) const feedRootHash = getFeedId(identity, domain)
if (feedRootHash) return cb(null, feedRootHash) if (feedRootHash) return cb(null, feedRootHash)
const feedRoot = MsgV2.createRoot(group, type, keypair) const feedRoot = MsgV2.createRoot(identity, domain, keypair)
add(feedRoot, MsgV2.getMsgHash(feedRoot), (err, rec) => { add(feedRoot, MsgV2.getMsgHash(feedRoot), (err, rec) => {
// prettier-ignore // prettier-ignore
if (err) return cb(new Error('initializeFeed() failed to add root', { cause: err })); if (err) return cb(new Error('initializeFeed() failed to add root', { cause: err }));
@ -238,40 +238,40 @@ exports.init = function initDB(peer, config) {
}) })
} }
function createGroup(opts, cb) { function createIdentity(opts, cb) {
const keypair = opts?.keypair ?? config.keypair const keypair = opts?.keypair ?? config.keypair
let msg let msg
try { try {
msg = MsgV2.createGroup(keypair, opts?._nonce) msg = MsgV2.createIdentity(keypair, opts?._nonce)
} catch (err) { } catch (err) {
return cb(new Error('group.create() failed', { cause: err })) return cb(new Error('identity.create() failed', { cause: err }))
} }
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV2.getMsgHash(msg)
logAppend(msgHash, msg, (err, rec) => { logAppend(msgHash, msg, (err, rec) => {
// prettier-ignore // prettier-ignore
if (err) return cb(new Error('group.create() failed in the log', { cause: err })) if (err) return cb(new Error('identity.create() failed in the log', { cause: err }))
onRecordAdded.set(rec) onRecordAdded.set(rec)
cb(null, rec) cb(null, rec)
}) })
} }
function addToGroup(opts, cb) { function addToIdentity(opts, cb) {
if (!opts?.keypair) return cb(new Error('group.add() requires a `keypair`')) if (!opts?.keypair) return cb(new Error('identity.add() requires a `keypair`'))
if (!opts?.group) return cb(new Error('group.add() requires a `group`')) if (!opts?.identity) return cb(new Error('identity.add() requires a `identity`'))
const addedKeypair = opts.keypair const addedKeypair = opts.keypair
const signingKeypair = config.keypair const signingKeypair = config.keypair
// Fill-in tangle opts: // Fill-in tangle opts:
const tangles = populateTangles([opts.group]) const tangles = populateTangles([opts.identity])
const fullOpts = { const fullOpts = {
group: null, identity: null,
groupTips: null, identityTips: null,
tangles, tangles,
keypair: signingKeypair, keypair: signingKeypair,
data: { add: addedKeypair.public }, data: { add: addedKeypair.public },
type: 'group', domain: 'identity',
} }
// Create the actual message: // Create the actual message:
@ -279,13 +279,13 @@ exports.init = function initDB(peer, config) {
try { try {
msg = MsgV2.create(fullOpts) msg = MsgV2.create(fullOpts)
} catch (err) { } catch (err) {
return cb(new Error('group.add() failed', { cause: err })) return cb(new Error('identity.add() failed', { cause: err }))
} }
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV2.getMsgHash(msg)
logAppend(msgHash, msg, (err, rec) => { logAppend(msgHash, msg, (err, rec) => {
// prettier-ignore // prettier-ignore
if (err) return cb(new Error('group.add() failed to append the log', { cause: err })) if (err) return cb(new Error('identity.add() failed to append the log', { cause: err }))
onRecordAdded.set(rec) onRecordAdded.set(rec)
cb(null, rec) cb(null, rec)
}) })
@ -303,8 +303,8 @@ exports.init = function initDB(peer, config) {
} }
} }
if (!opts.data) return cb(new Error('feed.publish() requires a `data`')) if (!opts.data) return cb(new Error('feed.publish() requires a `data`'))
if (!opts.type) return cb(new Error('feed.publish() requires a `type`')) if (!opts.domain) return cb(new Error('feed.publish() requires a `domain`'))
if (!opts.group) return cb(new Error('feed.publish() requires a `group`')) if (!opts.identity) return cb(new Error('feed.publish() requires a `identity`'))
initializeFeed(opts, (err, feedRootHash) => { initializeFeed(opts, (err, feedRootHash) => {
// prettier-ignore // prettier-ignore
@ -314,9 +314,9 @@ exports.init = function initDB(peer, config) {
const tangleTemplates = opts.tangles ?? [] const tangleTemplates = opts.tangles ?? []
tangleTemplates.push(feedRootHash) tangleTemplates.push(feedRootHash)
const tangles = populateTangles(tangleTemplates) const tangles = populateTangles(tangleTemplates)
const groupTangle = new DBTangle(opts.group, records()) const identityTangle = new DBTangle(opts.identity, records())
const groupTips = [...groupTangle.getTips()] const identityTips = [...identityTangle.getTips()]
const fullOpts = { ...opts, tangles, groupTips, keypair } const fullOpts = { ...opts, tangles, identityTips, keypair }
// If opts ask for encryption, encrypt and put ciphertext in opts.data // If opts ask for encryption, encrypt and put ciphertext in opts.data
const recps = fullOpts.data.recps const recps = fullOpts.data.recps
@ -367,10 +367,10 @@ exports.init = function initDB(peer, config) {
}) })
} }
function getFeedId(groupId, findType) { function getFeedId(id, findDomain) {
const findGroup = MsgV2.stripGroup(groupId) const findIdentity = MsgV2.stripIdentity(id)
for (const rec of records()) { for (const rec of records()) {
if (MsgV2.isFeedRoot(rec.msg, findGroup, findType)) return rec.hash if (MsgV2.isFeedRoot(rec.msg, findIdentity, findDomain)) return rec.hash
} }
return null return null
} }
@ -436,9 +436,9 @@ exports.init = function initDB(peer, config) {
installEncryptionFormat, installEncryptionFormat,
loaded, loaded,
add, add,
group: { identity: {
create: createGroup, create: createIdentity,
add: addToGroup, add: addToIdentity,
}, },
feed: { feed: {
publish: publishToFeed, publish: publishToFeed,

View File

@ -1,22 +0,0 @@
const { stripGroup } = require('./strip')
function isEmptyObject(obj) {
for (const _key in obj) {
return false
}
return true
}
function isFeedRoot(msg, groupId = 0, findType = 0) {
const { dataHash, dataSize, group, groupTips, tangles, type } = msg.metadata
if (dataHash !== null) return false
if (dataSize !== 0) return false
if (groupId === 0 && !group) return false
if (groupId !== 0 && group !== stripGroup(groupId)) return false
if (groupTips !== null) return false
if (!isEmptyObject(tangles)) return false
if (findType !== 0 && type !== findType) return false
return true
}
module.exports = isFeedRoot

View File

@ -23,7 +23,7 @@ function getMsgHashBuf(msg) {
*/ */
function getMsgHash(x) { function getMsgHash(x) {
if (typeof x === 'string') { if (typeof x === 'string') {
if (x.startsWith('ppppp:message/v2/')) { if (x.startsWith('ppppp:message/v3/')) {
const msgUri = x const msgUri = x
const parts = msgUri.split('/') const parts = msgUri.split('/')
return parts[parts.length - 1] return parts[parts.length - 1]
@ -43,12 +43,12 @@ function getMsgHash(x) {
* @returns {string} * @returns {string}
*/ */
function getMsgId(msg) { function getMsgId(msg) {
const { group, type } = msg.metadata const { identity, domain } = msg.metadata
const msgHash = getMsgHash(msg) const msgHash = getMsgHash(msg)
if (type) { if (domain) {
return `ppppp:message/v2/${group}/${type}/${msgHash}` return `ppppp:message/v3/${identity}/${domain}/${msgHash}`
} else { } else {
return `ppppp:message/v2/${group}/${msgHash}` return `ppppp:message/v3/${identity}/${msgHash}`
} }
} }

View File

@ -4,12 +4,12 @@ const b4a = require('b4a')
const stringify = require('json-canon') const stringify = require('json-canon')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const union = require('set.prototype.union') const union = require('set.prototype.union')
const { stripGroup } = require('./strip') const { stripIdentity } = require('./strip')
const isFeedRoot = require('./is-feed-root') const isFeedRoot = require('./is-feed-root')
const { getMsgId, getMsgHash } = require('./get-msg-id') const { getMsgId, getMsgHash } = require('./get-msg-id')
const representData = require('./represent-data') const representData = require('./represent-data')
const { const {
validateType, validateDomain,
validateData, validateData,
validate, validate,
validateBatch, validateBatch,
@ -18,55 +18,56 @@ const {
const Tangle = require('./tangle') const Tangle = require('./tangle')
/** /**
* @typedef {Iterator<Msg> & {values: () => Iterator<Msg>}} MsgIter
* @typedef {import('ppppp-keypair').Keypair} Keypair * @typedef {import('ppppp-keypair').Keypair} Keypair
*/
/**
* @typedef {Iterator<Msg> & {values: () => Iterator<Msg>}} MsgIter
*
* @typedef {Buffer | Uint8Array} B4A * @typedef {Buffer | Uint8Array} B4A
*
* @typedef {{
* depth: number;
* prev: Array<string>;
* }} TangleMetadata
*
* @typedef {{
* data: any;
* metadata: {
* dataHash: string | null;
* dataSize: number;
* identity: string | null;
* identityTips: Array<string> | null;
* tangles: Record<string, TangleMetadata>;
* domain: string;
* v: 3;
* };
* pubkey: string;
* sig: string;
* }} Msg
*
* @typedef {{
* data: any;
* domain: string;
* keypair: Keypair;
* identity: string | null;
* identityTips: Array<string> | null;
* tangles: Record<string, Tangle>;
* }} CreateOpts
*/ */
/** function getFeedRootHash(id, domain) {
* @typedef {Object} TangleMetadata /** @type {Msg} */
* @property {number} depth
* @property {Array<string>} prev
*/
/**
* @typedef {Object} Msg
* @property {*} data
* @property {Object} metadata
* @property {string} metadata.dataHash
* @property {number} metadata.dataSize
* @property {string | null} metadata.group
* @property {Array<string> | null} metadata.groupTips
* @property {Record<string, TangleMetadata>} metadata.tangles
* @property {string} metadata.type
* @property {2} metadata.v
* @property {string} pubkey
* @property {string} sig
*/
/**
* @typedef {Object} CreateOpts
* @property {*} data
* @property {string} type
* @property {Keypair} keypair
* @property {string | null} group
* @property {Array<string> | null} groupTips
* @property {Record<string, Tangle>} tangles
*/
function getFeedRootHash(groupId, type) {
const group = stripGroup(groupId)
const msg = { const msg = {
data: null, data: null,
metadata: { metadata: {
dataHash: null, dataHash: null,
dataSize: 0, dataSize: 0,
group, identity: stripIdentity(id),
groupTips: null, identityTips: null,
tangles: {}, tangles: {},
type, domain,
v: 2, v: 3,
}, },
pubkey: '', pubkey: '',
sig: '', sig: '',
@ -85,12 +86,12 @@ function toPlaintextBuffer(opts) {
*/ */
function create(opts) { function create(opts) {
let err let err
if ((err = validateType(opts.type))) throw err if ((err = validateDomain(opts.domain))) throw err
if (!opts.tangles) throw new Error('opts.tangles is required') if (!opts.tangles) throw new Error('opts.tangles is required')
const [dataHash, dataSize] = representData(opts.data) const [dataHash, dataSize] = representData(opts.data)
const group = opts.group ? stripGroup(opts.group) : null const identity = opts.identity ? stripIdentity(opts.identity) : null
const groupTips = opts.groupTips ? opts.groupTips.sort() : null const identityTips = opts.identityTips ? opts.identityTips.sort() : null
const tangles = {} const tangles = {}
if (opts.tangles) { if (opts.tangles) {
@ -108,16 +109,17 @@ function create(opts) {
throw new Error(`cannot create msg without tangles, that's the case for createRoot()`) throw new Error(`cannot create msg without tangles, that's the case for createRoot()`)
} }
/** @type {Msg} */
const msg = { const msg = {
data: opts.data, data: opts.data,
metadata: { metadata: {
dataHash, dataHash,
dataSize, dataSize,
group, identity,
groupTips, identityTips,
tangles, tangles,
type: opts.type, domain: opts.domain,
v: 2, v: 3,
}, },
pubkey: opts.keypair.public, pubkey: opts.keypair.public,
sig: '', sig: '',
@ -132,25 +134,26 @@ function create(opts) {
} }
/** /**
* @param {string} group * @param {string} id
* @param {string} type * @param {string} domain
* @param {Keypair} keypair * @param {Keypair} keypair
* @returns {Msg} * @returns {Msg}
*/ */
function createRoot(group, type, keypair) { function createRoot(id, domain, keypair) {
let err let err
if ((err = validateType(type))) throw err if ((err = validateDomain(domain))) throw err
/** @type {Msg} */
const msg = { const msg = {
data: null, data: null,
metadata: { metadata: {
dataHash: null, dataHash: null,
dataSize: 0, dataSize: 0,
group, identity: id,
groupTips: null, identityTips: null,
tangles: {}, tangles: {},
type, domain,
v: 2, v: 3,
}, },
pubkey: keypair.public, pubkey: keypair.public,
sig: '', sig: '',
@ -168,14 +171,18 @@ function createRoot(group, type, keypair) {
* @param {string} nonce * @param {string} nonce
* @returns {Msg} * @returns {Msg}
*/ */
function createGroup(keypair, nonce = base58.encode(crypto.randomBytes(32))) { function createIdentity(
keypair,
nonce = () => base58.encode(crypto.randomBytes(32))
) {
const actualNonce = typeof nonce === 'function' ? nonce() : nonce
return create({ return create({
data: { add: keypair.public, nonce }, data: { add: keypair.public, nonce: actualNonce },
group: null, identity: null,
groupTips: null, identityTips: null,
keypair, keypair,
tangles: {}, tangles: {},
type: 'group', domain: 'identity',
}) })
} }
@ -203,9 +210,9 @@ module.exports = {
getFeedRootHash, getFeedRootHash,
create, create,
createRoot, createRoot,
createGroup, createIdentity,
erase, erase,
stripGroup, stripIdentity,
toPlaintextBuffer, toPlaintextBuffer,
fromPlaintextBuffer, fromPlaintextBuffer,
Tangle, Tangle,

View File

@ -0,0 +1,22 @@
const { stripIdentity } = require('./strip')
function isEmptyObject(obj) {
for (const _key in obj) {
return false
}
return true
}
function isFeedRoot(msg, id = 0, findDomain = 0) {
const { dataHash, dataSize, identity, identityTips, tangles, domain } = msg.metadata
if (dataHash !== null) return false
if (dataSize !== 0) return false
if (id === 0 && !identity) return false
if (id !== 0 && identity !== stripIdentity(id)) return false
if (identityTips !== null) return false
if (!isEmptyObject(tangles)) return false
if (findDomain !== 0 && domain !== findDomain) return false
return true
}
module.exports = isFeedRoot

View File

@ -5,7 +5,7 @@ function stripMsgKey(msgKey) {
if (msgKey.key) return stripMsgKey(msgKey.key) if (msgKey.key) return stripMsgKey(msgKey.key)
else return getMsgHash(msgKey) else return getMsgHash(msgKey)
} }
if (msgKey.startsWith('ppppp:message/v2/')) { if (msgKey.startsWith('ppppp:message/v3/')) {
const parts = msgKey.split('/') const parts = msgKey.split('/')
return parts[parts.length - 1] return parts[parts.length - 1]
} else { } else {
@ -17,13 +17,13 @@ function stripMsgKey(msgKey) {
* @param {string} id * @param {string} id
* @returns {string} * @returns {string}
*/ */
function stripGroup(id) { function stripIdentity(id) {
if (id.startsWith('ppppp:group/v2/') === false) return id if (id.startsWith('ppppp:identity/v3/') === false) return id
const withoutPrefix = id.replace('ppppp:group/v2/', '') const withoutPrefix = id.replace('ppppp:identity/v3/', '')
return withoutPrefix.split('/')[0] return withoutPrefix.split('/')[0]
} }
module.exports = { module.exports = {
stripMsgKey, stripMsgKey,
stripGroup, stripIdentity,
} }

View File

@ -189,14 +189,14 @@ class Tangle {
const metadata = this.#rootMsg.metadata const metadata = this.#rootMsg.metadata
if (metadata.dataSize > 0) return false if (metadata.dataSize > 0) return false
if (metadata.dataHash !== null) return false if (metadata.dataHash !== null) return false
if (metadata.groupTips !== null) return false if (metadata.identityTips !== null) return false
return true return true
} }
getFeed() { getFeed() {
if (!this.isFeed()) return null if (!this.isFeed()) return null
const { group, type } = this.#rootMsg.metadata const { identity, domain } = this.#rootMsg.metadata
return { group, type } return { identity, domain }
} }
shortestPathToRoot(msgHash) { shortestPathToRoot(msgHash) {

View File

@ -24,21 +24,21 @@ function validateShape(msg) {
// prettier-ignore // prettier-ignore
return 'invalid message: must have metadata.dataSize\n' + JSON.stringify(msg) return 'invalid message: must have metadata.dataSize\n' + JSON.stringify(msg)
} }
if (!('group' in msg.metadata)) { if (!('identity' in msg.metadata)) {
return 'invalid message: must have metadata.group\n' + JSON.stringify(msg) return 'invalid message: must have metadata.identity\n' + JSON.stringify(msg)
} }
if (!('groupTips' in msg.metadata)) { if (!('identityTips' in msg.metadata)) {
// prettier-ignore // prettier-ignore
return 'invalid message: must have metadata.groupTips\n' + JSON.stringify(msg) return 'invalid message: must have metadata.identityTips\n' + JSON.stringify(msg)
} }
if (!('tangles' in msg.metadata)) { if (!('tangles' in msg.metadata)) {
return 'invalid message: must have metadata.tangles\n' + JSON.stringify(msg) return 'invalid message: must have metadata.tangles\n' + JSON.stringify(msg)
} }
if (!('type' in msg.metadata)) { if (!('domain' in msg.metadata)) {
return 'invalid message: must have metadata.type\n' + JSON.stringify(msg) return 'invalid message: must have metadata.domain\n' + JSON.stringify(msg)
} }
if (msg.metadata.v !== 2) { if (msg.metadata.v !== 3) {
return 'invalid message: must have metadata.v 2\n' + JSON.stringify(msg) return 'invalid message: must have metadata.v=3\n' + JSON.stringify(msg)
} }
if (typeof msg.sig !== 'string') { if (typeof msg.sig !== 'string') {
return 'invalid message: must have sig\n' + JSON.stringify(msg) return 'invalid message: must have sig\n' + JSON.stringify(msg)
@ -63,13 +63,13 @@ function validatePubkey(msg) {
} }
} }
function validateGroupPubkey(msg, pubkeys) { function validateIdentityPubkey(msg, pubkeys) {
// Unusual case: if the msg is a feed root, ignore the group and pubkey // Unusual case: if the msg is a feed root, ignore the identity and pubkey
if (isFeedRoot(msg)) return if (isFeedRoot(msg)) return
if (msg.metadata.group && !pubkeys.has(msg.pubkey)) { if (msg.metadata.identity && !pubkeys.has(msg.pubkey)) {
// prettier-ignore // prettier-ignore
return `invalid message: pubkey "${msg.pubkey}" should have been one of "${[...pubkeys]}" from the group "${msg.metadata.group}"\n` + JSON.stringify(msg) return `invalid message: pubkey "${msg.pubkey}" should have been one of "${[...pubkeys]}" from the identity "${msg.metadata.identity}"\n` + JSON.stringify(msg)
} }
} }
@ -143,14 +143,14 @@ function validateTangle(msg, tangle, tangleId) {
return `invalid message: depth "${depth}" should have been a positive integer\n` + JSON.stringify(msg) return `invalid message: depth "${depth}" should have been a positive integer\n` + JSON.stringify(msg)
} }
if (tangle.isFeed()) { if (tangle.isFeed()) {
const { group, type } = tangle.getFeed() const { identity, domain } = tangle.getFeed()
if (type !== msg.metadata.type) { if (domain !== msg.metadata.domain) {
// prettier-ignore // prettier-ignore
return `invalid message: type "${msg.metadata.type}" should have been feed type "${type}"\n` + JSON.stringify(msg) return `invalid message: domain "${msg.metadata.domain}" should have been feed domain "${domain}"\n` + JSON.stringify(msg)
} }
if (group !== msg.metadata.group) { if (identity !== msg.metadata.identity) {
// prettier-ignore // prettier-ignore
return `invalid message: group "${msg.metadata.group}" should have been feed group "${group}"\n` + JSON.stringify(msg) return `invalid message: identity "${msg.metadata.identity}" should have been feed identity "${identity}"\n` + JSON.stringify(msg)
} }
} }
let lastPrev = null let lastPrev = null
@ -213,22 +213,22 @@ function validateTangleRoot(msg, msgHash, tangleId) {
} }
} }
function validateType(type) { function validateDomain(domain) {
if (!type || typeof type !== 'string') { if (!domain || typeof domain !== 'string') {
// prettier-ignore // prettier-ignore
return `invalid type: "${type}" (${typeof type}) should have been a string` return `invalid domain: "${domain}" (${typeof domain}) should have been a string`
} }
if (type.length > 100) { if (domain.length > 100) {
// prettier-ignore // prettier-ignore
return `invalid type: "${type}" is 100+ characters long` return `invalid domain: "${domain}" is 100+ characters long`
} }
if (type.length < 3) { if (domain.length < 3) {
// prettier-ignore // prettier-ignore
return `invalid type: "${type}" is shorter than 3 characters` return `invalid domain: "${domain}" is shorter than 3 characters`
} }
if (/[^a-zA-Z0-9_]/.test(type)) { if (/[^a-zA-Z0-9_]/.test(domain)) {
// prettier-ignore // prettier-ignore
return `invalid type: "${type}" contains characters other than a-z, A-Z, 0-9, or _` return `invalid domain: "${domain}" contains characters other than a-z, A-Z, 0-9, or _`
} }
} }
@ -266,8 +266,8 @@ function validate(msg, tangle, pubkeys, msgHash, rootHash) {
if ((err = validatePubkey(msg))) return err if ((err = validatePubkey(msg))) return err
if ((err = validateDataSize(msg))) return err if ((err = validateDataSize(msg))) return err
if ((err = validateData(msg))) return err if ((err = validateData(msg))) return err
if ((err = validateType(msg.metadata.type))) return err if ((err = validateDomain(msg.metadata.domain))) return err
if ((err = validateGroupPubkey(msg, pubkeys))) return err if ((err = validateIdentityPubkey(msg, pubkeys))) return err
if (tangle.size() === 0) { if (tangle.size() === 0) {
if ((err = validateTangleRoot(msg, msgHash, rootHash))) return err if ((err = validateTangleRoot(msg, msgHash, rootHash))) return err
} else { } else {
@ -277,7 +277,7 @@ function validate(msg, tangle, pubkeys, msgHash, rootHash) {
} }
module.exports = { module.exports = {
validateType, validateDomain,
validateData, validateData,
validate, validate,
validateMsgHash, validateMsgHash,

View File

@ -1,3 +1,115 @@
# Msg V3
Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24
## Terminology
- **Msg** = published data that is signed and shareable
- **Msg hash** = hash(msg.metadata)
- **Tangle** = any single-root DAG of msgs that can be replicated by peers
- **Tangle Root** = the origin msg of a tangle
- **Tangle Tips** = tangle msgs that are not yet referenced by any other msg in the tangle
- **Tangle ID** = Msg hash of the tangle's root msg
- **Identity tangle** = tangle with msgs that add (or remove?) asymmetric-crypto public keys
- **ID** = tangle ID of the identity tangle, refers to the "identity" of a person or a group
- **Feed** = tangle with msgs authored by (any pubkey in) an identity
- **Feed root** = a msg that is deterministically predictable and empty, so to allow others to pre-know its hash
- **Feed ID** = ID of a feed (Msg ID of the feed's root msg)
JSON
```typescript
interface Msg {
data: any | null // any object, or null
metadata: {
dataHash: ContentHash | null // blake3 hash of the `content` object serialized
dataSize: number // byte size (unsigned integer) of the `content` object serialized
identity: string | null // blake3 hash ofn a identity tangle root msg, or null
identityTips: Array<string> | null // list of blake3 hashes of identity tangle tips, or null
tangles: {
// for each tangle this msg belongs to, identified by the tangle's root
[rootMsgHash: string]: {
depth: number // maximum distance (positive integer) from this msg to the root
prev: Array<MsgHash> // list of msg hashes of existing msgs, unique set and ordered alphabetically
}
}
domain: string // alphanumeric string, at least 3 chars, max 100 chars
v: 2 // hard-coded at 2, indicates the version of the feed format
}
pubkey: Pubkey // base58 encoded string for the author's public key
sig: Signature // Signs the `metadata` object
}
```
## Identity tangle msgs
Msgs in an identity tangle are special because they have empty `identity` and `identityTips` fields.
```typescript
interface Msg {
data: {
add: string // pubkey being added to the identity
nonce?: string // nonce required only on the identity tangle's root
}
metadata: {
dataHash: ContentHash
dataSize: number
identity: null // MUST be null
identityTips: null // MUST be null
tangles: {
[identityTangleId: string]: {
depth: number // maximum distance (positive integer) from this msg to the root
prev: Array<MsgHash> // list of msg hashes of existing msgs, unique set and ordered alphabetically
}
}
domain: 'identity' // MUST be 'identity'
v: 2
}
pubkey: Pubkey
sig: Signature
}
```
## Feed root
The root msg for a feed is special, its `metadata` is predictable and can be constructed by any peer. It is a data-less msg with the following shape:
```typescript
interface Msg {
data: null // MUST be null
metadata: {
dataHash: null // MUST be null
dataSize: 0 // MUST be 0
identity: string // MUST be an ID
identityTips: null // MUST be null
tangles: {} // MUST be empty object
domain: string
v: 2
}
pubkey: Pubkey
sig: Signature
}
```
Thus, given a `identity` and a `domain`, any peer can construct the `metadata` part of the feed root msg, and thus can derive the "msg ID" for the root based on that `metadata`.
Given the root msg ID, any peer can thus refer to the feed tangle, because the root msg ID is the tangle ID for the feed tangle.
Note also that _any peer_ can construct the root msg and sign it! Which renders the signatures for feed roots meaningless and ignorable.
## Prev links
A msg can refer to 0 or more prev msgs. The prev links are used to build the tangle.
The `prev` array for a tangle should list:
- All current "tips" (msgs that are not yet listed inside any `prev`) of this tangle
- All msgs that are at the previous "lipmaa" depth relative to this `depth`
## JSON serialization
Whenever we need to serialize any JSON in the context of creating a Feed V1 message, we follow the "JSON Canonicalization Scheme" (JSC) defined by [RFC 8785](https://tools.ietf.org/html/rfc8785).
# Msg V2 # Msg V2
Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24 Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24

View File

@ -7,7 +7,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 Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../lib/msg-v2') const MsgV3 = require('../lib/msg-v3')
const DIR = path.join(os.tmpdir(), 'ppppp-db-add') const DIR = path.join(os.tmpdir(), 'ppppp-db-add')
rimraf.sync(DIR) rimraf.sync(DIR)
@ -21,25 +21,25 @@ test('add()', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const groupMsg0 = MsgV2.createGroup(keypair) const identityMsg0 = MsgV3.createIdentity(keypair)
const group = MsgV2.getMsgHash(groupMsg0) const id = MsgV3.getMsgHash(identityMsg0)
await p(peer.db.add)(groupMsg0, group) await p(peer.db.add)(identityMsg0, id)
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(id, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const recRoot = await p(peer.db.add)(rootMsg, rootHash) const recRoot = await p(peer.db.add)(rootMsg, rootHash)
assert.equal(recRoot.msg.metadata.dataSize, 0, 'root msg added') assert.equal(recRoot.msg.metadata.dataSize, 0, 'root msg added')
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(recRoot.hash, recRoot.msg) tangle.add(recRoot.hash, recRoot.msg)
const inputMsg = MsgV2.create({ const inputMsg = MsgV3.create({
keypair, keypair,
type: 'post', domain: 'post',
data: { text: 'This is the first post!' }, data: { text: 'This is the first post!' },
group, identity: id,
groupTips: [group], identityTips: [id],
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },

View File

@ -21,13 +21,13 @@ test('del', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const group = (await p(peer.db.group.create)(null)).hash const id = (await p(peer.db.identity.create)(null)).hash
const msgHashes = [] const msgHashes = []
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
const rec = await p(peer.db.feed.publish)({ const rec = await p(peer.db.feed.publish)({
group, identity: id,
type: 'post', domain: 'post',
data: { text: 'm' + i }, data: { text: 'm' + i },
}) })
msgHashes.push(rec.hash) msgHashes.push(rec.hash)
@ -35,7 +35,7 @@ test('del', async (t) => {
const before = [] const before = []
for (const msg of peer.db.msgs()) { for (const msg of peer.db.msgs()) {
if (msg.data && msg.metadata.group) before.push(msg.data.text) if (msg.data && msg.metadata.identity) before.push(msg.data.text)
} }
assert.deepEqual(before, ['m0', 'm1', 'm2', 'm3', 'm4'], 'msgs before the delete') assert.deepEqual(before, ['m0', 'm1', 'm2', 'm3', 'm4'], 'msgs before the delete')
@ -44,7 +44,7 @@ test('del', async (t) => {
const after = [] const after = []
for (const msg of peer.db.msgs()) { for (const msg of peer.db.msgs()) {
if (msg.data && msg.metadata.group) after.push(msg.data.text) if (msg.data && msg.metadata.identity) after.push(msg.data.text)
} }
assert.deepEqual(after, ['m0', 'm1', 'm3', 'm4'], 'msgs after the delete') assert.deepEqual(after, ['m0', 'm1', 'm3', 'm4'], 'msgs after the delete')
@ -83,7 +83,7 @@ test('del', async (t) => {
assert.deepEqual( assert.deepEqual(
persistedMsgs persistedMsgs
.filter((msg) => msg.data && msg.metadata.group) .filter((msg) => msg.data && msg.metadata.identity)
.map((msg) => msg.data.text), .map((msg) => msg.data.text),
['m0', 'm1', 'm3', 'm4'], ['m0', 'm1', 'm3', 'm4'],
'msgs in disk after the delete' 'msgs in disk after the delete'

View File

@ -21,13 +21,13 @@ test('erase', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const group = (await p(peer.db.group.create)(null)).hash const id = (await p(peer.db.identity.create)(null)).hash
const msgHashes = [] const msgHashes = []
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
const rec = await p(peer.db.feed.publish)({ const rec = await p(peer.db.feed.publish)({
group, identity: id,
type: 'post', domain: 'post',
data: { text: 'm' + i }, data: { text: 'm' + i },
}) })
msgHashes.push(rec.hash) msgHashes.push(rec.hash)
@ -35,7 +35,7 @@ test('erase', async (t) => {
const before = [] const before = []
for (const msg of peer.db.msgs()) { for (const msg of peer.db.msgs()) {
if (msg.data && msg.metadata.group) before.push(msg.data.text) if (msg.data && msg.metadata.identity) before.push(msg.data.text)
} }
assert.deepEqual( assert.deepEqual(
@ -48,7 +48,7 @@ test('erase', async (t) => {
const after = [] const after = []
for (const msg of peer.db.msgs()) { for (const msg of peer.db.msgs()) {
if (msg.data && msg.metadata.group) after.push(msg.data.text) if (msg.data && msg.metadata.identity) after.push(msg.data.text)
} }
assert.deepEqual(after, ['m0', 'm1', 'm3', 'm4'], '4 msgs after the erase') assert.deepEqual(after, ['m0', 'm1', 'm3', 'm4'], '4 msgs after the erase')

View File

@ -7,14 +7,14 @@ 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 Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../lib/msg-v2') const MsgV3 = require('../lib/msg-v3')
const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish') const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish')
rimraf.sync(DIR) rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
let peer let peer
let group let id
let rootMsg let rootMsg
let rootHash let rootHash
test('setup', async (t) => { test('setup', async (t) => {
@ -25,16 +25,16 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
group = (await p(peer.db.group.create)(null)).hash id = (await p(peer.db.identity.create)(null)).hash
rootMsg = MsgV2.createRoot(group, 'post', keypair) rootMsg = MsgV3.createRoot(id, 'post', keypair)
rootHash = MsgV2.getMsgHash(rootMsg) rootHash = MsgV3.getMsgHash(rootMsg)
await p(peer.db.add)(rootMsg, rootHash) await p(peer.db.add)(rootMsg, rootHash)
}) })
test('feed.getId()', async (t) => { test('feed.getId()', async (t) => {
const id = peer.db.feed.getId(group, 'post') const feedId = peer.db.feed.getId(id, 'post')
assert.equal(id, rootHash, 'feed.getId() returns root hash') assert.equal(feedId, rootHash, 'feed.getId() returns root hash')
}) })
test('teardown', (t) => { test('teardown', (t) => {

View File

@ -7,7 +7,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 Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../lib/msg-v2') const MsgV3 = require('../lib/msg-v3')
const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish') const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish')
rimraf.sync(DIR) rimraf.sync(DIR)
@ -15,7 +15,7 @@ rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const bobKeypair = Keypair.generate('ed25519', 'bob') const bobKeypair = Keypair.generate('ed25519', 'bob')
let peer let peer
let group let id
let rootMsg let rootMsg
let rootHash let rootHash
test('setup', async (t) => { test('setup', async (t) => {
@ -26,9 +26,9 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
group = (await p(peer.db.group.create)(null)).hash id = (await p(peer.db.identity.create)(null)).hash
rootMsg = MsgV2.createRoot(group, 'post', keypair) rootMsg = MsgV3.createRoot(id, 'post', keypair)
rootHash = MsgV2.getMsgHash(rootMsg) rootHash = MsgV3.getMsgHash(rootMsg)
}) })
let msgHash1 let msgHash1
@ -36,8 +36,8 @@ let rec1
let msgHash2 let msgHash2
test('feed.publish()', async (t) => { test('feed.publish()', async (t) => {
rec1 = await p(peer.db.feed.publish)({ rec1 = await p(peer.db.feed.publish)({
group, identity: id,
type: 'post', domain: 'post',
data: { text: 'I am 1st post' }, data: { text: 'I am 1st post' },
}) })
assert.equal(rec1.msg.data.text, 'I am 1st post', 'msg1 text correct') assert.equal(rec1.msg.data.text, 'I am 1st post', 'msg1 text correct')
@ -52,11 +52,11 @@ test('feed.publish()', async (t) => {
'msg1 tangle prev correct' 'msg1 tangle prev correct'
) )
msgHash1 = MsgV2.getMsgHash(rec1.msg) msgHash1 = MsgV3.getMsgHash(rec1.msg)
const rec2 = await p(peer.db.feed.publish)({ const rec2 = await p(peer.db.feed.publish)({
group, identity: id,
type: 'post', domain: 'post',
data: { text: 'I am 2nd post' }, data: { text: 'I am 2nd post' },
}) })
assert.equal(rec2.msg.data.text, 'I am 2nd post', 'msg2 text correct') assert.equal(rec2.msg.data.text, 'I am 2nd post', 'msg2 text correct')
@ -70,19 +70,19 @@ test('feed.publish()', async (t) => {
[msgHash1], [msgHash1],
'msg2 tangle prev correct' 'msg2 tangle prev correct'
) )
msgHash2 = MsgV2.getMsgHash(rec2.msg) msgHash2 = MsgV3.getMsgHash(rec2.msg)
}) })
test('add() forked then feed.publish() merged', async (t) => { test('add() forked then feed.publish() merged', async (t) => {
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
tangle.add(rec1.hash, rec1.msg) tangle.add(rec1.hash, rec1.msg)
const msg3 = MsgV2.create({ const msg3 = MsgV3.create({
keypair, keypair,
group, identity: id,
groupTips: [group], identityTips: [id],
type: 'post', domain: 'post',
data: { text: '3rd post forked from 1st' }, data: { text: '3rd post forked from 1st' },
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
@ -90,11 +90,11 @@ test('add() forked then feed.publish() merged', async (t) => {
}) })
const rec3 = await p(peer.db.add)(msg3, rootHash) const rec3 = await p(peer.db.add)(msg3, rootHash)
const msgHash3 = MsgV2.getMsgHash(rec3.msg) const msgHash3 = MsgV3.getMsgHash(rec3.msg)
const rec4 = await p(peer.db.feed.publish)({ const rec4 = await p(peer.db.feed.publish)({
group, identity: id,
type: 'post', domain: 'post',
data: { text: 'I am 4th post' }, data: { text: 'I am 4th post' },
}) })
assert.ok(rec4, '4th post published') assert.ok(rec4, '4th post published')
@ -119,8 +119,8 @@ test('add() forked then feed.publish() merged', async (t) => {
test('feed.publish() encrypted with box', async (t) => { test('feed.publish() encrypted with box', async (t) => {
const recEncrypted = await p(peer.db.feed.publish)({ const recEncrypted = await p(peer.db.feed.publish)({
group, identity: id,
type: 'post', domain: 'post',
data: { text: 'I am chewing food', recps: [keypair.public] }, data: { text: 'I am chewing food', recps: [keypair.public] },
encryptionFormat: 'box', encryptionFormat: 'box',
}) })
@ -133,15 +133,15 @@ test('feed.publish() encrypted with box', async (t) => {
test('feed.publish() with tangles', async (t) => { test('feed.publish() with tangles', async (t) => {
const recA = await p(peer.db.feed.publish)({ const recA = await p(peer.db.feed.publish)({
group, identity: id,
type: 'comment', domain: 'comment',
data: { text: 'I am root' }, data: { text: 'I am root' },
}) })
assert.equal(recA.msg.data.text, 'I am root', 'root text correct') assert.equal(recA.msg.data.text, 'I am root', 'root text correct')
const recB = await p(peer.db.feed.publish)({ const recB = await p(peer.db.feed.publish)({
group, identity: id,
type: 'comment', domain: 'comment',
data: { text: 'I am comment 1' }, data: { text: 'I am comment 1' },
tangles: [recA.hash], tangles: [recA.hash],
keypair: bobKeypair, keypair: bobKeypair,

View File

@ -7,14 +7,14 @@ 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 Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../lib/msg-v2') const MsgV3 = require('../lib/msg-v3')
const DIR = path.join(os.tmpdir(), 'ppppp-db-get') const DIR = path.join(os.tmpdir(), 'ppppp-db-get')
rimraf.sync(DIR) rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
let peer let peer
let group let id
let msgHash1 let msgHash1
let msgId1 let msgId1
test('setup', async (t) => { test('setup', async (t) => {
@ -25,15 +25,15 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
group = (await p(peer.db.group.create)(null)).hash id = (await p(peer.db.identity.create)(null)).hash
const rec1 = await p(peer.db.feed.publish)({ const rec1 = await p(peer.db.feed.publish)({
group, identity: id,
type: 'post', domain: 'post',
data: { text: 'I am 1st post' }, data: { text: 'I am 1st post' },
}) })
msgHash1 = MsgV2.getMsgHash(rec1.msg) msgHash1 = MsgV3.getMsgHash(rec1.msg)
msgId1 = MsgV2.getMsgId(rec1.msg) msgId1 = MsgV3.getMsgId(rec1.msg)
}) })
test('get() supports ppppp URIs', async (t) => { test('get() supports ppppp URIs', async (t) => {

View File

@ -26,7 +26,7 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const group = (await p(peer.db.group.create)(null)).hash const id = (await p(peer.db.identity.create)(null)).hash
// Slow down append so that we can trigger msg creation in parallel // Slow down append so that we can trigger msg creation in parallel
const originalAppend = peer.db._getLog().append const originalAppend = peer.db._getLog().append
@ -36,25 +36,25 @@ test('setup', async (t) => {
rootPost = ( rootPost = (
await p(peer.db.feed.publish)({ await p(peer.db.feed.publish)({
group, identity: id,
keypair: keypairA, keypair: keypairA,
type: 'comment', domain: 'comment',
data: { text: 'root' }, data: { text: 'root' },
}) })
).hash ).hash
const [{ hash: reply1B }, { hash: reply1C }] = await Promise.all([ const [{ hash: reply1B }, { hash: reply1C }] = await Promise.all([
p(peer.db.feed.publish)({ p(peer.db.feed.publish)({
group, identity: id,
keypair: keypairB, keypair: keypairB,
type: 'comment', domain: 'comment',
data: { text: 'reply 1B' }, data: { text: 'reply 1B' },
tangles: [rootPost], tangles: [rootPost],
}), }),
p(peer.db.feed.publish)({ p(peer.db.feed.publish)({
group, identity: id,
keypair: keypairC, keypair: keypairC,
type: 'comment', domain: 'comment',
data: { text: 'reply 1C' }, data: { text: 'reply 1C' },
tangles: [rootPost], tangles: [rootPost],
}), }),
@ -64,9 +64,9 @@ test('setup', async (t) => {
reply2A = ( reply2A = (
await p(peer.db.feed.publish)({ await p(peer.db.feed.publish)({
group, identity: id,
keypair: keypairA, keypair: keypairA,
type: 'comment', domain: 'comment',
data: { text: 'reply 2' }, data: { text: 'reply 2' },
tangles: [rootPost], tangles: [rootPost],
}) })
@ -74,16 +74,16 @@ test('setup', async (t) => {
const [{ hash: reply3B }, { hash: reply3C }] = await Promise.all([ const [{ hash: reply3B }, { hash: reply3C }] = await Promise.all([
p(peer.db.feed.publish)({ p(peer.db.feed.publish)({
group, identity: id,
keypair: keypairB, keypair: keypairB,
type: 'comment', domain: 'comment',
data: { text: 'reply 3B' }, data: { text: 'reply 3B' },
tangles: [rootPost], tangles: [rootPost],
}), }),
p(peer.db.feed.publish)({ p(peer.db.feed.publish)({
group, identity: id,
keypair: keypairC, keypair: keypairC,
type: 'comment', domain: 'comment',
data: { text: 'reply 3C' }, data: { text: 'reply 3C' },
tangles: [rootPost], tangles: [rootPost],
}), }),

View File

@ -8,10 +8,10 @@ const SecretStack = require('secret-stack')
const caps = require('ssb-caps') const caps = require('ssb-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const DIR = path.join(os.tmpdir(), 'ppppp-db-group-add') const DIR = path.join(os.tmpdir(), 'ppppp-db-identity-add')
rimraf.sync(DIR) rimraf.sync(DIR)
test('group.add()', async (t) => { test('identity.add()', async (t) => {
const keypair1 = Keypair.generate('ed25519', 'alice') const keypair1 = Keypair.generate('ed25519', 'alice')
const keypair2 = Keypair.generate('ed25519', 'bob') const keypair2 = Keypair.generate('ed25519', 'bob')
@ -21,19 +21,19 @@ test('group.add()', async (t) => {
.call(null, { keypair: keypair1, path: DIR }) .call(null, { keypair: keypair1, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const groupRec0 = await p(peer.db.group.create)({ keypair: keypair1 }) const identityRec0 = await p(peer.db.identity.create)({ keypair: keypair1 })
const group = groupRec0.hash const id = identityRec0.hash
const groupRec1 = await p(peer.db.group.add)({ group, keypair: keypair2 }) const identityRec1 = await p(peer.db.identity.add)({ identity: id, keypair: keypair2 })
assert.ok(groupRec1, 'groupRec1 exists') assert.ok(identityRec1, 'identityRec1 exists')
const { hash, msg } = groupRec1 const { hash, msg } = identityRec1
assert.ok(hash, 'hash exists') assert.ok(hash, 'hash exists')
assert.equal(msg.data.add, keypair2.public, 'msg.data.add NEW KEY') assert.equal(msg.data.add, keypair2.public, 'msg.data.add NEW KEY')
assert.equal(msg.metadata.group, null, 'msg.metadata.group') assert.equal(msg.metadata.identity, null, 'msg.metadata.identity')
assert.equal(msg.metadata.groupTips, null, 'msg.metadata.groupTips') assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips')
assert.deepEqual( assert.deepEqual(
msg.metadata.tangles, msg.metadata.tangles,
{ [group]: { depth: 1, prev: [group] } }, { [id]: { depth: 1, prev: [id] } },
'msg.metadata.tangles' 'msg.metadata.tangles'
) )
assert.equal(msg.pubkey, keypair1.public, 'msg.pubkey OLD KEY') assert.equal(msg.pubkey, keypair1.public, 'msg.pubkey OLD KEY')
@ -41,7 +41,7 @@ test('group.add()', async (t) => {
await p(peer.close)() await p(peer.close)()
}) })
test('publish with a key in the group', async (t) => { test('publish with a key in the identity', async (t) => {
rimraf.sync(DIR) rimraf.sync(DIR)
const keypair1 = Keypair.generate('ed25519', 'alice') const keypair1 = Keypair.generate('ed25519', 'alice')
@ -54,33 +54,33 @@ test('publish with a key in the group', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const groupRec0 = await p(peer.db.group.create)({ keypair: keypair1 }) const identityRec0 = await p(peer.db.identity.create)({ keypair: keypair1 })
const group = groupRec0.hash const identity = identityRec0.hash
const groupRec1 = await p(peer.db.group.add)({ group, keypair: keypair2 }) const identityRec1 = await p(peer.db.identity.add)({ identity, keypair: keypair2 })
const postRec = await p(peer.db.feed.publish)({ const postRec = await p(peer.db.feed.publish)({
group, identity,
type: 'post', domain: 'post',
data: { text: 'hello' }, data: { text: 'hello' },
keypair: keypair2, keypair: keypair2,
}) })
assert.equal(postRec.msg.data.text, 'hello', 'post text correct') assert.equal(postRec.msg.data.text, 'hello', 'post text correct')
const postsId = peer.db.feed.getId(group, 'post') const postsId = peer.db.feed.getId(identity, 'post')
assert.ok(postsId, 'postsId exists') assert.ok(postsId, 'postsId exists')
const recs = [...peer.db.records()] const recs = [...peer.db.records()]
assert.equal(recs.length, 4, '4 records') assert.equal(recs.length, 4, '4 records')
const [_groupRec0, _groupRec1, postsRoot, _post] = recs const [_identityRec0, _identityRec1, postsRoot, _post] = recs
assert.deepEqual(_groupRec0.msg, groupRec0.msg, 'groupMsg0') assert.deepEqual(_identityRec0.msg, identityRec0.msg, 'identityMsg0')
assert.deepEqual(_groupRec1.msg, groupRec1.msg, 'groupMsg1') assert.deepEqual(_identityRec1.msg, identityRec1.msg, 'identityMsg1')
assert.deepEqual(postsRoot.msg.metadata, { assert.deepEqual(postsRoot.msg.metadata, {
dataHash: null, dataHash: null,
dataSize: 0, dataSize: 0,
group, identity,
groupTips: null, identityTips: null,
tangles: {}, tangles: {},
type: 'post', domain: 'post',
v: 2, v: 3,
}, 'postsRoot') }, 'postsRoot')
assert.deepEqual(_post.msg, postRec.msg, 'postMsg') assert.deepEqual(_post.msg, postRec.msg, 'postMsg')
@ -97,8 +97,8 @@ test('publish with a key in the group', async (t) => {
await carol.db.loaded() await carol.db.loaded()
await p(carol.db.add)(groupRec0.msg, group) await p(carol.db.add)(identityRec0.msg, identity)
await p(carol.db.add)(groupRec1.msg, group) await p(carol.db.add)(identityRec1.msg, identity)
await p(carol.db.add)(postsRoot.msg, postsId) await p(carol.db.add)(postsRoot.msg, postsId)
await p(carol.db.add)(postRec.msg, postsId) await p(carol.db.add)(postRec.msg, postsId)
// t.pass('carol added all messages successfully') // t.pass('carol added all messages successfully')

View File

@ -8,10 +8,10 @@ const SecretStack = require('secret-stack')
const caps = require('ssb-caps') const caps = require('ssb-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const DIR = path.join(os.tmpdir(), 'ppppp-db-group-create') const DIR = path.join(os.tmpdir(), 'ppppp-db-identity-create')
rimraf.sync(DIR) rimraf.sync(DIR)
test('group.create() without args', async (t) => { test('identity.create() without args', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const peer = SecretStack({ appKey: caps.shs }) const peer = SecretStack({ appKey: caps.shs })
.use(require('../lib')) .use(require('../lib'))
@ -19,20 +19,20 @@ test('group.create() without args', async (t) => {
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const groupRec0 = await p(peer.db.group.create)({}) const identityRec0 = await p(peer.db.identity.create)({})
assert.ok(groupRec0, 'groupRec0 exists') assert.ok(identityRec0, 'identityRec0 exists')
const { hash, msg } = groupRec0 const { hash, msg } = identityRec0
assert.ok(hash, 'hash exists') assert.ok(hash, 'hash exists')
assert.equal(msg.data.add, keypair.public, 'msg.data.add') assert.equal(msg.data.add, keypair.public, 'msg.data.add')
assert.equal(msg.metadata.group, null, 'msg.metadata.group') assert.equal(msg.metadata.identity, null, 'msg.metadata.identity')
assert.equal(msg.metadata.groupTips, null, 'msg.metadata.groupTips') assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips')
assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles') assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles')
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await p(peer.close)() await p(peer.close)()
}) })
test('group.create() with "keypair" arg', async (t) => { test('identity.create() with "keypair" arg', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const peer = SecretStack({ appKey: caps.shs }) const peer = SecretStack({ appKey: caps.shs })
@ -41,13 +41,13 @@ test('group.create() with "keypair" arg', async (t) => {
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const groupRec0 = await p(peer.db.group.create)({ keypair }) const identityRec0 = await p(peer.db.identity.create)({ keypair })
assert.ok(groupRec0, 'groupRec0 exists') assert.ok(identityRec0, 'identityRec0 exists')
const { hash, msg } = groupRec0 const { hash, msg } = identityRec0
assert.ok(hash, 'hash exists') assert.ok(hash, 'hash exists')
assert.equal(msg.data.add, keypair.public, 'msg.data.add') assert.equal(msg.data.add, keypair.public, 'msg.data.add')
assert.equal(msg.metadata.group, null, 'msg.metadata.group') assert.equal(msg.metadata.identity, null, 'msg.metadata.identity')
assert.equal(msg.metadata.groupTips, null, 'msg.metadata.groupTips') assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips')
assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles') assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles')
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')

View File

@ -1,118 +0,0 @@
const test = require('node:test')
const assert = require('node:assert')
const Keypair = require('ppppp-keypair')
const MsgV2 = require('../../lib/msg-v2')
test('validate root msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
const group = MsgV2.getMsgHash(MsgV2.createGroup(keypair, 'alice'))
const pubkeys = new Set([keypair.public])
const rootMsg = MsgV2.createRoot(group, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash)
const err = MsgV2.validate(rootMsg, tangle, pubkeys, rootHash, rootHash)
assert.ifError(err, 'valid root msg')
})
test('validate group tangle', (t) => {
const pubkeys = new Set()
const keypair1 = Keypair.generate('ed25519', 'alice')
pubkeys.add(keypair1.public)
const groupMsg0 = MsgV2.createGroup(keypair1, 'alice')
const group = MsgV2.getMsgHash(groupMsg0)
const groupMsg0Hash = group
const tangle = new MsgV2.Tangle(group)
let err = MsgV2.validate(groupMsg0, tangle, pubkeys, groupMsg0Hash, group)
assert.ifError(err, 'valid group root msg')
tangle.add(group, groupMsg0)
const keypair2 = Keypair.generate('ed25519', 'bob')
const groupMsg1 = MsgV2.create({
group: null,
groupTips: null,
type: 'group',
data: { add: keypair2.public },
tangles: {
[group]: tangle,
},
keypair: keypair1, // announcing keypair2 but signing with keypair1
})
const groupMsg1Hash = MsgV2.getMsgHash(groupMsg1)
err = MsgV2.validate(groupMsg1, tangle, pubkeys, groupMsg1Hash, group)
assert.ifError(err, 'valid group msg')
})
test('validate 2nd msg with existing root', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
const group = MsgV2.getMsgHash(MsgV2.createGroup(keypair, 'alice'))
const pubkeys = new Set([keypair.public])
const rootMsg = MsgV2.createRoot(group, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({
group,
groupTips: [group],
type: 'post',
data: { text: 'Hello world!' },
tangles: {
[rootHash]: tangle,
},
keypair,
})
const msgHash1 = MsgV2.getMsgHash(msg1)
tangle.add(msgHash1, msg1)
const err = MsgV2.validate(msg1, tangle, pubkeys, msgHash1, rootHash)
assert.ifError(err, 'valid 2nd msg')
})
test('validate 2nd forked msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
const group = MsgV2.getMsgHash(MsgV2.createGroup(keypair, 'alice'))
const pubkeys = new Set([keypair.public])
const rootMsg = MsgV2.createRoot(group, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1A = MsgV2.create({
group,
groupTips: [group],
type: 'post',
data: { text: 'Hello world!' },
tangles: {
[rootHash]: tangle,
},
keypair,
})
const msgHash1A = MsgV2.getMsgHash(msg1A)
const msg1B = MsgV2.create({
group,
groupTips: [group],
type: 'post',
data: { text: 'Hello world!' },
tangles: {
[rootHash]: tangle,
},
keypair,
})
const msgHash1B = MsgV2.getMsgHash(msg1B)
tangle.add(msgHash1A, msg1A)
tangle.add(msgHash1B, msg1B)
const err = MsgV2.validate(msg1B, tangle, pubkeys, msgHash1B, rootHash)
assert.ifError(err, 'valid 2nd forked msg')
})

View File

@ -1,64 +1,64 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../../lib/msg-v2') const MsgV3 = require('../../lib/msg-v3')
let group let identity
test('MsgV2.createGroup()', (t) => { test('MsgV3.createIdentity()', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const groupMsg0 = MsgV2.createGroup(keypair, 'MYNONCE') const identityMsg0 = MsgV3.createIdentity(keypair, 'MYNONCE')
console.log(JSON.stringify(groupMsg0, null, 2)) console.log(JSON.stringify(identityMsg0, null, 2))
assert.equal(groupMsg0.data.add, keypair.public, 'data.add') assert.equal(identityMsg0.data.add, keypair.public, 'data.add')
assert.equal(groupMsg0.metadata.dataHash, 'THi3VkJeaf8aTkLSNJUdFD', 'hash') assert.equal(identityMsg0.metadata.dataHash, 'THi3VkJeaf8aTkLSNJUdFD', 'hash')
assert.equal(groupMsg0.metadata.dataSize, 72, 'size') assert.equal(identityMsg0.metadata.dataSize, 72, 'size')
assert.equal(groupMsg0.metadata.group, null, 'group') assert.equal(identityMsg0.metadata.identity, null, 'identity')
assert.equal(groupMsg0.metadata.groupTips, null, 'groupTips') assert.equal(identityMsg0.metadata.identityTips, null, 'identityTips')
assert.deepEqual(groupMsg0.metadata.tangles, {}, 'tangles') assert.deepEqual(identityMsg0.metadata.tangles, {}, 'tangles')
assert.equal(groupMsg0.metadata.type, 'group', 'type') assert.equal(identityMsg0.metadata.domain, 'identity', 'domain')
assert.equal(groupMsg0.metadata.v, 2, 'v') assert.equal(identityMsg0.metadata.v, 3, 'v')
assert.equal(groupMsg0.pubkey, keypair.public, 'pubkey') assert.equal(identityMsg0.pubkey, keypair.public, 'pubkey')
group = MsgV2.getMsgHash(groupMsg0) identity = MsgV3.getMsgHash(identityMsg0)
assert.equal(group, 'XKKmEBmqKGa5twQ2HNSk7t', 'group ID') assert.equal(identity, 'WnAX17Lm2ktfPUJ5ARXq73', 'identity ID')
}) })
let rootMsg = null let rootMsg = null
let rootHash = null let rootHash = null
test('MsgV2.createRoot()', (t) => { test('MsgV3.createRoot()', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
rootMsg = MsgV2.createRoot(group, 'post', keypair) rootMsg = MsgV3.createRoot(identity, 'post', keypair)
console.log(JSON.stringify(rootMsg, null, 2)) console.log(JSON.stringify(rootMsg, null, 2))
assert.equal(rootMsg.data, null, 'data') assert.equal(rootMsg.data, null, 'data')
assert.equal(rootMsg.metadata.dataHash, null, 'hash') assert.equal(rootMsg.metadata.dataHash, null, 'hash')
assert.equal(rootMsg.metadata.dataSize, 0, 'size') assert.equal(rootMsg.metadata.dataSize, 0, 'size')
assert.equal(rootMsg.metadata.group, group, 'group') assert.equal(rootMsg.metadata.identity, identity, 'identity')
assert.equal(rootMsg.metadata.groupTips, null, 'groupTips') assert.equal(rootMsg.metadata.identityTips, null, 'identityTips')
assert.deepEqual(rootMsg.metadata.tangles, {}, 'tangles') assert.deepEqual(rootMsg.metadata.tangles, {}, 'tangles')
assert.equal(rootMsg.metadata.type, 'post', 'type') assert.equal(rootMsg.metadata.domain, 'post', 'domain')
assert.equal(rootMsg.metadata.v, 2, 'v') assert.equal(rootMsg.metadata.v, 3, 'v')
assert.equal(rootMsg.pubkey, keypair.public, 'pubkey') assert.equal(rootMsg.pubkey, keypair.public, 'pubkey')
rootHash = MsgV2.getMsgHash(rootMsg) rootHash = MsgV3.getMsgHash(rootMsg)
assert.equal(rootHash, 'PzuT1Dwbbgn6a8NeLuHuKw', 'root hash') assert.equal(rootHash, '5G4FJTWaGr7ZBUJGge6Qeg', 'root hash')
}) })
test('MsgV2.create()', (t) => { test('MsgV3.create()', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }
const tangle1 = new MsgV2.Tangle(rootHash) const tangle1 = new MsgV3.Tangle(rootHash)
tangle1.add(rootHash, rootMsg) tangle1.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
keypair, keypair,
data, data,
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle1, [rootHash]: tangle1,
}, },
@ -68,7 +68,7 @@ test('MsgV2.create()', (t) => {
assert.deepEqual(msg1.data, data, 'data') assert.deepEqual(msg1.data, data, 'data')
assert.deepEqual( assert.deepEqual(
Object.keys(msg1.metadata), Object.keys(msg1.metadata),
['dataHash', 'dataSize', 'group', 'groupTips', 'tangles', 'type', 'v'], ['dataHash', 'dataSize', 'identity', 'identityTips', 'tangles', 'domain', 'v'],
'metadata shape' 'metadata shape'
) )
assert.deepEqual( assert.deepEqual(
@ -77,8 +77,8 @@ test('MsgV2.create()', (t) => {
'metadata.dataHash' 'metadata.dataHash'
) )
assert.deepEqual(msg1.metadata.dataSize, 23, 'metadata.dataSize') assert.deepEqual(msg1.metadata.dataSize, 23, 'metadata.dataSize')
assert.equal(msg1.metadata.group, group, 'metadata.group') assert.equal(msg1.metadata.identity, identity, 'metadata.identity')
assert.deepEqual(msg1.metadata.groupTips, [group], 'metadata.groupTips') assert.deepEqual(msg1.metadata.identityTips, [identity], 'metadata.identityTips')
assert.deepEqual( assert.deepEqual(
Object.keys(msg1.metadata.tangles), Object.keys(msg1.metadata.tangles),
[rootHash], [rootHash],
@ -86,8 +86,8 @@ test('MsgV2.create()', (t) => {
) )
assert.equal(msg1.metadata.tangles[rootHash].depth, 1, 'tangle depth') assert.equal(msg1.metadata.tangles[rootHash].depth, 1, 'tangle depth')
assert.deepEqual(msg1.metadata.tangles[rootHash].prev, [rootHash], 'tangle prev') assert.deepEqual(msg1.metadata.tangles[rootHash].prev, [rootHash], 'tangle prev')
assert.equal(msg1.metadata.type, 'post', 'metadata.type') assert.equal(msg1.metadata.domain, 'post', 'metadata.domain')
assert.deepEqual(msg1.metadata.v, 2, 'metadata.v') assert.deepEqual(msg1.metadata.v, 3, 'metadata.v')
assert.equal( assert.equal(
msg1.pubkey, msg1.pubkey,
'4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW', '4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW',
@ -95,30 +95,30 @@ test('MsgV2.create()', (t) => {
) )
assert.equal( assert.equal(
msg1.sig, msg1.sig,
'CW8gWiiqtEgPQ2NjXWHJb5aeW4vkKMG9d1BqPJDjSJaw6xX6s5GUTvoobNSBtaLv8CKNXHHJXSr9Vbe7Cew9pkv', 'xZGu2Kb19XicfoihgBZ84jRs4XuNgVBd2bK45Cum2fdVDNJUE3f8Ejf6apfZFyE8iAfPDEVWFNAJB6E52EaWEAm',
'sig' 'sig'
) )
const msgHash1 = '7miH6Zh63cyMJTT5bhDjZF' const msgHash1 = 'NF389yT2td9gz5TvRuZMB6'
assert.equal( assert.equal(
MsgV2.getMsgId(msg1), MsgV3.getMsgId(msg1),
`ppppp:message/v2/${group}/post/${msgHash1}`, `ppppp:message/v3/${identity}/post/${msgHash1}`,
'getMsgId' 'getMsgId'
) )
const tangle2 = new MsgV2.Tangle(rootHash) const tangle2 = new MsgV3.Tangle(rootHash)
tangle2.add(rootHash, rootMsg) tangle2.add(rootHash, rootMsg)
tangle2.add(msgHash1, msg1) tangle2.add(msgHash1, msg1)
const data2 = { text: 'Ola mundo!' } const data2 = { text: 'Ola mundo!' }
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
keypair, keypair,
data: data2, data: data2,
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle2, [rootHash]: tangle2,
}, },
@ -128,7 +128,7 @@ test('MsgV2.create()', (t) => {
assert.deepEqual(msg2.data, data2, 'data') assert.deepEqual(msg2.data, data2, 'data')
assert.deepEqual( assert.deepEqual(
Object.keys(msg2.metadata), Object.keys(msg2.metadata),
['dataHash', 'dataSize', 'group', 'groupTips', 'tangles', 'type', 'v'], ['dataHash', 'dataSize', 'identity', 'identityTips', 'tangles', 'domain', 'v'],
'metadata shape' 'metadata shape'
) )
assert.deepEqual( assert.deepEqual(
@ -137,8 +137,8 @@ test('MsgV2.create()', (t) => {
'metadata.dataHash' 'metadata.dataHash'
) )
assert.deepEqual(msg2.metadata.dataSize, 21, 'metadata.dataSize') assert.deepEqual(msg2.metadata.dataSize, 21, 'metadata.dataSize')
assert.equal(msg2.metadata.group, group, 'metadata.group') assert.equal(msg2.metadata.identity, identity, 'metadata.identity')
assert.deepEqual(msg2.metadata.groupTips, [group], 'metadata.groupTips') assert.deepEqual(msg2.metadata.identityTips, [identity], 'metadata.identityTips')
assert.deepEqual( assert.deepEqual(
Object.keys(msg2.metadata.tangles), Object.keys(msg2.metadata.tangles),
[rootHash], [rootHash],
@ -146,8 +146,8 @@ test('MsgV2.create()', (t) => {
) )
assert.equal(msg2.metadata.tangles[rootHash].depth, 2, 'tangle depth') assert.equal(msg2.metadata.tangles[rootHash].depth, 2, 'tangle depth')
assert.deepEqual(msg2.metadata.tangles[rootHash].prev, [msgHash1], 'tangle prev') assert.deepEqual(msg2.metadata.tangles[rootHash].prev, [msgHash1], 'tangle prev')
assert.equal(msg2.metadata.type, 'post', 'metadata.type') assert.equal(msg2.metadata.domain, 'post', 'metadata.domain')
assert.deepEqual(msg2.metadata.v, 2, 'metadata.v') assert.deepEqual(msg2.metadata.v, 3, 'metadata.v')
assert.equal( assert.equal(
msg2.pubkey, msg2.pubkey,
'4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW', '4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW',
@ -155,47 +155,47 @@ test('MsgV2.create()', (t) => {
) )
assert.equal( assert.equal(
msg2.sig, msg2.sig,
'33PStdQ8kdvL1pSpd6x9LuxcpEvDmsRNhAq7t75v66cthSHHuiJVqp57b9J7QVXp7a1Jw5qaZLycYQspJRbKNWyW', '2XHQcG8KeNbdz8m5fDs2QtT7jcxgEqHxv7SkSYVpKQAJ1S8HGn3dmLxw3J5vWmu1vhYhWS6GDE1hfMtvmfiCAy54',
'sig' 'sig'
) )
assert.deepEqual( assert.deepEqual(
MsgV2.getMsgId(msg2), MsgV3.getMsgId(msg2),
`ppppp:message/v2/${group}/post/HTtEmjCBXGBRTMM3mgekWu`, `ppppp:message/v3/${identity}/post/HNQp1oUu3zmgD1s11xiR7y`,
'getMsgId' 'getMsgId'
) )
}) })
test('create() handles DAG tips correctly', (t) => { test('create() handles DAG tips correctly', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
keypair, keypair,
data: { text: '1' }, data: { text: '1' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
assert.deepEqual( assert.deepEqual(
msg1.metadata.tangles[rootHash].prev, msg1.metadata.tangles[rootHash].prev,
[MsgV2.getFeedRootHash(group, 'post')], [MsgV3.getFeedRootHash(identity, 'post')],
'msg1.prev is root' 'msg1.prev is root'
) )
tangle.add(msgHash1, msg1) tangle.add(msgHash1, msg1)
const msg2A = MsgV2.create({ const msg2A = MsgV3.create({
keypair, keypair,
data: { text: '2A' }, data: { text: '2A' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
@ -206,17 +206,17 @@ test('create() handles DAG tips correctly', (t) => {
'msg2A.prev is msg1' 'msg2A.prev is msg1'
) )
const msg2B = MsgV2.create({ const msg2B = MsgV3.create({
keypair, keypair,
data: { text: '2B' }, data: { text: '2B' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash2B = MsgV2.getMsgHash(msg2B) const msgHash2B = MsgV3.getMsgHash(msg2B)
assert.deepEqual( assert.deepEqual(
msg2B.metadata.tangles[rootHash].prev, msg2B.metadata.tangles[rootHash].prev,
[msgHash1], [msgHash1],
@ -225,17 +225,17 @@ test('create() handles DAG tips correctly', (t) => {
tangle.add(msgHash2B, msg2B) tangle.add(msgHash2B, msg2B)
const msg3 = MsgV2.create({ const msg3 = MsgV3.create({
keypair, keypair,
data: { text: '3' }, data: { text: '3' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash3 = MsgV2.getMsgHash(msg3) const msgHash3 = MsgV3.getMsgHash(msg3)
assert.deepEqual( assert.deepEqual(
msg3.metadata.tangles[rootHash].prev, msg3.metadata.tangles[rootHash].prev,
[rootHash, msgHash2B].sort(), [rootHash, msgHash2B].sort(),
@ -243,16 +243,16 @@ test('create() handles DAG tips correctly', (t) => {
) )
tangle.add(msgHash3, msg3) tangle.add(msgHash3, msg3)
const msgHash2A = MsgV2.getMsgHash(msg2A) const msgHash2A = MsgV3.getMsgHash(msg2A)
tangle.add(msgHash2A, msg2A) tangle.add(msgHash2A, msg2A)
// t.pass('msg2A comes into awareness') // t.pass('msg2A comes into awareness')
const msg4 = MsgV2.create({ const msg4 = MsgV3.create({
keypair, keypair,
data: { text: '4' }, data: { text: '4' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },

View File

@ -1,84 +1,84 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../../lib/msg-v2') const MsgV3 = require('../../lib/msg-v3')
test('invalid type not a string', (t) => { test('invalid domain not a string', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV2.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
type: 123, domain: 123,
}) })
}, },
/invalid type/, /invalid domain/,
'not a string' 'not a string'
) )
}) })
test('invalid type with "/" character', (t) => { test('invalid domain with "/" character', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV2.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
type: 'group/init', domain: 'group/init',
}) })
}, },
/invalid type/, /invalid domain/,
'invalid type if contains /' 'invalid domain if contains /'
) )
}) })
test('invalid type with "*" character', (t) => { test('invalid domain with "*" character', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV2.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
type: 'star*', domain: 'star*',
}) })
}, },
/invalid type/, /invalid domain/,
'invalid type if contains *' 'invalid domain if contains *'
) )
}) })
test('invalid type too short', (t) => { test('invalid domain too short', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV2.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
type: 'xy', domain: 'xy',
}) })
}, },
/shorter than 3/, /shorter than 3/,
'invalid type if too short' 'invalid domain if too short'
) )
}) })
test('invalid type too long', (t) => { test('invalid domain too long', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV2.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
type: 'a'.repeat(120), domain: 'a'.repeat(120),
}) })
}, },
/100\+ characters long/, /100\+ characters long/,
'invalid type if too long' 'invalid domain if too long'
) )
}) })

View File

@ -2,35 +2,35 @@ const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const base58 = require('bs58') const base58 = require('bs58')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../../lib/msg-v2') const MsgV3 = require('../../lib/msg-v3')
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const group = MsgV2.getMsgHash(MsgV2.createGroup(keypair, 'MYNONCE')) const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'MYNONCE'))
const pubkeys = new Set([keypair.public]) const pubkeys = new Set([keypair.public])
test('invalid msg with non-array prev', (t) => { test('invalid msg with non-array prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg = MsgV2.create({ const msg = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
msg.metadata.tangles[rootHash].prev = null msg.metadata.tangles[rootHash].prev = null
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV3.getMsgHash(msg)
const err = MsgV2.validate(msg, tangle, pubkeys, msgHash, rootHash) const err = MsgV3.validate(msg, tangle, pubkeys, msgHash, rootHash)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match( assert.match(
err, err,
@ -42,40 +42,40 @@ test('invalid msg with non-array prev', (t) => {
test('invalid msg with bad prev', (t) => { test('invalid msg with bad prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
tangle.add(msgHash1, msg1) tangle.add(msgHash1, msg1)
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
msg2.metadata.tangles[rootHash].depth = 1 msg2.metadata.tangles[rootHash].depth = 1
msg2.metadata.tangles[rootHash].prev = [1234] msg2.metadata.tangles[rootHash].prev = [1234]
const msgHash2 = MsgV2.getMsgHash(msg2) const msgHash2 = MsgV3.getMsgHash(msg2)
const err = MsgV2.validate(msg2, tangle, pubkeys, msgHash2, rootHash) const err = MsgV3.validate(msg2, tangle, pubkeys, msgHash2, rootHash)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match( assert.match(
err, err,
@ -87,42 +87,42 @@ test('invalid msg with bad prev', (t) => {
test('invalid msg with URI in prev', (t) => { test('invalid msg with URI in prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
tangle.add(msgHash1, msg1) tangle.add(msgHash1, msg1)
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash2 = MsgV2.getMsgHash(msg2) const msgHash2 = MsgV3.getMsgHash(msg2)
const randBuf = Buffer.alloc(16).fill(16) const randBuf = Buffer.alloc(16).fill(16)
const fakeMsgKey1 = `ppppp:message/v2/${base58.encode(randBuf)}` const fakeMsgKey1 = `ppppp:message/v3/${base58.encode(randBuf)}`
msg2.metadata.tangles[rootHash].depth = 1 msg2.metadata.tangles[rootHash].depth = 1
msg2.metadata.tangles[rootHash].prev = [fakeMsgKey1] msg2.metadata.tangles[rootHash].prev = [fakeMsgKey1]
const err = MsgV2.validate(msg2, tangle, pubkeys, msgHash2, rootHash) const err = MsgV3.validate(msg2, tangle, pubkeys, msgHash2, rootHash)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match(err, /prev item ".*" is a URI/, 'invalid 2nd msg description') assert.match(err, /prev item ".*" is a URI/, 'invalid 2nd msg description')
}) })
@ -130,55 +130,55 @@ test('invalid msg with URI in prev', (t) => {
test('invalid msg with unknown prev', (t) => { test('invalid msg with unknown prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
tangle.add(msgHash1, msg1) tangle.add(msgHash1, msg1)
const unknownMsg = MsgV2.create({ const unknownMsg = MsgV3.create({
keypair, keypair,
data: { text: 'Alien' }, data: { text: 'Alien' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const unknownMsgHash = MsgV2.getMsgHash(unknownMsg) const unknownMsgHash = MsgV3.getMsgHash(unknownMsg)
const fakeRootHash = 'ABCDEabcde' + rootHash.substring(10) const fakeRootHash = 'ABCDEabcde' + rootHash.substring(10)
const tangle2 = new MsgV2.Tangle(fakeRootHash) const tangle2 = new MsgV3.Tangle(fakeRootHash)
tangle2.add(fakeRootHash, rootMsg) tangle2.add(fakeRootHash, rootMsg)
tangle2.add(unknownMsgHash, unknownMsg) tangle2.add(unknownMsgHash, unknownMsg)
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle2, [rootHash]: tangle2,
}, },
}) })
const msgHash2 = MsgV2.getMsgHash(msg2) const msgHash2 = MsgV3.getMsgHash(msg2)
const err = MsgV2.validate(msg2, tangle, pubkeys, msgHash2, rootHash) const err = MsgV3.validate(msg2, tangle, pubkeys, msgHash2, rootHash)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match(err, /all prev are locally unknown/, 'invalid 2nd msg description') assert.match(err, /all prev are locally unknown/, 'invalid 2nd msg description')
}) })
@ -187,59 +187,59 @@ test('invalid feed msg with a different pubkey', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob') const keypairB = Keypair.generate('ed25519', 'bob')
const groupB = MsgV2.getMsgHash(MsgV2.createGroup(keypairB, 'MYNONCE')) const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'MYNONCE'))
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const feedTangle = new MsgV2.Tangle(rootHash) const feedTangle = new MsgV3.Tangle(rootHash)
feedTangle.add(rootHash, rootMsg) feedTangle.add(rootHash, rootMsg)
const msg = MsgV2.create({ const msg = MsgV3.create({
keypair: keypairB, keypair: keypairB,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group: groupB, identity: identityB,
groupTips: [groupB], identityTips: [identityB],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: feedTangle, [rootHash]: feedTangle,
}, },
}) })
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV3.getMsgHash(msg)
const err = MsgV2.validate(msg, feedTangle, pubkeys, msgHash, rootHash) const err = MsgV3.validate(msg, feedTangle, pubkeys, msgHash, rootHash)
assert.ok(err, 'invalid msg throws') assert.ok(err, 'invalid msg throws')
assert.match( assert.match(
err, err,
/pubkey ".*" should have been one of ".*" from the group ".*"/, /pubkey ".*" should have been one of ".*" from the identity ".*"/,
'invalid msg' 'invalid msg'
) )
}) })
test('invalid feed msg with a different type', (t) => { test('invalid feed msg with a different domain', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const feedTangle = new MsgV2.Tangle(rootHash) const feedTangle = new MsgV3.Tangle(rootHash)
feedTangle.add(rootHash, rootMsg) feedTangle.add(rootHash, rootMsg)
const msg = MsgV2.create({ const msg = MsgV3.create({
keypair: keypairA, keypair: keypairA,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'comment', domain: 'comment',
tangles: { tangles: {
[rootHash]: feedTangle, [rootHash]: feedTangle,
}, },
}) })
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV3.getMsgHash(msg)
const err = MsgV2.validate(msg, feedTangle, pubkeys, msgHash, rootHash) const err = MsgV3.validate(msg, feedTangle, pubkeys, msgHash, rootHash)
assert.ok(err, 'invalid msg throws') assert.ok(err, 'invalid msg throws')
assert.match( assert.match(
err, err,
/type "comment" should have been feed type "post"/, /domain "comment" should have been feed domain "post"/,
'invalid feed msg' 'invalid feed msg'
) )
}) })
@ -247,50 +247,50 @@ test('invalid feed msg with a different type', (t) => {
test('invalid feed msg with non-alphabetical prev', (t) => { test('invalid feed msg with non-alphabetical prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
keypair, keypair,
data: { text: '1' }, data: { text: '1' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
keypair, keypair,
data: { text: '2' }, data: { text: '2' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash2 = MsgV2.getMsgHash(msg2) const msgHash2 = MsgV3.getMsgHash(msg2)
tangle.add(msgHash1, msg1) tangle.add(msgHash1, msg1)
tangle.add(msgHash2, msg2) tangle.add(msgHash2, msg2)
const msg3 = MsgV2.create({ const msg3 = MsgV3.create({
keypair, keypair,
data: { text: '3' }, data: { text: '3' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash3 = MsgV2.getMsgHash(msg3) const msgHash3 = MsgV3.getMsgHash(msg3)
let prevHashes = msg3.metadata.tangles[rootHash].prev let prevHashes = msg3.metadata.tangles[rootHash].prev
if (prevHashes[0] < prevHashes[1]) { if (prevHashes[0] < prevHashes[1]) {
@ -300,7 +300,7 @@ test('invalid feed msg with non-alphabetical prev', (t) => {
} }
msg3.metadata.tangles[rootHash].prev = prevHashes msg3.metadata.tangles[rootHash].prev = prevHashes
const err = MsgV2.validate(msg3, tangle, pubkeys, msgHash3, rootHash) const err = MsgV3.validate(msg3, tangle, pubkeys, msgHash3, rootHash)
assert.ok(err, 'invalid 3rd msg throws') assert.ok(err, 'invalid 3rd msg throws')
assert.match( assert.match(
err, err,
@ -312,28 +312,28 @@ test('invalid feed msg with non-alphabetical prev', (t) => {
test('invalid feed msg with duplicate prev', (t) => { test('invalid feed msg with duplicate prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
keypair, keypair,
data: { text: '1' }, data: { text: '1' },
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
const [prevHash] = msg1.metadata.tangles[rootHash].prev const [prevHash] = msg1.metadata.tangles[rootHash].prev
msg1.metadata.tangles[rootHash].prev = [prevHash, prevHash] msg1.metadata.tangles[rootHash].prev = [prevHash, prevHash]
const err = MsgV2.validate(msg1, tangle, pubkeys, msgHash1, rootHash) const err = MsgV3.validate(msg1, tangle, pubkeys, msgHash1, rootHash)
assert.ok(err, 'invalid 1st msg throws') assert.ok(err, 'invalid 1st msg throws')
assert.match(err, /prev ".*" contains duplicates/, 'invalid error message') assert.match(err, /prev ".*" contains duplicates/, 'invalid error message')
}) })

View File

@ -1,29 +1,29 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../../lib/msg-v2') const MsgV3 = require('../../lib/msg-v3')
test('lipmaa prevs', (t) => { test('lipmaa prevs', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const group = MsgV2.getMsgHash(MsgV2.createGroup(keypair, 'MYNONCE')) const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'MYNONCE'))
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }
const rootMsg = MsgV2.createRoot(group, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV2.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV2.Tangle(rootHash) const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg) tangle.add(rootHash, rootMsg)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
keypair, keypair,
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
tangle.add(msgHash1, msg1) tangle.add(msgHash1, msg1)
assert.equal(msg1.metadata.tangles[rootHash].depth, 1, 'msg1 depth') assert.equal(msg1.metadata.tangles[rootHash].depth, 1, 'msg1 depth')
assert.deepEqual( assert.deepEqual(
@ -32,17 +32,17 @@ test('lipmaa prevs', (t) => {
'msg1 prev' 'msg1 prev'
) )
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
keypair, keypair,
}) })
const msgHash2 = MsgV2.getMsgHash(msg2) const msgHash2 = MsgV3.getMsgHash(msg2)
tangle.add(msgHash2, msg2) tangle.add(msgHash2, msg2)
assert.equal(msg2.metadata.tangles[rootHash].depth, 2, 'msg2 depth') assert.equal(msg2.metadata.tangles[rootHash].depth, 2, 'msg2 depth')
assert.deepEqual( assert.deepEqual(
@ -51,17 +51,17 @@ test('lipmaa prevs', (t) => {
'msg2 prev' 'msg2 prev'
) )
const msg3 = MsgV2.create({ const msg3 = MsgV3.create({
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
keypair, keypair,
}) })
const msgHash3 = MsgV2.getMsgHash(msg3) const msgHash3 = MsgV3.getMsgHash(msg3)
tangle.add(msgHash3, msg3) tangle.add(msgHash3, msg3)
assert.equal(msg3.metadata.tangles[rootHash].depth, 3, 'msg3 depth') assert.equal(msg3.metadata.tangles[rootHash].depth, 3, 'msg3 depth')
assert.deepEqual( assert.deepEqual(
@ -70,17 +70,17 @@ test('lipmaa prevs', (t) => {
'msg3 prev (has lipmaa!)' 'msg3 prev (has lipmaa!)'
) )
const msg4 = MsgV2.create({ const msg4 = MsgV3.create({
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
keypair, keypair,
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
data, data,
}) })
const msgHash4 = MsgV2.getMsgHash(msg4) const msgHash4 = MsgV3.getMsgHash(msg4)
tangle.add(msgHash4, msg4) tangle.add(msgHash4, msg4)
assert.equal(msg4.metadata.tangles[rootHash].depth, 4, 'msg4 depth') assert.equal(msg4.metadata.tangles[rootHash].depth, 4, 'msg4 depth')
assert.deepEqual( assert.deepEqual(
@ -89,17 +89,17 @@ test('lipmaa prevs', (t) => {
'msg4 prev' 'msg4 prev'
) )
const msg5 = MsgV2.create({ const msg5 = MsgV3.create({
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
keypair, keypair,
}) })
const msgHash5 = MsgV2.getMsgHash(msg5) const msgHash5 = MsgV3.getMsgHash(msg5)
tangle.add(msgHash5, msg5) tangle.add(msgHash5, msg5)
assert.equal(msg5.metadata.tangles[rootHash].depth, 5, 'msg5 depth') assert.equal(msg5.metadata.tangles[rootHash].depth, 5, 'msg5 depth')
assert.deepEqual( assert.deepEqual(
@ -108,17 +108,17 @@ test('lipmaa prevs', (t) => {
'msg5 prev' 'msg5 prev'
) )
const msg6 = MsgV2.create({ const msg6 = MsgV3.create({
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
keypair, keypair,
}) })
const msgHash6 = MsgV2.getMsgHash(msg6) const msgHash6 = MsgV3.getMsgHash(msg6)
tangle.add(msgHash6, msg6) tangle.add(msgHash6, msg6)
assert.equal(msg6.metadata.tangles[rootHash].depth, 6, 'msg6 depth') assert.equal(msg6.metadata.tangles[rootHash].depth, 6, 'msg6 depth')
assert.deepEqual( assert.deepEqual(
@ -127,17 +127,17 @@ test('lipmaa prevs', (t) => {
'msg6 prev' 'msg6 prev'
) )
const msg7 = MsgV2.create({ const msg7 = MsgV3.create({
group, identity,
groupTips: [group], identityTips: [identity],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHash]: tangle, [rootHash]: tangle,
}, },
keypair, keypair,
}) })
const msgHash7 = MsgV2.getMsgHash(msg7) const msgHash7 = MsgV3.getMsgHash(msg7)
tangle.add(msgHash7, msg7) tangle.add(msgHash7, msg7)
assert.equal(msg7.metadata.tangles[rootHash].depth, 7, 'msg7 depth') assert.equal(msg7.metadata.tangles[rootHash].depth, 7, 'msg7 depth')
assert.deepEqual( assert.deepEqual(

View File

@ -1,48 +1,48 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV2 = require('../../lib/msg-v2') const MsgV3 = require('../../lib/msg-v3')
test('simple multi-author tangle', (t) => { test('simple multi-author tangle', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob') const keypairB = Keypair.generate('ed25519', 'bob')
const groupA = MsgV2.getMsgHash(MsgV2.createGroup(keypairA, 'alice')) const identityA = MsgV3.getMsgHash(MsgV3.createIdentity(keypairA, 'alice'))
const groupB = MsgV2.getMsgHash(MsgV2.createGroup(keypairB, 'bob')) const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'bob'))
const rootMsgA = MsgV2.createRoot(groupA, 'post', keypairA) const rootMsgA = MsgV3.createRoot(identityA, 'post', keypairA)
const rootHashA = MsgV2.getMsgHash(rootMsgA) const rootHashA = MsgV3.getMsgHash(rootMsgA)
const tangleA = new MsgV2.Tangle(rootHashA) const tangleA = new MsgV3.Tangle(rootHashA)
tangleA.add(rootHashA, rootMsgA) tangleA.add(rootHashA, rootMsgA)
const rootMsgB = MsgV2.createRoot(groupB, 'post', keypairB) const rootMsgB = MsgV3.createRoot(identityB, 'post', keypairB)
const rootHashB = MsgV2.getMsgHash(rootMsgB) const rootHashB = MsgV3.getMsgHash(rootMsgB)
const tangleB = new MsgV2.Tangle(rootHashB) const tangleB = new MsgV3.Tangle(rootHashB)
tangleB.add(rootHashB, rootMsgB) tangleB.add(rootHashB, rootMsgB)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
group: groupA, identity: identityA,
groupTips: [groupA], identityTips: [identityA],
type: 'post', domain: 'post',
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
tangles: { tangles: {
[rootHashA]: tangleA, [rootHashA]: tangleA,
}, },
keypair: keypairA, keypair: keypairA,
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
assert.deepEqual( assert.deepEqual(
Object.keys(msg1.metadata.tangles), Object.keys(msg1.metadata.tangles),
[rootHashA], [rootHashA],
'msg1 has only feed tangle' 'msg1 has only feed tangle'
) )
const tangleX = new MsgV2.Tangle(msgHash1) const tangleX = new MsgV3.Tangle(msgHash1)
tangleX.add(msgHash1, msg1) tangleX.add(msgHash1, msg1)
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
group: groupB, identity: identityB,
groupTips: [groupB], identityTips: [identityB],
type: 'post', domain: 'post',
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
tangles: { tangles: {
[rootHashB]: tangleB, [rootHashB]: tangleB,
@ -82,34 +82,34 @@ test('simple multi-author tangle', (t) => {
test('lipmaa in multi-author tangle', (t) => { test('lipmaa in multi-author tangle', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob') const keypairB = Keypair.generate('ed25519', 'bob')
const groupA = MsgV2.getMsgHash(MsgV2.createGroup(keypairA, 'alice')) const identityA = MsgV3.getMsgHash(MsgV3.createIdentity(keypairA, 'alice'))
const groupB = MsgV2.getMsgHash(MsgV2.createGroup(keypairB, 'bob')) const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'bob'))
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }
const rootMsgA = MsgV2.createRoot(groupA, 'post', keypairA) const rootMsgA = MsgV3.createRoot(identityA, 'post', keypairA)
const rootHashA = MsgV2.getMsgHash(rootMsgA) const rootHashA = MsgV3.getMsgHash(rootMsgA)
const tangleA = new MsgV2.Tangle(rootHashA) const tangleA = new MsgV3.Tangle(rootHashA)
tangleA.add(rootHashA, rootMsgA) tangleA.add(rootHashA, rootMsgA)
const rootMsgB = MsgV2.createRoot(groupB, 'post', keypairB) const rootMsgB = MsgV3.createRoot(identityB, 'post', keypairB)
const rootHashB = MsgV2.getMsgHash(rootMsgB) const rootHashB = MsgV3.getMsgHash(rootMsgB)
const tangleB = new MsgV2.Tangle(rootHashB) const tangleB = new MsgV3.Tangle(rootHashB)
tangleB.add(rootHashB, rootMsgB) tangleB.add(rootHashB, rootMsgB)
const msg1 = MsgV2.create({ const msg1 = MsgV3.create({
group: groupA, identity: identityA,
groupTips: [groupA], identityTips: [identityA],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHashA]: tangleA, [rootHashA]: tangleA,
}, },
keypair: keypairA, keypair: keypairA,
}) })
const msgHash1 = MsgV2.getMsgHash(msg1) const msgHash1 = MsgV3.getMsgHash(msg1)
tangleA.add(msgHash1, msg1) tangleA.add(msgHash1, msg1)
const tangleThread = new MsgV2.Tangle(msgHash1) const tangleThread = new MsgV3.Tangle(msgHash1)
tangleThread.add(msgHash1, msg1) tangleThread.add(msgHash1, msg1)
assert.deepEqual( assert.deepEqual(
@ -118,10 +118,10 @@ test('lipmaa in multi-author tangle', (t) => {
'A:msg1 has only feed tangle' 'A:msg1 has only feed tangle'
) )
const msg2 = MsgV2.create({ const msg2 = MsgV3.create({
group: groupB, identity: identityB,
groupTips: [groupB], identityTips: [identityB],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHashB]: tangleB, [rootHashB]: tangleB,
@ -129,7 +129,7 @@ test('lipmaa in multi-author tangle', (t) => {
}, },
keypair: keypairB, keypair: keypairB,
}) })
const msgHash2 = MsgV2.getMsgHash(msg2) const msgHash2 = MsgV3.getMsgHash(msg2)
tangleB.add(msgHash2, msg2) tangleB.add(msgHash2, msg2)
tangleThread.add(msgHash2, msg2) tangleThread.add(msgHash2, msg2)
@ -139,10 +139,10 @@ test('lipmaa in multi-author tangle', (t) => {
'B:msg2 points to A:msg1' 'B:msg2 points to A:msg1'
) )
const msg3 = MsgV2.create({ const msg3 = MsgV3.create({
group: groupB, identity: identityB,
groupTips: [groupB], identityTips: [identityB],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHashB]: tangleB, [rootHashB]: tangleB,
@ -150,7 +150,7 @@ test('lipmaa in multi-author tangle', (t) => {
}, },
keypair: keypairB, keypair: keypairB,
}) })
const msgHash3 = MsgV2.getMsgHash(msg3) const msgHash3 = MsgV3.getMsgHash(msg3)
tangleB.add(msgHash3, msg3) tangleB.add(msgHash3, msg3)
tangleThread.add(msgHash3, msg3) tangleThread.add(msgHash3, msg3)
@ -160,10 +160,10 @@ test('lipmaa in multi-author tangle', (t) => {
'B:msg3 points to B:msg2' 'B:msg3 points to B:msg2'
) )
const msg4 = MsgV2.create({ const msg4 = MsgV3.create({
group: groupA, identity: identityA,
groupTips: [groupA], identityTips: [identityA],
type: 'post', domain: 'post',
data, data,
tangles: { tangles: {
[rootHashA]: tangleA, [rootHashA]: tangleA,
@ -171,7 +171,7 @@ test('lipmaa in multi-author tangle', (t) => {
}, },
keypair: keypairA, keypair: keypairA,
}) })
const msgHash4 = MsgV2.getMsgHash(msg4) const msgHash4 = MsgV3.getMsgHash(msg4)
tangleB.add(msgHash4, msg4) tangleB.add(msgHash4, msg4)
tangleThread.add(msgHash4, msg4) tangleThread.add(msgHash4, msg4)

View File

@ -0,0 +1,118 @@
const test = require('node:test')
const assert = require('node:assert')
const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3')
test('validate root msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'alice'))
const pubkeys = new Set([keypair.public])
const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV3.Tangle(rootHash)
const err = MsgV3.validate(rootMsg, tangle, pubkeys, rootHash, rootHash)
assert.ifError(err, 'valid root msg')
})
test('validate identity tangle', (t) => {
const pubkeys = new Set()
const keypair1 = Keypair.generate('ed25519', 'alice')
pubkeys.add(keypair1.public)
const identityMsg0 = MsgV3.createIdentity(keypair1, 'alice')
const identity = MsgV3.getMsgHash(identityMsg0)
const identityMsg0Hash = identity
const tangle = new MsgV3.Tangle(identity)
let err = MsgV3.validate(identityMsg0, tangle, pubkeys, identityMsg0Hash, identity)
assert.ifError(err, 'valid identity root msg')
tangle.add(identity, identityMsg0)
const keypair2 = Keypair.generate('ed25519', 'bob')
const identityMsg1 = MsgV3.create({
identity: null,
identityTips: null,
domain: 'identity',
data: { add: keypair2.public },
tangles: {
[identity]: tangle,
},
keypair: keypair1, // announcing keypair2 but signing with keypair1
})
const identityMsg1Hash = MsgV3.getMsgHash(identityMsg1)
err = MsgV3.validate(identityMsg1, tangle, pubkeys, identityMsg1Hash, identity)
assert.ifError(err, 'valid identity msg')
})
test('validate 2nd msg with existing root', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'alice'))
const pubkeys = new Set([keypair.public])
const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1 = MsgV3.create({
identity,
identityTips: [identity],
domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[rootHash]: tangle,
},
keypair,
})
const msgHash1 = MsgV3.getMsgHash(msg1)
tangle.add(msgHash1, msg1)
const err = MsgV3.validate(msg1, tangle, pubkeys, msgHash1, rootHash)
assert.ifError(err, 'valid 2nd msg')
})
test('validate 2nd forked msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'alice'))
const pubkeys = new Set([keypair.public])
const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV3.getMsgHash(rootMsg)
const tangle = new MsgV3.Tangle(rootHash)
tangle.add(rootHash, rootMsg)
const msg1A = MsgV3.create({
identity,
identityTips: [identity],
domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[rootHash]: tangle,
},
keypair,
})
const msgHash1A = MsgV3.getMsgHash(msg1A)
const msg1B = MsgV3.create({
identity,
identityTips: [identity],
domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[rootHash]: tangle,
},
keypair,
})
const msgHash1B = MsgV3.getMsgHash(msg1B)
tangle.add(msgHash1A, msg1A)
tangle.add(msgHash1B, msg1B)
const err = MsgV3.validate(msg1B, tangle, pubkeys, msgHash1B, rootHash)
assert.ifError(err, 'valid 2nd forked msg')
})

View File

@ -19,12 +19,12 @@ test('msgs() iterator', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const group = (await p(peer.db.group.create)(null)).hash const identity = (await p(peer.db.identity.create)(null)).hash
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
await p(peer.db.feed.publish)({ await p(peer.db.feed.publish)({
group, identity,
type: i % 2 === 0 ? 'post' : 'about', domain: i % 2 === 0 ? 'post' : 'about',
data: data:
i % 2 === 0 i % 2 === 0
? { text: 'hello ' + i } ? { text: 'hello ' + i }
@ -36,8 +36,8 @@ test('msgs() iterator', async (t) => {
const abouts = [] const abouts = []
for (const msg of peer.db.msgs()) { for (const msg of peer.db.msgs()) {
if (!msg.data) continue if (!msg.data) continue
if (msg.metadata.type === 'post') posts.push(msg.data.text) if (msg.metadata.domain === 'post') posts.push(msg.data.text)
else if (msg.metadata.type === 'about') abouts.push(msg.data.name) else if (msg.metadata.domain === 'about') abouts.push(msg.data.name)
} }
assert.deepEqual(posts, ['hello 0', 'hello 2', 'hello 4'], 'queried posts') assert.deepEqual(posts, ['hello 0', 'hello 2', 'hello 4'], 'queried posts')

View File

@ -19,7 +19,7 @@ test('onRecordAdded', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const group = (await p(peer.db.group.create)(null)).hash const identity = (await p(peer.db.identity.create)(null)).hash
const listened = [] const listened = []
var remove = peer.db.onRecordAdded((ev) => { var remove = peer.db.onRecordAdded((ev) => {
@ -27,8 +27,8 @@ test('onRecordAdded', async (t) => {
}) })
const rec1 = await p(peer.db.feed.publish)({ const rec1 = await p(peer.db.feed.publish)({
group, identity,
type: 'post', domain: 'post',
data: { text: 'I am hungry' }, data: { text: 'I am hungry' },
}) })
assert.equal(rec1.msg.data.text, 'I am hungry', 'msg1 text correct') assert.equal(rec1.msg.data.text, 'I am hungry', 'msg1 text correct')
@ -36,7 +36,7 @@ test('onRecordAdded', async (t) => {
await p(setTimeout)(500) await p(setTimeout)(500)
assert.equal(listened.length, 3) assert.equal(listened.length, 3)
assert.equal(listened[0].msg.metadata.group, null, 'group root') assert.equal(listened[0].msg.metadata.identity, null, 'identity root')
assert.equal(listened[1].msg.data, null, 'root') assert.equal(listened[1].msg.data, null, 'root')
assert.equal(listened[1].msg.metadata.dataSize, 0, 'root') assert.equal(listened[1].msg.metadata.dataSize, 0, 'root')
assert.deepEqual(listened[2], rec1, 'actual record') assert.deepEqual(listened[2], rec1, 'actual record')

View File

@ -19,14 +19,14 @@ test('publish some msgs, close, re-open', async (t) => {
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const group = (await p(peer.db.group.create)(null)).hash const identity = (await p(peer.db.identity.create)(null)).hash
// t.pass('opened db') // t.pass('opened db')
const msgHashes = [] const msgHashes = []
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
const rec = await p(peer.db.feed.publish)({ const rec = await p(peer.db.feed.publish)({
group, identity,
type: 'post', domain: 'post',
data: { text: 'hello ' + i }, data: { text: 'hello ' + i },
}) })
msgHashes.push(rec.hash) msgHashes.push(rec.hash)
@ -49,7 +49,7 @@ test('publish some msgs, close, re-open', async (t) => {
const texts = [] const texts = []
for (const msg of peer2.db.msgs()) { for (const msg of peer2.db.msgs()) {
if (!msg.data || !msg.metadata.group) continue if (!msg.data || !msg.metadata.identity) continue
texts.push(msg.data.text) texts.push(msg.data.text)
} }

View File

@ -18,12 +18,12 @@ test('records() iterator', async (t) => {
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const group = (await p(peer.db.group.create)(null)).hash const identity = (await p(peer.db.identity.create)(null)).hash
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
await p(peer.db.feed.publish)({ await p(peer.db.feed.publish)({
group, identity,
type: i % 2 === 0 ? 'post' : 'about', domain: i % 2 === 0 ? 'post' : 'about',
data: data:
i % 2 === 0 i % 2 === 0
? { text: 'hello ' + i } ? { text: 'hello ' + i }
@ -34,7 +34,7 @@ test('records() iterator', async (t) => {
let count = 0 let count = 0
for (const rec of peer.db.records()) { for (const rec of peer.db.records()) {
if (!rec.msg.data) continue if (!rec.msg.data) continue
if (!rec.msg.metadata.group) continue if (!rec.msg.metadata.identity) continue
assert.ok(rec.misc.size > rec.msg.metadata.dataSize, 'size > dataSize') assert.ok(rec.misc.size > rec.msg.metadata.dataSize, 'size > dataSize')
count++ count++
} }