rename type=>domain, group=>identity

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

View File

@ -1,6 +1,6 @@
const base58 = require('bs58')
const 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,

View File

@ -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,

View File

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

View File

@ -23,7 +23,7 @@ function getMsgHashBuf(msg) {
*/
function getMsgHash(x) {
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}`
}
}

View File

@ -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,

View File

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

View File

@ -5,7 +5,7 @@ function stripMsgKey(msgKey) {
if (msgKey.key) return stripMsgKey(msgKey.key)
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,
}

View File

@ -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) {

View File

@ -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,

View File

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

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack')
const 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,
},

View File

@ -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'

View File

@ -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')

View File

@ -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) => {

View File

@ -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,

View File

@ -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) => {

View File

@ -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],
}),

View File

@ -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')

View File

@ -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')

View File

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

View File

@ -1,64 +1,64 @@
const test = require('node:test')
const 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,
},

View File

@ -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'
)
})

View File

@ -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')
})

View File

@ -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(

View File

@ -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)

View File

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

View File

@ -19,12 +19,12 @@ test('msgs() iterator', async (t) => {
await peer.db.loaded()
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')

View File

@ -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')

View File

@ -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)
}

View File

@ -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++
}