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

View File

@ -6,13 +6,13 @@ const base58 = require('bs58')
const Obz = require('obz')
const Keypair = require('ppppp-keypair')
const Log = require('./log')
const MsgV3 = require('./msg-v3')
const MsgV4 = require('./msg-v4')
const {
SIGNATURE_TAG_ACCOUNT_ADD,
ACCOUNT_SELF,
ACCOUNT_ANY,
ACCOUNT_DOMAIN_PREFIX,
} = require('./msg-v3/constants')
} = require('./msg-v4/constants')
const ReadyGate = require('./utils/ready-gate')
const Ghosts = require('./ghosts')
const { decrypt } = require('./encryption')
@ -22,11 +22,11 @@ const { decrypt } = require('./encryption')
* @typedef {import('ppppp-keypair').KeypairPublicSlice} KeypairPublicSlice
* @typedef {import('ppppp-keypair').KeypairPrivateSlice} KeypairPrivateSlice
* @typedef {string} MsgID
* @typedef {import('./msg-v3').Msg} Msg
* @typedef {import('./msg-v3').AccountData} AccountData
* @typedef {import('./msg-v3').AccountPower} AccountPower
* @typedef {import('./msg-v4').Msg} Msg
* @typedef {import('./msg-v4').AccountData} AccountData
* @typedef {import('./msg-v4').AccountPower} AccountPower
* @typedef {import('./msg-v4/tangle')} Tangle
* @typedef {import('./encryption').EncryptionFormat} EncryptionFormat
* @typedef {import('./msg-v3/tangle')} Tangle
* @typedef {Buffer | Uint8Array} B4A
* @typedef {{
* db: {
@ -85,7 +85,7 @@ function assertValidConfig(config) {
}
}
class DBTangle extends MsgV3.Tangle {
class DBTangle extends MsgV4.Tangle {
/** @type {(msgID: MsgID) => Msg | undefined} */
#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
* @param {Tangle | null} accountTangle
* @returns {Set<string>}
*/
function getPubkeysInAccount(accountTangle) {
const pubkeys = new Set()
if (!accountTangle) return pubkeys
function getSigkeysInAccount(accountTangle) {
const sigkeys = new Set()
if (!accountTangle) return sigkeys
// TODO: prune the accountTangle beyond msg.metadata.accountTips
for (const msgID of accountTangle.topoSort()) {
const msg = get(msgID)
@ -373,9 +373,9 @@ function initDB(peer, config) {
const purpose = data.key?.purpose
if (purpose !== 'sig' && purpose !== 'shs-and-sig') 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)
}
// Identify the account and its pubkeys:
// Identify the account and its sigkeys:
/** @type {Tangle | null} */
let accountTangle
try {
@ -413,7 +413,7 @@ function initDB(peer, config) {
} catch (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
if (!!rec.msg.data && ghosts.read(tangleID).has(rec.id)) {
@ -421,7 +421,7 @@ function initDB(peer, config) {
}
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 })
}
@ -436,9 +436,9 @@ function initDB(peer, config) {
// Unwrap encrypted inner msg and verify it too
if (typeof rec.msg.data === 'string') {
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 innerMsgID = MsgV3.getMsgID(innerMsg)
const innerMsgID = MsgV4.getMsgID(innerMsg)
const innerRec = { id: innerMsgID, msg: innerMsg }
try {
verifyRec(innerRec, innerMsgID)
@ -456,7 +456,7 @@ function initDB(peer, config) {
* @returns {MsgID}
*/
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)
for (const id in rec.msg.metadata.tangles) {
if (tangleID) {
@ -478,7 +478,7 @@ function initDB(peer, config) {
* @param {CB<RecPresent>} 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
// 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}
*/
function validateAccountMsg(msg, accountTangle) {
if (!MsgV3.isRoot(msg)) {
if (!MsgV4.isRoot(msg)) {
/** @type {AccountData} */
const data = msg.data
if (data.action === 'add') {
// Does this msg.pubkey have the "add" power?
// Does this msg.sigkey have the "add" power?
const keypair = {
curve: /** @type {const} */ ('ed25519'),
public: msg.pubkey,
public: msg.sigkey,
}
const powers = getAccountPowers(accountTangle, keypair)
if (!powers.has('add')) {
// 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'
@ -542,8 +542,8 @@ function initDB(peer, config) {
const mootID = findMoot(account, domain)?.id
if (mootID) return cb(null, mootID)
const moot = MsgV3.createMoot(account, domain, keypair)
add(moot, MsgV3.getMsgID(moot), (err, rec) => {
const moot = MsgV4.createMoot(account, domain, keypair)
add(moot, MsgV4.getMsgID(moot), (err, rec) => {
// prettier-ignore
if (err) return cb(new Error('initializeFeed() failed to add root', { cause: err }));
cb(null, rec.id)
@ -607,7 +607,7 @@ function initDB(peer, config) {
}
}
// 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)
}
@ -660,11 +660,11 @@ function initDB(peer, config) {
let msg
try {
msg = MsgV3.createAccount(keypair, domain, opts?._nonce)
msg = MsgV4.createAccount(keypair, domain, opts?._nonce)
} catch (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) => {
// prettier-ignore
@ -857,11 +857,11 @@ function initDB(peer, config) {
// Create the actual message:
let msg
try {
msg = MsgV3.create(fullOpts)
msg = MsgV4.create(fullOpts)
} catch (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) => {
// prettier-ignore
@ -927,7 +927,7 @@ function initDB(peer, config) {
// If opts ask for encryption, encrypt and put ciphertext in opts.data
const recps = fullOpts.data.recps
if (Array.isArray(recps) && recps.length > 0) {
const plaintext = MsgV3.toPlaintextBuffer(fullOpts)
const plaintext = MsgV4.toPlaintextBuffer(fullOpts)
const encryptOpts = {
...fullOpts,
recps: recps.map(
@ -959,12 +959,12 @@ function initDB(peer, config) {
// Create the actual message:
let msg
try {
msg = MsgV3.create(fullOpts)
msg = MsgV4.create(fullOpts)
} catch (err) {
// prettier-ignore
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:
logAppend(msgID, msg, (err, rec) => {
@ -982,9 +982,9 @@ function initDB(peer, config) {
* @returns {RecPresent | null}
*/
function findMoot(id, findDomain) {
const findAccount = MsgV3.stripAccount(id)
const findAccount = MsgV4.stripAccount(id)
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
}
}
@ -1113,7 +1113,7 @@ function initDB(peer, config) {
if (!rec) return cb()
if (!rec.msg) 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 seq = misc?.seq ?? -1
const offset = misc?.offset ?? -1
@ -1163,7 +1163,7 @@ function initDB(peer, config) {
},
feed: {
publish: publishToFeed,
getID: MsgV3.getMootID,
getID: MsgV4.getMootID,
findMoot,
},
getRecord,

View File

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

View File

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

View File

@ -21,7 +21,7 @@ const Tangle = require('./tangle')
const {
ACCOUNT_SELF,
ACCOUNT_ANY,
SIGNATURE_TAG_MSG_V3,
SIGNATURE_TAG_MSG_V4,
} = require('./constants')
const { isEmptyObject } = require('./util')
@ -42,9 +42,9 @@ const { isEmptyObject } = require('./util')
* [tangleID in string]: TangleMetadata
* };
* domain: string;
* v: 3;
* v: 4;
* };
* pubkey: string;
* sigkey: string;
* sig: string;
* }} Msg
*/
@ -62,9 +62,9 @@ const { isEmptyObject } = require('./util')
* [tangleID in string]: TangleMetadata
* };
* domain: string;
* v: 3;
* v: 4;
* };
* pubkey: string;
* sigkey: string;
* sig: string;
* }} FeedMsg
*/
@ -142,9 +142,9 @@ function getMootID(id, domain) {
accountTips: null,
tangles: {},
domain,
v: 3,
v: 4,
},
pubkey: '',
sigkey: '',
sig: '',
}
@ -192,15 +192,15 @@ function create(opts) {
accountTips,
tangles,
domain: opts.domain,
v: 3,
v: 4,
},
pubkey: opts.keypair.public,
sigkey: opts.keypair.public,
sig: '',
}
if ((err = validateData(msg))) throw err
const signableBuf = b4a.from(
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata),
SIGNATURE_TAG_MSG_V4 + stringify(msg.metadata),
'utf8'
)
msg.sig = Keypair.sign(opts.keypair, signableBuf)
@ -228,14 +228,14 @@ function createMoot(id, domain, keypair) {
accountTips: null,
tangles: {},
domain,
v: 3,
v: 4,
},
pubkey: keypair.public,
sigkey: keypair.public,
sig: '',
}
const signableBuf = b4a.from(
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata),
SIGNATURE_TAG_MSG_V4 + stringify(msg.metadata),
'utf8'
)
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 base58 = require('bs58')
// @ts-ignore
@ -14,8 +14,8 @@ const stringify = require('json-canon')
*/
function representData(data) {
const dataBuf = b4a.from(stringify(data), 'utf8')
const fullHash = /** @type {B4A} */ (blake3.hash(dataBuf))
const dataHash = base58.encode(fullHash.subarray(0, 16))
const fullHash = crypto.createHash('sha512').update(dataBuf).digest()
const dataHash = base58.encode(fullHash.subarray(0, 32))
const dataSize = dataBuf.length
return [dataHash, dataSize]
}

View File

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

View File

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

View File

@ -62,7 +62,7 @@ test('account.add()', async (t) => {
{ [account]: { depth: 1, prev: [account] } },
'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)
@ -198,7 +198,7 @@ test('account.add()', async (t) => {
accountTips: null,
tangles: {},
domain: 'post',
v: 3,
v: 4,
},
'postsRoot'
)

View File

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

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack')
const caps = require('ppppp-caps')
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')
rimraf.sync(DIR)
@ -21,20 +21,20 @@ test('add()', async (t) => {
await peer.db.loaded()
const accountMsg0 = MsgV3.createAccount(keypair, 'person', 'aliceNonce')
const id = MsgV3.getMsgID(accountMsg0)
const accountMsg0 = MsgV4.createAccount(keypair, 'person', 'aliceNonce')
const id = MsgV4.getMsgID(accountMsg0)
await p(peer.db.add)(accountMsg0, id)
const rootMsg = MsgV3.createMoot(id, 'post', keypair)
const rootID = MsgV3.getMsgID(rootMsg)
const rootMsg = MsgV4.createMoot(id, 'post', keypair)
const rootID = MsgV4.getMsgID(rootMsg)
const recRoot = await p(peer.db.add)(rootMsg, rootID)
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)
const inputMsg = MsgV3.create({
const inputMsg = MsgV4.create({
keypair,
domain: '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!')
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)
})

View File

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

View File

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

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack')
const caps = require('ppppp-caps')
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')
rimraf.sync(DIR)
@ -22,8 +22,8 @@ test('feed.findMoot()', async (t) => {
await peer.db.loaded()
const id = await p(peer.db.account.create)({ subdomain: 'person' })
const moot = MsgV3.createMoot(id, 'post', keypair)
const mootID = MsgV3.getMsgID(moot)
const moot = MsgV4.createMoot(id, 'post', keypair)
const mootID = MsgV4.getMsgID(moot)
await p(peer.db.add)(moot, mootID)

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack')
const caps = require('ppppp-caps')
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')
rimraf.sync(DIR)
@ -22,8 +22,8 @@ test('feed.getID()', async (t) => {
await peer.db.loaded()
const id = await p(peer.db.account.create)({ subdomain: 'person' })
const moot = MsgV3.createMoot(id, 'post', keypair)
const mootID = MsgV3.getMsgID(moot)
const moot = MsgV4.createMoot(id, 'post', keypair)
const mootID = MsgV4.getMsgID(moot)
assert.equal(
peer.db.feed.getID(id, 'post'),

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack')
const caps = require('ppppp-caps')
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')
rimraf.sync(DIR)
@ -30,8 +30,8 @@ test('feed.publish()', async (t) => {
await peer.db.loaded()
id = await p(peer.db.account.create)({ subdomain: 'person' })
moot = MsgV3.createMoot(id, 'post', keypair)
mootID = MsgV3.getMsgID(moot)
moot = MsgV4.createMoot(id, 'post', keypair)
mootID = MsgV4.getMsgID(moot)
}
let msgID1
@ -56,7 +56,7 @@ test('feed.publish()', async (t) => {
'msg1 tangle prev correct'
)
msgID1 = MsgV3.getMsgID(rec1.msg)
msgID1 = MsgV4.getMsgID(rec1.msg)
const rec2 = await p(peer.db.feed.publish)({
account: id,
@ -74,15 +74,15 @@ test('feed.publish()', async (t) => {
[msgID1],
'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) => {
const tangle = new MsgV3.Tangle(mootID)
const tangle = new MsgV4.Tangle(mootID)
tangle.add(mootID, moot)
tangle.add(rec1.id, rec1.msg)
const msg3 = MsgV3.create({
const msg3 = MsgV4.create({
keypair,
account: id,
accountTips: [id],
@ -94,7 +94,7 @@ test('feed.publish()', async (t) => {
})
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)({
account: id,

View File

@ -7,7 +7,7 @@ const rimraf = require('rimraf')
const SecretStack = require('secret-stack')
const caps = require('ppppp-caps')
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')
rimraf.sync(DIR)
@ -28,7 +28,7 @@ test('get()', async (t) => {
domain: '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)
assert.ok(msg, 'msg exists')

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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