From bd6bb021c11ea73ca7951a1530a2d4644e3eda90 Mon Sep 17 00:00:00 2001 From: Hayden Young Date: Sat, 3 Jun 2023 06:00:25 +0800 Subject: [PATCH] docs: Lamport clock docs. (#77) * docs: Lamport clock docs. * docs: Formatting. * docs: Formatting. * docs: Formatting. --- src/identities/providers/publickey.js | 4 +-- src/oplog/clock.js | 29 ++++++++++++++++- src/oplog/conflict-resolution.js | 12 +++++-- src/oplog/entry.js | 14 +++++--- src/oplog/log.js | 4 +-- src/sync.js | 47 +++++++++++++++------------ 6 files changed, 76 insertions(+), 34 deletions(-) diff --git a/src/identities/providers/publickey.js b/src/identities/providers/publickey.js index 67f6dad..2bf978c 100644 --- a/src/identities/providers/publickey.js +++ b/src/identities/providers/publickey.js @@ -44,8 +44,8 @@ const PublicKeyIdentityProvider = ({ keystore }) => { /** * Gets the id. * @memberof module:IdentityProviders.IdentityProvider-PublicKey - * @param {String} id The id to retrieve. - * @return {String} The identity's id. + * @param {string} id The id to retrieve. + * @return {string} The identity's id. * @instance */ const getId = async ({ id } = {}) => { diff --git a/src/oplog/clock.js b/src/oplog/clock.js index 038f5fc..8926873 100644 --- a/src/oplog/clock.js +++ b/src/oplog/clock.js @@ -1,4 +1,19 @@ -/* Lamport Clock */ +/** + * @module Clock + * @description + * The lamport clock. + */ + +/** + * Compares two clocks by time and then, time is the same, by id. + * + * compareClocks should never return zero (0). If it does, a and b refer to the + * same clock. + * @param {module:Clock} a The first clock. + * @param {module:Clock} b The second clock. + * @return {number} Returns a negative integer if clock a is less than clock b + * otherwise a positive integer is returned. + */ const compareClocks = (a, b) => { // Calculate the "distance" based on the clock, ie. lower or greater const dist = a.time - b.time @@ -10,10 +25,22 @@ const compareClocks = (a, b) => { return dist } +/** + * Advances a clock's time by 1, returning a new instance of Clock. + * @param {module:Clock} clock The clock to advance. + * @return {module:Clock} A new instance of clock with time advanced by 1. + */ const tickClock = (clock) => { return Clock(clock.id, ++clock.time) } +/** + * Creates an instance of Clock. + * @function + * @param {string} id A unique identifier. + * @param {number} [time=0] A natural number (including 0). + * @instance + */ const Clock = (id, time) => { time = time || 0 diff --git a/src/oplog/conflict-resolution.js b/src/oplog/conflict-resolution.js index d4b4339..51e75e7 100644 --- a/src/oplog/conflict-resolution.js +++ b/src/oplog/conflict-resolution.js @@ -26,7 +26,10 @@ function LastWriteWins (a, b) { * Sort two entries by their clock time. * @param {Entry} a First entry to compare * @param {Entry} b Second entry to compare - * @param {function(a, b)} resolveConflict A function to call if entries are concurrent (happened at the same time). The function should take in two entries and return 1 if the first entry should be chosen and -1 if the second entry should be chosen. + * @param {function(a, b)} resolveConflict A function to call if entries are + * concurrent (happened at the same time). The function should take in two + * entries and return 1 if the first entry should be chosen and -1 if the + * second entry should be chosen. * @return {number} 1 if a is greater, -1 if b is greater */ function SortByClocks (a, b, resolveConflict) { @@ -41,7 +44,9 @@ function SortByClocks (a, b, resolveConflict) { * Sort two entries by their clock id. * @param {Entry} a First entry to compare * @param {Entry} b Second entry to compare - * @param {function(a, b)} resolveConflict A function to call if the clocks ids are the same. The function should take in two entries and return 1 if the first entry should be chosen and -1 if the second entry should be chosen. + * @param {function(a, b)} resolveConflict A function to call if the clocks ids + * are the same. The function should take in two entries and return 1 if the + * first entry should be chosen and -1 if the second entry should be chosen. * @return {number} 1 if a is greater, -1 if b is greater */ function SortByClockId (a, b, resolveConflict) { @@ -53,7 +58,8 @@ function SortByClockId (a, b, resolveConflict) { } /** - * A wrapper function to throw an error if the results of a passed function return zero + * A wrapper function to throw an error if the results of a passed function + * return zero * @param {function(a, b)} [tiebreaker] The tiebreaker function to validate. * @return {function(a, b)} 1 if a is greater, -1 if b is greater * @throws {Error} if func ever returns 0 diff --git a/src/oplog/entry.js b/src/oplog/entry.js index e215194..81d2bb0 100644 --- a/src/oplog/entry.js +++ b/src/oplog/entry.js @@ -17,10 +17,13 @@ const hashStringEncoding = base58btc * Create an Entry * @param {Identity} identity The identity instance * @param {string} logId The unique identifier for this log - * @param {*} data Data of the entry to be added. Can be any JSON.stringifyable data - * @param {Clock} [clock] The clock - * @param {Array} [next=[]] An array of CIDs as base58btc encoded strings - * @param {Array} [refs=[]] An array of CIDs as base58btc encoded strings + * @param {*} data Data of the entry to be added. Can be any JSON.stringifyable + * data. + * @param {module:Clock} [clock] The clock + * @param {Array} [next=[]] An array of CIDs as base58btc encoded + * strings. + * @param {Array} [refs=[]] An array of CIDs as base58btc encoded + * strings. * @return {Promise} * @example * const entry = await Entry.create(identity, 'log1', 'hello') @@ -59,7 +62,8 @@ const create = async (identity, id, payload, clock = null, next = [], refs = []) * * @param {Identities} identities Identities system to use * @param {Entry} entry The entry being verified - * @return {Promise} A promise that resolves to a boolean value indicating if the signature is valid + * @return {Promise} A promise that resolves to a boolean value indicating if + * the signature is valid. */ const verify = async (identities, entry) => { if (!identities) throw new Error('Identities is required, cannot verify entry') diff --git a/src/oplog/log.js b/src/oplog/log.js index 41c3ef9..a2d7e80 100644 --- a/src/oplog/log.js +++ b/src/oplog/log.js @@ -45,7 +45,7 @@ const DefaultAccessController = async () => { * @param {Object} options.access AccessController (./default-access-controller) * @param {Array} options.entries An Array of Entries from which to create the log * @param {Array} options.heads Set the heads of the log - * @param {Clock} options.clock Set the clock of the log + * @param {module:Clock} options.clock Set the clock of the log * @param {Function} options.sortFn The sort function - by default LastWriteWins * @return {module:Log~Log} sync An instance of Log * @memberof module:Log @@ -80,7 +80,7 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora /** * Returns the clock of the log. - * @return {Clock} + * @return {module:Clock} * @memberof module:Log~Log * @instance */ diff --git a/src/sync.js b/src/sync.js index de934bc..984cf9b 100644 --- a/src/sync.js +++ b/src/sync.js @@ -18,26 +18,29 @@ const DefaultTimeout = 30000 // 30 seconds * When Sync is started, a peer subscribes to a pubsub topic of the log's id. * Upon subscribing to the topic, peers already connected to the topic receive * the subscription message and "dial" the subscribing peer using a libp2p - * custom protocol. Once connected to the subscribing peer on a direct peer-to-peer - * connection, the dialing peer and the subscribing peer exchange the heads of the Log - * each peer currently has. Once completed, the peers have the same "local state". + * custom protocol. Once connected to the subscribing peer on a direct + * peer-to-peer connection, the dialing peer and the subscribing peer exchange * the heads of the Log each peer currently has. Once completed, the peers have * the same "local state". * - * Once the initial sync has completed, peers notify one another of updates to the - * log, ie. updates to the database, using the initially opened pubsub topic subscription. - * A peer with new heads broadcasts changes to other peers by publishing the updated heads - * to the pubsub topic. Peers subscribed to the same topic will then receive the update and + * Once the initial sync has completed, peers notify one another of updates to + * the log, ie. updates to the database, using the initially opened pubsub + * topic subscription. + * A peer with new heads broadcasts changes to other peers by publishing the + * updated heads + * to the pubsub topic. Peers subscribed to the same topic will then receive + * the update and * will update their log's state, the heads, accordingly. * - * The Sync Protocol is eventually consistent. It guarantees that once all messages - * have been sent and received, peers will observe the same log state and values. - * The Sync Protocol does not guarantee the order in which messages are received or - * even that a message is recieved at all, nor any timing on when messages are received. + * The Sync Protocol is eventually consistent. It guarantees that once all + * messages have been sent and received, peers will observe the same log state + * and values. The Sync Protocol does not guarantee the order in which messages + * are received or even that a message is recieved at all, nor any timing on + * when messages are received. * - * Note that the Sync Protocol does not retrieve the full log when synchronizing the - * heads. Rather only the "latest entries" in the log, the heads, are exchanged. In order - * to retrieve the full log and each entry, the user would call the log.traverse() or - * log.iterator() functions, which go through the log and retrieve each missing - * log entry from IPFS. + * Note that the Sync Protocol does not retrieve the full log when + * synchronizing the heads. Rather only the "latest entries" in the log, the + * heads, are exchanged. In order to retrieve the full log and each entry, the + * user would call the log.traverse() or log.iterator() functions, which go + * through the log and retrieve each missing log entry from IPFS. * * @example * // Using defaults @@ -59,11 +62,13 @@ const DefaultTimeout = 30000 // 30 seconds * @param {Object} params One or more parameters for configuring Sync. * @param {IPFS} params.ipfs An IPFS instance. * @param {Log} params.log The log instance to sync. - * @param {EventEmitter} [params.events] An event emitter to use. Events emitted are 'join', 'leave' and 'error'. If the parameter is not provided, an EventEmitter will be created. - * @param {onSynced} [params.onSynced] A callback function that is called after the peer - * has received heads from another peer. - * @param {Boolean} [params.start] True if sync should start automatically, false - * otherwise. Defaults to true. + * @param {EventEmitter} [params.events] An event emitter to use. Events + * emitted are 'join', 'leave' and 'error'. If the parameter is not provided, + * an EventEmitter will be created. + * @param {onSynced} [params.onSynced] A callback function that is called after + * the peer has received heads from another peer. + * @param {Boolean} [params.start] True if sync should start automatically, + * false otherwise. Defaults to true. * @return {module:Sync~Sync} sync An instance of the Sync Protocol. * @memberof module:Sync * @instance