This commit is contained in:
Andre Staltz 2023-12-25 12:24:35 +02:00
parent fd2e50438a
commit 3a2bfe25d0
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
27 changed files with 546 additions and 352 deletions

View File

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

View File

@ -6,13 +6,13 @@ const base58 = require('bs58')
const Obz = require('obz') const Obz = require('obz')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const Log = require('./log') const Log = require('./log')
const MsgV3 = require('./msg-v3') const MsgV4 = require('./msg-v4')
const { const {
SIGNATURE_TAG_ACCOUNT_ADD, SIGNATURE_TAG_ACCOUNT_ADD,
ACCOUNT_SELF, ACCOUNT_SELF,
ACCOUNT_ANY, ACCOUNT_ANY,
ACCOUNT_DOMAIN_PREFIX, ACCOUNT_DOMAIN_PREFIX,
} = require('./msg-v3/constants') } = require('./msg-v4/constants')
const ReadyGate = require('./utils/ready-gate') const ReadyGate = require('./utils/ready-gate')
const Ghosts = require('./ghosts') const Ghosts = require('./ghosts')
const { decrypt } = require('./encryption') const { decrypt } = require('./encryption')
@ -22,11 +22,11 @@ const { decrypt } = require('./encryption')
* @typedef {import('ppppp-keypair').KeypairPublicSlice} KeypairPublicSlice * @typedef {import('ppppp-keypair').KeypairPublicSlice} KeypairPublicSlice
* @typedef {import('ppppp-keypair').KeypairPrivateSlice} KeypairPrivateSlice * @typedef {import('ppppp-keypair').KeypairPrivateSlice} KeypairPrivateSlice
* @typedef {string} MsgID * @typedef {string} MsgID
* @typedef {import('./msg-v3').Msg} Msg * @typedef {import('./msg-v4').Msg} Msg
* @typedef {import('./msg-v3').AccountData} AccountData * @typedef {import('./msg-v4').AccountData} AccountData
* @typedef {import('./msg-v3').AccountPower} AccountPower * @typedef {import('./msg-v4').AccountPower} AccountPower
* @typedef {import('./msg-v4/tangle')} Tangle
* @typedef {import('./encryption').EncryptionFormat} EncryptionFormat * @typedef {import('./encryption').EncryptionFormat} EncryptionFormat
* @typedef {import('./msg-v3/tangle')} Tangle
* @typedef {Buffer | Uint8Array} B4A * @typedef {Buffer | Uint8Array} B4A
* @typedef {{ * @typedef {{
* db: { * db: {
@ -85,7 +85,7 @@ function assertValidConfig(config) {
} }
} }
class DBTangle extends MsgV3.Tangle { class DBTangle extends MsgV4.Tangle {
/** @type {(msgID: MsgID) => Msg | undefined} */ /** @type {(msgID: MsgID) => Msg | undefined} */
#getMsg #getMsg
@ -354,15 +354,15 @@ function initDB(peer, config) {
} }
/** /**
* Find which pubkeys are authorized to sign this msg given the account. * Find which sigkeys are authorized to sign this msg given the account.
* *
* @private * @private
* @param {Tangle | null} accountTangle * @param {Tangle | null} accountTangle
* @returns {Set<string>} * @returns {Set<string>}
*/ */
function getPubkeysInAccount(accountTangle) { function getSigkeysInAccount(accountTangle) {
const pubkeys = new Set() const sigkeys = new Set()
if (!accountTangle) return pubkeys if (!accountTangle) return sigkeys
// TODO: prune the accountTangle beyond msg.metadata.accountTips // TODO: prune the accountTangle beyond msg.metadata.accountTips
for (const msgID of accountTangle.topoSort()) { for (const msgID of accountTangle.topoSort()) {
const msg = get(msgID) const msg = get(msgID)
@ -373,9 +373,9 @@ function initDB(peer, config) {
const purpose = data.key?.purpose const purpose = data.key?.purpose
if (purpose !== 'sig' && purpose !== 'shs-and-sig') continue if (purpose !== 'sig' && purpose !== 'shs-and-sig') continue
if (data.key.algorithm !== 'ed25519') continue if (data.key.algorithm !== 'ed25519') continue
pubkeys.add(data.key.bytes) sigkeys.add(data.key.bytes)
} }
return pubkeys return sigkeys
} }
/** /**
@ -405,7 +405,7 @@ function initDB(peer, config) {
tangle.add(rec.id, rec.msg) tangle.add(rec.id, rec.msg)
} }
// Identify the account and its pubkeys: // Identify the account and its sigkeys:
/** @type {Tangle | null} */ /** @type {Tangle | null} */
let accountTangle let accountTangle
try { try {
@ -413,7 +413,7 @@ function initDB(peer, config) {
} catch (err) { } catch (err) {
return new Error('Unknown account tangle owning this msg', { cause: err }) return new Error('Unknown account tangle owning this msg', { cause: err })
} }
const pubkeys = getPubkeysInAccount(accountTangle) const sigkeys = getSigkeysInAccount(accountTangle)
// Don't accept ghosts to come back, unless they are trail msgs // Don't accept ghosts to come back, unless they are trail msgs
if (!!rec.msg.data && ghosts.read(tangleID).has(rec.id)) { if (!!rec.msg.data && ghosts.read(tangleID).has(rec.id)) {
@ -421,7 +421,7 @@ function initDB(peer, config) {
} }
let err let err
if ((err = MsgV3.validate(rec.msg, tangle, pubkeys, rec.id, tangleID))) { if ((err = MsgV4.validate(rec.msg, tangle, sigkeys, rec.id, tangleID))) {
return new Error('Invalid msg', { cause: err }) return new Error('Invalid msg', { cause: err })
} }
@ -436,9 +436,9 @@ function initDB(peer, config) {
// Unwrap encrypted inner msg and verify it too // Unwrap encrypted inner msg and verify it too
if (typeof rec.msg.data === 'string') { if (typeof rec.msg.data === 'string') {
const recDecrypted = decrypt(rec, peer, config) const recDecrypted = decrypt(rec, peer, config)
if (MsgV3.isMsg(recDecrypted.msg.data)) { if (MsgV4.isMsg(recDecrypted.msg.data)) {
const innerMsg = /** @type {Msg} */ (recDecrypted.msg.data) const innerMsg = /** @type {Msg} */ (recDecrypted.msg.data)
const innerMsgID = MsgV3.getMsgID(innerMsg) const innerMsgID = MsgV4.getMsgID(innerMsg)
const innerRec = { id: innerMsgID, msg: innerMsg } const innerRec = { id: innerMsgID, msg: innerMsg }
try { try {
verifyRec(innerRec, innerMsgID) verifyRec(innerRec, innerMsgID)
@ -456,7 +456,7 @@ function initDB(peer, config) {
* @returns {MsgID} * @returns {MsgID}
*/ */
function inferTangleID(rec) { function inferTangleID(rec) {
if (MsgV3.isRoot(rec.msg)) return rec.id if (MsgV4.isRoot(rec.msg)) return rec.id
let tangleID = /**@type {string | null}*/ (null) let tangleID = /**@type {string | null}*/ (null)
for (const id in rec.msg.metadata.tangles) { for (const id in rec.msg.metadata.tangles) {
if (tangleID) { if (tangleID) {
@ -478,7 +478,7 @@ function initDB(peer, config) {
* @param {CB<RecPresent>} cb * @param {CB<RecPresent>} cb
*/ */
function add(msg, tangleID, cb) { function add(msg, tangleID, cb) {
const msgID = MsgV3.getMsgID(msg) const msgID = MsgV4.getMsgID(msg)
// TODO: optimize this. Perhaps have a Map() of msgID -> record // TODO: optimize this. Perhaps have a Map() of msgID -> 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.
@ -512,19 +512,19 @@ function initDB(peer, config) {
* @returns {string | undefined} * @returns {string | undefined}
*/ */
function validateAccountMsg(msg, accountTangle) { function validateAccountMsg(msg, accountTangle) {
if (!MsgV3.isRoot(msg)) { if (!MsgV4.isRoot(msg)) {
/** @type {AccountData} */ /** @type {AccountData} */
const data = msg.data const data = msg.data
if (data.action === 'add') { if (data.action === 'add') {
// Does this msg.pubkey have the "add" power? // Does this msg.sigkey have the "add" power?
const keypair = { const keypair = {
curve: /** @type {const} */ ('ed25519'), curve: /** @type {const} */ ('ed25519'),
public: msg.pubkey, public: msg.sigkey,
} }
const powers = getAccountPowers(accountTangle, keypair) const powers = getAccountPowers(accountTangle, keypair)
if (!powers.has('add')) { if (!powers.has('add')) {
// prettier-ignore // prettier-ignore
return `invalid account msg: pubkey "${msg.pubkey}" does not have "add" power` return `invalid account msg: sigkey "${msg.sigkey}" does not have "add" power`
} }
} }
// TODO validate 'del' // TODO validate 'del'
@ -542,8 +542,8 @@ function initDB(peer, config) {
const mootID = findMoot(account, domain)?.id const mootID = findMoot(account, domain)?.id
if (mootID) return cb(null, mootID) if (mootID) return cb(null, mootID)
const moot = MsgV3.createMoot(account, domain, keypair) const moot = MsgV4.createMoot(account, domain, keypair)
add(moot, MsgV3.getMsgID(moot), (err, rec) => { add(moot, MsgV4.getMsgID(moot), (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 }));
cb(null, rec.id) cb(null, rec.id)
@ -607,7 +607,7 @@ function initDB(peer, config) {
} }
} }
// prettier-ignore // prettier-ignore
const err = new Error(`account.find() failed for pubkey=${keypair.public} subdomain=${opts.subdomain}`, { cause: 'ENOENT' }); const err = new Error(`account.find() failed for sigkey=${keypair.public} subdomain=${opts.subdomain}`, { cause: 'ENOENT' });
cb(err) cb(err)
} }
@ -660,11 +660,11 @@ function initDB(peer, config) {
let msg let msg
try { try {
msg = MsgV3.createAccount(keypair, domain, opts?._nonce) msg = MsgV4.createAccount(keypair, domain, opts?._nonce)
} catch (err) { } catch (err) {
return cb(new Error('account.create() failed', { cause: err })) return cb(new Error('account.create() failed', { cause: err }))
} }
const msgID = MsgV3.getMsgID(msg) const msgID = MsgV4.getMsgID(msg)
logAppend(msgID, msg, (err, rec) => { logAppend(msgID, msg, (err, rec) => {
// prettier-ignore // prettier-ignore
@ -857,11 +857,11 @@ function initDB(peer, config) {
// Create the actual message: // Create the actual message:
let msg let msg
try { try {
msg = MsgV3.create(fullOpts) msg = MsgV4.create(fullOpts)
} catch (err) { } catch (err) {
return cb(new Error('account.add() failed', { cause: err })) return cb(new Error('account.add() failed', { cause: err }))
} }
const msgID = MsgV3.getMsgID(msg) const msgID = MsgV4.getMsgID(msg)
logAppend(msgID, msg, (err, rec) => { logAppend(msgID, msg, (err, rec) => {
// prettier-ignore // prettier-ignore
@ -927,7 +927,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 = MsgV3.toPlaintextBuffer(fullOpts) const plaintext = MsgV4.toPlaintextBuffer(fullOpts)
const encryptOpts = { const encryptOpts = {
...fullOpts, ...fullOpts,
recps: recps.map( recps: recps.map(
@ -959,12 +959,12 @@ function initDB(peer, config) {
// Create the actual message: // Create the actual message:
let msg let msg
try { try {
msg = MsgV3.create(fullOpts) msg = MsgV4.create(fullOpts)
} catch (err) { } catch (err) {
// prettier-ignore // prettier-ignore
return cb(new Error('feed.publish() failed to create message', { cause: err })) return cb(new Error('feed.publish() failed to create message', { cause: err }))
} }
const msgID = MsgV3.getMsgID(msg) const msgID = MsgV4.getMsgID(msg)
// Encode the native message and append it to the log: // Encode the native message and append it to the log:
logAppend(msgID, msg, (err, rec) => { logAppend(msgID, msg, (err, rec) => {
@ -982,9 +982,9 @@ function initDB(peer, config) {
* @returns {RecPresent | null} * @returns {RecPresent | null}
*/ */
function findMoot(id, findDomain) { function findMoot(id, findDomain) {
const findAccount = MsgV3.stripAccount(id) const findAccount = MsgV4.stripAccount(id)
for (const rec of records()) { for (const rec of records()) {
if (rec.msg && MsgV3.isMoot(rec.msg, findAccount, findDomain)) { if (rec.msg && MsgV4.isMoot(rec.msg, findAccount, findDomain)) {
return rec return rec
} }
} }
@ -1113,7 +1113,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()
rec.msg = MsgV3.erase(rec.msg) rec.msg = MsgV4.erase(rec.msg)
const misc = miscRegistry.get(rec) const misc = miscRegistry.get(rec)
const seq = misc?.seq ?? -1 const seq = misc?.seq ?? -1
const offset = misc?.offset ?? -1 const offset = misc?.offset ?? -1
@ -1163,7 +1163,7 @@ function initDB(peer, config) {
}, },
feed: { feed: {
publish: publishToFeed, publish: publishToFeed,
getID: MsgV3.getMootID, getID: MsgV4.getMootID,
findMoot, findMoot,
}, },
getRecord, getRecord,

View File

@ -7,6 +7,6 @@ module.exports = {
ACCOUNT_DOMAIN_PREFIX: 'account__', ACCOUNT_DOMAIN_PREFIX: 'account__',
SIGNATURE_TAG_MSG_V3: ':msg-v3:', SIGNATURE_TAG_MSG_V4: ':msg-v4:',
SIGNATURE_TAG_ACCOUNT_ADD: ':account-add:', SIGNATURE_TAG_ACCOUNT_ADD: ':account-add:',
} }

View File

@ -1,5 +1,5 @@
const b4a = require('b4a') const b4a = require('b4a')
const blake3 = require('blake3') const crypto = require('crypto')
const base58 = require('bs58') const base58 = require('bs58')
// @ts-ignore // @ts-ignore
const stringify = require('json-canon') const stringify = require('json-canon')
@ -15,8 +15,8 @@ const stringify = require('json-canon')
*/ */
function getMsgHashBuf(msg) { function getMsgHashBuf(msg) {
const metadataBuf = b4a.from(stringify(msg.metadata), 'utf8') const metadataBuf = b4a.from(stringify(msg.metadata), 'utf8')
const longHash = b4a.from(blake3.hash(metadataBuf)) const metadataHash = crypto.createHash('sha512').update(metadataBuf).digest()
return longHash.subarray(0, 16) return b4a.from(metadataHash.subarray(0, 32))
} }
/** /**
@ -25,7 +25,7 @@ function getMsgHashBuf(msg) {
*/ */
function getMsgID(x) { function getMsgID(x) {
if (typeof x === 'string') { if (typeof x === 'string') {
if (x.startsWith('ppppp:message/v3/')) { if (x.startsWith('ppppp:message/v4/')) {
const msgUri = x const msgUri = x
const parts = msgUri.split('/') const parts = msgUri.split('/')
return parts[parts.length - 1] return parts[parts.length - 1]
@ -48,9 +48,9 @@ function getMsgURI(msg) {
const { account, domain } = msg.metadata const { account, domain } = msg.metadata
const msgHash = getMsgID(msg) const msgHash = getMsgID(msg)
if (domain) { if (domain) {
return `ppppp:message/v3/${account}/${domain}/${msgHash}` return `ppppp:message/v4/${account}/${domain}/${msgHash}`
} else { } else {
return `ppppp:message/v3/${account}/${msgHash}` return `ppppp:message/v4/${account}/${msgHash}`
} }
} }

View File

@ -21,7 +21,7 @@ const Tangle = require('./tangle')
const { const {
ACCOUNT_SELF, ACCOUNT_SELF,
ACCOUNT_ANY, ACCOUNT_ANY,
SIGNATURE_TAG_MSG_V3, SIGNATURE_TAG_MSG_V4,
} = require('./constants') } = require('./constants')
const { isEmptyObject } = require('./util') const { isEmptyObject } = require('./util')
@ -42,9 +42,9 @@ const { isEmptyObject } = require('./util')
* [tangleID in string]: TangleMetadata * [tangleID in string]: TangleMetadata
* }; * };
* domain: string; * domain: string;
* v: 3; * v: 4;
* }; * };
* pubkey: string; * sigkey: string;
* sig: string; * sig: string;
* }} Msg * }} Msg
*/ */
@ -62,9 +62,9 @@ const { isEmptyObject } = require('./util')
* [tangleID in string]: TangleMetadata * [tangleID in string]: TangleMetadata
* }; * };
* domain: string; * domain: string;
* v: 3; * v: 4;
* }; * };
* pubkey: string; * sigkey: string;
* sig: string; * sig: string;
* }} FeedMsg * }} FeedMsg
*/ */
@ -142,9 +142,9 @@ function getMootID(id, domain) {
accountTips: null, accountTips: null,
tangles: {}, tangles: {},
domain, domain,
v: 3, v: 4,
}, },
pubkey: '', sigkey: '',
sig: '', sig: '',
} }
@ -192,15 +192,15 @@ function create(opts) {
accountTips, accountTips,
tangles, tangles,
domain: opts.domain, domain: opts.domain,
v: 3, v: 4,
}, },
pubkey: opts.keypair.public, sigkey: opts.keypair.public,
sig: '', sig: '',
} }
if ((err = validateData(msg))) throw err if ((err = validateData(msg))) throw err
const signableBuf = b4a.from( const signableBuf = b4a.from(
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata), SIGNATURE_TAG_MSG_V4 + stringify(msg.metadata),
'utf8' 'utf8'
) )
msg.sig = Keypair.sign(opts.keypair, signableBuf) msg.sig = Keypair.sign(opts.keypair, signableBuf)
@ -228,14 +228,14 @@ function createMoot(id, domain, keypair) {
accountTips: null, accountTips: null,
tangles: {}, tangles: {},
domain, domain,
v: 3, v: 4,
}, },
pubkey: keypair.public, sigkey: keypair.public,
sig: '', sig: '',
} }
const signableBuf = b4a.from( const signableBuf = b4a.from(
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata), SIGNATURE_TAG_MSG_V4 + stringify(msg.metadata),
'utf8' 'utf8'
) )
msg.sig = Keypair.sign(keypair, signableBuf) msg.sig = Keypair.sign(keypair, signableBuf)

View File

@ -1,4 +1,4 @@
const blake3 = require('blake3') const crypto = require('crypto')
const b4a = require('b4a') const b4a = require('b4a')
const base58 = require('bs58') const base58 = require('bs58')
// @ts-ignore // @ts-ignore
@ -14,8 +14,8 @@ const stringify = require('json-canon')
*/ */
function representData(data) { function representData(data) {
const dataBuf = b4a.from(stringify(data), 'utf8') const dataBuf = b4a.from(stringify(data), 'utf8')
const fullHash = /** @type {B4A} */ (blake3.hash(dataBuf)) const fullHash = crypto.createHash('sha512').update(dataBuf).digest()
const dataHash = base58.encode(fullHash.subarray(0, 16)) const dataHash = base58.encode(fullHash.subarray(0, 32))
const dataSize = dataBuf.length const dataSize = dataBuf.length
return [dataHash, dataSize] return [dataHash, dataSize]
} }

View File

@ -7,8 +7,8 @@
* @returns {string} * @returns {string}
*/ */
function stripAccount(id) { function stripAccount(id) {
if (id.startsWith('ppppp:account/v3/') === false) return id if (id.startsWith('ppppp:account/v4/') === false) return id
const withoutPrefix = id.replace('ppppp:account/v3/', '') const withoutPrefix = id.replace('ppppp:account/v4/', '')
return withoutPrefix.split('/')[0] return withoutPrefix.split('/')[0]
} }

View File

@ -7,7 +7,7 @@ const Tangle = require('./tangle')
const representData = require('./represent-data') const representData = require('./represent-data')
const isMoot = require('./is-moot') const isMoot = require('./is-moot')
const { const {
SIGNATURE_TAG_MSG_V3, SIGNATURE_TAG_MSG_V4,
ACCOUNT_SELF, ACCOUNT_SELF,
ACCOUNT_ANY, ACCOUNT_ANY,
} = require('./constants') } = require('./constants')
@ -49,8 +49,8 @@ function validateShape(msg) {
if (!('domain' in msg.metadata)) { if (!('domain' in msg.metadata)) {
return 'invalid msg: must have metadata.domain\n' + JSON.stringify(msg) return 'invalid msg: must have metadata.domain\n' + JSON.stringify(msg)
} }
if (msg.metadata.v !== 3) { if (msg.metadata.v !== 4) {
return 'invalid msg: must have metadata.v=3\n' + JSON.stringify(msg) return 'invalid msg: must have metadata.v=4\n' + JSON.stringify(msg)
} }
if (typeof msg.sig !== 'string') { if (typeof msg.sig !== 'string') {
return 'invalid msg: must have sig\n' + JSON.stringify(msg) return 'invalid msg: must have sig\n' + JSON.stringify(msg)
@ -61,21 +61,21 @@ function validateShape(msg) {
* @param {Msg} msg * @param {Msg} msg
* @returns {string | undefined} * @returns {string | undefined}
*/ */
function validatePubkey(msg) { function validateSigkey(msg) {
const { pubkey } = msg const { sigkey } = msg
if (typeof pubkey !== 'string') { if (typeof sigkey !== 'string') {
// prettier-ignore // prettier-ignore
return `invalid msg: pubkey "${pubkey}" should have been a string\n` + JSON.stringify(msg) return `invalid msg: sigkey "${sigkey}" should have been a string\n` + JSON.stringify(msg)
} }
try { try {
const pubkeyBuf = base58.decode(pubkey) const sigkeyBuf = base58.decode(sigkey)
if (pubkeyBuf.length !== 32) { if (sigkeyBuf.length !== 32) {
// prettier-ignore // prettier-ignore
return `invalid msg: decoded "pubkey" should be 32 bytes but was ${pubkeyBuf.length}\n` + JSON.stringify(msg) return `invalid msg: decoded "sigkey" should be 32 bytes but was ${sigkeyBuf.length}\n` + JSON.stringify(msg)
} }
} catch (err) { } catch (err) {
// prettier-ignore // prettier-ignore
return `invalid msg: pubkey "${pubkey}" should have been a base58 string\n` + JSON.stringify(msg) return `invalid msg: sigkey "${sigkey}" should have been a base58 string\n` + JSON.stringify(msg)
} }
} }
@ -83,18 +83,18 @@ function validatePubkey(msg) {
* *
* @param {Msg} msg * @param {Msg} msg
* @param {Tangle} tangle * @param {Tangle} tangle
* @param {Set<string>} pubkeys * @param {Set<string>} sigkeys
* @returns {string | undefined} * @returns {string | undefined}
*/ */
function validatePubkeyAndAccount(msg, tangle, pubkeys) { function validateSigkeyAndAccount(msg, tangle, sigkeys) {
if (tangle.type === 'feed' || tangle.type === 'weave') { if (tangle.type === 'feed' || tangle.type === 'weave') {
if (msg.metadata.account === ACCOUNT_SELF) { if (msg.metadata.account === ACCOUNT_SELF) {
// prettier-ignore // prettier-ignore
return `invalid msg: account "${msg.metadata.account}" cannot be "self" in a feed tangle\n` + JSON.stringify(msg) return `invalid msg: account "${msg.metadata.account}" cannot be "self" in a feed tangle\n` + JSON.stringify(msg)
} }
if (msg.metadata.account !== ACCOUNT_ANY && !pubkeys.has(msg.pubkey)) { if (msg.metadata.account !== ACCOUNT_ANY && !sigkeys.has(msg.sigkey)) {
// prettier-ignore // prettier-ignore
return `invalid msg: pubkey "${msg.pubkey}" should have been one of "${[...pubkeys]}" from the account "${msg.metadata.account}"\n` + JSON.stringify(msg) return `invalid msg: sigkey "${msg.sigkey}" should have been one of "${[...sigkeys]}" from the account "${msg.metadata.account}"\n` + JSON.stringify(msg)
} }
} else if (tangle.type === 'account') { } else if (tangle.type === 'account') {
if (msg.metadata.account !== ACCOUNT_SELF) { if (msg.metadata.account !== ACCOUNT_SELF) {
@ -115,9 +115,9 @@ function validatePubkeyAndAccount(msg, tangle, pubkeys) {
function validateMsgID(str) { function validateMsgID(str) {
try { try {
const hashBuf = b4a.from(base58.decode(str)) const hashBuf = b4a.from(base58.decode(str))
if (hashBuf.length !== 16) { if (hashBuf.length !== 32) {
// prettier-ignore // prettier-ignore
return `invalid msgID "${str}": should have 16 bytes but has ${hashBuf.length}` return `invalid msgID "${str}": should have 32 bytes but has ${hashBuf.length}`
} }
} catch (err) { } catch (err) {
return `invalid msgID "${str}": should have been a base58 string` return `invalid msgID "${str}": should have been a base58 string`
@ -147,12 +147,12 @@ function validateSignature(msg) {
} }
const signableBuf = b4a.from( const signableBuf = b4a.from(
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata), SIGNATURE_TAG_MSG_V4 + stringify(msg.metadata),
'utf8' 'utf8'
) )
const keypair = { const keypair = {
curve: /** @type {const} */ ('ed25519'), curve: /** @type {const} */ ('ed25519'),
public: msg.pubkey, public: msg.sigkey,
} }
const verified = Keypair.verify(keypair, signableBuf, sig) const verified = Keypair.verify(keypair, signableBuf, sig)
if (!verified) { if (!verified) {
@ -322,21 +322,21 @@ function validateDataSizeHash(msg) {
/** /**
* @param {Msg} msg * @param {Msg} msg
* @param {Tangle} tangle * @param {Tangle} tangle
* @param {Set<string>} pubkeys * @param {Set<string>} sigkeys
* @param {string} msgID * @param {string} msgID
* @param {string} rootID * @param {string} rootID
*/ */
function validate(msg, tangle, pubkeys, msgID, rootID) { function validate(msg, tangle, sigkeys, msgID, rootID) {
let err let err
if ((err = validateShape(msg))) return err if ((err = validateShape(msg))) return err
if ((err = validatePubkey(msg))) return err if ((err = validateSigkey(msg))) return err
if ((err = validateData(msg))) return err if ((err = validateData(msg))) return err
if (tangle.type === 'feed' && isMoot(msg)) return // nothing else to check if (tangle.type === 'feed' && isMoot(msg)) return // nothing else to check
if ((err = validateDataSizeHash(msg))) return err if ((err = validateDataSizeHash(msg))) return err
if ((err = validateDomain(msg.metadata.domain))) return err if ((err = validateDomain(msg.metadata.domain))) return err
if ((err = validatePubkeyAndAccount(msg, tangle, pubkeys))) return err if ((err = validateSigkeyAndAccount(msg, tangle, sigkeys))) return err
if (msgID === rootID) { if (msgID === rootID) {
if ((err = validateTangleRoot(msg, msgID, rootID))) return err if ((err = validateTangleRoot(msg, msgID, rootID))) return err
} else { } else {

View File

@ -1,3 +1,181 @@
# Msg V4
Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24
## Terminology
- **Hash** = base58 encoded string of the first 32 bytes of a sha512 hash
- **Msg** = `{data,metadata,sigkey,sig}` published by a peer
- **Msg ID** = `hash(msg.metadata)`
- **Tangle** = a single-root DAG of msgs that can be replicated by peers
- **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 ID of the tangle's root msg
- **Account** = tangle with msgs that add (or remove?) cryptographic keys
- **Account ID** = tangle ID of the account tangle
- **Feed** = tangle with msgs authored by (any sigkey in) an account
- **Moot** = the root of a feed, a msg that is deterministically predictable and empty, so to allow others to pre-know its msg ID, and thus the feed ID
- **Feed ID** = ID of the moot of a feed (Msg ID of the feed's root msg)
JSON
```typescript
interface Msg {
data: Record<string, any> | string | null // a plaintext object, or ciphertext string, or null
metadata: {
account: string | 'self' | 'any' // msg ID of account root, or the string 'self', or the string 'any'
accountTips: Array<string> | null // list (of unique items sorted lexicographically) of msg IDs of account tangle tips, or null
dataHash: string | null // hash of the `data` object serialized
dataSize: number // byte size (unsigned integer) of the `data` object serialized
domain: string // alphanumeric string, at least 3 chars, max 100 chars
tangles: {
// for each tangle this msg belongs to, identified by the tangle's ID
[tangleID: string]: {
depth: number // maximum distance (positive integer) from this msg to the root
prev: Array<MsgID> // list (of unique items sorted lexicographically) of msg IDs of existing msgs
}
}
v: 4 // hard-coded at 4, indicates the version of the feed format
}
sigkey: Sigkey // base58 encoded string for the author's public key
sig: Signature // Signs the `metadata` object
}
```
**Depth:** we NEED this field because it is the most reliable way of calculating lipmaa distances between msgs, in the face of sliced replication. For example, given that older messages (except the certificate pool) would be deleted, the "graph depth" calculation for a msg may change over time, but we need a way of keeping this calculation stable and deterministic.
## Account tangle msgs
Msgs in an account tangle are special because they have empty `account` and `accountTips` fields.
```typescript
interface Msg {
data: AccountData
metadata: {
account: 'self' // MUST be the string 'self'
accountTips: null // MUST be null
dataHash: string
dataSize: number
domain: string // alphanumeric string, must start with "account__"
tangles: {
[accountTangleID: string]: {
depth: number
prev: Array<MsgID>
}
}
v: 4
}
sigkey: Sigkey
sig: Signature
}
type AccountData = AccountAdd | AccountDel
// (if key is sig) "add" means this key can validly add more keys to the account
// (if key is sig) "del" means this key can validly revoke keys from the account
// (if key is shs) "internal-encryption" means this peer can get symmetric key
// (if key is shs) "external-encryption" means this peer can get asymmetric key
type AccountPower = 'add' | 'del' | 'internal-encryption' | 'external-encryption'
type AccountAdd = {
action: 'add'
key: Key
nonce?: string // nonce required only on the account tangle's root
consent?: string // base58 encoded signature of the string `:account-add:<ID>` where `<ID>` is the account's ID, required only on non-root msgs
accountPowers?: Array<AccountPower> // list of powers granted to this key, defaults to []
}
type AccountDel = {
action: 'del'
key: Key
}
type Key =
| {
purpose: 'shs-and-sig' // secret-handshake and digital signatures
algorithm: 'ed25519' // libsodium crypto_sign_detached
bytes: string // base58 encoded string for the public key
}
| {
purpose: 'external-encryption' // asymmetric encryption
algorithm: 'x25519-xsalsa20-poly1305' // libsodium crypto_box_easy
bytes: string // base58 encoded string of the public key
}
| {
purpose: 'sig' // secret-handshake and digital signatures
algorithm: 'ed25519' // libsodium crypto_sign_detached
bytes: string // base58 encoded string for the public key
}
```
Examples of `AccountData`:
- Registering the first public key:
```json
{
"action": "add",
"key": {
"purpose": "shs-and-sig",
"algorithm": "ed25519",
"bytes": "3JrJiHEQzRFMzEqWawfBgq2DSZDyihP1NHXshqcL8pB9"
},
"nonce": "6GHR1ZFFSB3C5qAGwmSwVH8f7byNo8Cqwn5PcyG3qDvS"
}
```
- Revoking a public key:
```json
{
"action": "del",
"key": {
"purpose": "shs-and-sig",
"algorithm": "ed25519",
"bytes": "3JrJiHEQzRFMzEqWawfBgq2DSZDyihP1NHXshqcL8pB9"
}
}
```
## 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
account: string // MUST be an ID
accountTips: null // MUST be null
tangles: {} // MUST be empty object
domain: string
v: 4
}
sigkey: Sigkey
sig: Signature
}
```
Thus, given a `account` 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. Thus the name "moot".
## 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 Msg V4 message, we follow the "JSON Canonicalization Scheme" (JSC) defined by [RFC 8785](https://tools.ietf.org/html/rfc8785).
A serialized msg must not be larger than 65535 UTF-8 bytes.
# Msg V3 # Msg V3
Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24 Background: https://github.com/ssbc/ssb2-discussion-forum/issues/24
@ -37,7 +215,7 @@ interface Msg {
v: 3 // hard-coded at 3, indicates the version of the feed format v: 3 // hard-coded at 3, indicates the version of the feed format
} }
pubkey: Pubkey // base58 encoded string for the author's public key pubkey: Pubkey // base58 encoded string for the author's public key
sig: Signature // Signs the `metadata` object sig: Signature // base58 encoded string of the signature of the UTF8 string ":msg-v4:<METADATA>" where `<METADATA>` is the msg.metadata object serialized
} }
``` ```
@ -64,7 +242,7 @@ interface Msg {
} }
v: 3 v: 3
} }
pubkey: Pubkey sigkey: Pubkey
sig: Signature sig: Signature
} }

View File

@ -62,7 +62,7 @@ test('account.add()', async (t) => {
{ [account]: { depth: 1, prev: [account] } }, { [account]: { depth: 1, prev: [account] } },
'msg.metadata.tangles' 'msg.metadata.tangles'
) )
assert.equal(msg.pubkey, keypair1.public, 'msg.pubkey OLD KEY') assert.equal(msg.sigkey, keypair1.public, 'msg.sigkey OLD KEY')
assert.equal(peer.db.account.has({ account, keypair: keypair2 }), true) assert.equal(peer.db.account.has({ account, keypair: keypair2 }), true)
@ -198,7 +198,7 @@ test('account.add()', async (t) => {
accountTips: null, accountTips: null,
tangles: {}, tangles: {},
domain: 'post', domain: 'post',
v: 3, v: 4,
}, },
'postsRoot' 'postsRoot'
) )

View File

@ -47,7 +47,7 @@ test('account.create() ', async (t) => {
[], [],
'msg.metadata.tangles' 'msg.metadata.tangles'
) )
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') assert.equal(msg.sigkey, keypair.public, 'msg.sigkey')
await p(peer.close)() await p(peer.close)()
}) })
@ -76,7 +76,7 @@ test('account.create() ', async (t) => {
[], [],
'msg.metadata.tangles' 'msg.metadata.tangles'
) )
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') assert.equal(msg.sigkey, keypair.public, 'msg.sigkey')
await p(peer.close)() await p(peer.close)()
}) })
@ -154,7 +154,7 @@ test('account.create() ', async (t) => {
[], [],
'msg.metadata.tangles' 'msg.metadata.tangles'
) )
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey') assert.equal(msg.sigkey, keypair.public, 'msg.sigkey')
await p(peer.close)() await p(peer.close)()
}) })

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack') const SecretStack = require('secret-stack')
const caps = require('ppppp-caps') const caps = require('ppppp-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../lib/msg-v3') const MsgV4 = require('../lib/msg-v4')
const DIR = path.join(os.tmpdir(), 'ppppp-db-add') const DIR = path.join(os.tmpdir(), 'ppppp-db-add')
rimraf.sync(DIR) rimraf.sync(DIR)
@ -21,20 +21,20 @@ test('add()', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const accountMsg0 = MsgV3.createAccount(keypair, 'person', 'aliceNonce') const accountMsg0 = MsgV4.createAccount(keypair, 'person', 'aliceNonce')
const id = MsgV3.getMsgID(accountMsg0) const id = MsgV4.getMsgID(accountMsg0)
await p(peer.db.add)(accountMsg0, id) await p(peer.db.add)(accountMsg0, id)
const rootMsg = MsgV3.createMoot(id, 'post', keypair) const rootMsg = MsgV4.createMoot(id, 'post', keypair)
const rootID = MsgV3.getMsgID(rootMsg) const rootID = MsgV4.getMsgID(rootMsg)
const recRoot = await p(peer.db.add)(rootMsg, rootID) const recRoot = await p(peer.db.add)(rootMsg, rootID)
assert.equal(recRoot.msg.metadata.dataSize, 0, 'root msg added') assert.equal(recRoot.msg.metadata.dataSize, 0, 'root msg added')
const tangle = new MsgV3.Tangle(rootID) const tangle = new MsgV4.Tangle(rootID)
tangle.add(recRoot.id, recRoot.msg) tangle.add(recRoot.id, recRoot.msg)
const inputMsg = MsgV3.create({ const inputMsg = MsgV4.create({
keypair, keypair,
domain: 'post', domain: 'post',
data: { text: 'This is the first post!' }, data: { text: 'This is the first post!' },
@ -49,7 +49,7 @@ test('add()', async (t) => {
assert.equal(rec.msg.data.text, 'This is the first post!') assert.equal(rec.msg.data.text, 'This is the first post!')
const stats = await p(peer.db.log.stats)() const stats = await p(peer.db.log.stats)()
assert.deepEqual(stats, { totalBytes: 1450, deletedBytes: 0 }) assert.deepEqual(stats, { totalBytes: 1662, deletedBytes: 0 })
await p(peer.close)(true) await p(peer.close)(true)
}) })

View File

@ -51,7 +51,7 @@ test('del()', async (t) => {
const stats1 = await p(peer.db.log.stats)() const stats1 = await p(peer.db.log.stats)()
assert.deepEqual( assert.deepEqual(
stats1, stats1,
{ totalBytes: 3399, deletedBytes: 0 }, { totalBytes: 4158, deletedBytes: 0 },
'stats before delete and compact' 'stats before delete and compact'
) )
@ -102,7 +102,7 @@ test('del()', async (t) => {
const stats2 = await p(log.stats)() const stats2 = await p(log.stats)()
assert.deepEqual( assert.deepEqual(
stats2, stats2,
{ totalBytes: 2889, deletedBytes: 0 }, { totalBytes: 3495, deletedBytes: 0 },
'stats after delete and compact' 'stats after delete and compact'
) )

View File

@ -49,7 +49,7 @@ test('erase()', async (t) => {
'5 msgs before the erase' '5 msgs before the erase'
) )
const EXPECTED_TOTAL_BYTES = 3399 const EXPECTED_TOTAL_BYTES = 4158
const stats1 = await p(peer.db.log.stats)() const stats1 = await p(peer.db.log.stats)()
assert.deepEqual( assert.deepEqual(
stats1, stats1,

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack') const SecretStack = require('secret-stack')
const caps = require('ppppp-caps') const caps = require('ppppp-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../lib/msg-v3') const MsgV4 = require('../lib/msg-v4')
const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-find-moot') const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-find-moot')
rimraf.sync(DIR) rimraf.sync(DIR)
@ -22,8 +22,8 @@ test('feed.findMoot()', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const id = await p(peer.db.account.create)({ subdomain: 'person' }) const id = await p(peer.db.account.create)({ subdomain: 'person' })
const moot = MsgV3.createMoot(id, 'post', keypair) const moot = MsgV4.createMoot(id, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
await p(peer.db.add)(moot, mootID) await p(peer.db.add)(moot, mootID)

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack') const SecretStack = require('secret-stack')
const caps = require('ppppp-caps') const caps = require('ppppp-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../lib/msg-v3') const MsgV4 = require('../lib/msg-v4')
const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-get-id') const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-get-id')
rimraf.sync(DIR) rimraf.sync(DIR)
@ -22,8 +22,8 @@ test('feed.getID()', async (t) => {
await peer.db.loaded() await peer.db.loaded()
const id = await p(peer.db.account.create)({ subdomain: 'person' }) const id = await p(peer.db.account.create)({ subdomain: 'person' })
const moot = MsgV3.createMoot(id, 'post', keypair) const moot = MsgV4.createMoot(id, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
assert.equal( assert.equal(
peer.db.feed.getID(id, 'post'), peer.db.feed.getID(id, 'post'),

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack') const SecretStack = require('secret-stack')
const caps = require('ppppp-caps') const caps = require('ppppp-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../lib/msg-v3') const MsgV4 = require('../lib/msg-v4')
const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish') const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish')
rimraf.sync(DIR) rimraf.sync(DIR)
@ -30,8 +30,8 @@ test('feed.publish()', async (t) => {
await peer.db.loaded() await peer.db.loaded()
id = await p(peer.db.account.create)({ subdomain: 'person' }) id = await p(peer.db.account.create)({ subdomain: 'person' })
moot = MsgV3.createMoot(id, 'post', keypair) moot = MsgV4.createMoot(id, 'post', keypair)
mootID = MsgV3.getMsgID(moot) mootID = MsgV4.getMsgID(moot)
} }
let msgID1 let msgID1
@ -56,7 +56,7 @@ test('feed.publish()', async (t) => {
'msg1 tangle prev correct' 'msg1 tangle prev correct'
) )
msgID1 = MsgV3.getMsgID(rec1.msg) msgID1 = MsgV4.getMsgID(rec1.msg)
const rec2 = await p(peer.db.feed.publish)({ const rec2 = await p(peer.db.feed.publish)({
account: id, account: id,
@ -74,15 +74,15 @@ test('feed.publish()', async (t) => {
[msgID1], [msgID1],
'msg2 tangle prev correct' 'msg2 tangle prev correct'
) )
msgID2 = MsgV3.getMsgID(rec2.msg) msgID2 = MsgV4.getMsgID(rec2.msg)
}) })
await t.test('merges tangle after a forked add()', async (t) => { await t.test('merges tangle after a forked add()', async (t) => {
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
tangle.add(rec1.id, rec1.msg) tangle.add(rec1.id, rec1.msg)
const msg3 = MsgV3.create({ const msg3 = MsgV4.create({
keypair, keypair,
account: id, account: id,
accountTips: [id], accountTips: [id],
@ -94,7 +94,7 @@ test('feed.publish()', async (t) => {
}) })
const rec3 = await p(peer.db.add)(msg3, mootID) const rec3 = await p(peer.db.add)(msg3, mootID)
const msgID3 = MsgV3.getMsgID(rec3.msg) const msgID3 = MsgV4.getMsgID(rec3.msg)
const rec4 = await p(peer.db.feed.publish)({ const rec4 = await p(peer.db.feed.publish)({
account: id, account: id,

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack') const SecretStack = require('secret-stack')
const caps = require('ppppp-caps') const caps = require('ppppp-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../lib/msg-v3') const MsgV4 = require('../lib/msg-v4')
const DIR = path.join(os.tmpdir(), 'ppppp-db-get') const DIR = path.join(os.tmpdir(), 'ppppp-db-get')
rimraf.sync(DIR) rimraf.sync(DIR)
@ -28,7 +28,7 @@ test('get()', async (t) => {
domain: 'post', domain: 'post',
data: { text: 'I am 1st post' }, data: { text: 'I am 1st post' },
}) })
const msgID1 = MsgV3.getMsgID(rec1.msg) const msgID1 = MsgV4.getMsgID(rec1.msg)
const msg = peer.db.get(msgID1) const msg = peer.db.get(msgID1)
assert.ok(msg, 'msg exists') assert.ok(msg, 'msg exists')

View File

@ -1,13 +1,13 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV4 = require('../../lib/msg-v4')
let account let account
test('MsgV3.createAccount()', (t) => { test('MsgV4.createAccount()', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const accountMsg0 = MsgV3.createAccount(keypair, 'person', 'MYNONCE') const accountMsg0 = MsgV4.createAccount(keypair, 'person', 'MYNONCE')
if (process.env.VERBOSE) console.log(JSON.stringify(accountMsg0, null, 2)) if (process.env.VERBOSE) console.log(JSON.stringify(accountMsg0, null, 2))
assert.deepEqual( assert.deepEqual(
@ -24,26 +24,34 @@ test('MsgV3.createAccount()', (t) => {
}, },
'data' 'data'
) )
assert.equal(accountMsg0.metadata.dataHash, 'NxJZecVcVUWmUkk6cAn9JV', 'hash') assert.equal(
accountMsg0.metadata.dataHash,
'4dDbfLtNMjzMgvvCA71tp6CiLjAa5bzzeHsbYuC4dpMT',
'hash'
)
assert.equal(accountMsg0.metadata.dataSize, 210, 'size') assert.equal(accountMsg0.metadata.dataSize, 210, 'size')
assert.equal(accountMsg0.metadata.account, 'self', 'account') assert.equal(accountMsg0.metadata.account, 'self', 'account')
assert.equal(accountMsg0.metadata.accountTips, null, 'accountTips') assert.equal(accountMsg0.metadata.accountTips, null, 'accountTips')
assert.deepEqual(accountMsg0.metadata.tangles, {}, 'tangles') assert.deepEqual(accountMsg0.metadata.tangles, {}, 'tangles')
assert.equal(accountMsg0.metadata.domain, 'person', 'domain') assert.equal(accountMsg0.metadata.domain, 'person', 'domain')
assert.equal(accountMsg0.metadata.v, 3, 'v') assert.equal(accountMsg0.metadata.v, 4, 'v')
assert.equal(accountMsg0.pubkey, keypair.public, 'pubkey') assert.equal(accountMsg0.sigkey, keypair.public, 'sigkey')
assert.equal(MsgV3.isFeedMsg(accountMsg0), false, 'not a feed msg') assert.equal(MsgV4.isFeedMsg(accountMsg0), false, 'not a feed msg')
account = MsgV3.getMsgID(accountMsg0) account = MsgV4.getMsgID(accountMsg0)
assert.equal(account, 'UQN1Qmxr4rr9nCMQKs9u8P', 'account ID') assert.equal(
account,
'Lq6xwbdvGVmSsY3oYRugpZ3DY8chX9SLhRhjJKyZHQn',
'account ID'
)
}) })
let moot = null let moot = null
let mootID = null let mootID = null
test('MsgV3.createMoot()', (t) => { test('MsgV4.createMoot()', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
moot = MsgV3.createMoot(account, 'post', keypair) moot = MsgV4.createMoot(account, 'post', keypair)
if (process.env.VERBOSE) console.log(JSON.stringify(moot, null, 2)) if (process.env.VERBOSE) console.log(JSON.stringify(moot, null, 2))
assert.equal(moot.data, null, 'data') assert.equal(moot.data, null, 'data')
@ -53,22 +61,26 @@ test('MsgV3.createMoot()', (t) => {
assert.equal(moot.metadata.accountTips, null, 'accountTips') assert.equal(moot.metadata.accountTips, null, 'accountTips')
assert.deepEqual(moot.metadata.tangles, {}, 'tangles') assert.deepEqual(moot.metadata.tangles, {}, 'tangles')
assert.equal(moot.metadata.domain, 'post', 'domain') assert.equal(moot.metadata.domain, 'post', 'domain')
assert.equal(moot.metadata.v, 3, 'v') assert.equal(moot.metadata.v, 4, 'v')
assert.equal(moot.pubkey, keypair.public, 'pubkey') assert.equal(moot.sigkey, keypair.public, 'sigkey')
assert.equal(MsgV3.isFeedMsg(moot), false, 'not a feed msg') assert.equal(MsgV4.isFeedMsg(moot), false, 'not a feed msg')
mootID = MsgV3.getMsgID(moot) mootID = MsgV4.getMsgID(moot)
assert.equal(mootID, 'AP2rJSfm9TwpNcMmbUsnRa', 'moot ID') assert.equal(
mootID,
'HH3P5muTjZkQC7uRKpzczGWbPNZBtk4BR4msyCNjwxpU',
'moot ID'
)
}) })
test('MsgV3.create()', (t) => { test('MsgV4.create()', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }
const tangle1 = new MsgV3.Tangle(mootID) const tangle1 = new MsgV4.Tangle(mootID)
tangle1.add(mootID, moot) tangle1.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
keypair, keypair,
data, data,
account, account,
@ -96,7 +108,7 @@ test('MsgV3.create()', (t) => {
) )
assert.deepEqual( assert.deepEqual(
msg1.metadata.dataHash, msg1.metadata.dataHash,
'9R7XmBhHF5ooPg34j9TQcz', 'APwSxrZUBx5wTHcT42fJTyddEjqkEAPXVMwaczTSuHTJ',
'metadata.dataHash' 'metadata.dataHash'
) )
assert.deepEqual(msg1.metadata.dataSize, 23, 'metadata.dataSize') assert.deepEqual(msg1.metadata.dataSize, 23, 'metadata.dataSize')
@ -110,30 +122,30 @@ test('MsgV3.create()', (t) => {
assert.equal(msg1.metadata.tangles[mootID].depth, 1, 'tangle depth') assert.equal(msg1.metadata.tangles[mootID].depth, 1, 'tangle depth')
assert.deepEqual(msg1.metadata.tangles[mootID].prev, [mootID], 'tangle prev') assert.deepEqual(msg1.metadata.tangles[mootID].prev, [mootID], 'tangle prev')
assert.equal(msg1.metadata.domain, 'post', 'metadata.domain') assert.equal(msg1.metadata.domain, 'post', 'metadata.domain')
assert.deepEqual(msg1.metadata.v, 3, 'metadata.v') assert.deepEqual(msg1.metadata.v, 4, 'metadata.v')
assert.equal( assert.equal(
msg1.pubkey, msg1.sigkey,
'4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW', '4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW',
'pubkey' 'sigkey'
) )
assert.equal( assert.equal(
msg1.sig, msg1.sig,
'rh8bc8QY7ju7yi4rt6y9njCyS3TVV1SBjn5dWGpKKRrC3XDMBc9KeNJgVCJLK8b8uiU5F49avAWt35P9kNaWZYH', '58LBLLJtqqRUteQRS5djhK2xxTG4VKjwibjKirqXU4LQKijD59NnrnHag5JsL54srJdhseSYaDhQoaWacbMd82v3',
'sig' 'sig'
) )
assert.equal(MsgV3.isFeedMsg(msg1), true, 'is a feed msg') assert.equal(MsgV4.isFeedMsg(msg1), true, 'is a feed msg')
const msgID1 = 'MUvfNDk3gMPRy9CpTDEuvW' const msgID1 = '4hFeNiBSrRaxW1PKxJd6QDju4B1kZGT8g2LBHwGSpz6M'
assert.equal(MsgV3.getMsgID(msg1), msgID1, 'getMsgID') assert.equal(MsgV4.getMsgID(msg1), msgID1, 'getMsgID')
const tangle2 = new MsgV3.Tangle(mootID) const tangle2 = new MsgV4.Tangle(mootID)
tangle2.add(mootID, moot) tangle2.add(mootID, moot)
tangle2.add(msgID1, msg1) tangle2.add(msgID1, msg1)
const data2 = { text: 'Ola mundo!' } const data2 = { text: 'Ola mundo!' }
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
keypair, keypair,
data: data2, data: data2,
account, account,
@ -161,7 +173,7 @@ test('MsgV3.create()', (t) => {
) )
assert.deepEqual( assert.deepEqual(
msg2.metadata.dataHash, msg2.metadata.dataHash,
'XuZEzH1Dhy1yuRMcviBBcN', 'D8AD5odaS2YizdvmqZacQ1XVNmRxgw9hXoEvSuPYpa8G',
'metadata.dataHash' 'metadata.dataHash'
) )
assert.deepEqual(msg2.metadata.dataSize, 21, 'metadata.dataSize') assert.deepEqual(msg2.metadata.dataSize, 21, 'metadata.dataSize')
@ -175,27 +187,31 @@ test('MsgV3.create()', (t) => {
assert.equal(msg2.metadata.tangles[mootID].depth, 2, 'tangle depth') assert.equal(msg2.metadata.tangles[mootID].depth, 2, 'tangle depth')
assert.deepEqual(msg2.metadata.tangles[mootID].prev, [msgID1], 'tangle prev') assert.deepEqual(msg2.metadata.tangles[mootID].prev, [msgID1], 'tangle prev')
assert.equal(msg2.metadata.domain, 'post', 'metadata.domain') assert.equal(msg2.metadata.domain, 'post', 'metadata.domain')
assert.deepEqual(msg2.metadata.v, 3, 'metadata.v') assert.deepEqual(msg2.metadata.v, 4, 'metadata.v')
assert.equal( assert.equal(
msg2.pubkey, msg2.sigkey,
'4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW', '4mjQ5aJu378cEu6TksRG3uXAiKFiwGjYQtWAjfVjDAJW',
'pubkey' 'sigkey'
) )
assert.equal( assert.equal(
msg2.sig, msg2.sig,
'3NscyRLJZP8mtq4DhhNPfwtw8yzoWsFytxGxD2QAqjW64RMeRLP5czN5mMYm4nCqRtXvzRgRhqgN1qtz9hWW14S4', '5KEQBLYg5iYhd3R8rSTtH4uPwVAQvwuXhNE9wmNEFiJtNCkHkNdrZ8X85bRsdekqgewvmPtue27QcqgcT2m4gjmS',
'sig' 'sig'
) )
assert.deepEqual(MsgV3.getMsgID(msg2), 'XMeQ6sbW3mjLYRLR4dAmKD', 'getMsgID') assert.deepEqual(
MsgV4.getMsgID(msg2),
'CrMez268VffqRiHvSZe6DtGVSfBhXWqfEh7D2ftPEbQ3',
'getMsgID'
)
}) })
test('MsgV3.create() handles DAG tips correctly', (t) => { test('MsgV4.create() handles DAG tips correctly', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
keypair, keypair,
data: { text: '1' }, data: { text: '1' },
account, account,
@ -205,16 +221,16 @@ test('MsgV3.create() handles DAG tips correctly', (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
assert.deepEqual( assert.deepEqual(
msg1.metadata.tangles[mootID].prev, msg1.metadata.tangles[mootID].prev,
[MsgV3.getMootID(account, 'post')], [MsgV4.getMootID(account, 'post')],
'msg1.prev is root' 'msg1.prev is root'
) )
tangle.add(msgID1, msg1) tangle.add(msgID1, msg1)
const msg2A = MsgV3.create({ const msg2A = MsgV4.create({
keypair, keypair,
data: { text: '2A' }, data: { text: '2A' },
account, account,
@ -230,7 +246,7 @@ test('MsgV3.create() handles DAG tips correctly', (t) => {
'msg2A.prev is msg1' 'msg2A.prev is msg1'
) )
const msg2B = MsgV3.create({ const msg2B = MsgV4.create({
keypair, keypair,
data: { text: '2B' }, data: { text: '2B' },
account, account,
@ -240,7 +256,7 @@ test('MsgV3.create() handles DAG tips correctly', (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID2B = MsgV3.getMsgID(msg2B) const msgID2B = MsgV4.getMsgID(msg2B)
assert.deepEqual( assert.deepEqual(
msg2B.metadata.tangles[mootID].prev, msg2B.metadata.tangles[mootID].prev,
[msgID1], [msgID1],
@ -249,7 +265,7 @@ test('MsgV3.create() handles DAG tips correctly', (t) => {
tangle.add(msgID2B, msg2B) tangle.add(msgID2B, msg2B)
const msg3 = MsgV3.create({ const msg3 = MsgV4.create({
keypair, keypair,
data: { text: '3' }, data: { text: '3' },
account, account,
@ -259,7 +275,7 @@ test('MsgV3.create() handles DAG tips correctly', (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID3 = MsgV3.getMsgID(msg3) const msgID3 = MsgV4.getMsgID(msg3)
assert.deepEqual( assert.deepEqual(
msg3.metadata.tangles[mootID].prev, msg3.metadata.tangles[mootID].prev,
[mootID, msgID2B].sort(), [mootID, msgID2B].sort(),
@ -267,11 +283,11 @@ test('MsgV3.create() handles DAG tips correctly', (t) => {
) )
tangle.add(msgID3, msg3) tangle.add(msgID3, msg3)
const msgID2A = MsgV3.getMsgID(msg2A) const msgID2A = MsgV4.getMsgID(msg2A)
tangle.add(msgID2A, msg2A) tangle.add(msgID2A, msg2A)
// t.pass('msg2A comes into awareness') // t.pass('msg2A comes into awareness')
const msg4 = MsgV3.create({ const msg4 = MsgV4.create({
keypair, keypair,
data: { text: '4' }, data: { text: '4' },
account, account,

View File

@ -1,15 +1,15 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV4 = require('../../lib/msg-v4')
test('MsgV3 domain validation', async (t) => { test('MsgV4 domain validation', async (t) => {
await t.test('Not a string', (t) => { await t.test('Not a string', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 123, domain: 123,
@ -25,7 +25,7 @@ test('MsgV3 domain validation', async (t) => {
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'group/init', domain: 'group/init',
@ -41,7 +41,7 @@ test('MsgV3 domain validation', async (t) => {
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'star*', domain: 'star*',
@ -57,7 +57,7 @@ test('MsgV3 domain validation', async (t) => {
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'xy', domain: 'xy',
@ -73,7 +73,7 @@ test('MsgV3 domain validation', async (t) => {
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'a'.repeat(120), domain: 'a'.repeat(120),

View File

@ -2,25 +2,25 @@ const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const base58 = require('bs58') const base58 = require('bs58')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV4 = require('../../lib/msg-v4')
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const account = MsgV3.getMsgID( const account = MsgV4.getMsgID(
MsgV3.createAccount(keypair, 'person', 'MYNONCE') MsgV4.createAccount(keypair, 'person', 'MYNONCE')
) )
const pubkeys = new Set([keypair.public]) const sigkeys = new Set([keypair.public])
test('MsgV3 tangles prev validation', async (t) => { test('MsgV4 tangles prev validation', async (t) => {
await t.test('Non-array is a bad prev', (t) => { await t.test('Non-array is a bad prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg = MsgV3.create({ const msg = MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -31,9 +31,9 @@ test('MsgV3 tangles prev validation', async (t) => {
}, },
}) })
msg.metadata.tangles[mootID].prev = null msg.metadata.tangles[mootID].prev = null
const msgID = MsgV3.getMsgID(msg) const msgID = MsgV4.getMsgID(msg)
const err = MsgV3.validate(msg, tangle, pubkeys, msgID, mootID) const err = MsgV4.validate(msg, tangle, sigkeys, msgID, mootID)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match( assert.match(
err, err,
@ -45,13 +45,13 @@ test('MsgV3 tangles prev validation', async (t) => {
await t.test('Number not allowed in prev', (t) => { await t.test('Number not allowed in prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -61,10 +61,10 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
tangle.add(msgID1, msg1) tangle.add(msgID1, msg1)
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -76,9 +76,9 @@ test('MsgV3 tangles prev validation', async (t) => {
}) })
msg2.metadata.tangles[mootID].depth = 1 msg2.metadata.tangles[mootID].depth = 1
msg2.metadata.tangles[mootID].prev = [1234] msg2.metadata.tangles[mootID].prev = [1234]
const msgID2 = MsgV3.getMsgID(msg2) const msgID2 = MsgV4.getMsgID(msg2)
const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID) const err = MsgV4.validate(msg2, tangle, sigkeys, msgID2, mootID)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match( assert.match(
err, err,
@ -90,13 +90,13 @@ test('MsgV3 tangles prev validation', async (t) => {
await t.test('URI not allowed in prev', (t) => { await t.test('URI not allowed in prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -106,10 +106,10 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
tangle.add(msgID1, msg1) tangle.add(msgID1, msg1)
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -119,13 +119,13 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID2 = MsgV3.getMsgID(msg2) const msgID2 = MsgV4.getMsgID(msg2)
const randBuf = Buffer.alloc(16).fill(16) const randBuf = Buffer.alloc(16).fill(16)
const fakeMsgKey1 = `ppppp:message/v3/${base58.encode(randBuf)}` const fakeMsgKey1 = `ppppp:message/v4/${base58.encode(randBuf)}`
msg2.metadata.tangles[mootID].depth = 1 msg2.metadata.tangles[mootID].depth = 1
msg2.metadata.tangles[mootID].prev = [fakeMsgKey1] msg2.metadata.tangles[mootID].prev = [fakeMsgKey1]
const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID) const err = MsgV4.validate(msg2, tangle, sigkeys, msgID2, mootID)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match(err, /prev item ".*" is a URI/, 'invalid 2nd msg description') assert.match(err, /prev item ".*" is a URI/, 'invalid 2nd msg description')
}) })
@ -133,13 +133,13 @@ test('MsgV3 tangles prev validation', async (t) => {
await t.test('Locally unknown prev msgID', (t) => { await t.test('Locally unknown prev msgID', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -149,10 +149,10 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
tangle.add(msgID1, msg1) tangle.add(msgID1, msg1)
const unknownMsg = MsgV3.create({ const unknownMsg = MsgV4.create({
keypair, keypair,
data: { text: 'Alien' }, data: { text: 'Alien' },
account, account,
@ -162,14 +162,14 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const unknownMsgID = MsgV3.getMsgID(unknownMsg) const unknownMsgID = MsgV4.getMsgID(unknownMsg)
const fakeMootID = 'ABCDEabcde' + mootID.substring(10) const fakeMootID = 'ABCDEabcde' + mootID.substring(10)
const tangle2 = new MsgV3.Tangle(fakeMootID) const tangle2 = new MsgV4.Tangle(fakeMootID)
tangle2.add(fakeMootID, moot) tangle2.add(fakeMootID, moot)
tangle2.add(unknownMsgID, unknownMsg) tangle2.add(unknownMsgID, unknownMsg)
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -179,9 +179,9 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle2, [mootID]: tangle2,
}, },
}) })
const msgID2 = MsgV3.getMsgID(msg2) const msgID2 = MsgV4.getMsgID(msg2)
const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID) const err = MsgV4.validate(msg2, tangle, sigkeys, msgID2, mootID)
assert.ok(err, 'invalid 2nd msg throws') assert.ok(err, 'invalid 2nd msg throws')
assert.match( assert.match(
err, err,
@ -190,20 +190,20 @@ test('MsgV3 tangles prev validation', async (t) => {
) )
}) })
await t.test('Feed msg with the wrong pubkey', (t) => { await t.test('Feed msg with the wrong sigkey', (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 accountB = MsgV3.getMsgID( const accountB = MsgV4.getMsgID(
MsgV3.createAccount(keypairB, 'person', 'MYNONCE') MsgV4.createAccount(keypairB, 'person', 'MYNONCE')
) )
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const feedTangle = new MsgV3.Tangle(mootID) const feedTangle = new MsgV4.Tangle(mootID)
feedTangle.add(mootID, moot) feedTangle.add(mootID, moot)
const msg = MsgV3.create({ const msg = MsgV4.create({
keypair: keypairB, keypair: keypairB,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account: accountB, account: accountB,
@ -213,13 +213,13 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: feedTangle, [mootID]: feedTangle,
}, },
}) })
const msgID = MsgV3.getMsgID(msg) const msgID = MsgV4.getMsgID(msg)
const err = MsgV3.validate(msg, feedTangle, pubkeys, msgID, mootID) const err = MsgV4.validate(msg, feedTangle, sigkeys, msgID, mootID)
assert.ok(err, 'invalid msg throws') assert.ok(err, 'invalid msg throws')
assert.match( assert.match(
err, err,
/pubkey ".*" should have been one of ".*" from the account ".*"/, /sigkey ".*" should have been one of ".*" from the account ".*"/,
'invalid msg' 'invalid msg'
) )
}) })
@ -227,12 +227,12 @@ test('MsgV3 tangles prev validation', async (t) => {
await t.test('Feed msg with the wrong domain', (t) => { await t.test('Feed msg with the wrong domain', (t) => {
const keypairA = Keypair.generate('ed25519', 'alice') const keypairA = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const feedTangle = new MsgV3.Tangle(mootID) const feedTangle = new MsgV4.Tangle(mootID)
feedTangle.add(mootID, moot) feedTangle.add(mootID, moot)
const msg = MsgV3.create({ const msg = MsgV4.create({
keypair: keypairA, keypair: keypairA,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
@ -242,9 +242,9 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: feedTangle, [mootID]: feedTangle,
}, },
}) })
const msgID = MsgV3.getMsgID(msg) const msgID = MsgV4.getMsgID(msg)
const err = MsgV3.validate(msg, feedTangle, pubkeys, msgID, mootID) const err = MsgV4.validate(msg, feedTangle, sigkeys, msgID, mootID)
assert.ok(err, 'invalid msg throws') assert.ok(err, 'invalid msg throws')
assert.match( assert.match(
err, err,
@ -256,13 +256,13 @@ test('MsgV3 tangles prev validation', async (t) => {
await t.test('Feed msg with non-alphabetically sorted prev', (t) => { await t.test('Feed msg with non-alphabetically sorted prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
keypair, keypair,
data: { text: '1' }, data: { text: '1' },
account, account,
@ -272,9 +272,9 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
keypair, keypair,
data: { text: '2' }, data: { text: '2' },
account, account,
@ -284,12 +284,12 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID2 = MsgV3.getMsgID(msg2) const msgID2 = MsgV4.getMsgID(msg2)
tangle.add(msgID1, msg1) tangle.add(msgID1, msg1)
tangle.add(msgID2, msg2) tangle.add(msgID2, msg2)
const msg3 = MsgV3.create({ const msg3 = MsgV4.create({
keypair, keypair,
data: { text: '3' }, data: { text: '3' },
account, account,
@ -299,7 +299,7 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID3 = MsgV3.getMsgID(msg3) const msgID3 = MsgV4.getMsgID(msg3)
let prevMsgIDs = msg3.metadata.tangles[mootID].prev let prevMsgIDs = msg3.metadata.tangles[mootID].prev
if (prevMsgIDs[0] < prevMsgIDs[1]) { if (prevMsgIDs[0] < prevMsgIDs[1]) {
@ -309,7 +309,7 @@ test('MsgV3 tangles prev validation', async (t) => {
} }
msg3.metadata.tangles[mootID].prev = prevMsgIDs msg3.metadata.tangles[mootID].prev = prevMsgIDs
const err = MsgV3.validate(msg3, tangle, pubkeys, msgID3, mootID) const err = MsgV4.validate(msg3, tangle, sigkeys, msgID3, mootID)
assert.ok(err, 'invalid 3rd msg throws') assert.ok(err, 'invalid 3rd msg throws')
assert.match( assert.match(
err, err,
@ -321,13 +321,13 @@ test('MsgV3 tangles prev validation', async (t) => {
await t.test('Feed msg with duplicate prev', (t) => { await t.test('Feed msg with duplicate prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
keypair, keypair,
data: { text: '1' }, data: { text: '1' },
account, account,
@ -337,12 +337,12 @@ test('MsgV3 tangles prev validation', async (t) => {
[mootID]: tangle, [mootID]: tangle,
}, },
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
const [prevID] = msg1.metadata.tangles[mootID].prev const [prevID] = msg1.metadata.tangles[mootID].prev
msg1.metadata.tangles[mootID].prev = [prevID, prevID] msg1.metadata.tangles[mootID].prev = [prevID, prevID]
const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID) const err = MsgV4.validate(msg1, tangle, sigkeys, msgID1, mootID)
assert.ok(err, 'invalid 1st msg throws') assert.ok(err, 'invalid 1st msg throws')
assert.match(err, /prev ".*" contains duplicates/, 'invalid error message') assert.match(err, /prev ".*" contains duplicates/, 'invalid error message')
}) })

View File

@ -1,21 +1,21 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV4 = require('../../lib/msg-v4')
test('MsgV3 lipmaa prevs', (t) => { test('MsgV4 lipmaa prevs', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const account = MsgV3.getMsgID( const account = MsgV4.getMsgID(
MsgV3.createAccount(keypair, 'person', 'MYNONCE') MsgV4.createAccount(keypair, 'person', 'MYNONCE')
) )
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -25,12 +25,12 @@ test('MsgV3 lipmaa prevs', (t) => {
}, },
keypair, keypair,
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
tangle.add(msgID1, msg1) tangle.add(msgID1, msg1)
assert.equal(msg1.metadata.tangles[mootID].depth, 1, 'msg1 depth') assert.equal(msg1.metadata.tangles[mootID].depth, 1, 'msg1 depth')
assert.deepEqual(msg1.metadata.tangles[mootID].prev, [mootID], 'msg1 prev') assert.deepEqual(msg1.metadata.tangles[mootID].prev, [mootID], 'msg1 prev')
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -40,12 +40,12 @@ test('MsgV3 lipmaa prevs', (t) => {
}, },
keypair, keypair,
}) })
const msgID2 = MsgV3.getMsgID(msg2) const msgID2 = MsgV4.getMsgID(msg2)
tangle.add(msgID2, msg2) tangle.add(msgID2, msg2)
assert.equal(msg2.metadata.tangles[mootID].depth, 2, 'msg2 depth') assert.equal(msg2.metadata.tangles[mootID].depth, 2, 'msg2 depth')
assert.deepEqual(msg2.metadata.tangles[mootID].prev, [msgID1], 'msg2 prev') assert.deepEqual(msg2.metadata.tangles[mootID].prev, [msgID1], 'msg2 prev')
const msg3 = MsgV3.create({ const msg3 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -55,7 +55,7 @@ test('MsgV3 lipmaa prevs', (t) => {
}, },
keypair, keypair,
}) })
const msgID3 = MsgV3.getMsgID(msg3) const msgID3 = MsgV4.getMsgID(msg3)
tangle.add(msgID3, msg3) tangle.add(msgID3, msg3)
assert.equal(msg3.metadata.tangles[mootID].depth, 3, 'msg3 depth') assert.equal(msg3.metadata.tangles[mootID].depth, 3, 'msg3 depth')
assert.deepEqual( assert.deepEqual(
@ -64,7 +64,7 @@ test('MsgV3 lipmaa prevs', (t) => {
'msg3 prev (has lipmaa!)' 'msg3 prev (has lipmaa!)'
) )
const msg4 = MsgV3.create({ const msg4 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -74,12 +74,12 @@ test('MsgV3 lipmaa prevs', (t) => {
}, },
data, data,
}) })
const msgID4 = MsgV3.getMsgID(msg4) const msgID4 = MsgV4.getMsgID(msg4)
tangle.add(msgID4, msg4) tangle.add(msgID4, msg4)
assert.equal(msg4.metadata.tangles[mootID].depth, 4, 'msg4 depth') assert.equal(msg4.metadata.tangles[mootID].depth, 4, 'msg4 depth')
assert.deepEqual(msg4.metadata.tangles[mootID].prev, [msgID3], 'msg4 prev') assert.deepEqual(msg4.metadata.tangles[mootID].prev, [msgID3], 'msg4 prev')
const msg5 = MsgV3.create({ const msg5 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -89,12 +89,12 @@ test('MsgV3 lipmaa prevs', (t) => {
}, },
keypair, keypair,
}) })
const msgID5 = MsgV3.getMsgID(msg5) const msgID5 = MsgV4.getMsgID(msg5)
tangle.add(msgID5, msg5) tangle.add(msgID5, msg5)
assert.equal(msg5.metadata.tangles[mootID].depth, 5, 'msg5 depth') assert.equal(msg5.metadata.tangles[mootID].depth, 5, 'msg5 depth')
assert.deepEqual(msg5.metadata.tangles[mootID].prev, [msgID4], 'msg5 prev') assert.deepEqual(msg5.metadata.tangles[mootID].prev, [msgID4], 'msg5 prev')
const msg6 = MsgV3.create({ const msg6 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -104,12 +104,12 @@ test('MsgV3 lipmaa prevs', (t) => {
}, },
keypair, keypair,
}) })
const msgID6 = MsgV3.getMsgID(msg6) const msgID6 = MsgV4.getMsgID(msg6)
tangle.add(msgID6, msg6) tangle.add(msgID6, msg6)
assert.equal(msg6.metadata.tangles[mootID].depth, 6, 'msg6 depth') assert.equal(msg6.metadata.tangles[mootID].depth, 6, 'msg6 depth')
assert.deepEqual(msg6.metadata.tangles[mootID].prev, [msgID5], 'msg6 prev') assert.deepEqual(msg6.metadata.tangles[mootID].prev, [msgID5], 'msg6 prev')
const msg7 = MsgV3.create({ const msg7 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -119,7 +119,7 @@ test('MsgV3 lipmaa prevs', (t) => {
}, },
keypair, keypair,
}) })
const msgID7 = MsgV3.getMsgID(msg7) const msgID7 = MsgV4.getMsgID(msg7)
tangle.add(msgID7, msg7) tangle.add(msgID7, msg7)
assert.equal(msg7.metadata.tangles[mootID].depth, 7, 'msg7 depth') assert.equal(msg7.metadata.tangles[mootID].depth, 7, 'msg7 depth')
assert.deepEqual( assert.deepEqual(

View File

@ -1,32 +1,32 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV4 = require('../../lib/msg-v4')
test('MsgV3.Tangle simple multi-author tangle', (t) => { test('MsgV4.Tangle 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 accountA = MsgV3.getMsgID( const accountA = MsgV4.getMsgID(
MsgV3.createAccount(keypairA, 'person', 'alice') MsgV4.createAccount(keypairA, 'person', 'alice')
) )
const accountB = MsgV3.getMsgID( const accountB = MsgV4.getMsgID(
MsgV3.createAccount(keypairB, 'person', 'bob') MsgV4.createAccount(keypairB, 'person', 'bob')
) )
const mootA = MsgV3.createMoot(accountA, 'post', keypairA) const mootA = MsgV4.createMoot(accountA, 'post', keypairA)
const mootAID = MsgV3.getMsgID(mootA) const mootAID = MsgV4.getMsgID(mootA)
const tangleA = new MsgV3.Tangle(mootAID) const tangleA = new MsgV4.Tangle(mootAID)
tangleA.add(mootAID, mootA) tangleA.add(mootAID, mootA)
assert.equal(tangleA.id, mootAID, 'tangle.id') assert.equal(tangleA.id, mootAID, 'tangle.id')
assert.equal(tangleA.root, mootA, 'tangle.root') assert.equal(tangleA.root, mootA, 'tangle.root')
const mootB = MsgV3.createMoot(accountB, 'post', keypairB) const mootB = MsgV4.createMoot(accountB, 'post', keypairB)
const mootBID = MsgV3.getMsgID(mootB) const mootBID = MsgV4.getMsgID(mootB)
const tangleB = new MsgV3.Tangle(mootBID) const tangleB = new MsgV4.Tangle(mootBID)
tangleB.add(mootBID, mootB) tangleB.add(mootBID, mootB)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
account: accountA, account: accountA,
accountTips: [accountA], accountTips: [accountA],
domain: 'post', domain: 'post',
@ -36,17 +36,17 @@ test('MsgV3.Tangle simple multi-author tangle', (t) => {
}, },
keypair: keypairA, keypair: keypairA,
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
assert.deepEqual( assert.deepEqual(
Object.keys(msg1.metadata.tangles), Object.keys(msg1.metadata.tangles),
[mootAID], [mootAID],
'msg1 has only feed tangle' 'msg1 has only feed tangle'
) )
const tangleX = new MsgV3.Tangle(msgID1) const tangleX = new MsgV4.Tangle(msgID1)
tangleX.add(msgID1, msg1) tangleX.add(msgID1, msg1)
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
account: accountB, account: accountB,
accountTips: [accountB], accountTips: [accountB],
domain: 'post', domain: 'post',
@ -86,29 +86,29 @@ test('MsgV3.Tangle simple multi-author tangle', (t) => {
) )
}) })
test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => { test('MsgV4.Tangle 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 accountA = MsgV3.getMsgID( const accountA = MsgV4.getMsgID(
MsgV3.createAccount(keypairA, 'person', 'alice') MsgV4.createAccount(keypairA, 'person', 'alice')
) )
const accountB = MsgV3.getMsgID( const accountB = MsgV4.getMsgID(
MsgV3.createAccount(keypairB, 'person', 'bob') MsgV4.createAccount(keypairB, 'person', 'bob')
) )
const data = { text: 'Hello world!' } const data = { text: 'Hello world!' }
const mootA = MsgV3.createMoot(accountA, 'post', keypairA) const mootA = MsgV4.createMoot(accountA, 'post', keypairA)
const mootAID = MsgV3.getMsgID(mootA) const mootAID = MsgV4.getMsgID(mootA)
const tangleA = new MsgV3.Tangle(mootAID) const tangleA = new MsgV4.Tangle(mootAID)
tangleA.add(mootAID, mootA) tangleA.add(mootAID, mootA)
const mootB = MsgV3.createMoot(accountB, 'post', keypairB) const mootB = MsgV4.createMoot(accountB, 'post', keypairB)
const mootBID = MsgV3.getMsgID(mootB) const mootBID = MsgV4.getMsgID(mootB)
const tangleB = new MsgV3.Tangle(mootBID) const tangleB = new MsgV4.Tangle(mootBID)
tangleB.add(mootBID, mootB) tangleB.add(mootBID, mootB)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
account: accountA, account: accountA,
accountTips: [accountA], accountTips: [accountA],
domain: 'post', domain: 'post',
@ -118,9 +118,9 @@ test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => {
}, },
keypair: keypairA, keypair: keypairA,
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
tangleA.add(msgID1, msg1) tangleA.add(msgID1, msg1)
const tangleThread = new MsgV3.Tangle(msgID1) const tangleThread = new MsgV4.Tangle(msgID1)
tangleThread.add(msgID1, msg1) tangleThread.add(msgID1, msg1)
assert.deepEqual( assert.deepEqual(
@ -129,7 +129,7 @@ test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => {
'A:msg1 has only feed tangle' 'A:msg1 has only feed tangle'
) )
const msg2 = MsgV3.create({ const msg2 = MsgV4.create({
account: accountB, account: accountB,
accountTips: [accountB], accountTips: [accountB],
domain: 'post', domain: 'post',
@ -140,7 +140,7 @@ test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => {
}, },
keypair: keypairB, keypair: keypairB,
}) })
const msgID2 = MsgV3.getMsgID(msg2) const msgID2 = MsgV4.getMsgID(msg2)
tangleB.add(msgID2, msg2) tangleB.add(msgID2, msg2)
tangleThread.add(msgID2, msg2) tangleThread.add(msgID2, msg2)
@ -150,7 +150,7 @@ test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => {
'B:msg2 points to A:msg1' 'B:msg2 points to A:msg1'
) )
const msg3 = MsgV3.create({ const msg3 = MsgV4.create({
account: accountB, account: accountB,
accountTips: [accountB], accountTips: [accountB],
domain: 'post', domain: 'post',
@ -161,7 +161,7 @@ test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => {
}, },
keypair: keypairB, keypair: keypairB,
}) })
const msgID3 = MsgV3.getMsgID(msg3) const msgID3 = MsgV4.getMsgID(msg3)
tangleB.add(msgID3, msg3) tangleB.add(msgID3, msg3)
tangleThread.add(msgID3, msg3) tangleThread.add(msgID3, msg3)
@ -171,7 +171,7 @@ test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => {
'B:msg3 points to B:msg2' 'B:msg3 points to B:msg2'
) )
const msg4 = MsgV3.create({ const msg4 = MsgV4.create({
account: accountA, account: accountA,
accountTips: [accountA], accountTips: [accountA],
domain: 'post', domain: 'post',
@ -182,7 +182,7 @@ test('MsgV3.Tangle lipmaa in multi-author tangle', (t) => {
}, },
keypair: keypairA, keypair: keypairA,
}) })
const msgID4 = MsgV3.getMsgID(msg4) const msgID4 = MsgV4.getMsgID(msg4)
tangleB.add(msgID4, msg4) tangleB.add(msgID4, msg4)
tangleThread.add(msgID4, msg4) tangleThread.add(msgID4, msg4)

View File

@ -1,41 +1,41 @@
const test = require('node:test') const test = require('node:test')
const assert = require('node:assert') const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV4 = require('../../lib/msg-v4')
test('MsgV3 validation', async (t) => { test('MsgV4 validation', async (t) => {
await t.test('Correct root msg', (t) => { await t.test('Correct root msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const account = MsgV3.getMsgID( const account = MsgV4.getMsgID(
MsgV3.createAccount(keypair, 'person', 'alice') MsgV4.createAccount(keypair, 'person', 'alice')
) )
const pubkeys = new Set([keypair.public]) const sigkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const err = MsgV3.validate(moot, tangle, pubkeys, mootID, mootID) const err = MsgV4.validate(moot, tangle, sigkeys, mootID, mootID)
assert.ifError(err, 'valid root msg') assert.ifError(err, 'valid root msg')
}) })
await t.test('Correct account tangle', (t) => { await t.test('Correct account tangle', (t) => {
const pubkeys = new Set() const sigkeys = new Set()
const keypair1 = Keypair.generate('ed25519', 'alice') const keypair1 = Keypair.generate('ed25519', 'alice')
pubkeys.add(keypair1.public) sigkeys.add(keypair1.public)
const accountMsg0 = MsgV3.createAccount(keypair1, 'person', 'alice') const accountMsg0 = MsgV4.createAccount(keypair1, 'person', 'alice')
const account = MsgV3.getMsgID(accountMsg0) const account = MsgV4.getMsgID(accountMsg0)
const accountMsg0ID = account const accountMsg0ID = account
const tangle = new MsgV3.Tangle(account) const tangle = new MsgV4.Tangle(account)
tangle.add(accountMsg0ID, accountMsg0) tangle.add(accountMsg0ID, accountMsg0)
let err = MsgV3.validate( let err = MsgV4.validate(
accountMsg0, accountMsg0,
tangle, tangle,
pubkeys, sigkeys,
accountMsg0ID, accountMsg0ID,
account account
) )
@ -45,7 +45,7 @@ test('MsgV3 validation', async (t) => {
const keypair2 = Keypair.generate('ed25519', 'bob') const keypair2 = Keypair.generate('ed25519', 'bob')
const accountMsg1 = MsgV3.create({ const accountMsg1 = MsgV4.create({
account: 'self', account: 'self',
accountTips: null, accountTips: null,
domain: 'account', domain: 'account',
@ -55,25 +55,25 @@ test('MsgV3 validation', async (t) => {
}, },
keypair: keypair1, // announcing keypair2 but signing with keypair1 keypair: keypair1, // announcing keypair2 but signing with keypair1
}) })
const accountMsg1ID = MsgV3.getMsgID(accountMsg1) const accountMsg1ID = MsgV4.getMsgID(accountMsg1)
err = MsgV3.validate(accountMsg1, tangle, pubkeys, accountMsg1ID, account) err = MsgV4.validate(accountMsg1, tangle, sigkeys, accountMsg1ID, account)
assert.ifError(err, 'valid account msg') assert.ifError(err, 'valid account msg')
}) })
await t.test('2nd msg correct with existing root', (t) => { await t.test('2nd msg correct with existing root', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const account = MsgV3.getMsgID( const account = MsgV4.getMsgID(
MsgV3.createAccount(keypair, 'person', 'alice') MsgV4.createAccount(keypair, 'person', 'alice')
) )
const pubkeys = new Set([keypair.public]) const sigkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -83,26 +83,26 @@ test('MsgV3 validation', async (t) => {
}, },
keypair, keypair,
}) })
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
tangle.add(msgID1, msg1) tangle.add(msgID1, msg1)
const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID) const err = MsgV4.validate(msg1, tangle, sigkeys, msgID1, mootID)
assert.ifError(err, 'valid 2nd msg') assert.ifError(err, 'valid 2nd msg')
}) })
await t.test('2nd forked msg correct', (t) => { await t.test('2nd forked msg correct', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const account = MsgV3.getMsgID( const account = MsgV4.getMsgID(
MsgV3.createAccount(keypair, 'person', 'alice') MsgV4.createAccount(keypair, 'person', 'alice')
) )
const pubkeys = new Set([keypair.public]) const sigkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1A = MsgV3.create({ const msg1A = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -112,9 +112,9 @@ test('MsgV3 validation', async (t) => {
}, },
keypair, keypair,
}) })
const msgID1A = MsgV3.getMsgID(msg1A) const msgID1A = MsgV4.getMsgID(msg1A)
const msg1B = MsgV3.create({ const msg1B = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -124,27 +124,27 @@ test('MsgV3 validation', async (t) => {
}, },
keypair, keypair,
}) })
const msgID1B = MsgV3.getMsgID(msg1B) const msgID1B = MsgV4.getMsgID(msg1B)
tangle.add(msgID1A, msg1A) tangle.add(msgID1A, msg1A)
tangle.add(msgID1B, msg1B) tangle.add(msgID1B, msg1B)
const err = MsgV3.validate(msg1B, tangle, pubkeys, msgID1B, mootID) const err = MsgV4.validate(msg1B, tangle, sigkeys, msgID1B, mootID)
assert.ifError(err, 'valid 2nd forked msg') assert.ifError(err, 'valid 2nd forked msg')
}) })
await t.test('Correct erased msg', (t) => { await t.test('Correct erased msg', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const account = MsgV3.getMsgID( const account = MsgV4.getMsgID(
MsgV3.createAccount(keypair, 'person', 'alice') MsgV4.createAccount(keypair, 'person', 'alice')
) )
const pubkeys = new Set([keypair.public]) const sigkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV4.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV4.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg1 = MsgV3.create({ const msg1 = MsgV4.create({
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
@ -155,9 +155,9 @@ test('MsgV3 validation', async (t) => {
keypair, keypair,
}) })
msg1.data = null msg1.data = null
const msgID1 = MsgV3.getMsgID(msg1) const msgID1 = MsgV4.getMsgID(msg1)
const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID) const err = MsgV4.validate(msg1, tangle, sigkeys, msgID1, mootID)
assert.ifError(err, 'valid erased msg') assert.ifError(err, 'valid erased msg')
}) })
}) })