add isRoot() and isGhostable()

This commit is contained in:
Andre Staltz 2023-10-19 16:54:37 +03:00
parent 30365793a7
commit b08f9baee9
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
2 changed files with 99 additions and 2 deletions

View File

@ -21,6 +21,11 @@ const PREFIX = 'record_v1__'
* update: Record<string, any>,
* supersedes: Array<MsgID>,
* }} RecordData
* @typedef {{
* record?: {
* ghostSpan?: number
* }
* }} Config
*/
/**
@ -65,11 +70,13 @@ function assertDBPlugin(peer) {
/**
* @param {{ db: PPPPPDB | null, close: ClosableHook }} peer
* @param {any} config
* @param {Config} config
*/
function initRecord(peer, config) {
assertDBPlugin(peer)
const ghostSpan = config.record?.ghostSpan ?? 32
//#region state
let accountID = /** @type {string | null} */ (null)
let loadPromise = /** @type {Promise<void> | null} */ (null)
@ -398,6 +405,63 @@ function initRecord(peer, config) {
return record
}
/**
* @public
* @param {Msg} msg
* @returns {boolean}
*/
function isRoot(msg) {
return isValidRecordMoot(msg)
}
/**
* @public
* @param {MsgID} ghostableMsgID
* @param {MsgID} tangleID
*/
function isGhostable(ghostableMsgID, tangleID) {
if (ghostableMsgID === tangleID) return false
assertDBPlugin(peer)
const tangle = peer.db.getTangle(tangleID)
const msg = peer.db.get(ghostableMsgID)
// prettier-ignore
if (!tangle || tangle.size === 0) throw new Error(`isGhostable() tangleID "${tangleID}" is empty`)
// prettier-ignore
if (!msg) throw new Error(`isGhostable() msgID "${ghostableMsgID}" does not exist in the database`)
// prettier-ignore
if (!isValidRecordMoot(tangle.root)) throw new Error(`isGhostable() tangleID "${tangleID}" is not a record`)
// Discover field roots
const fieldRootIDs = new Set()
const msgIDs = tangle.topoSort()
for (const msgID of msgIDs) {
const msg = peer.db.get(msgID)
if (!msg?.data) continue
for (const supersededMsgID of msg.data.supersedes) {
fieldRootIDs.delete(supersededMsgID)
}
fieldRootIDs.add(msgID)
}
// Get minimum depth of all field roots
let minFieldRootDepth = Infinity
for (const fieldRootID of fieldRootIDs) {
const depth = tangle.getDepth(fieldRootID)
if (depth < minFieldRootDepth) minFieldRootDepth = depth
// field roots are not ghostables
if (fieldRootID === ghostableMsgID) return false
// msgs suceeding field roots are not ghostables
if (tangle.precedes(fieldRootID, ghostableMsgID)) return false
}
const minGhostDepth = minFieldRootDepth - ghostSpan
const ghostableMsgDepth = msg.metadata.tangles[tangleID].depth
if (ghostableMsgDepth >= minGhostDepth) return true
return false
}
/**
* @public
* @param {string} subdomain
@ -453,6 +517,8 @@ function initRecord(peer, config) {
load,
update,
read,
isRoot,
isGhostable,
getFieldRoots,
getMinRequiredDepth,
squeeze,

View File

@ -16,7 +16,11 @@ const aliceKeypair = Keypair.generate('ed25519', 'alice')
let peer
let aliceID
test('setup', async (t) => {
peer = createPeer({ keypair: aliceKeypair, path: DIR })
peer = createPeer({
keypair: aliceKeypair,
path: DIR,
record: { ghostSpan: 4 },
})
await peer.db.loaded()
@ -130,6 +134,33 @@ test('Record squeeze', async (t) => {
assert.deepEqual(fieldRoots6, fieldRoots5, 'fieldRoots')
})
test('Record isRoot', (t) => {
const moot = MsgV3.createMoot(aliceID, 'record_v1__profile', aliceKeypair)
assert.ok(peer.record.isRoot(moot), 'isRoot')
})
test('Record isGhostable', (t) => {
const moot = MsgV3.createMoot(aliceID, 'record_v1__profile', aliceKeypair)
const mootID = MsgV3.getMsgID(moot)
const tangle = peer.db.getTangle(mootID)
const msgIDs = tangle.topoSort()
const fieldRoots = peer.record.getFieldRoots('profile')
assert.deepEqual(fieldRoots.age, [msgIDs[7]])
// Remember from the setup, that ghostSpan=4
assert.equal(msgIDs.length, 8);
assert.equal(peer.record.isGhostable(msgIDs[0], mootID), false) // moot
assert.equal(peer.record.isGhostable(msgIDs[1], mootID), false)
assert.equal(peer.record.isGhostable(msgIDs[2], mootID), false)
assert.equal(peer.record.isGhostable(msgIDs[3], mootID), true) // in ghostSpan
assert.equal(peer.record.isGhostable(msgIDs[4], mootID), true) // in ghostSpan
assert.equal(peer.record.isGhostable(msgIDs[5], mootID), true) // in ghostSpan
assert.equal(peer.record.isGhostable(msgIDs[6], mootID), true) // in ghostSpan
assert.equal(peer.record.isGhostable(msgIDs[7], mootID), false) // field root
})
test('Record receives old branched update', async (t) => {
const moot = MsgV3.createMoot(aliceID, 'record_v1__profile', aliceKeypair)
const mootID = MsgV3.getMsgID(moot)