mirror of https://codeberg.org/pzp/pzp-db.git
rename type=>domain, group=>identity
This commit is contained in:
parent
08d3450f20
commit
d97d29fca0
|
@ -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,
|
||||
|
|
68
lib/index.js
68
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,
|
||||
|
|
|
@ -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
|
|
@ -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}`
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Msg> & {values: () => Iterator<Msg>}} MsgIter
|
||||
* @typedef {import('ppppp-keypair').Keypair} Keypair
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Iterator<Msg> & {values: () => Iterator<Msg>}} MsgIter
|
||||
*
|
||||
* @typedef {Buffer | Uint8Array} B4A
|
||||
*
|
||||
* @typedef {{
|
||||
* depth: number;
|
||||
* prev: Array<string>;
|
||||
* }} TangleMetadata
|
||||
*
|
||||
* @typedef {{
|
||||
* data: any;
|
||||
* metadata: {
|
||||
* dataHash: string | null;
|
||||
* dataSize: number;
|
||||
* identity: string | null;
|
||||
* identityTips: Array<string> | null;
|
||||
* tangles: Record<string, TangleMetadata>;
|
||||
* domain: string;
|
||||
* v: 3;
|
||||
* };
|
||||
* pubkey: string;
|
||||
* sig: string;
|
||||
* }} Msg
|
||||
*
|
||||
* @typedef {{
|
||||
* data: any;
|
||||
* domain: string;
|
||||
* keypair: Keypair;
|
||||
* identity: string | null;
|
||||
* identityTips: Array<string> | null;
|
||||
* tangles: Record<string, Tangle>;
|
||||
* }} CreateOpts
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TangleMetadata
|
||||
* @property {number} depth
|
||||
* @property {Array<string>} prev
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Msg
|
||||
* @property {*} data
|
||||
* @property {Object} metadata
|
||||
* @property {string} metadata.dataHash
|
||||
* @property {number} metadata.dataSize
|
||||
* @property {string | null} metadata.group
|
||||
* @property {Array<string> | null} metadata.groupTips
|
||||
* @property {Record<string, TangleMetadata>} metadata.tangles
|
||||
* @property {string} metadata.type
|
||||
* @property {2} metadata.v
|
||||
* @property {string} pubkey
|
||||
* @property {string} sig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} CreateOpts
|
||||
* @property {*} data
|
||||
* @property {string} type
|
||||
* @property {Keypair} keypair
|
||||
* @property {string | null} group
|
||||
* @property {Array<string> | null} groupTips
|
||||
* @property {Record<string, Tangle>} tangles
|
||||
*/
|
||||
|
||||
function getFeedRootHash(groupId, type) {
|
||||
const group = stripGroup(groupId)
|
||||
|
||||
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,
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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) {
|
|
@ -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,
|
112
protospec.md
112
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<string> | null // list of blake3 hashes of identity tangle tips, or null
|
||||
tangles: {
|
||||
// for each tangle this msg belongs to, identified by the tangle's root
|
||||
[rootMsgHash: string]: {
|
||||
depth: number // maximum distance (positive integer) from this msg to the root
|
||||
prev: Array<MsgHash> // list of msg hashes of existing msgs, unique set and ordered alphabetically
|
||||
}
|
||||
}
|
||||
domain: string // alphanumeric string, at least 3 chars, max 100 chars
|
||||
v: 2 // hard-coded at 2, indicates the version of the feed format
|
||||
}
|
||||
pubkey: Pubkey // base58 encoded string for the author's public key
|
||||
sig: Signature // Signs the `metadata` object
|
||||
}
|
||||
```
|
||||
|
||||
## Identity tangle msgs
|
||||
|
||||
Msgs in an identity tangle are special because they have empty `identity` and `identityTips` fields.
|
||||
|
||||
```typescript
|
||||
interface Msg {
|
||||
data: {
|
||||
add: string // pubkey being added to the identity
|
||||
nonce?: string // nonce required only on the identity tangle's root
|
||||
}
|
||||
metadata: {
|
||||
dataHash: ContentHash
|
||||
dataSize: number
|
||||
identity: null // MUST be null
|
||||
identityTips: null // MUST be null
|
||||
tangles: {
|
||||
[identityTangleId: string]: {
|
||||
depth: number // maximum distance (positive integer) from this msg to the root
|
||||
prev: Array<MsgHash> // list of msg hashes of existing msgs, unique set and ordered alphabetically
|
||||
}
|
||||
}
|
||||
domain: 'identity' // MUST be 'identity'
|
||||
v: 2
|
||||
}
|
||||
pubkey: Pubkey
|
||||
sig: Signature
|
||||
}
|
||||
```
|
||||
|
||||
## Feed root
|
||||
|
||||
The root msg for a feed is special, its `metadata` is predictable and can be constructed by any peer. It is a data-less msg with the following shape:
|
||||
|
||||
```typescript
|
||||
interface Msg {
|
||||
data: null // MUST be null
|
||||
metadata: {
|
||||
dataHash: null // MUST be null
|
||||
dataSize: 0 // MUST be 0
|
||||
identity: string // MUST be an ID
|
||||
identityTips: null // MUST be null
|
||||
tangles: {} // MUST be empty object
|
||||
domain: string
|
||||
v: 2
|
||||
}
|
||||
pubkey: Pubkey
|
||||
sig: Signature
|
||||
}
|
||||
```
|
||||
|
||||
Thus, given a `identity` and a `domain`, any peer can construct the `metadata` part of the feed root msg, and thus can derive the "msg ID" for the root based on that `metadata`.
|
||||
|
||||
Given the root msg ID, any peer can thus refer to the feed tangle, because the root msg ID is the tangle ID for the feed tangle.
|
||||
|
||||
Note also that _any peer_ can construct the root msg and sign it! Which renders the signatures for feed roots meaningless and ignorable.
|
||||
|
||||
## Prev links
|
||||
|
||||
A msg can refer to 0 or more prev msgs. The prev links are used to build the tangle.
|
||||
|
||||
The `prev` array for a tangle should list:
|
||||
|
||||
- All current "tips" (msgs that are not yet listed inside any `prev`) of this tangle
|
||||
- All msgs that are at the previous "lipmaa" depth relative to this `depth`
|
||||
|
||||
## JSON serialization
|
||||
|
||||
Whenever we need to serialize any JSON in the context of creating a Feed V1 message, we follow the "JSON Canonicalization Scheme" (JSC) defined by [RFC 8785](https://tools.ietf.org/html/rfc8785).
|
||||
|
||||
# Msg V2
|
||||
|
||||
Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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],
|
||||
}),
|
||||
|
|
|
@ -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')
|
|
@ -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')
|
||||
|
|
@ -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')
|
||||
})
|
|
@ -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,
|
||||
},
|
|
@ -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'
|
||||
)
|
||||
})
|
|
@ -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')
|
||||
})
|
|
@ -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(
|
|
@ -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)
|
||||
|
|
@ -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')
|
||||
})
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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++
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue