Skip to content

Commit

Permalink
Prune the accountTangle in getSigkeysInAccount (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
Powersource authored Mar 25, 2024
1 parent d332308 commit e40c7cf
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 12 deletions.
26 changes: 17 additions & 9 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,11 @@ function initDB(peer, config) {

/**
* @param {Pick<RecPresent, 'id' | 'msg'>} rec
* @returns {Tangle | null}
* @returns {DBTangle | null}
*/
function getAccountTangle(rec) {
const accountID = getAccountID(rec)
let accountTangle = /** @type {Tangle | null} */ (null)
let accountTangle = /** @type {DBTangle | null} */ (null)
if (accountID) {
accountTangle = new DBTangle(accountID, records(), get)
if (rec.id === accountID) {
Expand All @@ -397,15 +397,20 @@ function initDB(peer, config) {
* Find which sigkeys are authorized to sign this msg given the account.
*
* @private
* @param {Tangle | null} accountTangle
* @param {DBTangle | null} accountTangle
* @param {Msg['metadata']['accountTips']} accountTipsInMsg
* @returns {Set<string>}
*/
function getSigkeysInAccount(accountTangle) {
function getSigkeysInAccount(accountTangle, accountTipsInMsg) {
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)

const prunedTangle = accountTangle.slice(
undefined,
accountTipsInMsg ?? undefined
)

for (const msg of prunedTangle) {
if (!msg?.data) continue
/** @type {AccountData} */
const data = msg.data
Expand Down Expand Up @@ -461,14 +466,17 @@ function initDB(peer, config) {
}

// Identify the account and its sigkeys:
/** @type {Tangle | null} */
/** @type {DBTangle | null} */
let accountTangle
try {
accountTangle = getAccountTangle(rec)
} catch (err) {
return new Error('Unknown account tangle owning this msg', { cause: err })
}
const sigkeys = getSigkeysInAccount(accountTangle)
const sigkeys = getSigkeysInAccount(
accountTangle,
rec.msg.metadata.accountTips
)

// Don't accept ghosts to come back, unless they are trail msgs
if (!!rec.msg.data && ghosts.read(tangleID).has(rec.id)) {
Expand Down
2 changes: 1 addition & 1 deletion lib/msg-v4/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ function isRoot(msg) {
* @returns {msg is FeedMsg<T>}
*/
function isFeedMsg(msg) {
const {account, accountTips} = msg.metadata
const { account, accountTips } = msg.metadata
return Array.isArray(accountTips) && account !== 'self' && account !== 'any'
}

Expand Down
4 changes: 2 additions & 2 deletions test/msg-v4/tangles.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,6 @@ test('MsgV4.Tangle can add msgs in random order', (t) => {
tangle.add(msgID2, msg2)
tangle.add(msgID1, msg1)

assert.deepEqual(tangle.topoSort(), [mootAID, msgID1, msgID2]);
assert.deepEqual(tangle.topoSort(), [mootAID, msgID1, msgID2])
assert.deepEqual([...tangle.tips], [msgID2], 'tangle tips')
})
})
88 changes: 88 additions & 0 deletions test/sigkeys.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const test = require('node:test')
const assert = require('node:assert')
const path = require('node:path')
const p = require('node:util').promisify
const os = require('node:os')
const rimraf = require('rimraf')
const Keypair = require('ppppp-keypair')
const { createPeer } = require('./util')
const MsgV4 = require('../lib/msg-v4')

const DIR = path.join(os.tmpdir(), 'ppppp-db-sigkeys')
const DIR2 = path.join(os.tmpdir(), 'ppppp-db-sigkeys2')
rimraf.sync(DIR)
rimraf.sync(DIR2)

test('sigkeys', async (t) => {
await t.test(
"Can't add msg that is signed by key newer than what accountTips points to",
async () => {
const keypair1 = Keypair.generate('ed25519', 'alice')
const keypair2 = Keypair.generate('ed25519', 'alice2')
const keypairOther = Keypair.generate('ed25519', 'bob')

const peer = createPeer({ keypair: keypair1, path: DIR })
const peerOther = createPeer({ keypair: keypairOther, path: DIR2 })

await peer.db.loaded()
await peerOther.db.loaded()

const account = await p(peer.db.account.create)({
keypair: keypair1,
subdomain: 'person',
})
const accountMsg0 = peer.db.get(account)

const consent = peer.db.account.consent({ account, keypair: keypair2 })

const accountRec1 = await p(peer.db.account.add)({
account,
keypair: keypair2,
consent,
powers: ['external-encryption'],
})

const goodRec = await p(peer.db.feed.publish)({
account,
domain: 'post',
data: { text: 'potatoGood' },
keypair: keypair2,
})

const postMootId = peer.db.feed.getID(account, 'post')
const postMootMsg = peer.db.get(postMootId)

const tangle = new MsgV4.Tangle(postMootId)
tangle.add(postMootId, postMootMsg)
tangle.add(goodRec.id, goodRec.msg)
const badMsg = MsgV4.create({
account,
accountTips: [account], // intentionally excluding keypair2
domain: 'post',
keypair: keypair2, // intentionally using newer key than accountTips points to
tangles: {
[postMootId]: tangle,
},
data: { text: 'potato' },
})
await assert.rejects(
p(peer.db.add)(badMsg, postMootId),
/add\(\) failed to verify msg/,
"Shouldn't be able to add() own bad msg"
)

await p(peerOther.db.add)(accountMsg0, account)
await p(peerOther.db.add)(accountRec1.msg, account)
await p(peerOther.db.add)(postMootMsg, postMootId)
await p(peerOther.db.add)(goodRec.msg, postMootId)
await assert.rejects(
p(peerOther.db.add)(badMsg, postMootId),
/add\(\) failed to verify msg/,
"Shouldn't be able to add() someone else's bad msg"
)

await p(peer.close)()
await p(peerOther.close)()
}
)
})

0 comments on commit e40c7cf

Please sign in to comment.