From d97d29fca0ceb2a8ce9b2bfd64cc4bdbf6351933 Mon Sep 17 00:00:00 2001 From: Andre Staltz Date: Sun, 25 Jun 2023 11:07:22 +0300 Subject: [PATCH] rename type=>domain, group=>identity --- lib/encryption.js | 4 +- lib/index.js | 68 +++--- lib/msg-v2/is-feed-root.js | 22 -- lib/{msg-v2 => msg-v3}/get-msg-id.js | 10 +- lib/{msg-v2 => msg-v3}/index.js | 133 ++++++----- lib/msg-v3/is-feed-root.js | 22 ++ lib/{msg-v2 => msg-v3}/represent-data.js | 0 lib/{msg-v2 => msg-v3}/strip.js | 10 +- lib/{msg-v2 => msg-v3}/tangle.js | 6 +- lib/{msg-v2 => msg-v3}/validation.js | 58 ++--- protospec.md | 112 +++++++++ test/add.test.js | 22 +- test/del.test.js | 12 +- test/erase.test.js | 10 +- test/feed-get-id.test.js | 14 +- test/feed-publish.test.js | 50 ++-- test/get.test.js | 14 +- test/getTangle.test.js | 26 +-- ...group-add.test.js => identity-add.test.js} | 52 ++--- ...create.test.js => identity-create.test.js} | 26 +-- test/msg-v2/validate.test.js | 118 ---------- test/{msg-v2 => msg-v3}/create.test.js | 156 ++++++------- .../invalid-domain.test.js} | 46 ++-- test/{msg-v2 => msg-v3}/invalid-prev.test.js | 220 +++++++++--------- test/{msg-v2 => msg-v3}/lipmaa.test.js | 80 +++---- test/{msg-v2 => msg-v3}/tangles.test.js | 96 ++++---- test/msg-v3/validate.test.js | 118 ++++++++++ test/msgs-iterator.test.js | 10 +- test/on-record-added.test.js | 8 +- test/re-open.test.js | 8 +- test/records-iterator.test.js | 8 +- 31 files changed, 829 insertions(+), 710 deletions(-) delete mode 100644 lib/msg-v2/is-feed-root.js rename lib/{msg-v2 => msg-v3}/get-msg-id.js (81%) rename lib/{msg-v2 => msg-v3}/index.js (65%) create mode 100644 lib/msg-v3/is-feed-root.js rename lib/{msg-v2 => msg-v3}/represent-data.js (100%) rename lib/{msg-v2 => msg-v3}/strip.js (66%) rename lib/{msg-v2 => msg-v3}/tangle.js (97%) rename lib/{msg-v2 => msg-v3}/validation.js (81%) rename test/{group-add.test.js => identity-add.test.js} (63%) rename test/{group-create.test.js => identity-create.test.js} (63%) delete mode 100644 test/msg-v2/validate.test.js rename test/{msg-v2 => msg-v3}/create.test.js (53%) rename test/{msg-v2/invalid-type.test.js => msg-v3/invalid-domain.test.js} (58%) rename test/{msg-v2 => msg-v3}/invalid-prev.test.js (53%) rename test/{msg-v2 => msg-v3}/lipmaa.test.js (64%) rename test/{msg-v2 => msg-v3}/tangles.test.js (59%) create mode 100644 test/msg-v3/validate.test.js diff --git a/lib/encryption.js b/lib/encryption.js index 9238171..fbe6e1d 100644 --- a/lib/encryption.js +++ b/lib/encryption.js @@ -1,6 +1,6 @@ const base58 = require('bs58') const b4a = require('b4a') -const MsgV2 = require('./msg-v2') +const MsgV3 = require('./msg-v3') /** * @typedef {import('./index').Rec} Rec @@ -48,7 +48,7 @@ function decrypt(rec, peer, config) { if (!plaintextBuf) return rec // Reconstruct KVT in JS encoding - const msgDecrypted = MsgV2.fromPlaintextBuffer(plaintextBuf, msgEncrypted) + const msgDecrypted = MsgV3.fromPlaintextBuffer(plaintextBuf, msgEncrypted) return { hash: rec.hash, diff --git a/lib/index.js b/lib/index.js index 664ffdb..12cb8ea 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,12 +5,12 @@ const promisify = require('promisify-4loc') const b4a = require('b4a') const base58 = require('bs58') const Obz = require('obz') -const MsgV2 = require('./msg-v2') +const MsgV2 = require('./msg-v3') const { ReadyGate } = require('./utils') 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 pubkeys = new Set() - if (msg.metadata.group) { - const groupTangle = new DBTangle(msg.metadata.group, records()) - if (!groupTangle.has(msg.metadata.group)) { + if (msg.metadata.identity) { + const identityTangle = new DBTangle(msg.metadata.identity, records()) + if (!identityTangle.has(msg.metadata.identity)) { // 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) if (!msg?.data?.add) continue pubkeys.add(msg.data.add) @@ -225,12 +225,12 @@ exports.init = function initDB(peer, config) { function initializeFeed(opts, cb) { 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) - const feedRoot = MsgV2.createRoot(group, type, keypair) + const feedRoot = MsgV2.createRoot(identity, domain, keypair) add(feedRoot, MsgV2.getMsgHash(feedRoot), (err, rec) => { // prettier-ignore 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 let msg try { - msg = MsgV2.createGroup(keypair, opts?._nonce) + msg = MsgV2.createIdentity(keypair, opts?._nonce) } 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) logAppend(msgHash, msg, (err, rec) => { // 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) cb(null, rec) }) } - function addToGroup(opts, cb) { - if (!opts?.keypair) return cb(new Error('group.add() requires a `keypair`')) - if (!opts?.group) return cb(new Error('group.add() requires a `group`')) + function addToIdentity(opts, cb) { + if (!opts?.keypair) return cb(new Error('identity.add() requires a `keypair`')) + if (!opts?.identity) return cb(new Error('identity.add() requires a `identity`')) const addedKeypair = opts.keypair const signingKeypair = config.keypair // Fill-in tangle opts: - const tangles = populateTangles([opts.group]) + const tangles = populateTangles([opts.identity]) const fullOpts = { - group: null, - groupTips: null, + identity: null, + identityTips: null, tangles, keypair: signingKeypair, data: { add: addedKeypair.public }, - type: 'group', + domain: 'identity', } // Create the actual message: @@ -279,13 +279,13 @@ exports.init = function initDB(peer, config) { try { msg = MsgV2.create(fullOpts) } 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) logAppend(msgHash, msg, (err, rec) => { // 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) 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.type) return cb(new Error('feed.publish() requires a `type`')) - if (!opts.group) return cb(new Error('feed.publish() requires a `group`')) + if (!opts.domain) return cb(new Error('feed.publish() requires a `domain`')) + if (!opts.identity) return cb(new Error('feed.publish() requires a `identity`')) initializeFeed(opts, (err, feedRootHash) => { // prettier-ignore @@ -314,9 +314,9 @@ exports.init = function initDB(peer, config) { const tangleTemplates = opts.tangles ?? [] tangleTemplates.push(feedRootHash) const tangles = populateTangles(tangleTemplates) - const groupTangle = new DBTangle(opts.group, records()) - const groupTips = [...groupTangle.getTips()] - const fullOpts = { ...opts, tangles, groupTips, keypair } + const identityTangle = new DBTangle(opts.identity, records()) + const identityTips = [...identityTangle.getTips()] + const fullOpts = { ...opts, tangles, identityTips, keypair } // If opts ask for encryption, encrypt and put ciphertext in opts.data const recps = fullOpts.data.recps @@ -367,10 +367,10 @@ exports.init = function initDB(peer, config) { }) } - function getFeedId(groupId, findType) { - const findGroup = MsgV2.stripGroup(groupId) + function getFeedId(id, findDomain) { + const findIdentity = MsgV2.stripIdentity(id) 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 } @@ -436,9 +436,9 @@ exports.init = function initDB(peer, config) { installEncryptionFormat, loaded, add, - group: { - create: createGroup, - add: addToGroup, + identity: { + create: createIdentity, + add: addToIdentity, }, feed: { publish: publishToFeed, diff --git a/lib/msg-v2/is-feed-root.js b/lib/msg-v2/is-feed-root.js deleted file mode 100644 index 4342598..0000000 --- a/lib/msg-v2/is-feed-root.js +++ /dev/null @@ -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 diff --git a/lib/msg-v2/get-msg-id.js b/lib/msg-v3/get-msg-id.js similarity index 81% rename from lib/msg-v2/get-msg-id.js rename to lib/msg-v3/get-msg-id.js index 6d92036..2e4dbe2 100644 --- a/lib/msg-v2/get-msg-id.js +++ b/lib/msg-v3/get-msg-id.js @@ -23,7 +23,7 @@ function getMsgHashBuf(msg) { */ function getMsgHash(x) { if (typeof x === 'string') { - if (x.startsWith('ppppp:message/v2/')) { + if (x.startsWith('ppppp:message/v3/')) { const msgUri = x const parts = msgUri.split('/') return parts[parts.length - 1] @@ -43,12 +43,12 @@ function getMsgHash(x) { * @returns {string} */ function getMsgId(msg) { - const { group, type } = msg.metadata + const { identity, domain } = msg.metadata const msgHash = getMsgHash(msg) - if (type) { - return `ppppp:message/v2/${group}/${type}/${msgHash}` + if (domain) { + return `ppppp:message/v3/${identity}/${domain}/${msgHash}` } else { - return `ppppp:message/v2/${group}/${msgHash}` + return `ppppp:message/v3/${identity}/${msgHash}` } } diff --git a/lib/msg-v2/index.js b/lib/msg-v3/index.js similarity index 65% rename from lib/msg-v2/index.js rename to lib/msg-v3/index.js index 3b88e63..155067a 100644 --- a/lib/msg-v2/index.js +++ b/lib/msg-v3/index.js @@ -4,12 +4,12 @@ const b4a = require('b4a') const stringify = require('json-canon') const Keypair = require('ppppp-keypair') const union = require('set.prototype.union') -const { stripGroup } = require('./strip') +const { stripIdentity } = require('./strip') const isFeedRoot = require('./is-feed-root') const { getMsgId, getMsgHash } = require('./get-msg-id') const representData = require('./represent-data') const { - validateType, + validateDomain, validateData, validate, validateBatch, @@ -18,55 +18,56 @@ const { const Tangle = require('./tangle') /** - * @typedef {Iterator & {values: () => Iterator}} MsgIter * @typedef {import('ppppp-keypair').Keypair} Keypair + */ + +/** + * @typedef {Iterator & {values: () => Iterator}} MsgIter + * * @typedef {Buffer | Uint8Array} B4A + * + * @typedef {{ + * depth: number; + * prev: Array; + * }} TangleMetadata + * + * @typedef {{ + * data: any; + * metadata: { + * dataHash: string | null; + * dataSize: number; + * identity: string | null; + * identityTips: Array | null; + * tangles: Record; + * domain: string; + * v: 3; + * }; + * pubkey: string; + * sig: string; + * }} Msg + * + * @typedef {{ + * data: any; + * domain: string; + * keypair: Keypair; + * identity: string | null; + * identityTips: Array | null; + * tangles: Record; + * }} CreateOpts */ -/** - * @typedef {Object} TangleMetadata - * @property {number} depth - * @property {Array} prev - */ - -/** - * @typedef {Object} Msg - * @property {*} data - * @property {Object} metadata - * @property {string} metadata.dataHash - * @property {number} metadata.dataSize - * @property {string | null} metadata.group - * @property {Array | null} metadata.groupTips - * @property {Record} 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 | null} groupTips - * @property {Record} tangles - */ - -function getFeedRootHash(groupId, type) { - const group = stripGroup(groupId) - +function getFeedRootHash(id, domain) { + /** @type {Msg} */ const msg = { data: null, metadata: { dataHash: null, dataSize: 0, - group, - groupTips: null, + identity: stripIdentity(id), + identityTips: null, tangles: {}, - type, - v: 2, + domain, + v: 3, }, pubkey: '', sig: '', @@ -85,12 +86,12 @@ function toPlaintextBuffer(opts) { */ function create(opts) { 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') const [dataHash, dataSize] = representData(opts.data) - const group = opts.group ? stripGroup(opts.group) : null - const groupTips = opts.groupTips ? opts.groupTips.sort() : null + const identity = opts.identity ? stripIdentity(opts.identity) : null + const identityTips = opts.identityTips ? opts.identityTips.sort() : null const 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()`) } + /** @type {Msg} */ const msg = { data: opts.data, metadata: { dataHash, dataSize, - group, - groupTips, + identity, + identityTips, tangles, - type: opts.type, - v: 2, + domain: opts.domain, + v: 3, }, pubkey: opts.keypair.public, sig: '', @@ -132,25 +134,26 @@ function create(opts) { } /** - * @param {string} group - * @param {string} type + * @param {string} id + * @param {string} domain * @param {Keypair} keypair * @returns {Msg} */ -function createRoot(group, type, keypair) { +function createRoot(id, domain, keypair) { let err - if ((err = validateType(type))) throw err + if ((err = validateDomain(domain))) throw err + /** @type {Msg} */ const msg = { data: null, metadata: { dataHash: null, dataSize: 0, - group, - groupTips: null, + identity: id, + identityTips: null, tangles: {}, - type, - v: 2, + domain, + v: 3, }, pubkey: keypair.public, sig: '', @@ -168,14 +171,18 @@ function createRoot(group, type, keypair) { * @param {string} nonce * @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({ - data: { add: keypair.public, nonce }, - group: null, - groupTips: null, + data: { add: keypair.public, nonce: actualNonce }, + identity: null, + identityTips: null, keypair, tangles: {}, - type: 'group', + domain: 'identity', }) } @@ -203,9 +210,9 @@ module.exports = { getFeedRootHash, create, createRoot, - createGroup, + createIdentity, erase, - stripGroup, + stripIdentity, toPlaintextBuffer, fromPlaintextBuffer, Tangle, diff --git a/lib/msg-v3/is-feed-root.js b/lib/msg-v3/is-feed-root.js new file mode 100644 index 0000000..1e9cfdc --- /dev/null +++ b/lib/msg-v3/is-feed-root.js @@ -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 diff --git a/lib/msg-v2/represent-data.js b/lib/msg-v3/represent-data.js similarity index 100% rename from lib/msg-v2/represent-data.js rename to lib/msg-v3/represent-data.js diff --git a/lib/msg-v2/strip.js b/lib/msg-v3/strip.js similarity index 66% rename from lib/msg-v2/strip.js rename to lib/msg-v3/strip.js index 22e3bc4..40947af 100644 --- a/lib/msg-v2/strip.js +++ b/lib/msg-v3/strip.js @@ -5,7 +5,7 @@ function stripMsgKey(msgKey) { if (msgKey.key) return stripMsgKey(msgKey.key) else return getMsgHash(msgKey) } - if (msgKey.startsWith('ppppp:message/v2/')) { + if (msgKey.startsWith('ppppp:message/v3/')) { const parts = msgKey.split('/') return parts[parts.length - 1] } else { @@ -17,13 +17,13 @@ function stripMsgKey(msgKey) { * @param {string} id * @returns {string} */ -function stripGroup(id) { - if (id.startsWith('ppppp:group/v2/') === false) return id - const withoutPrefix = id.replace('ppppp:group/v2/', '') +function stripIdentity(id) { + if (id.startsWith('ppppp:identity/v3/') === false) return id + const withoutPrefix = id.replace('ppppp:identity/v3/', '') return withoutPrefix.split('/')[0] } module.exports = { stripMsgKey, - stripGroup, + stripIdentity, } diff --git a/lib/msg-v2/tangle.js b/lib/msg-v3/tangle.js similarity index 97% rename from lib/msg-v2/tangle.js rename to lib/msg-v3/tangle.js index f1647ee..15c1f41 100644 --- a/lib/msg-v2/tangle.js +++ b/lib/msg-v3/tangle.js @@ -189,14 +189,14 @@ class Tangle { const metadata = this.#rootMsg.metadata if (metadata.dataSize > 0) return false if (metadata.dataHash !== null) return false - if (metadata.groupTips !== null) return false + if (metadata.identityTips !== null) return false return true } getFeed() { if (!this.isFeed()) return null - const { group, type } = this.#rootMsg.metadata - return { group, type } + const { identity, domain } = this.#rootMsg.metadata + return { identity, domain } } shortestPathToRoot(msgHash) { diff --git a/lib/msg-v2/validation.js b/lib/msg-v3/validation.js similarity index 81% rename from lib/msg-v2/validation.js rename to lib/msg-v3/validation.js index 4e4d8a3..4bda8a8 100644 --- a/lib/msg-v2/validation.js +++ b/lib/msg-v3/validation.js @@ -24,21 +24,21 @@ function validateShape(msg) { // prettier-ignore return 'invalid message: must have metadata.dataSize\n' + JSON.stringify(msg) } - if (!('group' in msg.metadata)) { - return 'invalid message: must have metadata.group\n' + JSON.stringify(msg) + if (!('identity' in msg.metadata)) { + return 'invalid message: must have metadata.identity\n' + JSON.stringify(msg) } - if (!('groupTips' in msg.metadata)) { + if (!('identityTips' in msg.metadata)) { // 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)) { return 'invalid message: must have metadata.tangles\n' + JSON.stringify(msg) } - if (!('type' in msg.metadata)) { - return 'invalid message: must have metadata.type\n' + JSON.stringify(msg) + if (!('domain' in msg.metadata)) { + return 'invalid message: must have metadata.domain\n' + JSON.stringify(msg) } - if (msg.metadata.v !== 2) { - return 'invalid message: must have metadata.v 2\n' + JSON.stringify(msg) + if (msg.metadata.v !== 3) { + return 'invalid message: must have metadata.v=3\n' + JSON.stringify(msg) } if (typeof msg.sig !== 'string') { return 'invalid message: must have sig\n' + JSON.stringify(msg) @@ -63,13 +63,13 @@ function validatePubkey(msg) { } } -function validateGroupPubkey(msg, pubkeys) { - // Unusual case: if the msg is a feed root, ignore the group and pubkey +function validateIdentityPubkey(msg, pubkeys) { + // Unusual case: if the msg is a feed root, ignore the identity and pubkey if (isFeedRoot(msg)) return - if (msg.metadata.group && !pubkeys.has(msg.pubkey)) { + if (msg.metadata.identity && !pubkeys.has(msg.pubkey)) { // 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) } if (tangle.isFeed()) { - const { group, type } = tangle.getFeed() - if (type !== msg.metadata.type) { + const { identity, domain } = tangle.getFeed() + if (domain !== msg.metadata.domain) { // 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 - 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 @@ -213,22 +213,22 @@ function validateTangleRoot(msg, msgHash, tangleId) { } } -function validateType(type) { - if (!type || typeof type !== 'string') { +function validateDomain(domain) { + if (!domain || typeof domain !== 'string') { // 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 - 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 - 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 - 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 = validateDataSize(msg))) return err if ((err = validateData(msg))) return err - if ((err = validateType(msg.metadata.type))) return err - if ((err = validateGroupPubkey(msg, pubkeys))) return err + if ((err = validateDomain(msg.metadata.domain))) return err + if ((err = validateIdentityPubkey(msg, pubkeys))) return err if (tangle.size() === 0) { if ((err = validateTangleRoot(msg, msgHash, rootHash))) return err } else { @@ -277,7 +277,7 @@ function validate(msg, tangle, pubkeys, msgHash, rootHash) { } module.exports = { - validateType, + validateDomain, validateData, validate, validateMsgHash, diff --git a/protospec.md b/protospec.md index 4d4c0e7..6dc94e5 100644 --- a/protospec.md +++ b/protospec.md @@ -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 | 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 // 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 // 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 Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24 diff --git a/test/add.test.js b/test/add.test.js index 5573d50..9d8d3e5 100644 --- a/test/add.test.js +++ b/test/add.test.js @@ -7,7 +7,7 @@ const rimraf = require('rimraf') const SecretStack = require('secret-stack') const caps = require('ssb-caps') 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') rimraf.sync(DIR) @@ -21,25 +21,25 @@ test('add()', async (t) => { await peer.db.loaded() - const groupMsg0 = MsgV2.createGroup(keypair) - const group = MsgV2.getMsgHash(groupMsg0) + const identityMsg0 = MsgV3.createIdentity(keypair) + 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 rootHash = MsgV2.getMsgHash(rootMsg) + const rootMsg = MsgV3.createRoot(id, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) const recRoot = await p(peer.db.add)(rootMsg, rootHash) 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) - const inputMsg = MsgV2.create({ + const inputMsg = MsgV3.create({ keypair, - type: 'post', + domain: 'post', data: { text: 'This is the first post!' }, - group, - groupTips: [group], + identity: id, + identityTips: [id], tangles: { [rootHash]: tangle, }, diff --git a/test/del.test.js b/test/del.test.js index 808f46b..b9fcc04 100644 --- a/test/del.test.js +++ b/test/del.test.js @@ -21,13 +21,13 @@ test('del', async (t) => { 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 = [] for (let i = 0; i < 5; i++) { const rec = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity: id, + domain: 'post', data: { text: 'm' + i }, }) msgHashes.push(rec.hash) @@ -35,7 +35,7 @@ test('del', async (t) => { const before = [] 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') @@ -44,7 +44,7 @@ test('del', async (t) => { const after = [] 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') @@ -83,7 +83,7 @@ test('del', async (t) => { assert.deepEqual( persistedMsgs - .filter((msg) => msg.data && msg.metadata.group) + .filter((msg) => msg.data && msg.metadata.identity) .map((msg) => msg.data.text), ['m0', 'm1', 'm3', 'm4'], 'msgs in disk after the delete' diff --git a/test/erase.test.js b/test/erase.test.js index 0126bc3..90eef43 100644 --- a/test/erase.test.js +++ b/test/erase.test.js @@ -21,13 +21,13 @@ test('erase', async (t) => { 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 = [] for (let i = 0; i < 5; i++) { const rec = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity: id, + domain: 'post', data: { text: 'm' + i }, }) msgHashes.push(rec.hash) @@ -35,7 +35,7 @@ test('erase', async (t) => { const before = [] 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( @@ -48,7 +48,7 @@ test('erase', async (t) => { const after = [] 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') diff --git a/test/feed-get-id.test.js b/test/feed-get-id.test.js index 1a76e05..0c0f95e 100644 --- a/test/feed-get-id.test.js +++ b/test/feed-get-id.test.js @@ -7,14 +7,14 @@ const rimraf = require('rimraf') const SecretStack = require('secret-stack') const caps = require('ssb-caps') 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') rimraf.sync(DIR) const keypair = Keypair.generate('ed25519', 'alice') let peer -let group +let id let rootMsg let rootHash test('setup', async (t) => { @@ -25,16 +25,16 @@ test('setup', async (t) => { await peer.db.loaded() - group = (await p(peer.db.group.create)(null)).hash - rootMsg = MsgV2.createRoot(group, 'post', keypair) - rootHash = MsgV2.getMsgHash(rootMsg) + id = (await p(peer.db.identity.create)(null)).hash + rootMsg = MsgV3.createRoot(id, 'post', keypair) + rootHash = MsgV3.getMsgHash(rootMsg) await p(peer.db.add)(rootMsg, rootHash) }) test('feed.getId()', async (t) => { - const id = peer.db.feed.getId(group, 'post') - assert.equal(id, rootHash, 'feed.getId() returns root hash') + const feedId = peer.db.feed.getId(id, 'post') + assert.equal(feedId, rootHash, 'feed.getId() returns root hash') }) test('teardown', (t) => { diff --git a/test/feed-publish.test.js b/test/feed-publish.test.js index 0d5f207..1faac04 100644 --- a/test/feed-publish.test.js +++ b/test/feed-publish.test.js @@ -7,7 +7,7 @@ const rimraf = require('rimraf') const SecretStack = require('secret-stack') const caps = require('ssb-caps') 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') rimraf.sync(DIR) @@ -15,7 +15,7 @@ rimraf.sync(DIR) const keypair = Keypair.generate('ed25519', 'alice') const bobKeypair = Keypair.generate('ed25519', 'bob') let peer -let group +let id let rootMsg let rootHash test('setup', async (t) => { @@ -26,9 +26,9 @@ test('setup', async (t) => { await peer.db.loaded() - group = (await p(peer.db.group.create)(null)).hash - rootMsg = MsgV2.createRoot(group, 'post', keypair) - rootHash = MsgV2.getMsgHash(rootMsg) + id = (await p(peer.db.identity.create)(null)).hash + rootMsg = MsgV3.createRoot(id, 'post', keypair) + rootHash = MsgV3.getMsgHash(rootMsg) }) let msgHash1 @@ -36,8 +36,8 @@ let rec1 let msgHash2 test('feed.publish()', async (t) => { rec1 = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity: id, + domain: 'post', data: { text: 'I am 1st post' }, }) 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' ) - msgHash1 = MsgV2.getMsgHash(rec1.msg) + msgHash1 = MsgV3.getMsgHash(rec1.msg) const rec2 = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity: id, + domain: 'post', data: { text: 'I am 2nd post' }, }) assert.equal(rec2.msg.data.text, 'I am 2nd post', 'msg2 text correct') @@ -70,19 +70,19 @@ test('feed.publish()', async (t) => { [msgHash1], 'msg2 tangle prev correct' ) - msgHash2 = MsgV2.getMsgHash(rec2.msg) + msgHash2 = MsgV3.getMsgHash(rec2.msg) }) 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(rec1.hash, rec1.msg) - const msg3 = MsgV2.create({ + const msg3 = MsgV3.create({ keypair, - group, - groupTips: [group], - type: 'post', + identity: id, + identityTips: [id], + domain: 'post', data: { text: '3rd post forked from 1st' }, tangles: { [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 msgHash3 = MsgV2.getMsgHash(rec3.msg) + const msgHash3 = MsgV3.getMsgHash(rec3.msg) const rec4 = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity: id, + domain: 'post', data: { text: 'I am 4th post' }, }) 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) => { const recEncrypted = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity: id, + domain: 'post', data: { text: 'I am chewing food', recps: [keypair.public] }, encryptionFormat: 'box', }) @@ -133,15 +133,15 @@ test('feed.publish() encrypted with box', async (t) => { test('feed.publish() with tangles', async (t) => { const recA = await p(peer.db.feed.publish)({ - group, - type: 'comment', + identity: id, + domain: 'comment', data: { text: 'I am root' }, }) assert.equal(recA.msg.data.text, 'I am root', 'root text correct') const recB = await p(peer.db.feed.publish)({ - group, - type: 'comment', + identity: id, + domain: 'comment', data: { text: 'I am comment 1' }, tangles: [recA.hash], keypair: bobKeypair, diff --git a/test/get.test.js b/test/get.test.js index cf2317b..df5c968 100644 --- a/test/get.test.js +++ b/test/get.test.js @@ -7,14 +7,14 @@ const rimraf = require('rimraf') const SecretStack = require('secret-stack') const caps = require('ssb-caps') 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') rimraf.sync(DIR) const keypair = Keypair.generate('ed25519', 'alice') let peer -let group +let id let msgHash1 let msgId1 test('setup', async (t) => { @@ -25,15 +25,15 @@ test('setup', async (t) => { 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)({ - group, - type: 'post', + identity: id, + domain: 'post', data: { text: 'I am 1st post' }, }) - msgHash1 = MsgV2.getMsgHash(rec1.msg) - msgId1 = MsgV2.getMsgId(rec1.msg) + msgHash1 = MsgV3.getMsgHash(rec1.msg) + msgId1 = MsgV3.getMsgId(rec1.msg) }) test('get() supports ppppp URIs', async (t) => { diff --git a/test/getTangle.test.js b/test/getTangle.test.js index c34013f..6e86e84 100644 --- a/test/getTangle.test.js +++ b/test/getTangle.test.js @@ -26,7 +26,7 @@ test('setup', async (t) => { 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 const originalAppend = peer.db._getLog().append @@ -36,25 +36,25 @@ test('setup', async (t) => { rootPost = ( await p(peer.db.feed.publish)({ - group, + identity: id, keypair: keypairA, - type: 'comment', + domain: 'comment', data: { text: 'root' }, }) ).hash const [{ hash: reply1B }, { hash: reply1C }] = await Promise.all([ p(peer.db.feed.publish)({ - group, + identity: id, keypair: keypairB, - type: 'comment', + domain: 'comment', data: { text: 'reply 1B' }, tangles: [rootPost], }), p(peer.db.feed.publish)({ - group, + identity: id, keypair: keypairC, - type: 'comment', + domain: 'comment', data: { text: 'reply 1C' }, tangles: [rootPost], }), @@ -64,9 +64,9 @@ test('setup', async (t) => { reply2A = ( await p(peer.db.feed.publish)({ - group, + identity: id, keypair: keypairA, - type: 'comment', + domain: 'comment', data: { text: 'reply 2' }, tangles: [rootPost], }) @@ -74,16 +74,16 @@ test('setup', async (t) => { const [{ hash: reply3B }, { hash: reply3C }] = await Promise.all([ p(peer.db.feed.publish)({ - group, + identity: id, keypair: keypairB, - type: 'comment', + domain: 'comment', data: { text: 'reply 3B' }, tangles: [rootPost], }), p(peer.db.feed.publish)({ - group, + identity: id, keypair: keypairC, - type: 'comment', + domain: 'comment', data: { text: 'reply 3C' }, tangles: [rootPost], }), diff --git a/test/group-add.test.js b/test/identity-add.test.js similarity index 63% rename from test/group-add.test.js rename to test/identity-add.test.js index 775b82a..bfe4b8c 100644 --- a/test/group-add.test.js +++ b/test/identity-add.test.js @@ -8,10 +8,10 @@ const SecretStack = require('secret-stack') const caps = require('ssb-caps') 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) -test('group.add()', async (t) => { +test('identity.add()', async (t) => { const keypair1 = Keypair.generate('ed25519', 'alice') const keypair2 = Keypair.generate('ed25519', 'bob') @@ -21,19 +21,19 @@ test('group.add()', async (t) => { .call(null, { keypair: keypair1, path: DIR }) await peer.db.loaded() - const groupRec0 = await p(peer.db.group.create)({ keypair: keypair1 }) - const group = groupRec0.hash + const identityRec0 = await p(peer.db.identity.create)({ keypair: keypair1 }) + const id = identityRec0.hash - const groupRec1 = await p(peer.db.group.add)({ group, keypair: keypair2 }) - assert.ok(groupRec1, 'groupRec1 exists') - const { hash, msg } = groupRec1 + const identityRec1 = await p(peer.db.identity.add)({ identity: id, keypair: keypair2 }) + assert.ok(identityRec1, 'identityRec1 exists') + const { hash, msg } = identityRec1 assert.ok(hash, 'hash exists') 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.groupTips, null, 'msg.metadata.groupTips') + assert.equal(msg.metadata.identity, null, 'msg.metadata.identity') + assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips') assert.deepEqual( msg.metadata.tangles, - { [group]: { depth: 1, prev: [group] } }, + { [id]: { depth: 1, prev: [id] } }, 'msg.metadata.tangles' ) assert.equal(msg.pubkey, keypair1.public, 'msg.pubkey OLD KEY') @@ -41,7 +41,7 @@ test('group.add()', async (t) => { 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) const keypair1 = Keypair.generate('ed25519', 'alice') @@ -54,33 +54,33 @@ test('publish with a key in the group', async (t) => { await peer.db.loaded() - const groupRec0 = await p(peer.db.group.create)({ keypair: keypair1 }) - const group = groupRec0.hash - const groupRec1 = await p(peer.db.group.add)({ group, keypair: keypair2 }) + const identityRec0 = await p(peer.db.identity.create)({ keypair: keypair1 }) + const identity = identityRec0.hash + const identityRec1 = await p(peer.db.identity.add)({ identity, keypair: keypair2 }) const postRec = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity, + domain: 'post', data: { text: 'hello' }, keypair: keypair2, }) 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') const recs = [...peer.db.records()] assert.equal(recs.length, 4, '4 records') - const [_groupRec0, _groupRec1, postsRoot, _post] = recs - assert.deepEqual(_groupRec0.msg, groupRec0.msg, 'groupMsg0') - assert.deepEqual(_groupRec1.msg, groupRec1.msg, 'groupMsg1') + const [_identityRec0, _identityRec1, postsRoot, _post] = recs + assert.deepEqual(_identityRec0.msg, identityRec0.msg, 'identityMsg0') + assert.deepEqual(_identityRec1.msg, identityRec1.msg, 'identityMsg1') assert.deepEqual(postsRoot.msg.metadata, { dataHash: null, dataSize: 0, - group, - groupTips: null, + identity, + identityTips: null, tangles: {}, - type: 'post', - v: 2, + domain: 'post', + v: 3, }, 'postsRoot') 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 p(carol.db.add)(groupRec0.msg, group) - await p(carol.db.add)(groupRec1.msg, group) + await p(carol.db.add)(identityRec0.msg, identity) + await p(carol.db.add)(identityRec1.msg, identity) await p(carol.db.add)(postsRoot.msg, postsId) await p(carol.db.add)(postRec.msg, postsId) // t.pass('carol added all messages successfully') diff --git a/test/group-create.test.js b/test/identity-create.test.js similarity index 63% rename from test/group-create.test.js rename to test/identity-create.test.js index 7454df5..6fa6b33 100644 --- a/test/group-create.test.js +++ b/test/identity-create.test.js @@ -8,10 +8,10 @@ const SecretStack = require('secret-stack') const caps = require('ssb-caps') 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) -test('group.create() without args', async (t) => { +test('identity.create() without args', async (t) => { const keypair = Keypair.generate('ed25519', 'alice') const peer = SecretStack({ appKey: caps.shs }) .use(require('../lib')) @@ -19,20 +19,20 @@ test('group.create() without args', async (t) => { .call(null, { keypair, path: DIR }) await peer.db.loaded() - const groupRec0 = await p(peer.db.group.create)({}) - assert.ok(groupRec0, 'groupRec0 exists') - const { hash, msg } = groupRec0 + const identityRec0 = await p(peer.db.identity.create)({}) + assert.ok(identityRec0, 'identityRec0 exists') + const { hash, msg } = identityRec0 assert.ok(hash, 'hash exists') assert.equal(msg.data.add, keypair.public, 'msg.data.add') - assert.equal(msg.metadata.group, null, 'msg.metadata.group') - assert.equal(msg.metadata.groupTips, null, 'msg.metadata.groupTips') + assert.equal(msg.metadata.identity, null, 'msg.metadata.identity') + assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips') assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles') assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') 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 peer = SecretStack({ appKey: caps.shs }) @@ -41,13 +41,13 @@ test('group.create() with "keypair" arg', async (t) => { .call(null, { keypair, path: DIR }) await peer.db.loaded() - const groupRec0 = await p(peer.db.group.create)({ keypair }) - assert.ok(groupRec0, 'groupRec0 exists') - const { hash, msg } = groupRec0 + const identityRec0 = await p(peer.db.identity.create)({ keypair }) + assert.ok(identityRec0, 'identityRec0 exists') + const { hash, msg } = identityRec0 assert.ok(hash, 'hash exists') assert.equal(msg.data.add, keypair.public, 'msg.data.add') - assert.equal(msg.metadata.group, null, 'msg.metadata.group') - assert.equal(msg.metadata.groupTips, null, 'msg.metadata.groupTips') + assert.equal(msg.metadata.identity, null, 'msg.metadata.identity') + assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips') assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles') assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') diff --git a/test/msg-v2/validate.test.js b/test/msg-v2/validate.test.js deleted file mode 100644 index 19c7b7d..0000000 --- a/test/msg-v2/validate.test.js +++ /dev/null @@ -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') -}) diff --git a/test/msg-v2/create.test.js b/test/msg-v3/create.test.js similarity index 53% rename from test/msg-v2/create.test.js rename to test/msg-v3/create.test.js index 542db4a..002c7f8 100644 --- a/test/msg-v2/create.test.js +++ b/test/msg-v3/create.test.js @@ -1,64 +1,64 @@ const test = require('node:test') const assert = require('node:assert') const Keypair = require('ppppp-keypair') -const MsgV2 = require('../../lib/msg-v2') +const MsgV3 = require('../../lib/msg-v3') -let group -test('MsgV2.createGroup()', (t) => { +let identity +test('MsgV3.createIdentity()', (t) => { const keypair = Keypair.generate('ed25519', 'alice') - const groupMsg0 = MsgV2.createGroup(keypair, 'MYNONCE') - console.log(JSON.stringify(groupMsg0, null, 2)) + const identityMsg0 = MsgV3.createIdentity(keypair, 'MYNONCE') + console.log(JSON.stringify(identityMsg0, null, 2)) - assert.equal(groupMsg0.data.add, keypair.public, 'data.add') - assert.equal(groupMsg0.metadata.dataHash, 'THi3VkJeaf8aTkLSNJUdFD', 'hash') - assert.equal(groupMsg0.metadata.dataSize, 72, 'size') - assert.equal(groupMsg0.metadata.group, null, 'group') - assert.equal(groupMsg0.metadata.groupTips, null, 'groupTips') - assert.deepEqual(groupMsg0.metadata.tangles, {}, 'tangles') - assert.equal(groupMsg0.metadata.type, 'group', 'type') - assert.equal(groupMsg0.metadata.v, 2, 'v') - assert.equal(groupMsg0.pubkey, keypair.public, 'pubkey') + assert.equal(identityMsg0.data.add, keypair.public, 'data.add') + assert.equal(identityMsg0.metadata.dataHash, 'THi3VkJeaf8aTkLSNJUdFD', 'hash') + assert.equal(identityMsg0.metadata.dataSize, 72, 'size') + assert.equal(identityMsg0.metadata.identity, null, 'identity') + assert.equal(identityMsg0.metadata.identityTips, null, 'identityTips') + assert.deepEqual(identityMsg0.metadata.tangles, {}, 'tangles') + assert.equal(identityMsg0.metadata.domain, 'identity', 'domain') + assert.equal(identityMsg0.metadata.v, 3, 'v') + assert.equal(identityMsg0.pubkey, keypair.public, 'pubkey') - group = MsgV2.getMsgHash(groupMsg0) - assert.equal(group, 'XKKmEBmqKGa5twQ2HNSk7t', 'group ID') + identity = MsgV3.getMsgHash(identityMsg0) + assert.equal(identity, 'WnAX17Lm2ktfPUJ5ARXq73', 'identity ID') }) let rootMsg = null let rootHash = null -test('MsgV2.createRoot()', (t) => { +test('MsgV3.createRoot()', (t) => { 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)) assert.equal(rootMsg.data, null, 'data') assert.equal(rootMsg.metadata.dataHash, null, 'hash') assert.equal(rootMsg.metadata.dataSize, 0, 'size') - assert.equal(rootMsg.metadata.group, group, 'group') - assert.equal(rootMsg.metadata.groupTips, null, 'groupTips') + assert.equal(rootMsg.metadata.identity, identity, 'identity') + assert.equal(rootMsg.metadata.identityTips, null, 'identityTips') assert.deepEqual(rootMsg.metadata.tangles, {}, 'tangles') - assert.equal(rootMsg.metadata.type, 'post', 'type') - assert.equal(rootMsg.metadata.v, 2, 'v') + assert.equal(rootMsg.metadata.domain, 'post', 'domain') + assert.equal(rootMsg.metadata.v, 3, 'v') assert.equal(rootMsg.pubkey, keypair.public, 'pubkey') - rootHash = MsgV2.getMsgHash(rootMsg) - assert.equal(rootHash, 'PzuT1Dwbbgn6a8NeLuHuKw', 'root hash') + rootHash = MsgV3.getMsgHash(rootMsg) + assert.equal(rootHash, '5G4FJTWaGr7ZBUJGge6Qeg', 'root hash') }) -test('MsgV2.create()', (t) => { +test('MsgV3.create()', (t) => { const keypair = Keypair.generate('ed25519', 'alice') const data = { text: 'Hello world!' } - const tangle1 = new MsgV2.Tangle(rootHash) + const tangle1 = new MsgV3.Tangle(rootHash) tangle1.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ + const msg1 = MsgV3.create({ keypair, data, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle1, }, @@ -68,7 +68,7 @@ test('MsgV2.create()', (t) => { assert.deepEqual(msg1.data, data, 'data') assert.deepEqual( Object.keys(msg1.metadata), - ['dataHash', 'dataSize', 'group', 'groupTips', 'tangles', 'type', 'v'], + ['dataHash', 'dataSize', 'identity', 'identityTips', 'tangles', 'domain', 'v'], 'metadata shape' ) assert.deepEqual( @@ -77,8 +77,8 @@ test('MsgV2.create()', (t) => { 'metadata.dataHash' ) assert.deepEqual(msg1.metadata.dataSize, 23, 'metadata.dataSize') - assert.equal(msg1.metadata.group, group, 'metadata.group') - assert.deepEqual(msg1.metadata.groupTips, [group], 'metadata.groupTips') + assert.equal(msg1.metadata.identity, identity, 'metadata.identity') + assert.deepEqual(msg1.metadata.identityTips, [identity], 'metadata.identityTips') assert.deepEqual( Object.keys(msg1.metadata.tangles), [rootHash], @@ -86,8 +86,8 @@ test('MsgV2.create()', (t) => { ) assert.equal(msg1.metadata.tangles[rootHash].depth, 1, 'tangle depth') assert.deepEqual(msg1.metadata.tangles[rootHash].prev, [rootHash], 'tangle prev') - assert.equal(msg1.metadata.type, 'post', 'metadata.type') - assert.deepEqual(msg1.metadata.v, 2, 'metadata.v') + assert.equal(msg1.metadata.domain, 'post', 'metadata.domain') + assert.deepEqual(msg1.metadata.v, 3, 'metadata.v') assert.equal( msg1.pubkey, '4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW', @@ -95,30 +95,30 @@ test('MsgV2.create()', (t) => { ) assert.equal( msg1.sig, - 'CW8gWiiqtEgPQ2NjXWHJb5aeW4vkKMG9d1BqPJDjSJaw6xX6s5GUTvoobNSBtaLv8CKNXHHJXSr9Vbe7Cew9pkv', + 'xZGu2Kb19XicfoihgBZ84jRs4XuNgVBd2bK45Cum2fdVDNJUE3f8Ejf6apfZFyE8iAfPDEVWFNAJB6E52EaWEAm', 'sig' ) - const msgHash1 = '7miH6Zh63cyMJTT5bhDjZF' + const msgHash1 = 'NF389yT2td9gz5TvRuZMB6' assert.equal( - MsgV2.getMsgId(msg1), - `ppppp:message/v2/${group}/post/${msgHash1}`, + MsgV3.getMsgId(msg1), + `ppppp:message/v3/${identity}/post/${msgHash1}`, 'getMsgId' ) - const tangle2 = new MsgV2.Tangle(rootHash) + const tangle2 = new MsgV3.Tangle(rootHash) tangle2.add(rootHash, rootMsg) tangle2.add(msgHash1, msg1) const data2 = { text: 'Ola mundo!' } - const msg2 = MsgV2.create({ + const msg2 = MsgV3.create({ keypair, data: data2, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle2, }, @@ -128,7 +128,7 @@ test('MsgV2.create()', (t) => { assert.deepEqual(msg2.data, data2, 'data') assert.deepEqual( Object.keys(msg2.metadata), - ['dataHash', 'dataSize', 'group', 'groupTips', 'tangles', 'type', 'v'], + ['dataHash', 'dataSize', 'identity', 'identityTips', 'tangles', 'domain', 'v'], 'metadata shape' ) assert.deepEqual( @@ -137,8 +137,8 @@ test('MsgV2.create()', (t) => { 'metadata.dataHash' ) assert.deepEqual(msg2.metadata.dataSize, 21, 'metadata.dataSize') - assert.equal(msg2.metadata.group, group, 'metadata.group') - assert.deepEqual(msg2.metadata.groupTips, [group], 'metadata.groupTips') + assert.equal(msg2.metadata.identity, identity, 'metadata.identity') + assert.deepEqual(msg2.metadata.identityTips, [identity], 'metadata.identityTips') assert.deepEqual( Object.keys(msg2.metadata.tangles), [rootHash], @@ -146,8 +146,8 @@ test('MsgV2.create()', (t) => { ) assert.equal(msg2.metadata.tangles[rootHash].depth, 2, 'tangle depth') assert.deepEqual(msg2.metadata.tangles[rootHash].prev, [msgHash1], 'tangle prev') - assert.equal(msg2.metadata.type, 'post', 'metadata.type') - assert.deepEqual(msg2.metadata.v, 2, 'metadata.v') + assert.equal(msg2.metadata.domain, 'post', 'metadata.domain') + assert.deepEqual(msg2.metadata.v, 3, 'metadata.v') assert.equal( msg2.pubkey, '4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW', @@ -155,47 +155,47 @@ test('MsgV2.create()', (t) => { ) assert.equal( msg2.sig, - '33PStdQ8kdvL1pSpd6x9LuxcpEvDmsRNhAq7t75v66cthSHHuiJVqp57b9J7QVXp7a1Jw5qaZLycYQspJRbKNWyW', + '2XHQcG8KeNbdz8m5fDs2QtT7jcxgEqHxv7SkSYVpKQAJ1S8HGn3dmLxw3J5vWmu1vhYhWS6GDE1hfMtvmfiCAy54', 'sig' ) assert.deepEqual( - MsgV2.getMsgId(msg2), - `ppppp:message/v2/${group}/post/HTtEmjCBXGBRTMM3mgekWu`, + MsgV3.getMsgId(msg2), + `ppppp:message/v3/${identity}/post/HNQp1oUu3zmgD1s11xiR7y`, 'getMsgId' ) }) test('create() handles DAG tips correctly', (t) => { const keypair = Keypair.generate('ed25519', 'alice') - const tangle = new MsgV2.Tangle(rootHash) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ + const msg1 = MsgV3.create({ keypair, data: { text: '1' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) assert.deepEqual( msg1.metadata.tangles[rootHash].prev, - [MsgV2.getFeedRootHash(group, 'post')], + [MsgV3.getFeedRootHash(identity, 'post')], 'msg1.prev is root' ) tangle.add(msgHash1, msg1) - const msg2A = MsgV2.create({ + const msg2A = MsgV3.create({ keypair, data: { text: '2A' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, @@ -206,17 +206,17 @@ test('create() handles DAG tips correctly', (t) => { 'msg2A.prev is msg1' ) - const msg2B = MsgV2.create({ + const msg2B = MsgV3.create({ keypair, data: { text: '2B' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash2B = MsgV2.getMsgHash(msg2B) + const msgHash2B = MsgV3.getMsgHash(msg2B) assert.deepEqual( msg2B.metadata.tangles[rootHash].prev, [msgHash1], @@ -225,17 +225,17 @@ test('create() handles DAG tips correctly', (t) => { tangle.add(msgHash2B, msg2B) - const msg3 = MsgV2.create({ + const msg3 = MsgV3.create({ keypair, data: { text: '3' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash3 = MsgV2.getMsgHash(msg3) + const msgHash3 = MsgV3.getMsgHash(msg3) assert.deepEqual( msg3.metadata.tangles[rootHash].prev, [rootHash, msgHash2B].sort(), @@ -243,16 +243,16 @@ test('create() handles DAG tips correctly', (t) => { ) tangle.add(msgHash3, msg3) - const msgHash2A = MsgV2.getMsgHash(msg2A) + const msgHash2A = MsgV3.getMsgHash(msg2A) tangle.add(msgHash2A, msg2A) // t.pass('msg2A comes into awareness') - const msg4 = MsgV2.create({ + const msg4 = MsgV3.create({ keypair, data: { text: '4' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, diff --git a/test/msg-v2/invalid-type.test.js b/test/msg-v3/invalid-domain.test.js similarity index 58% rename from test/msg-v2/invalid-type.test.js rename to test/msg-v3/invalid-domain.test.js index c9cd680..9cd49fb 100644 --- a/test/msg-v2/invalid-type.test.js +++ b/test/msg-v3/invalid-domain.test.js @@ -1,84 +1,84 @@ const test = require('node:test') const assert = require('node:assert') 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') assert.throws( () => { - MsgV2.create({ + MsgV3.create({ keypair, data: { text: 'Hello world!' }, - type: 123, + domain: 123, }) }, - /invalid type/, + /invalid domain/, 'not a string' ) }) -test('invalid type with "/" character', (t) => { +test('invalid domain with "/" character', (t) => { const keypair = Keypair.generate('ed25519', 'alice') assert.throws( () => { - MsgV2.create({ + MsgV3.create({ keypair, data: { text: 'Hello world!' }, - type: 'group/init', + domain: 'group/init', }) }, - /invalid type/, - 'invalid type if contains /' + /invalid domain/, + 'invalid domain if contains /' ) }) -test('invalid type with "*" character', (t) => { +test('invalid domain with "*" character', (t) => { const keypair = Keypair.generate('ed25519', 'alice') assert.throws( () => { - MsgV2.create({ + MsgV3.create({ keypair, data: { text: 'Hello world!' }, - type: 'star*', + domain: 'star*', }) }, - /invalid type/, - 'invalid type if contains *' + /invalid domain/, + 'invalid domain if contains *' ) }) -test('invalid type too short', (t) => { +test('invalid domain too short', (t) => { const keypair = Keypair.generate('ed25519', 'alice') assert.throws( () => { - MsgV2.create({ + MsgV3.create({ keypair, data: { text: 'Hello world!' }, - type: 'xy', + domain: 'xy', }) }, /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') assert.throws( () => { - MsgV2.create({ + MsgV3.create({ keypair, data: { text: 'Hello world!' }, - type: 'a'.repeat(120), + domain: 'a'.repeat(120), }) }, /100\+ characters long/, - 'invalid type if too long' + 'invalid domain if too long' ) }) diff --git a/test/msg-v2/invalid-prev.test.js b/test/msg-v3/invalid-prev.test.js similarity index 53% rename from test/msg-v2/invalid-prev.test.js rename to test/msg-v3/invalid-prev.test.js index 8ba214c..198e9c3 100644 --- a/test/msg-v2/invalid-prev.test.js +++ b/test/msg-v3/invalid-prev.test.js @@ -2,35 +2,35 @@ const test = require('node:test') const assert = require('node:assert') const base58 = require('bs58') const Keypair = require('ppppp-keypair') -const MsgV2 = require('../../lib/msg-v2') +const MsgV3 = require('../../lib/msg-v3') 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]) test('invalid msg with non-array prev', (t) => { const keypair = Keypair.generate('ed25519', 'alice') - const rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) - const tangle = new MsgV2.Tangle(rootHash) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg = MsgV2.create({ + const msg = MsgV3.create({ keypair, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) 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.match( err, @@ -42,40 +42,40 @@ test('invalid msg with non-array prev', (t) => { test('invalid msg with bad prev', (t) => { const keypair = Keypair.generate('ed25519', 'alice') - const rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) - const tangle = new MsgV2.Tangle(rootHash) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ + const msg1 = MsgV3.create({ keypair, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) tangle.add(msgHash1, msg1) - const msg2 = MsgV2.create({ + const msg2 = MsgV3.create({ keypair, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) msg2.metadata.tangles[rootHash].depth = 1 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.match( err, @@ -87,42 +87,42 @@ test('invalid msg with bad prev', (t) => { test('invalid msg with URI in prev', (t) => { const keypair = Keypair.generate('ed25519', 'alice') - const rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) - const tangle = new MsgV2.Tangle(rootHash) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ + const msg1 = MsgV3.create({ keypair, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) tangle.add(msgHash1, msg1) - const msg2 = MsgV2.create({ + const msg2 = MsgV3.create({ keypair, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash2 = MsgV2.getMsgHash(msg2) + const msgHash2 = MsgV3.getMsgHash(msg2) 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].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.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) => { const keypair = Keypair.generate('ed25519', 'alice') - const rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) - const tangle = new MsgV2.Tangle(rootHash) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ + const msg1 = MsgV3.create({ keypair, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) tangle.add(msgHash1, msg1) - const unknownMsg = MsgV2.create({ + const unknownMsg = MsgV3.create({ keypair, data: { text: 'Alien' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const unknownMsgHash = MsgV2.getMsgHash(unknownMsg) + const unknownMsgHash = MsgV3.getMsgHash(unknownMsg) const fakeRootHash = 'ABCDEabcde' + rootHash.substring(10) - const tangle2 = new MsgV2.Tangle(fakeRootHash) + const tangle2 = new MsgV3.Tangle(fakeRootHash) tangle2.add(fakeRootHash, rootMsg) tangle2.add(unknownMsgHash, unknownMsg) - const msg2 = MsgV2.create({ + const msg2 = MsgV3.create({ keypair, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [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.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 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 rootHash = MsgV2.getMsgHash(rootMsg) - const feedTangle = new MsgV2.Tangle(rootHash) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) + const feedTangle = new MsgV3.Tangle(rootHash) feedTangle.add(rootHash, rootMsg) - const msg = MsgV2.create({ + const msg = MsgV3.create({ keypair: keypairB, data: { text: 'Hello world!' }, - group: groupB, - groupTips: [groupB], - type: 'post', + identity: identityB, + identityTips: [identityB], + domain: 'post', tangles: { [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.match( err, - /pubkey ".*" should have been one of ".*" from the group ".*"/, + /pubkey ".*" should have been one of ".*" from the identity ".*"/, '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 rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) - const feedTangle = new MsgV2.Tangle(rootHash) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) + const feedTangle = new MsgV3.Tangle(rootHash) feedTangle.add(rootHash, rootMsg) - const msg = MsgV2.create({ + const msg = MsgV3.create({ keypair: keypairA, data: { text: 'Hello world!' }, - group, - groupTips: [group], - type: 'comment', + identity, + identityTips: [identity], + domain: 'comment', tangles: { [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.match( err, - /type "comment" should have been feed type "post"/, + /domain "comment" should have been feed domain "post"/, '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) => { const keypair = Keypair.generate('ed25519', 'alice') - const rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) - const tangle = new MsgV2.Tangle(rootHash) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ + const msg1 = MsgV3.create({ keypair, data: { text: '1' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) - const msg2 = MsgV2.create({ + const msg2 = MsgV3.create({ keypair, data: { text: '2' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash2 = MsgV2.getMsgHash(msg2) + const msgHash2 = MsgV3.getMsgHash(msg2) tangle.add(msgHash1, msg1) tangle.add(msgHash2, msg2) - const msg3 = MsgV2.create({ + const msg3 = MsgV3.create({ keypair, data: { text: '3' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash3 = MsgV2.getMsgHash(msg3) + const msgHash3 = MsgV3.getMsgHash(msg3) let prevHashes = msg3.metadata.tangles[rootHash].prev if (prevHashes[0] < prevHashes[1]) { @@ -300,7 +300,7 @@ test('invalid feed msg with non-alphabetical prev', (t) => { } 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.match( err, @@ -312,28 +312,28 @@ test('invalid feed msg with non-alphabetical prev', (t) => { test('invalid feed msg with duplicate prev', (t) => { const keypair = Keypair.generate('ed25519', 'alice') - const rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) - const tangle = new MsgV2.Tangle(rootHash) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ + const msg1 = MsgV3.create({ keypair, data: { text: '1' }, - group, - groupTips: [group], - type: 'post', + identity, + identityTips: [identity], + domain: 'post', tangles: { [rootHash]: tangle, }, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) const [prevHash] = msg1.metadata.tangles[rootHash].prev 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.match(err, /prev ".*" contains duplicates/, 'invalid error message') }) diff --git a/test/msg-v2/lipmaa.test.js b/test/msg-v3/lipmaa.test.js similarity index 64% rename from test/msg-v2/lipmaa.test.js rename to test/msg-v3/lipmaa.test.js index ea1cee7..40c81c0 100644 --- a/test/msg-v2/lipmaa.test.js +++ b/test/msg-v3/lipmaa.test.js @@ -1,29 +1,29 @@ const test = require('node:test') const assert = require('node:assert') const Keypair = require('ppppp-keypair') -const MsgV2 = require('../../lib/msg-v2') +const MsgV3 = require('../../lib/msg-v3') test('lipmaa prevs', (t) => { 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 rootMsg = MsgV2.createRoot(group, 'post', keypair) - const rootHash = MsgV2.getMsgHash(rootMsg) - const tangle = new MsgV2.Tangle(rootHash) + const rootMsg = MsgV3.createRoot(identity, 'post', keypair) + const rootHash = MsgV3.getMsgHash(rootMsg) + const tangle = new MsgV3.Tangle(rootHash) tangle.add(rootHash, rootMsg) - const msg1 = MsgV2.create({ - group, - groupTips: [group], - type: 'post', + const msg1 = MsgV3.create({ + identity, + identityTips: [identity], + domain: 'post', data, tangles: { [rootHash]: tangle, }, keypair, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) tangle.add(msgHash1, msg1) assert.equal(msg1.metadata.tangles[rootHash].depth, 1, 'msg1 depth') assert.deepEqual( @@ -32,17 +32,17 @@ test('lipmaa prevs', (t) => { 'msg1 prev' ) - const msg2 = MsgV2.create({ - group, - groupTips: [group], - type: 'post', + const msg2 = MsgV3.create({ + identity, + identityTips: [identity], + domain: 'post', data, tangles: { [rootHash]: tangle, }, keypair, }) - const msgHash2 = MsgV2.getMsgHash(msg2) + const msgHash2 = MsgV3.getMsgHash(msg2) tangle.add(msgHash2, msg2) assert.equal(msg2.metadata.tangles[rootHash].depth, 2, 'msg2 depth') assert.deepEqual( @@ -51,17 +51,17 @@ test('lipmaa prevs', (t) => { 'msg2 prev' ) - const msg3 = MsgV2.create({ - group, - groupTips: [group], - type: 'post', + const msg3 = MsgV3.create({ + identity, + identityTips: [identity], + domain: 'post', data, tangles: { [rootHash]: tangle, }, keypair, }) - const msgHash3 = MsgV2.getMsgHash(msg3) + const msgHash3 = MsgV3.getMsgHash(msg3) tangle.add(msgHash3, msg3) assert.equal(msg3.metadata.tangles[rootHash].depth, 3, 'msg3 depth') assert.deepEqual( @@ -70,17 +70,17 @@ test('lipmaa prevs', (t) => { 'msg3 prev (has lipmaa!)' ) - const msg4 = MsgV2.create({ - group, - groupTips: [group], - type: 'post', + const msg4 = MsgV3.create({ + identity, + identityTips: [identity], + domain: 'post', keypair, tangles: { [rootHash]: tangle, }, data, }) - const msgHash4 = MsgV2.getMsgHash(msg4) + const msgHash4 = MsgV3.getMsgHash(msg4) tangle.add(msgHash4, msg4) assert.equal(msg4.metadata.tangles[rootHash].depth, 4, 'msg4 depth') assert.deepEqual( @@ -89,17 +89,17 @@ test('lipmaa prevs', (t) => { 'msg4 prev' ) - const msg5 = MsgV2.create({ - group, - groupTips: [group], - type: 'post', + const msg5 = MsgV3.create({ + identity, + identityTips: [identity], + domain: 'post', data, tangles: { [rootHash]: tangle, }, keypair, }) - const msgHash5 = MsgV2.getMsgHash(msg5) + const msgHash5 = MsgV3.getMsgHash(msg5) tangle.add(msgHash5, msg5) assert.equal(msg5.metadata.tangles[rootHash].depth, 5, 'msg5 depth') assert.deepEqual( @@ -108,17 +108,17 @@ test('lipmaa prevs', (t) => { 'msg5 prev' ) - const msg6 = MsgV2.create({ - group, - groupTips: [group], - type: 'post', + const msg6 = MsgV3.create({ + identity, + identityTips: [identity], + domain: 'post', data, tangles: { [rootHash]: tangle, }, keypair, }) - const msgHash6 = MsgV2.getMsgHash(msg6) + const msgHash6 = MsgV3.getMsgHash(msg6) tangle.add(msgHash6, msg6) assert.equal(msg6.metadata.tangles[rootHash].depth, 6, 'msg6 depth') assert.deepEqual( @@ -127,17 +127,17 @@ test('lipmaa prevs', (t) => { 'msg6 prev' ) - const msg7 = MsgV2.create({ - group, - groupTips: [group], - type: 'post', + const msg7 = MsgV3.create({ + identity, + identityTips: [identity], + domain: 'post', data, tangles: { [rootHash]: tangle, }, keypair, }) - const msgHash7 = MsgV2.getMsgHash(msg7) + const msgHash7 = MsgV3.getMsgHash(msg7) tangle.add(msgHash7, msg7) assert.equal(msg7.metadata.tangles[rootHash].depth, 7, 'msg7 depth') assert.deepEqual( diff --git a/test/msg-v2/tangles.test.js b/test/msg-v3/tangles.test.js similarity index 59% rename from test/msg-v2/tangles.test.js rename to test/msg-v3/tangles.test.js index 25b5654..a044987 100644 --- a/test/msg-v2/tangles.test.js +++ b/test/msg-v3/tangles.test.js @@ -1,48 +1,48 @@ const test = require('node:test') const assert = require('node:assert') const Keypair = require('ppppp-keypair') -const MsgV2 = require('../../lib/msg-v2') +const MsgV3 = require('../../lib/msg-v3') test('simple multi-author tangle', (t) => { const keypairA = Keypair.generate('ed25519', 'alice') const keypairB = Keypair.generate('ed25519', 'bob') - const groupA = MsgV2.getMsgHash(MsgV2.createGroup(keypairA, 'alice')) - const groupB = MsgV2.getMsgHash(MsgV2.createGroup(keypairB, 'bob')) + const identityA = MsgV3.getMsgHash(MsgV3.createIdentity(keypairA, 'alice')) + const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'bob')) - const rootMsgA = MsgV2.createRoot(groupA, 'post', keypairA) - const rootHashA = MsgV2.getMsgHash(rootMsgA) - const tangleA = new MsgV2.Tangle(rootHashA) + const rootMsgA = MsgV3.createRoot(identityA, 'post', keypairA) + const rootHashA = MsgV3.getMsgHash(rootMsgA) + const tangleA = new MsgV3.Tangle(rootHashA) tangleA.add(rootHashA, rootMsgA) - const rootMsgB = MsgV2.createRoot(groupB, 'post', keypairB) - const rootHashB = MsgV2.getMsgHash(rootMsgB) - const tangleB = new MsgV2.Tangle(rootHashB) + const rootMsgB = MsgV3.createRoot(identityB, 'post', keypairB) + const rootHashB = MsgV3.getMsgHash(rootMsgB) + const tangleB = new MsgV3.Tangle(rootHashB) tangleB.add(rootHashB, rootMsgB) - const msg1 = MsgV2.create({ - group: groupA, - groupTips: [groupA], - type: 'post', + const msg1 = MsgV3.create({ + identity: identityA, + identityTips: [identityA], + domain: 'post', data: { text: 'Hello world!' }, tangles: { [rootHashA]: tangleA, }, keypair: keypairA, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) assert.deepEqual( Object.keys(msg1.metadata.tangles), [rootHashA], 'msg1 has only feed tangle' ) - const tangleX = new MsgV2.Tangle(msgHash1) + const tangleX = new MsgV3.Tangle(msgHash1) tangleX.add(msgHash1, msg1) - const msg2 = MsgV2.create({ - group: groupB, - groupTips: [groupB], - type: 'post', + const msg2 = MsgV3.create({ + identity: identityB, + identityTips: [identityB], + domain: 'post', data: { text: 'Hello world!' }, tangles: { [rootHashB]: tangleB, @@ -82,34 +82,34 @@ test('simple multi-author tangle', (t) => { test('lipmaa in multi-author tangle', (t) => { const keypairA = Keypair.generate('ed25519', 'alice') const keypairB = Keypair.generate('ed25519', 'bob') - const groupA = MsgV2.getMsgHash(MsgV2.createGroup(keypairA, 'alice')) - const groupB = MsgV2.getMsgHash(MsgV2.createGroup(keypairB, 'bob')) + const identityA = MsgV3.getMsgHash(MsgV3.createIdentity(keypairA, 'alice')) + const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'bob')) const data = { text: 'Hello world!' } - const rootMsgA = MsgV2.createRoot(groupA, 'post', keypairA) - const rootHashA = MsgV2.getMsgHash(rootMsgA) - const tangleA = new MsgV2.Tangle(rootHashA) + const rootMsgA = MsgV3.createRoot(identityA, 'post', keypairA) + const rootHashA = MsgV3.getMsgHash(rootMsgA) + const tangleA = new MsgV3.Tangle(rootHashA) tangleA.add(rootHashA, rootMsgA) - const rootMsgB = MsgV2.createRoot(groupB, 'post', keypairB) - const rootHashB = MsgV2.getMsgHash(rootMsgB) - const tangleB = new MsgV2.Tangle(rootHashB) + const rootMsgB = MsgV3.createRoot(identityB, 'post', keypairB) + const rootHashB = MsgV3.getMsgHash(rootMsgB) + const tangleB = new MsgV3.Tangle(rootHashB) tangleB.add(rootHashB, rootMsgB) - const msg1 = MsgV2.create({ - group: groupA, - groupTips: [groupA], - type: 'post', + const msg1 = MsgV3.create({ + identity: identityA, + identityTips: [identityA], + domain: 'post', data, tangles: { [rootHashA]: tangleA, }, keypair: keypairA, }) - const msgHash1 = MsgV2.getMsgHash(msg1) + const msgHash1 = MsgV3.getMsgHash(msg1) tangleA.add(msgHash1, msg1) - const tangleThread = new MsgV2.Tangle(msgHash1) + const tangleThread = new MsgV3.Tangle(msgHash1) tangleThread.add(msgHash1, msg1) assert.deepEqual( @@ -118,10 +118,10 @@ test('lipmaa in multi-author tangle', (t) => { 'A:msg1 has only feed tangle' ) - const msg2 = MsgV2.create({ - group: groupB, - groupTips: [groupB], - type: 'post', + const msg2 = MsgV3.create({ + identity: identityB, + identityTips: [identityB], + domain: 'post', data, tangles: { [rootHashB]: tangleB, @@ -129,7 +129,7 @@ test('lipmaa in multi-author tangle', (t) => { }, keypair: keypairB, }) - const msgHash2 = MsgV2.getMsgHash(msg2) + const msgHash2 = MsgV3.getMsgHash(msg2) tangleB.add(msgHash2, msg2) tangleThread.add(msgHash2, msg2) @@ -139,10 +139,10 @@ test('lipmaa in multi-author tangle', (t) => { 'B:msg2 points to A:msg1' ) - const msg3 = MsgV2.create({ - group: groupB, - groupTips: [groupB], - type: 'post', + const msg3 = MsgV3.create({ + identity: identityB, + identityTips: [identityB], + domain: 'post', data, tangles: { [rootHashB]: tangleB, @@ -150,7 +150,7 @@ test('lipmaa in multi-author tangle', (t) => { }, keypair: keypairB, }) - const msgHash3 = MsgV2.getMsgHash(msg3) + const msgHash3 = MsgV3.getMsgHash(msg3) tangleB.add(msgHash3, msg3) tangleThread.add(msgHash3, msg3) @@ -160,10 +160,10 @@ test('lipmaa in multi-author tangle', (t) => { 'B:msg3 points to B:msg2' ) - const msg4 = MsgV2.create({ - group: groupA, - groupTips: [groupA], - type: 'post', + const msg4 = MsgV3.create({ + identity: identityA, + identityTips: [identityA], + domain: 'post', data, tangles: { [rootHashA]: tangleA, @@ -171,7 +171,7 @@ test('lipmaa in multi-author tangle', (t) => { }, keypair: keypairA, }) - const msgHash4 = MsgV2.getMsgHash(msg4) + const msgHash4 = MsgV3.getMsgHash(msg4) tangleB.add(msgHash4, msg4) tangleThread.add(msgHash4, msg4) diff --git a/test/msg-v3/validate.test.js b/test/msg-v3/validate.test.js new file mode 100644 index 0000000..5cdf291 --- /dev/null +++ b/test/msg-v3/validate.test.js @@ -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') +}) diff --git a/test/msgs-iterator.test.js b/test/msgs-iterator.test.js index 295c892..2219d53 100644 --- a/test/msgs-iterator.test.js +++ b/test/msgs-iterator.test.js @@ -19,12 +19,12 @@ test('msgs() iterator', async (t) => { 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++) { await p(peer.db.feed.publish)({ - group, - type: i % 2 === 0 ? 'post' : 'about', + identity, + domain: i % 2 === 0 ? 'post' : 'about', data: i % 2 === 0 ? { text: 'hello ' + i } @@ -36,8 +36,8 @@ test('msgs() iterator', async (t) => { const abouts = [] for (const msg of peer.db.msgs()) { if (!msg.data) continue - if (msg.metadata.type === 'post') posts.push(msg.data.text) - else if (msg.metadata.type === 'about') abouts.push(msg.data.name) + if (msg.metadata.domain === 'post') posts.push(msg.data.text) + else if (msg.metadata.domain === 'about') abouts.push(msg.data.name) } assert.deepEqual(posts, ['hello 0', 'hello 2', 'hello 4'], 'queried posts') diff --git a/test/on-record-added.test.js b/test/on-record-added.test.js index 5c8f1b2..1c7b81a 100644 --- a/test/on-record-added.test.js +++ b/test/on-record-added.test.js @@ -19,7 +19,7 @@ test('onRecordAdded', async (t) => { 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 = [] var remove = peer.db.onRecordAdded((ev) => { @@ -27,8 +27,8 @@ test('onRecordAdded', async (t) => { }) const rec1 = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity, + domain: 'post', data: { text: 'I am hungry' }, }) assert.equal(rec1.msg.data.text, 'I am hungry', 'msg1 text correct') @@ -36,7 +36,7 @@ test('onRecordAdded', async (t) => { await p(setTimeout)(500) 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.metadata.dataSize, 0, 'root') assert.deepEqual(listened[2], rec1, 'actual record') diff --git a/test/re-open.test.js b/test/re-open.test.js index 7118953..27a8cfb 100644 --- a/test/re-open.test.js +++ b/test/re-open.test.js @@ -19,14 +19,14 @@ test('publish some msgs, close, re-open', async (t) => { .call(null, { keypair, path: DIR }) 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') const msgHashes = [] for (let i = 0; i < 6; i++) { const rec = await p(peer.db.feed.publish)({ - group, - type: 'post', + identity, + domain: 'post', data: { text: 'hello ' + i }, }) msgHashes.push(rec.hash) @@ -49,7 +49,7 @@ test('publish some msgs, close, re-open', async (t) => { const texts = [] 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) } diff --git a/test/records-iterator.test.js b/test/records-iterator.test.js index d75b3cd..ead3e40 100644 --- a/test/records-iterator.test.js +++ b/test/records-iterator.test.js @@ -18,12 +18,12 @@ test('records() iterator', async (t) => { .call(null, { keypair, path: DIR }) 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++) { await p(peer.db.feed.publish)({ - group, - type: i % 2 === 0 ? 'post' : 'about', + identity, + domain: i % 2 === 0 ? 'post' : 'about', data: i % 2 === 0 ? { text: 'hello ' + i } @@ -34,7 +34,7 @@ test('records() iterator', async (t) => { let count = 0 for (const rec of peer.db.records()) { 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') count++ }