identity tangle always has metadata.identity=self

This commit is contained in:
Andre Staltz 2023-06-25 20:48:20 +03:00
parent 674e2ba66c
commit df98d499f1
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
23 changed files with 271 additions and 104 deletions

View File

@ -8,7 +8,7 @@ const b4a = require('b4a')
const base58 = require('bs58') const base58 = require('bs58')
// @ts-ignore // @ts-ignore
const Obz = require('obz') const Obz = require('obz')
const MsgV2 = require('./msg-v3') const MsgV3 = require('./msg-v3')
const { ReadyGate } = require('./utils') const { ReadyGate } = require('./utils')
const { decrypt } = require('./encryption') const { decrypt } = require('./encryption')
@ -58,7 +58,7 @@ const { decrypt } = require('./encryption')
* @typedef {(...args: [Error] | []) => void} CBVoid * @typedef {(...args: [Error] | []) => void} CBVoid
*/ */
class DBTangle extends MsgV2.Tangle { class DBTangle extends MsgV3.Tangle {
/** /**
* @param {string} rootHash * @param {string} rootHash
* @param {Iterable<Rec>} recordsIter * @param {Iterable<Rec>} recordsIter
@ -260,7 +260,7 @@ function initDB(peer, config) {
* @param {CB<Rec>} cb * @param {CB<Rec>} cb
*/ */
function add(msg, tangleRootHash, cb) { function add(msg, tangleRootHash, cb) {
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV3.getMsgHash(msg)
// TODO: optimize this. Perhaps have a Map() of msgHash -> record // TODO: optimize this. Perhaps have a Map() of msgHash -> record
// Or even better, a bloom filter. If you just want to answer no/perhaps. // Or even better, a bloom filter. If you just want to answer no/perhaps.
@ -272,7 +272,7 @@ function initDB(peer, config) {
const tangle = new DBTangle(tangleRootHash, records()) const tangle = new DBTangle(tangleRootHash, records())
const pubkeys = new Set() const pubkeys = new Set()
if (msg.metadata.identity) { if (msg.metadata.identity && msg.metadata.identity !== 'self') {
const identityTangle = new DBTangle(msg.metadata.identity, records()) const identityTangle = new DBTangle(msg.metadata.identity, records())
if (!identityTangle.has(msg.metadata.identity)) { if (!identityTangle.has(msg.metadata.identity)) {
// prettier-ignore // prettier-ignore
@ -286,7 +286,7 @@ function initDB(peer, config) {
} }
let err let err
if ((err = MsgV2.validate(msg, tangle, pubkeys, msgHash, tangleRootHash))) { if ((err = MsgV3.validate(msg, tangle, pubkeys, msgHash, tangleRootHash))) {
return cb(new Error('add() failed msg validation', { cause: err })) return cb(new Error('add() failed msg validation', { cause: err }))
} }
@ -308,8 +308,8 @@ function initDB(peer, config) {
const feedRootHash = getFeedId(identity, domain) const feedRootHash = getFeedId(identity, domain)
if (feedRootHash) return cb(null, feedRootHash) if (feedRootHash) return cb(null, feedRootHash)
const feedRoot = MsgV2.createRoot(identity, domain, keypair) const feedRoot = MsgV3.createRoot(identity, domain, keypair)
add(feedRoot, MsgV2.getMsgHash(feedRoot), (err, rec) => { add(feedRoot, MsgV3.getMsgHash(feedRoot), (err, rec) => {
// prettier-ignore // prettier-ignore
if (err) return cb(new Error('initializeFeed() failed to add root', { cause: err })); if (err) return cb(new Error('initializeFeed() failed to add root', { cause: err }));
const recHash = /** @type {string} */ (rec.hash) const recHash = /** @type {string} */ (rec.hash)
@ -318,25 +318,109 @@ function initDB(peer, config) {
} }
/** /**
* @param {{keypair?: Keypair, _nonce?: string} | null} opts * @param {Rec} rec
* @param {CB<Rec>} cb * @returns {string | null}
*/
function getIdentityId(rec) {
if (!rec.msg) return null
if (rec.msg.metadata.identity === 'self') {
for (const tangleId in rec.msg.metadata.tangles) {
return tangleId
}
return rec.hash
} else if (rec.msg.metadata.identity) {
return rec.msg.metadata.identity
} else {
return null
}
}
/**
* @param {{
* keypair?: Keypair;
* domain: string;
* }} opts
* @param {CB<string>} cb
*/
function findIdentity(opts, cb) {
if (!opts.domain)
return cb(new Error('identity.find() requires a `domain`'))
const keypair = opts?.keypair ?? config.keypair
const domain = opts.domain
for (let i = 0; i < recs.length; i++) {
const rec = recs[i]
if (!rec) continue
if (!rec.msg) continue
if (!rec.msg.data) continue
if (
rec.msg.metadata.identity === 'self' &&
rec.msg.data.add === keypair.public &&
rec.msg.metadata.domain === domain
) {
const identityId = getIdentityId(rec)
if (identityId) {
cb(null, identityId)
} else {
// prettier-ignore
cb(new Error(`identity.find() failed to find ID in ${JSON.stringify(rec.msg)}`))
}
break
}
}
// prettier-ignore
const err = new Error(`identity.find() failed for pubkey=${keypair.public} domain=${domain}`, { cause: 'ENOENT' });
cb(err)
}
/**
* @param {{
* keypair?: Keypair,
* domain: string,
* _nonce?: string
* }} opts
* @param {CB<string>} cb
*/ */
function createIdentity(opts, cb) { function createIdentity(opts, cb) {
if (!opts.domain)
return cb(new Error('identity.create() requires a `domain`'))
const keypair = opts?.keypair ?? config.keypair const keypair = opts?.keypair ?? config.keypair
const domain = opts.domain
let msg let msg
try { try {
msg = MsgV2.createIdentity(keypair, opts?._nonce) msg = MsgV3.createIdentity(keypair, domain, opts?._nonce)
} catch (err) { } catch (err) {
return cb(new Error('identity.create() failed', { cause: err })) return cb(new Error('identity.create() failed', { cause: err }))
} }
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV3.getMsgHash(msg)
logAppend(msgHash, msg, (err, rec) => { logAppend(msgHash, msg, (err, rec) => {
// prettier-ignore // prettier-ignore
if (err) return cb(new Error('identity.create() failed in the log', { cause: err })) if (err) return cb(new Error('identity.create() failed in the log', { cause: err }))
onRecordAdded.set(rec) onRecordAdded.set(rec)
cb(null, rec) const recHash = /** @type {string} */ (rec.hash)
cb(null, recHash)
})
}
/**
* @param {{
* keypair?: Keypair,
* domain: string,
* _nonce?: string
* }} opts
* @param {CB<string>} cb
*/
function findOrCreateIdentity(opts, cb) {
findIdentity(opts, (err, identityId) => {
if (err?.cause === 'ENOENT') {
createIdentity(opts, cb)
} else if (err) {
cb(err)
} else {
cb(null, identityId)
}
}) })
} }
@ -355,7 +439,7 @@ function initDB(peer, config) {
// Fill-in tangle opts: // Fill-in tangle opts:
const tangles = populateTangles([opts.identity]) const tangles = populateTangles([opts.identity])
const fullOpts = { const fullOpts = {
identity: null, identity: 'self',
identityTips: null, identityTips: null,
tangles, tangles,
keypair: signingKeypair, keypair: signingKeypair,
@ -366,11 +450,11 @@ function initDB(peer, config) {
// Create the actual message: // Create the actual message:
let msg let msg
try { try {
msg = MsgV2.create(fullOpts) msg = MsgV3.create(fullOpts)
} catch (err) { } catch (err) {
return cb(new Error('identity.add() failed', { cause: err })) return cb(new Error('identity.add() failed', { cause: err }))
} }
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV3.getMsgHash(msg)
logAppend(msgHash, msg, (err, rec) => { logAppend(msgHash, msg, (err, rec) => {
// prettier-ignore // prettier-ignore
@ -421,7 +505,7 @@ function initDB(peer, config) {
// If opts ask for encryption, encrypt and put ciphertext in opts.data // If opts ask for encryption, encrypt and put ciphertext in opts.data
const recps = fullOpts.data.recps const recps = fullOpts.data.recps
if (Array.isArray(recps) && recps.length > 0) { if (Array.isArray(recps) && recps.length > 0) {
const plaintext = MsgV2.toPlaintextBuffer(fullOpts) const plaintext = MsgV3.toPlaintextBuffer(fullOpts)
const encryptOpts = { const encryptOpts = {
...fullOpts, ...fullOpts,
recps: recps.map( recps: recps.map(
@ -454,11 +538,11 @@ function initDB(peer, config) {
// Create the actual message: // Create the actual message:
let msg let msg
try { try {
msg = MsgV2.create(fullOpts) msg = MsgV3.create(fullOpts)
} catch (err) { } catch (err) {
return cb(new Error('feed.publish() failed', { cause: err })) return cb(new Error('feed.publish() failed', { cause: err }))
} }
const msgHash = MsgV2.getMsgHash(msg) const msgHash = MsgV3.getMsgHash(msg)
// Encode the native message and append it to the log: // Encode the native message and append it to the log:
logAppend(msgHash, msg, (err, rec) => { logAppend(msgHash, msg, (err, rec) => {
@ -475,9 +559,9 @@ function initDB(peer, config) {
* @param {string} findDomain * @param {string} findDomain
*/ */
function getFeedId(id, findDomain) { function getFeedId(id, findDomain) {
const findIdentity = MsgV2.stripIdentity(id) const findIdentity = MsgV3.stripIdentity(id)
for (const rec of records()) { for (const rec of records()) {
if (rec.msg && MsgV2.isFeedRoot(rec.msg, findIdentity, findDomain)) { if (rec.msg && MsgV3.isFeedRoot(rec.msg, findIdentity, findDomain)) {
return rec.hash return rec.hash
} }
} }
@ -531,7 +615,7 @@ function initDB(peer, config) {
if (!rec) return cb() if (!rec) return cb()
if (!rec.msg) return cb() if (!rec.msg) return cb()
if (!rec.msg.data) return cb() if (!rec.msg.data) return cb()
recs[rec.misc.seq].msg = MsgV2.erase(rec.msg) recs[rec.misc.seq].msg = MsgV3.erase(rec.msg)
// FIXME: persist this change to disk!! Not supported by AAOL yet // FIXME: persist this change to disk!! Not supported by AAOL yet
cb() cb()
} }
@ -564,7 +648,9 @@ function initDB(peer, config) {
loaded, loaded,
add, add,
identity: { identity: {
find: findIdentity,
create: createIdentity, create: createIdentity,
findOrCreate: findOrCreateIdentity,
add: addToIdentity, add: addToIdentity,
}, },
feed: { feed: {

View File

@ -37,7 +37,7 @@ const Tangle = require('./tangle')
* metadata: { * metadata: {
* dataHash: string | null; * dataHash: string | null;
* dataSize: number; * dataSize: number;
* identity: string | null; * identity: string | (typeof IDENTITY_SELF) | null;
* identityTips: Array<string> | null; * identityTips: Array<string> | null;
* tangles: Record<string, TangleMetadata>; * tangles: Record<string, TangleMetadata>;
* domain: string; * domain: string;
@ -57,6 +57,8 @@ const Tangle = require('./tangle')
* }} CreateOpts * }} CreateOpts
*/ */
const IDENTITY_SELF = /** @type {const} */ 'self'
/** /**
* @param {string} id * @param {string} id
* @param {string} domain * @param {string} domain
@ -178,21 +180,23 @@ function createRoot(id, domain, keypair) {
/** /**
* @param {Keypair} keypair * @param {Keypair} keypair
* @param {string} domain
* @param {string | (() => string)} nonce * @param {string | (() => string)} nonce
* @returns {Msg} * @returns {Msg}
*/ */
function createIdentity( function createIdentity(
keypair, keypair,
domain,
nonce = () => base58.encode(crypto.randomBytes(32)) nonce = () => base58.encode(crypto.randomBytes(32))
) { ) {
const actualNonce = typeof nonce === 'function' ? nonce() : nonce const actualNonce = typeof nonce === 'function' ? nonce() : nonce
return create({ return create({
data: { add: keypair.public, nonce: actualNonce }, data: { add: keypair.public, nonce: actualNonce },
identity: null, identity: IDENTITY_SELF,
identityTips: null, identityTips: null,
keypair, keypair,
tangles: {}, tangles: {},
domain: 'identity', domain,
}) })
} }

View File

@ -20,7 +20,8 @@ function isEmptyObject(obj) {
* @param {string | 0} findDomain * @param {string | 0} findDomain
*/ */
function isFeedRoot(msg, id = 0, findDomain = 0) { function isFeedRoot(msg, id = 0, findDomain = 0) {
const { dataHash, dataSize, identity, identityTips, tangles, domain } = msg.metadata const { dataHash, dataSize, identity, identityTips, tangles, domain } =
msg.metadata
if (dataHash !== null) return false if (dataHash !== null) return false
if (dataSize !== 0) return false if (dataSize !== 0) return false
if (id === 0 && !identity) return false if (id === 0 && !identity) return false

View File

@ -88,7 +88,11 @@ function validateIdentityPubkey(msg, pubkeys) {
// Unusual case: if the msg is a feed root, ignore the identity and pubkey // Unusual case: if the msg is a feed root, ignore the identity and pubkey
if (isFeedRoot(msg)) return if (isFeedRoot(msg)) return
if (msg.metadata.identity && !pubkeys.has(msg.pubkey)) { if (
msg.metadata.identity &&
msg.metadata.identity !== 'self' &&
!pubkeys.has(msg.pubkey)
) {
// prettier-ignore // prettier-ignore
return `invalid message: pubkey "${msg.pubkey}" should have been one of "${[...pubkeys]}" from the identity "${msg.metadata.identity}"\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)
} }

View File

@ -24,7 +24,7 @@ interface Msg {
metadata: { metadata: {
dataHash: ContentHash | null // blake3 hash of the `content` object serialized dataHash: ContentHash | null // blake3 hash of the `content` object serialized
dataSize: number // byte size (unsigned integer) 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 identity: string | 'self' | null // blake3 hash of an identity tangle root msg, or the string 'self', or null
identityTips: Array<string> | null // list of blake3 hashes of identity tangle tips, or null identityTips: Array<string> | null // list of blake3 hashes of identity tangle tips, or null
tangles: { tangles: {
// for each tangle this msg belongs to, identified by the tangle's root // for each tangle this msg belongs to, identified by the tangle's root
@ -54,7 +54,7 @@ interface Msg {
metadata: { metadata: {
dataHash: ContentHash dataHash: ContentHash
dataSize: number dataSize: number
identity: null // MUST be null identity: 'self' // MUST be the string 'self'
identityTips: null // MUST be null identityTips: null // MUST be null
tangles: { tangles: {
[identityTangleId: string]: { [identityTangleId: string]: {
@ -62,7 +62,7 @@ interface Msg {
prev: Array<MsgHash> // list of msg hashes of existing msgs, unique set and ordered alphabetically prev: Array<MsgHash> // list of msg hashes of existing msgs, unique set and ordered alphabetically
} }
} }
domain: 'identity' // MUST be 'identity' domain: string // alphanumeric string, at least 3 chars, max 100 chars
v: 2 v: 2
} }
pubkey: Pubkey pubkey: Pubkey

View File

@ -21,7 +21,7 @@ test('add()', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const identityMsg0 = MsgV3.createIdentity(keypair) const identityMsg0 = MsgV3.createIdentity(keypair, 'person')
const id = MsgV3.getMsgHash(identityMsg0) const id = MsgV3.getMsgHash(identityMsg0)
await p(peer.db.add)(identityMsg0, id) await p(peer.db.add)(identityMsg0, id)

View File

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

View File

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

View File

@ -25,7 +25,7 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
id = (await p(peer.db.identity.create)(null)).hash id = (await p(peer.db.identity.create)({domain: 'person'}))
rootMsg = MsgV3.createRoot(id, 'post', keypair) rootMsg = MsgV3.createRoot(id, 'post', keypair)
rootHash = MsgV3.getMsgHash(rootMsg) rootHash = MsgV3.getMsgHash(rootMsg)

View File

@ -26,7 +26,7 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
id = (await p(peer.db.identity.create)(null)).hash id = (await p(peer.db.identity.create)({domain: 'person'}))
rootMsg = MsgV3.createRoot(id, 'post', keypair) rootMsg = MsgV3.createRoot(id, 'post', keypair)
rootHash = MsgV3.getMsgHash(rootMsg) rootHash = MsgV3.getMsgHash(rootMsg)
}) })

View File

@ -25,7 +25,7 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
id = (await p(peer.db.identity.create)(null)).hash id = (await p(peer.db.identity.create)({domain: 'person'}))
const rec1 = await p(peer.db.feed.publish)({ const rec1 = await p(peer.db.feed.publish)({
identity: id, identity: id,

View File

@ -26,7 +26,7 @@ test('setup', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const id = (await p(peer.db.identity.create)(null)).hash const id = (await p(peer.db.identity.create)({domain: 'person'}))
// Slow down append so that we can trigger msg creation in parallel // Slow down append so that we can trigger msg creation in parallel
const originalAppend = peer.db._getLog().append const originalAppend = peer.db._getLog().append

View File

@ -21,15 +21,20 @@ test('identity.add()', async (t) => {
.call(null, { keypair: keypair1, path: DIR }) .call(null, { keypair: keypair1, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const identityRec0 = await p(peer.db.identity.create)({ keypair: keypair1 }) const id = await p(peer.db.identity.create)({
const id = identityRec0.hash keypair: keypair1,
domain: 'person',
})
const identityRec1 = await p(peer.db.identity.add)({ identity: id, keypair: keypair2 }) const identityRec1 = await p(peer.db.identity.add)({
identity: id,
keypair: keypair2,
})
assert.ok(identityRec1, 'identityRec1 exists') assert.ok(identityRec1, 'identityRec1 exists')
const { hash, msg } = identityRec1 const { hash, msg } = identityRec1
assert.ok(hash, 'hash exists') assert.ok(hash, 'hash exists')
assert.equal(msg.data.add, keypair2.public, 'msg.data.add NEW KEY') assert.equal(msg.data.add, keypair2.public, 'msg.data.add NEW KEY')
assert.equal(msg.metadata.identity, null, 'msg.metadata.identity') assert.equal(msg.metadata.identity, 'self', 'msg.metadata.identity')
assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips') assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips')
assert.deepEqual( assert.deepEqual(
msg.metadata.tangles, msg.metadata.tangles,
@ -54,9 +59,15 @@ test('publish with a key in the identity', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const identityRec0 = await p(peer.db.identity.create)({ keypair: keypair1 }) const identity = await p(peer.db.identity.create)({
const identity = identityRec0.hash keypair: keypair1,
const identityRec1 = await p(peer.db.identity.add)({ identity, keypair: keypair2 }) domain: 'person',
})
const identityMsg0 = peer.db.get(identity)
const identityRec1 = await p(peer.db.identity.add)({
identity,
keypair: keypair2,
})
const postRec = await p(peer.db.feed.publish)({ const postRec = await p(peer.db.feed.publish)({
identity, identity,
@ -71,9 +82,11 @@ test('publish with a key in the identity', async (t) => {
const recs = [...peer.db.records()] const recs = [...peer.db.records()]
assert.equal(recs.length, 4, '4 records') assert.equal(recs.length, 4, '4 records')
const [_identityRec0, _identityRec1, postsRoot, _post] = recs const [_identityRec0, _identityRec1, postsRoot, _post] = recs
assert.deepEqual(_identityRec0.msg, identityRec0.msg, 'identityMsg0') assert.deepEqual(_identityRec0.msg, identityMsg0, 'identityMsg0')
assert.deepEqual(_identityRec1.msg, identityRec1.msg, 'identityMsg1') assert.deepEqual(_identityRec1.msg, identityRec1.msg, 'identityMsg1')
assert.deepEqual(postsRoot.msg.metadata, { assert.deepEqual(
postsRoot.msg.metadata,
{
dataHash: null, dataHash: null,
dataSize: 0, dataSize: 0,
identity, identity,
@ -81,7 +94,9 @@ test('publish with a key in the identity', async (t) => {
tangles: {}, tangles: {},
domain: 'post', domain: 'post',
v: 3, v: 3,
}, 'postsRoot') },
'postsRoot'
)
assert.deepEqual(_post.msg, postRec.msg, 'postMsg') assert.deepEqual(_post.msg, postRec.msg, 'postMsg')
await p(peer.close)() await p(peer.close)()
@ -97,7 +112,7 @@ test('publish with a key in the identity', async (t) => {
await carol.db.loaded() await carol.db.loaded()
await p(carol.db.add)(identityRec0.msg, identity) await p(carol.db.add)(identityMsg0, identity)
await p(carol.db.add)(identityRec1.msg, identity) await p(carol.db.add)(identityRec1.msg, identity)
await p(carol.db.add)(postsRoot.msg, postsId) await p(carol.db.add)(postsRoot.msg, postsId)
await p(carol.db.add)(postRec.msg, postsId) await p(carol.db.add)(postRec.msg, postsId)

View File

@ -11,7 +11,7 @@ const Keypair = require('ppppp-keypair')
const DIR = path.join(os.tmpdir(), 'ppppp-db-identity-create') const DIR = path.join(os.tmpdir(), 'ppppp-db-identity-create')
rimraf.sync(DIR) rimraf.sync(DIR)
test('identity.create() without args', async (t) => { test('identity.create() with just "domain"', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const peer = SecretStack({ appKey: caps.shse }) const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib')) .use(require('../lib'))
@ -19,20 +19,23 @@ test('identity.create() without args', async (t) => {
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const identityRec0 = await p(peer.db.identity.create)({}) const identity = await p(peer.db.identity.create)({ domain: 'person' })
assert.ok(identityRec0, 'identityRec0 exists') assert.ok(identity, 'identityRec0 exists')
const { hash, msg } = identityRec0 const msg = peer.db.get(identity)
assert.ok(hash, 'hash exists')
assert.equal(msg.data.add, keypair.public, 'msg.data.add') assert.equal(msg.data.add, keypair.public, 'msg.data.add')
assert.equal(msg.metadata.identity, null, 'msg.metadata.identity') assert.equal(msg.metadata.identity, 'self', 'msg.metadata.identity')
assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips') assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips')
assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles') assert.deepEqual(
Object.keys(msg.metadata.tangles),
[],
'msg.metadata.tangles'
)
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await p(peer.close)() await p(peer.close)()
}) })
test('identity.create() with "keypair" arg', async (t) => { test('identity.create() with "keypair" and "domain"', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const peer = SecretStack({ appKey: caps.shse }) const peer = SecretStack({ appKey: caps.shse })
@ -41,14 +44,20 @@ test('identity.create() with "keypair" arg', async (t) => {
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const identityRec0 = await p(peer.db.identity.create)({ keypair }) const identity = await p(peer.db.identity.create)({
assert.ok(identityRec0, 'identityRec0 exists') keypair,
const { hash, msg } = identityRec0 domain: 'person',
assert.ok(hash, 'hash exists') })
assert.ok(identity, 'identity created')
const msg = peer.db.get(identity)
assert.equal(msg.data.add, keypair.public, 'msg.data.add') assert.equal(msg.data.add, keypair.public, 'msg.data.add')
assert.equal(msg.metadata.identity, null, 'msg.metadata.identity') assert.equal(msg.metadata.identity, 'self', 'msg.metadata.identity')
assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips') assert.equal(msg.metadata.identityTips, null, 'msg.metadata.identityTips')
assert.deepEqual(Object.keys(msg.metadata.tangles), [], 'msg.metadata.tangles') assert.deepEqual(
Object.keys(msg.metadata.tangles),
[],
'msg.metadata.tangles'
)
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await p(peer.close)() await p(peer.close)()

View File

@ -7,21 +7,21 @@ let identity
test('MsgV3.createIdentity()', (t) => { test('MsgV3.createIdentity()', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const identityMsg0 = MsgV3.createIdentity(keypair, 'MYNONCE') const identityMsg0 = MsgV3.createIdentity(keypair, 'person', 'MYNONCE')
console.log(JSON.stringify(identityMsg0, null, 2)) console.log(JSON.stringify(identityMsg0, null, 2))
assert.equal(identityMsg0.data.add, keypair.public, 'data.add') assert.equal(identityMsg0.data.add, keypair.public, 'data.add')
assert.equal(identityMsg0.metadata.dataHash, 'THi3VkJeaf8aTkLSNJUdFD', 'hash') assert.equal(identityMsg0.metadata.dataHash, 'THi3VkJeaf8aTkLSNJUdFD', 'hash')
assert.equal(identityMsg0.metadata.dataSize, 72, 'size') assert.equal(identityMsg0.metadata.dataSize, 72, 'size')
assert.equal(identityMsg0.metadata.identity, null, 'identity') assert.equal(identityMsg0.metadata.identity, 'self', 'identity')
assert.equal(identityMsg0.metadata.identityTips, null, 'identityTips') assert.equal(identityMsg0.metadata.identityTips, null, 'identityTips')
assert.deepEqual(identityMsg0.metadata.tangles, {}, 'tangles') assert.deepEqual(identityMsg0.metadata.tangles, {}, 'tangles')
assert.equal(identityMsg0.metadata.domain, 'identity', 'domain') assert.equal(identityMsg0.metadata.domain, 'person', 'domain')
assert.equal(identityMsg0.metadata.v, 3, 'v') assert.equal(identityMsg0.metadata.v, 3, 'v')
assert.equal(identityMsg0.pubkey, keypair.public, 'pubkey') assert.equal(identityMsg0.pubkey, keypair.public, 'pubkey')
identity = MsgV3.getMsgHash(identityMsg0) identity = MsgV3.getMsgHash(identityMsg0)
assert.equal(identity, 'WnAX17Lm2ktfPUJ5ARXq73', 'identity ID') assert.equal(identity, 'v7vBrnrCTahjgkpoaZrWm', 'identity ID')
}) })
let rootMsg = null let rootMsg = null
@ -43,7 +43,7 @@ test('MsgV3.createRoot()', (t) => {
assert.equal(rootMsg.pubkey, keypair.public, 'pubkey') assert.equal(rootMsg.pubkey, keypair.public, 'pubkey')
rootHash = MsgV3.getMsgHash(rootMsg) rootHash = MsgV3.getMsgHash(rootMsg)
assert.equal(rootHash, '5G4FJTWaGr7ZBUJGge6Qeg', 'root hash') assert.equal(rootHash, 'HPtwPD552ajEurwpgQRfTX', 'root hash')
}) })
test('MsgV3.create()', (t) => { test('MsgV3.create()', (t) => {
@ -95,11 +95,11 @@ test('MsgV3.create()', (t) => {
) )
assert.equal( assert.equal(
msg1.sig, msg1.sig,
'xZGu2Kb19XicfoihgBZ84jRs4XuNgVBd2bK45Cum2fdVDNJUE3f8Ejf6apfZFyE8iAfPDEVWFNAJB6E52EaWEAm', '2FhnKsDKCxEV4JUM1nmPp3oSFJ8zL7r4SjMNogDHeAzCWQLgVmKiexgUDSE4k9C3eT4Uy3SZbBhRY75WJAqvtHHf',
'sig' 'sig'
) )
const msgHash1 = 'NF389yT2td9gz5TvRuZMB6' const msgHash1 = 'FK4jCKFZDGwecydC8bitgR'
assert.equal( assert.equal(
MsgV3.getMsgId(msg1), MsgV3.getMsgId(msg1),
@ -155,13 +155,13 @@ test('MsgV3.create()', (t) => {
) )
assert.equal( assert.equal(
msg2.sig, msg2.sig,
'2XHQcG8KeNbdz8m5fDs2QtT7jcxgEqHxv7SkSYVpKQAJ1S8HGn3dmLxw3J5vWmu1vhYhWS6GDE1hfMtvmfiCAy54', '3B6WQvDdKvRhZeZDfE9LY4HrnhZTJHRJ86FazBg1xxco2S1eHG44UwR9TSpthiXQ1X51h2VeDeGPV6Fdma69BMN9',
'sig' 'sig'
) )
assert.deepEqual( assert.deepEqual(
MsgV3.getMsgId(msg2), MsgV3.getMsgId(msg2),
`ppppp:message/v3/${identity}/post/HNQp1oUu3zmgD1s11xiR7y`, `ppppp:message/v3/${identity}/post/AYXun8rEc3SNGZYM252TAS`,
'getMsgId' 'getMsgId'
) )
}) })

View File

@ -5,7 +5,9 @@ const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV3 = require('../../lib/msg-v3')
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'MYNONCE')) const identity = MsgV3.getMsgHash(
MsgV3.createIdentity(keypair, 'person', 'MYNONCE')
)
const pubkeys = new Set([keypair.public]) const pubkeys = new Set([keypair.public])
test('invalid msg with non-array prev', (t) => { test('invalid msg with non-array prev', (t) => {
@ -180,14 +182,20 @@ test('invalid msg with unknown prev', (t) => {
const err = MsgV3.validate(msg2, tangle, pubkeys, msgHash2, rootHash) const err = MsgV3.validate(msg2, tangle, pubkeys, msgHash2, rootHash)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match(err, /all prev are locally unknown/, 'invalid 2nd msg description') assert.match(
err,
/all prev are locally unknown/,
'invalid 2nd msg description'
)
}) })
test('invalid feed msg with a different pubkey', (t) => { test('invalid feed msg with a different pubkey', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob') const keypairB = Keypair.generate('ed25519', 'bob')
const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'MYNONCE')) const identityB = MsgV3.getMsgHash(
MsgV3.createIdentity(keypairB, 'person', 'MYNONCE')
)
const rootMsg = MsgV3.createRoot(identity, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
const rootHash = MsgV3.getMsgHash(rootMsg) const rootHash = MsgV3.getMsgHash(rootMsg)

View File

@ -5,7 +5,9 @@ const MsgV3 = require('../../lib/msg-v3')
test('lipmaa prevs', (t) => { test('lipmaa prevs', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'MYNONCE')) const identity = MsgV3.getMsgHash(
MsgV3.createIdentity(keypair, 'person', 'MYNONCE')
)
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }
const rootMsg = MsgV3.createRoot(identity, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)

View File

@ -6,8 +6,12 @@ const MsgV3 = require('../../lib/msg-v3')
test('simple multi-author tangle', (t) => { test('simple multi-author tangle', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob') const keypairB = Keypair.generate('ed25519', 'bob')
const identityA = MsgV3.getMsgHash(MsgV3.createIdentity(keypairA, 'alice')) const identityA = MsgV3.getMsgHash(
const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'bob')) MsgV3.createIdentity(keypairA, 'person', 'alice')
)
const identityB = MsgV3.getMsgHash(
MsgV3.createIdentity(keypairB, 'person', 'bob')
)
const rootMsgA = MsgV3.createRoot(identityA, 'post', keypairA) const rootMsgA = MsgV3.createRoot(identityA, 'post', keypairA)
const rootHashA = MsgV3.getMsgHash(rootMsgA) const rootHashA = MsgV3.getMsgHash(rootMsgA)
@ -82,8 +86,12 @@ test('simple multi-author tangle', (t) => {
test('lipmaa in multi-author tangle', (t) => { test('lipmaa in multi-author tangle', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob') const keypairB = Keypair.generate('ed25519', 'bob')
const identityA = MsgV3.getMsgHash(MsgV3.createIdentity(keypairA, 'alice')) const identityA = MsgV3.getMsgHash(
const identityB = MsgV3.getMsgHash(MsgV3.createIdentity(keypairB, 'bob')) MsgV3.createIdentity(keypairA, 'person', 'alice')
)
const identityB = MsgV3.getMsgHash(
MsgV3.createIdentity(keypairB, 'person', 'bob')
)
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }

View File

@ -5,7 +5,9 @@ const MsgV3 = require('../../lib/msg-v3')
test('validate root msg', (t) => { test('validate root msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'alice')) const identity = MsgV3.getMsgHash(
MsgV3.createIdentity(keypair, 'person', 'alice')
)
const pubkeys = new Set([keypair.public]) const pubkeys = new Set([keypair.public])
const rootMsg = MsgV3.createRoot(identity, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
@ -21,13 +23,19 @@ test('validate identity tangle', (t) => {
const keypair1 = Keypair.generate('ed25519', 'alice') const keypair1 = Keypair.generate('ed25519', 'alice')
pubkeys.add(keypair1.public) pubkeys.add(keypair1.public)
const identityMsg0 = MsgV3.createIdentity(keypair1, 'alice') const identityMsg0 = MsgV3.createIdentity(keypair1, 'person', 'alice')
const identity = MsgV3.getMsgHash(identityMsg0) const identity = MsgV3.getMsgHash(identityMsg0)
const identityMsg0Hash = identity const identityMsg0Hash = identity
const tangle = new MsgV3.Tangle(identity) const tangle = new MsgV3.Tangle(identity)
let err = MsgV3.validate(identityMsg0, tangle, pubkeys, identityMsg0Hash, identity) let err = MsgV3.validate(
identityMsg0,
tangle,
pubkeys,
identityMsg0Hash,
identity
)
assert.ifError(err, 'valid identity root msg') assert.ifError(err, 'valid identity root msg')
tangle.add(identity, identityMsg0) tangle.add(identity, identityMsg0)
@ -46,13 +54,21 @@ test('validate identity tangle', (t) => {
}) })
const identityMsg1Hash = MsgV3.getMsgHash(identityMsg1) const identityMsg1Hash = MsgV3.getMsgHash(identityMsg1)
err = MsgV3.validate(identityMsg1, tangle, pubkeys, identityMsg1Hash, identity) err = MsgV3.validate(
identityMsg1,
tangle,
pubkeys,
identityMsg1Hash,
identity
)
assert.ifError(err, 'valid identity msg') assert.ifError(err, 'valid identity msg')
}) })
test('validate 2nd msg with existing root', (t) => { test('validate 2nd msg with existing root', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'alice')) const identity = MsgV3.getMsgHash(
MsgV3.createIdentity(keypair, 'person', 'alice')
)
const pubkeys = new Set([keypair.public]) const pubkeys = new Set([keypair.public])
const rootMsg = MsgV3.createRoot(identity, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)
@ -79,7 +95,9 @@ test('validate 2nd msg with existing root', (t) => {
test('validate 2nd forked msg', (t) => { test('validate 2nd forked msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const identity = MsgV3.getMsgHash(MsgV3.createIdentity(keypair, 'alice')) const identity = MsgV3.getMsgHash(
MsgV3.createIdentity(keypair, 'person', 'alice')
)
const pubkeys = new Set([keypair.public]) const pubkeys = new Set([keypair.public])
const rootMsg = MsgV3.createRoot(identity, 'post', keypair) const rootMsg = MsgV3.createRoot(identity, 'post', keypair)

View File

@ -19,7 +19,7 @@ test('msgs() iterator', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const identity = (await p(peer.db.identity.create)(null)).hash const identity = (await p(peer.db.identity.create)({domain: 'person'}))
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
await p(peer.db.feed.publish)({ await p(peer.db.feed.publish)({

View File

@ -19,7 +19,7 @@ test('onRecordAdded', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const identity = (await p(peer.db.identity.create)(null)).hash const identity = (await p(peer.db.identity.create)({domain: 'person'}))
const listened = [] const listened = []
var remove = peer.db.onRecordAdded((ev) => { var remove = peer.db.onRecordAdded((ev) => {
@ -36,7 +36,7 @@ test('onRecordAdded', async (t) => {
await p(setTimeout)(500) await p(setTimeout)(500)
assert.equal(listened.length, 3) assert.equal(listened.length, 3)
assert.equal(listened[0].msg.metadata.identity, null, 'identity root') assert.equal(listened[0].msg.metadata.identity, 'self', 'identity root')
assert.equal(listened[1].msg.data, null, 'root') assert.equal(listened[1].msg.data, null, 'root')
assert.equal(listened[1].msg.metadata.dataSize, 0, 'root') assert.equal(listened[1].msg.metadata.dataSize, 0, 'root')
assert.deepEqual(listened[2], rec1, 'actual record') assert.deepEqual(listened[2], rec1, 'actual record')

View File

@ -19,7 +19,7 @@ test('publish some msgs, close, re-open', async (t) => {
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const identity = (await p(peer.db.identity.create)(null)).hash const identity = await p(peer.db.identity.create)({ domain: 'person' })
// t.pass('opened db') // t.pass('opened db')
const msgHashes = [] const msgHashes = []
@ -49,7 +49,7 @@ test('publish some msgs, close, re-open', async (t) => {
const texts = [] const texts = []
for (const msg of peer2.db.msgs()) { for (const msg of peer2.db.msgs()) {
if (!msg.data || !msg.metadata.identity) continue if (!msg.data || !(msg.metadata.identity?.length > 4)) continue
texts.push(msg.data.text) texts.push(msg.data.text)
} }

View File

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