diff --git a/assets/examples/agents.html b/assets/examples/agents.html index d3aa5c8ba..cfb0018ca 100644 --- a/assets/examples/agents.html +++ b/assets/examples/agents.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -176,11 +191,19 @@

agents.js

* | `--distributor-only` | Distributor + workers + producer loop until SIGINT/ SIGTERM (no Bitcoin / Lightning) | * | `--bitcoin-only` | Regtest Bitcoin + distributor + producer; no Lightning (lighter “real” stack) | * | `--work-ms=<n>` | Worker simulated job duration (also `FABRIC_AGENTS_WORK_MS`) | + * | `--status-json` | Emit machine-readable status lines (also `FABRIC_AGENTS_STATUS_JSON=1`) | * * ## Environment * - * - **`FABRIC_AGENTS_SKIP_CHAIN=1`** — CI-style smoke (exit after queue drains); overridden by `--distributor-only` / `--bitcoin-only`. + * - **`FABRIC_AGENTS_SKIP_CHAIN=1`** — CI-style smoke (exit after queue drains); same worker caps as `--smoke` unless `--distributor-only` / `--bitcoin-only`. * - **`FABRIC_AGENTS_WORK_MS`** — per-job delay inside workers (default `1200` full stack, `25` when skip-chain). + * - **`FABRIC_AGENTS_PRODUCER_TARGET`** — number of accepted jobs to process before stopping (default `1000`). + * - **`FABRIC_AGENTS_MAX_QUEUE`** — queue capacity before unpaid jobs must rebid (default `64`). + * - **`FABRIC_AGENTS_PRODUCER_INTERVAL_MS`** — producer tick interval in ms (default `100`). + * - **`FABRIC_AGENTS_PRODUCER_BATCH_SIZE`** — jobs attempted per producer tick (default `max(4, cores)`). + * - **`FABRIC_AGENTS_FAST_BID_EVERY`** — submit a pre-paid fast-lane bid every N jobs (default `25`). + * - **`FABRIC_AGENTS_FAST_BID_AMOUNT`** — sats used for periodic fast-lane bids (default `3`). + * - **`FABRIC_AGENTS_STATUS_JSON=1`** — emit JSON status snapshots (plus normal human-readable logs). * - **`FABRIC_AGENTS_BOOT_IDLE_MS`** — max wait (ms) for initial queue drain after startup (default `120000`). * - **`FABRIC_AGENTS_WORK_SHA256_ITERS`** — optional CPU work per job (SHA-256 loop, capped at 2M) for load testing. * - **`FABRIC_MNEMONIC`** — optional; smoke / distributor-only use {@link FIXTURE_SEED} when unset. @@ -217,7 +240,8 @@

agents.js

help: false, smoke: false, distributorOnly: false, - bitcoinOnly: false + bitcoinOnly: false, + statusJson: false }; const proc = globalThis.process; const argv = proc.argv.slice(2); @@ -227,10 +251,15 @@

agents.js

else if (a === '--smoke') flags.smoke = true; else if (a === '--distributor-only') flags.distributorOnly = true; else if (a === '--bitcoin-only') flags.bitcoinOnly = true; - else if (a === '--work-ms' && argv[i + 1]) { - proc.env.FABRIC_AGENTS_WORK_MS = String(argv[++i]); + else if (a === '--status-json') flags.statusJson = true; + else if (a === '--work-ms') { + const next = argv[i + 1]; + if (next != null && next !== '' && !String(next).startsWith('-')) { + proc.env.FABRIC_AGENTS_WORK_MS = String(argv[++i]); + } } else if (/^--work-ms=/.test(a)) { - proc.env.FABRIC_AGENTS_WORK_MS = a.replace(/^--work-ms=/, ''); + const v = a.replace(/^--work-ms=/, ''); + if (v !== '') proc.env.FABRIC_AGENTS_WORK_MS = v; } } return flags; @@ -260,7 +289,16 @@

agents.js

const process = require('process'); const { encodeCheck, decodeCheck } = require('../functions/base58'); const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); -const { FIXTURE_SEED } = require('../constants'); +const { FIXTURE_SEED } = require('../constants'); +const { + producerConfigFromEnv, + scheduleAgentProducers +} = require('./agents/producer'); +const { + defaultWork, + cloneState, + runWorkerLoop +} = require('./agents/worker'); @@ -335,22 +373,38 @@

agents.js

const _cpuCount = Math.max(1, os.cpus().length);
-/** Full example uses all CPUs; skip-chain smoke caps workers so CI / laptops do not spawn dozens of threads. */
-const numberOfCores = process.env.FABRIC_AGENTS_SKIP_CHAIN === '1'
+/**
+ * Light paths cap workers / shorten delays: explicit `--smoke`, or skip-chain env when not running
+ * distributor-only / bitcoin-only full demos (those intentionally use all CPUs).
+ */
+const _agentsLightWorkerProfile = AGENTS_CLI.smoke ||
+  (process.env.FABRIC_AGENTS_SKIP_CHAIN === '1' &&
+    !AGENTS_CLI.distributorOnly &&
+    !AGENTS_CLI.bitcoinOnly);
+/** Full stack uses all CPUs; smoke / skip-chain caps workers so CI / laptops do not spawn dozens of threads. */
+const numberOfCores = _agentsLightWorkerProfile
   ? Math.min(4, _cpuCount)
   : _cpuCount;
 const workDelayMsEnv = Number(process.env.FABRIC_AGENTS_WORK_MS);
 const AGENT_WORK_DELAY_MS = (Number.isFinite(workDelayMsEnv) && workDelayMsEnv >= 0)
   ? workDelayMsEnv
-  : (process.env.FABRIC_AGENTS_SKIP_CHAIN === '1' ? 25 : 1200);
-const DEFAULT_MAX_QUEUE = 100;
+  : (_agentsLightWorkerProfile ? 25 : 1200);
+const maxQueueEnv = Number(process.env.FABRIC_AGENTS_MAX_QUEUE);
+const DEFAULT_MAX_QUEUE = (Number.isFinite(maxQueueEnv) && maxQueueEnv > 0)
+  ? Math.floor(maxQueueEnv)
+  : 64;
 const BITCOIN_NETWORK = 'regtest';
 const bootIdleMsEnv = Number(process.env.FABRIC_AGENTS_BOOT_IDLE_MS);
 const FABRIC_AGENTS_BOOT_IDLE_MS = (Number.isFinite(bootIdleMsEnv) && bootIdleMsEnv > 0)
   ? bootIdleMsEnv
   : 120000;
-const PRODUCER_INTERVAL_MS = 200;
-const PRODUCER_BATCH_SIZE = Math.max(2, Math.ceil(numberOfCores / 2));
+const PRODUCER_CONFIG = producerConfigFromEnv(numberOfCores);
+
+/** Initial queue-drain deadline: respect `FABRIC_AGENTS_BOOT_IDLE_MS` but keep a sensible floor. */
+function agentsBootstrapDrainDeadlineMs (waves, delayMs) {
+  const computed = 3000 + waves * delayMs + 2000;
+  return Math.min(FABRIC_AGENTS_BOOT_IDLE_MS, Math.max(25000, computed));
+}
 const INITIAL_SPENDABLE_BLOCKS = 101;
 const BLOCK_INTERVAL_MS = 10000;
 const LIGHTNING_FUNDING_RATIO = 0.5;
@@ -363,7 +417,8 @@ 

agents.js

const workShaEnv = Number(process.env.FABRIC_AGENTS_WORK_SHA256_ITERS); const WORK_SHA256_ITERS = (Number.isFinite(workShaEnv) && workShaEnv > 0) ? Math.min(Math.floor(workShaEnv), 2_000_000) - : 0;
+ : 0; +const AGENTS_STATUS_JSON = AGENTS_CLI.statusJson || process.env.FABRIC_AGENTS_STATUS_JSON === '1';
@@ -376,51 +431,6 @@

agents.js

§
-

Work Function

- - - -
-
-
const work = function (payload = {}) {
-  this.queueProcessed = (this.queueProcessed || 0) + 1;
-  this.lastPayload = payload.index;
-
-  console.debug(`[WORKER:INNER] Worker #${this.workerIndex} Starting work...`, payload.index);
-  return new Promise((resolve) => {
-    if (WORK_SHA256_ITERS > 0) {
-      let buf = crypto.randomBytes(32);
-      for (let i = 0; i < WORK_SHA256_ITERS; i++) {
-        buf = crypto.createHash('sha256').update(buf).update(Buffer.from(String(i % 65536))).digest();
-      }
-      this.lastWorkDigest = buf.toString('hex', 0, 8);
-    }
-    const newState = { ...payload.state, workerIndex: this.workerIndex, incrementor: this.queueProcessed };
-    const entityID = `${newState.workerIndex}:${newState.incrementor}:${payload.index}`;
-    setTimeout(() => {
-      console.log('[WORKER:INNER] Work complete:', payload);
-      resolve({
-        depth: payload.state.depth,
-        parent: payload.state.parent,
-        output: payload.index,
-        entity: entityID,
-        state: { ...newState, id: entityID }
-      });
-    }, AGENT_WORK_DELAY_MS);
-  });
-};
-
-
- - - - -
  • -
    - -
    - § -

    Settings

    @@ -428,7 +438,7 @@

    agents.js

    const configuration = {
    -  function: work,
    +  function: defaultWork,
       maxQueue: DEFAULT_MAX_QUEUE,
       wallet: {
         keys: [
    @@ -442,54 +452,19 @@ 

    agents.js

  • -
  • -
    - -
    - § -
    -

    Functions - TODO: move these to functions/

    - -
    - -
    -
    -
    function cloneState (value) {
    -  try {
    -    return JSON.parse(JSON.stringify(value || {}));
    -  } catch {
    -    return {};
    -  }
    -}
    -
    -function compileWorkerFunction (source) {
    -  if (!source) return null;
    -  try {
    -
    -
    - -
  • - - -
  • +
  • - § + §
    -

    Example-only trusted code path to preserve settings.function behavior.

    +

    Functions

    -
        return new Function(`return (${source});`)();
    -  } catch {
    -    return null;
    -  }
    -}
    -
    +            
     function createJobID (payload = {}, paymentSats = 0) {
       const envelope = {
         type: 'DistributorWork',
    @@ -502,19 +477,6 @@ 

    agents.js

    return new Entity(envelope).id; } -function computeWorkerStateID (state = {}) { - return new Entity({ - workerIndex: state.workerIndex, - processed: state.processed, - depth: state.depth, - errors: state.errors, - lastJobID: state.lastJobID, - queueProcessed: state.queueProcessed, - lastPayload: state.lastPayload, - parentStateID: state.parentStateID - }).id; -} - async function mineBlocksToAddress (bitcoin, address, count = 1) { if (!bitcoin || !address) throw new Error('Bitcoin instance and address are required for mining.'); const blocks = await bitcoin._makeRPCRequest('generatetoaddress', [count, address]); @@ -764,10 +726,18 @@

    agents.js

    --distributor-only Distributor + periodic work until SIGINT (no Bitcoin / Lightning) --bitcoin-only Managed regtest bitcoind + distributor until SIGINT (no Lightning) --work-ms=<n> Per-job worker delay in ms (same as FABRIC_AGENTS_WORK_MS) + --status-json Emit JSON status snapshots (same as FABRIC_AGENTS_STATUS_JSON=1) Environment: FABRIC_AGENTS_SKIP_CHAIN=1 CI-style smoke (exit after drain); ignored if --distributor-only / --bitcoin-only FABRIC_AGENTS_WORK_MS Worker simulated job duration + FABRIC_AGENTS_PRODUCER_TARGET Accepted jobs target before stopping (default 1000) + FABRIC_AGENTS_MAX_QUEUE Queue capacity before unpaid jobs must rebid (default 64) + FABRIC_AGENTS_PRODUCER_INTERVAL_MS Producer tick interval in ms (default 100) + FABRIC_AGENTS_PRODUCER_BATCH_SIZE Jobs attempted each producer tick (default max(4, cores)) + FABRIC_AGENTS_FAST_BID_EVERY Submit a fast-lane paid bid every N jobs (default 25) + FABRIC_AGENTS_FAST_BID_AMOUNT Sats for periodic fast-lane bids (default 3) + FABRIC_AGENTS_STATUS_JSON=1 Emit JSON status snapshots FABRIC_AGENTS_BOOT_IDLE_MS Max ms to wait for initial queue drain (default 120000) FABRIC_AGENTS_WORK_SHA256_ITERS Optional SHA-256 iterations per job (load test; cap 2M) FABRIC_MNEMONIC Optional BIP-39 mnemonic for keys @@ -779,71 +749,6 @@

    agents.js

    `); } -/** - * @param {{ master: Distributor, lightning?: object, aliceLightning?: object }} opts - * @returns {{ producerTimer: NodeJS.Timeout, statusTimer: NodeJS.Timeout }} - */ -function scheduleAgentProducers ({ master, lightning, aliceLightning }) { - let producerSequence = 0; - const producerTimer = setInterval(() => { - for (let i = 0; i < PRODUCER_BATCH_SIZE; i++) { - try { - master.requestWork({ index: `work-${producerSequence++}` }, 0); - } catch (error) { - console.warn('[STATUS]', 'Queue submit skipped:', error.message); - break; - } - } - }, PRODUCER_INTERVAL_MS); - - const statusTimer = setInterval(() => { - Promise.resolve().then(async () => { - let snapshot = { - masterFunds: {}, - aliceFunds: {}, - masterChannels: [], - aliceChannels: [], - updatedAt: null - }; - if (lightning && aliceLightning) { - snapshot = await getLightningStatusSnapshot(lightning, aliceLightning); - } - master.setExternalStatus(snapshot); - - const status = master.status(); - const aliceChannels = Array.isArray(snapshot.aliceChannels) ? snapshot.aliceChannels : []; - const masterChannels = Array.isArray(snapshot.masterChannels) ? snapshot.masterChannels : []; - const aliceFunds = snapshot.aliceFunds || {}; - const masterFunds = snapshot.masterFunds || {}; - const workerSummary = status.workers.map((worker) => { - const state = worker.state || {}; - return `w${worker.index}:done=${worker.processed},err=${worker.errors},${worker.busy ? 'busy' : 'idle'},state.jobs=${state.queueProcessed || 0},state.depth=${state.depth || 0},state.last=${state.lastPayload || '-'}`; - }).join(' | '); - - console.log( - `[MASTER] [STATUS] queue=${status.queueDepth} completed=${status.completed} failed=${status.failed} processed=${status.processed} busy=${status.workersBusy}/${status.workersTotal} ${workerSummary}` - ); - - if (lightning && aliceLightning) { - console.log( - `[ALICE] [STATUS:LIGHTNING] channels=${aliceChannels.length} funds=${aliceFunds.total_msat || aliceFunds.total || '-'}` - ); - console.log( - `[MASTER] [STATUS:LIGHTNING] channels=${masterChannels.length} funds=${masterFunds.total_msat || masterFunds.total || '-'}` - ); - } - - console.log( - `[MASTER] [MERKLE] depth=${status.stateDepth} root=${status.historyRoot || '-'} tip=${status.stateTip || '-'} parent=${status.stateParent || '-'}` - ); - }).catch((error) => { - console.warn('[STATUS]', 'Status tick failed:', error.message); - }); - }, 5000); - - return { producerTimer, statusTimer }; -} - /** * Long-running distributor + workers only (no Bitcoin / Lightning). * @param {object} [input] @@ -879,19 +784,28 @@

    agents.js

    const queuedJobs = numberOfCores + 1; const waves = Math.ceil(queuedJobs / Math.max(1, numberOfCores)); const delayMs = Math.max(AGENT_WORK_DELAY_MS, 1); - const idleDeadline = Math.max(25000, 3000 + waves * delayMs + 2000); + const idleDeadline = agentsBootstrapDrainDeadlineMs(waves, delayMs); const drained = await master.waitForIdle(idleDeadline); if (!drained) { throw new Error(`Bootstrap queue did not drain within ${idleDeadline}ms`); } console.log('[DEVELOP] Initial queue drained; starting producer loop.'); - const timers = scheduleAgentProducers({ master, lightning: null, aliceLightning: null }); + const timers = scheduleAgentProducers({ + master, + lightning: null, + aliceLightning: null, + getLightningStatusSnapshot, + statusJsonEnabled: AGENTS_STATUS_JSON, + config: PRODUCER_CONFIG + }); producerTimer = timers.producerTimer; statusTimer = timers.statusTimer; + const summary = await timers.done; - console.log('[DEVELOP] Distributor-only demo running.'); - await new Promise(() => {}); + console.log(`[DEVELOP] Distributor-only producer complete: accepted=${summary.accepted}/${summary.target} rebid_ok=${summary.rebidSuccess} max_bid=${summary.maxBid}`); + await master.stop(); + return summary; } /** @@ -996,12 +910,28 @@

    agents.js

    console.warn(`[DEVELOP] Initial queue still busy after ${FABRIC_AGENTS_BOOT_IDLE_MS}ms; continuing with producer.`); } - const timers = scheduleAgentProducers({ master, lightning: null, aliceLightning: null }); + const timers = scheduleAgentProducers({ + master, + lightning: null, + aliceLightning: null, + getLightningStatusSnapshot, + statusJsonEnabled: AGENTS_STATUS_JSON, + config: PRODUCER_CONFIG + }); producerTimer = timers.producerTimer; statusTimer = timers.statusTimer; + const summary = await timers.done; - console.log('[DEVELOP] Bitcoin + distributor demo running (no Lightning). Ctrl+C to stop.'); - await new Promise(() => {}); + console.log(`[DEVELOP] Bitcoin+distributor producer complete: accepted=${summary.accepted}/${summary.target} rebid_ok=${summary.rebidSuccess} max_bid=${summary.maxBid}`); + await master.stop(); + if (blockTimer) { + clearInterval(blockTimer); + blockTimer = null; + } + if (bitcoin) { + await bitcoin.stop(); + } + return summary; } async function payRequestedAmount (lightning, aliceLightning, request = {}) { @@ -1032,11 +962,11 @@

    agents.js

  • -
  • +
  • - § + §

    Continue with default wallet target.

    @@ -1051,11 +981,11 @@

    agents.js

  • -
  • +
  • - § + §

    Descriptor imports of xpub watch-only keys are rejected on private-key wallets.

    @@ -1076,11 +1006,11 @@

    agents.js

  • -
  • +
  • - § + §

    Ignore and try create flow below.

    @@ -1118,11 +1048,11 @@

    agents.js

  • -
  • +
  • - § + §

    TODO: document all descriptors

    @@ -1167,11 +1097,11 @@

    agents.js

  • -
  • +
  • - § + §

    BIP32 version bytes for public extended keys.

    @@ -1193,83 +1123,6 @@

    agents.js

    return encodeCheck(remapped); } -function runWorkerLoop () { - const run = compileWorkerFunction(workerData && workerData.functionSource); - const boundState = Object.assign({ - workerIndex: workerData && workerData.workerIndex, - processed: 0, - depth: 0, - errors: 0, - lastJobID: null, - parentStateID: null, - stateID: null - }, cloneState(workerData && workerData.initialState)); - - boundState.stateID = computeWorkerStateID(boundState); - - if (!parentPort) return; -
    - - -
  • - - -
  • -
    - -
    - § -
    -

    TODO: migrate this to functions/onParentMessage.js

    - -
    - -
    -
    -
      parentPort.on('message', async (message) => {
    -    if (!message) return;
    -    if (message.type === 'shutdown') {
    -      process.exit(0);
    -      return;
    -    }
    -    if (message.type !== 'start') return;
    -
    -    try {
    -      boundState.lastJobID = message.id || null;
    -      boundState.parentStateID = boundState.stateID || message.parentStateID || boundState.parentStateID || null;
    -      boundState.parent = boundState.parentStateID;
    -      boundState.depth = (boundState.depth || 0) + 1;
    -      if (typeof run === 'function') {
    -        const result = await run.call(boundState, Object.assign({}, message.payload, {
    -          state: boundState
    -        }));
    -
    -        if (result && typeof result === 'object' && result.state && typeof result.state === 'object') {
    -          Object.assign(boundState, result.state);
    -        }
    -      }
    -
    -      boundState.processed++;
    -      boundState.stateID = computeWorkerStateID(boundState);
    -
    -      parentPort.postMessage({
    -        type: 'done',
    -        id: message.id,
    -        pid: process.pid,
    -        state: cloneState(boundState)
    -      });
    -    } catch (error) {
    -      boundState.errors++;
    -      parentPort.postMessage({
    -        type: 'error',
    -        id: message.id,
    -        error: error.message,
    -        state: cloneState(boundState)
    -      });
    -    }
    -  });
    -}
    -
     /**
      * A Distributor is a service that distributes work to multiple agents.
      */
    @@ -1287,11 +1140,11 @@ 

    agents.js

  • -
  • +
  • - § + §

    Arbitrary state

    @@ -1310,11 +1163,11 @@

    agents.js

  • -
  • +
  • - § + §

    Internal state

    @@ -1328,6 +1181,10 @@

    agents.js

    maxQueue: settings.maxQueue || DEFAULT_MAX_QUEUE, completed: 0, failed: 0, + paymentCreditSats: 0, + paidJobsCompleted: 0, + paidJobsFailed: 0, + totalPaidSatsEarned: 0, nextWorkerIndex: 0, stateTip: null, externalStatus: { @@ -1395,8 +1252,12 @@

    agents.js

    workerIndex: i, initialState: { queueProcessed: 0, - lastPayload: null + lastPayload: null, + workSha256Iters: WORK_SHA256_ITERS, + workDelayMs: AGENT_WORK_DELAY_MS }, + workSha256Iters: WORK_SHA256_ITERS, + workDelayMs: AGENT_WORK_DELAY_MS, functionSource: (this.settings.function && this.settings.function.toString) ? this.settings.function.toString() : null @@ -1408,6 +1269,9 @@

    agents.js

    core.__processed = 0; core.__errors = 0; core.__lastJobID = null; + core.__activePaymentSats = 0; + core.__earnedSats = 0; + core.__paidJobs = 0; core.__state = {}; this._state.cores.push(core); @@ -1419,7 +1283,14 @@

    agents.js

    core.__lastJobID = message.id || null; core.__state = message.state || core.__state; this._state.content.completed++; + if (core.__activePaymentSats > 0) { + core.__earnedSats += core.__activePaymentSats; + core.__paidJobs++; + this._state.content.paidJobsCompleted++; + this._state.content.totalPaidSatsEarned += core.__activePaymentSats; + } this._snapshotGlobalState('worker-done'); + core.__activePaymentSats = 0; core.__busy = false; this._dispatchWork(); } @@ -1429,7 +1300,21 @@

    agents.js

    core.__lastJobID = message.id || null; core.__state = message.state || core.__state; this._state.content.failed++; + if (core.__activePaymentSats > 0) { + this._state.content.paidJobsFailed++; + } this._snapshotGlobalState('worker-error'); + const errDetail = (message && message.error != null) + ? message.error + : (message && message.message) || 'worker error'; + this.emit('workerJobFailed', { + workerIndex: core.__index, + jobId: core.__lastJobID, + detail: errDetail, + state: core.__state + }); + console.warn('[DEVELOP] Worker job failed:', errDetail); + core.__activePaymentSats = 0; core.__busy = false; this._dispatchWork(); } @@ -1523,14 +1408,21 @@

    agents.js

    if (queue.length >= maxQueue && !paidPriority) { const topCost = queue[0].paymentSats; const cost = topCost + 1; + const availableCredit = Number(this._state.content.paymentCreditSats || 0); + + if (availableCredit >= cost) { + this._state.content.paymentCreditSats = availableCredit - cost; + paymentSats = cost; + } else { + this.emit('payments:request', { + amount: cost, + description: 'Priority work payment', + recipient: 'Priority work payment', + payload + }); - this.emit('payments:request', { - amount: cost, - description: 'Priority work payment', - recipient: 'Priority work payment' - }); - - throw new Error(`Queue full (${maxQueue}). Pay ${cost} satoshi for priority.`); + throw new Error(`Queue full (${maxQueue}). Pay ${cost} satoshi for priority.`); + } } const job = { @@ -1546,11 +1438,11 @@

    agents.js

  • -
  • +
  • - § + §

    Paid work skips the line when queue capacity is exhausted.

    @@ -1570,11 +1462,11 @@

    agents.js

  • -
  • +
  • - § + §

    Dispatch work to the workers.

    @@ -1589,11 +1481,11 @@

    agents.js

  • -
  • +
  • - § + §

    Return the job ID.

    @@ -1625,11 +1517,11 @@

    agents.js

  • -
  • +
  • - § + §

    Take job from queue and dispatch to worker.

    @@ -1639,6 +1531,7 @@

    agents.js

          const job = queue.shift();
           core.__busy = true;
    +      core.__activePaymentSats = Number(job.paymentSats) || 0;
           try {
             core.postMessage({
               type: 'start',
    @@ -1648,6 +1541,7 @@ 

    agents.js

    }); } catch (error) { queue.unshift(job); + core.__activePaymentSats = 0; core.__busy = false; this.emit('error', 'Failed to dispatch job:', error.message); } @@ -1658,7 +1552,9 @@

    agents.js

    const start = Date.now(); while (true) { - const queueEmpty = this._state.content.queue.length === 0; + const q = this._state.content.queue.length; + if (this._state.cores.length === 0 && q > 0) return false; + const queueEmpty = q === 0; const workersBusy = this._state.cores.some((core) => core && core.__busy); if (queueEmpty && !workersBusy) return true; @@ -1679,6 +1575,19 @@

    agents.js

    return this; } + creditPayment (amountSats = 0, metadata = {}) { + const amount = Math.max(0, Math.floor(Number(amountSats) || 0)); + if (amount <= 0) return this._state.content.paymentCreditSats; + this._state.content.paymentCreditSats += amount; + this.emit('payments:credited', { + amount, + availableCredit: this._state.content.paymentCreditSats, + metadata + }); + this._snapshotGlobalState('payment-credited'); + return this._state.content.paymentCreditSats; + } + status () { const workers = this._state.cores.map((core) => ({ index: core.__index, @@ -1686,6 +1595,8 @@

    agents.js

    processed: core.__processed || 0, errors: core.__errors || 0, lastJobID: core.__lastJobID || null, + earnedSats: core.__earnedSats || 0, + paidJobs: core.__paidJobs || 0, state: core.__state || {} })); @@ -1697,6 +1608,10 @@

    agents.js

    queueDepth: this._state.content.queue.length, completed: this._state.content.completed, failed: this._state.content.failed || 0, + paymentCreditSats: this._state.content.paymentCreditSats || 0, + paidJobsCompleted: this._state.content.paidJobsCompleted || 0, + paidJobsFailed: this._state.content.paidJobsFailed || 0, + totalPaidSatsEarned: this._state.content.totalPaidSatsEarned || 0, processed: (this._state.content.completed || 0) + (this._state.content.failed || 0), stateTip: this._state.id, stateParent: this._state.history.length > 1 ? this._state.history[this._state.history.length - 2] : null, @@ -1724,7 +1639,7 @@

    agents.js

    async function main (input = {}) { if (!isMainThread) { - runWorkerLoop(input); + runWorkerLoop({ parentPort, workerData, processObj: process }); return; } @@ -1776,11 +1691,13 @@

    agents.js

    const queuedJobs = numberOfCores + 1; const waves = Math.ceil(queuedJobs / Math.max(1, numberOfCores)); const delayMs = Math.max(AGENT_WORK_DELAY_MS, 1); - const idleDeadline = Math.max(25000, 3000 + waves * delayMs + 2000); + const idleDeadline = agentsBootstrapDrainDeadlineMs(waves, delayMs); const drained = await skipMaster.waitForIdle(idleDeadline); if (!drained) { throw new Error(`Skip-chain smoke: queue did not drain within ${idleDeadline}ms`); } + const status = skipMaster.status(); + console.log(`[DEVELOP] Skip-chain smoke earnings: paid_done=${status.paidJobsCompleted} earned=${status.totalPaidSatsEarned}sats credit=${status.paymentCreditSats}sats`); console.log('[DEVELOP] Skip-chain smoke: initial queue drained OK.'); console.log('[DEVELOP] Skip-chain smoke: complete.'); return; @@ -1853,11 +1770,11 @@

    agents.js

  • -
  • +
  • - § + §

    Last-resort cleanup to avoid orphaned managed daemons.

    @@ -1907,6 +1824,61 @@

    agents.js

    }); }); + const cleanupBootstrapFailure = async () => { + if (blockTimer) { + clearInterval(blockTimer); + blockTimer = null; + } + if (master) { + try { + await master.stop(); + } catch (e) { + console.error('[DEVELOP] Bootstrap cleanup: distributor stop failed:', e && e.message ? e.message : e); + } + master = null; + } + if (aliceLightning) { + try { + await aliceLightning.stop(); + } catch (e) { + console.error('[DEVELOP] Bootstrap cleanup: Alice Lightning stop failed:', e && e.message ? e.message : e); + } + try { + if (aliceLightning._child && aliceLightning._child.exitCode === null) { + aliceLightning._child.kill('SIGKILL'); + } + } catch { /* ignore */ } + aliceLightning = null; + } + if (lightning) { + try { + await lightning.stop(); + } catch (e) { + console.error('[DEVELOP] Bootstrap cleanup: Lightning stop failed:', e && e.message ? e.message : e); + } + try { + if (lightning._child && lightning._child.exitCode === null) { + lightning._child.kill('SIGKILL'); + } + } catch { /* ignore */ } + lightning = null; + } + if (bitcoin) { + try { + await bitcoin.stop(); + } catch (e) { + console.error('[DEVELOP] Bootstrap cleanup: Bitcoin stop failed:', e && e.message ? e.message : e); + } + try { + if (bitcoin._nodeProcess && bitcoin._nodeProcess.exitCode === null) { + bitcoin._nodeProcess.kill('SIGKILL'); + } + } catch { /* ignore */ } + bitcoin = null; + } + }; + + try { bitcoin = new Bitcoin({ network: BITCOIN_NETWORK, mode: 'rpc', @@ -2077,18 +2049,19 @@

    agents.js

    key: staticKey || input.key || null })); - let paymentInFlight = false; + let paymentInFlight = false; + const deferredPaymentRequests = [];
  • -
  • +
  • - § + §

    TODO: migrate to functions/

    @@ -2098,18 +2071,35 @@

    agents.js

      master.on('payments:request', async (request) => {
         if (paymentInFlight) {
    -      console.warn('[PAYMENTS] Payment already in-flight, skipping duplicate request.');
    +      deferredPaymentRequests.push(request);
    +      console.warn('[PAYMENTS] Payment already in-flight, queueing duplicate request.');
           return;
         }
     
         paymentInFlight = true;
         try {
           const result = await payRequestedAmount(lightning, aliceLightning, request);
    +      master.creditPayment(result.sats, {
    +        invoiceHash: result.invoice && (result.invoice.paymentHash || result.invoice.payment_hash || null),
    +        request
    +      });
    +      if (request && request.payload) {
    +        try {
    +          const paidJobID = master.requestWork(request.payload, 0);
    +          console.log(`[PAYMENTS] Re-queued paid priority work as job ${paidJobID}.`);
    +        } catch (queueError) {
    +          console.warn('[PAYMENTS] Could not re-queue paid priority work:', queueError.message);
    +        }
    +      }
           console.log(`[PAYMENTS] Paid ${result.sats} sats via Lightning (invoice=${result.invoice.paymentHash || '-'}).`);
         } catch (error) {
           console.error('[PAYMENTS] Failed to complete payment request:', error.message);
         } finally {
           paymentInFlight = false;
    +      const next = deferredPaymentRequests.shift();
    +      if (next) {
    +        setImmediate(() => master.emit('payments:request', next));
    +      }
         }
       });
     
    @@ -2131,11 +2121,11 @@ 

    agents.js

  • -
  • +
  • - § + §

    Start the master distributor.

    @@ -2150,11 +2140,11 @@

    agents.js

  • -
  • +
  • - § + §

    Demonstrate standard and paid-priority queueing behavior.

    @@ -2171,9 +2161,25 @@

    agents.js

    console.warn(`[DEVELOP] Initial queue still busy after ${FABRIC_AGENTS_BOOT_IDLE_MS}ms; continuing with producer.`); } - const timers = scheduleAgentProducers({ master, lightning, aliceLightning }); + const timers = scheduleAgentProducers({ + master, + lightning, + aliceLightning, + getLightningStatusSnapshot, + statusJsonEnabled: AGENTS_STATUS_JSON, + config: PRODUCER_CONFIG + }); producerTimer = timers.producerTimer; statusTimer = timers.statusTimer; + const summary = await timers.done; + console.log(`[DEVELOP] Full-stack producer complete: accepted=${summary.accepted}/${summary.target} rebid_ok=${summary.rebidSuccess} max_bid=${summary.maxBid}`); + await cleanupBootstrapFailure(); + return summary; + } catch (bootstrapError) { + console.error('[DEVELOP] Full-stack bootstrap failed:', bootstrapError && bootstrapError.message ? bootstrapError.message : bootstrapError); + await cleanupBootstrapFailure(); + throw bootstrapError; + } } if (isMainThread) { @@ -2183,9 +2189,14 @@

    agents.js

    }).then((exitNote) => { if (exitNote === 'no-complete-log') return; console.log('[DEVELOP]', 'Main thread complete'); + const skipChainEnv = process.env.FABRIC_AGENTS_SKIP_CHAIN === '1'; + const smokeExit = (AGENTS_CLI.smoke || skipChainEnv) && !AGENTS_CLI.distributorOnly && !AGENTS_CLI.bitcoinOnly; + if (require.main === module && smokeExit) { + process.exit(0); + } }); } else { - runWorkerLoop(); + runWorkerLoop({ parentPort, workerData, processObj: process }); } module.exports = main; diff --git a/assets/examples/app.html b/assets/examples/app.html index 85b522a9f..be7911569 100644 --- a/assets/examples/app.html +++ b/assets/examples/app.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -156,7 +171,7 @@

    Offline-first Applications with

    Warning

    This example is intended for downstream consumers — those seeking to implement client-facing applications using Fabric.

    Quickstart

    -

    Ensure that you are using NodeJS 24.14.1 — execute in your clone of the Fabric Core repository.

    +

    Ensure that you are using NodeJS 24.15.0 — execute in your clone of the Fabric Core repository.

    Cloning Fabric

    Run the following commands:

    git clone git@github.com:FabricLabs/fabric.git
    diff --git a/assets/examples/bitcoin.html b/assets/examples/bitcoin.html
    index 330dc12d1..ac978dc4f 100644
    --- a/assets/examples/bitcoin.html
    +++ b/assets/examples/bitcoin.html
    @@ -62,6 +62,16 @@
                   
     
     
    +              
    +                examples/fabric-basic-usage.js
    +              
    +
    +
    +              
    +                examples/fabric-demo.js
    +              
    +
    +
                   
                     examples/fabric.js
                   
    @@ -102,6 +112,11 @@
                   
     
     
    +              
    +                examples/onion-forward.js
    +              
    +
    +
                   
                     examples/oracle.js
                   
    @@ -168,11 +183,10 @@ 

    bitcoin.js

    const Fabric = require('../'); const Bitcoin = require('../services/bitcoin'); -const Wallet = require('../types/wallet'); async function main () { - let fabric = new Fabric(); - let bitcoin = new Bitcoin({ network: 'regtest' });
    + const fabric = new Fabric(); + const bitcoin = new Bitcoin({ network: 'regtest' });
    @@ -185,14 +199,13 @@

    bitcoin.js

    §
    -

    let wallet = new Wallet();

    +

    Listen for messages from the Bitcoin service

    -
    -  bitcoin.on('message', function (msg) {
    +            
      bitcoin.on('message', function (msg) {
         console.log('[DEVELOP]', 'Bitcoin emitted message:', msg);
       });
    @@ -207,13 +220,13 @@

    bitcoin.js

    §
    -

    fabric.use(bitcoin);

    +

    Use the Bitcoin service in the Fabric instance

    -
      await bitcoin.start();
    +
      fabric.use(bitcoin);
    @@ -226,10 +239,16 @@

    bitcoin.js

    §
    -

    await wallet.start();

    +

    Start the Bitcoin service

    +
    +
    +
      await bitcoin.start();
    +
    +
    +
  • @@ -278,9 +297,16 @@

    bitcoin.js

    -
    }
    -
    -module.exports = main();
    +
    +  await bitcoin.stop();
    +}
    +
    +if (require.main === module) {
    +  main().catch((exception) => {
    +    console.error('[EXAMPLES:BITCOIN]', 'Main Process Exception:', exception);
    +    process.exitCode = 1;
    +  });
    +}
    diff --git a/assets/examples/blockchain.html b/assets/examples/blockchain.html index c784f22b5..1755cd62d 100644 --- a/assets/examples/blockchain.html +++ b/assets/examples/blockchain.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/chain.html b/assets/examples/chain.html index 3dc779312..f61d86016 100644 --- a/assets/examples/chain.html +++ b/assets/examples/chain.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -185,19 +200,19 @@

    chain.js

    const chain = new Chain(); async function main () { - await chain.storage.open(); + await chain.start(); chain.on('block', function (block) { console.log('[CHAIN]', 'new block:', block); console.log('[CHAIN]', 'chain:', chain); }); - chain.append({ test: 'foo' }); - chain.append({ test: 'bar' }); + await chain.append({ test: 'foo' }); + await chain.append({ test: 'bar' }); - await chain.storage.close(); + await chain.stop(); - return this; + return chain; } main().catch((exception) => { diff --git a/assets/examples/cli.html b/assets/examples/cli.html index 27ccecd15..0c53b8fdd 100644 --- a/assets/examples/cli.html +++ b/assets/examples/cli.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/collection.html b/assets/examples/collection.html index c7f1934df..0f7034cbd 100644 --- a/assets/examples/collection.html +++ b/assets/examples/collection.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/environment.html b/assets/examples/environment.html index e6726f767..df3b8d179 100644 --- a/assets/examples/environment.html +++ b/assets/examples/environment.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/fabric-basic-usage.html b/assets/examples/fabric-basic-usage.html new file mode 100644 index 000000000..4abcdd08e --- /dev/null +++ b/assets/examples/fabric-basic-usage.html @@ -0,0 +1,206 @@ + + + + + + fabric-basic-usage.js + + + + + + +
    +
    + + + + +
    + + + \ No newline at end of file diff --git a/assets/examples/fabric-demo.html b/assets/examples/fabric-demo.html new file mode 100644 index 000000000..f68fd075c --- /dev/null +++ b/assets/examples/fabric-demo.html @@ -0,0 +1,216 @@ + + + + + + fabric-demo.js + + + + + + +
    +
    + + + + +
    + + + \ No newline at end of file diff --git a/assets/examples/fabric.html b/assets/examples/fabric.html index 41a2d3c73..c38301190 100644 --- a/assets/examples/fabric.html +++ b/assets/examples/fabric.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -219,8 +234,9 @@

    Getting Started with Fabric

    async function main() {
    -  let fabric = new Fabric();
    -  console.log('[EXAMPLE]', 'Fabric:', fabric);
    +  const fabric = new Fabric();
    +  console.log('[EXAMPLE:FABRIC]', 'id:', fabric.id);
    +  console.log('[EXAMPLE:FABRIC]', 'clock:', fabric.clock);
     }
    diff --git a/assets/examples/game.html b/assets/examples/game.html index fc7d981ba..fcfdeed17 100644 --- a/assets/examples/game.html +++ b/assets/examples/game.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -178,7 +193,6 @@

    game.js

    }); game.use('spawn', function (input) { - var self = this; var data = _.clone(template); data.id = Math.random(); @@ -264,8 +278,6 @@

    game.js

    }); game.use('battle', function (input) { - var self = this; - console.log('battling...', input.spawns); input.spawns[0].stack.push('attack'); diff --git a/assets/examples/heartbeat.html b/assets/examples/heartbeat.html index 0f02d4403..80f4a32c2 100644 --- a/assets/examples/heartbeat.html +++ b/assets/examples/heartbeat.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -181,7 +196,8 @@

    heartbeat.js

    -
    const Peer = require('../types/peer');
    +            
    const { setMaxListeners } = require('events');
    +const Peer = require('../types/peer');
     const { Swarm } = require('../types/peer');
     const Message = require('../types/message');
    @@ -206,7 +222,8 @@

    heartbeat.js

    seeds: ['localhost:7777'] }; -async function main () { +async function main () { + setMaxListeners(0);
    @@ -276,7 +293,24 @@

    heartbeat.js

    console.log('[EXAMPLES:HEARTBEAT]', 'Starting Swarm...'); await swarm.start(); - console.log('[EXAMPLES:HEARTBEAT]', 'Swarm started!'); + console.log('[EXAMPLES:HEARTBEAT]', 'Swarm started!'); + + const stopWithTimeout = async (instance, label, timeoutMs = 1200) => { + if (!instance || typeof instance.stop !== 'function') return; + try { + await Promise.race([ + instance.stop(), + new Promise((resolve) => setTimeout(resolve, timeoutMs)) + ]); + } catch (error) { + console.warn('[EXAMPLES:HEARTBEAT]', `${label} stop warning:`, error.message); + } + }; + + const shutdown = async () => { + await stopWithTimeout(swarm, 'Swarm'); + await stopWithTimeout(seeder, 'Seeder'); + }; @@ -289,16 +323,18 @@

    heartbeat.js

    §
    -

    Send Regular Updates (outside of internal ping/pong)

    +

    Send a single explicit heartbeat over the current v1 base message type.

    -
      const heartbeat = setInterval(function () {
    -    console.warn('[EXAMPLES:HEARTBEAT]', 'Starting to send interval message...');
    -    const message = Message.fromVector(['Generic', Date.now().toString()]);
    -    console.log('[EXAMPLES:HEARTBEAT]', 'Sending :', message.raw);
    +
      console.warn('[EXAMPLES:HEARTBEAT]', 'Sending heartbeat...');
    +  const message = Message.fromVector(['P2P_BASE_MESSAGE', JSON.stringify({
    +    type: 'Heartbeat',
    +    object: { at: Date.now() }
    +  })]);
    +  seeder.broadcast(message.toBuffer());
    @@ -311,33 +347,16 @@

    heartbeat.js

    §
    -

    Send interval message through seed node

    +

    Give peers a short window to process, then shut down cleanly.

    -
        seeder.broadcast(message);
    -
    -
    +
      await new Promise((resolve) => setTimeout(resolve, 1500));
    +  await shutdown();
     
    -      
    -
    -
    -      
  • -
    - -
    - § -
    -

    Send interval message through swarm agent - swarm.broadcast(message);

    - -
    - -
    -
    -
      }, 5000);
    +  if (require.main === module) process.exit(0);
     }
     
     main().catch(function exceptionHandler (exception) {
    diff --git a/assets/examples/http.html b/assets/examples/http.html
    index 725a2f2a5..74214dff6 100644
    --- a/assets/examples/http.html
    +++ b/assets/examples/http.html
    @@ -62,6 +62,16 @@
                   
     
     
    +              
    +                examples/fabric-basic-usage.js
    +              
    +
    +
    +              
    +                examples/fabric-demo.js
    +              
    +
    +
                   
                     examples/fabric.js
                   
    @@ -102,6 +112,11 @@
                   
     
     
    +              
    +                examples/onion-forward.js
    +              
    +
    +
                   
                     examples/oracle.js
                   
    @@ -152,7 +167,10 @@
                 §
               

    Exposing ARCs with HTTP

    -

    Fabric makes it easy to publish applications to the Web, +

    Downstream apps set path to their assets/ (or similar). Files there are served first; unhandled + paths fall through to the packaged @fabric/http assets/ (e.g. Fomantic / semantic) — no extra + settings required. See FabricHTTPServer in @fabric/http types/server.js static middleware order. + Fabric makes it easy to publish applications to the Web, giving downstream users access to a hosted instance of the application.

    By using @fabric/http we can import an existing Fabric diff --git a/assets/examples/index.html b/assets/examples/index.html index ef26c0736..fc0f67292 100644 --- a/assets/examples/index.html +++ b/assets/examples/index.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -182,7 +197,7 @@

    Importing Fabric

    -
    const Fabric = require('@fabric/core');
    +
    const Fabric = require('../');
    @@ -223,7 +238,7 @@

    Importing Fabric

    -
      let app = new Fabric();
    +
      const app = new Fabric();
    @@ -237,14 +252,15 @@

    Importing Fabric

    §
      -
    1. Add some data…
    2. +
    3. Add some state with a deterministic in-memory path.
    -
      await app._POST(`/inscriptions`, { input: 'Hello, world!' });
    +
      await app._SET('/examples/index/message', { input: 'Hello, world!' });
    +  const stored = await app._GET('/examples/index/message');
    @@ -258,17 +274,22 @@

    Importing Fabric

    §
      -
    1. Output some results!
    2. +
    3. Output some results without dumping the full object graph.
    -
      console.log('app:', app);
    +            
      console.log('[EXAMPLES:INDEX]', 'id:', app.id);
    +  console.log('[EXAMPLES:INDEX]', 'clock:', app.clock);
    +  console.log('[EXAMPLES:INDEX]', 'stored:', stored);
     }
     
    -main();
    +main().catch((exception) => { + console.error('[EXAMPLES:INDEX]', 'Main Process Exception:', exception); + process.exitCode = 1; +});
    diff --git a/assets/examples/lightning.html b/assets/examples/lightning.html index 2fe301d63..3826731b8 100644 --- a/assets/examples/lightning.html +++ b/assets/examples/lightning.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/message.html b/assets/examples/message.html index 92af0a346..ee7a60185 100644 --- a/assets/examples/message.html +++ b/assets/examples/message.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -220,7 +235,7 @@

    message.js

    -
      const message = Message.fromVector(['GenericMessage', '"Hello, world!"']);
    +
      const message = Message.fromVector(['P2P_BASE_MESSAGE', '"Hello, world!"']);
    diff --git a/assets/examples/network.html b/assets/examples/network.html index 67f5381a8..dab75cb06 100644 --- a/assets/examples/network.html +++ b/assets/examples/network.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/onion-forward.html b/assets/examples/onion-forward.html new file mode 100644 index 000000000..4f9fb7dda --- /dev/null +++ b/assets/examples/onion-forward.html @@ -0,0 +1,226 @@ + + + + + + onion-forward.js + + + + + + +
    +
    + + + +
      + +
    • +
      +

      onion-forward.js

      +
      +
    • + + + +
    • +
      + +
      + § +
      + +
      + +
      +
      +
      'use strict';
      +
      +/**
      + * Offline smoke for directed onion nesting (P2P_FORWARD).
      + * Run: node examples/onion-forward.js
      + *
      + * Network path: Peer#sendOnion / Hub SendOnion — see docs/P2P_FORWARD.md
      + */
      +
      +const Key = require('../types/key');
      +const Message = require('../types/message');
      +const {
      +  wrapOnionPath,
      +  tryDecodeForward,
      +  xOnlyFromKey
      +} = require('../functions/fabricOnion');
      +
      +const origin = new Key();
      +const relay = new Key();
      +const dest = new Key();
      +
      +const payload = Message.fromVector(['P2P_CHAT_MESSAGE', 'hello via onion']);
      +payload.signWithKey(origin);
      +
      +const outer = wrapOnionPath({
      +  path: [xOnlyFromKey(relay), xOnlyFromKey(dest)],
      +  payload,
      +  key: origin
      +});
      +
      +let layer = tryDecodeForward(outer);
      +console.log('hop1 nextPeer', layer.nextPeer.toString('hex').slice(0, 16) + '…', 'ttl=', layer.ttl);
      +layer = tryDecodeForward(Message.fromBuffer(layer.inner));
      +console.log('hop2 nextPeer', layer.nextPeer.toString('hex').slice(0, 16) + '…', 'ttl=', layer.ttl);
      +const inner = Message.fromBuffer(layer.inner);
      +console.log('payload', inner.type, inner.data.toString('utf8'));
      +console.log('ok — nested P2P_FORWARD layers round-trip');
      +
      +
      + +
    • + +
    +
    + + + \ No newline at end of file diff --git a/assets/examples/oracle.html b/assets/examples/oracle.html index 8fca79666..4a7db054f 100644 --- a/assets/examples/oracle.html +++ b/assets/examples/oracle.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/p2pkh.html b/assets/examples/p2pkh.html index cd4bf9240..3f3c6be77 100644 --- a/assets/examples/p2pkh.html +++ b/assets/examples/p2pkh.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/relay.html b/assets/examples/relay.html index 8be384bbd..4a89ec477 100644 --- a/assets/examples/relay.html +++ b/assets/examples/relay.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -165,6 +180,7 @@

    relay.js

    'use strict';
     
     require('debug-trace')({ always: true });
    +const { setMaxListeners } = require('events');
     
     const SEEDS = {
       origin: 'unknown burger engine plug teach spot squeeze fringe ethics skate riot brand hurry melody double then trumpet impulse lesson inflict enlist eager region ride',
    @@ -195,6 +211,7 @@ 

    relay.js

    const Message = require('../types/message'); async function main () { + setMaxListeners(0); const swarm = { origin: new Peer({ listen: true, @@ -237,12 +254,14 @@

    relay.js

      swarm.origin.on('peer:candidate', async function (peer) {
         console.log('[EXAMPLES:RELAY]', 'Origin Peer emitted "peer:candidate" event:', peer);
    -
         if (peer.id === DESTINATION_ID) {
    -      console.warn('[EXAMPLES:RELAY]', 'Peer event was destination peer!');
    -      console.warn('[EXAMPLES:RELAY]', 'Origin node peers:', swarm.origin.peers);
    -      console.warn('[EXAMPLES:RELAY]', 'Relay node peers:', swarm.relayer.peers);
    -      console.warn('[EXAMPLES:RELAY]', 'Destination node peers:', swarm.destination.peers);
    + console.warn('[EXAMPLES:RELAY]', 'Destination candidate discovered.'); + } + }); + + swarm.destination.on('message', async function handleSwarmMessage (msg) { + console.log('[EXAMPLES:RELAY]', 'Got message on destination:', msg); + });
    @@ -255,20 +274,23 @@

    relay.js

    §
    -

    Send Message

    +

    Start component services

    -
          let message = Message.fromVector(['Generic', 'Hello, world!']);
    -      await swarm.origin.broadcast(message);
    -    }
    -  });
    +            
      console.log('[EXAMPLES:RELAY]', 'Starting origin Peer...');
    +  await swarm.origin.start();
    +  console.log('[EXAMPLES:RELAY]', 'Origin Peer started!');
     
    -  swarm.destination.on('message', async function handleSwarmMessage (msg) {
    -    console.log('[EXAMPLES:RELAY]', 'Got message on destination:', msg);
    -  });
    + console.log('[EXAMPLES:RELAY]', 'Starting relayer Peer...'); + await swarm.relayer.start(); + console.log('[EXAMPLES:RELAY]', 'Relayer Peer started!'); + + console.log('[EXAMPLES:RELAY]', 'Starting destination Peer...'); + await swarm.destination.start(); + console.log('[EXAMPLES:RELAY]', 'Destination Peer started!');
    @@ -281,23 +303,25 @@

    relay.js

    §
    -

    Start component services

    +

    Allow peering to settle before broadcasting.

    -
      console.log('[EXAMPLES:RELAY]', 'Starting origin Peer...');
    -  await swarm.origin.start();
    -  console.log('[EXAMPLES:RELAY]', 'Origin Peer started!');
    +            
      await new Promise((resolve) => setTimeout(resolve, 1200));
     
    -  console.log('[EXAMPLES:RELAY]', 'Starting relayer Peer...');
    -  await swarm.relayer.start();
    -  console.log('[EXAMPLES:RELAY]', 'Relayer Peer started!');
    +  const message = Message.fromVector(['P2P_BASE_MESSAGE', JSON.stringify({
    +    type: 'RelayExample',
    +    object: { text: 'Hello, world!' }
    +  })]);
    +  await swarm.origin.broadcast(message.toBuffer());
     
    -  console.log('[EXAMPLES:RELAY]', 'Starting destination Peer...');
    -  await swarm.destination.start();
    -  console.log('[EXAMPLES:RELAY]', 'Destination Peer started!');
    +  await new Promise((resolve) => setTimeout(resolve, 1500));
    +
    +  await swarm.destination.stop();
    +  await swarm.relayer.stop();
    +  await swarm.origin.stop();
     }
     
     main().catch(function exceptionHandler (exception) {
    diff --git a/assets/examples/service.html b/assets/examples/service.html
    index 4732b965d..cf0809b35 100644
    --- a/assets/examples/service.html
    +++ b/assets/examples/service.html
    @@ -62,6 +62,16 @@
                   
     
     
    +              
    +                examples/fabric-basic-usage.js
    +              
    +
    +
    +              
    +                examples/fabric-demo.js
    +              
    +
    +
                   
                     examples/fabric.js
                   
    @@ -102,6 +112,11 @@
                   
     
     
    +              
    +                examples/onion-forward.js
    +              
    +
    +
                   
                     examples/oracle.js
                   
    @@ -169,12 +184,16 @@ 

    service.js

    /** Minimal URI → response cache (replaces legacy Stash type). */ const stash = new Map(); +if (typeof self === 'undefined' || typeof self.addEventListener !== 'function') { + console.log('[EXAMPLES:SERVICE]', 'This example targets a Service Worker context (run in browser).'); + process.exit(0); +} + self.addEventListener('message', function (e) { e.source.postMessage('[GUARDIAN]', 'Hello! Your message was: ' + e.data); }); self.addEventListener('fetch', async function (event) { - const self = this; console.log('[GUARDIAN]', 'request:', event); const path = event.request.url; diff --git a/assets/examples/store.html b/assets/examples/store.html index ea76376e8..62d7e77f9 100644 --- a/assets/examples/store.html +++ b/assets/examples/store.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js diff --git a/assets/examples/swarm.html b/assets/examples/swarm.html index 749893d60..999b8f678 100644 --- a/assets/examples/swarm.html +++ b/assets/examples/swarm.html @@ -62,6 +62,16 @@ + + examples/fabric-basic-usage.js + + + + + examples/fabric-demo.js + + + examples/fabric.js @@ -102,6 +112,11 @@ + + examples/onion-forward.js + + + examples/oracle.js @@ -181,7 +196,8 @@

    swarm.js

    -
    const Peer = require('../types/peer');
    +            
    const { setMaxListeners } = require('events');
    +const Peer = require('../types/peer');
     const { Swarm } = require('../types/peer');
     const Message = require('../types/message');
    @@ -219,6 +235,25 @@

    swarm.js

    §
    +

    Swarm demos fan out listeners during NOISE handshakes.

    + +
    + +
    +
    +
      setMaxListeners(0);
    +
    +
    + +
  • + + +
  • +
    + +
    + § +

    Create a Hub (seeder peer) and a Swarm (peer cluster)

    @@ -234,11 +269,11 @@

    swarm.js

  • -
  • +
  • - § + §

    Listeners

    @@ -259,11 +294,11 @@

    swarm.js

  • -
  • +
  • - § + §

    Start component services

    @@ -288,11 +323,11 @@

    swarm.js

  • -
  • +
  • - § + §

    Connect downstream “client” Peer console.log(‘[EXAMPLES:SWARM]’, ‘Connecting downstream Peer to Swarm…’); @@ -303,11 +338,11 @@

    swarm.js

  • -
  • +
  • - § + §

    TODO: create entities on seed node TODO: receive entities from seed node @@ -315,25 +350,36 @@

    swarm.js

    -
  • - - -
  • -
    - -
    - § -
    -

    Send Regular Updates (outside of internal ping/pong)

    - -
    -
    -
      let heartbeat = setInterval(function () {
    -    console.warn('[EXAMPLES:SWARM]', 'Starting to send interval message...');
    -    let message = Message.fromVector(['Generic', Date.now().toString()]);
    -    console.log('[EXAMPLES:SWARM]', 'Sending :', message.raw);
    +
    +  const stopWithTimeout = async (instance, label, timeoutMs = 1200) => {
    +    if (!instance || typeof instance.stop !== 'function') return;
    +    try {
    +      await Promise.race([
    +        instance.stop(),
    +        new Promise((resolve) => setTimeout(resolve, timeoutMs))
    +      ]);
    +    } catch (error) {
    +      console.warn('[EXAMPLES:SWARM]', `${label} stop warning:`, error.message);
    +    }
    +  };
    +
    +  const shutdown = async () => {
    +    await stopWithTimeout(downstream, 'Downstream');
    +    await stopWithTimeout(swarm, 'Swarm');
    +    await stopWithTimeout(seeder, 'Seeder');
    +  };
    +
    +  console.warn('[EXAMPLES:SWARM]', 'Sending one swarm message...');
    +  const message = Message.fromVector(['P2P_BASE_MESSAGE', JSON.stringify({
    +    type: 'SwarmExample',
    +    object: { at: Date.now() }
    +  })]);
    +  seeder.broadcast(message.toBuffer());
    +
    +  await new Promise((resolve) => setTimeout(resolve, 1500));
    +  await shutdown();
    @@ -346,33 +392,13 @@

    swarm.js

    §
    -

    Send interval message through seed node

    - - - -
    -
    -
        seeder.broadcast(message);
    -
    -
    - -
  • - - -
  • -
    - -
    - § -
    -

    Send interval message through swarm agent - swarm.broadcast(message);

    +

    Some peer transports may keep sockets around briefly in demos.

    -
      }, 5000);
    +            
      if (require.main === module) process.exit(0);
     }
     
     main().catch(function exceptionHandler (exception) {
    diff --git a/assets/examples/witness.html b/assets/examples/witness.html
    index c0805137c..a14e76fcb 100644
    --- a/assets/examples/witness.html
    +++ b/assets/examples/witness.html
    @@ -62,6 +62,16 @@
                   
     
     
    +              
    +                examples/fabric-basic-usage.js
    +              
    +
    +
    +              
    +                examples/fabric-demo.js
    +              
    +
    +
                   
                     examples/fabric.js
                   
    @@ -102,6 +112,11 @@
                   
     
     
    +              
    +                examples/onion-forward.js
    +              
    +
    +
                   
                     examples/oracle.js
                   
    diff --git a/docs/Actor.html b/docs/Actor.html
    index 062a8d6b6..386e8ae2f 100644
    --- a/docs/Actor.html
    +++ b/docs/Actor.html
    @@ -33,9 +33,2718 @@ 

    Class: Actor

    -

    (protected) Actor(actoropt) → {Actor}

    +

    (protected) Actor()

    + +
    Base Actor: JSON-shaped _state.content observed with + fast-json-patch; Actor#commit turns diffs into Actor#history and emits + commit plus message (type: 'ActorMessage', data.type: 'Changes'). + Identity — Actor#id is SHA256(hex) of the 32-byte preimage buffer; Actor#preimage is + SHA256(UTF-8) of pretty-printed Actor#toGenericMessage { type, object } with sorted keys + (Actor#toObject); uses Hash256.compute. Treat id as a content address, not an + arbitrary app string hash. Wire traffic — see Message (extends Actor, AMP). Same narrative as + DEVELOPERS.md (Actor and Message) and @fileoverview above (also on + types_actor.js.html source page). +
    + + +
    + +
    +
    + + + + +

    Constructor

    + + + +

    (protected) new Actor()

    + + + + + + + + + + + + + + + + + + +
    Properties:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    id + + + String + + + + 64-char hex: SHA256 of the 32-byte digest represented by Actor#preimage.
    preimage + + + String + + + + 64-char hex: SHA256 of UTF-8 pretty JSON of Actor#toGenericMessage.
    + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Actor#event:commit
    • + +
    • message Emits structured objects; on Actor#commit,event: type: 'ActorMessage' with patch metadata (not necessarily a Message AMP instance).
    • +
    + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + +
      +
    • EventEmitter
    • +
    + + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (static) fromAny(input) → {Actor}

    + + + + + + +
    + Create an Actor from a variety of formats. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Target Object to create.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (static) randomBytes(countopt) → {Buffer}

    + + + + + + +
    + Get a number of random bytes from the runtime environment. +
    + + + + + + + + + +
    Parameters:
    -
    Generic Fabric Actor.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    count + + + Number + + + + + + <optional>
    + + + + + +
    + + 32 + + Number of random bytes to retrieve.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + The random bytes. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +
    + + + + + + + + + +
    + +
    + +

    Actor(actoropt) → {Actor}

    @@ -46,11 +2755,9 @@

    (protected) Constructor

    - -

    (protected) new Actor(actoropt) → {Actor}

    +

    new Actor(actoropt) → {Actor}

    @@ -179,7 +2886,7 @@
    Properties
    - BIP24 Mnemonic to use as a seed phrase. + Optional mnemonic or seed string stored into state (see BIP39 / wallet docs — not validated here). @@ -264,81 +2971,6 @@
    Properties
    -
    Properties:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    id - - - String - - - - Unique identifier for this Actor (id === SHA256(preimage)).
    preimage - - - String - - - - Input hash for the `id` property (preimage === SHA256(ActorState)).
    - - - -
    @@ -370,7 +3002,7 @@
    Properties:
    @@ -387,11 +3019,6 @@
    Properties:
    -
    Fires:
    -
      -
    • event:message Fabric Message objects.
    • -
    - @@ -558,7 +3185,7 @@
    Parameters:
    @@ -721,7 +3348,7 @@
    Parameters:
    @@ -835,7 +3462,7 @@

    commit @@ -949,7 +3576,7 @@

    export @@ -1112,7 +3739,7 @@
    Parameters:
    @@ -1226,7 +3853,7 @@

    pause @@ -1340,7 +3967,7 @@

    serialize @@ -1522,7 +4149,7 @@
    Parameters:
    @@ -1636,7 +4263,7 @@

    sign @@ -1807,7 +4434,7 @@
    Parameters:
    @@ -1921,7 +4548,7 @@

    toBuffer @@ -2103,7 +4730,7 @@
    Parameters:
    @@ -2226,7 +4853,7 @@

    toObject @@ -2336,7 +4963,7 @@

    unpause @@ -2519,7 +5146,7 @@
    Parameters:
    @@ -2682,7 +5309,7 @@
    Parameters:
    @@ -2865,7 +5492,7 @@
    Parameters:
    @@ -2938,14 +5565,18 @@

    Classes

    Global


    diff --git a/docs/Bitcoin.html b/docs/Bitcoin.html index 18be7d0d6..5f461d511 100644 --- a/docs/Bitcoin.html +++ b/docs/Bitcoin.html @@ -325,7 +325,7 @@
    Properties
    @@ -434,7 +434,7 @@

    UAString @@ -500,7 +500,7 @@

    height @@ -566,7 +566,7 @@

    tip @@ -708,7 +708,7 @@
    Parameters:
    @@ -970,7 +970,7 @@
    Parameters:
    @@ -1030,6 +1030,174 @@
    Returns:
    +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + +

    (async) _buildPSBT(options) → {PSBT}

    @@ -1133,7 +1301,7 @@
    Parameters:
    @@ -1247,7 +1415,7 @@

    (async) _c
    @@ -1388,7 +1556,7 @@

    Parameters:
    @@ -1456,7 +1624,7 @@

    (async) <
    - Hand a Block message as supplied by an SPV client. + Hand a Block message as supplied by an SPV client.
    @@ -1551,7 +1719,7 @@
    Parameters:
    @@ -1639,7 +1807,7 @@
    Parameters:
    - Block + Block @@ -1692,7 +1860,7 @@
    Parameters:
    @@ -1833,7 +2001,7 @@
    Parameters:
    @@ -1975,7 +2143,7 @@
    Parameters:
    @@ -2191,7 +2359,7 @@
    Parameters:
    @@ -2355,7 +2523,7 @@
    Parameters:
    @@ -2517,7 +2685,7 @@
    Parameters:
    @@ -2730,7 +2898,7 @@
    Properties
    @@ -2790,7 +2958,7 @@
    Returns:
    -

    (async) _registerActor(actor) → {Promise}

    +

    _readObject(input) → {Object}

    @@ -2798,7 +2966,7 @@

    (async) - Register an Actor with the Service. + Parse an Object into a corresponding Fabric state.

    @@ -2834,7 +3002,7 @@
    Parameters:
    - actor + input @@ -2850,7 +3018,7 @@
    Parameters:
    - Instance of the Actor. + Object to read as input. @@ -2875,7 +3043,7 @@
    Parameters:
    @@ -2902,7 +3070,7 @@
    Parameters:
    @@ -2933,7 +3101,7 @@
    Returns:
    - Resolves upon successful registration. + Fabric state.
    @@ -2944,7 +3112,7 @@
    Returns:
    - Promise + Object
    @@ -2962,7 +3130,7 @@
    Returns:
    -

    (async) _requestBlockAtHeight(height) → {Object}

    +

    (async) _registerActor(actor) → {Promise}

    @@ -2970,7 +3138,7 @@

    (async)
    - Retrieve the equivalent to `getblockhash` from Bitcoin Core. + Register an Actor with the Service.
    @@ -3006,13 +3174,13 @@
    Parameters:
    - height + actor - Number + Object @@ -3022,7 +3190,7 @@
    Parameters:
    - Height of block to retrieve. + Instance of the Actor. @@ -3043,6 +3211,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -3065,7 +3242,7 @@
    Parameters:
    @@ -3096,7 +3273,7 @@
    Returns:
    - The block hash. + Resolves upon successful registration.
    @@ -3107,7 +3284,7 @@
    Returns:
    - Object + Promise
    @@ -3125,7 +3302,7 @@
    Returns:
    -

    (async) _send(message)

    +

    (async) _requestBlockAtHeight(height) → {Object}

    @@ -3133,7 +3310,7 @@

    (async) _send - Sends a message. + Retrieve the equivalent to `getblockhash` from Bitcoin Core.

    @@ -3169,13 +3346,13 @@
    Parameters:
    - message + height - Mixed + Number @@ -3185,7 +3362,7 @@
    Parameters:
    - Message to send. + Height of block to retrieve. @@ -3206,15 +3383,6 @@
    Parameters:
    -
    Overrides:
    -
    - -
    - @@ -3237,7 +3405,7 @@
    Parameters:
    @@ -3264,6 +3432,28 @@
    Parameters:
    +
    Returns:
    + + +
    + The block hash. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + @@ -3275,7 +3465,7 @@
    Parameters:
    -

    (async) _subscribeToShard(shard)

    +

    (async) _send(message)

    @@ -3283,7 +3473,7 @@

    (async) - Attach event handlers for a supplied list of addresses. + Sends a message. @@ -3319,13 +3509,13 @@
    Parameters:
    - shard + message - Shard + Mixed @@ -3335,7 +3525,7 @@
    Parameters:
    - List of addresses to monitor. + Message to send. @@ -3356,6 +3546,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -3378,7 +3577,7 @@
    Parameters:
    @@ -3416,7 +3615,4487 @@
    Parameters:
    -

    (async) applyP2pAddNodes(peers, commandopt) → {Promise.<Array.<string>>}

    +

    (async) _subscribeToShard(shard)

    + + + + + + +
    + Attach event handlers for a supplied list of addresses. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    shard + + + Shard + + + + List of addresses to monitor.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) applyP2pAddNodes(peers, commandopt) → {Promise.<Array.<string>>}

    + + + + + + +
    + Connect to Bitcoin P2P peers via RPC (`addnode`). Best-effort per peer; failures emit `warning`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    peers + + + Array.<string> + + + + + + + + + + + +
    command + + + string + + + + + + <optional>
    + + + + + +
    + + 'add' + + add | onetry | remove
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Peers successfully passed to `addnode` +
    + + + +
    +
    + Type +
    +
    + + Promise.<Array.<string>> + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    (async) broadcast(tx)

    + + + + + + +
    + Broadcast a transaction to the Bitcoin network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    tx + + + TX + + + + Bitcoin transaction
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(addr)

    + + + + + + +
    + Connect to a Fabric Peer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    addr + + + String + + + + Address to connect to.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) flushChainToSnapshot(snapshotBlockHash) → {Promise.<{ok: boolean, steps: number, snapshotBlockHash: string}>}

    + + + + + + +
    + Rewind the attached Bitcoin Core node to a known-good tip by repeatedly calling `invalidateblock` + on the current best block until `getbestblockhash` matches `snapshotBlockHash`. + Allowed on regtest, playnet, signet, testnet, testnet4 unless settings.flushChainAllowUnsafeNetworks. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    snapshotBlockHash + + + string + + + + 64-char hex block hash to keep as the active tip.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<{ok: boolean, steps: number, snapshotBlockHash: string}> + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    (async) getAddressInfo(address) → {Promise.<Object>}

    + + + + + + +
    + Blockchain explorer: fetch address info (balance, tx count, recent txs). + Requires `explorerBaseUrl` (Core has no generic address index over RPC alone). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    address + + + String + + + + Bitcoin address.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Address info { address, chain_stats, mempool_stats, recent_txs }. +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) getBlockInfo(hashOrHeight) → {Promise.<Object>}

    + + + + + + +
    + Blockchain explorer: fetch block info by hash or height. + Uses RPC when available; optional HTTP API when `explorerBaseUrl` is set. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    hashOrHeight + + + String + | + + Number + + + + Block hash (hex) or block height.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Block info { hash, height, time, txcount, size, ... }. +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) getTransactionInfo(txid) → {Promise.<Object>}

    + + + + + + +
    + Blockchain explorer: fetch transaction info by txid. + Uses RPC when available; optional HTTP API when `explorerBaseUrl` is set. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    txid + + + String + + + + Transaction ID (hex).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Transaction info. +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start()

    + + + + + + +
    + Start the Bitcoin service, including the initiation of outbound requests. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) stop()

    @@ -3424,7 +8103,99 @@

    (async) - Connect to Bitcoin P2P peers via RPC (`addnode`). Best-effort per peer; failures emit `warning`. + Stop the Bitcoin service. + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor.
    @@ -3452,8 +8223,6 @@
    Parameters:
    - Default - Description @@ -3464,13 +8233,13 @@
    Parameters:
    - peers + pipe - Array.<string> + TransformStream @@ -3479,6 +8248,8 @@
    Parameters:
    + <optional>
    + @@ -3487,56 +8258,136 @@
    Parameters:
    - - + Pipe to stream to. + - - + + - - command - +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + - string - - - <optional>
    +

    tick() → {Number}

    - +
    + Move forward one clock cycle. +
    - - 'add' - - add | onetry | remove - - - @@ -3552,6 +8403,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -3574,7 +8434,7 @@
    Parameters:
    @@ -3604,10 +8464,6 @@
    Parameters:
    Returns:
    -
    - Peers successfully passed to `addnode` -
    -
    @@ -3616,7 +8472,7 @@
    Returns:
    - Promise.<Array.<string>> + Number
    @@ -3634,7 +8490,7 @@
    Returns:
    -

    beat() → {Service}

    +

    toBuffer() → {Buffer}

    @@ -3642,7 +8498,7 @@

    beat - Compute latest state. + Casts the Actor to a normalized Buffer. @@ -3670,7 +8526,7 @@

    beat @@ -3697,7 +8553,7 @@

    beat @@ -3714,11 +8570,6 @@

    beatService + Buffer @@ -3758,7 +8609,7 @@

    Returns:
    -

    (async) broadcast(tx)

    +

    toGenericMessage(typeopt) → {Object}

    @@ -3766,7 +8617,10 @@

    (async) broa
    - Broadcast a transaction to the Bitcoin network. + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network.
    @@ -3790,7 +8644,11 @@

    Parameters:
    Type + Attributes + + + Default Description @@ -3802,53 +8660,51 @@
    Parameters:
    - tx + type - TX + String + + <optional>
    - Bitcoin transaction - - - - - - - - - - - -
    + + + 'FabricActorState' + + Logical message type string. + + + +
    @@ -3857,11 +8713,11 @@
    Parameters:
    -
    Source:
    -
    +
    Overrides:
    +
    @@ -3872,7 +8728,6 @@
    Parameters:
    -
    @@ -3885,86 +8740,96 @@
    Parameters:
    +
    Source:
    +
    + +
    + +
    See:
    +
    + +
    +
    -

    (async) connect(addr)

    -
    - Connect to a Fabric Peer. -
    +
    Returns:
    +
    + `{ type, object }` +
    +
    +
    + Type +
    +
    -
    Parameters:
    + Object - - - + + - - - - - - - - +

    toObject() → {Object}

    - +
    + Returns the Actor's current state as an Object. +
    - - - -
    NameTypeDescription
    addr - String - Address to connect to.
    @@ -3984,7 +8849,7 @@
    Parameters:
    @@ -4011,7 +8876,7 @@
    Parameters:
    @@ -4038,6 +8903,24 @@
    Parameters:
    +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + @@ -4049,7 +8932,7 @@
    Parameters:
    -

    get(path) → {Mixed}

    +

    trust(source) → {Service}

    @@ -4057,7 +8940,7 @@

    get - Retrieve a key from the State. + Explicitly trust all events from a known source. @@ -4093,13 +8976,13 @@
    Parameters:
    - path + source - Path + EventEmitter @@ -4109,7 +8992,7 @@
    Parameters:
    - Key to retrieve. + Emitter of events. @@ -4134,7 +9017,7 @@
    Parameters:
    @@ -4161,7 +9044,7 @@
    Parameters:
    @@ -4192,7 +9075,7 @@
    Returns:
    - Returns the target value if found, otherwise null. + Instance of Service after binding events.
    @@ -4203,7 +9086,7 @@
    Returns:
    - Mixed + Service
    @@ -4221,7 +9104,7 @@
    Returns:
    -

    (async) getAddressInfo(address) → {Promise.<Object>}

    +

    unpause() → {Actor}

    @@ -4229,8 +9112,7 @@

    (async) - Blockchain explorer: fetch address info (balance, tx count, recent txs). - Requires `explorerBaseUrl` (Core has no generic address index over RPC alone). + Toggles `status` property to unpaused. @@ -4241,55 +9123,6 @@

    (async) Parameters:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    address - - - String - - - - Bitcoin address.
    - - @@ -4303,6 +9136,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -4325,7 +9167,7 @@
    Parameters:
    @@ -4356,7 +9198,7 @@
    Returns:
    - Address info { address, chain_stats, mempool_stats, recent_txs }. + Instance of the Actor.
    @@ -4367,7 +9209,7 @@
    Returns:
    - Promise.<Object> + Actor
    @@ -4385,7 +9227,7 @@
    Returns:
    -

    (async) getBlockInfo(hashOrHeight) → {Promise.<Object>}

    +

    value(formatopt) → {Object}

    @@ -4393,8 +9235,7 @@

    (async) g
    - Blockchain explorer: fetch block info by hash or height. - Uses RPC when available; optional HTTP API when `explorerBaseUrl` is set. + Get the inner value of the Actor with an optional cast type.
    @@ -4418,7 +9259,11 @@

    Parameters:
    Type + Attributes + + + Default Description @@ -4430,16 +9275,24 @@
    Parameters:
    - hashOrHeight + format String - | - Number + + + + + + + + <optional>
    + + @@ -4447,9 +9300,14 @@
    Parameters:
    + + object - Block hash (hex) or block height. + + + + Cast the value to one of: `buffer, hex, json, string` @@ -4470,6 +9328,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -4492,7 +9359,7 @@
    Parameters:
    @@ -4523,7 +9390,7 @@
    Returns:
    - Block info { hash, height, time, txcount, size, ... }. + Inner value of the Actor as an Object, or cast to the requested `format`.
    @@ -4534,7 +9401,7 @@
    Returns:
    - Promise.<Object> + Object
    @@ -4552,7 +9419,7 @@
    Returns:
    -

    (async) getTransactionInfo(txid) → {Promise.<Object>}

    +

    when(event, method) → {EventEmitter}

    @@ -4560,8 +9427,7 @@

    (async) - Blockchain explorer: fetch transaction info by txid. - Uses RPC when available; optional HTTP API when `explorerBaseUrl` is set. + Bind a method to an event, with current state as the immutable context. @@ -4597,7 +9463,7 @@
    Parameters:
    - txid + event @@ -4613,7 +9479,30 @@
    Parameters:
    - Transaction ID (hex). + Name of the event upon which to execute `method` as a function. + + + + + + + method + + + + + + function + + + + + + + + + + Function to execute when named Event `event` is encountered. @@ -4634,6 +9523,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -4656,7 +9554,7 @@
    Parameters:
    @@ -4687,7 +9585,7 @@
    Returns:
    - Transaction info. + Instance of EventEmitter.
    @@ -4698,7 +9596,7 @@
    Returns:
    - Promise.<Object> + EventEmitter
    @@ -4716,7 +9614,7 @@
    Returns:
    -

    handler(message) → {Service}

    +

    (static) bitcoindChainDataDirSegment(network) → {string}

    @@ -4724,8 +9622,8 @@

    handler - Default route handler for an incoming message. Follows the Activity - Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ + Bitcoin Core chain data subdirectory under {@code -datadir} (empty string for mainnet cookie at datadir root). + Matches Core layout: `regtest/`, `testnet3/`, `signet/`, `testnet4/`, or root for mainnet. @@ -4761,13 +9659,13 @@
    Parameters:
    - message + network - Activity + string @@ -4777,7 +9675,7 @@
    Parameters:
    - Message object. + Fabric network name (mainnet, testnet, regtest, signet, testnet4, playnet, …). @@ -4798,15 +9696,6 @@
    Parameters:
    -
    Overrides:
    -
    - -
    - @@ -4829,7 +9718,7 @@
    Parameters:
    @@ -4859,10 +9748,6 @@
    Parameters:
    Returns:
    -
    - Chainable method. -
    -
    @@ -4871,7 +9756,7 @@
    Returns:
    - Service + string
    @@ -4889,7 +9774,7 @@
    Returns:
    -

    init()

    +

    (static) buildLocalCookieProbePaths(opts) → {Array.<string>}

    @@ -4897,8 +9782,7 @@

    init - Called by Web Components. - TODO: move to @fabric/http/types/spa + Ordered local cookie paths to probe for RPC (env override, project stores, Electron mirror, ~/.bitcoin, optional settings datadir). @@ -4909,12 +9793,60 @@

    init + + + Name -
    + Type + + + + + + Description + + + + + + + + + opts + + + + + + BitcoinLocalCookieProbeOpts + + + + + + + + + + + + + + + + + + + + + +
    @@ -4922,14 +9854,6 @@

    initOverrides: -
    - -
    @@ -4953,7 +9877,7 @@

    init @@ -4980,6 +9904,23 @@

    initArray.<string> + + + +

    + @@ -4991,16 +9932,13 @@

    initlock(durationopt) → {Boolean}

    +

    (static) buildRegtestCookiePathList(opts) → {Array.<string>}

    -
    - Attempt to acquire a lock for `duration` seconds. -
    @@ -5023,12 +9961,8 @@
    Parameters:
    Type - Attributes - - Default - Description @@ -5039,24 +9973,13 @@
    Parameters:
    - duration + opts - Number - - - - - - - - - <optional>
    - - + BitcoinRegtestCookieOpts @@ -5064,14 +9987,9 @@
    Parameters:
    - - - 1000 - - - Number of milliseconds to hold lock. + @@ -5092,14 +10010,6 @@
    Parameters:
    -
    Overrides:
    -
    - -
    @@ -5108,6 +10018,12 @@
    Parameters:
    +
    Deprecated:
    +
    +
      +
    • Use #buildLocalCookieProbePaths with `network: 'regtest'`.
    • +
    +
    @@ -5123,7 +10039,7 @@
    Parameters:
    @@ -5153,10 +10069,6 @@
    Parameters:
    Returns:
    -
    - true if locked, false if unable to lock. -
    -
    @@ -5165,7 +10077,7 @@
    Returns:
    - Boolean + Array.<string>
    @@ -5183,7 +10095,7 @@
    Returns:
    -

    (async) route(msg) → {Promise}

    +

    (static) cookiePathForBitcoind(datadirRoot, network) → {string}

    @@ -5191,7 +10103,7 @@

    (async) route - Resolve a State from a particular Message object. + `.cookie` path under a resolved bitcoind datadir root for the given Fabric network. @@ -5227,13 +10139,13 @@
    Parameters:
    - msg + datadirRoot - Message + string @@ -5243,7 +10155,30 @@
    Parameters:
    - Explicit Fabric Message. + Absolute or project-relative resolved datadir (Core {@code -datadir} value). + + + + + + + network + + + + + + string + + + + + + + + + + @@ -5264,15 +10199,6 @@
    Parameters:
    -
    Overrides:
    -
    - -
    - @@ -5295,7 +10221,7 @@
    Parameters:
    @@ -5325,10 +10251,6 @@
    Parameters:
    Returns:
    -
    - Resolves with resulting State. -
    -
    @@ -5337,7 +10259,7 @@
    Returns:
    - Promise + string
    @@ -5355,7 +10277,7 @@
    Returns:
    -

    (async) send(channel, message) → {Service}

    +

    (static) cookiePathForChainSubtree(datadirRoot, chainSubdir) → {string}

    @@ -5363,7 +10285,8 @@

    (async) send - Send a message to a channel. + Cookie file under explicit chain subdirectory (empty string = mainnet-style datadir/.cookie only). + Prefer #cookiePathForBitcoind when you have a Fabric network name. @@ -5399,13 +10322,13 @@
    Parameters:
    - channel + datadirRoot - String + string @@ -5415,20 +10338,20 @@
    Parameters:
    - Channel name to which the message will be sent. + - message + chainSubdir - String + string @@ -5438,7 +10361,7 @@
    Parameters:
    - Content of the message to send. + e.g. `regtest`, `signet`, or `''` @@ -5459,21 +10382,12 @@
    Parameters:
    -
    Overrides:
    -
    - -
    - - - - - - - + + + + + + @@ -5490,7 +10404,7 @@
    Parameters:
    @@ -5520,10 +10434,6 @@
    Parameters:
    Returns:
    -
    - Chainable method. -
    -
    @@ -5532,7 +10442,7 @@
    Returns:
    - Service + string
    @@ -5550,7 +10460,7 @@
    Returns:
    -

    set(path) → {Mixed}

    +

    (static) defaultStoresRelativeDirsForProbe(network, constraintsopt) → {Array.<string>}

    @@ -5558,7 +10468,7 @@

    set - Set a key in the State to a particular value. + Typical `stores/…` paths under the project for the network (for cookie discovery before node spawn). @@ -5582,6 +10492,8 @@
    Parameters:
    Type + Attributes + @@ -5594,23 +10506,64 @@
    Parameters:
    - path + network - Path + string + + - Key to retrieve. + + + + + + + + + + + + + + constraints + + + + + + BitcoinCookieProbeConstraints + + + + + + + + + <optional>
    + + + + + + + + + + + @@ -5631,15 +10584,6 @@
    Parameters:
    -
    Overrides:
    -
    - -
    - @@ -5662,7 +10606,7 @@
    Parameters:
    @@ -5700,7 +10644,7 @@
    Returns:
    - Mixed + Array.<string>
    @@ -5718,7 +10662,7 @@
    Returns:
    -

    (async) start()

    +

    (static) parentDirNameForCookieProbe(cookiePath) → {string}

    @@ -5726,7 +10670,7 @@

    (async) start - Start the Bitcoin service, including the initiation of outbound requests. + Parent directory name of `.cookie` (for probe logging). @@ -5737,68 +10681,60 @@

    (async) startParameters:

    + + + - -
    - - - - - - - - -
    Overrides:
    -
    - -
    - +
    + + + + + + + + + + - + +
    NameTypeDescription
    cookiePath + string -
    Source:
    -
    - -
    +
    +
    @@ -5819,16 +10755,20 @@

    (async) start(async) stop()

    -
    - Stop the Bitcoin service. -
    +
    Source:
    +
    + +
    @@ -5836,13 +10776,13 @@

    (async) stop -
    @@ -5852,13 +10792,22 @@

    (async) stopReturns:

    +
    +
    + Type +
    +
    + string +
    +
    @@ -5869,24 +10818,20 @@

    (async) stopSource: -
    - -
    +

    (static) resolveBitcoinCookieFileForLocalRead(filePath) → {string|null}

    -

    +
    + Resolve {@code FABRIC_BITCOIN_COOKIE_FILE}-style paths: normalize, bound length, and keep relative paths + inside the process cwd (absolute paths allowed for explicit operator overrides). +
    @@ -5896,38 +10841,53 @@

    (async) stopParameters:

    + + + + + + + + + + + -

    tick() → {Number}

    + + + + +
    NameTypeDescription
    filePath + string -
    - Move forward one clock cycle. -
    +
    @@ -5943,15 +10903,6 @@

    tickOverrides: -
    - -
    - @@ -5974,7 +10925,7 @@

    tick @@ -6012,7 +10963,10 @@
    Returns:
    - Number + string + | + + null
    @@ -6030,7 +10984,7 @@
    Returns:
    -

    trust(source) → {Service}

    +

    (static) resolveBitcoinDatadirForLocalAccess(datadir) → {string|null}

    @@ -6038,7 +10992,8 @@

    trust - Explicitly trust all events from a known source. + Resolve a configured bitcoind datadir for local cookie discovery. Relative paths are cwd-anchored + and must not escape the project root; absolute paths are normalized as-is. @@ -6074,13 +11029,13 @@
    Parameters:
    - source + datadir - EventEmitter + string @@ -6090,7 +11045,7 @@
    Parameters:
    - Emitter of events. + @@ -6111,15 +11066,6 @@
    Parameters:
    -
    Overrides:
    -
    - -
    - @@ -6142,7 +11088,7 @@
    Parameters:
    @@ -6172,10 +11118,6 @@
    Parameters:
    Returns:
    -
    - Instance of Service after binding events. -
    -
    @@ -6184,7 +11126,10 @@
    Returns:
    - Service + string + | + + null
    @@ -6202,7 +11147,7 @@
    Returns:
    -

    when(event, method) → {EventEmitter}

    +

    (async, static) tryReadRpcCookieFileCredentials(cookiePath) → {Promise.<({username: string, password: string}|null)>}

    @@ -6210,7 +11155,7 @@

    when - Bind a method to an event, with current state as the immutable context. + Read Bitcoin Core {@code .cookie} (user:password) using async I/O. Path must already be resolved/normalized. @@ -6246,36 +11191,13 @@
    Parameters:
    - event - - - - - - String - - - - - - - - - - Name of the event upon which to execute `method` as a function. - - - - - - - method + cookiePath - function + string @@ -6285,7 +11207,7 @@
    Parameters:
    - Function to execute when named Event `event` is encountered. + @@ -6306,15 +11228,6 @@
    Parameters:
    -
    Overrides:
    -
    - -
    - @@ -6337,7 +11250,7 @@
    Parameters:
    @@ -6367,10 +11280,6 @@
    Parameters:
    Returns:
    -
    - Instance of EventEmitter. -
    -
    @@ -6379,7 +11288,7 @@
    Returns:
    - EventEmitter + Promise.<({username: string, password: string}|null)>
    @@ -6410,14 +11319,18 @@

    Classes

    Global


    diff --git a/docs/Block.html b/docs/Block.html new file mode 100644 index 000000000..f2e852b22 --- /dev/null +++ b/docs/Block.html @@ -0,0 +1,1417 @@ + + + + + + Class: Block · Docs + + + + + + + + + +
    +

    Class: Block

    + + + + +
    + +
    + +

    Block(inputopt)

    + + +
    + +
    +
    + + + + + + +

    new Block(inputopt)

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + +

    Members

    + + + +

    clock

    + + + + +
    + Alias used by Beacon codecs / tests. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    payload

    + + + + +
    + Beacon / legacy entry alias for `data`. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    addSignature(pubkey, sig) → {Block}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    pubkey + + + string + + + +
    sig + + + string + | + + Buffer + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Block + + +
    +
    + + + + + + + + + + + + + +

    sign(key) → {string}

    + + + + + + +
    + Author Schnorr over signingString (gossip). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + Key + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + signature hex +
    + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    toRecord() → {object}

    + + + + + + +
    + JSON-safe ledger record. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + + +
    +
    + + + + + + + + + + + + + +

    validate(optsopt) → {Object}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    consensus + + + string + + + + + + <optional>
    + + + + + +
    validators + + + Array.<string> + + + + + + <optional>
    + + + + + +
    threshold + + + number + + + + + + <optional>
    + + + + + +
    bits + + + number + | + + null + + + + + + <optional>
    + + + + + +
    requireParent + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (static) fromRecord(record) → {Block}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    record + + + object + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Block + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/Bond.html b/docs/Bond.html new file mode 100644 index 000000000..f66de973f --- /dev/null +++ b/docs/Bond.html @@ -0,0 +1,6643 @@ + + + + + + Class: Bond · Docs + + + + + + + + + +
    +

    Class: Bond

    + + + + +
    + +
    + +

    Bond()

    + + +
    + +
    +
    + + + + + + +

    new Bond()

    + + + + + + +
    + On-chain or logical bond / stake terms layered on Contract. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + + String + + + + + + + + + + + + Path to store the value at.
    value + + + Object + + + + + + + + + + + + Document to store.
    commit + + + Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + + Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _taprootPolicyInputs(overridesopt) → {object}

    + + + + + + +
    + Shared spend-policy inputs for #toTaprootContract. + Subclasses (e.g. Federation) may override to supply validators from state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    overrides + + + object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + + Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    deploy() → {String}

    + + + + + + +
    + Deploys the contract. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Message ID. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    start() → {Contract}

    + + + + + + +
    + Start the Contract. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + State "STARTED" iteration of the Contract. +
    + + + +
    +
    + Type +
    +
    + + Contract + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    toAddress(networkopt) → {string}

    + + + + + + +
    + Bech32m P2TR address for this contract's spend policy. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    network + + + string + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Service}

    + + + + + + +
    + Explicitly trust all events from a known source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Emitter of events.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of Service after binding events. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    when(event, method) → {EventEmitter}

    + + + + + + +
    + Bind a method to an event, with current state as the immutable context. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    event + + + String + + + + Name of the event upon which to execute `method` as a function.
    method + + + function + + + + Function to execute when named Event `event` is encountered.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of EventEmitter. +
    + + + +
    +
    + Type +
    +
    + + EventEmitter + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/CLI.html b/docs/CLI.html deleted file mode 100644 index 9fabcc208..000000000 --- a/docs/CLI.html +++ /dev/null @@ -1,695 +0,0 @@ - - - - - - Class: CLI · Docs - - - - - - - - - -
    -

    Class: CLI

    - - - - -
    - -
    - -

    CLI(settingsopt)

    - -
    Provides a Command Line Interface (CLI) for interacting with - the Fabric network using a terminal emulator.
    - - -
    - -
    -
    - - - - -

    Constructor

    - - - -

    new CLI(settingsopt)

    - - - - - - -
    - Create a terminal-based interface for a User. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeAttributesDescription
    settings - - - Object - - - - - - <optional>
    - - - - - -
    Configuration values. -
    Properties
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeAttributesDescription
    currencies - - - Array - - - - - - <optional>
    - - - - - -
    List of currencies to support.
    - -
    - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - -

    Methods

    - - - - - - - -

    (async) _handleGrantCommand(params)

    - - - - - - -
    - Creates a token for the target signer with a provided role and some optional data. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    params - - - Array - - - - Parameters array.
    - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -

    (async) start()

    - - - - - - -
    - Starts (and renders) the CLI. -
    - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -

    (async) stop()

    - - - - - - -
    - Disconnect all interfaces and exit the process. -
    - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -
    - -
    - - - - - - \ No newline at end of file diff --git a/docs/Chain.html b/docs/Chain.html index 8e46cbb23..cb008f476 100644 --- a/docs/Chain.html +++ b/docs/Chain.html @@ -33,7 +33,7 @@

    Class: Chain

    -

    Chain(genesis)

    +

    Chain(originopt)

    Chain.
    @@ -50,17 +50,13 @@

    Constructor

    -

    new Chain(genesis)

    +

    new Chain(originopt)

    -
    - Holds an immutable chain of events. -
    - @@ -82,6 +78,8 @@
    Parameters:
    Type + Attributes + @@ -94,113 +92,237 @@
    Parameters:
    - genesis + origin - Vector + Object + + <optional>
    - Initial state for the chain of events. - - - + + +
    Properties
    -
    Properties:
    + + + + -
    Name
    - - - + - + + + + - - - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - Map + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeTypeAttributesDescription
    Description
    consensusname - + String - String + + <optional>
    -
    Current name.
    `pow` (default), `federation`, or `gossip`
    seal + + + String + + + + + + <optional>
    + + + + + +
    Deprecated alias for consensus (`block`→`pow`)
    entries + + + Array + + + + + + <optional>
    -
    indices + Seed block records (federation/gossip)
    blocks + + + Array + + + + + + <optional>
    + + + + + +
    Seed block records
    + + + + + + + - +
    Properties:
    + + + + + + + + + + + + + + + + + + + - + + @@ -251,7 +373,7 @@
    Properties:
    @@ -299,6 +421,718 @@
    Properties:
    +

    Members

    + + + +

    entries

    + + + + +
    + Cloned block records (federation/gossip). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    seal

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Use consensusMode
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    _isEntrySeal()

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Yes
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    append() → {Promise.<Chain>|object}

    + + + + + + +
    + Append a Block (or Block-shaped object). + Policy chains return the tip view synchronously; pow returns a Promise. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Chain> + | + + object + + +
    +
    + + + + + + + + + + + + + +

    (static) create(optsopt) → {Chain}

    + + + + + + + + + + + + + + +
    Parameters:
    + + +
    NameTypeDescription
    storageconsensus - Storage + String @@ -210,7 +332,7 @@
    Properties:
    -
    `pow` | `federation` | `gossip`
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    consensus + + + string + + + + + + <optional>
    + + + + + +
    seal + + + string + + + + + + <optional>
    + + + + + +
    genesis + + + Object + + + + + + <optional>
    + + + + + +
    entries + + + Array.<Object> + + + + + + <optional>
    + + + + + +
    blocks + + + Array.<Object> + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Chain + + +
    +
    + + + + + + + @@ -318,14 +1152,18 @@

    Classes

    Global


    diff --git a/docs/Channel.html b/docs/Channel.html index 61e39ab95..ddd91ade7 100644 --- a/docs/Channel.html +++ b/docs/Channel.html @@ -33,15 +33,12 @@

    Class: Channel

    -

    Channel(settingsopt)

    - -
    The Channel is a encrypted connection with a member of your - Peer group, with some amount of $BTC bonded and paid for each - correctly-validated message. +

    Channel()

    - Channels in Fabric are powerful tools for application development, as they - can empower users with income opportunities in exchange for delivering - service to the network. +
    Payment / capacity channel between peers: balances (incoming / + outgoing), counterparty handle, optional asset caps (MAX_CHANNEL_VALUE). Extends + StateActor. Wording below is product-oriented; + wire safety still depends on the Lightning/Bitcoin services you attach, not this object alone.
    @@ -57,17 +54,13 @@

    Constructor

    -

    new Channel(settingsopt)

    +

    new Channel()

    -
    - Creates a channel between two peers. - of many transactions over time, to be settled on-chain later. -
    @@ -77,72 +70,58 @@

    new ChannelParameters:

    - - - - +
    -
    - - - - - - - - - - - - -
    NameTypeAttributesDescription
    settings - Object - - <optional>
    +
    Source:
    +
    + +
    -
    Configuration for the channel.
    +
    -
    @@ -158,25 +137,23 @@
    Parameters:
    + +

    Extends

    + + -
    Source:
    -
    - -
    @@ -184,11 +161,11 @@
    Parameters:
    -
    +

    Methods

    @@ -196,34 +173,171 @@
    Parameters:
    +

    _readObject(input) → {Object}

    +
    + Parse an Object into a corresponding Fabric state. +
    - +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + -

    Methods

    @@ -334,7 +448,7 @@
    Parameters:
    @@ -372,7 +486,7 @@
    Parameters:
    -

    (async) fund(input)

    +

    adopt(changes) → {Actor}

    @@ -380,7 +494,7 @@

    (async) fund - Fund the channel. + Explicitly adopt a set of JSONPatch-encoded changes. @@ -416,13 +530,13 @@
    Parameters:
    - input + changes - Mixed + Array @@ -432,7 +546,7 @@
    Parameters:
    - Instance of a Transaction. + List of JSONPatch operations to apply. @@ -450,6 +564,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -475,7 +598,7 @@
    Parameters:
    @@ -502,18 +625,40 @@
    Parameters:
    +
    Returns:
    + + +
    + Instance of the Actor. +
    +
    +
    + Type +
    +
    + Actor +
    +
    -

    (async) open(channel)

    + + + + + + + + +

    commit()

    @@ -521,7 +666,7 @@

    (async) open - Opens a Channel with a Peer. + Increment the vector clock, broadcast all changes as a transaction. @@ -532,64 +677,62 @@

    (async) openParameters:

    - - - - +
    -
    - - - - +
    Overrides:
    +
    + +
    - - - - - - -
    NameTypeDescription
    channel - Object - Channel settings.
    +
    Source:
    +
    + +
    -
    +
    @@ -612,22 +755,21 @@
    Parameters:
    -
    Source:
    -
    - -
    +

    deserialize(input) → {State}

    -

    + + + +
    + Take a hex-encoded input and convert to a State object. +
    + @@ -636,11 +778,6926 @@
    Parameters:
    +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + String + + + + [description]
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + [description] +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    fork() → {State}

    + + + + + + +
    + Creates a new child State, with `@parent` set to + the current State by immutable identifier. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    (async) fund(input)

    + + + + + + +
    + Fund the channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Mixed + + + + Instance of a Transaction.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    inherits(other) → {Number}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    other + + + State + + + + Peer State whose `settings.namespace` is appended to `settings.tags`.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New length of `settings.tags`. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    (async) open(channel)

    + + + + + + +
    + Opens a Channel with a Peer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + Object + + + + Channel settings.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    render() → {String}

    + + + + + + +
    + Compose a JSON string for network consumption. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + JSON-encoded String. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    serialize(inputopt) → {Buffer}

    + + + + + + +
    + Convert to Buffer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + Mixed + + + + + + <optional>
    + + + + + +
    Input to serialize.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Store-able blob. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toHTML()

    + + + + + + +
    + Converts the State to an HTML document. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toString() → {String}

    + + + + + + +
    + Unmarshall an existing state to an instance of a Blob. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Serialized Blob. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {State}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Event stream.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + this +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +

    Channel(settingsopt)

    + + +
    + +
    +
    + + + + + + +

    new Channel(settingsopt)

    + + + + + + +
    + Creates a channel between two peers (bidirectional by default; settings.mode, settings.asset, …). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    settings + + + Object + + + + + + <optional>
    + + + + + +
    Configuration for the channel.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    add(amount)

    + + + + + + +
    + Add an amount to the channel's balance. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    amount + + + Number + + + + Amount value to add to current outgoing balance.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit()

    + + + + + + +
    + Increment the vector clock, broadcast all changes as a transaction. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    deserialize(input) → {State}

    + + + + + + +
    + Take a hex-encoded input and convert to a State object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + String + + + + [description]
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + [description] +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    fork() → {State}

    + + + + + + +
    + Creates a new child State, with `@parent` set to + the current State by immutable identifier. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    (async) fund(input)

    + + + + + + +
    + Fund the channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Mixed + + + + Instance of a Transaction.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    inherits(other) → {Number}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    other + + + State + + + + Peer State whose `settings.namespace` is appended to `settings.tags`.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New length of `settings.tags`. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    (async) open(channel)

    + + + + + + +
    + Opens a Channel with a Peer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + Object + + + + Channel settings.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    render() → {String}

    + + + + + + +
    + Compose a JSON string for network consumption. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + JSON-encoded String. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    serialize(inputopt) → {Buffer}

    + + + + + + +
    + Convert to Buffer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + Mixed + + + + + + <optional>
    + + + + + +
    Input to serialize.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Store-able blob. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toHTML()

    + + + + + + +
    + Converts the State to an HTML document. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toString() → {String}

    + + + + + + +
    + Unmarshall an existing state to an instance of a Blob. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Serialized Blob. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {State}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Event stream.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + this +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    @@ -667,14 +7724,18 @@

    Classes

    Global


    diff --git a/docs/Circuit.html b/docs/Circuit.html index 7487abac1..7b716b283 100644 --- a/docs/Circuit.html +++ b/docs/Circuit.html @@ -172,14 +172,18 @@

    Classes

    Global


    diff --git a/docs/Collection.html b/docs/Collection.html index 523559222..668518c84 100644 --- a/docs/Collection.html +++ b/docs/Collection.html @@ -2546,14 +2546,18 @@

    Classes

    Global


    diff --git a/docs/Contract.html b/docs/Contract.html new file mode 100644 index 000000000..dc8d0ced7 --- /dev/null +++ b/docs/Contract.html @@ -0,0 +1,6617 @@ + + + + + + Class: Contract · Docs + + + + + + + + + +
    +

    Class: Contract

    + + + + +
    + +
    + +

    Contract()

    + + +
    + +
    +
    + + + + + + +

    new Contract()

    + + + + + + +
    + Service-backed agreement template: DOT graphs, a derived circuit structure, deploy/genesis/publish flows, + and JSON-Patch–observed commits. Specialized by Bond, Federation, and Distribution. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + + String + + + + + + + + + + + + Path to store the value at.
    value + + + Object + + + + + + + + + + + + Document to store.
    commit + + + Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + + Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _taprootPolicyInputs(overridesopt) → {object}

    + + + + + + +
    + Shared spend-policy inputs for #toTaprootContract. + Subclasses (e.g. Federation) may override to supply validators from state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    overrides + + + object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + + Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    deploy() → {String}

    + + + + + + +
    + Deploys the contract. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Message ID. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    start() → {Contract}

    + + + + + + +
    + Start the Contract. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + State "STARTED" iteration of the Contract. +
    + + + +
    +
    + Type +
    +
    + + Contract + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    toAddress(networkopt) → {string}

    + + + + + + +
    + Bech32m P2TR address for this contract's spend policy. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    network + + + string + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Service}

    + + + + + + +
    + Explicitly trust all events from a known source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Emitter of events.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of Service after binding events. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    when(event, method) → {EventEmitter}

    + + + + + + +
    + Bind a method to an event, with current state as the immutable context. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    event + + + String + + + + Name of the event upon which to execute `method` as a function.
    method + + + function + + + + Function to execute when named Event `event` is encountered.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of EventEmitter. +
    + + + +
    +
    + Type +
    +
    + + EventEmitter + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/FabricShell.html b/docs/Disk.html similarity index 60% rename from docs/FabricShell.html rename to docs/Disk.html index 6e90b9d40..684b520c3 100644 --- a/docs/FabricShell.html +++ b/docs/Disk.html @@ -3,7 +3,7 @@ - Class: FabricShell · Docs + Class: Disk · Docs + + + + + + +
    +

    Class: Transition

    + + + + +
    + +
    + +

    + Entity.Transition() +

    + +
    JSON Patch diff between two Entity snapshots (origin, + target, changes). Built via Transition.between, Transition#fromTarget, or manual + changes; uses fast-json-patch observe/generate. +
    + + +
    + +
    +
    + + + + +

    Constructor

    + + + +

    new Transition()

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _downsample(inputopt)

    + + + + + + +
    + Return a Fabric-labeled Object for this Entity. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + Mixed + + + + + + <optional>
    + + + + + +
    Input to downsample. If not provided, current Entity will be used.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    toJSON() → {String}

    + + + + + + +
    + Produces a string of JSON, representing the entity. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + JSON-encoded object. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    toRaw() → {Buffer}

    + + + + + + +
    + As a Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Slice of memory. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/Entity.html b/docs/Entity.html new file mode 100644 index 000000000..f94ae4def --- /dev/null +++ b/docs/Entity.html @@ -0,0 +1,652 @@ + + + + + + Class: Entity · Docs + + + + + + + + + +
    +

    Class: Entity

    + + + + +
    + +
    + +

    Entity()

    + +
    Structured document type: extends EventEmitter (not Actor) with + @type / @data shape, JSON serialization, and id = SHA256(toJSON()). + Different model from Actor#id (sorted generic envelope). Entity.Transition (JSON Patch + between entity states) is the supported migration path — see DEVELOPERS.md (Consolidated prototypes). +
    + + +
    + +
    +
    + + + + +

    Constructor

    + + + +

    new Entity()

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + +
      +
    • EventEmitter
    • +
    + + + + + + + +

    Classes

    + +
    +
    Transition
    +
    +
    + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _downsample(inputopt)

    + + + + + + +
    + Return a Fabric-labeled Object for this Entity. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + Mixed + + + + + + <optional>
    + + + + + +
    Input to downsample. If not provided, current Entity will be used.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    toJSON() → {String}

    + + + + + + +
    + Produces a string of JSON, representing the entity. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + JSON-encoded object. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    toRaw() → {Buffer}

    + + + + + + +
    + As a Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Slice of memory. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/Environment.html b/docs/Environment.html index c99c78b81..eb8ccb136 100644 --- a/docs/Environment.html +++ b/docs/Environment.html @@ -360,7 +360,7 @@
    Parameters:
    @@ -420,6 +420,166 @@
    Returns:
    +

    _hasSingleNumericPortSuffix(host) → {boolean}

    + + + + + + +
    + True when `host` is `name:port` with exactly one colon and a numeric port (IPv4 or hostname). + Bare IPv6 literals (`::1`, `2001:db8::1`) have multiple colons — do not treat as host:port. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    host + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + +

    _parseConfigValue(value) → {*}

    @@ -523,7 +683,7 @@
    Parameters:
    @@ -686,7 +846,7 @@
    Parameters:
    @@ -849,7 +1009,7 @@
    Parameters:
    @@ -1047,7 +1207,7 @@
    Parameters:
    @@ -1161,7 +1321,7 @@

    start @@ -1234,14 +1394,18 @@

    Classes

    Global


    diff --git a/docs/Fabric.html b/docs/Fabric.html index 35750f600..ee0c74b29 100644 --- a/docs/Fabric.html +++ b/docs/Fabric.html @@ -33,9 +33,10 @@

    Class: Fabric

    -

    Fabric(config)

    +

    Fabric()

    -
    Reliable decentralized infrastructure.
    +
    Facade Service that bundles Chain, Machine, Store, Peer, and related + types for experiments and apps. Prefer importing leaf types in production; this class re-exports many of them as statics.
    @@ -50,7 +51,6717 @@

    Constructor

    -

    new Fabric(config)

    +

    new Fabric()

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + +

    Members

    + + + +

    (static) DistributedExecution

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Not a Fabric type. Use Machine, Program, + `functions/beaconFederationSigning`, and `functions/fabricCanonicalJson`.
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    (static) Federation

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    (static) Scribe

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Use State. Alias for backward compatibility.
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    (static) Vector

    + + + + +
    + EventEmitter-only instruction handle; use State / Machine for signed payloads. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + + String + + + + + + + + + + + + Path to store the value at.
    value + + + Object + + + + + + + + + + + + Document to store.
    commit + + + Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + + Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    compute() → {Fabric}

    + + + + + + +
    + Process the current stack. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting instance of the stack. +
    + + + +
    +
    + Type +
    +
    + + Fabric + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + + Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    push(value) → {Stack}

    + + + + + + +
    + Push an instruction onto the stack. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + + Instruction + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Stack + + +
    +
    + + + + + + + + + + + + + +

    (async) register(service)

    + + + + + + +
    + Register an available Service using an ES6 Class. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    service + + + Class + + + + The ES6 Class.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start()

    + + + + + + +
    + Start the service, including the initiation of an outbound connection + to any peers designated in the service's configuration. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Fabric}

    + + + + + + +
    + Blindly consume messages from a Source, relying on `this.chain` to + verify results. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Any object which implements the `EventEmitter` pattern.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns itself. +
    + + + +
    +
    + Type +
    +
    + + Fabric + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    when(event, method) → {EventEmitter}

    + + + + + + +
    + Bind a method to an event, with current state as the immutable context. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    event + + + String + + + + Name of the event upon which to execute `method` as a function.
    method + + + function + + + + Function to execute when named Event `event` is encountered.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of EventEmitter. +
    + + + +
    +
    + Type +
    +
    + + EventEmitter + + +
    +
    + + + + + + + + + + + + + +

    + +
    + + + + + + + +
    + +
    + +

    Fabric(settingsopt)

    + + +
    + +
    +
    + + + + + + +

    new Fabric(settingsopt)

    @@ -58,76 +6769,5859 @@

    new Fabric - The Fabric type implements a peer-to-peer protocol for - establishing and settling of mutually-agreed upon proofs of - work. Contract execution takes place in the local node first, - then is optionally shared with the network. + The Fabric type implements a peer-to-peer protocol for establishing and settling mutually agreed proofs of work. + Contract execution runs locally first, then may be shared with the network. +

    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    settings + + + Object + + + + + + <optional>
    + + + + + +
    + + {} + + Engine settings (merged into this.settings); typically includes + path, persistent, and state (initial Actor content). +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Fabric#event:thread
    • + +
    • Fabric#event:step Emitted on a compute step.
    • +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Members

    + + + +

    (static) DistributedExecution

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Not a Fabric type. Use Machine, Program, + `functions/beaconFederationSigning`, and `functions/fabricCanonicalJson`.
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    (static) Federation

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    (static) Scribe

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Use State. Alias for backward compatibility.
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    (static) Vector

    + + + + +
    + EventEmitter-only instruction handle; use State / Machine for signed payloads. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + + String + + + + + + + + + + + + Path to store the value at.
    value + + + Object + + + + + + + + + + + + Document to store.
    commit + + + Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + + Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    compute() → {Fabric}

    + + + + + + +
    + Process the current stack. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting instance of the stack. +
    + + + +
    +
    + Type +
    +
    + + Fabric + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + + Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    push(value) → {Stack}

    + + + + + + +
    + Push an instruction onto the stack. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + + Instruction + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Stack + + +
    +
    + + + + + + + + + + + + + +

    (async) register(service)

    + + + + + + +
    + Register an available Service using an ES6 Class. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    service + + + Class + + + + The ES6 Class.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start()

    + + + + + + +
    + Start the service, including the initiation of an outbound connection + to any peers designated in the service's configuration. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + - Utilizing - +
    Inherited From:
    +
    + +
    -
    Parameters:
    - - - - - - - - - - - - - - +
    -
    -
    NameTypeDescription
    config +
    Source:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - Vector + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    -
    Initial configuration for the Fabric engine. This can be considered the "genesis" state for any contract using the system. If a chain of events is maintained over long periods of time, `state` can be considered "in contention", and it is demonstrated that the outstanding value of the contract remains to be settled.
    +
    Inherited From:
    +
    + +
    -
    @@ -147,58 +12641,71 @@
    Parameters:
    +
    Source:
    +
    + +
    +
    See:
    +
    + +
    -
    Source:
    -
    - -
    +
    -
    -
    Fires:
    -
      -
    • Fabric#event:thread
    • -
    • Fabric#event:step Emitted on a `compute` step.
    • -
    +
    Returns:
    +
    + `{ type, object }` +
    +
    +
    + Type +
    +
    + Object +
    +
    - @@ -207,17 +12714,19 @@
    Fires:
    +

    toObject() → {Object}

    -

    Members

    +
    + Returns the Actor's current state as an Object. +
    -

    (static) DistributedExecution

    @@ -234,6 +12743,15 @@

    (static + +
    Inherited From:
    +
    + +
    @@ -259,7 +12777,7 @@

    (static
    @@ -279,10 +12797,6 @@

    (static -

    (static) Federation

    - - - @@ -290,14 +12804,22 @@

    (static) F -
    +
    Returns:
    +
    +
    + Type +
    +
    + Object +
    +
    @@ -311,20 +12833,17 @@

    (static) F +

    trust(source) → {Fabric}

    -
    Source:
    -
    - -
    +
    + Blindly consume messages from a Source, relying on `this.chain` to + verify results. +
    @@ -332,42 +12851,55 @@

    (static) F -

    +

    Parameters:
    + + + + + -

    Methods

    + + + + + + -

    compute() → {Fabric}

    + + + + +
    NameTypeDescription
    source + EventEmitter -
    - Process the current stack. -
    +
    Any object which implements the `EventEmitter` pattern.
    @@ -383,6 +12915,15 @@

    computeOverrides: +
    + +
    + @@ -405,7 +12946,7 @@

    compute @@ -436,7 +12977,7 @@
    Returns:
    - Resulting instance of the stack. + Returns itself.
    @@ -465,7 +13006,7 @@
    Returns:
    -

    push(value) → {Stack}

    +

    unpause() → {Actor}

    @@ -473,7 +13014,7 @@

    push - Push an instruction onto the stack. + Toggles `status` property to unpaused. @@ -484,55 +13025,6 @@

    push - - - - Name - - - Type - - - - - - Description - - - - - - - - - value - - - - - - Instruction - - - - - - - - - - - - - - - - - @@ -543,6 +13035,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -568,7 +13069,7 @@
    Parameters:
    @@ -598,6 +13099,10 @@
    Parameters:
    Returns:
    +
    + Instance of the Actor. +
    +
    @@ -606,7 +13111,7 @@
    Returns:
    - Stack + Actor
    @@ -624,7 +13129,7 @@
    Returns:
    -

    (async) register(service)

    +

    value(formatopt) → {Object}

    @@ -632,7 +13137,7 @@

    (async) regis
    - Register an available Service using an ES6 Class. + Get the inner value of the Actor with an optional cast type.
    @@ -656,8 +13161,12 @@

    Parameters:
    Type + Attributes + + Default + Description @@ -668,23 +13177,39 @@
    Parameters:
    - service + format - Class + String + + <optional>
    - The ES6 Class. + + + + + + + + + + object + + + + + Cast the value to one of: `buffer, hex, json, string` @@ -702,6 +13227,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -727,7 +13261,7 @@
    Parameters:
    @@ -754,6 +13288,28 @@
    Parameters:
    +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + @@ -765,7 +13321,7 @@
    Parameters:
    -

    trust(source) → {Fabric}

    +

    when(event, method) → {EventEmitter}

    @@ -773,8 +13329,7 @@

    trust - Blindly consume messages from a Source, relying on `this.chain` to - verify results. + Bind a method to an event, with current state as the immutable context. @@ -810,13 +13365,13 @@
    Parameters:
    - source + event - EventEmitter + String @@ -826,7 +13381,30 @@
    Parameters:
    - Any object which implements the `EventEmitter` pattern. + Name of the event upon which to execute `method` as a function. + + + + + + + method + + + + + + function + + + + + + + + + + Function to execute when named Event `event` is encountered. @@ -844,6 +13422,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -869,7 +13456,7 @@
    Parameters:
    @@ -900,7 +13487,7 @@
    Returns:
    - Returns itself. + Instance of EventEmitter.
    @@ -911,7 +13498,7 @@
    Returns:
    - Fabric + EventEmitter
    @@ -942,14 +13529,18 @@

    Classes

    Global


    diff --git a/docs/Federation.html b/docs/Federation.html index 8484b5bc1..62f6c3a58 100644 --- a/docs/Federation.html +++ b/docs/Federation.html @@ -245,6 +245,177 @@

    Methods

    +

    _taprootPolicyInputs(overridesopt) → {object}

    + + + + + + +
    + Federation validators live on consensus state, not only constructor settings. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    overrides + + + object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + + +
    +
    + + + + + + + + + + + + +

    createMultiSignature(msg) → {Object}

    @@ -1143,14 +1314,18 @@

    Classes

    Global


    diff --git a/docs/Filesystem.html b/docs/Filesystem.html index 91a0f83d4..3ddb47176 100644 --- a/docs/Filesystem.html +++ b/docs/Filesystem.html @@ -259,7 +259,7 @@
    Properties
    @@ -393,7 +393,7 @@

    _loadFro
    @@ -507,7 +507,7 @@

    ls @@ -670,7 +670,7 @@
    Parameters:
    @@ -784,7 +784,7 @@

    sync @@ -898,7 +898,7 @@

    (async) sy
    @@ -1084,7 +1084,7 @@

    Parameters:
    @@ -1157,14 +1157,18 @@

    Classes

    Global


    diff --git a/docs/HKDF.html b/docs/HKDF.html index 796bf1080..7ae7b17f6 100644 --- a/docs/HKDF.html +++ b/docs/HKDF.html @@ -564,14 +564,18 @@

    Classes

    Global


    diff --git a/docs/Hash256.html b/docs/Hash256.html index 07c49c465..6778643c0 100644 --- a/docs/Hash256.html +++ b/docs/Hash256.html @@ -700,14 +700,18 @@

    Classes

    Global


    diff --git a/docs/Identity.html b/docs/Identity.html index 9d38aca62..86a929247 100644 --- a/docs/Identity.html +++ b/docs/Identity.html @@ -33,9 +33,14 @@

    Class: Identity

    -

    Identity(settingsopt) → {Identity}

    +

    Identity()

    -
    Manage a network identity.
    +
    BIP32/BIP39 identity wrapping Key: mnemonic / xprv / passphrase, derivation + m/44'/7778'/account'/0/index (see derivation getter). Important: this class + overrides Actor#id with toString() (human-facing / Bech32-style identity), not the + content-addressed Actor#id / preimage chain from Actor#toGenericMessage. Use + pubkey, pubkeyhash, or explicit hashing when you need stable bytes. +
    @@ -50,16 +55,13 @@

    Constructor

    -

    new Identity(settingsopt) → {Identity}

    +

    new Identity()

    -
    - Create an instance of an Identity. -
    @@ -69,331 +71,291 @@

    new Identity< -

    Parameters:
    - - - - +
    -
    - - - - - - - - - - - - -
    NameTypeAttributesDescription
    settings - Object - - <optional>
    +
    Source:
    +
    + +
    -
    Settings for the Identity. -
    Properties
    - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - +
    Inherited From:
    +
    + +
    - - - - - - - - - - - - - +
    Returns:
    - + + - - - - -
    NameTypeAttributesDefaultDescription
    seed - String - +

    Extends

    - <optional>
    + -
    - BIP 39 seed phrase.
    xprv - String +

    Methods

    -
    - <optional>
    +

    _readObject(input) → {Object}

    -
    +
    + Parse an Object into a corresponding Fabric state. +
    -
    Serialized BIP 32 master private key.
    xpub +
    Parameters:
    - String + + + + - + - + + + - + + - + Object - - + - - - + - Number + +
    NameType - <optional>
    +
    Description
    input + - Serialized BIP 32 master public key.
    account + Object to read as input.
    -
    - <optional>
    +
    -
    - 0 - BIP 44 account index.
    index - Number - - <optional>
    +
    Source:
    +
    + +
    -
    - 0 + - BIP 44 key index.
    passphrase - String - - <optional>
    +
    + Fabric state. +
    +
    +
    + Type +
    +
    + Object -
    - Passphrase for the key.
    -
    +

    adopt(changes) → {Actor}

    -
    +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    @@ -403,71 +365,85 @@
    Properties
    +
    Parameters:
    + + + + + + + + + + -
    Source:
    -
    - -
    + + + + + +
    NameTypeDescription
    changes + Array - + List of JSONPatch operations to apply.
    + +
    + + + + + + +
    Inherited From:
    +
    + +
    -
    Returns:
    -
    - Instance of the identity. -
    -
    -
    - Type -
    -
    - Identity -
    -
    @@ -476,8 +452,16 @@
    Returns:
    - + +
    Source:
    +
    + +
    @@ -485,6 +469,7 @@
    Returns:
    +
    @@ -493,7 +478,6 @@
    Returns:
    -

    Methods

    @@ -501,7 +485,40 @@

    Methods

    -

    toString() → {String}

    +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    @@ -509,7 +526,7 @@

    toString - Retrieve the bech32m-encoded identity. + Resolve the current state to a commitment. @@ -530,6 +547,15 @@

    toStringInherited From: +
    + +
    @@ -555,7 +581,7 @@

    toString @@ -586,7 +612,7 @@
    Returns:
    - Public identity. + 32-byte ID
    @@ -615,6 +641,4763 @@
    Returns:
    +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toString() → {String}

    + + + + + + +
    + Retrieve the bech32m-encoded identity. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Public identity. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    + +
    + + + + + + + +
    + +
    + +

    Identity(settingsopt) → {Identity}

    + + +
    + +
    +
    + + + + + + +

    new Identity(settingsopt) → {Identity}

    + + + + + + +
    + Create an instance of an Identity. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    settings + + + Object + + + + + + <optional>
    + + + + + +
    Settings for the Identity. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    seed + + + String + + + + + + <optional>
    + + + + + +
    + + BIP 39 seed phrase.
    xprv + + + String + + + + + + <optional>
    + + + + + +
    + + Serialized BIP 32 master private key.
    xpub + + + String + + + + + + <optional>
    + + + + + +
    + + Serialized BIP 32 master public key.
    account + + + Number + + + + + + <optional>
    + + + + + +
    + + 0 + + BIP 44 account index.
    index + + + Number + + + + + + <optional>
    + + + + + +
    + + 0 + + BIP 44 key index.
    passphrase + + + String + + + + + + <optional>
    + + + + + +
    + + Passphrase for the key.
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the identity. +
    + + + +
    +
    + Type +
    +
    + + Identity + + +
    +
    + + + + + + + + +
    + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toString() → {String}

    + + + + + + +
    + Retrieve the bech32m-encoded identity. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Public identity. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + +
    @@ -628,14 +5411,18 @@

    Classes

    Global


    diff --git a/docs/Interface.html b/docs/Interface.html index 1d5fa45a1..d1d2f536f 100644 --- a/docs/Interface.html +++ b/docs/Interface.html @@ -399,7 +399,7 @@
    Parameters:
    @@ -552,7 +552,7 @@
    Parameters:
    @@ -644,7 +644,7 @@

    now @@ -754,7 +754,7 @@

    (async) start @@ -846,7 +846,7 @@

    (async) stop @@ -897,14 +897,18 @@

    Classes

    Global


    diff --git a/docs/Key.html b/docs/Key.html index 2128cbc7f..4ebdf4a91 100644 --- a/docs/Key.html +++ b/docs/Key.html @@ -422,7 +422,7 @@
    Properties
    @@ -523,7 +523,7 @@

    iv @@ -609,7 +609,7 @@

    secure @@ -750,7 +750,7 @@
    Parameters:
    @@ -916,7 +916,7 @@
    Parameters:
    @@ -1080,7 +1080,7 @@
    Parameters:
    @@ -1194,7 +1194,7 @@

    toWIF @@ -1415,7 +1415,7 @@
    Parameters:
    @@ -1604,7 +1604,7 @@
    Parameters:
    @@ -1791,7 +1791,7 @@
    Parameters:
    @@ -1997,7 +1997,7 @@
    Parameters:
    @@ -2070,14 +2070,18 @@

    Classes

    Global


    diff --git a/docs/Ledger.html b/docs/Ledger.html index ae7dac20c..8af87ae39 100644 --- a/docs/Ledger.html +++ b/docs/Ledger.html @@ -240,7 +240,7 @@

    Extends

    @@ -267,7 +267,7 @@

    Methods

    -

    (async) append(item) → {Promise}

    +

    _readObject(input) → {Object}

    @@ -275,7 +275,7 @@

    (async) append<
    - Attempts to append a Page to the ledger. + Parse an Object into a corresponding Fabric state.
    @@ -311,13 +311,13 @@

    Parameters:
    - item + input - Mixed + Object @@ -327,7 +327,7 @@
    Parameters:
    - Item to store. + Object to read as input. @@ -348,6 +348,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -370,7 +379,7 @@
    Parameters:
    @@ -401,7 +410,7 @@
    Returns:
    - Resolves after the change has been committed. + Fabric state.
    @@ -412,7 +421,7 @@
    Returns:
    - Promise + Object
    @@ -430,7 +439,7 @@
    Returns:
    -

    inherits(scribe) → {Scribe}

    +

    adopt(changes) → {Actor}

    @@ -438,7 +447,7 @@

    inherits - Use an existing Scribe instance as a parent. + Explicitly adopt a set of JSONPatch-encoded changes. @@ -474,13 +483,13 @@
    Parameters:
    - scribe + changes - Scribe + Array @@ -490,7 +499,7 @@
    Parameters:
    - Instance of Scribe to use as parent. + List of JSONPatch operations to apply. @@ -515,7 +524,7 @@
    Parameters:
    @@ -542,7 +551,7 @@
    Parameters:
    @@ -573,7 +582,7 @@
    Returns:
    - The configured instance of the Scribe. + Instance of the Actor.
    @@ -584,7 +593,7 @@
    Returns:
    - Scribe + Actor
    @@ -602,7 +611,7 @@
    Returns:
    -

    now() → {Number}

    +

    (async) append(item) → {Promise}

    @@ -610,7 +619,7 @@

    now - Retrives the current timestamp, in milliseconds. + Attempts to append a Page to the ledger. @@ -621,12 +630,60 @@

    now + + + Name -
    + Type + + + + + + Description + + + + + + + + + item + + + + + + Mixed + + + + + + + + + + Item to store. + + + + + + + + + + + +
    @@ -634,14 +691,6 @@

    nowOverrides: -
    - -
    @@ -665,7 +714,7 @@

    now @@ -696,7 +745,7 @@
    Returns:
    - Number representation of the millisecond Integer value. + Resolves after the change has been committed.
    @@ -707,10 +756,98 @@
    Returns:
    - Number + Promise + + +
    +

    + + + + + + + + + + + + + +

    commit()

    + + + + + + +
    + Increment the vector clock, broadcast all changes as a transaction. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + +
    Source:
    +
    +
    + + + + + + +
    @@ -725,7 +862,20 @@
    Returns:
    -

    trust(source) → {Scribe}

    + + + + + + + + + + + + + +

    deserialize(input) → {State}

    @@ -733,7 +883,7 @@

    trust - Blindly bind event handlers to the Source. + Take a hex-encoded input and convert to a State object. @@ -769,13 +919,13 @@
    Parameters:
    - source + input - Source + String @@ -785,7 +935,2636 @@
    Parameters:
    - Event stream. + [description] + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + [description] +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    fork() → {State}

    + + + + + + +
    + Creates a new child State, with `@parent` set to + the current State by immutable identifier. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    inherits(other) → {Number}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    other + + + State + + + + Peer State whose `settings.namespace` is appended to `settings.tags`.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New length of `settings.tags`. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    render() → {String}

    + + + + + + +
    + Compose a JSON string for network consumption. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + JSON-encoded String. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    serialize(inputopt) → {Buffer}

    + + + + + + +
    + Convert to Buffer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + Mixed + + + + + + <optional>
    + + + + + +
    Input to serialize.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Store-able blob. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toHTML()

    + + + + + + +
    + Converts the State to an HTML document. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toString() → {String}

    + + + + + + +
    + Unmarshall an existing state to an instance of a Blob. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Serialized Blob. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {State}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Event stream.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + this +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -810,7 +3589,7 @@
    Parameters:
    @@ -837,7 +3616,7 @@
    Parameters:
    @@ -868,7 +3647,7 @@
    Returns:
    - Instance of the Scribe. + Inner value of the Actor as an Object, or cast to the requested `format`.
    @@ -879,7 +3658,7 @@
    Returns:
    - Scribe + Object
    @@ -910,14 +3689,18 @@

    Classes

    Global


    diff --git a/docs/Lightning.html b/docs/Lightning.html index 7d0426c35..16b27e7a9 100644 --- a/docs/Lightning.html +++ b/docs/Lightning.html @@ -35,7 +35,9 @@

    Class: Lightning

    Lightning(settingsopt) → {Lightning}

    -
    Manage a Lightning node.
    +
    Manage a Lightning node (Core Lightning JSON-RPC over Unix socket). + + BOLT checklist: `docs/BOLT_COMPATIBILITY.md`. RPC map: `docs/LIGHTNING_COMPAT.md`.
    @@ -165,7 +167,7 @@
    Parameters:
    @@ -174,6 +176,13 @@
    Parameters:
    +
    See:
    +
    + +
    + @@ -235,27 +244,17 @@

    Members

    -

    (static) CLN_RPC_METHODS :ReadonlyArray.<string>

    +

    rpc

    - Core Lightning JSON-RPC method names invoked by this service (see docs/LIGHTNING_COMPAT.md). + Optional RPC client handle (e.g. REST/grpc); not used by Core Lightning socket `_makeRPCRequest`.
    -
    Type:
    -
      -
    • - - ReadonlyArray.<string> - - -
    • -
    - @@ -291,7 +290,7 @@
    Type:
    @@ -311,26 +310,32 @@
    Type:
    +

    (static) CLN_RPC_METHODS :ReadonlyArray.<string>

    -

    Methods

    +
    + Core Lightning JSON-RPC method names invoked by this service (see docs/LIGHTNING_COMPAT.md). +
    +
    Type:
    +
      +
    • + ReadonlyArray.<string> -

      (async) _makeRPCRequest(method, paramsopt, timeoutMsopt) → {Object|String}

      +
    • +
    -
    - Make an RPC request through the Lightning UNIX socket. -
    +
    @@ -340,159 +345,144 @@

    (async) Parameters:

    -
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    - - - - - - - - - - - - +
    Source:
    +
    + +
    - + - +

    (static) DOCS :Readonly.<{boltCompatibility: string, fabricLightningOffers: string, fabricLightningMarkets: string, fabricPaymentBech32: string, lightningCompat: string}>

    - - - +
    + Paths to canonical Markdown docs (relative to the `@fabric/core` package root). + `fabricLightningMarkets` and `fabricLightningOffers` point at the same file (Fabric **markets** vs Lightning BOLT12 **offers**). +
    - +
    Type:
    +
      +
    • -
    + Readonly.<{boltCompatibility: string, fabricLightningOffers: string, fabricLightningMarkets: string, fabricPaymentBech32: string, lightningCompat: string}> - +
    -
    - - - - - - +
    Source:
    +
    + +
    - + - - - - -
    NameTypeAttributesDefaultDescription
    method - String - - - Name of method to call.
    params + + - Array - - <optional>
    -
    - Array of parameters.
    timeoutMs - Number - - <optional>
    -
    - 30000 - Optional timeout in ms; default 30000. Prevents hanging when lightningd is busy.
    +

    Methods

    -
    +

    (async) _makeRPCRequest(method, paramsopt, timeoutMsopt) → {Object|String}

    + +
    + Make an RPC request through the Lightning UNIX socket. +
    @@ -502,100 +492,145 @@
    Parameters:
    +
    Parameters:
    + + + + + + -
    Source:
    -
    - -
    + + + + + + + - + + -
    Returns:
    + + + -
    -
    - Type -
    -
    - Object - | - String +
    + - - + + + + -
    - Computes the total liquidity of the Lightning node. -
    + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    method + String + + -
    - Respond from the Lightning node. -
    +
    Name of method to call.
    params + Array + + <optional>
    -

    (async) computeLiquidity() → {Object}

    +
    + Array of parameters.
    timeoutMs + + + Number + + + + + + <optional>
    + + + + + +
    + 30000 + Optional timeout in ms; default 30000. Prevents hanging when lightningd is busy.
    @@ -633,7 +668,7 @@

    (async) @@ -664,7 +699,7 @@
    Returns:
    - Liquidity in BTC. + Respond from the Lightning node.
    @@ -676,6 +711,9 @@
    Returns:
    Object + | + + String
    @@ -693,7 +731,7 @@
    Returns:
    -

    (async) createChannel(peer, amount, pushMsatopt, optionsopt)

    +

    (async) callRpc(method, paramsopt, timeoutMsopt) → {Promise.<*>}

    @@ -701,7 +739,8 @@

    (async)
    - Creates a new Lightning channel. + Invoke any Core Lightning JSON-RPC method over the lightningd socket (escape hatch for methods without a typed wrapper). + Named `callRpc` so it does not shadow the `rpc` instance property.
    @@ -741,7 +780,7 @@

    Parameters:
    - peer + method @@ -769,20 +808,20 @@
    Parameters:
    - Public key of the peer to create a channel with. + RPC method name (e.g. `listpeers`). - amount + params - String + Array @@ -791,6 +830,8 @@
    Parameters:
    + <optional>
    + @@ -801,26 +842,25 @@
    Parameters:
    + [] + - Amount in satoshis to fund the channel. + Positional/object params as accepted by lightningd for that method. - pushMsat + timeoutMs Number - | - - null @@ -841,63 +881,138 @@
    Parameters:
    - null + 30000 - Optional push amount in millisatoshis. + Optional timeout. + + - - options - - Object +
    - - - <optional>
    - - - {} - - Optional overrides (e.g. minconf for regtest). - - - -
    +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + RPC result. +
    + + + +
    +
    + Type +
    +
    + + Promise.<*> + + +
    +
    + + + + + + + + + + + + + +

    (async) computeLiquidity() → {Object}

    + + + + + + +
    + Computes the total liquidity of the Lightning node. +
    + + + + + + + + + + + + + +
    @@ -928,7 +1043,7 @@
    Parameters:
    @@ -955,6 +1070,28 @@
    Parameters:
    +
    Returns:
    + + +
    + Liquidity in BTC. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + @@ -966,7 +1103,7 @@
    Parameters:
    -

    (async) createInvoice(amount)

    +

    (async) createChannel(peer, amount, pushMsatopt, optionsopt)

    @@ -974,7 +1111,7 @@

    (async)
    - Create a new Lightning invoice. + Creates a new Lightning channel.
    @@ -998,7 +1135,11 @@

    Parameters:
    Type + Attributes + + + Default Description @@ -1008,6 +1149,41 @@
    Parameters:
    + + + peer + + + + + + String + + + + + + + + + + + + + + + + + + + + + + Public key of the peer to create a channel with. + + + + amount @@ -1023,10 +1199,103 @@
    Parameters:
    + - Amount in millisatoshi (msat). + + + + + + + + + + + + Amount in satoshis to fund the channel. + + + + + + + pushMsat + + + + + + Number + | + + null + + + + + + + + + <optional>
    + + + + + + + + + + + + null + + + + + Optional push amount in millisatoshis. + + + + + + + options + + + + + + Object + + + + + + + + + <optional>
    + + + + + + + + + + + + {} + + + + + Optional overrides (e.g. minconf for regtest). @@ -1069,7 +1338,7 @@
    Parameters:
    @@ -1101,6 +1370,2390 @@
    Parameters:
    + + + + + + +

    (async) createInvoice(amount)

    + + + + + + +
    + Create a new Lightning invoice. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    amount + + + String + + + + Amount in millisatoshi (msat).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) createInvoiceRequest(params) → {Promise.<Object>}

    + + + + + + +
    + Create a BOLT12 `invoice_request` (you request that someone else pay you via their offer flow). Returns `bolt12` (`lnr1…`). (Core Lightning `invoicerequest`, v22.11+.) +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    params + + + Object + + + + `{ amount, description, issuer?, label?, absolute_expiry?, single_use? }` — see CLN docs for amount formats.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) createOffer(params) → {Promise.<Object>}

    + + + + + + +
    + Create a BOLT12 offer (requires `experimental-offers` / modern CLN). Pass-through to `offer`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    params + + + Object + + + + Keyword args, e.g. `{ amount_msat, description, label, issuer, ... }`.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Offer result (includes `bolt12` / offer id per CLN version). +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) decodeLightning(boltString) → {Promise.<Object>}

    + + + + + + +
    + Decode a BOLT11 invoice or BOLT12 offer string (Core Lightning `decode`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    boltString + + + String + + + + `lnbc...`, `lno1...`, etc.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Decoded fields. +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) decodePay(bolt11) → {Promise.<Object>}

    + + + + + + +
    + Decode a BOLT11 invoice for payment fields (Core Lightning `decodepay`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    bolt11 + + + String + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Decoded pay details. +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) disableInvoiceRequest(invreqId) → {Promise.<Object>}

    + + + + + + +
    + Disable an `invoice_request` so no further invoices are accepted (Core Lightning `disableinvoicerequest`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    invreqId + + + String + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) disableOffer(offerId) → {Promise.<Object>}

    + + + + + + +
    + Disable a local offer by id (Core Lightning `disableoffer`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    offerId + + + String + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) fetchInvoice(offerOrParams, invoiceParamsopt) → {Promise.<Object>}

    + + + + + + +
    + Request a BOLT11 invoice from a BOLT12 offer (Core Lightning `fetchinvoice`). + + Call either: + - `fetchInvoice('lno1…')` or `fetchInvoice('lno1…', { amount_msat, quantity, payer_note, timeout, … })` + - **Recurrence** (when the offer is recurring): `recurrence_counter` (start at 0), `recurrence_start`, `recurrence_label` (stable label linking the series; required when counter is set). See Core Lightning **`fetchinvoice`** and [BOLT #12](https://github.com/lightning/bolts/blob/master/12-offer-encoding.md). + - `fetchInvoice({ offer: 'lno1…', amount_msat, … })` — single keyword object as accepted by CLN. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    offerOrParams + + + String + | + + Object + + + + + + + + + + + + Bolt12 offer string (`lno1…`), or one params object with an `offer` field.
    invoiceParams + + + Object + + + + + + <optional>
    + + + + + +
    + + null + + When the first arg is a string, optional extra fields merged into the RPC (second positional group).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Invoice response (includes `invoice` bolt11 when successful). +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) getRoute(destinationId, amountMsat, riskfactoropt, cltvOrRouteOptionsopt) → {Promise.<Object>}

    + + + + + + +
    + Route probe (Core Lightning `getroute`). + CLN order: `id`, `amount_msat`, `riskfactor`, `cltv`, `fromid`, `fuzzpercent`, `exclude`, `maxhops` + — the fourth **positional** is `cltv`, not `maxhops`. To set `maxhops` (or other tail fields) use the + `routeOptions` object so intermediate slots are sent as `null` where needed. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    destinationId + + + String + + + + + + + + + + + + Destination node id (pubkey).
    amountMsat + + + Number + | + + String + + + + + + + + + + + +
    riskfactor + + + Number + + + + + + <optional>
    + + + + + +
    + + 10 + +
    cltvOrRouteOptions + + + Number + | + + Object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + Omitted: three-arg RPC. If a number: fourth positional (`cltv`). + If an object: tail fields `cltv`, `fromid`, `fuzzpercent`, `exclude`, `maxhops` (each optional).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) listInvoiceRequests(filteropt) → {Promise.<Object>}

    + + + + + + +
    + List `invoice_request` records (Core Lightning `listinvoicerequests`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    filter + + + String + | + + Object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + `invreq_id` string, or `{ invreq_id?, active_only? }`, or null for all.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) listOffers(filteropt) → {Promise.<Object>}

    + + + + + + +
    + List offers created on this node (Core Lightning `listoffers`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    filter + + + String + | + + Object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + `offer_id` string, or `{ offer_id?, active_only? }`, or null for all.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) pay(invoiceOrParams, timeoutMsopt) → {Promise.<Object>}

    + + + + + + +
    + Pay a BOLT11 invoice or BOLT12-fetched bolt11 string (Core Lightning `pay`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    invoiceOrParams + + + String + | + + Object + + + + + + + + + + + + Bolt11 string, or keyword object (e.g. `{ bolt11 }`, `{ bolt12 }` per CLN).
    timeoutMs + + + Number + + + + + + <optional>
    + + + + + +
    + + 30000 + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Payment result. +
    + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + + + + + + + +

    (async) sendInvoice(params) → {Promise.<Object>}

    + + + + + + +
    + Create and send a BOLT12 invoice to the issuer of an `invoice_request` (Core Lightning `sendinvoice`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    params + + + Object + + + + `{ invreq, label, amount_msat?, timeout?, quantity? }` — `invreq` is the `lnr1…` string.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<Object> + + +
    +
    + + + + + + + @@ -1222,7 +3875,7 @@
    Parameters:
    @@ -1291,14 +3944,18 @@

    Classes

    Global


    diff --git a/docs/Logger.html b/docs/Logger.html index 96a15e0fd..e27e8ef19 100644 --- a/docs/Logger.html +++ b/docs/Logger.html @@ -351,7 +351,7 @@
    Parameters:
    @@ -523,7 +523,7 @@
    Parameters:
    @@ -646,7 +646,7 @@

    commit @@ -769,7 +769,7 @@

    export @@ -941,7 +941,7 @@
    Parameters:
    @@ -1230,7 +1230,7 @@

    pause @@ -1353,7 +1353,7 @@

    serialize @@ -1544,7 +1544,7 @@
    Parameters:
    @@ -1667,7 +1667,7 @@

    sign @@ -2072,7 +2072,7 @@
    Parameters:
    @@ -2195,7 +2195,7 @@

    toBuffer @@ -2386,7 +2386,7 @@
    Parameters:
    @@ -2518,7 +2518,7 @@

    toObject @@ -2637,7 +2637,7 @@

    unpause @@ -2829,7 +2829,7 @@
    Parameters:
    @@ -2902,14 +2902,18 @@

    Classes

    Global


    diff --git a/docs/Machine.html b/docs/Machine.html index 711959b1f..5d6228548 100644 --- a/docs/Machine.html +++ b/docs/Machine.html @@ -29,37 +29,6614 @@

    Class: Machine

    +
    + +
    + +

    Machine()

    + +
    Deterministic virtual machine layer extending Actor: script/stack, fixed memory buffer, + clock, and a Key-backed generator for reproducible “random” bits (sip). Consumes State-signed + instruction entries from Fabric#push — not the same as P2P Message dispatch (see + types/message.js). +
    + + +
    + +
    +
    + + + + +

    Constructor

    + + + +

    new Machine()

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) compute(input) → {Machine}

    + + + + + + +
    + Computes the next "step" for our current Vector. Analagous to `sum`. + The top item on the stack is always the memory held at current position, + so counts should always begin with 0. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Value to pass as input.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the resulting machine. +
    + + + +
    +
    + Type +
    +
    + + Machine + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    loadProgram(program) → {Machine}

    + + + + + + +
    + Load a Program onto this machine (sets script steps). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    program + + + Program + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Machine + + +
    +
    + + + + + + + + + + + + + +

    parseManifest(raw, programopt) → {Object}

    + + + + + + +
    + Parse program manifest v1 (former DistributedExecution.parseDistributedManifestV1). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    raw + + + Object + + + + + + + + + + + +
    program + + + Program + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + When provided, programId/hash must match.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) runProgram(program, inputopt) → {Promise.<{ok: boolean, stack: Array, tip: *, trace: Array, runCommitmentHex: (string|null), error: (string|undefined)}>}

    + + + + + + +
    + Load and compute a Program; return stack tip + run commitment for L1 binding. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    program + + + Program + | + + Object + + + + + + + + + +
    input + + + * + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<{ok: boolean, stack: Array, tip: *, trace: Array, runCommitmentHex: (string|null), error: (string|undefined)}> + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    sip(nopt) → {Number}

    + + + + + + +
    + Get `n` bits of deterministic random data. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    n + + + Number + + + + + + <optional>
    + + + + + +
    + + 128 + + Number of bits to retrieve.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Random bits from Generator. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    slurp(nopt) → {Number}

    + + + + + + +
    + Get `n` bytes of deterministic random data. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    n + + + Number + + + + + + <optional>
    + + + + + +
    + + 32 + + Number of bytes to retrieve.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Random bytes from Generator. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (static) fromObjectString(inputopt) → {Array.<Buffer>}

    + + + + + + +
    + Parse a JSON object of Buffer-like entries into an array of Buffers (legacy wire / script helper). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    input + + + string + + + + + + <optional>
    + + + + + +
    + + '' + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Buffer> + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + + + + +

    Machine(settings)

    -
    General-purpose state machine with Vector-based instructions.
    + +
    + +
    +
    + + + + + + +

    new Machine(settings)

    + + + + + + +
    + Create a Machine. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    settings + + + Object + + + + Run-time configuration.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) compute(input) → {Machine}

    + + + + + + +
    + Computes the next "step" for our current Vector. Analagous to `sum`. + The top item on the stack is always the memory held at current position, + so counts should always begin with 0. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Value to pass as input.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the resulting machine. +
    + + + +
    +
    + Type +
    +
    + + Machine + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    loadProgram(program) → {Machine}

    + + + + + + +
    + Load a Program onto this machine (sets script steps). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    program + + + Program + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Machine + + +
    +
    + + + + + + + + + + + + + +

    parseManifest(raw, programopt) → {Object}

    + + + + + + +
    + Parse program manifest v1 (former DistributedExecution.parseDistributedManifestV1). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    raw + + + Object + + + + + + + + + + + +
    program + + + Program + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + When provided, programId/hash must match.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) runProgram(program, inputopt) → {Promise.<{ok: boolean, stack: Array, tip: *, trace: Array, runCommitmentHex: (string|null), error: (string|undefined)}>}

    + + + + + + +
    + Load and compute a Program; return stack tip + run commitment for L1 binding. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    program + + + Program + | + + Object + + + + + + + + + +
    input + + + * + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<{ok: boolean, stack: Array, tip: *, trace: Array, runCommitmentHex: (string|null), error: (string|undefined)}> + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    sip(nopt) → {Number}

    + + + + + + +
    + Get `n` bits of deterministic random data. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    n + + + Number + + + + + + <optional>
    + + + + + +
    + + 128 + + Number of bits to retrieve.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Random bits from Generator. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    slurp(nopt) → {Number}

    + + + + + + +
    + Get `n` bytes of deterministic random data. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    n + + + Number + + + + + + <optional>
    + + + + + +
    + + 32 + + Number of bytes to retrieve.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Random bytes from Generator. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + - -
    -
    +
    Returns:
    -

    Constructor

    +
    +
    + Type +
    +
    + Buffer -

    new Machine(settings)

    +
    +
    -
    - Create a Machine. -
    @@ -67,104 +6644,121 @@

    new MachinetoGenericMessage(typeopt) → {Object}

    -
    Parameters:
    - - - - +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    - - - - - - +
    Parameters:
    - +
    NameTypeDescription
    settings
    + + - - Object + + - + + + + - - + - -
    + NameTypeAttributesDefaultDescription
    Run-time configuration.
    + + type + + String -
    + + + <optional>
    + + + 'FabricActorState' + + Logical message type string. + + + +
    + + + -
    Source:
    -
    - -
    +
    Inherited From:
    +
    + +
    -
    @@ -180,16 +6774,32 @@
    Parameters:
    + +
    Source:
    +
    + +
    +
    See:
    +
    +
    +
  • https://dev.fabric.pub/messages
  • + + +

    @@ -203,7 +6813,28 @@
    Parameters:
    -

    Methods

    + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    @@ -211,7 +6842,13 @@

    Methods

    -

    (async) compute(input) → {Machine}

    + + + + + + +

    toObject() → {Object}

    @@ -219,9 +6856,7 @@

    (async) comput
    - Computes the next "step" for our current Vector. Analagous to `sum`. - The top item on the stack is always the memory held at current position, - so counts should always begin with 0. + Returns the Actor's current state as an Object.
    @@ -232,53 +6867,123 @@

    (async) comput -

    Parameters:
    - - - - +
    -
    - - - +
    Inherited From:
    +
    + +
    - - - - - - - -
    NameTypeDescription
    input - Object - Value to pass as input.
    + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + @@ -291,6 +6996,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -316,7 +7030,7 @@
    Parameters:
    @@ -347,7 +7061,7 @@
    Returns:
    - Instance of the resulting machine. + Instance of the Actor.
    @@ -358,7 +7072,7 @@
    Returns:
    - Machine + Actor
    @@ -376,7 +7090,7 @@
    Returns:
    -

    sip(nopt) → {Number}

    +

    value(formatopt) → {Object}

    @@ -384,7 +7098,7 @@

    sip - Get `n` bits of deterministic random data. + Get the inner value of the Actor with an optional cast type. @@ -424,13 +7138,13 @@
    Parameters:
    - n + format - Number + String @@ -451,12 +7165,12 @@
    Parameters:
    - 128 + object - Number of bits to retrieve. + Cast the value to one of: `buffer, hex, json, string` @@ -474,6 +7188,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -499,7 +7222,7 @@
    Parameters:
    @@ -530,7 +7253,7 @@
    Returns:
    - Random bits from Generator. + Inner value of the Actor as an Object, or cast to the requested `format`.
    @@ -541,7 +7264,7 @@
    Returns:
    - Number + Object
    @@ -559,7 +7282,7 @@
    Returns:
    -

    slurp(nopt) → {Number}

    +

    (static) fromObjectString(inputopt) → {Array.<Buffer>}

    @@ -567,7 +7290,7 @@

    slurp - Get `n` bytes of deterministic random data. + Parse a JSON object of Buffer-like entries into an array of Buffers (legacy wire / script helper). @@ -607,13 +7330,13 @@
    Parameters:
    - n + input - Number + string @@ -634,12 +7357,12 @@
    Parameters:
    - 32 + '' - Number of bytes to retrieve. + @@ -682,7 +7405,7 @@
    Parameters:
    @@ -712,10 +7435,6 @@
    Parameters:
    Returns:
    -
    - Random bytes from Generator. -
    -
    @@ -724,7 +7443,7 @@
    Returns:
    - Number + Array.<Buffer>
    @@ -755,14 +7474,18 @@

    Classes

    Global


    diff --git a/docs/Message.html b/docs/Message.html index bf63ab4e6..c96054113 100644 --- a/docs/Message.html +++ b/docs/Message.html @@ -33,11 +33,32 @@

    Class: Message

    -

    Message(message) → {Message}

    - -
    The Message type defines the Application Messaging Protocol, or AMP. - Each Actor in the network receives and broadcasts messages, - selectively disclosing new routes to peers which may have open circuits.
    +

    Message()

    + +
    Application Messaging Protocol (AMP) — binary envelope for what Peer, + Service, and bridges actually exchange. Extends Actor for construction and state helpers, but on the wire + you think in opcodes, headers (parent, author as x-only pubkey, hash, preimage, + 64-byte Schnorr signature), and payload. + +

    SigningMessage#signWithKey / Message#verifyWithKey use BIP-340 Schnorr on tagged + hash Fabric/Message over header (signature field zeroed) + body. This is not Bitcoin Signed + Message (ECDSA + Core prefix).

    + +

    Type namesMessage#wireType / Message#type use SCREAMING_SNAKE wire labels from + opcode decode; Message#friendlyType and Message#toObject's type use PascalCase (or legacy) + JSON names. Message.wireTypeFromFriendly / Message.friendlyTypeFromWire bridge the two. See file header + maps (WIRE_TYPE_DECODE_ORDER, LEGACY_MESSAGE_TYPE_ALIASES) when aligning @fabric/http + or Hub.

    + +

    Body (V1) — Prefer Message.fromFields / Message#toFields with a + registered body schema (see body codec helpers on this module). Bodies are C-like typed fields, not JSON; + JSON bridging is @fabric/http. See docs/MESSAGE_BODY.md.

    + +

    Narrative — See DEVELOPERS.md (Actor and Message) and Actor + @fileoverview; home HTML is generated from DEVELOPERS.md, while this page comes from + types/message.js. +

    +
    @@ -52,72 +73,19 @@

    Constructor

    -

    new Message(message) → {Message}

    - - - - - - -
    - The `Message` type is standardized in Fabric as a Array, which can be added to any other vector to compute a resulting state. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    message +

    new Message()

    - Object -
    Message vector. Will be serialized by Array#_serialize.
    @@ -155,7 +123,7 @@
    Parameters:
    @@ -182,35 +150,24 @@
    Parameters:
    -
    Returns:
    - - -
    - Instance of the message. -
    - -
    -
    - Type -
    -
    - Message -
    -
    + +

    Extends

    + - @@ -229,13 +186,13 @@

    Members

    -

    _sensitive

    +

    _explicitPreimage

    - When true, body preimage field is zeroed on wire (no SHA256(body) commitment). + When true, an explicit HTLC / circuit payment preimage was set — do not clobber.
    @@ -275,7 +232,7 @@

    _sensitive<
    @@ -295,13 +252,14 @@

    _sensitive< -

    friendlyType

    +

    _sensitive

    - JSON-oriented type label (historical PascalCase aliases). Use in APIs and `toObject().type`. + When true, keep wire preimage zeroed (no payment secret). Default public + messages already use zeros — see Message#preimage (Lightning-style).
    @@ -341,7 +299,7 @@

    friendlyT
    @@ -361,16 +319,13 @@

    friendlyT -

    preimage

    +

    bodyBuffer

    - Optional 32-byte preimage on wire: - - **All zeros:** sensitive payload (no commitment) or legacy; Message#sensitive uses this. - - **SHA256(body):** default for non-sensitive messages (single digest; Message#hash is double-SHA256(body)). - - **Other:** explicit HTLC secret or custom (must match what was signed). + Raw body Buffer (preferred over UTF-8 `data` getter for binary field bodies).
    @@ -410,7 +365,7 @@

    preimage @@ -430,13 +385,13 @@

    preimagewireType

    +

    friendlyType

    - AMP wire type string (SCREAMING_SNAKE_CASE / opcode-canonical). Same as Message#type. + JSON-oriented type label (historical PascalCase aliases). Use in APIs and `toObject().type`.
    @@ -476,7 +431,7 @@

    wireType @@ -496,25 +451,18 @@

    wireTypeMethods

    - - - - - - - -

    _setSigner(key) → {Message}

    - - +

    preimage

    - Sets the signer for the message. + Optional 32-byte **payment** preimage on wire (Lightning-style): + - **All zeros (default / public):** no HTLC secret; body integrity is only Message#hash + (double-SHA256(body)). Do **not** put SHA256(body) here — that collides with circuit HTLC chains. + - **Non-zero:** explicit payment secret for inventory HTLC / Fabric Circuit hops + (`payment_hash = SHA256(preimage)`), covered by the Schnorr signature. + - Message#sensitive forces zeros and refuses to clobber an explicit secret.
    @@ -523,114 +471,84 @@

    _setSigner< +
    -
    Parameters:
    - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - Object - Key object with pubkey property. -
    Properties
    +
    Source:
    +
    + +
    - - - - - + - - - - - - - - - +
    -
    -
    NameTypeDescription
    pubkey +

    wireType

    - String - | - Buffer +
    + AMP wire type string (SCREAMING_SNAKE_CASE / opcode-canonical). Same as Message#type. +
    -
    Public key
    -
    -
    @@ -646,6 +564,14 @@
    Properties
    +
    Source:
    +
    + +
    @@ -653,91 +579,91 @@
    Properties
    +
    -
    Source:
    -
    - -
    +

    Methods

    -
    +

    _readObject(input) → {Object}

    +
    + Parse an Object into a corresponding Fabric state. +
    -
    Returns:
    -
    - Instance of the Message with associated signer. -
    -
    -
    - Type -
    -
    - Message +
    Parameters:
    -
    -
    + + + + + + + + + + + -

    asRaw() → {Buffer}

    + + + + +
    NameTypeDescription
    input + Object -
    - Returns a Buffer of the complete message. -
    +
    Object to read as input.
    @@ -750,6 +676,15 @@

    asRawInherited From: +
    + +
    @@ -775,7 +710,7 @@

    asRaw @@ -806,7 +741,7 @@
    Returns:
    - Buffer of the encoded Message. + Fabric state.
    @@ -817,7 +752,7 @@
    Returns:
    - Buffer + Object
    @@ -835,7 +770,7 @@
    Returns:
    -

    signWithKey(key) → {Message}

    +

    _setSigner(key) → {Message}

    @@ -843,9 +778,7 @@

    signWithKe
    - Signs the message using a specific key. - Uses BIP-340 Schnorr signatures with tagged hash "Fabric/Message". - Signs the complete message (header + body) as per C implementation. + Sets the signer for the message.
    @@ -897,7 +830,7 @@

    Parameters:
    - Key object with private key and sign method. + Key object with pubkey property.
    Properties
    @@ -923,7 +856,7 @@
    Properties
    - private + pubkey @@ -942,68 +875,6971 @@
    Properties
    - Private key + Public key + + - - - pubkey - - - - + + - String - | - Buffer + + - +
    - Public key - - - sign - - function - - Signing function - - - - - - - + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Message with associated signer. +
    + + + +
    +
    + Type +
    +
    + + Message + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    asRaw() → {Buffer}

    + + + + + + +
    + Returns a Buffer of the complete message. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Buffer of the encoded Message. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    signWithKey(key) → {Message}

    + + + + + + +
    + Signs the message using a specific key. + Uses BIP-340 Schnorr signatures with tagged hash "Fabric/Message". + Signs the complete message (header + body) as per C implementation. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + Object + + + + Key object with private key and sign method. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    private + + + String + | + + Buffer + + + + Private key
    pubkey + + + String + | + + Buffer + + + + Public key
    sign + + + function + + + + Signing function
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + +
    Throws:
    + + + +
    +
    +
    + If attempting to sign without a private key +
    +
    +
    +
    +
    +
    + Type +
    +
    + + Error + + +
    +
    +
    +
    +
    + + + + + +
    Returns:
    + + +
    + Signed message. +
    + + + +
    +
    + Type +
    +
    + + Message + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toFields() → {object|null}

    + + + + + + +
    + Decode body bytes via the registered field schema for this message type. + Truncated / malformed peer bodies return {@code null} (protocol violation) instead of throwing. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Field map, or null if no schema / empty / undecodable body. +
    + + + +
    +
    + Type +
    +
    + + object + | + + null + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    verify() → {Boolean}

    + + + + + + +
    + Verify a message's signature. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `true` if the signature is valid, `false` if not. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    verifyWithKey(key) → {Boolean}

    + + + + + + +
    + Verify a message's signature with a specific key. + Uses BIP-340 Schnorr signature verification with tagged hash "Fabric/Message". + Verifies the complete message (header + body) as per C implementation. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + Object + + + + Key object with verify method. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    verify + + + function + + + + Verification function
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `true` if the signature is valid, `false` if not. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    (static) fromFields(type, fieldsopt, optsopt) → {Message}

    + + + + + + +
    + Build a Message whose body is encoded from a registered field schema (V1). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + string + | + + number + + + + + + + + + + + + Wire or friendly type name (or opcode).
    fields + + + object + + + + + + <optional>
    + + + + + +
    + + {} + + Named fields matching the schema.
    opts + + + object + + + + + + <optional>
    + + + + + +
    + + {} + + Extra Message constructor options (`signer`, `sensitive`, …).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Message + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +

    Message(inputopt) → {Message}

    + + +
    + +
    +
    + + + + + + +

    new Message(inputopt) → {Message}

    + + + + + + +
    + Build a message from an object. Prefer type/data; @type / @data + are accepted for backward compatibility. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    input + + + Object + + + + + + <optional>
    + + + + + +
    + + {} + + Initial fields: type or @type, data or + @data, optional signer, sensitive, preimage. +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance ready for Message#asRaw, Message#signWithKey, etc. +
    + + + +
    +
    + Type +
    +
    + + Message + + +
    +
    + + + + + + + + +
    + + + + + + + + + + + + + + +

    Members

    + + + +

    _explicitPreimage

    + + + + +
    + When true, an explicit HTLC / circuit payment preimage was set — do not clobber. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _sensitive

    + + + + +
    + When true, keep wire preimage zeroed (no payment secret). Default public + messages already use zeros — see Message#preimage (Lightning-style). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    bodyBuffer

    + + + + +
    + Raw body Buffer (preferred over UTF-8 `data` getter for binary field bodies). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    friendlyType

    + + + + +
    + JSON-oriented type label (historical PascalCase aliases). Use in APIs and `toObject().type`. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    preimage

    + + + + +
    + Optional 32-byte **payment** preimage on wire (Lightning-style): + - **All zeros (default / public):** no HTLC secret; body integrity is only Message#hash + (double-SHA256(body)). Do **not** put SHA256(body) here — that collides with circuit HTLC chains. + - **Non-zero:** explicit payment secret for inventory HTLC / Fabric Circuit hops + (`payment_hash = SHA256(preimage)`), covered by the Schnorr signature. + - Message#sensitive forces zeros and refuses to clobber an explicit secret. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    wireType

    + + + + +
    + AMP wire type string (SCREAMING_SNAKE_CASE / opcode-canonical). Same as Message#type. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _setSigner(key) → {Message}

    + + + + + + +
    + Sets the signer for the message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + Object + + + + Key object with pubkey property. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    pubkey + + + String + | + + Buffer + + + + Public key
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Message with associated signer. +
    + + + +
    +
    + Type +
    +
    + + Message + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    asRaw() → {Buffer}

    + + + + + + +
    + Returns a Buffer of the complete message. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Buffer of the encoded Message. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    signWithKey(key) → {Message}

    + + + + + + +
    + Signs the message using a specific key. + Uses BIP-340 Schnorr signatures with tagged hash "Fabric/Message". + Signs the complete message (header + body) as per C implementation. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + Object + + + + Key object with private key and sign method. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    private + + + String + | + + Buffer + + + + Private key
    pubkey + + + String + | + + Buffer + + + + Public key
    sign + + + function + + + + Signing function
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + +
    Throws:
    + + + +
    +
    +
    + If attempting to sign without a private key +
    +
    +
    +
    +
    +
    + Type +
    +
    + + Error + + +
    +
    +
    +
    +
    + + + + + +
    Returns:
    + + +
    + Signed message. +
    + + + +
    +
    + Type +
    +
    + + Message + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toFields() → {object|null}

    + + + + + + +
    + Decode body bytes via the registered field schema for this message type. + Truncated / malformed peer bodies return {@code null} (protocol violation) instead of throwing. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Field map, or null if no schema / empty / undecodable body. +
    + + + +
    +
    + Type +
    +
    + + object + | + + null + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    verify() → {Boolean}

    + + + + + + +
    + Verify a message's signature. +
    + + + + + + + @@ -1041,7 +7877,7 @@
    Properties
    @@ -1066,87 +7902,158 @@
    Properties
    -
    Throws:
    + + +
    Returns:
    + + +
    + `true` if the signature is valid, `false` if not. +
    -
    - If attempting to sign without a private key -
    + Type
    -
    -
    -
    -
    - Type -
    -
    +
    - Error + Boolean -
    -
    -
    -
    +
    -
    Returns:
    -
    - Signed message. + + + + + + +

    verifyWithKey(key) → {Boolean}

    + + + + + + +
    + Verify a message's signature with a specific key. + Uses BIP-340 Schnorr signature verification with tagged hash "Fabric/Message". + Verifies the complete message (header + body) as per C implementation.
    -
    -
    - Type -
    -
    - Message -
    -
    +
    Parameters:
    + + + + + + + + + -

    verify() → {Boolean}

    + + + + + + + -
    - Verify a message's signature. -
    + + + + +
    NameTypeDescription
    key + + + Object + + + + Key object with verify method. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    verify + + + function + + + + Verification function
    + +
    @@ -1184,7 +8091,7 @@

    verify @@ -1244,7 +8151,7 @@
    Returns:
    -

    verifyWithKey(key) → {Boolean}

    +

    (static) fromFields(type, fieldsopt, optsopt) → {Message}

    @@ -1252,9 +8159,7 @@

    verifyWi
    - Verify a message's signature with a specific key. - Uses BIP-340 Schnorr signature verification with tagged hash "Fabric/Message". - Verifies the complete message (header + body) as per C implementation. + Build a Message whose body is encoded from a registered field schema (V1).
    @@ -1278,8 +8183,12 @@

    Parameters:
    Type + Attributes + + Default + Description @@ -1290,72 +8199,116 @@
    Parameters:
    - key + type - Object + string + | + + number + - Key object with verify method. -
    Properties
    - - - + - - + + + - - - - + + - - + - + - + + + + + + + + + + + + + + + + + + + + + + -
    NameType + Wire or friendly type name (or opcode).
    Description
    fields
    verify - + object - function + + <optional>
    -
    Verification function
    + + {} + + Named fields matching the schema.
    opts + + + object + + + + + + <optional>
    -
    + + + + + + + + + + {} + + + Extra Message constructor options (`signer`, `sensitive`, …). @@ -1398,7 +8351,7 @@
    Properties
    @@ -1428,10 +8381,6 @@
    Properties
    Returns:
    -
    - `true` if the signature is valid, `false` if not. -
    -
    @@ -1440,7 +8389,7 @@
    Returns:
    - Boolean + Message
    @@ -1471,14 +8420,18 @@

    Classes

    Global


    diff --git a/docs/Peer.html b/docs/Peer.html index ae50e1134..3f6e09dd3 100644 --- a/docs/Peer.html +++ b/docs/Peer.html @@ -33,9 +33,11 @@

    Class: Peer

    -

    Peer(configopt)

    +

    Peer()

    -
    An in-memory representation of a node in our network.
    +
    P2P node: TCP/NOISE sessions, gossip, and relay of Message (AMP) frames. Extends Service + (hence Actor). Opcode and receipt semantics must stay aligned with @fabric/http and Hub when you add types — + see Message wire vs friendly names and constants opcodes.
    @@ -50,16 +52,13 @@

    Constructor

    -

    new Peer(configopt)

    +

    new Peer()

    -
    - Create an instance of Peer. -
    @@ -69,258 +68,220 @@

    new PeerParameters:

    - - - - +
    -
    - - - - - - - - - - - - -
    NameTypeAttributesDescription
    config - Object - - <optional>
    +
    Source:
    +
    + +
    -
    Initialization Vector for this peer. -
    Properties
    - - - + - - - - - - - - - - - + - - - - - - - - +
    -
    - - - - - - - - - - + - - +

    _candidateKeys

    - - - - - -
    NameTypeAttributesDefaultDescription
    listen - Boolean - +

    Extends

    - <optional>
    + -
    - Whether or not to listen for connections.
    upnp +

    Members

    - Boolean +

    _authorizedDocumentKeyReveals

    -
    - <optional>
    +
    + Authorized sealed-document key reveals: `"documentId|paymentHashHex"` → meta. + Populated only after verified settlement (never from public hash echo). +
    -
    - Whether or not to use UPNP for automatic configuration.
    port - Number - - <optional>
    -
    +
    Source:
    +
    + +
    - 7777 -
    Port to use for P2P connections.
    peers - Array - - <optional>
    +
    + `host:port` keys for P2P_PEERING_OFFER candidate queue dedup. +
    + + + + + + + +
    -
    - [] - List of initial peers.
    -
    -
    +
    Source:
    +
    + +
    @@ -328,6 +289,7 @@
    Properties
    +
    @@ -336,29 +298,25 @@
    Properties
    +

    _chatRelayByOrigin

    +
    + origin address → { count, windowStart } for chat mesh relay rate limiting. +
    -
    Source:
    -
    - -
    +
    -
    @@ -380,9 +338,16 @@
    Properties
    -

    +
    Source:
    +
    + +
    @@ -390,22 +355,22 @@
    Properties
    +

    -

    Members

    -

    _candidateKeys

    +

    _contractPatchAllowList

    - `host:port` keys for P2P_PEERING_OFFER candidate queue dedup. + contractId → Set of lowercase compressed pubkeys allowed to apply state ops.
    @@ -445,7 +410,7 @@

    _candid
    @@ -465,13 +430,13 @@

    _candid -

    _gossipPayloadSeen

    +

    _documentRelayRoutes

    - Logical gossip payload dedup (excludes signature / hop churn). + Private reverse routes for rewritten DocumentRequests (never gossiped).
    @@ -511,7 +476,7 @@

    _go
    @@ -531,13 +496,13 @@

    _go -

    _gossipRelayByOrigin

    +

    _gossipPayloadSeen

    - origin address → { count, windowStart } for gossip relay rate limiting. + Logical gossip payload dedup (excludes signature / hop churn).
    @@ -577,7 +542,7 @@

    _
    @@ -597,16 +562,13 @@

    _ -

    _outboundDialTargets

    +

    _gossipRelayByOrigin

    - `host:port` strings we opened via Peer#_connect (outbound dials). - P2P_SESSION_OFFER must not destroy these when the same peer also opens an inbound - socket (mesh star): otherwise RPC paths that use the listen address (e.g. ChainSyncRequest) - see `peer not connected` while an ephemeral inbound key remains. + origin address → { count, windowStart } for gossip relay rate limiting.
    @@ -646,7 +608,7 @@

    _
    @@ -666,13 +628,13 @@

    _ -

    _peeringPayloadSeen

    +

    _inboundNoiseStaticPubkeyByAddress

    - Logical peering-offer payload dedup (ignores per-hop re-signing). + Inbound address -> NOISE static pubkey hex (FLUSH_CHAIN allowlist only; never for AMP verify).
    @@ -712,7 +674,7 @@

    _p
    @@ -732,13 +694,13 @@

    _p -

    _peeringRelayByOrigin

    +

    _logicalDupPenaltyByOrigin

    - origin address → { count, windowStart } for peering-offer relay rate limiting. + `host:port` → { windowStart, penalized } — soft logical-duplicate derank once per window.
    @@ -778,7 +740,7 @@

    @@ -798,17 +760,28 @@

    -

    _wireInboundByOrigin

    +

    _logicalRegisterOnce :Map.<string, {type: string, signer: (string|null), at: number}>

    - `host:port` → { credits, windowStart, penalized } — inbound wire flood / de-rank (per peer). + Logical first-writer-wins registrations (content-addressed keys). + Catches re-signed duplicates of CONTRACT_PUBLISH / DOCUMENT_PUBLISH / etc.
    +
    Type:
    +
      +
    • + + Map.<string, {type: string, signer: (string|null), at: number}> + + +
    • +
    + @@ -844,7 +817,7 @@

    _
    @@ -864,10 +837,17 @@

    _ -

    address

    +

    _outboundDialTargets

    + +
    + `host:port` strings we opened via Peer#_connect (outbound dials). + P2P_SESSION_OFFER must not destroy these when the same peer also opens an inbound + socket (mesh star): otherwise RPC paths that use the listen address (e.g. ChainSyncRequest) + see `peer not connected` while an ephemeral inbound key remains. +
    @@ -892,13 +872,6 @@

    addressDeprecated: -
    -
      -
    • Yes
    • -
    -
    - @@ -913,7 +886,7 @@

    address @@ -933,17 +906,27 @@

    addressmessages

    +

    _peerBans :Map.<string, {until: number, reason: string}>

    - Wire-envelope dedup (SHA-256 of full buffer); FIFO-capped via Peer#_rememberWireHash. + Temporary bans after hard misbehavior: `addr:` or `pk:` → { until, reason }.
    +
    Type:
    +
      +
    • + + Map.<string, {until: number, reason: string}> + + +
    • +
    + @@ -979,7 +962,7 @@

    messages @@ -999,26 +982,26 @@

    messages_peeringPayloadSeen

    -

    Methods

    +
    + Logical peering-offer payload dedup (ignores advisory peeringHop; frames relay as-is). +
    -

    _connect(target)

    +
    -
    - Open a Fabric connection to the target address and initiate the Fabric Protocol. -
    @@ -1028,53 +1011,52 @@

    _connectParameters:

    - - - - - - - - - - +
    Source:
    +
    + +
    - - - - - -
    NameTypeDescription
    target - String + - Target address.
    +

    _peeringRelayByOrigin

    + + + + +
    + origin address → { count, windowStart } for peering-offer relay rate limiting. +
    + @@ -1112,7 +1094,7 @@
    Parameters:
    @@ -1132,10 +1114,14 @@
    Parameters:
    +

    _wireInboundByOrigin

    +
    + `host:port` → { credits, windowStart, penalized } — inbound wire flood / de-rank (per peer). +
    @@ -1143,6 +1129,7 @@
    Parameters:
    +
    @@ -1150,15 +1137,38766 @@
    Parameters:
    -

    _derankPeerForWireTraffic(originName, penalty, reason)

    -
    - Lower registry Peer#knownPeers score for a connection (Bitcoin Core misbehavior analogue). + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    address

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Yes
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    blobTransfers

    + + + + +
    + Buyer-side verified blob reassembly (DocumentBlobIndex). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    messages

    + + + + +
    + Wire-envelope dedup (SHA-256 of full buffer); FIFO-capped via Peer#_rememberWireHash. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    pendingDocumentRequests

    + + + + +
    + Pending DOCUMENT_REQUEST entries when Peer#settings.autoFulfillDocumentRequests is false. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    pendingSealedDeliveries

    + + + + +
    + Ciphertext awaiting content-key reveal: documentId → meta. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + + String + + + + + + + + + + + + Path to store the value at.
    value + + + Object + + + + + + + + + + + + Document to store.
    commit + + + Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _announceAlias(alias, originopt, _socketopt)

    + + + + + + +
    + Broadcast a personal nickname as first-class P2P_PEER_ALIAS + (UTF-8 body = nickname text only). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    alias + + + string + + + + + + + + + + + +
    origin + + + Object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + Optional origin to exclude from broadcast
    _socket + + + * + + + + + + <optional>
    + + + + + +
    + + null + + Unused (API compatibility)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _announceLocalDocumentsToPeer(peerAddress)

    + + + + + + +
    + Re-send all local document publishes to one peer (same bytes as Peer#_publishDocument). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    peerAddress + + + string + + + + connection key in Peer#connections
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    _applyPeerMisbehavior(originName, reason, optsopt)

    + + + + + + +
    + Lower registry score and optionally destroy the TCP connection (hard misbehavior). + Hard disconnects also install a temporary ban (address + known pubkey). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    originName + + + string + | + + null + | + + undefined + + + + + + + + + +
    reason + + + string + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    penalty + + + number + + + + + + <optional>
    + + + + + +
    disconnect + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _attachHtlcOfferToItem(item, req, contentHashHex, amountSats) → {object|null}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    item + + + object + + + +
    req + + + object + + + + inventory request object
    contentHashHex + + + string + + + +
    amountSats + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _banPeer(originName, reason)

    + + + + + + +
    + Ban a connection address (and mapped pubkey when known) for Peer#settings.peerScore.banTtlMs. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + +
    reason + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _buildDocumentParsedForPublish(documentId, content) → {Object}

    + + + + + + +
    + Build hub-compatible document metadata for purchaseContentHashHex. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + String + + + +
    content + + + String + + + + UTF-8 body
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Parsed document record (whitelisted fields) +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _buildPublishDocumentWireBuffers(documentId, body, rateSats) → {Array.<Buffer>}

    + + + + + + +
    + AMP buffers for one document: canonical `DocumentPublish`, then optional pricing `P2P_DOCUMENT_PUBLISH`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + string + + + +
    body + + + string + + + + UTF-8 body
    rateSats + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Buffer> + + +
    +
    + + + + + + + + + + + + + +

    _chatRateLimitAllow(originName) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + Connection id (e.g. `host:port`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _claimLogicalRegistration(type, object, signerPubkeyHexopt) → {Object}

    + + + + + + +
    + Claim a logical registration key (first writer wins). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + string + + + + + + + + + + + +
    object + + + object + | + + null + | + + undefined + + + + + + + + + + + +
    signerPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _claimLogicalRegistrationOrPunish(type, object, signerPubkeyHexopt, originNameopt) → {Object}

    + + + + + + +
    + Claim logical registration; on duplicate, apply misbehavior and return claim. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + string + + + + + + + + + + + +
    object + + + object + | + + null + | + + undefined + + + + + + + + + + + +
    signerPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    originName + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _collectDocumentCatalogInventoryItems(reqopt) → {Array.<object>}

    + + + + + + +
    + Items for Hub-style `kind: 'documents'` inventory (Fabric UI / `@fabric/hub` merge expects `object.kind`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    req + + + Object + + + + + + <optional>
    + + + + + +
    request object subset
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<object> + + +
    +
    + + + + + + + + + + + + + +

    _connect(target)

    + + + + + + +
    + Open a Fabric connection to the target address and initiate the Fabric Protocol. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    target + + + String + + + + Target address.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _contractPublishSignerAuthorized(object, signerPubkeyHex) → {boolean}

    + + + + + + +
    + When a publish body declares authority arrays, the AMP wire signer must be + one of them. Bodies with no authorities are allowed (observe-only; empty + patch allow-list). Missing signer (local seed) is allowed. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    object + + + object + + + + + +
    signerPubkeyHex + + + string + | + + null + + + + + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _derankPeerForWireTraffic(originName, penalty, reason)

    + + + + + + +
    + Lower registry Peer#knownPeers score for a connection (Bitcoin Core misbehavior analogue). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + +
    penalty + + + number + + + +
    reason + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _enqueuePeeringCandidate(host, port)

    + + + + + + +
    + Enqueue a fabric candidate from P2P_PEERING_OFFER; FIFO-capped and deduped by host:port. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    host + + + string + + + +
    port + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _fillPeerSlots() → {Peer}

    + + + + + + +
    + Attempt to fill available connection slots with new peers. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the peer. +
    + + + +
    +
    + Type +
    +
    + + Peer + + +
    +
    + + + + + + + + + + + + + +

    _flushChainSenderPubkeyHex()

    + + + + + + +
    + FLUSH_CHAIN sender hex: Peer#peers[addr].publicKey if set, else inbound NOISE static (allowlist must match). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _getDocumentSealedMeta(documentId) → {object|null}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _gossipPayloadDedupKey(msg) → {string}

    + + + + + + +
    + Stable id for gossip *logical* content (ignores advisory `gossipHop`; frames are forwarded bit-identical). + Mesh frames are relayed bit-identical; wire-hash dedup also prevents loops. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + object + + + + Generic message (`type`, `object`, …)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + hex sha256 +
    + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    _gossipRateLimitAllow(originName) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + Connection id (e.g. `host:port`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _handleDocumentContentKeyReveal(reveal, originopt) → {Object}

    + + + + + + +
    + Apply a content-key reveal to a pending sealed delivery. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    reveal + + + Object + + + + + + + + + + + +
    origin + + + Object + + + + + + <optional>
    + + + + + +
    + + null + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _handleDocumentRequestWire(message, origin, socket, optionsopt)

    + + + + + + +
    + Handle inbound `DOCUMENT_REQUEST`: emit `documentRequest` / `DocumentRequest`, then either + send `P2P_FILE_SEND` (when Peer#settings.autoFulfillDocumentRequests), queue for + operator approve, or relay when the document is not held. + + Peel / foreign-signed `P2P_RELAY` deliveries are local-observe only: never fulfill or + queue against the TCP last hop, and never second-flood the inner under that hop. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    message + + + Message + + + + + + + + + + + +
    origin + + + Object + + + + + + + + + + + +
    socket + + + * + + + + + + + + + + + +
    options + + + Object + + + + + + <optional>
    + + + + + +
    + + null + + same delivery opts as Peer#_handleFabricMessage
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _handleFabricMessage(buffer, originopt, socketopt, optionsopt) → {Peer}

    + + + + + + +
    + Handle a Fabric Message buffer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    buffer + + + Buffer + + + + + + + + + + + +
    origin + + + object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    socket + + + object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    options + + + Object + + + + + + <optional>
    + + + + + +
    + + null + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    relayDepth + + + number + + + + + + <optional>
    + + + + + +
    skipRelayFlood + + + boolean + + + + + + <optional>
    + + + + + +
    peeledForward + + + boolean + + + + + + <optional>
    + + + + + +
    true when delivered via Peer#_handleP2PForward peel
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Peer. +
    + + + +
    +
    + Type +
    +
    + + Peer + + +
    +
    + + + + + + + + + + + + + +

    _ingestP2pFileSend(message, origin) → {Object}

    + + + + + + +
    + Verify / accumulate an inbound `P2P_FILE_SEND` against the DocumentBlobIndex rules. + Sealed frames store ciphertext until KEY_REVEAL_TYPE. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Message + | + + Object + + + +
    origin + + + Object + + + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _isPeerBanned(originName, pubkeyHexopt) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    originName + + + string + | + + null + | + + undefined + + + + + + + + + + + + null + +
    pubkeyHex + + + string + | + + null + | + + undefined + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    (async) _loadPeerRegistry() → {Promise.<void>}

    + + + + + + +
    + Load persistent peer registry from LevelDB. + Uses classic-level in Node, browser-level (IndexedDB) in browser. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<void> + + +
    +
    + + + + + + + + + + + + + +

    _localXOnlyPeerId() → {Buffer}

    + + + + + + +
    + Local node x-only pubkey (AMP {@code author} / {@code P2P_FORWARD.nextPeer} encoding). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    _logicalRegisterDuplicateMisbehavior(originName, type, claim, signerPubkeyHexopt)

    + + + + + + +
    + Soft (once/window) or hijack (CONTRACT_PUBLISH other signer) penalty for logical duplicates. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    originName + + + string + | + + null + | + + undefined + + + + + + + + + +
    type + + + string + + + + + + + + + +
    claim + + + Object + + + + + + + + + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    prior + + + Object + | + + null + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    signer + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + +
    + +
    signerPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _logicalRegistrationKey(type, object) → {string|null}

    + + + + + + +
    + Content-addressed key for first-writer-wins registration types. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    type + + + string + + + + wire / generic type name
    object + + + object + | + + null + | + + undefined + + + + message body / object
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + string + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _mayRevealDocumentContentKey(documentId, paymentHashHex, parsed, optsopt) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    paymentHashHex + + + string + + + + + + + + + +
    parsed + + + object + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    forceReveal + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _maybeReverseRelayFileSend(fileObj, origin) → {boolean}

    + + + + + + +
    + Forward a relayed `P2P_FILE_SEND` / key reveal back toward the buyer using reverse routes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    fileObj + + + object + + + +
    origin + + + Object + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if forwarded (caller should skip local ingest) +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _mergeContractPatchAllowList(contractId, object, _publisherPubkeyHexopt)

    + + + + + + +
    + Build the set of pubkeys allowed to apply CONTRACT_MESSAGE ops for a newly + registered contract. Called only on first registration of a contract id — + republishes must not invoke this (see Peer#_registerContract). + Membership is taken **only** from body authority arrays (`parties`, + `validators`, `owners`, `members`, `authorities`). The wire signer is never + granted rights unless already listed there. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    contractId + + + string + + + + + + + + + + + +
    object + + + object + + + + + + + + + + + + contract publish body
    _publisherPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + ignored (kept for call-site compat)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _peeringOfferPayloadDedupKey(msg) → {string}

    + + + + + + +
    + Stable id for peering-offer *logical* content (ignores advisory `peeringHop`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + object + + + + Generic message (`type`, `object`, …)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + hex sha256 +
    + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    _peeringRateLimitAllow(originName) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + Connection id (e.g. `host:port`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _privateRelayDocumentRequest(parsed, origin, originalMessageopt)

    + + + + + + +
    + Rewrite a budgeted DocumentRequest (privacy) and forward with reduced maxSats. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    parsed + + + object + + + + + + + + + + + +
    origin + + + Object + + + + + + + + + + + +
    originalMessage + + + Message + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _publishDocument(documentId, contentopt, rateSatsopt)

    + + + + + + +
    + Store a document locally and gossip to peers. + 1) **Canonical** `DOCUMENT_PUBLISH` wire message (same bytes as hub `documentPublishEnvelope`) for L1 `contentHash`. + 2) If `rateSats > 0`, a **pricing** `GENERIC` `P2P_DOCUMENT_PUBLISH` with `rate` and `contentHash` (sat ask). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    documentId + + + String + + + + + + + + + + + + Catalog key (e.g. CLI document name).
    content + + + String + + + + + + <optional>
    + + + + + +
    + + '' + + UTF-8 body stored under Peer#state.documents.
    rateSats + + + Number + + + + + + <optional>
    + + + + + +
    + + 0 + + Ask price in satoshis (gossip only; not part of canonical hash).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + + Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _registerContract(object, publisherPubkeyHexopt) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    object + + + object + + + + + + + + + + + +
    publisherPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true when newly registered (or already present no-op) +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _registryScoreForConnectionAddress(connAddress) → {number}

    + + + + + + +
    + Best-effort registry score for a live connection key (`host:port`), using mapped Fabric id when known. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    connAddress + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + number + + +
    +
    + + + + + + + + + + + + + +

    _registryScoreForFlushChainSender(connAddress, senderPubkeyHex) → {number}

    + + + + + + +
    + FLUSH_CHAIN trust score bound to verified sender key. + + Prevents trusting attacker-controlled `P2P_SESSION_OFFER.actor.id` aliases + by refusing `_addressToId`-mapped scores unless that mapped registry entry + is explicitly bound to the same verified sender pubkey. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    connAddress + + + string + + + +
    senderPubkeyHex + + + string + + + + verified sender pubkey hex (from NOISE/static or trusted peer record)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + number + + +
    +
    + + + + + + + + + + + + + +

    _relayGenericPayload()

    + + + + + + +
    + Relay inventory / generic payloads. When {@code wireMessage} is present, forward it + bit-identical (no hop re-sign). Otherwise the local agent originates a new signed frame + and may wrap it in {@code P2P_RELAY} for mesh delivery. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _relayWirePayload()

    + + + + + + +
    + Originate a locally signed {@code P2P_RELAY} envelope around already-signed inner AMP bytes. + Used only when *this* agent starts a flood (e.g. inventory without a prior wire frame). + Inbound {@code P2P_RELAY} must never call this — forward the original outer bit-identical. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _resolveAddressByXOnly(peerId) → {string|null}

    + + + + + + +
    + Resolve a live connection address for an x-only (or compressed) peer pubkey. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    peerId + + + Buffer + | + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + connection key ({@code host:port}) or null +
    + + + +
    +
    + Type +
    +
    + + string + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _respondInventoryFromLocalDocuments(message, origin) → {boolean}

    + + + + + + +
    + Reply to `INVENTORY_REQUEST` with `INVENTORY_RESPONSE` built from local documents and rates. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Object + + + + Generic body from Peer#_handleGenericMessage
    origin + + + Object + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if an `INVENTORY_RESPONSE` was written to the requester +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _savePeerRegistry()

    + + + + + + +
    + Persist peer registry to LevelDB (debounced). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _sendDocumentContentKeyReveal(documentId, peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Reveal the AES content key to a peer (only after payment-hash match). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    peerAddress + + + string + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    settlementId + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _sendLocalInventoryDocumentsWireResponse(originName, items, optsopt) → {boolean}

    + + + + + + +
    + Write INVENTORY_RESPONSE (`P2P_INVENTORY_RESPONSE`) compatible with `@fabric/hub` Bridge merging + (body includes `kind: 'documents'` so the browser can merge `object.items`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    originName + + + string + + + + + + + + + + connection key Peer#connections
    items + + + Array.<object> + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    allowEmpty + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _sendP2pFileSendToPeer(documentId, peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Send a locally stored document as indexed, wire-sized `P2P_FILE_SEND` blobs. + Priced sealed docs send **ciphertext** (safe without the content key). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    peerAddress + + + string + + + + + + + + + + connection key in Peer#connections
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    blobIndex + + + number + + + + + + <optional>
    + + + + + +
    revealKey + + + boolean + + + + + + <optional>
    + + + + + +
    settlementId + + + string + + + + + + <optional>
    + + + + + +
    routeId + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if at least one frame was written +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _sendPrivateRelayedDocumentRequest(msg, origin, parsed) → {boolean}

    + + + + + + +
    + Deliver a rewritten private DocumentRequest without mesh broadcast. + Preference: onion `relayPath` → explicit `nextPeer` → fan-out to TCP peers + other than the inbound origin. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + signed DocumentRequest
    origin + + + Object + | + + null + + + +
    parsed + + + object + + + + inbound request body
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _signerMayPatchContract(contractId, signerPubkeyHex) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    contractId + + + string + + + +
    signerPubkeyHex + + + string + | + + null + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _startFabricPingKeepalive(socket, encryptWrite)

    + + + + + + +
    + Periodic P2P_PING and track expected P2P_PONG replies so registry score cannot be + self-inflated by unsolicited pongs (see FLUSH_CHAIN trust gate). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    socket + + + * + + + + — connection object (stores `_fabricPingOutstanding`, `_keepalive`)
    encryptWrite + + + * + + + + — NOISE encrypt stream with `.write(Buffer)` (`client.encrypt` / `handler.encrypt`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _upsertPeerRegistry(address, updatesopt)

    + + + + + + +
    + Upsert a peer into the persistent registry (state.peers) and schedule save to LevelDB. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    address + + + string + + + + + + + + + + Peer address (e.g. host:port).
    updates + + + Object + + + + + + <optional>
    + + + + + +
    Fields to set/merge (id, score, firstSeen, lastSeen, alias, publicKey).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _wireInboundCreditCost(wireType) → {number}

    + + + + + + +
    + Credit cost for inbound wire messages (heavier types consume more of the peer's budget). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    wireType + + + string + | + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + number + + +
    +
    + + + + + + + + + + + + + +

    _wireInboundRateAllowPeer(originName, creditCost) → {boolean}

    + + + + + + +
    + Apply rolling-window credits; on overflow, de-rank once per window and reject the message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + connection key (host:port)
    creditCost + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + false = drop message +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    approveDocumentRequest(requestKey) → {Object}

    + + + + + + +
    + Approve a pending DOCUMENT_REQUEST and send `P2P_FILE_SEND`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    requestKey + + + string + + + + pending key, or document id when unique
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    authorizeDocumentKeyReveal(opts) → {Object}

    + + + + + + +
    + Record that settlement for a sealed document was verified so a matching + DocumentRequest may receive the AES content key (not merely the ciphertext). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    opts + + + object + + + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    contentHashHex + + + string + + + + + + + + + + payment hash SHA256(K)
    settlementId + + + string + + + + + + <optional>
    + + + + + +
    txid + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ ok, error?, key? }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    broadcast(message)

    + + + + + + +
    + Write a Buffer to all connected peers. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Buffer + + + + Message buffer to send.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + + Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    denyDocumentRequest(requestKey) → {Object}

    + + + + + + +
    + Deny / drop a pending DOCUMENT_REQUEST without sending bytes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    requestKey + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    listPendingDocumentRequests() → {Array.<object>}

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + pending DOCUMENT_REQUEST rows (consent mode) +
    + + + +
    +
    + Type +
    +
    + + Array.<object> + + +
    +
    + + + + + + + + + + + + + +

    (async) listen() → {Peer}

    + + + + + + +
    + Start listening for connections. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Peer + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    openSealedDeliveryWithPreimage(documentId, preimageHex) → {Object}

    + + + + + + +
    + Open a pending sealed delivery using an HTLC claim preimage (on-chain witness). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + string + + + +
    preimageHex + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    relayFromTrustedPeers(origin, message, minScoreExclusiveopt)

    + + + + + + +
    + Relay an AMP message only to connected peers whose persistent registry score is strictly greater than + Peer#settings.flushChainMinTrustedScore (default 800). Used for `P2P_FLUSH_CHAIN`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    origin + + + string + | + + null + + + + + + + + + + + + Connection key to skip (inbound sender), or null when originating locally.
    message + + + Message + | + + Buffer + + + + + + + + + + + +
    minScoreExclusive + + + number + + + + + + <optional>
    + + + + + +
    + + null + + Override trust threshold (relay if peer score > this value).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    requestDocument(documentId, peerAddressopt, optsopt) → {boolean}

    + + + + + + +
    + Send a signed `DocumentRequest` to one peer (or broadcast when peerAddress is omitted). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    documentId + + + string + + + + + + + + + + + +
    peerAddress + + + string + + + + + + <optional>
    + + + + + +
    + + null + +
    opts + + + object + + + + + + <optional>
    + + + + + +
    + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    maxSats + + + number + + + + + + <optional>
    + + + + + +
    relayHop + + + number + + + + + + <optional>
    + + + + + +
    blobIndex + + + number + + + + + + <optional>
    + + + + + +
    blobTotal + + + number + + + + + + <optional>
    + + + + + +
    contentHashHex + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    requestPeerInventory(peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Ask a connected peer for their document catalog (`kind: 'documents'`) or L1 offers (`offerBtc`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    peerAddress + + + string + + + + + + + + + + connection key, Fabric id, or host:port
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    offerBtc + + + boolean + + + + + + <optional>
    + + + + + +
    + + false + +
    kind + + + string + + + + + + <optional>
    + + + + + +
    + + 'documents' + +
    maxSats + + + number + + + + + + <optional>
    + + + + + +
    + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    sendDocumentFileToPeer(documentId, peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Public helper: push document bytes to a peer (same wire path as Peer#_handleDocumentRequestWire fulfillment). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    peerAddress + + + string + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    blobIndex + + + number + + + + + + <optional>
    + + + + + +
    revealKey + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    sendFlushChainToTrustedPeers(object) → {number}

    + + + + + + +
    + Sign and send `P2P_FLUSH_CHAIN` to all connected peers with registry score > threshold. + Body JSON: `{ snapshotBlockHash, network?, label? }`. + + **Receivers** (see `P2P_FLUSH_CHAIN` handler) require **both**: + 1. Sender pubkey in Peer#settings.flushChainAuthorizedPubkeys (non-empty allowlist), and + 2. Registry score above Peer#settings.flushChainMinTrustedScore. + Registry score bumps on `P2P_PONG` only when that pong answers an outbound ping on the same + connection (`_fabricPingOutstanding`), so unsolicited pongs cannot inflate trust alone. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    object + + + Object + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + number of sockets written +
    + + + +
    +
    + Type +
    +
    + + number + + +
    +
    + + + + + + + + + + + + + +

    sendOnion(path, payload) → {boolean}

    + + + + + + +
    + Send {@code payload} along a source-routed onion path of Fabric peer pubkeys. + Builds nested {@code P2P_FORWARD} layers and writes the outer frame only to + {@code path[0]} (immediate hop). Destination learns the last hop's IP, not + the originator's. See module:@fabric/core/functions/fabricOnion. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Array.<(Buffer|string)> + + + + hop pubkeys; first = next TCP peer, last = deliverer
    payload + + + Message + | + + Buffer + + + + innermost application Message (should already be signed)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if the outer frame was written to the first hop +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start()

    + + + + + + +
    + Start the Peer. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) stop()

    + + + + + + +
    + Stop the peer. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Service}

    + + + + + + +
    + Explicitly trust all events from a known source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Emitter of events.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of Service after binding events. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    when(event, method) → {EventEmitter}

    + + + + + + +
    + Bind a method to an event, with current state as the immutable context. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    event + + + String + + + + Name of the event upon which to execute `method` as a function.
    method + + + function + + + + Function to execute when named Event `event` is encountered.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of EventEmitter. +
    + + + +
    +
    + Type +
    +
    + + EventEmitter + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +

    Peer(configopt)

    + + +
    + +
    +
    + + + + + + +

    new Peer(configopt)

    + + + + + + +
    + Create an instance of Peer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    config + + + Object + + + + + + <optional>
    + + + + + +
    Initialization Vector for this peer. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    listen + + + Boolean + + + + + + <optional>
    + + + + + +
    + + Whether or not to listen for connections.
    upnp + + + Boolean + + + + + + <optional>
    + + + + + +
    + + Whether or not to use UPNP for automatic configuration.
    port + + + Number + + + + + + <optional>
    + + + + + +
    + + 7777 + + Port to use for P2P connections.
    listenPortAttempts + + + Number + + + + + + <optional>
    + + + + + +
    + + 20 + + When the listen port is in use (`EADDRINUSE`), + try the next port up to this many times (same host).
    peers + + + Array + + + + + + <optional>
    + + + + + +
    + + [] + + List of initial peers.
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + +

    Members

    + + + +

    _authorizedDocumentKeyReveals

    + + + + +
    + Authorized sealed-document key reveals: `"documentId|paymentHashHex"` → meta. + Populated only after verified settlement (never from public hash echo). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _candidateKeys

    + + + + +
    + `host:port` keys for P2P_PEERING_OFFER candidate queue dedup. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _chatRelayByOrigin

    + + + + +
    + origin address → { count, windowStart } for chat mesh relay rate limiting. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _contractPatchAllowList

    + + + + +
    + contractId → Set of lowercase compressed pubkeys allowed to apply state ops. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _documentRelayRoutes

    + + + + +
    + Private reverse routes for rewritten DocumentRequests (never gossiped). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _gossipPayloadSeen

    + + + + +
    + Logical gossip payload dedup (excludes signature / hop churn). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _gossipRelayByOrigin

    + + + + +
    + origin address → { count, windowStart } for gossip relay rate limiting. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _inboundNoiseStaticPubkeyByAddress

    + + + + +
    + Inbound address -> NOISE static pubkey hex (FLUSH_CHAIN allowlist only; never for AMP verify). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _logicalDupPenaltyByOrigin

    + + + + +
    + `host:port` → { windowStart, penalized } — soft logical-duplicate derank once per window. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _logicalRegisterOnce :Map.<string, {type: string, signer: (string|null), at: number}>

    + + + + +
    + Logical first-writer-wins registrations (content-addressed keys). + Catches re-signed duplicates of CONTRACT_PUBLISH / DOCUMENT_PUBLISH / etc. +
    + + + +
    Type:
    +
      +
    • + + Map.<string, {type: string, signer: (string|null), at: number}> + + +
    • +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _outboundDialTargets

    + + + + +
    + `host:port` strings we opened via Peer#_connect (outbound dials). + P2P_SESSION_OFFER must not destroy these when the same peer also opens an inbound + socket (mesh star): otherwise RPC paths that use the listen address (e.g. ChainSyncRequest) + see `peer not connected` while an ephemeral inbound key remains. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _peerBans :Map.<string, {until: number, reason: string}>

    + + + + +
    + Temporary bans after hard misbehavior: `addr:` or `pk:` → { until, reason }. +
    + + + +
    Type:
    +
      +
    • + + Map.<string, {until: number, reason: string}> + + +
    • +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _peeringPayloadSeen

    + + + + +
    + Logical peering-offer payload dedup (ignores advisory peeringHop; frames relay as-is). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _peeringRelayByOrigin

    + + + + +
    + origin address → { count, windowStart } for peering-offer relay rate limiting. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    _wireInboundByOrigin

    + + + + +
    + `host:port` → { credits, windowStart, penalized } — inbound wire flood / de-rank (per peer). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    address

    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    Deprecated:
    +
    +
      +
    • Yes
    • +
    +
    + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    blobTransfers

    + + + + +
    + Buyer-side verified blob reassembly (DocumentBlobIndex). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    messages

    + + + + +
    + Wire-envelope dedup (SHA-256 of full buffer); FIFO-capped via Peer#_rememberWireHash. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    pendingDocumentRequests

    + + + + +
    + Pending DOCUMENT_REQUEST entries when Peer#settings.autoFulfillDocumentRequests is false. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + +

    pendingSealedDeliveries

    + + + + +
    + Ciphertext awaiting content-key reveal: documentId → meta. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + + String + + + + + + + + + + + + Path to store the value at.
    value + + + Object + + + + + + + + + + + + Document to store.
    commit + + + Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _announceAlias(alias, originopt, _socketopt)

    + + + + + + +
    + Broadcast a personal nickname as first-class P2P_PEER_ALIAS + (UTF-8 body = nickname text only). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    alias + + + string + + + + + + + + + + + +
    origin + + + Object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + Optional origin to exclude from broadcast
    _socket + + + * + + + + + + <optional>
    + + + + + +
    + + null + + Unused (API compatibility)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _announceLocalDocumentsToPeer(peerAddress)

    + + + + + + +
    + Re-send all local document publishes to one peer (same bytes as Peer#_publishDocument). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    peerAddress + + + string + + + + connection key in Peer#connections
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    _applyPeerMisbehavior(originName, reason, optsopt)

    + + + + + + +
    + Lower registry score and optionally destroy the TCP connection (hard misbehavior). + Hard disconnects also install a temporary ban (address + known pubkey). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    originName + + + string + | + + null + | + + undefined + + + + + + + + + +
    reason + + + string + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    penalty + + + number + + + + + + <optional>
    + + + + + +
    disconnect + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _attachHtlcOfferToItem(item, req, contentHashHex, amountSats) → {object|null}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    item + + + object + + + +
    req + + + object + + + + inventory request object
    contentHashHex + + + string + + + +
    amountSats + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _banPeer(originName, reason)

    + + + + + + +
    + Ban a connection address (and mapped pubkey when known) for Peer#settings.peerScore.banTtlMs. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + +
    reason + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _buildDocumentParsedForPublish(documentId, content) → {Object}

    + + + + + + +
    + Build hub-compatible document metadata for purchaseContentHashHex. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + String + + + +
    content + + + String + + + + UTF-8 body
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Parsed document record (whitelisted fields) +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _buildPublishDocumentWireBuffers(documentId, body, rateSats) → {Array.<Buffer>}

    + + + + + + +
    + AMP buffers for one document: canonical `DocumentPublish`, then optional pricing `P2P_DOCUMENT_PUBLISH`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + string + + + +
    body + + + string + + + + UTF-8 body
    rateSats + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Buffer> + + +
    +
    + + + + + + + + + + + + + +

    _chatRateLimitAllow(originName) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + Connection id (e.g. `host:port`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _claimLogicalRegistration(type, object, signerPubkeyHexopt) → {Object}

    + + + + + + +
    + Claim a logical registration key (first writer wins). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + string + + + + + + + + + + + +
    object + + + object + | + + null + | + + undefined + + + + + + + + + + + +
    signerPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _claimLogicalRegistrationOrPunish(type, object, signerPubkeyHexopt, originNameopt) → {Object}

    + + + + + + +
    + Claim logical registration; on duplicate, apply misbehavior and return claim. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + string + + + + + + + + + + + +
    object + + + object + | + + null + | + + undefined + + + + + + + + + + + +
    signerPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    originName + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _collectDocumentCatalogInventoryItems(reqopt) → {Array.<object>}

    + + + + + + +
    + Items for Hub-style `kind: 'documents'` inventory (Fabric UI / `@fabric/hub` merge expects `object.kind`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    req + + + Object + + + + + + <optional>
    + + + + + +
    request object subset
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<object> + + +
    +
    + + + + + + + + + + + + + +

    _connect(target)

    + + + + + + +
    + Open a Fabric connection to the target address and initiate the Fabric Protocol. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    target + + + String + + + + Target address.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _contractPublishSignerAuthorized(object, signerPubkeyHex) → {boolean}

    + + + + + + +
    + When a publish body declares authority arrays, the AMP wire signer must be + one of them. Bodies with no authorities are allowed (observe-only; empty + patch allow-list). Missing signer (local seed) is allowed. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    object + + + object + + + + + +
    signerPubkeyHex + + + string + | + + null + + + + + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _derankPeerForWireTraffic(originName, penalty, reason)

    + + + + + + +
    + Lower registry Peer#knownPeers score for a connection (Bitcoin Core misbehavior analogue). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + +
    penalty + + + number + + + +
    reason + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _enqueuePeeringCandidate(host, port)

    + + + + + + +
    + Enqueue a fabric candidate from P2P_PEERING_OFFER; FIFO-capped and deduped by host:port. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    host + + + string + + + +
    port + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _fillPeerSlots() → {Peer}

    + + + + + + +
    + Attempt to fill available connection slots with new peers. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the peer. +
    + + + +
    +
    + Type +
    +
    + + Peer + + +
    +
    + + + + + + + + + + + + + +

    _flushChainSenderPubkeyHex()

    + + + + + + +
    + FLUSH_CHAIN sender hex: Peer#peers[addr].publicKey if set, else inbound NOISE static (allowlist must match). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _getDocumentSealedMeta(documentId) → {object|null}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + object + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _gossipPayloadDedupKey(msg) → {string}

    + + + + + + +
    + Stable id for gossip *logical* content (ignores advisory `gossipHop`; frames are forwarded bit-identical). + Mesh frames are relayed bit-identical; wire-hash dedup also prevents loops. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + object + + + + Generic message (`type`, `object`, …)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + hex sha256 +
    + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    _gossipRateLimitAllow(originName) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + Connection id (e.g. `host:port`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _handleDocumentContentKeyReveal(reveal, originopt) → {Object}

    + + + + + + +
    + Apply a content-key reveal to a pending sealed delivery. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    reveal + + + Object + + + + + + + + + + + +
    origin + + + Object + + + + + + <optional>
    + + + + + +
    + + null + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _handleDocumentRequestWire(message, origin, socket, optionsopt)

    + + + + + + +
    + Handle inbound `DOCUMENT_REQUEST`: emit `documentRequest` / `DocumentRequest`, then either + send `P2P_FILE_SEND` (when Peer#settings.autoFulfillDocumentRequests), queue for + operator approve, or relay when the document is not held. + + Peel / foreign-signed `P2P_RELAY` deliveries are local-observe only: never fulfill or + queue against the TCP last hop, and never second-flood the inner under that hop. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    message + + + Message + + + + + + + + + + + +
    origin + + + Object + + + + + + + + + + + +
    socket + + + * + + + + + + + + + + + +
    options + + + Object + + + + + + <optional>
    + + + + + +
    + + null + + same delivery opts as Peer#_handleFabricMessage
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _handleFabricMessage(buffer, originopt, socketopt, optionsopt) → {Peer}

    + + + + + + +
    + Handle a Fabric Message buffer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    buffer + + + Buffer + + + + + + + + + + + +
    origin + + + object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    socket + + + object + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    options + + + Object + + + + + + <optional>
    + + + + + +
    + + null + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    relayDepth + + + number + + + + + + <optional>
    + + + + + +
    skipRelayFlood + + + boolean + + + + + + <optional>
    + + + + + +
    peeledForward + + + boolean + + + + + + <optional>
    + + + + + +
    true when delivered via Peer#_handleP2PForward peel
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Peer. +
    + + + +
    +
    + Type +
    +
    + + Peer + + +
    +
    + + + + + + + + + + + + + +

    _ingestP2pFileSend(message, origin) → {Object}

    + + + + + + +
    + Verify / accumulate an inbound `P2P_FILE_SEND` against the DocumentBlobIndex rules. + Sealed frames store ciphertext until KEY_REVEAL_TYPE. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Message + | + + Object + + + +
    origin + + + Object + + + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    _isPeerBanned(originName, pubkeyHexopt) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    originName + + + string + | + + null + | + + undefined + + + + + + + + + + + + null + +
    pubkeyHex + + + string + | + + null + | + + undefined + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    (async) _loadPeerRegistry() → {Promise.<void>}

    + + + + + + +
    + Load persistent peer registry from LevelDB. + Uses classic-level in Node, browser-level (IndexedDB) in browser. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Promise.<void> + + +
    +
    + + + + + + + + + + + + + +

    _localXOnlyPeerId() → {Buffer}

    + + + + + + +
    + Local node x-only pubkey (AMP {@code author} / {@code P2P_FORWARD.nextPeer} encoding). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    _logicalRegisterDuplicateMisbehavior(originName, type, claim, signerPubkeyHexopt)

    + + + + + + +
    + Soft (once/window) or hijack (CONTRACT_PUBLISH other signer) penalty for logical duplicates. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    originName + + + string + | + + null + | + + undefined + + + + + + + + + +
    type + + + string + + + + + + + + + +
    claim + + + Object + + + + + + + + + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    prior + + + Object + | + + null + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    signer + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + +
    + +
    signerPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _logicalRegistrationKey(type, object) → {string|null}

    + + + + + + +
    + Content-addressed key for first-writer-wins registration types. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    type + + + string + + + + wire / generic type name
    object + + + object + | + + null + | + + undefined + + + + message body / object
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + string + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _mayRevealDocumentContentKey(documentId, paymentHashHex, parsed, optsopt) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    paymentHashHex + + + string + + + + + + + + + +
    parsed + + + object + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    forceReveal + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _maybeReverseRelayFileSend(fileObj, origin) → {boolean}

    + + + + + + +
    + Forward a relayed `P2P_FILE_SEND` / key reveal back toward the buyer using reverse routes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    fileObj + + + object + + + +
    origin + + + Object + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if forwarded (caller should skip local ingest) +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _mergeContractPatchAllowList(contractId, object, _publisherPubkeyHexopt)

    + + + + + + +
    + Build the set of pubkeys allowed to apply CONTRACT_MESSAGE ops for a newly + registered contract. Called only on first registration of a contract id — + republishes must not invoke this (see Peer#_registerContract). + Membership is taken **only** from body authority arrays (`parties`, + `validators`, `owners`, `members`, `authorities`). The wire signer is never + granted rights unless already listed there. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    contractId + + + string + + + + + + + + + + + +
    object + + + object + + + + + + + + + + + + contract publish body
    _publisherPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + + ignored (kept for call-site compat)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _peeringOfferPayloadDedupKey(msg) → {string}

    + + + + + + +
    + Stable id for peering-offer *logical* content (ignores advisory `peeringHop`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + object + + + + Generic message (`type`, `object`, …)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + hex sha256 +
    + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    _peeringRateLimitAllow(originName) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + Connection id (e.g. `host:port`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _privateRelayDocumentRequest(parsed, origin, originalMessageopt)

    + + + + + + +
    + Rewrite a budgeted DocumentRequest (privacy) and forward with reduced maxSats. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    parsed + + + object + + + + + + + + + + + +
    origin + + + Object + + + + + + + + + + + +
    originalMessage + + + Message + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _publishDocument(documentId, contentopt, rateSatsopt)

    + + + + + + +
    + Store a document locally and gossip to peers. + 1) **Canonical** `DOCUMENT_PUBLISH` wire message (same bytes as hub `documentPublishEnvelope`) for L1 `contentHash`. + 2) If `rateSats > 0`, a **pricing** `GENERIC` `P2P_DOCUMENT_PUBLISH` with `rate` and `contentHash` (sat ask). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    documentId + + + String + + + + + + + + + + + + Catalog key (e.g. CLI document name).
    content + + + String + + + + + + <optional>
    + + + + + +
    + + '' + + UTF-8 body stored under Peer#state.documents.
    rateSats + + + Number + + + + + + <optional>
    + + + + + +
    + + 0 + + Ask price in satoshis (gossip only; not part of canonical hash).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + + Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _registerContract(object, publisherPubkeyHexopt) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    object + + + object + + + + + + + + + + + +
    publisherPubkeyHex + + + string + | + + null + + + + + + <optional>
    + + + + + +
    + + null + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true when newly registered (or already present no-op) +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _registryScoreForConnectionAddress(connAddress) → {number}

    + + + + + + +
    + Best-effort registry score for a live connection key (`host:port`), using mapped Fabric id when known. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    connAddress + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + number + + +
    +
    + + + + + + + + + + + + + +

    _registryScoreForFlushChainSender(connAddress, senderPubkeyHex) → {number}

    + + + + + + +
    + FLUSH_CHAIN trust score bound to verified sender key. + + Prevents trusting attacker-controlled `P2P_SESSION_OFFER.actor.id` aliases + by refusing `_addressToId`-mapped scores unless that mapped registry entry + is explicitly bound to the same verified sender pubkey. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    connAddress + + + string + + + +
    senderPubkeyHex + + + string + + + + verified sender pubkey hex (from NOISE/static or trusted peer record)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + number + + +
    +
    + + + + + + + + + + + + + +

    _relayGenericPayload()

    + + + + + + +
    + Relay inventory / generic payloads. When {@code wireMessage} is present, forward it + bit-identical (no hop re-sign). Otherwise the local agent originates a new signed frame + and may wrap it in {@code P2P_RELAY} for mesh delivery. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _relayWirePayload()

    + + + + + + +
    + Originate a locally signed {@code P2P_RELAY} envelope around already-signed inner AMP bytes. + Used only when *this* agent starts a flood (e.g. inventory without a prior wire frame). + Inbound {@code P2P_RELAY} must never call this — forward the original outer bit-identical. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _resolveAddressByXOnly(peerId) → {string|null}

    + + + + + + +
    + Resolve a live connection address for an x-only (or compressed) peer pubkey. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    peerId + + + Buffer + | + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + connection key ({@code host:port}) or null +
    + + + +
    +
    + Type +
    +
    + + string + | + + null + + +
    +
    + + + + + + + + + + + + + +

    _respondInventoryFromLocalDocuments(message, origin) → {boolean}

    + + + + + + +
    + Reply to `INVENTORY_REQUEST` with `INVENTORY_RESPONSE` built from local documents and rates. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Object + + + + Generic body from Peer#_handleGenericMessage
    origin + + + Object + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if an `INVENTORY_RESPONSE` was written to the requester +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _savePeerRegistry()

    + + + + + + +
    + Persist peer registry to LevelDB (debounced). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _sendDocumentContentKeyReveal(documentId, peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Reveal the AES content key to a peer (only after payment-hash match). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    peerAddress + + + string + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    settlementId + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _sendLocalInventoryDocumentsWireResponse(originName, items, optsopt) → {boolean}

    + + + + + + +
    + Write INVENTORY_RESPONSE (`P2P_INVENTORY_RESPONSE`) compatible with `@fabric/hub` Bridge merging + (body includes `kind: 'documents'` so the browser can merge `object.items`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    originName + + + string + + + + + + + + + + connection key Peer#connections
    items + + + Array.<object> + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    allowEmpty + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _sendP2pFileSendToPeer(documentId, peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Send a locally stored document as indexed, wire-sized `P2P_FILE_SEND` blobs. + Priced sealed docs send **ciphertext** (safe without the content key). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    peerAddress + + + string + + + + + + + + + + connection key in Peer#connections
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    blobIndex + + + number + + + + + + <optional>
    + + + + + +
    revealKey + + + boolean + + + + + + <optional>
    + + + + + +
    settlementId + + + string + + + + + + <optional>
    + + + + + +
    routeId + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if at least one frame was written +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _sendPrivateRelayedDocumentRequest(msg, origin, parsed) → {boolean}

    + + + + + + +
    + Deliver a rewritten private DocumentRequest without mesh broadcast. + Preference: onion `relayPath` → explicit `nextPeer` → fan-out to TCP peers + other than the inbound origin. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + signed DocumentRequest
    origin + + + Object + | + + null + + + +
    parsed + + + object + + + + inbound request body
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _signerMayPatchContract(contractId, signerPubkeyHex) → {boolean}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    contractId + + + string + + + +
    signerPubkeyHex + + + string + | + + null + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    _startFabricPingKeepalive(socket, encryptWrite)

    + + + + + + +
    + Periodic P2P_PING and track expected P2P_PONG replies so registry score cannot be + self-inflated by unsolicited pongs (see FLUSH_CHAIN trust gate). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    socket + + + * + + + + — connection object (stores `_fabricPingOutstanding`, `_keepalive`)
    encryptWrite + + + * + + + + — NOISE encrypt stream with `.write(Buffer)` (`client.encrypt` / `handler.encrypt`)
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _upsertPeerRegistry(address, updatesopt)

    + + + + + + +
    + Upsert a peer into the persistent registry (state.peers) and schedule save to LevelDB. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    address + + + string + + + + + + + + + + Peer address (e.g. host:port).
    updates + + + Object + + + + + + <optional>
    + + + + + +
    Fields to set/merge (id, score, firstSeen, lastSeen, alias, publicKey).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    _wireInboundCreditCost(wireType) → {number}

    + + + + + + +
    + Credit cost for inbound wire messages (heavier types consume more of the peer's budget). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    wireType + + + string + | + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + number + + +
    +
    + + + + + + + + + + + + + +

    _wireInboundRateAllowPeer(originName, creditCost) → {boolean}

    + + + + + + +
    + Apply rolling-window credits; on overflow, de-rank once per window and reject the message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    originName + + + string + + + + connection key (host:port)
    creditCost + + + number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + false = drop message +
    + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    approveDocumentRequest(requestKey) → {Object}

    + + + + + + +
    + Approve a pending DOCUMENT_REQUEST and send `P2P_FILE_SEND`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    requestKey + + + string + + + + pending key, or document id when unique
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    authorizeDocumentKeyReveal(opts) → {Object}

    + + + + + + +
    + Record that settlement for a sealed document was verified so a matching + DocumentRequest may receive the AES content key (not merely the ciphertext). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    opts + + + object + + + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    contentHashHex + + + string + + + + + + + + + + payment hash SHA256(K)
    settlementId + + + string + + + + + + <optional>
    + + + + + +
    txid + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ ok, error?, key? }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    broadcast(message)

    + + + + + + +
    + Write a Buffer to all connected peers. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Buffer + + + + Message buffer to send.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + + Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    denyDocumentRequest(requestKey) → {Object}

    + + + + + + +
    + Deny / drop a pending DOCUMENT_REQUEST without sending bytes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    requestKey + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    listPendingDocumentRequests() → {Array.<object>}

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + pending DOCUMENT_REQUEST rows (consent mode) +
    + + + +
    +
    + Type +
    +
    + + Array.<object> + + +
    +
    + + + + + + + + + + + + + +

    (async) listen() → {Peer}

    + + + + + + +
    + Start listening for connections. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Peer + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    openSealedDeliveryWithPreimage(documentId, preimageHex) → {Object}

    + + + + + + +
    + Open a pending sealed delivery using an HTLC claim preimage (on-chain witness). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + string + + + +
    preimageHex + + + string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    relayFromTrustedPeers(origin, message, minScoreExclusiveopt)

    + + + + + + +
    + Relay an AMP message only to connected peers whose persistent registry score is strictly greater than + Peer#settings.flushChainMinTrustedScore (default 800). Used for `P2P_FLUSH_CHAIN`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    origin + + + string + | + + null + + + + + + + + + + + + Connection key to skip (inbound sender), or null when originating locally.
    message + + + Message + | + + Buffer + + + + + + + + + + + +
    minScoreExclusive + + + number + + + + + + <optional>
    + + + + + +
    + + null + + Override trust threshold (relay if peer score > this value).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    requestDocument(documentId, peerAddressopt, optsopt) → {boolean}

    + + + + + + +
    + Send a signed `DocumentRequest` to one peer (or broadcast when peerAddress is omitted). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    documentId + + + string + + + + + + + + + + + +
    peerAddress + + + string + + + + + + <optional>
    + + + + + +
    + + null + +
    opts + + + object + + + + + + <optional>
    + + + + + +
    + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    maxSats + + + number + + + + + + <optional>
    + + + + + +
    relayHop + + + number + + + + + + <optional>
    + + + + + +
    blobIndex + + + number + + + + + + <optional>
    + + + + + +
    blobTotal + + + number + + + + + + <optional>
    + + + + + +
    contentHashHex + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    requestPeerInventory(peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Ask a connected peer for their document catalog (`kind: 'documents'`) or L1 offers (`offerBtc`). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    peerAddress + + + string + + + + + + + + + + connection key, Fabric id, or host:port
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    offerBtc + + + boolean + + + + + + <optional>
    + + + + + +
    + + false + +
    kind + + + string + + + + + + <optional>
    + + + + + +
    + + 'documents' + +
    maxSats + + + number + + + + + + <optional>
    + + + + + +
    + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    sendDocumentFileToPeer(documentId, peerAddress, optsopt) → {boolean}

    + + + + + + +
    + Public helper: push document bytes to a peer (same wire path as Peer#_handleDocumentRequestWire fulfillment).
    @@ -1169,84 +39907,372 @@

    Parameters:

    +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    documentId + + + string + + + + + + + + + +
    peerAddress + + + string + + + + + + + + + +
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    blobIndex + + + number + + + + + + <optional>
    + + + + + +
    revealKey + + + boolean + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    - - - - - + - - - - - - - + +
    Returns:
    +
    +
    + Type +
    +
    -
    - + boolean + + - - - +

    sendFlushChainToTrustedPeers(object) → {number}

    - + + + + + +
    + Sign and send `P2P_FLUSH_CHAIN` to all connected peers with registry score > threshold. + Body JSON: `{ snapshotBlockHash, network?, label? }`. + + **Receivers** (see `P2P_FLUSH_CHAIN` handler) require **both**: + 1. Sender pubkey in Peer#settings.flushChainAuthorizedPubkeys (non-empty allowlist), and + 2. Registry score above Peer#settings.flushChainMinTrustedScore. + Registry score bumps on `P2P_PONG` only when that pong answers an outbound ping on the same + connection (`_fabricPingOutstanding`), so unsolicited pongs cannot inflate trust alone. +
    + + + + + + + + + +
    Parameters:
    + + +
    NameTypeDescription
    originName - string -
    penalty - number -
    + + + + + + + + + + + + + + + - + - + + - + + @@ -1463,7 +40517,7 @@
    Parameters:
    @@ -1490,9 +40544,26 @@
    Parameters:
    +
    Returns:
    + + +
    + true if the outer frame was written to the first hop +
    + + + +
    +
    + Type +
    +
    + boolean +
    +
    @@ -1501,7 +40572,12 @@
    Parameters:
    -

    _fillPeerSlots() → {Peer}

    + + + + + +

    serialize() → {String}

    @@ -1509,7 +40585,7 @@

    _fillPe
    - Attempt to fill available connection slots with new peers. + Serialize the Actor's current state into a JSON-formatted string.
    @@ -1530,6 +40606,15 @@

    _fillPe + +
    Inherited From:
    +
    + +
    @@ -1555,7 +40640,7 @@

    _fillPe
    @@ -1585,10 +40670,6 @@

    _fillPe

    Returns:
    -
    - Instance of the peer. -
    -
    @@ -1597,7 +40678,7 @@
    Returns:
    - Peer + String
    @@ -1615,7 +40696,7 @@
    Returns:
    -

    _gossipPayloadDedupKey(msg) → {string}

    +

    set(path) → {Mixed}

    @@ -1623,7 +40704,7 @@

    - Stable id for gossip *logical* content (ignores `gossipHop` and wire signature changes). + Set a key in the State to a particular value. @@ -1659,13 +40740,13 @@

    Parameters:
    - + + @@ -1693,6 +40774,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -1718,7 +40808,7 @@
    Parameters:
    @@ -1748,10 +40838,6 @@
    Parameters:
    Returns:
    -
    - hex sha256 -
    -
    @@ -1760,7 +40846,7 @@
    Returns:
    - string + Mixed
    @@ -1778,68 +40864,23 @@
    Returns:
    -

    _gossipRateLimitAllow(originName) → {boolean}

    - - - - - - - - - - - - - - -
    Parameters:
    - - -
    NameTypeDescription
    reasonobject - string + Object @@ -1299,7 +40325,7 @@
    Parameters:
    @@ -1326,9 +40352,26 @@
    Parameters:
    +
    Returns:
    + + +
    + number of sockets written +
    + + + +
    +
    + Type +
    +
    + number +
    +
    @@ -1337,7 +40380,12 @@
    Parameters:
    -

    _enqueuePeeringCandidate(host, port)

    + + + + + +

    sendOnion(path, payload) → {boolean}

    @@ -1345,7 +40393,10 @@

    - Enqueue a fabric candidate from P2P_PEERING_OFFER; FIFO-capped and deduped by host:port. + Send {@code payload} along a source-routed onion path of Fabric peer pubkeys. + Builds nested {@code P2P_FORWARD} layers and writes the outer frame only to + {@code path[0]} (immediate hop). Destination learns the last hop's IP, not + the originator's. See module:@fabric/core/functions/fabricOnion. @@ -1381,13 +40432,13 @@
    Parameters:

    hostpath - string + Array.<(Buffer|string)> @@ -1397,20 +40448,23 @@
    Parameters:
    -
    hop pubkeys; first = next TCP peer, last = deliverer
    portpayload - number + Message + | + + Buffer @@ -1420,7 +40474,7 @@
    Parameters:
    -
    innermost application Message (should already be signed)
    msgpath - object + Path @@ -1675,7 +40756,7 @@
    Parameters:
    -
    Generic message (`type`, `object`, …)Key to retrieve.
    - - - - - - - - - - - - - - - - - - - - - - - +

    sign() → {Actor}

    - +
    + Signs the Actor. +
    - - - -
    NameTypeDescription
    originName - string - Connection id (e.g. `host:port`)
    @@ -1852,6 +40893,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -1877,7 +40927,7 @@
    Parameters:
    @@ -1915,7 +40965,7 @@
    Returns:
    - boolean + Actor
    @@ -1933,72 +40983,23 @@
    Returns:
    -

    _handleFabricMessage(buffer) → {Peer}

    - - - - - - -
    - Handle a Fabric Message buffer. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - +

    (async) start()

    - +
    + Start the Peer. +
    - - - -
    NameTypeDescription
    buffer - Buffer -
    @@ -2014,6 +41015,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -2036,7 +41046,7 @@
    Parameters:
    @@ -2063,28 +41073,6 @@
    Parameters:
    -
    Returns:
    - - -
    - Instance of the Peer. -
    - - - -
    -
    - Type -
    -
    - - Peer - - -
    -
    - - @@ -2096,7 +41084,7 @@
    Returns:
    -

    (async) _loadPeerRegistry() → {Promise.<void>}

    +

    (async) stop()

    @@ -2104,8 +41092,7 @@

    (async) - Load persistent peer registry from LevelDB. - Uses classic-level in Node, browser-level (IndexedDB) in browser. + Stop the peer. @@ -2151,7 +41138,7 @@

    (async) @@ -2178,24 +41165,6 @@

    (async) Returns:

    - - - - -
    -
    - Type -
    -
    - - Promise.<void> - - -
    -
    - - @@ -2207,7 +41176,7 @@
    Returns:
    -

    _peeringOfferPayloadDedupKey(msg) → {string}

    +

    stream(pipeopt) → {TransformStream}

    @@ -2215,7 +41184,7 @@

    - Stable id for peering-offer *logical* content (ignores `peeringHop` and wire signature changes). + Returns a new output stream for the Actor.
    @@ -2239,6 +41208,8 @@
    Parameters:
    Type + Attributes + @@ -2251,23 +41222,33 @@
    Parameters:
    - msg + pipe - object + TransformStream + + + <optional>
    + + - Generic message (`type`, `object`, …) + + + + + + Pipe to stream to. @@ -2288,6 +41269,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -2310,7 +41300,7 @@
    Parameters:
    @@ -2341,7 +41331,7 @@
    Returns:
    - hex sha256 + New output stream for the Actor.
    @@ -2352,7 +41342,7 @@
    Returns:
    - string + TransformStream
    @@ -2370,68 +41360,23 @@
    Returns:
    -

    _peeringRateLimitAllow(originName) → {boolean}

    - - - - - - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - +

    tick() → {Number}

    - +
    + Move forward one clock cycle. +
    - - - -
    NameTypeDescription
    originName - string - Connection id (e.g. `host:port`)
    @@ -2444,6 +41389,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -2469,7 +41423,7 @@
    Parameters:
    @@ -2507,7 +41461,7 @@
    Returns:
    - boolean + Number
    @@ -2525,7 +41479,7 @@
    Returns:
    -

    _savePeerRegistry()

    +

    toBuffer() → {Buffer}

    @@ -2533,7 +41487,7 @@

    _sav
    - Persist peer registry to LevelDB (debounced). + Casts the Actor to a normalized Buffer.
    @@ -2554,6 +41508,15 @@

    _sav + +
    Inherited From:
    +
    + +
    @@ -2579,7 +41542,7 @@

    _sav
    @@ -2606,6 +41569,23 @@

    _sav +

    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + @@ -2617,7 +41597,8 @@

    _sav -

    _upsertPeerRegistry(address, updatesopt)

    + +

    toGenericMessage(typeopt) → {Object}

    @@ -2625,7 +41606,10 @@

    _u
    - Upsert a peer into the persistent registry (state.peers) and schedule save to LevelDB. + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network.
    @@ -2653,6 +41637,8 @@

    Parameters:
    + Default + Description @@ -2663,13 +41649,13 @@
    Parameters:
    - address + type - string + String @@ -2678,49 +41664,24 @@
    Parameters:
    + <optional>
    - - - - - - Peer address (e.g. host:port). - - - - - - - updates - - - - - - Object - - - - - <optional>
    - - + + 'FabricActorState' - - - Fields to set/merge (id, score, firstSeen, lastSeen, alias, publicKey). + Logical message type string. @@ -2738,6 +41699,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -2763,7 +41733,7 @@
    Parameters:
    @@ -2772,24 +41742,18 @@
    Parameters:
    +
    See:
    +
    +

    - - - - - - - - - - - - +
  • https://dev.fabric.pub/messages
  • + + +
    @@ -2801,75 +41765,60 @@
    Parameters:
    -

    _wireInboundCreditCost(wireType) → {number}

    +
    Returns:
    -
    - Credit cost for inbound wire messages (heavier types consume more of the peer's budget). +
    + `{ type, object }`
    +
    +
    + Type +
    +
    + Object +
    +
    -
    Parameters:
    - - - - - - - - - - - - - - - - - - - + +
    + Returns the Actor's current state as an Object. +
    - - - -
    NameTypeDescription
    wireType +

    toObject() → {Object}

    - string - | - number -
    @@ -2882,6 +41831,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -2907,7 +41865,7 @@
    Parameters:
    @@ -2945,7 +41903,7 @@
    Returns:
    - number + Object
    @@ -2963,7 +41921,7 @@
    Returns:
    -

    _wireInboundRateAllowPeer(originName, creditCost) → {boolean}

    +

    trust(source) → {Service}

    @@ -2971,7 +41929,7 @@

    - Apply rolling-window credits; on overflow, de-rank once per window and reject the message. + Explicitly trust all events from a known source.

    @@ -3007,36 +41965,13 @@
    Parameters:
    - originName - - - - - - string - - - - - - - - - - connection key (host:port) - - - - - - - creditCost + source - number + EventEmitter @@ -3046,7 +41981,7 @@
    Parameters:
    - + Emitter of events. @@ -3064,6 +41999,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -3089,7 +42033,7 @@
    Parameters:
    @@ -3120,7 +42064,7 @@
    Returns:
    - false = drop message + Instance of Service after binding events.
    @@ -3131,7 +42075,7 @@
    Returns:
    - boolean + Service
    @@ -3149,7 +42093,7 @@
    Returns:
    -

    broadcast(message)

    +

    unpause() → {Actor}

    @@ -3157,7 +42101,7 @@

    broadcast - Write a Buffer to all connected peers. + Toggles `status` property to unpaused. @@ -3168,63 +42112,62 @@

    broadcastParameters:

    - - - - +
    -
    - - - +
    Inherited From:
    +
    + +
    - - - - - - - -
    NameTypeDescription
    message - Buffer - Message buffer to send.
    + +
    Source:
    +
    + +
    -
    +
    @@ -3240,22 +42183,26 @@
    Parameters:
    +
    Returns:
    +
    + Instance of the Actor. +
    +
    +
    + Type +
    +
    + Actor -
    Source:
    -
    -
    +
    @@ -3263,19 +42210,23 @@
    Parameters:
    -

    +

    value(formatopt) → {Object}

    +
    + Get the inner value of the Actor with an optional cast type. +
    + @@ -3284,67 +42235,91 @@
    Parameters:
    +
    Parameters:
    + + + + + -

    (async) listen() → {Peer}

    + + -
    - Start listening for connections. -
    + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + String + -
    + <optional>
    +
    + object + Cast the value to one of: `buffer, hex, json, string`
    +
    -
    Source:
    -
    +
    Inherited From:
    +
    @@ -3355,8 +42330,6 @@

    (async) listen< -

    - @@ -3371,25 +42344,21 @@

    (async) listen< -

    Returns:
    - +
    Source:
    +
    + +
    -
    - Chainable method. -
    -
    -
    - Type -
    -
    - Peer -
    @@ -3404,30 +42373,28 @@
    Returns:
    -

    (async) start()

    - - +
    Returns:
    -
    - Start the Peer. +
    + Inner value of the Actor as an Object, or cast to the requested `format`.
    +
    +
    + Type +
    +
    + Object - - - - - - - -
    +
    +
    @@ -3441,12 +42408,16 @@

    (async) startwhen(event, method) → {EventEmitter}

    +
    + Bind a method to an event, with current state as the immutable context. +
    @@ -3454,65 +42425,78 @@

    (async) startSource: -
    - -
    +
    Parameters:
    + + + + - + + + + + + + + + + + + -

    (async) stop()

    + + + + +
    NameTypeDescription
    event + String + Name of the event upon which to execute `method` as a function.
    method + function -
    - Stop the peer. -
    +
    Function to execute when named Event `event` is encountered.
    @@ -3525,6 +42509,15 @@

    (async) stopInherited From: +
    + +
    @@ -3550,7 +42543,7 @@

    (async) stop @@ -3577,6 +42570,28 @@

    (async) stopReturns:

    + + +
    + Instance of EventEmitter. +
    + + + +
    +
    + Type +
    +
    + + EventEmitter + + +
    +
    + + @@ -3601,14 +42616,18 @@

    Classes

    Global


    diff --git a/docs/Program.html b/docs/Program.html new file mode 100644 index 000000000..511f7fb3b --- /dev/null +++ b/docs/Program.html @@ -0,0 +1,1359 @@ + + + + + + Class: Program · Docs + + + + + + + + + +
    +

    Class: Program

    + + + + +
    + +
    + +

    Program(settingsopt)

    + + +
    + +
    +
    + + + + + + +

    new Program(settingsopt)

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    settings + + + object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    language + + + string + + + + + + <optional>
    + + + + + +
    source + + + string + | + + object + + + + + + <optional>
    + + + + + +
    bytecode + + + * + + + + + + <optional>
    + + + + + +
    steps + + + Array.<string> + + + + + + <optional>
    + + + + + +
    programId + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    compile() → {Object}

    + + + + + + +
    + Normalize language-specific form into `steps` / `bytecode`. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    hash() → {string}

    + + + + + + +
    + Content-address of language + source/bytecode/steps. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 64-char hex +
    + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    runCommitmentHex(result) → {string}

    + + + + + + +
    + Stable digest binding program identity to a compute result (L1 / OP_RETURN / witness). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    result + + + * + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 64-char hex +
    + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    toRedeemScript() → {Object}

    + + + + + + +
    + L1 redeem script scaffold (bitcoin-script only). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (static) from(optsopt) → {Program}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    opts + + + Object + + + + + + <optional>
    + + + + + +
    +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    language + + + string + + + + + + <optional>
    + + + + + +
    source + + + * + + + + + + <optional>
    + + + + + +
    bytecode + + + * + + + + + + <optional>
    + + + + + +
    steps + + + Array.<string> + + + + + + <optional>
    + + + + + +
    programId + + + string + + + + + + <optional>
    + + + + + +
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Program + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/Reader.html b/docs/Reader.html index d14faef7a..25ea4b309 100644 --- a/docs/Reader.html +++ b/docs/Reader.html @@ -154,7 +154,7 @@
    Parameters:
    @@ -239,14 +239,18 @@

    Classes

    Global


    diff --git a/docs/Redis.html b/docs/Redis.html index 2bc5d3066..6ad777fda 100644 --- a/docs/Redis.html +++ b/docs/Redis.html @@ -35,7 +35,7 @@

    Class: Redis

    Redis(settingsopt) → {Redis}

    -
    Connect and subscribe to Redis servers.
    +
    Connect and subscribe to Redis servers (node-redis v6).
    @@ -213,6 +213,39 @@
    Properties
    + + + + url + + + + + + String + + + + + + + + + <optional>
    + + + + + + + + + + + Optional redis URL (overrides host/port). + + + @@ -339,7 +372,7 @@

    Methods

    -

    (async) start() → {Redis}

    +

    (async) start() → {Promise.<Redis>}

    @@ -393,7 +426,7 @@

    (async) start @@ -423,10 +456,6 @@

    (async) startReturns:

    -
    - Instance of the service. -
    -
    @@ -435,7 +464,7 @@
    Returns:
    - Redis + Promise.<Redis>
    @@ -453,7 +482,7 @@
    Returns:
    -

    (async) stop() → {Redis}

    +

    (async) stop() → {Promise.<Redis>}

    @@ -507,7 +536,7 @@

    (async) stop @@ -537,9 +566,160 @@

    (async) stopReturns:

    -
    - Instance of the service. -
    + + +
    +
    + Type +
    +
    + + Promise.<Redis> + + +
    +
    + + + + + + + + + + + + + +

    (async) subscribe(name) → {Promise.<void>}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    name + + + string + + + + Channel name
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + @@ -549,7 +729,7 @@
    Returns:
    - Redis + Promise.<void>
    @@ -580,14 +760,18 @@

    Classes

    Global


    diff --git a/docs/Remote.html b/docs/Remote.html index 60f150f6c..be4003364 100644 --- a/docs/Remote.html +++ b/docs/Remote.html @@ -33,11 +33,11 @@

    Class: Remote

    -

    Remote(target)

    +

    Remote()

    -
    Interact with a remote Resource. This is currently the only - HTTP-related code that should remain in @fabric/core — all else must - be moved to @fabric/http before final release!
    +
    WebSocket client to a remote Fabric/Hub-style host (extends Actor). Per comment in + source, prefer moving richer HTTP to @fabric/http; this type stays for minimal Message-oriented + bridging. Uses browser/Node WebSocket with JSON Message payloads where applicable.
    @@ -52,16 +52,13 @@

    Constructor

    -

    new Remote(target)

    +

    new Remote()

    -
    - An in-memory representation of a node in our network. -
    @@ -71,128 +68,6 @@

    new RemoteParameters:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    target - - - Object - - - - Target object. -
    Properties
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    host - - - String - - - - Named host, e.g. "localhost".
    secure - - - String - - - - Require TLS session.
    - -
    - - - @@ -302,7 +177,7 @@
    Properties:
    @@ -338,6 +213,17 @@
    Properties:

    +

    Extends

    + + + + + + + + @@ -486,7 +372,7 @@
    Parameters:
    @@ -672,7 +558,7 @@
    Parameters:
    @@ -861,7 +747,7 @@
    Parameters:
    @@ -1047,7 +933,7 @@
    Parameters:
    @@ -1233,7 +1119,7 @@
    Parameters:
    @@ -1422,7 +1308,7 @@
    Parameters:
    @@ -1485,7 +1371,7 @@
    Returns:
    -

    (async) enumerate() → {Configuration}

    +

    _readObject(input) → {Object}

    @@ -1493,7 +1379,7 @@

    (async) enum
    - Enumerate the available Resources on the remote host. + Parse an Object into a corresponding Fabric state.
    @@ -1504,6 +1390,55 @@

    (async) enum +

    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + @@ -1514,6 +1449,15 @@

    (async) enum + +
    Inherited From:
    +
    + +
    @@ -1539,7 +1483,7 @@

    (async) enum
    @@ -1570,7 +1514,7 @@

    Returns:
    - An object with enumerable key/value pairs for the Application Resource Contract. + Fabric state.
    @@ -1581,7 +1525,7 @@
    Returns:
    - Configuration + Object
    @@ -1599,7 +1543,7 @@
    Returns:
    -

    (async) request(type, path, paramsopt) → {FabricHTTPResult}

    +

    adopt(changes) → {Actor}

    @@ -1607,7 +1551,7 @@

    (async) reques
    - Make an HTTP request to the configured authority. + Explicitly adopt a set of JSONPatch-encoded changes.
    @@ -1631,8 +1575,6 @@

    Parameters:
    Type - Attributes - @@ -1645,111 +1587,121 @@
    Parameters:
    - type + changes - String + Array - + List of JSONPatch operations to apply. + - + + - One of `GET`, `PUT`, `POST`, `DELETE`, or `OPTIONS`. - +
    - - path - - String +
    Inherited From:
    +
    + +
    - - - - The path to request from the authority. - - - params - + +
    Source:
    +
    + +
    - Object - - +
    - <optional>
    - - Options. - - - +
    Returns:
    +
    + Instance of the Actor. +
    -
    +
    +
    + Type +
    +
    + + Actor +
    +
    @@ -1763,20 +1715,41 @@
    Parameters:
    +

    commit() → {String}

    +
    + Resolve the current state to a commitment. +
    -
    Source:
    -
    + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    @@ -1787,7 +1760,6 @@
    Parameters:
    -
    @@ -1802,19 +1774,6127 @@
    Parameters:
    +
    Source:
    +
    + +
    -
    Returns:
    -
    -
    - Type -
    -
    - FabricHTTPResult + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) enumerate() → {Configuration}

    + + + + + + +
    + Enumerate the available Resources on the remote host. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + An object with enumerable key/value pairs for the Application Resource Contract. +
    + + + +
    +
    + Type +
    +
    + + Configuration + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) request(type, path, paramsopt) → {FabricHTTPResult}

    + + + + + + +
    + Make an HTTP request to the configured authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    type + + + String + + + + + + + + + + One of `GET`, `PUT`, `POST`, `DELETE`, or `OPTIONS`.
    path + + + String + + + + + + + + + + The path to request from the authority.
    params + + + Object + + + + + + <optional>
    + + + + + +
    Options.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + FabricHTTPResult + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +

    Remote(configopt)

    + + +
    + +
    +
    + + + + + + +

    new Remote(configopt)

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    config + + + Object + + + + + + <optional>
    + + + + + +
    + + {} + + host, port, secure, backoff, optional macaroon, …
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _DELETE(path, params) → {Object}

    + + + + + + +
    + HTTP DELETE on the configured Authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + HTTP Path to request.
    params + + + Object + + + + Map of parameters to supply.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + - Full description of remote resource. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _GET(path, params) → {FabricHTTPResult|String}

    + + + + + + +
    + HTTP GET against the configured Authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + HTTP Path to request.
    params + + + Object + + + + Map of parameters to supply.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Result of request. +
    + + + +
    +
    + Type +
    +
    + + FabricHTTPResult + | + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) _OPTIONS(path, params) → {Object}

    + + + + + + +
    + HTTP OPTIONS on the configured Authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + HTTP Path to request.
    params + + + Object + + + + Map of parameters to supply.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + - Full description of remote resource. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _PATCH(path, body) → {Object}

    + + + + + + +
    + HTTP PATCH on the configured Authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + HTTP Path to request.
    body + + + Object + + + + Map of parameters to supply.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + - Full description of remote resource. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _POST(path, params) → {FabricHTTPResult|String}

    + + + + + + +
    + HTTP POST against the configured Authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + HTTP Path to request.
    params + + + Object + + + + Map of parameters to supply.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Result of request. +
    + + + +
    +
    + Type +
    +
    + + FabricHTTPResult + | + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, body) → {FabricHTTPResult|String}

    + + + + + + +
    + HTTP PUT against the configured Authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + HTTP Path to request.
    body + + + Object + + + + Map of parameters to supply.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Result of request. +
    + + + +
    +
    + Type +
    +
    + + FabricHTTPResult + | + + String + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) enumerate() → {Configuration}

    + + + + + + +
    + Enumerate the available Resources on the remote host. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + An object with enumerable key/value pairs for the Application Resource Contract. +
    + + + +
    +
    + Type +
    +
    + + Configuration + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Object}

    + + + + + + +
    + Retrieve a value from the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to retrieve using JSONPointer.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) request(type, path, paramsopt) → {FabricHTTPResult}

    + + + + + + +
    + Make an HTTP request to the configured authority. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    type + + + String + + + + + + + + + + One of `GET`, `PUT`, `POST`, `DELETE`, or `OPTIONS`.
    path + + + String + + + + + + + + + + The path to request from the authority.
    params + + + Object + + + + + + <optional>
    + + + + + +
    Options.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + FabricHTTPResult + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path, value) → {Object}

    + + + + + + +
    + Set a value in the Actor's state by JSONPointer path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path to set using JSONPointer.
    value + + + Object + + + + Value to set.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Value of the path in the Actor's state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object
    @@ -1845,14 +7925,18 @@

    Classes

    Global


    diff --git a/docs/Resource.html b/docs/Resource.html index 8da3b27f2..3156112b7 100644 --- a/docs/Resource.html +++ b/docs/Resource.html @@ -33,9 +33,11 @@

    Class: Resource

    -

    Resource(definition)

    +

    Resource(definitionopt)

    -
    Generic interface for collections of digital objects.
    +
    Declarative application resource (routes, components, roles) persisted via Store. Pairs with + a Service implementation that honors the definition — see DEVELOPERS.md (Resources / ARCs). Extends Store + so commits and encryption options match the rest of the stack.
    @@ -50,7 +52,7 @@

    Constructor

    -

    new Resource(definition)

    +

    new Resource(definitionopt)

    @@ -78,9 +80,13 @@
    Parameters:
    Type + Attributes + Default + + Description @@ -103,10 +109,26 @@
    Parameters:
    + + + <optional>
    + + + + + + + + + + + {} + + - Initial parameters + Initial definition (name, routes, components, …). @@ -149,7 +171,7 @@
    Parameters:
    @@ -185,6 +207,68 @@
    Parameters:
    +

    Extends

    + + + + + + + + + + + + + + + + + + + + +

    Members

    + + + +

    codec

    + + + + +
    + Optional Codec for encrypted at-rest values (Level `valueEncoding`). + Browser and Hub-style apps typically use one Store with `codec` for + secrets and separate plain stores for cache/tips. +
    + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + @@ -199,6 +283,36 @@
    Parameters:
    + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + +

    Methods

    @@ -207,7 +321,7 @@

    Methods

    -

    (async) create(obj) → {Vector}

    +

    (async) _POST(key, value) → {Promise}

    @@ -215,7 +329,7 @@

    (async) create<
    - Create an instance of the Resource's type. + Insert something into a collection.
    @@ -251,13 +365,13 @@

    Parameters:
    - obj + key - Object + String @@ -267,7 +381,30 @@
    Parameters:
    - Map of the instance's properties and values. + Path to add data to. + + + + + + + value + + + + + + Mixed + + + + + + + + + + Object to store. @@ -285,6 +422,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -310,7 +456,7 @@
    Parameters:
    @@ -341,7 +487,7 @@
    Returns:
    - Resulting Vector with deterministic identifier. + Resolves on success with a String pointer.
    @@ -352,7 +498,7 @@
    Returns:
    - Vector + Promise
    @@ -370,7 +516,7 @@
    Returns:
    -

    (async) update(id, update) → {Vector}

    +

    (async) _REGISTER(obj) → {Vector}

    @@ -378,7 +524,7 @@

    (async) update<
    - Modify an existing instance of a Resource by its unique identifier. Produces a new instance. + Registers an Actor. Necessary to store in a collection.
    @@ -414,30 +560,7 @@

    Parameters:
    - id - - - - - - String - - - - - - - - - - Unique ID to update. - - - - - - - update + obj @@ -453,7 +576,7 @@
    Parameters:
    - Map of change to make (keys -> values). + Instance of the object to store. @@ -471,6 +594,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -496,7 +628,7 @@
    Parameters:
    @@ -527,7 +659,7 @@
    Returns:
    - Resulting Vector instance with updated identifier. + Returned from `storage.set`
    @@ -556,6 +688,3138 @@
    Returns:
    +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) create(obj) → {Vector}

    + + + + + + +
    + Create an instance of the Resource's type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    obj + + + Object + + + + Map of the instance's properties and values.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting Vector with deterministic identifier. +
    + + + +
    +
    + Type +
    +
    + + Vector + + +
    +
    + + + + + + + + + + + + + +

    (async) del(key)

    + + + + + + +
    + Remove a Value by Path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + Path + + + + Key to remove.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) flush()

    + + + + + + +
    + Wipes the storage. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) get(key) → {Promise}

    + + + + + + +
    + Barebones getter. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + String + + + + Name of data to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves on complete. `null` if not found. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) set(key, value)

    + + + + + + +
    + Set a `key` to a specific `value`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + String + + + + Address of the information.
    value + + + Mixed + + + + Content to store at `key`.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start() → {Promise}

    + + + + + + +
    + Start running the process. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves on complete. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Store}

    + + + + + + +
    + Implicitly trust an Event source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Event-emitting source.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting instance of Store with new trust. +
    + + + +
    +
    + Type +
    +
    + + Store + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) update(id, update) → {Vector}

    + + + + + + +
    + Modify an existing instance of a Resource by its unique identifier. Produces a new instance. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    id + + + String + + + + Unique ID to update.
    update + + + Object + + + + Map of change to make (keys -> values).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting Vector instance with updated identifier. +
    + + + +
    +
    + Type +
    +
    + + Vector + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + +
    @@ -569,14 +3833,18 @@

    Classes

    Global


    diff --git a/docs/RoundRobin.html b/docs/RoundRobin.html new file mode 100644 index 000000000..5bb4ac061 --- /dev/null +++ b/docs/RoundRobin.html @@ -0,0 +1,418 @@ + + + + + + Class: RoundRobin · Docs + + + + + + + + + +
    +

    Class: RoundRobin

    + + + + +
    + +
    + +

    RoundRobin()

    + + +
    + +
    +
    + + + + + + +

    new RoundRobin()

    + + + + + + +
    + Circuit specialization for round-robin selection over a set of nodes or peers. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    next(items) → {*|null}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    items + + + Array + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + * + | + + null + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/Scribe.html b/docs/Scribe.html index 714766ca4..0e49aa1e5 100644 --- a/docs/Scribe.html +++ b/docs/Scribe.html @@ -35,7 +35,7 @@

    Class: Scribe

    Scribe()

    -
    Deprecated 2021-11-06.
    +
    Deprecated 2021-11-06 — use FabricState (types/state). Scribe was merged into State.
    @@ -157,450 +157,6 @@

    new ScribeMethods

    - - - - - - - -

    inherits(scribe) → {Scribe}

    - - - - - - -
    - Use an existing Scribe instance as a parent. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    scribe - - - Scribe - - - - Instance of Scribe to use as parent.
    - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - - - - - - - - - - - - - - - -
    Returns:
    - - -
    - The configured instance of the Scribe. -
    - - - -
    -
    - Type -
    -
    - - Scribe - - -
    -
    - - - - - - - - - - - - - -

    now() → {Number}

    - - - - - - -
    - Retrives the current timestamp, in milliseconds. -
    - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - - - - - - - - - - - - - - - -
    Returns:
    - - -
    - Number representation of the millisecond Integer value. -
    - - - -
    -
    - Type -
    -
    - - Number - - -
    -
    - - - - - - - - - - - - - -

    trust(source) → {Scribe}

    - - - - - - -
    - Blindly bind event handlers to the Source. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    source - - - Source - - - - Event stream.
    - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - - - - - - - - - - - - - - - -
    Returns:
    - - -
    - Instance of the Scribe. -
    - - - -
    -
    - Type -
    -
    - - Scribe - - -
    -
    - - - - - - - - - @@ -618,14 +174,18 @@

    Classes

    Global


    diff --git a/docs/Script.html b/docs/Script.html index df53dda23..3842a9d8d 100644 --- a/docs/Script.html +++ b/docs/Script.html @@ -238,14 +238,18 @@

    Classes

    Global


    diff --git a/docs/Service.html b/docs/Service.html index e76c10134..a6e3e4567 100644 --- a/docs/Service.html +++ b/docs/Service.html @@ -33,16 +33,11 @@

    Class: Service

    -

    (protected) Service(settingsopt)

    +

    (protected) Service()

    -
    The "Service" is a simple model for processing messages in a distributed - system. Service instances are public interfaces for outside systems, - and typically advertise their presence to the network. - - To implement a Service, you will typically need to implement all methods from - this prototype. In general, `connect` and `send` are the highest-priority - jobs, and by default the `fabric` property will serve as an I/O stream using - familiar semantics.
    +
    Long-lived application surface extending Actor. Integrates external systems and the Fabric + network: peers consume and produce Message (AMP) instances, not ad-hoc JSON. Subclasses implement routing, + resources, and lifecycle (start/stop patterns — see AGENTS.md). The CLI/browser shell is Service.FabricShell.
    @@ -57,227 +52,19 @@

    Constructor

    -

    (protected) new Service(settingsopt)

    - - - - - - -
    - Create an instance of a Service. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeAttributesDescription
    settings - - - Object - - - - - - <optional>
    - - - - - -
    Configuration for this service. -
    Properties
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeAttributesDefaultDescription
    networking - - - Boolean - - - - - - <optional>
    - - - - - -
    - - true - - Whether or not to connect to the network.
    frequency - - - Object - - - - - - <optional>
    - - - - - -
    - - Interval frequency in hertz.
    state - - - Object - - - - - - <optional>
    - - +

    (protected) new Service()

    -
    - - Initial state to assign.
    -
    @@ -362,7 +149,7 @@
    Properties:
    @@ -398,6 +185,17 @@
    Properties:
    +

    Extends

    + + + + + + + + @@ -523,7 +321,7 @@
    Parameters:
    @@ -776,7 +574,7 @@
    Parameters:
    @@ -836,16 +634,12 @@
    Returns:
    -

    (async) _registerActor(actor) → {Promise}

    - +

    _appendWarning(msg) → {Service}

    -
    - Register an Actor with the Service. -
    @@ -880,13 +674,13 @@
    Parameters:
    - actor + msg - Object + String @@ -896,7 +690,7 @@
    Parameters:
    - Instance of the Actor. + Warning text (used by Service#_registerService duplicate guard). @@ -939,7 +733,7 @@
    Parameters:
    @@ -970,7 +764,7 @@
    Returns:
    - Resolves upon successful registration. + This instance.
    @@ -981,7 +775,7 @@
    Returns:
    - Promise + Service
    @@ -999,7 +793,7 @@
    Returns:
    -

    (async) _send(message)

    +

    _readObject(input) → {Object}

    @@ -1007,7 +801,7 @@

    (async) _send - Sends a message. + Parse an Object into a corresponding Fabric state. @@ -1043,13 +837,13 @@
    Parameters:
    - message + input - Mixed + Object @@ -1059,7 +853,7 @@
    Parameters:
    - Message to send. + Object to read as input. @@ -1077,6 +871,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -1102,7 +905,7 @@
    Parameters:
    @@ -1129,26 +932,48 @@
    Parameters:
    +
    Returns:
    +
    + Fabric state. +
    +
    +
    + Type +
    +
    + Object +
    +
    -

    beat() → {Service}

    -
    - Compute latest state. + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service.
    @@ -1159,6 +984,55 @@

    beat + + + + Name + + + Type + + + + + + Description + + + + + + + + + actor + + + + + + Object + + + + + + + + + + Instance of the Actor. + + + + + + + @@ -1194,7 +1068,7 @@

    beat @@ -1211,11 +1085,6 @@

    beat + Resolves upon successful registration. +

    +
    @@ -1237,7 +1110,7 @@
    Returns:
    - Service + Promise
    @@ -1255,7 +1128,7 @@
    Returns:
    -

    (async) connect(notify) → {Promise}

    +

    (async) _send(message)

    @@ -1263,7 +1136,7 @@

    (async) connec
    - Attach to network. + Sends a message.
    @@ -1289,8 +1162,6 @@

    Parameters:
    - Default - Description @@ -1301,13 +1172,13 @@
    Parameters:
    - notify + message - Boolean + Mixed @@ -1316,14 +1187,8 @@
    Parameters:
    - - - true - - - - Commit to changes. + Message to send. @@ -1366,7 +1231,7 @@
    Parameters:
    @@ -1393,28 +1258,6 @@
    Parameters:
    -
    Returns:
    - - -
    - Resolves to Fabric. -
    - - - -
    -
    - Type -
    -
    - - Promise - - -
    -
    - - @@ -1426,7 +1269,7 @@
    Returns:
    -

    get(path) → {Mixed}

    +

    adopt(changes) → {Actor}

    @@ -1434,7 +1277,7 @@

    get - Retrieve a key from the State. + Explicitly adopt a set of JSONPatch-encoded changes. @@ -1470,13 +1313,13 @@
    Parameters:
    - path + changes - Path + Array @@ -1486,7 +1329,7 @@
    Parameters:
    - Key to retrieve. + List of JSONPatch operations to apply. @@ -1504,6 +1347,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -1529,7 +1381,7 @@
    Parameters:
    @@ -1560,7 +1412,7 @@
    Returns:
    - Returns the target value if found, otherwise null. + Instance of the Actor.
    @@ -1571,7 +1423,7 @@
    Returns:
    - Mixed + Actor
    @@ -1589,7 +1441,7 @@
    Returns:
    -

    handler(message) → {Service}

    +

    beat() → {Service}

    @@ -1597,8 +1449,7 @@

    handler - Default route handler for an incoming message. Follows the Activity - Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ + Compute latest state. @@ -1609,55 +1460,6 @@

    handlerParameters:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    message - - - Activity - - - - Message object.
    - - @@ -1693,7 +1495,7 @@
    Parameters:
    @@ -1710,6 +1512,11 @@
    Parameters:
    +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + @@ -1723,10 +1530,6 @@
    Parameters:
    Returns:
    -
    - Chainable method. -
    -
    @@ -1753,7 +1556,7 @@
    Returns:
    -

    init()

    +

    commit() → {String}

    @@ -1761,8 +1564,7 @@

    init - Called by Web Components. - TODO: move to @fabric/http/types/spa + Resolve the current state to a commitment. @@ -1786,6 +1588,15 @@

    initOverrides: +
    + +
    + @@ -1808,7 +1619,7 @@

    init @@ -1835,6 +1646,28 @@

    init + 32-byte ID + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + @@ -1846,7 +1679,7 @@

    initlock(durationopt) → {Boolean}

    +

    (async) connect(notify) → {Promise}

    @@ -1854,7 +1687,7 @@

    lock - Attempt to acquire a lock for `duration` seconds. + Attach to network. @@ -1878,8 +1711,6 @@
    Parameters:
    Type - Attributes - Default @@ -1894,39 +1725,29 @@
    Parameters:
    - duration + notify - Number + Boolean - - - <optional>
    - - - - - - - - 1000 + true - Number of milliseconds to hold lock. + Commit to changes. @@ -1969,7 +1790,7 @@
    Parameters:
    @@ -2000,7 +1821,7 @@
    Returns:
    - true if locked, false if unable to lock. + Resolves to Fabric.
    @@ -2011,7 +1832,7 @@
    Returns:
    - Boolean + Promise
    @@ -2029,7 +1850,7 @@
    Returns:
    -

    (async) route(msg) → {Promise}

    +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    @@ -2037,7 +1858,7 @@

    (async) route - Resolve a State from a particular Message object. + Register Bitcoin-style primitive opcode metadata. @@ -2061,6 +1882,8 @@
    Parameters:
    Type + Attributes + @@ -2073,13 +1896,7852 @@
    Parameters:
    - msg + name - Message + string + + + + + + + + + + + + + + + + + + + + + + + + + definition + + + + + + Object + + + + + + + + + <optional>
    + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start()

    + + + + + + +
    + Start the service, including the initiation of an outbound connection + to any peers designated in the service's configuration. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Service}

    + + + + + + +
    + Explicitly trust all events from a known source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Emitter of events.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of Service after binding events. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    when(event, method) → {EventEmitter}

    + + + + + + +
    + Bind a method to an event, with current state as the immutable context. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    event + + + String + + + + Name of the event upon which to execute `method` as a function.
    method + + + function + + + + Function to execute when named Event `event` is encountered.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of EventEmitter. +
    + + + +
    +
    + Type +
    +
    + + EventEmitter + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +

    Service(settingsopt)

    + + +
    + +
    +
    + + + + + + +

    new Service(settingsopt)

    + + + + + + +
    + Create an instance of a Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    settings + + + Object + + + + + + <optional>
    + + + + + +
    Configuration for this service. +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    networking + + + Boolean + + + + + + <optional>
    + + + + + +
    + + true + + Whether or not to connect to the network.
    frequency + + + Object + + + + + + <optional>
    + + + + + +
    + + Interval frequency in hertz.
    state + + + Object + + + + + + <optional>
    + + + + + +
    + + Initial state to assign.
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + + String + + + + + + + + + + + + Path to store the value at.
    value + + + Object + + + + + + + + + + + + Document to store.
    commit + + + Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + + String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + + Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + + Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + +
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. + Contract body example: + `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + + string + + + + + + + + + + Contract label
    body + + + string + + + + + + + + + + Newline-delimited opcode list
    meta + + + Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity + Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + + Activity + + + + Message object.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. + TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + + Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + + Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -
    NameTypeDescription
    msg + + + Message @@ -2093,8 +9755,1101 @@
    Parameters:
    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + + String + + + + Channel name to which the message will be sent.
    message + + + String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + + Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start()

    + + + + + + +
    + Start the service, including the initiation of an outbound connection + to any peers designated in the service's configuration. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + @@ -2107,6 +10862,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -2132,7 +10896,7 @@
    Parameters:
    @@ -2162,10 +10926,6 @@
    Parameters:
    Returns:
    -
    - Resolves with resulting State. -
    -
    @@ -2174,7 +10934,7 @@
    Returns:
    - Promise + Buffer
    @@ -2192,7 +10952,7 @@
    Returns:
    -

    (async) send(channel, message) → {Service}

    +

    toGenericMessage(typeopt) → {Object}

    @@ -2200,7 +10960,10 @@

    (async) send - Send a message to a channel. + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. @@ -2224,7 +10987,11 @@
    Parameters:
    Type + Attributes + + + Default Description @@ -2236,7 +11003,7 @@
    Parameters:
    - channel + type @@ -2249,33 +11016,26 @@
    Parameters:
    + + <optional>
    - Channel name to which the message will be sent. - - - - - - message - + - String + + 'FabricActorState' - - - - Content of the message to send. + Logical message type string. @@ -2293,6 +11053,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -2318,7 +11087,7 @@
    Parameters:
    @@ -2327,6 +11096,15 @@
    Parameters:
    +
    See:
    +
    + +
    +

    @@ -2349,7 +11127,7 @@
    Returns:
    - Chainable method. + `{ type, object }`
    @@ -2360,7 +11138,7 @@
    Returns:
    - Service + Object
    @@ -2378,7 +11156,7 @@
    Returns:
    -

    set(path) → {Mixed}

    +

    toObject() → {Object}

    @@ -2386,7 +11164,7 @@

    set - Set a key in the State to a particular value. + Returns the Actor's current state as an Object. @@ -2397,64 +11175,62 @@

    set - - - Name +
    - Type - Description - - +
    Inherited From:
    +
    + +
    - - - path - - Path - - Key to retrieve. - - - +
    Source:
    +
    + +
    -
    +
    @@ -2470,21 +11246,22 @@
    Parameters:
    +
    Returns:
    +
    +
    + Type +
    +
    + Object -
    Source:
    -
    -
    +
    @@ -2492,69 +11269,78 @@
    Parameters:
    -
    +

    trust(source) → {Service}

    +
    + Explicitly trust all events from a known source. +
    -
    Returns:
    -
    -
    - Type -
    -
    - Mixed +
    Parameters:
    -
    -
    + + + + + + + + + + -

    (async) start()

    + + + + + +
    NameTypeDescription
    source + EventEmitter -
    - Start the service, including the initiation of an outbound connection - to any peers designated in the service's configuration. -
    + +
    Emitter of events.
    @@ -2592,7 +11378,7 @@

    (async) start @@ -2619,8 +11405,26 @@

    (async) startReturns:

    + + +
    + Instance of Service after binding events. +
    + + + +
    +
    + Type +
    +
    + + Service +
    +
    @@ -2630,7 +11434,11 @@

    (async) starttick() → {Number}

    + + + + +

    unpause() → {Actor}

    @@ -2638,7 +11446,7 @@

    tick - Move forward one clock cycle. + Toggles `status` property to unpaused. @@ -2659,6 +11467,15 @@

    tickInherited From: +
    + +
    @@ -2684,7 +11501,7 @@

    tick @@ -2714,6 +11531,10 @@

    tick + Instance of the Actor. + +
    @@ -2722,7 +11543,7 @@
    Returns:
    - Number + Actor
    @@ -2740,7 +11561,7 @@
    Returns:
    -

    trust(source) → {Service}

    +

    value(formatopt) → {Object}

    @@ -2748,7 +11569,7 @@

    trust - Explicitly trust all events from a known source. + Get the inner value of the Actor with an optional cast type. @@ -2772,7 +11593,11 @@
    Parameters:
    Type + Attributes + + + Default Description @@ -2784,23 +11609,39 @@
    Parameters:
    - source + format - EventEmitter + String + + + <optional>
    - Emitter of events. + + + + + + + + + object + + + + + Cast the value to one of: `buffer, hex, json, string` @@ -2818,6 +11659,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -2843,7 +11693,7 @@
    Parameters:
    @@ -2874,7 +11724,7 @@
    Returns:
    - Instance of Service after binding events. + Inner value of the Actor as an Object, or cast to the requested `format`.
    @@ -2885,7 +11735,7 @@
    Returns:
    - Service + Object
    @@ -3029,7 +11879,7 @@
    Parameters:
    @@ -3102,14 +11952,18 @@

    Classes

    Global


    diff --git a/docs/Session.html b/docs/Session.html index 4e5dc6b0c..4b18e995d 100644 --- a/docs/Session.html +++ b/docs/Session.html @@ -442,14 +442,18 @@

    Classes

    Global


    diff --git a/docs/State.html b/docs/State.html index 24606efd4..f40542919 100644 --- a/docs/State.html +++ b/docs/State.html @@ -33,11 +33,16 @@

    Class: State

    -

    (protected) State(data) → {State}

    - -
    The State is the core of most User-facing interactions. To - interact with the User, simply propose a change in the state by - committing to the outcome. This workflow keeps app design quite simple!
    +

    (protected) State()

    + +
    Named snapshot of application data extending Actor@type, + @data, @id, JSON Patch + flows. Absorbs former Scribe behavior: verbose / verbosity, now, + trust, start/stop, and structured log / error / + warn / debug (console + events). Channel, Document, Ledger, + Router, and Instruction extend State directly. Vector is an EventEmitter only. + Sibling concept to Entity. +
    @@ -52,72 +57,19 @@

    Constructor

    -

    (protected) new State(data) → {State}

    - - - - - - -
    - Creates a snapshot of some information. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    data +

    (protected) new State()

    - Mixed -
    Input data.
    @@ -299,7 +251,7 @@
    Properties:
    @@ -326,45 +278,33 @@
    Properties:
    -
    Returns:
    -
    - Resulting state. -
    -
    -
    - Type -
    -
    - State + -
    -
    +

    Extends

    + - -

    Extends

    -
      -
    • EventEmitter
    • -
    @@ -372,6 +312,7 @@

    Extends

    +

    Methods

    @@ -379,34 +320,72 @@

    Extends

    +

    _readObject(input) → {Object}

    -

    Methods

    +
    + Parse an Object into a corresponding Fabric state. +
    -

    commit()

    +
    Parameters:
    + + + + + + + + + + + + -
    - Increment the vector clock, broadcast all changes as a transaction. -
    + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    @@ -419,6 +398,15 @@

    commitInherited From: +
    + +
    @@ -444,7 +432,7 @@

    commit @@ -471,7 +459,26 @@

    commitReturns:

    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + +
    +
    @@ -482,7 +489,10 @@

    commitdeserialize(input) → {State}

    + + + +

    adopt(changes) → {Actor}

    @@ -490,7 +500,7 @@

    deserializ
    - Take a hex-encoded input and convert to a State object. + Explicitly adopt a set of JSONPatch-encoded changes.
    @@ -526,13 +536,13 @@

    Parameters:
    - input + changes - String + Array @@ -542,7 +552,7 @@
    Parameters:
    - [description] + List of JSONPatch operations to apply. @@ -560,6 +570,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -585,7 +604,7 @@
    Parameters:
    @@ -616,7 +635,7 @@
    Returns:
    - [description] + Instance of the Actor.
    @@ -627,7 +646,7 @@
    Returns:
    - State + Actor
    @@ -645,7 +664,7 @@
    Returns:
    -

    fork() → {State}

    +

    commit()

    @@ -653,8 +672,7 @@

    fork - Creates a new child State, with `@parent` set to - the current State by immutable identifier. + Increment the vector clock, broadcast all changes as a transaction. @@ -678,6 +696,15 @@

    forkOverrides: +
    + +
    + @@ -700,7 +727,7 @@

    fork @@ -727,24 +754,6 @@

    forkState - - - -

    - - @@ -756,7 +765,7 @@
    Returns:
    -

    get(path) → {Mixed}

    +

    deserialize(input) → {State}

    @@ -764,7 +773,7 @@

    get - Retrieve a key from the State. + Take a hex-encoded input and convert to a State object. @@ -800,13 +809,13 @@
    Parameters:
    - path + input - Path + String @@ -816,7 +825,7 @@
    Parameters:
    - Key to retrieve. + [description] @@ -859,7 +868,7 @@
    Parameters:
    @@ -889,6 +898,10 @@
    Parameters:
    Returns:
    +
    + [description] +
    +
    @@ -897,7 +910,7 @@
    Returns:
    - Mixed + State
    @@ -915,7 +928,7 @@
    Returns:
    -

    render() → {String}

    +

    export() → {Object}

    @@ -923,7 +936,7 @@

    render - Compose a JSON string for network consumption. + Export the Actor's state to a standard Object. @@ -944,6 +957,15 @@

    renderInherited From: +
    + +
    @@ -969,7 +991,7 @@

    render @@ -1000,7 +1022,7 @@
    Returns:
    - JSON-encoded String. + Standard object.
    @@ -1011,7 +1033,7 @@
    Returns:
    - String + Object
    @@ -1029,7 +1051,7 @@
    Returns:
    -

    serialize(inputopt) → {Buffer}

    +

    fork() → {State}

    @@ -1037,7 +1059,8 @@

    serialize - Convert to Buffer. + Creates a new child State, with `@parent` set to + the current State by immutable identifier. @@ -1048,72 +1071,55 @@

    serializeParameters:

    - - - - - - - - - +
    -
    - - - - - - - +
    Source:
    +
    + +
    - - - -
    NameTypeAttributesDescription
    input - Mixed - - <optional>
    -
    Input to serialize.
    +

    -
    @@ -1127,6 +1133,5272 @@
    Parameters:
    +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    inherits(other) → {Number}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    other + + + State + + + + Peer State whose `settings.namespace` is appended to `settings.tags`.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New length of `settings.tags`. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    render() → {String}

    + + + + + + +
    + Compose a JSON string for network consumption. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + JSON-encoded String. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    serialize(inputopt) → {Buffer}

    + + + + + + +
    + Convert to Buffer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + Mixed + + + + + + <optional>
    + + + + + +
    Input to serialize.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Store-able blob. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toHTML()

    + + + + + + +
    + Converts the State to an HTML document. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toString() → {String}

    + + + + + + +
    + Unmarshall an existing state to an instance of a Blob. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Serialized Blob. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {State}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Event stream.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + this +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (static) fromJSON(input) → {State}

    + + + + + + +
    + Marshall an input into an instance of a State. States have + absolute authority over their own domain, so choose your States wisely. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + String + + + + Arbitrary input.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting instance of the State. +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    + +
    + + + + + + + +
    + +
    + +

    State(data) → {State}

    + + +
    + +
    +
    + + + + + + +

    new State(data) → {State}

    + + + + + + +
    + Creates a snapshot of some information. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    data + + + Mixed + + + + Input data.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting state. +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + +
    + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit()

    + + + + + + +
    + Increment the vector clock, broadcast all changes as a transaction. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    deserialize(input) → {State}

    + + + + + + +
    + Take a hex-encoded input and convert to a State object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + String + + + + [description]
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + [description] +
    + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    fork() → {State}

    + + + + + + +
    + Creates a new child State, with `@parent` set to + the current State by immutable identifier. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + State + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    inherits(other) → {Number}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    other + + + State + + + + Peer State whose `settings.namespace` is appended to `settings.tags`.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New length of `settings.tags`. +
    + + + +
    +
    + Type +
    +
    + + Number + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    render() → {String}

    + + + + + + +
    + Compose a JSON string for network consumption. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + JSON-encoded String. +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    serialize(inputopt) → {Buffer}

    + + + + + + +
    + Convert to Buffer. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    input + + + Mixed + + + + + + <optional>
    + + + + + +
    Input to serialize.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Store-able blob. +
    + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + + Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toHTML()

    + + + + + + +
    + Converts the State to an HTML document. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + @@ -1144,7 +6416,7 @@
    Parameters:
    @@ -1174,10 +6446,6 @@
    Parameters:
    Returns:
    -
    - Store-able blob. -
    -
    @@ -1186,7 +6454,7 @@
    Returns:
    - Buffer + Object
    @@ -1204,7 +6472,7 @@
    Returns:
    -

    set(path) → {Mixed}

    +

    toString() → {String}

    @@ -1212,11 +6480,121 @@

    set - Set a key in the State to a particular value. + Unmarshall an existing state to an instance of a Blob. + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Serialized Blob.
    +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {State}

    + + + + + + + + @@ -1248,13 +6626,13 @@
    Parameters:
    - path + source - Path + EventEmitter @@ -1264,7 +6642,7 @@
    Parameters:
    - Key to retrieve. + Event stream. @@ -1307,7 +6685,7 @@
    Parameters:
    @@ -1337,6 +6715,10 @@
    Parameters:
    Returns:
    +
    + this +
    +
    @@ -1345,7 +6727,7 @@
    Returns:
    - Mixed + State
    @@ -1363,7 +6745,7 @@
    Returns:
    -

    toHTML()

    +

    unpause() → {Actor}

    @@ -1371,7 +6753,7 @@

    toHTML - Converts the State to an HTML document. + Toggles `status` property to unpaused. @@ -1392,6 +6774,15 @@

    toHTMLInherited From: +
    + +
    @@ -1417,7 +6808,7 @@

    toHTML @@ -1444,18 +6835,40 @@

    toHTMLReturns:

    + + +
    + Instance of the Actor. +
    + +
    +
    + Type +
    +
    + Actor +
    +
    -

    toString() → {String}

    + + + + + + + +

    value(formatopt) → {Object}

    @@ -1463,7 +6876,7 @@

    toString - Unmarshall an existing state to an instance of a Blob. + Get the inner value of the Actor with an optional cast type. @@ -1474,6 +6887,75 @@

    toStringParameters:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + @@ -1487,6 +6969,15 @@

    toStringOverrides: +
    + +
    + @@ -1509,7 +7000,7 @@

    toString @@ -1540,7 +7031,7 @@
    Returns:
    - Serialized Blob. + Inner value of the Actor as an Object, or cast to the requested `format`.
    @@ -1551,7 +7042,7 @@
    Returns:
    - String + Object
    @@ -1673,7 +7164,7 @@
    Parameters:
    @@ -1746,14 +7237,18 @@

    Classes

    Global


    diff --git a/docs/Store.html b/docs/Store.html index 3c0a45eb3..1d966aca4 100644 --- a/docs/Store.html +++ b/docs/Store.html @@ -33,9 +33,10 @@

    Class: Store

    -

    Store(settingsopt) → {Store}

    +

    Store()

    -
    Long-term storage.
    +
    Level-backed persistence extending Actor. Use optional Codec in settings.codec for + encrypted values; Store.openEncrypted matches Hub/shell keystore defaults. Commit/history behavior follows Actor.
    @@ -50,93 +51,19 @@

    Constructor

    -

    new Store(settingsopt) → {Store}

    - - - - - - -
    - Create an instance of a Store to manage long-term storage, which is - particularly useful when building a user-facing Product. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeAttributesDefaultDescription
    settings - - - Object - - - - - - <optional>
    - - +

    new Store()

    -
    - {} - configuration object.
    @@ -226,7 +153,7 @@
    Properties:
    @@ -253,35 +180,24 @@
    Properties:
    -
    Returns:
    - - -
    - Instance of the Store, ready to start. -
    - -
    -
    - Type -
    -
    - Store -
    -
    + +

    Extends

    + - @@ -348,7 +264,7 @@

    codec @@ -504,7 +420,7 @@
    Parameters:
    @@ -667,7 +583,7 @@
    Parameters:
    @@ -727,7 +643,7 @@
    Returns:
    -

    (async) del(key)

    +

    _readObject(input) → {Object}

    @@ -735,7 +651,7 @@

    (async) del - Remove a Value by Path. + Parse an Object into a corresponding Fabric state. @@ -771,13 +687,13 @@
    Parameters:
    - key + input - Path + Object @@ -787,7 +703,7 @@
    Parameters:
    - Key to remove. + Object to read as input. @@ -805,6 +721,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -830,7 +755,7 @@
    Parameters:
    @@ -857,6 +782,28 @@
    Parameters:
    +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + @@ -868,7 +815,7 @@
    Parameters:
    -

    (async) flush()

    +

    adopt(changes) → {Actor}

    @@ -876,7 +823,7 @@

    (async) flush - Wipes the storage. + Explicitly adopt a set of JSONPatch-encoded changes. @@ -887,42 +834,71 @@

    (async) flushParameters:

    + + + + + + + + + -
    +
    + + + + + + + + + + + + + +
    NameTypeDescription
    changes + Array + List of JSONPatch operations to apply.
    +
    -
    Source:
    -
    + + +
    Inherited From:
    +
    @@ -933,7 +909,6 @@

    (async) flush @@ -948,6 +923,14 @@

    (async) flushSource: +
    + +
    @@ -955,77 +938,72 @@

    (async) flush -

    (async) get(key) → {Promise}

    -
    - Barebones getter. -
    +
    Returns:
    +
    + Instance of the Actor. +
    -
    Parameters:
    +
    +
    + Type +
    +
    + Actor - - - - + + - - - - - - - - + +
    + Resolve the current state to a commitment. +
    - - - -
    NameTypeDescription
    key +

    commit() → {String}

    - String -
    Name of data to retrieve.
    @@ -1041,6 +1019,15 @@
    Parameters:
    +
    Overrides:
    +
    + +
    + @@ -1063,7 +1050,7 @@
    Parameters:
    @@ -1094,7 +1081,7 @@
    Returns:
    - Resolves on complete. `null` if not found. + 32-byte ID
    @@ -1105,7 +1092,7 @@
    Returns:
    - Promise + String
    @@ -1123,7 +1110,7 @@
    Returns:
    -

    (async) set(key, value)

    +

    (async) del(key)

    @@ -1131,7 +1118,7 @@

    (async) set - Set a `key` to a specific `value`. + Remove a Value by Path. @@ -1173,30 +1160,7 @@
    Parameters:
    - String - - - - - - - - - - Address of the information. - - - - - - - value - - - - - - Mixed + Path @@ -1206,7 +1170,7 @@
    Parameters:
    - Content to store at `key`. + Key to remove. @@ -1249,7 +1213,7 @@
    Parameters:
    @@ -1287,7 +1251,7 @@
    Parameters:
    -

    (async) start() → {Promise}

    +

    export() → {Object}

    @@ -1295,7 +1259,7 @@

    (async) start - Start running the process. + Export the Actor's state to a standard Object. @@ -1316,6 +1280,15 @@

    (async) startInherited From: +
    + +
    @@ -1341,7 +1314,7 @@

    (async) start @@ -1372,7 +1345,7 @@
    Returns:
    - Resolves on complete. + Standard object.
    @@ -1383,7 +1356,7 @@
    Returns:
    - Promise + Object
    @@ -1401,7 +1374,7 @@
    Returns:
    -

    trust(source) → {Store}

    +

    (async) flush()

    @@ -1409,7 +1382,7 @@

    trust - Implicitly trust an Event source. + Wipes the storage. @@ -1420,10 +1393,5592 @@

    trustParameters:

    - + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) get(key) → {Promise}

    + + + + + + +
    + Barebones getter. +
    + + + + + + + + + +
    Parameters:
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + String + + + + Name of data to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves on complete. `null` if not found. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) set(key, value)

    + + + + + + +
    + Set a `key` to a specific `value`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + String + + + + Address of the information.
    value + + + Mixed + + + + Content to store at `key`.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start() → {Promise}

    + + + + + + +
    + Start running the process. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves on complete. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Store}

    + + + + + + +
    + Implicitly trust an Event source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Event-emitting source.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting instance of Store with new trust. +
    + + + +
    +
    + Type +
    +
    + + Store + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + + String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (static) encryptedSettings(settingsopt) → {Object}

    + + + + + + +
    + Settings object for a Store with Codec at-rest encryption + (same defaults as the legacy `Keystore` type). Prefer `openEncrypted` or + `new Store(Store.encryptedSettings(...))` over ad-hoc Codec wiring. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    settings + + + Object + + + + + + <optional>
    + + + + + +
    + + {} + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Settings merged with `codec` when absent. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (static) openEncrypted(settingsopt) → {Store}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    settings + + + Object + + + + + + <optional>
    + + + + + +
    + + {} + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Store + + +
    +
    + + + + + + + + + + + + + +

    + +
    + + + + + + + +
    + +
    + +

    Store(settingsopt) → {Store}

    + + +
    + +
    +
    + + + + + + +

    new Store(settingsopt) → {Store}

    + + + + + + +
    + Create an instance of a Store to manage long-term storage (LevelDB by default). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    settings + + + Object + + + + + + <optional>
    + + + + + +
    + + {} + + configuration object (path, codec, persistent, …).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Store, ready to start. +
    + + + +
    +
    + Type +
    +
    + + Store + + +
    +
    + + + + + + + + +
    + + + + + + + + + + + + + + +

    Members

    + + + +

    codec

    + + + + +
    + Optional Codec for encrypted at-rest values (Level `valueEncoding`). + Browser and Hub-style apps typically use one Store with `codec` for + secrets and separate plain stores for cache/tips. +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _POST(key, value) → {Promise}

    + + + + + + +
    + Insert something into a collection. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + String + + + + Path to add data to.
    value + + + Mixed + + + + Object to store.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves on success with a String pointer. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _REGISTER(obj) → {Vector}

    + + + + + + +
    + Registers an Actor. Necessary to store in a collection. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    obj + + + Object + + + + Instance of the object to store.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returned from `storage.set` +
    + + + +
    +
    + Type +
    +
    + + Vector + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + + Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) del(key)

    + + + + + + +
    + Remove a Value by Path. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + Path + + + + Key to remove.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    (async) flush()

    + + + + + + +
    + Wipes the storage. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (async) get(key) → {Promise}

    + + + + + + +
    + Barebones getter. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + String + + + + Name of data to retrieve.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves on complete. `null` if not found. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + String + + +
    +
    + + + + + + + + + + + + + +

    (async) set(key, value)

    + + + + + + +
    + Set a `key` to a specific `value`. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + + String + + + + Address of the information.
    value + + + Mixed + + + + Content to store at `key`.
    + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start() → {Promise}

    + + + + + + +
    + Start running the process. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves on complete. +
    + + + +
    +
    + Type +
    +
    + + Promise + + +
    +
    + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + + TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + + TransformStream + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. + Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). + Actor#id derives from a digest of the pretty-printed generic message; extending this + envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + + String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Store}

    + + + + + + +
    + Implicitly trust an Event source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + + EventEmitter + + + + Event-emitting source.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resulting instance of Store with new trust. +
    + + + +
    +
    + Type +
    +
    + + Store + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + + Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + @@ -1433,8 +6988,12 @@
    Parameters:
    + + + + @@ -1445,23 +7004,39 @@
    Parameters:
    - + + + + + + + + + + + @@ -1479,6 +7054,15 @@
    Parameters:
    + +
    Inherited From:
    +
    + +
    @@ -1504,7 +7088,7 @@
    Parameters:
    @@ -1535,7 +7119,7 @@
    Returns:
    - Resulting instance of Store with new trust. + Inner value of the Actor as an Object, or cast to the requested `format`.
    @@ -1546,7 +7130,7 @@
    Returns:
    - Store + Object
    @@ -1689,7 +7273,7 @@
    Parameters:
    @@ -1868,7 +7452,7 @@
    Parameters:
    @@ -1937,14 +7521,18 @@

    Classes

    Global


    diff --git a/docs/Token.html b/docs/Token.html index 8be39be12..670c93bd7 100644 --- a/docs/Token.html +++ b/docs/Token.html @@ -613,7 +613,7 @@
    Parameters:
    @@ -685,14 +685,18 @@

    Classes

    Global


    diff --git a/docs/Tree.html b/docs/Tree.html index 630cf8979..f2344efbb 100644 --- a/docs/Tree.html +++ b/docs/Tree.html @@ -235,6 +235,76 @@
    Returns:
    +

    Members

    + + + +

    rootHex

    + + + + +
    + Hex encoding of Tree#root (empty string when the tree has no root bytes). +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + +

    Methods

    @@ -253,7 +323,7 @@

    addLeaf - Add a leaf to the tree. + Add a leaf to the tree (accumulates into `settings.leaves`). @@ -296,6 +366,9 @@
    Parameters:
    String + | + + Buffer @@ -462,7 +535,7 @@

    getLeaves @@ -535,14 +608,18 @@

    Classes

    Global


    diff --git a/docs/Vector.html b/docs/Vector.html index 714652e8b..61c8187ed 100644 --- a/docs/Vector.html +++ b/docs/Vector.html @@ -33,7 +33,11 @@

    Class: Vector

    -

    Vector(origin)

    +

    Vector()

    + +
    Lightweight event sink for instruction-stream and VM-adjacent signals. + Former State-backed fields (script, stack, known, serialization helpers) + live on Machine and State / Fabric#push instead.
    @@ -44,74 +48,23 @@

    VectorConstructor

    -

    new Vector(origin)

    - - - - - - -
    - An "Initialization" Vector. -
    - - - - - - - - - -
    Parameters:
    - - -

    TypeAttributesDefaultDescription
    sourceformat - EventEmitter + String + + <optional>
    + -
    Event-emitting source. + + object + + Cast the value to one of: `buffer, hex, json, string`
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    origin +

    new Vector()

    - Object -
    Input state (will map to `@data`.)
    @@ -149,7 +102,7 @@
    Parameters:
    @@ -185,10 +138,14 @@
    Parameters:
    +

    Extends

    +
      +
    • EventEmitter
    • +
    @@ -199,134 +156,20 @@
    Parameters:
    -

    Methods

    - - - - - - - -

    _serialize(input) → {String}

    - - - - - - -
    - _serialize is a placeholder, should be discussed. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    input - String - What to serialize. Defaults to `this.state`.
    - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Source:
    -
    - -
    - - - - - - - -
    - +
    +
    @@ -334,29 +177,24 @@
    Parameters:
    +
    +
    +

    Vector(optionsopt)

    -
    Returns:
    +
    -
    - - resulting string [JSON-encoded version of the local `@data` value.] -
    +
    +
    -
    -
    - Type -
    -
    - String -
    -
    +

    new Vector(optionsopt)

    @@ -370,79 +208,79 @@
    Returns:
    -

    toString(input) → {String}

    +
    Parameters:
    + + + + -
    - Render the output to a String. -
    + + + + + + -
    Parameters:
    + + -
    NameTypeAttributesDescription
    options
    - - - + + Object + - - - + + <optional>
    - - - - Mixed + + - + +
    Name - TypeDescription
    -
    input + Optional emitter options (e.g. captureRejections).
    - Arbitrary input. - - - +
    -
    @@ -463,28 +301,28 @@
    Parameters:
    +
    Source:
    +
    + +
    -
    Source:
    -
    - -
    +
    -
    @@ -500,22 +338,14 @@
    Parameters:
    -
    Returns:
    +
    -
    -
    - Type -
    -
    - String -
    -
    @@ -542,14 +372,18 @@

    Classes

    Global


    diff --git a/docs/Wallet.html b/docs/Wallet.html index d2088b57d..efc394644 100644 --- a/docs/Wallet.html +++ b/docs/Wallet.html @@ -392,7 +392,7 @@
    Properties:
    @@ -462,6 +462,82 @@
    Returns:
    +

    Members

    + + + +

    _emittedWalletTxKeys :Set.<string>

    + + + + + + +
    Type:
    +
      +
    • + + Set.<string> + + +
    • +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + +

    Methods

    @@ -575,7 +651,7 @@
    Parameters:
    @@ -716,7 +792,7 @@
    Parameters:
    @@ -880,7 +956,7 @@
    Parameters:
    @@ -918,7 +994,7 @@
    Parameters:
    -

    (async) _sign(tx)

    +

    _registerDerivedAddresses(key, optsopt)

    @@ -926,7 +1002,7 @@

    (async) _sign - Signs a transaction with the keyring. + Derive and watch a window of addresses for a key (all common script types). @@ -950,6 +1026,8 @@
    Parameters:
    Type + Attributes + @@ -962,19 +1040,60 @@
    Parameters:
    - tx + key - BcoinTX + Key + + + + + + + + + + + + + + + + + + + + + + + + + opts + + + + + + object + + + <optional>
    + + + + + + + @@ -1021,7 +1140,7 @@
    Parameters:
    @@ -1059,7 +1178,7 @@
    Parameters:
    -

    getAddressForScript(script)

    +

    (async) _sign(tx)

    @@ -1067,7 +1186,7 @@

    ge
    - Returns a bech32 address for the provided Script. + Signs a transaction with the keyring.
    @@ -1103,13 +1222,13 @@

    Parameters:
    - script + tx - Script + BcoinTX @@ -1162,7 +1281,7 @@
    Parameters:
    @@ -1200,72 +1319,19 @@
    Parameters:
    -

    getAddressFromRedeemScript(redeemScript)

    - - - - - - -
    - Generate a BitcoinAddress for the supplied BitcoinScript. -
    - - - - - - - - - -
    Parameters:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    redeemScript +

    (async) createHTLC()

    - BitcoinScript -
    @@ -1289,6 +1355,14 @@
    Parameters:
    +
    Deprecated:
    +
    +
      +
    • Legacy bcoin-style HTLC for channels — not the document-market P2TR profile. + Use Wallet.buildInventoryHtlcP2tr / `@fabric/core/functions/inventoryHtlc` instead.
    • +
    +
    + @@ -1303,7 +1377,7 @@
    Parameters:
    @@ -1341,7 +1415,7 @@
    Parameters:
    -

    publicKeyFromString(input)

    +

    getAddressForScript(script)

    @@ -1349,7 +1423,7 @@

    pu
    - Create a public key from a string. + Returns a bech32 address for the provided Script.
    @@ -1385,13 +1459,13 @@

    Parameters:
    - input + script - String + Script @@ -1401,7 +1475,7 @@
    Parameters:
    - Hex-encoded string to create key from. + @@ -1444,7 +1518,7 @@
    Parameters:
    @@ -1482,7 +1556,7 @@
    Parameters:
    -

    start()

    +

    getAddressFromRedeemScript(redeemScript)

    @@ -1490,7 +1564,7 @@

    start - Start the wallet, including listening for transactions. + Generate a BitcoinAddress for the supplied BitcoinScript. @@ -1501,53 +1575,62 @@

    startParameters:

    + + + - -
    +
    + + + + + + + + + + + +
    NameTypeDescription
    redeemScript + BitcoinScript +
    -
    Source:
    -
    - -
    +
    -
    @@ -1572,18 +1655,23 @@

    startSource: +
    + +
    -

    (static) createSeed(passphrase) → {FabricSeed}

    +

    -
    - Create a new seed phrase. -
    @@ -1593,53 +1681,35 @@

    (static) c -

    Parameters:
    - - - - - - - - - - - - - - - -
    NameTypeDescription
    passphrase +

    getWatchSet() → {object}

    - String -
    BIP 39 passphrase for key derivation.
    @@ -1677,7 +1747,7 @@
    Parameters:
    @@ -1708,7 +1778,7 @@
    Returns:
    - The seed object. + watch set snapshot
    @@ -1719,7 +1789,7 @@
    Returns:
    - FabricSeed + object
    @@ -1737,7 +1807,7 @@
    Returns:
    -

    (static) fromSeed(seed) → {Wallet}

    +

    ingestBitcoinBlock(block) → {Array.<object>}

    @@ -1745,7 +1815,7 @@

    (static) fro
    - Create a new Wallet from a seed object. + Process a new block tip (optionally with verbosity-2 `tx` / `transactions`).
    @@ -1781,13 +1851,13 @@

    Parameters:
    - seed + block - FabricSeed + object @@ -1797,7 +1867,7 @@
    Parameters:
    - Fabric seed. + @@ -1840,7 +1910,7 @@
    Parameters:
    @@ -1871,7 +1941,7 @@
    Returns:
    - Instance of the wallet. + related classifications
    @@ -1882,7 +1952,2460 @@
    Returns:
    - Wallet + Array.<object> + + +
    +

    + + + + + + + + + + + + + +

    ingestBitcoinTransaction(txOrHex, contextopt) → {object}

    + + + + + + +
    + Ingest a Bitcoin transaction (verbose RPC object or raw hex) and emit when related. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    txOrHex + + + object + | + + string + + + + + + + + + +
    context + + + object + + + + + + <optional>
    + + + + + +
    { tip, height, source }
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + classification +
    + + + +
    +
    + Type +
    +
    + + object + + +
    +
    + + + + + + + + + + + + + +

    listSeeds() → {Array.<{seedId: string, xpub: string, labels: Array.<string>}>}

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Array.<{seedId: string, xpub: string, labels: Array.<string>}> + + +
    +
    + + + + + + + + + + + + + +

    loadKey(input, labelsopt) → {Object}

    + + + + + + +
    + Register a key with optional labels. + Accepts a Key instance, pubkey hex string, or object-like key input. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    input + + + Key + | + + String + | + + Object + + + + + + + + + + + + Key material to load.
    labels + + + Array.<String> + + + + + + <optional>
    + + + + + +
    + + [] + + Optional labels.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Stored key descriptor. +
    + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    loadSeed(phrase, labelsopt, optsopt) → {Object}

    + + + + + + +
    + Load an additional BIP39 seed into this wallet's key collection (does not + replace the primary `this.key`). Addresses derived from the seed are watched. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    phrase + + + string + + + + + + + + + + + + BIP39 mnemonic
    labels + + + Array.<string> + + + + + + <optional>
    + + + + + +
    + + [] + +
    opts + + + object + + + + + + <optional>
    + + + + + +
    + + +
    Properties
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    addressWindow + + + number + + + + + + <optional>
    + + + + + +
    receive indices to watch (default gapLimit)
    + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object + + +
    +
    + + + + + + + + + + + + + +

    publicKeyFromString(input)

    + + + + + + +
    + Create a public key from a string. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + + String + + + + Hex-encoded string to create key from.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    start()

    + + + + + + +
    + Start the wallet, including listening for transactions. +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    watchAddress(address, metaopt)

    + + + + + + +
    + Watch a Bitcoin address for wallet-associated transactions. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    address + + + string + + + + + + + + + +
    meta + + + object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    watchHtlc(htlc)

    + + + + + + +
    + Watch an inventory HTLC offer (payment address + payment hash + optional settlement). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    htlc + + + object + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    watchPaymentHash(paymentHashHex, metaopt)

    + + + + + + +
    + Watch an HTLC payment hash (SHA256 preimage) so claim witnesses are detected. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    paymentHashHex + + + string + + + + + + + + + +
    meta + + + object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (static) buildHtlcFundingHints()

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    +
      +
    • module:functions/inventoryHtlc.buildHtlcFundingHints
    • +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (static) buildInventoryHtlcP2tr()

    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + +
    See:
    +
    +
      +
    • module:functions/inventoryHtlc.buildInventoryHtlcP2tr
    • +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (static) createSeed(passphrase) → {FabricSeed}

    + + + + + + +
    + Create a new seed phrase. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    passphrase + + + String + + + + BIP 39 passphrase for key derivation.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + The seed object. +
    + + + +
    +
    + Type +
    +
    + + FabricSeed + + +
    +
    + + + + + + + + + + + + + +

    (static) fromSeed(seed) → {Wallet}

    + + + + + + +
    + Create a new Wallet from a seed object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    seed + + + FabricSeed + + + + Fabric seed.
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the wallet. +
    + + + +
    +
    + Type +
    +
    + + Wallet + + +
    +
    + + + + + + + + + + + + + +

    (static) purchaseContentHashHex(documentId, parsed) → {string}

    + + + + + + +
    + Envelope (legacy / unsealed) payment hash. Prefer Wallet.resolveDocumentContentHashHex + when sealed meta or a content key may apply. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    documentId + + + string + + + +
    parsed + + + object + + + + Whitelisted document fields (see Peer#_buildDocumentParsedForPublish).
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + + + + + + + + + + + + +

    (static) resolveDocumentContentHashHex(opts) → {Object}

    + + + + + + +
    + Single path for buy / HTLC `contentHashHex` (sealed | envelope | blob). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    opts + + + object + + + + see module:functions/documentPaymentHash.resolveDocumentContentHashHex
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + Object
    @@ -1913,14 +4436,18 @@

    Classes

    Global


    diff --git a/docs/Worker.html b/docs/Worker.html index 751565b18..01513f1bb 100644 --- a/docs/Worker.html +++ b/docs/Worker.html @@ -151,7 +151,7 @@
    Parameters:
    @@ -312,7 +312,7 @@
    Parameters:
    @@ -385,14 +385,18 @@

    Classes

    Global


    diff --git a/docs/ZMQ.html b/docs/ZMQ.html index 41aec980a..e63bd4beb 100644 --- a/docs/ZMQ.html +++ b/docs/ZMQ.html @@ -333,6 +333,98 @@
    Returns:

    Methods

    + + + + + + +

    _emitErrorSafe()

    + + + + + + +
    + Avoid process crash when nothing listens for `error` (Node EventEmitter default). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + @@ -393,7 +485,7 @@

    (async) start @@ -507,7 +599,7 @@

    (async) stop @@ -580,14 +672,18 @@

    Classes

    Global


    diff --git a/docs/global.html b/docs/global.html index 36eef7558..64be63fca 100644 --- a/docs/global.html +++ b/docs/global.html @@ -103,7 +103,7 @@

    Members

    -

    (constant) BEACON_EPOCH_SIGNING_KIND :string

    +

    (constant) BODY_SCHEMA_BY_KEY :Map.<(number|string), Array.<{name: string, type: string}>>

    @@ -114,7 +114,7 @@
    Type:
    • - string + Map.<(number|string), Array.<{name: string, type: string}>>
    • @@ -155,7 +155,7 @@
      Type:
      @@ -175,15 +175,13 @@
      Type:
      -

      (constant) FRIENDLY_TYPE_BY_WIRE

      +

      GenericMessage

      - Wire-level type strings (ALL_CAPS, opcode decode) ↔ JSON-oriented friendly names (PascalCase - where historically used). Message#wireType / Message#type use wire names; - Message#friendlyType and Message#toObject `type` use friendly names. + Transitional Hub/browser catch-all (opcode GENERIC_MESSAGE_TYPE / 15103).
      @@ -223,7 +221,7 @@

      (consta
      @@ -243,23 +241,13 @@

      (consta -

      (constant) WIRE_TYPE_DECODE_ORDER

      +

      (constant) P2P_CHAT_MAX_CHARS

      - **Two parallel type names:** - - **Wire** (`wireType`, Message#type): SCREAMING_SNAKE_CASE strings from opcode - decode (`fromBuffer`, `toVector` first element). Matches AMP / `constants.js` style. - - **Friendly** (Message#friendlyType, `toObject().type`): PascalCase (or historical - labels) for JSON and human-facing APIs — see FRIENDLY_TYPE_BY_WIRE. - - Encode accepts **either** name via merged Message#types (canonical wire + legacy friendly). - Message.wireTypeFromFriendly / Message.friendlyTypeFromWire convert between them. - - Opcode → wire string order matches the historical `type` switch: when multiple labels share one - opcode (e.g. P2P vs Lightning), **first listed** in WIRE_TYPE_DECODE_ORDER wins. + Max UTF-8 code units for first-class P2P_CHAT_MESSAGE body (text only).
      @@ -299,7 +287,7 @@

      (const
      @@ -319,17 +307,13 @@

      (const -

      (constant) crypto

      +

      (constant) P2P_PEER_ALIAS_MAX_CHARS

      - Shared helpers for multi-operator contract execution: canonical payloads, - beacon epoch signing strings, and federation signature verification. - - Used by Hub Beacon, HTTP manifest routes (`@fabric/http`), and peers that - must reject messages outside the agreed program. + Max UTF-8 code units for first-class P2P_PEER_ALIAS body (nickname).
      @@ -369,7 +353,7 @@

      (constant) cryp
      @@ -389,13 +373,13 @@

      (constant) cryp -

      explorerBaseUrl

      +

      (constant) SCHEMA_P2P_FORWARD

      - Optional HTTP origin for block/tx/address REST fallback (e.g. a Hub). Null = RPC only. + Directed onion hop — see module:@fabric/core/functions/fabricOnion.
      @@ -435,7 +419,7 @@

      explor
      @@ -455,14 +439,10 @@

      explor -

      gossip

      - +

      (constant) SEAL_BLOCK

      -
      - Limits relay amplification on P2P_PEER_GOSSIP (hop TTL, payload dedup, per-origin rate). -
      @@ -487,6 +467,13 @@

      gossipDeprecated: +
      +
        +
      • Use CONSENSUS_*; aliases for one release.
      • +
      +
      + @@ -501,7 +488,7 @@

      gossip @@ -521,27 +508,12 @@

      gossipp2pAddNodes :Array.<string>

      - - - - -
      - After RPC is ready, call `addnode add` for each entry (outbound P2P only). - Used for LAN "playnet" regtest sync. Ignored on mainnet unless p2pAddNodesAllowMainnet is true. -
      - +

      (constant) Text

      -
      Type:
      -
        -
      • - Array.<string> -
      • -
      @@ -564,6 +536,13 @@
      Type:
      +
      Deprecated:
      +
      +
        +
      • Require module:services/text~Text from services/text.js instead.
      • +
      +
      + @@ -578,7 +557,7 @@
      Type:
      @@ -598,13 +577,21 @@
      Type:
      -

      p2pAddNodesAllowMainnet

      +

      (constant) crypto

      - When true, p2pAddNodes is applied even on mainnet (private deployments only). + Chain — ledger of Bitcoin-shaped Blocks with consensus policy: + + - `pow` (default) — parent-linked playnet / Bitcoin-style Block + mempool + - `federation` — linear tip; Elements-style k-of-n block signatures (Beacon) + - `gossip` — content-addressed data blocks; merge = union by block id + + Statechain document helpers (`functions/sidechainState`) hold the sealed JSON + document. Digests feed that document / Beacon sidechain heads; raw gossip is + never Beacon authority.
      @@ -644,7 +631,7 @@

      @@ -653,6 +640,15 @@

      See: +
      +
        +
      • docs/CHAIN.md
      • + +
      • docs/DISTRIBUTED_EXECUTION.md
      • +
      +
      +

    @@ -664,15 +660,17 @@

    wireTraffic

    +

    (constant) crypto

    - Inbound wire traffic budgeting (Bitcoin Core–style peer quality). - Credits accrue per rolling window; overflow de-ranks the peer (registry score) - and drops the message. Heavier opcodes cost more credits. + Multi-language Program — executable artifact for Machine, with optional + L1 Bitcoin redeem scaffolding for `bitcoin-script`. + + Languages: `fabric-opcodes` | `javascript` | `bitcoin-script` | `solidity` | `asm` + (solidity/asm compile stubs until Compiler frontends land).
    @@ -712,7 +710,7 @@

    wireTraffi
    @@ -721,6 +719,13 @@

    wireTraffi +
    See:
    +
    +
      +
    • docs/PROGRAM.md
    • +
    +
    +

    @@ -732,9 +737,9 @@

    wireTraffi +

    (constant) fabricCanonicalJson

    -

    Methods

    @@ -742,16 +747,13 @@

    Methods

    -

    epochCommitmentDigestHex(epochPayload) → {string}

    +
    -
    - SHA-256 hex digest of signingStringForBeaconEpoch (public commitment). -
    @@ -761,53 +763,70 @@

    Parameters:

    - - - +
    Deprecated:
    +
    +
      +
    • Not a Fabric type. Prefer: + - `functions/fabricCanonicalJson` (jsonSafe / stableStringify) + - `functions/beaconFederationSigning` (epoch signing / federation verify) + - `functions/fabricProgramManifest` / `Machine.parseManifest` (manifest v1) + - `types/program` + `types/machine` for execution -
    + Thin re-export kept for one release so Hub / older requires keep working. + + - - - - - - - +
    Source:
    +
    + +
    + + - - - + +

    (constant) merge

    + - -
    NameTypeDescription
    epochPayload - object + -
    + +
    + Beacon — L1-tied epoch chain that seals sidechain / contracts digests. + + Regtest: `createEpoch()` mines one block (`generatetoaddress`) then appends + a `BEACON_EPOCH` entry. Non-regtest: `recordEpochFromBlock` follows tips. + + Hub product wiring historically lived in hub.fabric.pub `contracts/beacon.js`; + that module re-exports this type. +
    + @@ -845,7 +864,7 @@
    Parameters:
    @@ -865,29 +884,26 @@
    Parameters:
    +

    (constant) merge

    +
    + Bitcoin-shaped Block: parent-linked header + merkle of leaves, with optional + PoW (`nonce`/`bits`), Elements-style federation signatures, and arbitrary `data`. +
    -
    Returns:
    -
    -
    - Type -
    -
    +
    - string -
    -
    @@ -901,7 +917,6 @@
    Returns:
    -

    friendlyTypeFromWire(wire) → {string}

    @@ -912,64 +927,64 @@

    f +
    Source:
    +
    + +
    -

    Parameters:
    +
    See:
    +
    +
      +
    • docs/CHAIN.md
    • +
    +
    - - - - + - - - - - +

    mineOnStart

    - - - +
    -
    - - -
    NameTypeDescription
    wire +
    + When false, `start()` does not mine an initial epoch (regtest). +
    - string -
    -
    @@ -985,6 +1000,14 @@
    Parameters:
    +
    Source:
    +
    + +
    @@ -992,27 +1015,23 @@
    Parameters:
    +
    -
    Source:
    -
    - -
    +

    (constant) networks

    -
    +
    + Fabric settings use `mainnet`; bitcoinjs-lib 7 names that network `bitcoin`. +
    @@ -1020,6 +1039,11 @@
    Parameters:
    +
    + + + + @@ -1027,21 +1051,36 @@
    Parameters:
    -
    Returns:
    -
    -
    - Type -
    -
    - string + + + + + + + + +
    Source:
    +
    +
    + + + + + + +
    @@ -1053,16 +1092,25 @@
    Returns:
    +

    Methods

    + + + + -

    isAllZero32(buf)

    +

    blockDigest(header) → {string}

    +
    + Content digest for merkle leaves / chain digest (includes optional federationWitness). +
    + @@ -1096,13 +1144,13 @@
    Parameters:
    - buf + header - Buffer + object @@ -1155,7 +1203,7 @@
    Parameters:
    @@ -1182,6 +1230,24 @@
    Parameters:
    +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + string + + +
    +
    + + @@ -1193,7 +1259,7 @@
    Parameters:
    -

    jsonSafe(value) → {*}

    +

    canonicalTypeCode(value) → {number|null}

    @@ -1201,7 +1267,7 @@

    jsonSafe - Drop `undefined` and normalize values the same way JSON.parse(JSON.stringify) does. + Resolve any opcode / wire name / friendly alias to the numeric AMP type code. @@ -1243,7 +1309,16 @@
    Parameters:
    - * + number + | + + string + | + + null + | + + undefined @@ -1296,7 +1371,7 @@
    Parameters:
    @@ -1334,7 +1409,10 @@
    Returns:
    - * + number + | + + null
    @@ -1352,7 +1430,7 @@
    Returns:
    -

    parseDistributedManifestV1(raw) → {object}

    +

    canonicalTypeName(value) → {string|null}

    @@ -1360,7 +1438,7 @@

    - Setup-phase manifest schema (v1): program identity + allowed traffic + optional federation policy. + Resolve any opcode / wire name / friendly alias to the SCREAMING_SNAKE wire label. @@ -1396,13 +1474,22 @@
    Parameters:
    - raw + value - object + number + | + + string + | + + null + | + + undefined @@ -1455,7 +1542,7 @@
    Parameters:
    @@ -1493,7 +1580,10 @@
    Returns:
    - object + string + | + + null
    @@ -1511,18 +1601,12 @@
    Returns:
    -

    peerDebugDerivedPublicSummary()

    - +

    isStructuredBlockInput(input) → {boolean}

    -
    - Safe debug label for a derived Key — never log private material. -
    - - @@ -1532,51 +1616,61 @@

    Parameters:

    -
    + + + + + + + + + + + + + + -
    Source:
    -
    - -
    + +
    NameTypeDescription
    input + object +
    +
    -
    @@ -1600,10 +1694,69 @@

    Source: +
    + +
    + + + + + + + +

    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    -

    signingStringForBeaconEpoch(epochPayload) → {string}

    + + + + + + + + + + +

    meetsProofOfWork(idHex, bits) → {boolean}

    @@ -1611,7 +1764,7 @@

    <
    - UTF-8 string that federation members sign for a beacon epoch (same bytes for all validators). + Soft playnet PoW: leading zero hex nibbles from `bits` (integer 0–64).
    @@ -1647,13 +1800,13 @@
    Parameters:
    - epochPayload + idHex - object + string @@ -1663,7 +1816,33 @@
    Parameters:
    - — clock, blockHash, height, balance, balanceSats, timestamp, … + + + + + + + + bits + + + + + + number + | + + null + + + + + + + + + + @@ -1706,7 +1885,7 @@
    Parameters:
    @@ -1744,7 +1923,7 @@
    Returns:
    - string + boolean
    @@ -1762,7 +1941,7 @@
    Returns:
    -

    stableStringify(value) → {string}

    +

    signingStringForBlock(header) → {string}

    @@ -1770,7 +1949,7 @@

    stable
    - Deterministic JSON (sorted object keys) for hashing and signing. + Canonical signing / digest body (excludes witness material).
    @@ -1806,13 +1985,13 @@

    Parameters:
    - value + header - * + object @@ -1865,7 +2044,7 @@
    Parameters:
    @@ -1921,7 +2100,7 @@
    Returns:
    -

    verifyFederationWitnessOnMessage(messageBuffer, witness, validatorPubkeys, thresholdopt) → {boolean}

    +

    typeEquals(a, b) → {boolean}

    @@ -1929,8 +2108,8 @@

    - Verify threshold Schnorr signatures over the **same** message buffer used when signing - (`Key.signSchnorr(messageBuffer)`), without requiring a full Federation instance. + True when two type references name the same AMP opcode (number, wire name, or friendly alias). + Unregistered string labels only match via exact trim equality. @@ -1954,12 +2133,8 @@
    Parameters:
    Type - Attributes - - Default - Description @@ -1970,118 +2145,224 @@
    Parameters:
    - messageBuffer + a - Buffer + number + | + + string + | + + null + | + + undefined - + + - + + + b + + + + + + number + | + + string + | + + null + | + + undefined - - — typically `Buffer.from(signingStringForBeaconEpoch(epoch), 'utf8')` + + + + + + - - witness - - object +
    + - - - - - - - - - validatorPubkeys - - Array.<string> +
    Source:
    +
    + +
    - - +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + + boolean + + +
    +
    + - - - - — compressed secp256k1 pubkeys, hex + + +

    Type Definitions

    + + + +

    BitcoinCookieProbeConstraints

    + + + + +
    + Constraints hint for cookie / store probe paths (e.g. pruned mainnet). +
    + + + +
    Type:
    +
      +
    • + + Object + + +
    • +
    + + + + + +
    Properties:
    + + + + + + + + + + + + + + + + + + + + + - + - + @@ -2117,8 +2448,6 @@
    Parameters:
    - -
    @@ -2150,7 +2479,7 @@
    Parameters:
    @@ -2170,83 +2499,122 @@
    Parameters:
    +

    BitcoinLocalCookieProbeOpts

    + -
    Returns:
    +
    Type:
    +
      +
    • + Object +
    • +
    -
    -
    - Type -
    -
    - boolean -
    -
    + +
    Properties:
    + + + +
    NameTypeAttributesDescription
    thresholdstorage - number + Object @@ -2094,20 +2375,70 @@
    Parameters:
    +
    +
    Properties
    -
    + + + - 1 + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    size + + + number + + + + + + <optional>
    + + + +
    + +
    + + + + + + + + + + + + + + + + -
    Parameters:
    + + + -
    NameTypeAttributesDescription
    network -

    wireTypeFromFriendly(friendly) → {string}

    + string +
    + <optional>
    +
    - - + - + + + + + + + + + + - - - + + + @@ -2266,10 +2642,39 @@
    Parameters:
    - -
    NameenvCookieFileType + string - Description + + <optional>
    + + + +
    friendlysettingsDatadir @@ -2259,6 +2627,14 @@
    Parameters:
    + + <optional>
    + + + +
    + + + + constraints + + + + BitcoinCookieProbeConstraints + + + + + + + + + <optional>
    + + + + + + + + + + + + + + @@ -2305,7 +2710,7 @@
    Parameters:
    @@ -2325,29 +2730,159 @@
    Parameters:
    +

    BitcoinRegtestCookieOpts

    + -
    Returns:
    +
    Type:
    +
      +
    • + Object + + +
    • +
    -
    -
    - Type -
    -
    - string +
    Properties:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    envCookieFile + + + string + + + + + + <optional>
    + + + +
    settingsDatadir + + + string + + + + + + <optional>
    + + + +
    + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    +
    -
    @@ -2355,6 +2890,11 @@
    Returns:
    +
    + + + + @@ -2374,14 +2914,18 @@

    Classes

    Global


    diff --git a/docs/global.html#Text b/docs/global.html#Text new file mode 100644 index 000000000..5730beab1 --- /dev/null +++ b/docs/global.html#Text @@ -0,0 +1,6469 @@ + + + + + Class: Text · Docs + + + + + + + + +

    Class: Text

    + + + + +
    + +
    + +

    Text()

    + + +
    + +
    +
    + + + + + + +

    new Text()

    + + + + + + +
    + Text-oriented Service stub (legacy name was TXT). +Static helpers mirror small utilities used in Sensemaker (tokenize, middle truncation, +relative time strings) and core helpers (module:functions/oxfordJoin). +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +

    Methods

    + + + + + + + +

    (async) _GET(path) → {Promise}

    + + + + + + +
    + Retrieve a value from the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + +String + + + + Path of the value to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with the result. +
    + + + +
    +
    + Type +
    +
    + +Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _PUT(path, value, commitopt) → {Promise}

    + + + + + + +
    + Store a value in the Service's state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    path + + +String + + + + + + + + + + + + Path to store the value at.
    value + + +Object + + + + + + + + + + + + Document to store.
    commit + + +Boolean + + + + + + <optional>
    + + + + + +
    + + false + + Sign the resulting state.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with with stored document. +
    + + + +
    +
    + Type +
    +
    + +Promise + + +
    +
    + + + + + + + + + + + + + +

    _appendWarning(msg) → {Service}

    + + + + + + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + +String + + + + Warning text (used by Service#_registerService duplicate guard).
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + This instance. +
    + + + +
    +
    + Type +
    +
    + +Service + + +
    +
    + + + + + + + + + + + + + +

    _readObject(input) → {Object}

    + + + + + + +
    + Parse an Object into a corresponding Fabric state. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    input + + +Object + + + + Object to read as input.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Fabric state. +
    + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    (async) _registerActor(actor) → {Promise}

    + + + + + + +
    + Register an Actor with the Service. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    actor + + +Object + + + + Instance of the Actor.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves upon successful registration. +
    + + + +
    +
    + Type +
    +
    + +Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) _send(message)

    + + + + + + +
    + Sends a message. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + +Mixed + + + + Message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    adopt(changes) → {Actor}

    + + + + + + +
    + Explicitly adopt a set of JSONPatch-encoded changes. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + +Array + + + + List of JSONPatch operations to apply.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + +Actor + + +
    +
    + + + + + + + + + + + + + +

    beat() → {Service}

    + + + + + + +
    + Compute latest state. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + +
    Fires:
    +
      +
    • Message#event:beat
    • +
    + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Service + + +
    +
    + + + + + + + + + + + + + +

    commit() → {String}

    + + + + + + +
    + Resolve the current state to a commitment. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + 32-byte ID +
    + + + +
    +
    + Type +
    +
    + +String + + +
    +
    + + + + + + + + + + + + + +

    (async) connect(notify) → {Promise}

    + + + + + + +
    + Attach to network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    notify + + +Boolean + + + + + + true + + Commit to changes.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves to Fabric. +
    + + + +
    +
    + Type +
    +
    + +Promise + + +
    +
    + + + + + + + + + + + + + +

    defineBitcoinOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Bitcoin-style primitive opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + +string + + + + + + + + + +
    definition + + +Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    defineFabricOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register Fabric opcode metadata. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + +string + + + + + + + + + +
    definition + + +Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcode(name, definitionopt) → {Object}

    + + + + + + +
    + Register a single opcode entry in the service registry. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + +string + + + + + + + + + + Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`)
    definition + + +Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    defineOpcodeContract(name, body, metaopt) → {Object}

    + + + + + + +
    + Register a newline-delimited opcode contract. +Contract body example: +`OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    name + + +string + + + + + + + + + + Contract label
    body + + +string + + + + + + + + + + Newline-delimited opcode list
    meta + + +Object + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    export() → {Object}

    + + + + + + +
    + Export the Actor's state to a standard Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Standard object. +
    + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    get(path) → {Mixed}

    + + + + + + +
    + Retrieve a key from the State. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + +Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Returns the target value if found, otherwise null. +
    + + + +
    +
    + Type +
    +
    + +Mixed + + +
    +
    + + + + + + + + + + + + + +

    handler(message) → {Service}

    + + + + + + +
    + Default route handler for an incoming message. Follows the Activity +Streams 2.0 spec: https://www.w3.org/TR/activitystreams-core/ +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    message + + +Activity + + + + Message object.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + +Service + + +
    +
    + + + + + + + + + + + + + +

    init()

    + + + + + + +
    + Called by Web Components. +TODO: move to @fabric/http/types/spa +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    listOpcodes() → {Array.<Object>}

    + + + + + + +
    + Snapshot opcode registry for UI / API use. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Array.<Object> + + +
    +
    + + + + + + + + + + + + + +

    lock(durationopt) → {Boolean}

    + + + + + + +
    + Attempt to acquire a lock for `duration` seconds. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    duration + + +Number + + + + + + <optional>
    + + + + + +
    + + 1000 + + Number of milliseconds to hold lock.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + true if locked, false if unable to lock. +
    + + + +
    +
    + Type +
    +
    + +Boolean + + +
    +
    + + + + + + + + + + + + + +

    pause() → {Actor}

    + + + + + + +
    + Toggles `status` property to paused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + +Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) route(msg) → {Promise}

    + + + + + + +
    + Resolve a State from a particular Message object. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    msg + + +Message + + + + Explicit Fabric Message.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Resolves with resulting State. +
    + + + +
    +
    + Type +
    +
    + +Promise + + +
    +
    + + + + + + + + + + + + + +

    (async) send(channel, message) → {Service}

    + + + + + + +
    + Send a message to a channel. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    channel + + +String + + + + Channel name to which the message will be sent.
    message + + +String + + + + Content of the message to send.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Chainable method. +
    + + + +
    +
    + Type +
    +
    + +Service + + +
    +
    + + + + + + + + + + + + + +

    serialize() → {String}

    + + + + + + +
    + Serialize the Actor's current state into a JSON-formatted string. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +String + + +
    +
    + + + + + + + + + + + + + +

    set(path) → {Mixed}

    + + + + + + +
    + Set a key in the State to a particular value. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + +Path + + + + Key to retrieve.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Mixed + + +
    +
    + + + + + + + + + + + + + +

    sign() → {Actor}

    + + + + + + +
    + Signs the Actor. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Actor + + +
    +
    + + + + + + + + + + + + + +

    (async) start()

    + + + + + + +
    + Start the service, including the initiation of an outbound connection +to any peers designated in the service's configuration. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +

    stream(pipeopt) → {TransformStream}

    + + + + + + +
    + Returns a new output stream for the Actor. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    pipe + + +TransformStream + + + + + + <optional>
    + + + + + +
    Pipe to stream to.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + New output stream for the Actor. +
    + + + +
    +
    + Type +
    +
    + +TransformStream + + +
    +
    + + + + + + + + + + + + + +

    tick() → {Number}

    + + + + + + +
    + Move forward one clock cycle. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Number + + +
    +
    + + + + + + + + + + + + + +

    toBuffer() → {Buffer}

    + + + + + + +
    + Casts the Actor to a normalized Buffer. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Buffer + + +
    +
    + + + + + + + + + + + + + +

    toGenericMessage(typeopt) → {Object}

    + + + + + + +
    + Casts the Actor to a generic message envelope for state announcements and history. +Shape is stable: `{ type, object }` where `object` is sorted-key state (Actor#toObject). +Actor#id derives from a digest of the pretty-printed generic message; extending this +envelope requires a format/version migration across the network. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    type + + +String + + + + + + <optional>
    + + + + + +
    + + 'FabricActorState' + + Logical message type string.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + +
    See:
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + `{ type, object }` +
    + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    toObject() → {Object}

    + + + + + + +
    + Returns the Actor's current state as an Object. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    trust(source) → {Service}

    + + + + + + +
    + Explicitly trust all events from a known source. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    source + + +EventEmitter + + + + Emitter of events.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of Service after binding events. +
    + + + +
    +
    + Type +
    +
    + +Service + + +
    +
    + + + + + + + + + + + + + +

    unpause() → {Actor}

    + + + + + + +
    + Toggles `status` property to unpaused. +
    + + + + + + + + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of the Actor. +
    + + + +
    +
    + Type +
    +
    + +Actor + + +
    +
    + + + + + + + + + + + + + +

    value(formatopt) → {Object}

    + + + + + + +
    + Get the inner value of the Actor with an optional cast type. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    format + + +String + + + + + + <optional>
    + + + + + +
    + + object + + Cast the value to one of: `buffer, hex, json, string`
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Inner value of the Actor as an Object, or cast to the requested `format`. +
    + + + +
    +
    + Type +
    +
    + +Object + + +
    +
    + + + + + + + + + + + + + +

    when(event, method) → {EventEmitter}

    + + + + + + +
    + Bind a method to an event, with current state as the immutable context. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    event + + +String + + + + Name of the event upon which to execute `method` as a function.
    method + + +function + + + + Function to execute when named Event `event` is encountered.
    + + + + + + +
    + + + + + + +
    Inherited From:
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + +
    + Instance of EventEmitter. +
    + + + +
    +
    + Type +
    +
    + +EventEmitter + + +
    +
    + + + + + + + + + + + + + +

    (static) oxfordJoin(list) → {string}

    + + + + + + +
    + Join a list with an Oxford comma (delegates to module:functions/oxfordJoin). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    list + + +Array.<string> + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +string + + +
    +
    + + + + + + + + + + + + + +

    (static) toRelativeTime(date) → {string}

    + + + + + + +
    + Human-readable relative time (e.g. 3 days ago), ported from Sensemaker. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    date + + +Date +| + +string +| + +number + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +string + + +
    +
    + + + + + + + + + + + + + +

    (static) tokenize(string) → {Array.<string>}

    + + + + + + +
    + Split on runs of whitespace (Sensemaker-style tokenization). +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    string + + +string + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +Array.<string> + + +
    +
    + + + + + + + + + + + + + +

    (static) truncateMiddle(fullStr, strLen, separatoropt) → {string}

    + + + + + + +
    + Shorten a string in the middle if longer than strLen. +
    + + + + + + + + + +
    Parameters:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDescription
    fullStr + + +string + + + + + + + + + +
    strLen + + +number + + + + + + + + + +
    separator + + +string + + + + + + <optional>
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + + + + + + + + + + + + +
    Returns:
    + + + + +
    +
    + Type +
    +
    + +string + + +
    +
    + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 54ba597c2..d95edf33f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -58,6 +58,7 @@

    Contents

  • Repository layout
  • Development workflow
  • Bitcoin service
  • +
  • Lightning BOLTs, RPC map & Hub desktop (npm link)
  • Message types
  • Architecture
  • Storage
  • @@ -71,11 +72,12 @@

    Vision

    Quick Start

    See also QUICKSTART.md for up-to-date instructions.

      -
    1. nvm use 22.14.0 (install nvm if needed)
    2. -
    3. From a clone of this repo: npm install (or npm install -g @fabric/core to put fabric on your PATH)
    4. +
    5. nvm use 24.15.0 (install nvm if needed; matches .nvmrc / package.json engines) + 0b. Ensure npm 12+ (npm -v). Node 24.15.0 may ship npm 11.x — upgrade with npm install -g npm@12 (or newer) before installing. Downstream packages that install Fabric from GitHub need .npmrc allow-git=all (npm 12+); see Hub / @fabric/http / GoonCitizen.
    6. +
    7. From a clone of this repo: npm install (or npm install -g @fabric/core to put fabric on your PATH).
    8. (optional) fabric setup to generate a master key and local config
    9. (optional) fabric keygen to generate a new master key without saving to disk (ephemeral)
    10. -
    11. Run fabric — the CLI entry is wired through types/cli.js and extends Service.FabricShell
    12. +
    13. Run fabric — the CLI entry is wired through types/cli.js and extends Service.FabricShell. Contracts (HTLCs, document sessions, programs, shell packs, …) are documented in docs/CONTRACTS.md; terminal UX in docs/CLI.md.

    Working from a git checkout (not the global package) is best when you are changing @fabric/core itself; use npm link or npm install ../fabric from downstream packages (Hub, HTTP server) as described in the repo README.

    Repository layout

    @@ -119,7 +121,7 @@

    Repository layout

    Development workflow

      -
    • Node: engines field in package.json is authoritative (currently Node 22.x).
    • +
    • Node: engines field in package.json is authoritative (currently Node 24.15.x).
    • Unit tests: npm test — runs Mocha recursively under tests/.
    • Lint: npm run lint / npm run lint:fix (Semistandard).
    • API reference: npm run make:api writes API.md from JSDoc (see scripts/list-jsdoc-type-files.js for which types/*.js files are included).
    • @@ -140,16 +142,20 @@

      Production & Release

      - Node 22.14.x, npm ci, npm run ci + Node 24.15.0 (see .nvmrc), npm ci, npm run ci docs/PRODUCTION.md + + Public leaf API + scoped RC claim + PUBLIC_API.md + Completion / privacy / security matrix - docs/PRODUCTION-CHECKLIST.md, PRIVACY.md, SECURITY.md + docs/PRODUCTION.md, PRIVACY.md, SECURITY.md, AUDIT.md Version tag, changelog, Hub & fabric-http bumps - docs/RELEASE_CHECKLIST.md + docs/PRODUCTION.md (§ Release checklist) Operator privacy model @@ -161,9 +167,11 @@

      Production & Release

      -

      Build scripts: npm run build runs make:all, which still has placeholder make:service / make:app / make:lib steps. The release gate for quality is npm run ci (full Mocha suite), not a successful npm run build. Track operator and bundle readiness in docs/PRODUCTION-CHECKLIST.md and docs/PRODUCTION.md.

      +

      Build scripts: npm run build runs make:all, which still has placeholder make:service / make:app / make:lib steps. The release gate for quality is npm run ci (full Mocha suite), not a successful npm run build. Track operator and bundle readiness in docs/PRODUCTION.md.

      Core Types (reference)

      -

      These live under types/*.js (CommonJS). The Fabric facade (types/fabric.js) re-exports many of them for quick experiments; production code usually imports a leaf type.

      +

      These live under types/*.js (CommonJS). The Fabric facade (types/fabric.js) re-exports many of them for quick experiments; production code must import a leaf type from the frozen map in PUBLIC_API.md.

      +

      Beacon (types/beacon): L1-tied epoch chain sealing sidechain / contracts digests (Hub re-exports as contracts/beacon.js).

      +

      Sidechain document (functions/sidechainState + documentRegistrySidechain): logical sidechain/STATE + journal/snapshots/tip restore. Wire updates use typed SIDECHAIN_STATE_PATCH fields; RFC6902 JSON is an @fabric/http edge transform. See docs/DISTRIBUTED_EXECUTION.md. Network-wide field map + Program: docs/NETWORK_STATE_PROGRAM.md; wire diagram contracts/protocol.dot.

      @@ -209,6 +217,42 @@

      Core Types (reference)

      Regenerate API.md with npm run make:api after JSDoc changes. Experimental or legacy-only files may be omitted via scripts/list-jsdoc-type-files.js.

      Bitcoin service (services/bitcoin)

      RPC is the source of truth when a node is connected. Optional HTTP fallback for block, transaction, and address-index reads is configured only via bitcoin.explorerBaseUrl or FABRIC_EXPLORER_URL (an origin, not a path). If unset, those helpers stay RPC-only or fail closed with a clear error — @fabric/core does not default to any public explorer.

      +

      Lightning: BOLTs, RPC map & Hub desktop (npm link)

      +
      + + + + + + + + + + + + + + + + + + + + + + + + +
      DocPurpose
      docs/BOLT_COMPATIBILITY.mdChecklist: BOLT #1–#12 vs delegated to lightningd vs exposed on Lightning
      docs/FABRIC_LIGHTNING_OFFERS.mdFabric markets (commerce / P2P) vs BOLT12; Lightning.Bolt12
      docs/FABRIC_PAYMENT_BECH32.mdfa1… bech32m Fabric-routed payments; Lightning.FabricPayment
      docs/LIGHTNING_COMPAT.mdCore Lightning JSON-RPC ↔ Fabric method names
      +

      Programmatic: require('@fabric/core/services/lightning').DOCS lists Markdown paths (fabricLightningMarkets and fabricLightningOffers are the same file); Lightning.Bolt12 re-exports functions/lightningBolt12.js (FabricLightningMarketRole / FabricLightningOfferRole); Lightning.Bolt12Semantics re-exports functions/bolt12Semantics.js (BIP-340 scope for decode, recurrence TLV helpers); Lightning.FabricPayment re-exports functions/fabricPaymentBech32.js (fa1… encode/decode and classifyPaymentEncodingString).

      +

      Manual test: @fabric/hub desktop + local @fabric/core / @fabric/http

      +

      After changing @fabric/core, you can exercise the stack from the @fabric/hub package (Electron / desktop; e.g. sibling clone hub.fabric.pub) by linking local packages. Typical order:

      +
        +
      1. This repo (@fabric/core): npm link — registers the global link for @fabric/core.
      2. +
      3. @fabric/http (sibling clone): npm link @fabric/core then npm link — HTTP server depends on core; second npm link publishes @fabric/http globally.
      4. +
      5. Hub (@fabric/hub): npm link @fabric/core and npm link @fabric/http — resolves both to your working trees.
      6. +
      +

      Then start the Hub desktop app (see Hub’s package.json, e.g. build:desktop / Electron scripts). Verify Lightning UI or HTTP mutations against your linked core. To unlink later: npm unlink @fabric/core / @fabric/http in Hub and HTTP, then reinstall published versions as needed.

      Message types (types/message)

      P2P_MESSAGE_RECEIPT (constants.P2P_MESSAGE_RECEIPT, 0x44) is the on-wire type for server acknowledgements of an inbound WebSocket/P2P message (payload JSON uses @type: Receipt). It is distinct from GenericMessage so clients can discriminate without parsing the body first.

      Architecture

      @@ -228,12 +272,15 @@

      Overview

      0. Assets

      Files here feed the default inventory for packaged releases. When using @fabric/http’s server, many assets are served from / (configurable). Use this tree for generated binaries, WASM, and bundled UI — avoid committing large binaries unless they are part of the release process.

      0.1 Inventory
      -

      For the 0.1 line, focus is Lightning-oriented document exchange. Operators running fabric chat can:

      +

      For the 0.1 line, focus is consenting peer file exchange plus ranked L1 document markets. Operators running fabric / fabric chat can:

        -
      1. Load a file into local inventory: /import <filename>
      2. -
      3. Publish to peers: /publish <documentID> <rate>
      4. -
      5. Request from the network: /request <documentID> <rate>
      6. +
      7. /import/publish <id> <rateSats>
      8. +
      9. /inventory [peer] [btc]/offers [id] (rank by price/latency/score)
      10. +
      11. Unpaid: /request → seller /approve; paid: /buy → pay → /confirm <settlementId> <txid>
      12. +
      13. Private relay fees: /relayfees; budgeted requests rewrite hops with fee skim
      14. +
      15. Large files: multi-blob offers and reassembly (see L1 doc)
      +

      Details: docs/L1_DOCUMENT_EXCHANGE.md; BIP: docs/bip-fabric-file-exchange.md.

      When this surface is stable and well tested, the project can tag 0.1.0-RC1 and move toward a security audit.

      0.2 Roadmap

      See the official Fabric roadmap for planned work.

      @@ -382,6 +429,14 @@

      Reference links

      QUICKSTART.md Install and first commands + + docs/CONTRACTS.md + Contracts as interfaces; Hub registry (Hub publishes first) + + + docs/CLI.md + Terminal /contracts, refunds, verbosity + AGENTS.md Agent services, lifecycle, workers @@ -414,6 +469,224 @@

      TODO

      + + + + +
      + +
      + +

      types/actor.js

      + + +
      + +
      +
      + + +
      Base Actor type for Fabric: JSON-shaped state, JSON Patch commits, and a + content-derived id. + +

      State_state.content is observed with fast-json-patch. + Actor#commit turns observer diffs into Actor#history entries and emits commit plus + message with type: 'ActorMessage' / data.type: 'Changes'. +

      + +

      Identity — Actor#id is a SHA256 digest (hex) of the 32-byte preimage buffer; + Actor#preimage is SHA256(UTF-8) of the pretty-printed Actor#toGenericMessage + { type, object } with sorted keys (Actor#toObject). Implementation uses Hash256.compute. + Treat id as a content address for that state shape, not an arbitrary application string hash. +

      + +

      Relationship to MessageMessage extends Actor and implements + AMP (wire headers, opcodes, Schnorr Fabric/Message). Downstream apps that only need a stable + storage key should not label that key Actor#id unless it is produced by this type. +

      + +

      Narrative docs: DEVELOPERS.md (section Actor and Message) and this file’s class JSDoc are + kept in sync; npm run make:docs embeds DEVELOPERS.md as the HTML home page, while Actor.html is generated + from here.

      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + +
      + + + + + + + +
      + + + + +
      + + + + + + + + + + + + + + + + + + + + +
      + +
      + + + + + + + +
      + +
      + +

      types/datastore.js

      + + +
      + +
      +
      + + +
      Ledger-adjacent Store extension. Consolidation with Store + is planned (single persistence surface + optional ledger facet); left as-is until that + design is scheduled.
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + +
      + + + + + + + +
      + + + + +
      + + + + + + + + + + + + + + + + + + + + +
      + +
      + + +
      diff --git a/docs/services_bitcoin.js.html b/docs/services_bitcoin.js.html index 8da3a35cb..5aff709db 100644 --- a/docs/services_bitcoin.js.html +++ b/docs/services_bitcoin.js.html @@ -39,12 +39,11 @@

      Source: services/bitcoin.js

      FABRIC_USER_AGENT } = require('../constants'); -const OP_TRACE = require('../contracts/trace'); - // Dependencies const crypto = require('crypto'); const children = require('child_process'); const fs = require('fs'); +const os = require('os'); const path = require('path'); // External Dependencies @@ -55,11 +54,9 @@

      Source: services/bitcoin.js

      // crypto support libraries // Use noble-curves-backed ECC shim instead of tiny-secp256k1 -const ECPairFactory = require('ecpair').default; const ecc = require('../types/ecc'); const bip65 = require('bip65'); const bip68 = require('bip68'); -const ECPair = ECPairFactory(ecc); const bitcoin = require('bitcoinjs-lib'); // Initialize bitcoinjs-lib with the ECC library @@ -74,7 +71,6 @@

      Source: services/bitcoin.js

      const Entity = require('../types/entity'); const Key = require('../types/key'); const Service = require('../types/service'); -const State = require('../types/state'); const Wallet = require('../types/wallet'); // Special Types (internal to Bitcoin) @@ -88,11 +84,328 @@

      Source: services/bitcoin.js

      ); } +const SATS_PER_BTC = 10 ** 8; +// Internal: max |sats - round(sats)| allowed for BTC -> sats conversion +// (reject fractional satoshis while tolerating float noise). +const SAT_ADJ_EPS = 1 / (10 ** 6); +// Internal: reject absurd cookie path lengths before filesystem reads. +const BITCOIN_COOKIE_PATH_MAX_LEN = 4096; + +// Internal allowlist: Bitcoin Core chain directory names under -datadir. +const BITCOIND_CHAIN_FOLDER_NAMES = new Set(['regtest', 'testnet3', 'testnet4', 'signet']); + +// Internal helper: append fixed path components under baseAbs and reject traversal. +function cookiePathUnderDatadirBase (baseAbs, parts) { + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + const root = path.resolve(baseAbs); + let cur = root; + for (const part of parts) { + // `part` is allowlisted (chain dir name or `.cookie`); traversal is checked via `relative` below. + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + cur = path.resolve(cur, part); + } + const rel = path.relative(root, cur); + if (rel.startsWith('..' + path.sep) || rel === '..' || path.isAbsolute(rel)) { + return null; + } + return cur; +} + +/** + * Constraints hint for cookie / store probe paths (e.g. pruned mainnet). + * @typedef {Object} BitcoinCookieProbeConstraints + * @property {Object} [storage] + * @property {number} [storage.size] + */ + +/** + * Options for {@link Bitcoin.buildLocalCookieProbePaths}. + * @typedef {Object} BitcoinLocalCookieProbeOpts + * @property {string} [network] + * @property {string} [envCookieFile] + * @property {string} [settingsDatadir] + * @property {BitcoinCookieProbeConstraints} [constraints] + */ + +/** + * Options for {@link Bitcoin.buildRegtestCookiePathList}. + * @typedef {Object} BitcoinRegtestCookieOpts + * @property {string} [envCookieFile] + * @property {string} [settingsDatadir] + */ + /** * Manages interaction with the Bitcoin network. * @augments Service */ class Bitcoin extends Service { + /** + * Bitcoin Core chain data subdirectory under {@code -datadir} (empty string for mainnet cookie at datadir root). + * Matches Core layout: `regtest/`, `testnet3/`, `signet/`, `testnet4/`, or root for mainnet. + * @param {string} network Fabric network name (mainnet, testnet, regtest, signet, testnet4, playnet, …). + * @returns {string} + */ + static bitcoindChainDataDirSegment (network) { + const n = String(network || 'mainnet').toLowerCase(); + if (n === 'regtest' || n === 'playnet') return 'regtest'; + if (n === 'testnet') return 'testnet3'; + if (n === 'testnet4') return 'testnet4'; + if (n === 'signet') return 'signet'; + return ''; + } + + /** + * Resolve a configured bitcoind datadir for local cookie discovery. Relative paths are cwd-anchored + * and must not escape the project root; absolute paths are normalized as-is. + * @param {string} datadir + * @returns {string|null} + */ + static resolveBitcoinDatadirForLocalAccess (datadir) { + if (datadir == null || typeof datadir !== 'string') return null; + const trimmed = datadir.trim(); + if (!trimmed || trimmed.includes('\0') || trimmed.length > BITCOIN_COOKIE_PATH_MAX_LEN) return null; + let abs; + if (path.isAbsolute(trimmed)) { + abs = path.normalize(trimmed); + } else { + // Relative: containment enforced by `path.relative` check below (cannot escape cwd). + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + abs = path.resolve(process.cwd(), trimmed); + } + if (!path.isAbsolute(trimmed)) { + const root = path.resolve(process.cwd()); + const rel = path.relative(root, abs); + if (rel === '' || rel.startsWith('..' + path.sep) || path.isAbsolute(rel)) { + return null; + } + } + return abs; + } + + /** + * Resolve {@code FABRIC_BITCOIN_COOKIE_FILE}-style paths: normalize, bound length, and keep relative paths + * inside the process cwd (absolute paths allowed for explicit operator overrides). + * @param {string} filePath + * @returns {string|null} + */ + static resolveBitcoinCookieFileForLocalRead (filePath) { + if (filePath == null || typeof filePath !== 'string') return null; + const trimmed = filePath.trim(); + if (!trimmed || trimmed.includes('\0') || trimmed.length > BITCOIN_COOKIE_PATH_MAX_LEN) return null; + let abs; + if (path.isAbsolute(trimmed)) { + abs = path.normalize(trimmed); + } else { + // Relative: containment enforced by `path.relative` check below (cannot escape cwd). + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + abs = path.resolve(process.cwd(), trimmed); + } + if (!path.isAbsolute(trimmed)) { + const root = path.resolve(process.cwd()); + const rel = path.relative(root, abs); + if (rel === '' || rel.startsWith('..' + path.sep) || path.isAbsolute(rel)) { + return null; + } + } + return abs; + } + + /** + * Read Bitcoin Core {@code .cookie} (user:password) using async I/O. Path must already be resolved/normalized. + * @param {string} cookiePath + * @returns {Promise<{ username: string, password: string }|null>} + */ + static async tryReadRpcCookieFileCredentials (cookiePath) { + if (typeof cookiePath !== 'string' || !cookiePath.trim()) return null; + const p = path.normalize(cookiePath.trim()); + if (p.includes('\0') || p.length > BITCOIN_COOKIE_PATH_MAX_LEN) return null; + try { + const raw = (await fs.promises.readFile(p, 'utf8')).trim(); + const colon = raw.indexOf(':'); + if (colon === -1) return null; + return { username: raw.slice(0, colon), password: raw.slice(colon + 1) }; + } catch { + return null; + } + } + + /** + * `.cookie` path under a resolved bitcoind datadir root for the given Fabric network. + * @param {string} datadirRoot Absolute or project-relative resolved datadir (Core {@code -datadir} value). + * @param {string} network + * @returns {string} + */ + static cookiePathForBitcoind (datadirRoot, network) { + const seg = Bitcoin.bitcoindChainDataDirSegment(network); + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + const base = path.resolve(datadirRoot); + if (!seg) { + const p = cookiePathUnderDatadirBase(base, ['.cookie']); + if (!p) throw new Error('[FABRIC:BITCOIN] invalid datadir for cookie path'); + return p; + } + if (!BITCOIND_CHAIN_FOLDER_NAMES.has(seg)) { + throw new Error(`[FABRIC:BITCOIN] internal: unexpected chain dir ${seg}`); + } + const p = cookiePathUnderDatadirBase(base, [seg, '.cookie']); + if (!p) throw new Error('[FABRIC:BITCOIN] invalid datadir for cookie path'); + return p; + } + + /** + * Cookie file under explicit chain subdirectory (empty string = mainnet-style datadir/.cookie only). + * Prefer {@link #cookiePathForBitcoind} when you have a Fabric network name. + * @param {string} datadirRoot + * @param {string} chainSubdir e.g. `regtest`, `signet`, or `''` + * @returns {string} + */ + static cookiePathForChainSubtree (datadirRoot, chainSubdir) { + const s = chainSubdir == null ? '' : String(chainSubdir); + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + const base = path.resolve(datadirRoot); + if (!s) { + const p = cookiePathUnderDatadirBase(base, ['.cookie']); + if (!p) throw new Error('[FABRIC:BITCOIN] invalid datadir for cookie path'); + return p; + } + if (!BITCOIND_CHAIN_FOLDER_NAMES.has(s)) { + throw new Error(`[FABRIC:BITCOIN] unsupported chain subdirectory: ${s}`); + } + const p = cookiePathUnderDatadirBase(base, [s, '.cookie']); + if (!p) throw new Error('[FABRIC:BITCOIN] invalid datadir for cookie path'); + return p; + } + + /** + * Typical `stores/…` paths under the project for the network (for cookie discovery before node spawn). + * @param {string} network + * @param {BitcoinCookieProbeConstraints} [constraints] + * @returns {string[]} + */ + static defaultStoresRelativeDirsForProbe (network, constraints) { + const n = String(network || 'mainnet').toLowerCase(); + switch (n) { + case 'regtest': + return ['stores/bitcoin-regtest']; + case 'playnet': + return ['stores/bitcoin-playnet']; + case 'testnet': + return ['stores/bitcoin-testnet']; + case 'testnet4': + return ['stores/bitcoin-testnet4']; + case 'signet': + return ['stores/bitcoin-signet']; + case 'mainnet': + default: { + const dirs = ['stores/bitcoin-mainnet']; + if (constraints && constraints.storage && constraints.storage.size) { + dirs.push('stores/bitcoin-mainnet-pruned'); + } + return dirs; + } + } + } + + /** + * Ordered local cookie paths to probe for RPC (env override, project stores, Electron mirror, ~/.bitcoin, optional settings datadir). + * @param {BitcoinLocalCookieProbeOpts} opts + * @returns {string[]} + */ + static buildLocalCookieProbePaths (opts = {}) { + const { network, envCookieFile, settingsDatadir, constraints } = opts; + const net = String(network || 'mainnet').toLowerCase(); + const list = []; + if (envCookieFile) { + const resolvedEnv = Bitcoin.resolveBitcoinCookieFileForLocalRead(String(envCookieFile)); + if (resolvedEnv) list.push(resolvedEnv); + } + const cwd = process.cwd(); + const mainPruned = Boolean(constraints && constraints.storage && constraints.storage.size); + const pushProjectStore = (storeReldir) => { + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + list.push(Bitcoin.cookiePathForBitcoind(path.resolve(cwd, storeReldir), net)); + }; + switch (net) { + case 'regtest': + pushProjectStore('stores/bitcoin-regtest'); + break; + case 'playnet': + pushProjectStore('stores/bitcoin-playnet'); + break; + case 'testnet': + pushProjectStore('stores/bitcoin-testnet'); + break; + case 'testnet4': + pushProjectStore('stores/bitcoin-testnet4'); + break; + case 'signet': + pushProjectStore('stores/bitcoin-signet'); + break; + default: + pushProjectStore('stores/bitcoin-mainnet'); + if (mainPruned) pushProjectStore('stores/bitcoin-mainnet-pruned'); + } + if (process.platform === 'darwin') { + const hd = os.homedir(); + const pushElectronStore = (folderName) => { + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + const electronRoot = path.join(hd, 'Library/Application Support/Electron/stores', folderName); + list.push(Bitcoin.cookiePathForBitcoind(electronRoot, net)); + }; + switch (net) { + case 'regtest': + pushElectronStore('bitcoin-regtest'); + break; + case 'playnet': + pushElectronStore('bitcoin-playnet'); + break; + case 'testnet': + pushElectronStore('bitcoin-testnet'); + break; + case 'testnet4': + pushElectronStore('bitcoin-testnet4'); + break; + case 'signet': + pushElectronStore('bitcoin-signet'); + break; + default: + pushElectronStore('bitcoin-mainnet'); + if (mainPruned) pushElectronStore('bitcoin-mainnet-pruned'); + } + } + { + // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal + const homeData = path.join(os.homedir(), '.bitcoin'); + list.push(Bitcoin.cookiePathForBitcoind(homeData, net)); + } + if (settingsDatadir && typeof settingsDatadir === 'string' && settingsDatadir.trim()) { + const resolved = Bitcoin.resolveBitcoinDatadirForLocalAccess(settingsDatadir); + if (resolved) list.push(Bitcoin.cookiePathForBitcoind(resolved, net)); + } + return list; + } + + /** + * Parent directory name of `.cookie` (for probe logging). + * @param {string} cookiePath + * @returns {string} + */ + static parentDirNameForCookieProbe (cookiePath) { + if (typeof cookiePath !== 'string' || !cookiePath) return 'unknown'; + const parts = cookiePath.split(/[/\\]/).filter(Boolean); + if (parts.length < 2) return parts[0] || 'unknown'; + return parts[parts.length - 2]; + } + + /** + * @deprecated Use {@link #buildLocalCookieProbePaths} with `network: 'regtest'`. + * @param {BitcoinRegtestCookieOpts} opts + * @returns {string[]} + */ + static buildRegtestCookiePathList (opts = {}) { + return Bitcoin.buildLocalCookieProbePaths({ ...opts, network: 'regtest' }); + } + /** * Creates an instance of the Bitcoin service. * @param {Object} [settings] Map of configuration options for the Bitcoin service. @@ -123,7 +436,7 @@

      Source: services/bitcoin.js

      spv: { port: 18332 }, - /** Optional HTTP origin for block/tx/address REST fallback (e.g. a Hub). Null = RPC only. */ + // Optional HTTP origin for block/tx/address REST fallback (e.g. a Hub). Null = RPC only. explorerBaseUrl: null, zmq: { host: 'localhost', @@ -151,19 +464,20 @@

      Source: services/bitcoin.js

      servers: [], targets: [], peers: [], - /** - * After RPC is ready, call `addnode <host:port> add` for each entry (outbound P2P only). - * Used for LAN "playnet" regtest sync. Ignored on mainnet unless {@link p2pAddNodesAllowMainnet} is true. - * @type {string[]} - */ + // After RPC is ready, call `addnode <host:port> add` for each entry (outbound P2P only). + // Used for LAN "playnet" regtest sync. Ignored on mainnet unless p2pAddNodesAllowMainnet is true. p2pAddNodes: [], - /** When true, {@link p2pAddNodes} is applied even on mainnet (private deployments only). */ + // When true, p2pAddNodes is applied even on mainnet (private deployments only). p2pAddNodesAllowMainnet: false, host: '127.0.0.1', port: 8333, // P2P port rpcport: 8332, // RPC port interval: 60000, // 10 * 60 * 1000, // every 10 minutes, write a checkpoint - verbosity: 2 + verbosity: 2, + // When true, flushChainToSnapshot is allowed on mainnet (dangerous). + flushChainAllowUnsafeNetworks: false, + // Safety cap for invalidateblock steps when rewinding to a snapshot tip. + flushChainMaxSteps: 100000 }, settings); const netNorm = this._normalizeChainName(this.settings.network || 'mainnet'); @@ -392,6 +706,7 @@

      Source: services/bitcoin.js

      _getDefaultRPCPort (network = 'mainnet') { switch (network) { case 'regtest': + case 'playnet': return 18443; case 'testnet': return 18332; @@ -407,6 +722,7 @@

      Source: services/bitcoin.js

      _getDefaultP2PPort (network = 'mainnet') { switch (network) { case 'regtest': + case 'playnet': return 18444; case 'testnet': return 18333; @@ -438,22 +754,37 @@

      Source: services/bitcoin.js

      } } + _rpcHostHasSingleNumericPortSuffix (host) { + const s = String(host).trim(); + if (s.startsWith('[')) return false; + const first = s.indexOf(':'); + const last = s.lastIndexOf(':'); + if (first === -1 || first !== last) return false; + return /^\d+$/.test(s.slice(last + 1)); + } + _normalizeRPCHost (value) { if (!value) return '127.0.0.1'; const host = String(value).trim(); if (!host) return '127.0.0.1'; - // bitcoin.conf may specify rpcbind/rpcconnect as host:port. - if (host.includes(':') && !host.startsWith('[') && host.split(':').length === 2) { - return host.split(':')[0]; + if (host.startsWith('[')) { + const close = host.indexOf(']'); + if (close !== -1) return host.slice(0, close + 1); + return host; + } + + if (this._rpcHostHasSingleNumericPortSuffix(host)) { + return host.slice(0, host.lastIndexOf(':')); } return host; } - _buildRPCProbeCandidates () { + async _buildRPCProbeCandidates () { const candidates = []; const seen = new Set(); + const cookiePathsTried = new Set(); const pushCandidate = (candidate) => { if (!candidate || !candidate.host || !candidate.rpcport) return; @@ -475,6 +806,34 @@

      Source: services/bitcoin.js

      candidates.push(normalized); }; + // Cookie-auth probes when no explicit RPC user/pass (mainnet, testnet*, signet, regtest, playnet, …). + if (!this.settings.username && !this.settings.password) { + const net = this.settings.network || 'mainnet'; + const rpcport = Number(this.settings.rpcport) || this._getDefaultRPCPort(net); + const host = this._normalizeRPCHost(this.settings.host || '127.0.0.1'); + const cookiePaths = Bitcoin.buildLocalCookieProbePaths({ + network: net, + envCookieFile: process.env.FABRIC_BITCOIN_COOKIE_FILE, + settingsDatadir: this.settings.datadir, + constraints: this.settings.constraints + }); + for (const cookiePath of cookiePaths) { + if (!cookiePath || cookiePathsTried.has(cookiePath)) continue; + cookiePathsTried.add(cookiePath); + const creds = await Bitcoin.tryReadRpcCookieFileCredentials(cookiePath); + if (!creds) continue; + pushCandidate({ + source: `cookie:${Bitcoin.parentDirNameForCookieProbe(cookiePath)}`, + host, + rpcport, + network: net, + username: creds.username, + password: creds.password, + secure: false + }); + } + } + if (Array.isArray(this.settings.rpcProbeCandidates)) { for (const candidate of this.settings.rpcProbeCandidates) { pushCandidate(candidate); @@ -557,7 +916,7 @@

      Source: services/bitcoin.js

      } async _detectExistingBitcoind () { - const candidates = this._buildRPCProbeCandidates(); + const candidates = await this._buildRPCProbeCandidates(); for (const candidate of candidates) { try { @@ -568,6 +927,18 @@

      Source: services/bitcoin.js

      ]); const detectedNetwork = this._normalizeChainName(chainInfo.chain || candidate.network); + const targetNetwork = this._normalizeChainName(this.settings.network || 'mainnet'); + + // Never reuse a daemon from a different chain. + if (detectedNetwork && targetNetwork && detectedNetwork !== targetNetwork) { + if (this.settings.debug) { + this.emit( + 'debug', + `[FABRIC:BITCOIN] Ignoring external ${detectedNetwork} daemon while target network is ${targetNetwork}` + ); + } + continue; + } if (this.settings.debug) { this.emit( 'debug', @@ -614,7 +985,7 @@

      Source: services/bitcoin.js

      try { bitcoin.address.toOutputScript(address, network); return true; - } catch (e) { + } catch { return false; } @@ -644,8 +1015,8 @@

      Source: services/bitcoin.js

      this._checkAllTargetBalances() ]).catch((exception) => { self.emit('error', `Unable to synchronize: ${exception}`); - }).then((output) => { - // self.emit('log', `Tick output: ${JSON.stringify(output, null, ' ')}`); + }).then((_output) => { + // self.emit('log', `Tick output: ${JSON.stringify(_output, null, ' ')}`); const beat = { clock: self._clock, @@ -697,8 +1068,22 @@

      Source: services/bitcoin.js

      if (!message.amount) throw new Error('Message must provide an amount.'); if (!message.destination) throw new Error('Message must provide a destination.'); - if (message.amount instanceof String) { - message.amount = message.amount.fixed().toPrecision(8); // TODO: evaluate precision behavior + if (typeof message.amount === 'number' && Number.isFinite(message.amount)) { + const sats = message.amount * SATS_PER_BTC; + const rounded = Math.round(sats); + if (!Number.isFinite(sats) || Math.abs(sats - rounded) > SAT_ADJ_EPS) { + throw new Error('Message amount must be a multiple of 1 satoshi (at most 8 decimal places in BTC).'); + } + message.amount = (rounded / SATS_PER_BTC).toFixed(8); + } else if (typeof message.amount === 'string' || message.amount instanceof String) { + const parsed = Number(message.amount instanceof String ? message.amount.valueOf() : message.amount); + if (!Number.isFinite(parsed)) throw new Error('Message amount must be numeric.'); + const sats = parsed * SATS_PER_BTC; + const rounded = Math.round(sats); + if (!Number.isFinite(sats) || Math.abs(sats - rounded) > SAT_ADJ_EPS) { + throw new Error('Message amount must be a multiple of 1 satoshi (at most 8 decimal places in BTC).'); + } + message.amount = (rounded / SATS_PER_BTC).toFixed(8); } const actor = new Actor(message); @@ -733,7 +1118,7 @@

      Source: services/bitcoin.js

      if (!(obj.transactions instanceof Array)) throw new Error('Block must provide transactions as an Array.'); for (const tx of obj.transactions) { - let transaction = await this.transactions.create(tx); + await this.transactions.create(tx); } let entity = new Entity(obj); @@ -779,8 +1164,6 @@

      Source: services/bitcoin.js

      async _registerBlock (obj) { let result = null; - let state = new State(obj); - let transform = [state.id, state.render()]; let prior = null; // TODO: ensure all appropriate fields, valid block @@ -933,7 +1316,7 @@

      Source: services/bitcoin.js

      raw: msg.toRaw().toString('hex') }; - let block = await this.blocks.create(template); + await this.blocks.create(template); } async _handleConnectMessage (entry, block) { @@ -1002,16 +1385,31 @@

      Source: services/bitcoin.js

      return 1; } + _keyNetworkNameForWif () { + const n = this._normalizeChainName(this.settings.network || 'mainnet'); + // Playnet runs against regtest-style bitcoind (WIF/bech32 prefixes match regtest, not mainnet). + if (n === 'playnet') return 'regtest'; + if (n === 'signet' || n === 'testnet4') return 'testnet'; + if (n === 'mainnet' || n === 'testnet' || n === 'regtest') return n; + return 'mainnet'; + } + async _dumpKeyPair (address) { const wif = await this._makeRPCRequest('dumpprivkey', [address]); - const pair = ECPair.fromWIF(wif, this.networks[this.settings.network]); - return pair; + const k = Key.fromWIF(wif, { network: this._keyNetworkNameForWif() }); + const priv = k.private; + const pubHex = k.public.encodeCompressed('hex'); + return { + privateKey: Buffer.isBuffer(priv) ? priv : Buffer.from(priv, 'hex'), + publicKey: Buffer.from(pubHex, 'hex') + }; } async _dumpPrivateKey (address) { const wif = await this._makeRPCRequest('dumpprivkey', [address]); - const pair = ECPair.fromWIF(wif, this.networks[this.settings.network]); - return pair.privateKey; + const k = Key.fromWIF(wif, { network: this._keyNetworkNameForWif() }); + const priv = k.private; + return Buffer.isBuffer(priv) ? priv : Buffer.from(priv, 'hex'); } async _loadPrivateKey (key) { @@ -1156,16 +1554,14 @@

      Source: services/bitcoin.js

      async _connectToSeedNodes () { for (let i = 0; i < this.settings.seeds.length; i++) { - let node = this.settings.seeds[i]; - this.connect(node); + this.connect(this.settings.seeds[i]); } } async _connectToEdgeNodes () { let bitcoin = this; - for (let id in this.settings.nodes) { - let node = this.settings.nodes[id]; + for (const _id in this.settings.nodes) { let peer = bcoin.Peer.fromOptions({ network: this.settings.network, agent: this.UAString, @@ -1219,32 +1615,100 @@

      Source: services/bitcoin.js

      try { switch (topic) { - case 'BitcoinBlock': - case 'BitcoinTransactionHash': + case 'BitcoinBlock': { + const rawHex = (content && typeof content === 'object' && content.content) + ? content.content + : (typeof content === 'string' ? content : null); + if (rawHex) this.emit('block', { raw: rawHex, source: 'BitcoinBlock' }); break; + } + case 'BitcoinTransactionHash': { + const txid = (content && typeof content === 'object' && content.content) + ? content.content + : null; + if (txid && this.settings.walletWatchMempool !== false) { + try { + const verbose = await this._makeRPCRequest('getrawtransaction', [txid, true]); + if (verbose) { + this.emit('transaction', Object.assign({}, verbose, { source: 'BitcoinTransactionHash' })); + if (this.wallet && typeof this.wallet.ingestBitcoinTransaction === 'function') { + this.wallet.ingestBitcoinTransaction(verbose, { source: 'BitcoinTransactionHash' }); + } + } + } catch (e) { + if (this.settings.debug) { + this.emit('debug', `[FABRIC:BITCOIN] mempool tx fetch ${txid}: ${e.message || e}`); + } + } + } + break; + } case 'BitcoinBlockHash': { const blockHashHex = (content && typeof content === 'object' && content.content) ? content.content : (JSON.parse(Buffer.isBuffer(content) ? content.toString() : String(content))).content; - const supply = await this._makeRPCRequest('gettxoutsetinfo', []); - this._state.content.height = supply.height; - this._state.content.tip = blockHashHex; - this._state.content.supply = supply.total_amount; - this.commit(); - this.emit('block', { + let height = null; + let supplyAmount = null; + try { + const supply = await this._makeRPCRequest('gettxoutsetinfo', []); + height = supply.height; + supplyAmount = supply.total_amount; + this._state.content.height = supply.height; + this._state.content.tip = blockHashHex; + this._state.content.supply = supply.total_amount; + this.commit(); + } catch (e) { + if (this.settings.debug) this.emit('debug', `[FABRIC:BITCOIN] tip supply: ${e.message || e}`); + } + + let verboseBlock = null; + try { + verboseBlock = await this._makeRPCRequest('getblock', [blockHashHex, 2]); + if (verboseBlock && verboseBlock.height != null) height = verboseBlock.height; + } catch (e) { + if (this.settings.debug) { + this.emit('debug', `[FABRIC:BITCOIN] getblock(${blockHashHex},2): ${e.message || e}`); + } + } + + const payload = { tip: blockHashHex, - height: supply.height, - supply: supply.total_amount - }); + hash: blockHashHex, + height, + supply: supplyAmount, + tx: (verboseBlock && verboseBlock.tx) || [], + source: 'BitcoinBlockHash' + }; + this.emit('block', payload); + if (this.wallet && typeof this.wallet.ingestBitcoinBlock === 'function') { + try { + this.wallet.ingestBitcoinBlock(payload); + } catch (e) { + this.emit('warning', `[FABRIC:BITCOIN] wallet block ingest: ${e.message || e}`); + } + } break; } case 'BitcoinTransaction': { try { + const rawHex = (content && typeof content === 'object' && content.content) + ? content.content + : null; + if (rawHex) { + const event = { hex: rawHex, source: 'BitcoinTransaction' }; + try { + const bitcoinjs = require('bitcoinjs-lib'); + event.txid = bitcoinjs.Transaction.fromHex(rawHex).getId(); + } catch (_) { /* ignore parse */ } + this.emit('transaction', event); + if (this.wallet && typeof this.wallet.ingestBitcoinTransaction === 'function') { + this.wallet.ingestBitcoinTransaction(rawHex, { source: 'BitcoinTransaction' }); + } + } const balance = await this._makeRPCRequest('getbalances', []).catch(() => null); if (balance != null) { this._state.balances.mine.trusted = balance; this.commit(); - this.emit('transaction', { balance: this._state.balances.mine.trusted }); } } catch (e) { if (this.settings.debug) this.emit('debug', `[FABRIC:BITCOIN] ZMQ BitcoinTransaction handler: ${e.message || e}`); @@ -1254,7 +1718,7 @@

      Source: services/bitcoin.js

      default: if (this.settings.verbosity >= 5) this.emit('debug', `[AUDIT] Unknown ZMQ topic: ${topic}`); } - } catch (exception) { + } catch { //', `Could not process ZMQ message: ${exception}`); } } @@ -1598,7 +2062,6 @@

      Source: services/bitcoin.js

      if (!this.rpc) return reject(new Error('RPC manager does not exist')); // Reuse existing RPC config but change the URL to target the specific wallet - const protocol = this.settings.secure ? 'https' : 'http'; const host = this.settings.host; const port = this.settings.rpcport; const auth = `${this.settings.username}:${this.settings.password}`; @@ -1647,7 +2110,7 @@

      Source: services/bitcoin.js

      async _checkAllTargetBalances () { for (let i = 0; i < this.settings.targets.length; i++) { - const balance = await this._getBalanceForAddress(this.settings.targets[i]); + await this._getBalanceForAddress(this.settings.targets[i]); } } @@ -1671,6 +2134,83 @@

      Source: services/bitcoin.js

      } } + /** + * Rewind the attached Bitcoin Core node to a known-good tip by repeatedly calling `invalidateblock` + * on the current best block until `getbestblockhash` matches `snapshotBlockHash`. + * Allowed on regtest, playnet, signet, testnet, testnet4 unless settings.flushChainAllowUnsafeNetworks. + * @param {string} snapshotBlockHash - 64-char hex block hash to keep as the active tip. + * @returns {Promise<{ ok: boolean, steps: number, snapshotBlockHash: string }>} + */ + async flushChainToSnapshot (snapshotBlockHash) { + const run = () => this._flushChainToSnapshotBody(snapshotBlockHash); + const p = (this._flushChainQueue || Promise.resolve()) + .catch(() => {}) + .then(run); + this._flushChainQueue = p.catch(() => {}); + return p; + } + + /** + * @private + */ + async _flushChainToSnapshotBody (snapshotBlockHash) { + const hex = String(snapshotBlockHash || '').trim().toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(hex)) { + throw new Error('flushChainToSnapshot: snapshotBlockHash must be 64 hex characters'); + } + const net = this._normalizeChainName(this.settings.network || 'mainnet'); + const allowed = + net === 'regtest' || + net === 'playnet' || + net === 'signet' || + net === 'testnet' || + net === 'testnet4'; + if (!allowed && !this.settings.flushChainAllowUnsafeNetworks) { + throw new Error(`flushChainToSnapshot: not allowed for network "${net}" (set flushChainAllowUnsafeNetworks to override)`); + } + const maxSteps = (typeof this.settings.flushChainMaxSteps === 'number' && this.settings.flushChainMaxSteps > 0) + ? this.settings.flushChainMaxSteps + : 100000; + + try { + await this._makeRPCRequest('getblockheader', [hex]); + } catch (err) { + const msg = err && err.message ? String(err.message) : String(err); + throw new Error(`flushChainToSnapshot: snapshot block not known to node (${msg})`); + } + + let cursor = String(await this._makeRPCRequest('getbestblockhash', [])).trim().toLowerCase(); + let reachable = false; + for (let i = 0; i < maxSteps; i++) { + if (cursor === hex) { + reachable = true; + break; + } + const header = await this._makeRPCRequest('getblockheader', [cursor]); + const prev = header && header.previousblockhash + ? String(header.previousblockhash).trim().toLowerCase() + : null; + if (!prev) break; + cursor = prev; + } + if (!reachable) { + throw new Error('flushChainToSnapshot: snapshot is not an ancestor of the active tip'); + } + + let steps = 0; + while (steps < maxSteps) { + const best = String(await this._makeRPCRequest('getbestblockhash', [])).trim().toLowerCase(); + if (best === hex) { + const out = { ok: true, steps, snapshotBlockHash: hex }; + this.emit('message', { type: 'BitcoinFlushChain', data: out }); + return out; + } + await this._makeRPCRequest('invalidateblock', [best]); + steps++; + } + throw new Error(`flushChainToSnapshot: exceeded flushChainMaxSteps=${maxSteps} before reaching snapshot`); + } + async _requestBlockHeader (hash) { return this._makeRPCRequest('getblockheader', [hash]); } @@ -1683,7 +2223,7 @@

      Source: services/bitcoin.js

      return this._makeRPCRequest('getblock', [hash]); } - async _getMempool (hash) { + async _getMempool (_hash) { return this._makeRPCRequest('getrawmempool'); } @@ -1872,19 +2412,19 @@

      Source: services/bitcoin.js

      throw new Error(`Invalid network: ${this.settings.network}`); } - // Calculate total input amount - let inputAmount = 0; + // Calculate total input amount (reserved for fee / change logic) + let _inputAmount = 0; for (const input of options.inputs) { const utxo = await this._makeRPCRequest('gettxout', [input.txid, input.vout]); if (utxo) { - inputAmount += utxo.value * 100000000; // Convert BTC to satoshis + _inputAmount += utxo.value * SATS_PER_BTC; // Convert BTC to satoshis } } - // Calculate total output amount - let outputAmount = 0; + // Calculate total output amount (reserved for fee / change logic) + let _outputAmount = 0; for (const output of options.outputs) { - outputAmount += output.value; + _outputAmount += output.value; } // TODO: add change output @@ -1897,7 +2437,7 @@

      Source: services/bitcoin.js

      const data = { hash: input.txid, index: input.vout, - sequence: 0xffffffff + sequence: -1 >>> 0 }; psbt.addInput(data); @@ -1909,7 +2449,7 @@

      Source: services/bitcoin.js

      const script = bitcoin.address.toOutputScript(output.address, network); const data = { script, - value: output.value + value: typeof output.value === 'bigint' ? output.value : BigInt(Math.round(Number(output.value))) }; psbt.addOutput(data); @@ -2136,7 +2676,17 @@

      Source: services/bitcoin.js

      ]; // Wait for all checks to complete - const results = await Promise.all(checks); + const [chainInfo] = await Promise.all(checks); + + const detectedNetwork = this._normalizeChainName(chainInfo && chainInfo.chain); + const targetNetwork = this._normalizeChainName(this.settings.network || 'mainnet'); + if (detectedNetwork && targetNetwork && detectedNetwork !== targetNetwork) { + this.emit( + 'warning', + `[FABRIC:BITCOIN] RPC endpoint chain mismatch: detected=${detectedNetwork} expected=${targetNetwork}` + ); + return false; + } if (this.settings.debug) { this.emit('debug', '[FABRIC:BITCOIN] Successfully connected to bitcoind'); @@ -2167,8 +2717,9 @@

      Source: services/bitcoin.js

      _defaultBitcoinP2pPort (network) { const n = String(network || '').toLowerCase(); - if (n === 'regtest') return 18444; - if (n === 'testnet' || n === 'testnet4') return 18333; + if (n === 'regtest' || n === 'playnet') return 18444; + if (n === 'testnet') return 18333; + if (n === 'testnet4') return 48333; if (n === 'signet') return 38333; return 8333; } @@ -2271,7 +2822,7 @@

      Source: services/bitcoin.js

      switch (this.settings.network) { default: case 'mainnet': - datadir = (this.settings.constraints.storage.size) ? './stores/bitcoin-pruned' : './stores/bitcoin'; + datadir = (this.settings.constraints.storage.size) ? './stores/bitcoin-mainnet-pruned' : './stores/bitcoin-mainnet'; break; case 'testnet': datadir = './stores/bitcoin-testnet'; @@ -2293,6 +2844,9 @@

      Source: services/bitcoin.js

      break; case 'playnet': datadir = './stores/bitcoin-playnet'; + params.push('-regtest'); + params.push('-fallbackfee=1.0'); + params.push('-maxtxfee=1.1'); break; } @@ -2309,7 +2863,8 @@

      Source: services/bitcoin.js

      this.settings.datadir = datadir; // for downstream users accessing the settings property, e.g. for lightning nodes // If storage constraints are set, prune the blockchain - if (this.settings.network !== 'regtest' && this.settings.constraints.storage.size) { + const chain = this._normalizeChainName(this.settings.network || 'mainnet'); + if (chain !== 'regtest' && chain !== 'playnet' && this.settings.constraints.storage.size) { params.push(`-prune=${this.settings.constraints.storage.size}`); } else { params.push(`-txindex`); @@ -2426,7 +2981,7 @@

      Source: services/bitcoin.js

      // await cleanup(); } }; - this._errorHandlers.unhandledRejection = async (reason, promise) => { + this._errorHandlers.unhandledRejection = async (reason, _promise) => { // Only handle rejections from this service's operations if (reason.source === 'bitcoin' || (this._nodeProcess && reason.pid === this._nodeProcess.pid)) { this.emit('error', '[FABRIC:BITCOIN] Unhandled rejection from Bitcoin service'); @@ -2443,32 +2998,22 @@

      Source: services/bitcoin.js

      // When using cookie auth, wait for bitcoind to create the cookie file then read credentials if (useCookieAuth) { - const chainSubdir = (() => { - const n = (this.settings.network || '').toLowerCase(); - if (n === 'regtest') return 'regtest'; - if (n === 'testnet') return 'testnet3'; - if (n === 'signet') return 'signet'; - return ''; - })(); - const cookiePath = path.resolve(process.cwd(), datadir, chainSubdir, '.cookie'); + const datadirRoot = Bitcoin.resolveBitcoinDatadirForLocalAccess(datadir); + if (!datadirRoot) { + throw new Error(`[FABRIC:BITCOIN] Invalid or unsafe datadir for cookie auth: ${datadir}`); + } + const cookiePath = Bitcoin.cookiePathForBitcoind(datadirRoot, this.settings.network); const cookieTimeoutMs = 15000; const cookiePollMs = 100; const cookieDeadline = Date.now() + cookieTimeoutMs; while (Date.now() < cookieDeadline) { - try { - if (fs.existsSync(cookiePath)) { - const raw = fs.readFileSync(cookiePath, 'utf8').trim(); - const colon = raw.indexOf(':'); - if (colon !== -1) { - this.settings.username = raw.slice(0, colon); - this.settings.password = raw.slice(colon + 1); - this.settings.authority = `http://127.0.0.1:${this.settings.rpcport}`; - if (this.settings.debug) this.emit('debug', '[FABRIC:BITCOIN] Read RPC credentials from cookie file'); - break; - } - } - } catch (e) { - // ignore read errors, keep polling + const creds = await Bitcoin.tryReadRpcCookieFileCredentials(cookiePath); + if (creds) { + this.settings.username = creds.username; + this.settings.password = creds.password; + this.settings.authority = `http://127.0.0.1:${this.settings.rpcport}`; + if (this.settings.debug) this.emit('debug', '[FABRIC:BITCOIN] Read RPC credentials from cookie file'); + break; } await new Promise(r => setTimeout(r, cookiePollMs)); } @@ -2499,26 +3044,17 @@

      Source: services/bitcoin.js

      const rpcport = this.settings.rpcport || 18443; this.settings.authority = `http://${host}:${rpcport}`; if (!this.settings.username || !this.settings.password) { - const chainSubdir = (() => { - const n = (this.settings.network || '').toLowerCase(); - if (n === 'regtest') return 'regtest'; - if (n === 'testnet') return 'testnet3'; - if (n === 'signet') return 'signet'; - return ''; - })(); - const cookiePath = path.resolve(process.cwd(), datadir, chainSubdir, '.cookie'); - try { - if (fs.existsSync(cookiePath)) { - const raw = fs.readFileSync(cookiePath, 'utf8').trim(); - const colon = raw.indexOf(':'); - if (colon !== -1) { - this.settings.username = raw.slice(0, colon); - this.settings.password = raw.slice(colon + 1); - if (this.settings.debug) this.emit('debug', '[FABRIC:BITCOIN] Read RPC credentials from cookie file (unmanaged)'); - } + const datadirRootUnmanaged = Bitcoin.resolveBitcoinDatadirForLocalAccess(datadir); + const cookiePathUnmanaged = datadirRootUnmanaged + ? Bitcoin.cookiePathForBitcoind(datadirRootUnmanaged, this.settings.network) + : null; + if (cookiePathUnmanaged) { + const credsUnmanaged = await Bitcoin.tryReadRpcCookieFileCredentials(cookiePathUnmanaged); + if (credsUnmanaged) { + this.settings.username = credsUnmanaged.username; + this.settings.password = credsUnmanaged.password; + if (this.settings.debug) this.emit('debug', '[FABRIC:BITCOIN] Read RPC credentials from cookie file (unmanaged)'); } - } catch (e) { - // Cookie not available } if (!this.settings.username || !this.settings.password) { this.settings.username = `fabric_${crypto.randomBytes(8).toString('hex')}`; @@ -2537,7 +3073,12 @@

      Source: services/bitcoin.js

      this.emit('debug', `[SERVICES:BITCOIN] Starting for network "${this.settings.network}"...`); this.status = 'STARTING'; - const existingBitcoindFound = await this._detectExistingBitcoind(); + const shouldProbeExternalNode = !( + this.settings.enforceIsolatedRegtest && + this.settings.managed && + this.settings.network === 'regtest' + ); + const existingBitcoindFound = shouldProbeExternalNode ? await this._detectExistingBitcoind() : false; if (existingBitcoindFound && this.settings.managed) { this.emit('log', '[FABRIC:BITCOIN] Existing bitcoind detected; not starting another managed instance'); this.settings.managed = false; @@ -2556,7 +3097,7 @@

      Source: services/bitcoin.js

      this.peer.on('error', this._handlePeerError.bind(this)); this.peer.on('packet', this._handlePeerPacket.bind(this)); this.peer.on('open', () => { - let block = this.peer.getBlock([this.network.genesis.hash]); + this.peer.getBlock([this.network.genesis.hash]); }); } @@ -2768,14 +3309,18 @@

      Classes

      Global


      diff --git a/docs/services_lightning.js.html b/docs/services_lightning.js.html index cbffa234b..ebf30917a 100644 --- a/docs/services_lightning.js.html +++ b/docs/services_lightning.js.html @@ -44,7 +44,6 @@

      Source: services/lightning.js

      // Fabric Types const Actor = require('../types/actor'); const Key = require('../types/key'); -const Remote = require('../types/remote'); const Service = require('../types/service'); const Machine = require('../types/machine'); @@ -58,8 +57,50 @@

      Source: services/lightning.js

      ); } +function shouldTreatLightningStderrAsError (line) { + const text = String(line || '').trim().toLowerCase(); + if (!text) return false; + return ( + text.includes('error') || + text.includes('fatal') || + text.includes('exception') || + text.includes('traceback') + ); +} + +function appendLightningStderrChunk (service, chunk) { + if (!service._lightningdStderrBuf) service._lightningdStderrBuf = ''; + service._lightningdStderrBuf += chunk.toString('utf8'); + const parts = service._lightningdStderrBuf.split(/\r?\n/); + service._lightningdStderrBuf = parts.pop() || ''; + for (const raw of parts) { + const line = raw.trim(); + if (!line) continue; + if (shouldTreatLightningStderrAsError(line)) { + service.emit('error', `[FABRIC:LIGHTNING] ${line}`); + continue; + } + if (service.settings.debug) service.emit('debug', `[FABRIC:LIGHTNING] ${line}`); + } +} + +function flushLightningStderrBuffer (service) { + if (!service._lightningdStderrBuf) return; + const line = service._lightningdStderrBuf.trim(); + service._lightningdStderrBuf = ''; + if (!line) return; + if (shouldTreatLightningStderrAsError(line)) { + service.emit('error', `[FABRIC:LIGHTNING] ${line}`); + return; + } + if (service.settings.debug) service.emit('debug', `[FABRIC:LIGHTNING] ${line}`); +} + /** - * Manage a Lightning node. + * Manage a Lightning node (Core Lightning JSON-RPC over Unix socket). + * + * BOLT checklist: `docs/BOLT_COMPATIBILITY.md`. RPC map: `docs/LIGHTNING_COMPAT.md`. + * @see Lightning.DOCS */ class Lightning extends Service { /** @@ -114,6 +155,7 @@

      Source: services/lightning.js

      this.settings.bitcoin.rpcpassword = this.settings.bitcoin.rpcpassword || this.settings.bitcoin.password; this.machine = new Machine(this.settings); + /** Optional RPC client handle (e.g. REST/grpc); not used by Core Lightning socket `_makeRPCRequest`. */ this.rpc = null; this.rest = null; this.status = 'disconnected'; @@ -163,6 +205,22 @@

      Source: services/lightning.js

      return 'regtest'; } + _bitcoinCliNetworkFlag () { + const network = String(this.settings.network || 'regtest').toLowerCase(); + switch (network) { + case 'main': + case 'bitcoin': + case 'mainnet': return null; + case 'test': + case 'testnet': return '-testnet'; + case 'testnet4': return '-testnet4'; + case 'signet': return '-signet'; + case 'regtest': + default: + return '-regtest'; + } + } + static plugin (state) { const lightning = new Lightning(state); const plugin = new LightningPlugin(state); @@ -251,6 +309,189 @@

      Source: services/lightning.js

      }; } + /** + * Invoke any Core Lightning JSON-RPC method over the lightningd socket (escape hatch for methods without a typed wrapper). + * Named `callRpc` so it does not shadow the `rpc` instance property. + * @param {String} method RPC method name (e.g. `listpeers`). + * @param {Array} [params=[]] Positional/object params as accepted by lightningd for that method. + * @param {Number} [timeoutMs=30000] Optional timeout. + * @returns {Promise<*>} RPC result. + */ + async callRpc (method, params = [], timeoutMs = 30000) { + return this._makeRPCRequest(method, params, timeoutMs); + } + + /** + * Decode a BOLT11 invoice or BOLT12 offer string (Core Lightning `decode`). + * @param {String} boltString `lnbc...`, `lno1...`, etc. + * @returns {Promise<Object>} Decoded fields. + */ + async decodeLightning (boltString) { + return this._makeRPCRequest('decode', [boltString]); + } + + /** + * Decode a BOLT11 invoice for payment fields (Core Lightning `decodepay`). + * @param {String} bolt11 + * @returns {Promise<Object>} Decoded pay details. + */ + async decodePay (bolt11) { + return this._makeRPCRequest('decodepay', [bolt11]); + } + + /** + * Create a BOLT12 offer (requires `experimental-offers` / modern CLN). Pass-through to `offer`. + * @param {Object} params Keyword args, e.g. `{ amount_msat, description, label, issuer, ... }`. + * @returns {Promise<Object>} Offer result (includes `bolt12` / offer id per CLN version). + */ + async createOffer (params) { + if (!params || typeof params !== 'object' || Array.isArray(params)) { + throw new Error('createOffer requires a params object (e.g. { amount_msat, description })'); + } + return this._makeRPCRequest('offer', [params]); + } + + /** + * Request a BOLT11 invoice from a BOLT12 offer (Core Lightning `fetchinvoice`). + * + * Call either: + * - `fetchInvoice('lno1…')` or `fetchInvoice('lno1…', { amount_msat, quantity, payer_note, timeout, … })` + * - **Recurrence** (when the offer is recurring): `recurrence_counter` (start at 0), `recurrence_start`, `recurrence_label` (stable label linking the series; required when counter is set). See Core Lightning **`fetchinvoice`** and [BOLT #12](https://github.com/lightning/bolts/blob/master/12-offer-encoding.md). + * - `fetchInvoice({ offer: 'lno1…', amount_msat, … })` — single keyword object as accepted by CLN. + * + * @param {String|Object} offerOrParams Bolt12 offer string (`lno1…`), or one params object with an `offer` field. + * @param {Object} [invoiceParams] When the first arg is a string, optional extra fields merged into the RPC (second positional group). + * @returns {Promise<Object>} Invoice response (includes `invoice` bolt11 when successful). + */ + async fetchInvoice (offerOrParams, invoiceParams = null) { + if (offerOrParams != null && typeof offerOrParams === 'object' && !Array.isArray(offerOrParams) && typeof offerOrParams.offer === 'string') { + if (invoiceParams != null && typeof invoiceParams === 'object' && !Array.isArray(invoiceParams)) { + return this._makeRPCRequest('fetchinvoice', [Object.assign({}, offerOrParams, invoiceParams)]); + } + return this._makeRPCRequest('fetchinvoice', [offerOrParams]); + } + if (typeof offerOrParams !== 'string') { + throw new Error('fetchInvoice requires offer string or params object with `offer`'); + } + if (invoiceParams != null && typeof invoiceParams === 'object' && !Array.isArray(invoiceParams)) { + return this._makeRPCRequest('fetchinvoice', [offerOrParams, invoiceParams]); + } + return this._makeRPCRequest('fetchinvoice', [offerOrParams]); + } + + /** + * Pay a BOLT11 invoice or BOLT12-fetched bolt11 string (Core Lightning `pay`). + * @param {String|Object} invoiceOrParams Bolt11 string, or keyword object (e.g. `{ bolt11 }`, `{ bolt12 }` per CLN). + * @param {Number} [timeoutMs=30000] + * @returns {Promise<Object>} Payment result. + */ + async pay (invoiceOrParams, timeoutMs = 30000) { + return this._makeRPCRequest('pay', [invoiceOrParams], timeoutMs); + } + + /** + * List offers created on this node (Core Lightning `listoffers`). + * @param {String|Object|null} [filter] `offer_id` string, or `{ offer_id?, active_only? }`, or null for all. + * @returns {Promise<Object>} + */ + async listOffers (filter = null) { + if (filter == null) return this._makeRPCRequest('listoffers', []); + if (typeof filter === 'string') return this._makeRPCRequest('listoffers', [filter]); + if (typeof filter === 'object' && !Array.isArray(filter)) { + return this._makeRPCRequest('listoffers', [filter]); + } + throw new Error('listOffers expects offer_id string, filter object, or null'); + } + + /** + * Disable a local offer by id (Core Lightning `disableoffer`). + * @param {String} offerId + * @returns {Promise<Object>} + */ + async disableOffer (offerId) { + return this._makeRPCRequest('disableoffer', [offerId]); + } + + /** + * Create a BOLT12 `invoice_request` (you request that someone else pay you via their offer flow). Returns `bolt12` (`lnr1…`). (Core Lightning `invoicerequest`, v22.11+.) + * @param {Object} params `{ amount, description, issuer?, label?, absolute_expiry?, single_use? }` — see CLN docs for amount formats. + * @returns {Promise<Object>} + */ + async createInvoiceRequest (params) { + if (!params || typeof params !== 'object' || Array.isArray(params)) { + throw new Error('createInvoiceRequest requires a params object (e.g. { amount, description })'); + } + return this._makeRPCRequest('invoicerequest', [params]); + } + + /** + * List `invoice_request` records (Core Lightning `listinvoicerequests`). + * @param {String|Object|null} [filter] `invreq_id` string, or `{ invreq_id?, active_only? }`, or null for all. + * @returns {Promise<Object>} + */ + async listInvoiceRequests (filter = null) { + if (filter == null) return this._makeRPCRequest('listinvoicerequests', []); + if (typeof filter === 'string') return this._makeRPCRequest('listinvoicerequests', [filter]); + if (typeof filter === 'object' && !Array.isArray(filter)) { + return this._makeRPCRequest('listinvoicerequests', [filter]); + } + throw new Error('listInvoiceRequests expects invreq_id string, filter object, or null'); + } + + /** + * Disable an `invoice_request` so no further invoices are accepted (Core Lightning `disableinvoicerequest`). + * @param {String} invreqId + * @returns {Promise<Object>} + */ + async disableInvoiceRequest (invreqId) { + return this._makeRPCRequest('disableinvoicerequest', [invreqId]); + } + + /** + * Create and send a BOLT12 invoice to the issuer of an `invoice_request` (Core Lightning `sendinvoice`). + * @param {Object} params `{ invreq, label, amount_msat?, timeout?, quantity? }` — `invreq` is the `lnr1…` string. + * @returns {Promise<Object>} + */ + async sendInvoice (params) { + if (!params || typeof params !== 'object' || Array.isArray(params)) { + throw new Error('sendInvoice requires a params object (e.g. { invreq, label })'); + } + return this._makeRPCRequest('sendinvoice', [params]); + } + + /** + * Route probe (Core Lightning `getroute`). + * CLN order: `id`, `amount_msat`, `riskfactor`, `cltv`, `fromid`, `fuzzpercent`, `exclude`, `maxhops` + * — the fourth **positional** is `cltv`, not `maxhops`. To set `maxhops` (or other tail fields) use the + * `routeOptions` object so intermediate slots are sent as `null` where needed. + * @param {String} destinationId Destination node id (pubkey). + * @param {Number|String} amountMsat + * @param {Number} [riskfactor=10] + * @param {Number|Object|null} [cltvOrRouteOptions] Omitted: three-arg RPC. If a number: fourth positional (`cltv`). + * If an object: tail fields `cltv`, `fromid`, `fuzzpercent`, `exclude`, `maxhops` (each optional). + * @returns {Promise<Object>} + */ + async getRoute (destinationId, amountMsat, riskfactor = 10, cltvOrRouteOptions = null) { + if (cltvOrRouteOptions != null && typeof cltvOrRouteOptions === 'object' && !Array.isArray(cltvOrRouteOptions)) { + const o = cltvOrRouteOptions; + return this._makeRPCRequest('getroute', [ + destinationId, + amountMsat, + riskfactor, + o.cltv != null ? o.cltv : null, + o.fromid != null ? o.fromid : null, + o.fuzzpercent != null ? o.fuzzpercent : null, + o.exclude != null ? o.exclude : null, + o.maxhops != null ? o.maxhops : null + ]); + } + const args = [destinationId, amountMsat, riskfactor]; + if (cltvOrRouteOptions != null) { + args.push(cltvOrRouteOptions); + } + return this._makeRPCRequest('getroute', args); + } + /** * Computes the total liquidity of the Lightning node. * @returns {Object} Liquidity in BTC. @@ -320,7 +561,7 @@

      Source: services/lightning.js

      ]; // Wait for all checks to complete - const results = await Promise.all(checks); + await Promise.all(checks); if (this.settings.debug) { this.emit('debug', '[FABRIC:LIGHTNING] Successfully connected to lightningd'); @@ -414,11 +655,19 @@

      Source: services/lightning.js

      if (this.settings.debug) this.emit('debug', `[FABRIC:LIGHTNING] ${data.toString('utf8').trim()}`); }); + this._lightningdStderrBuf = ''; this._child.stderr.on('data', (data) => { - this.emit('error', `[FABRIC:LIGHTNING] ${data.toString('utf8').trim()}`); + appendLightningStderrChunk(this, data); + }); + this._child.stderr.on('end', () => { + flushLightningStderrBuffer(this); + }); + this._child.stderr.on('close', () => { + flushLightningStderrBuffer(this); }); this._child.on('close', (code) => { + flushLightningStderrBuffer(this); if (this.settings.debug) this.emit('debug', `[FABRIC:LIGHTNING] Lightning node exited with code ${code}`); this.emit('log', `[FABRIC:LIGHTNING] Lightning node exited with code ${code}`); }); @@ -444,7 +693,7 @@

      Source: services/lightning.js

      if (child.exitCode === null) { try { child.kill('SIGKILL'); - } catch (error) { + } catch { // Ignore if process already exited between checks. } @@ -472,7 +721,7 @@

      Source: services/lightning.js

      // this.emit('error', err); } }; - this._errorHandlers.unhandledRejection = async (reason, promise) => { + this._errorHandlers.unhandledRejection = async (reason, _promise) => { // Only handle rejections from this service's operations if (reason.source === 'lightning' || (this._child && reason.pid === this._child.pid)) { this.emit('error', '[FABRIC:LIGHTNING] Unhandled rejection from Lightning service'); @@ -510,24 +759,26 @@

      Source: services/lightning.js

      if (this.settings.managed && this.settings.bitcoin) { if (this.settings.debug) this.emit('debug', '[FABRIC:LIGHTNING] Waiting for Lightning to be ready...'); try { - const bitcoinCli = children.spawn('bitcoin-cli', [ - '-regtest', - `-datadir=${this.settings.bitcoin.datadir}`, - '-rpcclienttimeout=60', - `-rpcconnect=${this.settings.bitcoin.host}`, - `-rpcport=${this.settings.bitcoin.rpcport}`, - `-rpcuser=${this.settings.bitcoin.rpcuser}`, - '-stdinrpcpass', - 'getblockchaininfo' - ]); + const bitcoinCliArgs = [ + `-datadir=${this.settings.bitcoin.datadir}`, + '-rpcclienttimeout=60', + `-rpcconnect=${this.settings.bitcoin.host}`, + `-rpcport=${this.settings.bitcoin.rpcport}`, + `-rpcuser=${this.settings.bitcoin.rpcuser}`, + '-stdinrpcpass', + 'getblockchaininfo' + ]; + const networkFlag = this._bitcoinCliNetworkFlag(); + if (networkFlag) bitcoinCliArgs.unshift(networkFlag); + const bitcoinCli = children.spawn('bitcoin-cli', bitcoinCliArgs); bitcoinCli.stdin.write(this.settings.bitcoin.rpcpassword + '\n'); bitcoinCli.stdin.end(); await new Promise((resolve, reject) => { - let output = ''; + let _output = ''; bitcoinCli.stdout.on('data', (data) => { - output += data.toString(); + _output += data.toString(); }); bitcoinCli.stderr.on('data', (data) => { const line = data.toString(); @@ -544,8 +795,10 @@

      Source: services/lightning.js

      }); if (this.settings.debug) this.emit('debug', '[FABRIC:LIGHTNING] Lightning is ready'); - } catch (error) { - throw new Error(`Could not connect to bitcoind using bitcoin-cli. Is lightningd running?\n\nMake sure you have bitcoind running and that bitcoin-cli is able to connect to bitcoind.\n\nYou can verify that your Bitcoin Core installation is ready for use by running:\n\n $ bitcoin-cli -regtest -datadir=${this.settings.bitcoin.datadir} -rpcclienttimeout=60 -rpcconnect=${this.settings.bitcoin.host} -rpcport=${this.settings.bitcoin.rpcport} -rpcuser=${this.settings.bitcoin.rpcuser} -stdinrpcpass echo 'hello world'`); + } catch { + const networkFlag = this._bitcoinCliNetworkFlag(); + const networkHint = networkFlag ? `${networkFlag} ` : ''; + throw new Error(`Could not connect to bitcoind using bitcoin-cli. Is lightningd running?\n\nMake sure you have bitcoind running and that bitcoin-cli is able to connect to bitcoind.\n\nYou can verify that your Bitcoin Core installation is ready for use by running:\n\n $ bitcoin-cli ${networkHint}-datadir=${this.settings.bitcoin.datadir} -rpcclienttimeout=60 -rpcconnect=${this.settings.bitcoin.host} -rpcport=${this.settings.bitcoin.rpcport} -rpcuser=${this.settings.bitcoin.rpcuser} -stdinrpcpass echo 'hello world'`); } } @@ -657,7 +910,7 @@

      Source: services/lightning.js

      if (settled) return; settled = true; if (timeoutId) clearTimeout(timeoutId); - try { client.destroy(); } catch (_) {} + try { client.destroy(); } catch {} fn(arg); }; @@ -677,7 +930,7 @@

      Source: services/lightning.js

      const response = JSON.parse(buffer); if (response.result !== undefined) return finish(resolve, response.result); if (response.error) return finish(reject, Object.assign(new Error(response.error.message || 'RPC error'), response.error)); - } catch (_) { + } catch { if (buffer.length > 2 * 1024 * 1024) finish(reject, new Error('Lightning RPC response too large')); } }); @@ -849,17 +1102,48 @@

      Source: services/lightning.js

      */ Lightning.CLN_RPC_METHODS = Object.freeze([ 'connect', + 'decode', + 'decodepay', + 'disableinvoicerequest', + 'disableoffer', + 'fetchinvoice', 'fundchannel', 'getinfo', + 'getroute', 'invoice', + 'invoicerequest', 'listchannels', 'listfunds', + 'listinvoicerequests', + 'listoffers', 'newaddr', + 'offer', + 'pay', + 'sendinvoice', 'stop' ]); +/** + * Paths to canonical Markdown docs (relative to the `@fabric/core` package root). + * `fabricLightningMarkets` and `fabricLightningOffers` point at the same file (Fabric **markets** vs Lightning BOLT12 **offers**). + * @type {Readonly<{ boltCompatibility: string, fabricLightningOffers: string, fabricLightningMarkets: string, fabricPaymentBech32: string, lightningCompat: string }>} + */ +Lightning.DOCS = Object.freeze({ + boltCompatibility: 'docs/BOLT_COMPATIBILITY.md', + fabricLightningOffers: 'docs/FABRIC_LIGHTNING_OFFERS.md', + fabricLightningMarkets: 'docs/FABRIC_LIGHTNING_OFFERS.md', + fabricPaymentBech32: 'docs/FABRIC_PAYMENT_BECH32.md', + lightningCompat: 'docs/LIGHTNING_COMPAT.md' +}); + Lightning.redactSensitiveCommandArg = redactSensitiveCommandArg; +Object.assign(Lightning, { + Bolt12: require('../functions/lightningBolt12'), + FabricPayment: require('../functions/fabricPaymentBech32'), + Bolt12Semantics: require('../functions/bolt12Semantics') +}); + module.exports = Lightning;

  • @@ -874,14 +1158,18 @@

    Classes

    Global


    diff --git a/docs/services_redis.js.html b/docs/services_redis.js.html index 7bde69674..425f4c361 100644 --- a/docs/services_redis.js.html +++ b/docs/services_redis.js.html @@ -38,7 +38,7 @@

    Source: services/redis.js

    const Message = require('../types/message'); /** - * Connect and subscribe to Redis servers. + * Connect and subscribe to Redis servers (node-redis v6). */ class Redis extends Service { /** @@ -46,13 +46,12 @@

    Source: services/redis.js

    * @param {Object} [settings] Settings for the Redis connection. * @param {String} [settings.host] Host for the Redis server. * @param {Number} [settings.port] Remote Redis service port. + * @param {String} [settings.url] Optional redis URL (overrides host/port). * @returns {Redis} Instance of the Redis service, ready to run `start()` */ constructor (settings = {}) { super(settings); - // Assign settings over the defaults - // NOTE: switch to lodash.merge if clobbering defaults this.settings = Object.assign({ host: 'localhost', port: 6379, @@ -65,30 +64,48 @@

    Source: services/redis.js

    return this; } + _clientOptions () { + if (this.settings.url) { + return { url: this.settings.url }; + } + return { + socket: { + host: this.settings.host, + port: this.settings.port + } + }; + } + + _emitChannelMessage (channel, message) { + const topic = channel != null ? String(channel) : ''; + const raw = Buffer.isBuffer(message) + ? message + : Buffer.from(message == null ? '' : String(message)); + const path = `channels/${topic}`; + this.emit('debug', `Redis message @ [${path}] (${raw.length} bytes) ⇒ ${raw.toString('hex')}`); + this.emit('message', Message.fromVector(['Generic', { + topic, + message: raw.toString('hex'), + encoding: 'hex' + }]).toObject()); + } + /** * Opens the connection and subscribes to the requested channels. - * @returns {Redis} Instance of the service. + * @returns {Promise<Redis>} */ async start () { const self = this; - this.socket = redis.createClient(this.settings); + this.socket = redis.createClient(this._clientOptions()); this.socket.on('error', function _handleSocketError (error) { self.emit('error', `Redis socket error: ${error}`); }); - this.socket.on('message', function _handleSocketMessage (topic, message) { - const path = `channels/${topic.toString()}`; - self.emit('debug', `Redis message @ [${path}] (${message.length} bytes) ⇒ ${message.toString('hex')}`); - self.emit('message', Message.fromVector(['Generic', { - topic: topic.toString(), - message: message.toString('hex'), - encoding: 'hex' - }]).toObject()); - }); + await this.socket.connect(); for (let i = 0; i < this.settings.subscriptions.length; i++) { - this.subscribe(this.settings.subscriptions[i]); + await this.subscribe(this.settings.subscriptions[i]); } this.status = 'STARTED'; @@ -101,17 +118,34 @@

    Source: services/redis.js

    /** * Closes the connection to the Redis server. - * @returns {Redis} Instance of the service. + * @returns {Promise<Redis>} */ async stop () { this.status = 'STOPPING'; - this.socket.close(); + if (this.socket) { + try { + if (typeof this.socket.isOpen === 'boolean' && this.socket.isOpen) { + await this.socket.quit(); + } + } catch (_) { + try { this.socket.disconnect(); } catch (__) {} + } + this.socket = null; + } this.status = 'STOPPED'; return this; } - subscribe (name) { - this.socket.subscribe(name); + /** + * @param {string} name Channel name + * @returns {Promise<void>} + */ + async subscribe (name) { + if (!this.socket) throw new Error('Redis client is not started'); + const channel = String(name); + await this.socket.subscribe(channel, (message, subscribedChannel) => { + this._emitChannelMessage(subscribedChannel || channel, message); + }); } } @@ -129,14 +163,18 @@

    Classes

    Global


    diff --git a/docs/services_text.js.html b/docs/services_text.js.html new file mode 100644 index 000000000..e4edae51b --- /dev/null +++ b/docs/services_text.js.html @@ -0,0 +1,199 @@ + + + + + + Source: services/text.js · Docs + + + + + + + + + +
    +

    Source: services/text.js

    + + + + +
    +
    +
    'use strict';
    +
    +const Service = require('../types/service');
    +const truncateMiddle = require('../functions/truncateMiddle');
    +const oxfordJoin = require('../functions/oxfordJoin');
    +
    +/**
    + * Text-oriented {@link Service} stub (legacy name was <code>TXT</code>).
    + * Static helpers mirror small utilities used in Sensemaker (tokenize, middle truncation,
    + * relative time strings) and core helpers ({@link module:functions/oxfordJoin}).
    + * @class Text
    + * @extends Service
    + */
    +class Text extends Service {
    +  constructor (config) {
    +    super(config);
    +    this.config = Object.assign({}, config);
    +  }
    +
    +  /**
    +   * Split on runs of whitespace (Sensemaker-style tokenization).
    +   * @param {string} string
    +   * @returns {string[]}
    +   */
    +  static tokenize (string) {
    +    return String(string).split(/\s/g);
    +  }
    +
    +  /**
    +   * Shorten a string in the middle if longer than <code>strLen</code>.
    +   * @param {string} fullStr
    +   * @param {number} strLen
    +   * @param {string} [separator]
    +   * @returns {string}
    +   */
    +  static truncateMiddle (fullStr, strLen, separator) {
    +    return truncateMiddle(fullStr, strLen, separator);
    +  }
    +
    +  /**
    +   * Human-readable relative time (e.g. <code>3 days ago</code>), ported from Sensemaker.
    +   * @param {Date|string|number} date
    +   * @returns {string}
    +   */
    +  static toRelativeTime (date) {
    +    const now = new Date();
    +    const then = new Date(date);
    +    const diff = now - then;
    +    const seconds = Math.floor(diff / 1000);
    +    const minutes = Math.floor(seconds / 60);
    +    const hours = Math.floor(minutes / 60);
    +    const days = Math.floor(hours / 24);
    +    const weeks = Math.floor(days / 7);
    +    const months = Math.floor(weeks / 4);
    +    const years = Math.floor(months / 12);
    +
    +    if (years > 0) return `${years} year${years === 1 ? '' : 's'} ago`;
    +    if (months > 0) return `${months} month${months === 1 ? '' : 's'} ago`;
    +    if (weeks > 0) return `${weeks} week${weeks === 1 ? '' : 's'} ago`;
    +    if (days > 0) return `${days} day${days === 1 ? '' : 's'} ago`;
    +    if (hours > 0) return `${hours} hour${hours === 1 ? '' : 's'} ago`;
    +    if (minutes > 0) return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
    +    if (seconds > 0) return `${seconds} second${seconds === 1 ? '' : 's'} ago`;
    +    return 'just now';
    +  }
    +
    +  /**
    +   * Join a list with an Oxford comma (delegates to {@link module:functions/oxfordJoin}).
    +   * @param {string[]} list
    +   * @returns {string}
    +   */
    +  static oxfordJoin (list) {
    +    return oxfordJoin(list);
    +  }
    +}
    +
    +module.exports = Text;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/services_txt.js.html b/docs/services_txt.js.html new file mode 100644 index 000000000..33c1f234b --- /dev/null +++ b/docs/services_txt.js.html @@ -0,0 +1,130 @@ + + + + + + Source: services/txt.js · Docs + + + + + + + + + +
    +

    Source: services/txt.js

    + + + + +
    +
    +
    'use strict';
    +
    +/**
    + * @deprecated Require {@link module:services/text~Text} from <code>services/text.js</code> instead.
    + */
    +const Text = require('./text');
    +module.exports = Text;
    +module.exports.TXT = Text;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/services_zmq.js.html b/docs/services_zmq.js.html index 26ae3783a..c12cff392 100644 --- a/docs/services_zmq.js.html +++ b/docs/services_zmq.js.html @@ -76,43 +76,49 @@

    Source: services/zmq.js

    return this; } + /** Avoid process crash when nothing listens for `error` (Node EventEmitter default). */ + _emitErrorSafe (err) { + if (this.listenerCount('error') > 0) this.emit('error', err); + else this.emit('warning', `[ZMQ] ${err && err.message ? err.message : err}`); + } + async connect () { this._state.status = 'CONNECTING'; this.socket = zeromq.socket('sub'); // Add connection event handlers this.socket.on('connect', () => { - console.log(`[ZMQ] Connected to ${this.settings.host}:${this.settings.port}`); + this.emit('debug', `[ZMQ] Connected to ${this.settings.host}:${this.settings.port}`); this._state.status = 'CONNECTED'; this._state.reconnectAttempts = 0; // Reset reconnection attempts on successful connect }); this.socket.on('disconnect', () => { - console.log(`[ZMQ] Disconnected from ${this.settings.host}:${this.settings.port}`); + this.emit('debug', `[ZMQ] Disconnected from ${this.settings.host}:${this.settings.port}`); this._state.status = 'DISCONNECTED'; }); this.socket.on('error', (error) => { - console.error('[ZMQ] Error:', error); + this._emitErrorSafe(error); }); this.socket.on('close', async (msg) => { - console.error('[ZMQ] Socket closed:', msg); + this.emit('debug', `[ZMQ] Socket closed: ${msg}`); // Only attempt reconnection if we haven't stopped the service intentionally if (this._state.status !== 'STOPPED' && this._state.status !== 'STOPPING') { if (this._state.reconnectAttempts < this.settings.maxReconnectAttempts) { this._state.reconnectAttempts++; - console.log(`[ZMQ] Attempting to reconnect (${this._state.reconnectAttempts}/${this.settings.maxReconnectAttempts})...`); + this.emit('debug', `[ZMQ] Attempting to reconnect (${this._state.reconnectAttempts}/${this.settings.maxReconnectAttempts})...`); setTimeout(async () => { try { await this.start(); } catch (err) { - console.error('[ZMQ] Reconnection failed:', err); + this._emitErrorSafe(err); } }, this.settings.reconnectInterval); } else { - console.error('[ZMQ] Max reconnection attempts reached. Giving up.'); - this.emit('error', new Error('Max reconnection attempts reached')); + this.emit('warning', '[ZMQ] Max reconnection attempts reached. Giving up.'); + this._emitErrorSafe(new Error('Max reconnection attempts reached')); } } }); @@ -190,14 +196,18 @@

    Classes

    Global


    diff --git a/docs/settings_deprecations.js.html b/docs/settings_deprecations.js.html index a2bd44b00..1bf153141 100644 --- a/docs/settings_deprecations.js.html +++ b/docs/settings_deprecations.js.html @@ -33,13 +33,13 @@

    Source: settings/deprecations.js

    'use strict';
     
    -const FabricScribe = require('../types/scribe');
    +const FabricState = require('../types/state');
     
     /**
    - * Deprecated 2021-11-06.
    + * Deprecated 2021-11-06 — use {@link FabricState} (<code>types/state</code>). <code>Scribe</code> was merged into <code>State</code>.
      * @deprecated
      */
    -class Scribe extends FabricScribe {}
    +class Scribe extends FabricState {}
     
     module.exports = {
       Scribe
    @@ -57,14 +57,18 @@ 

    Classes

    Global


    diff --git a/docs/types_actor.js.html b/docs/types_actor.js.html index 17b0b2a9c..6e8cb0ac9 100644 --- a/docs/types_actor.js.html +++ b/docs/types_actor.js.html @@ -33,6 +33,28 @@

    Source: types/actor.js

    'use strict';
     
    +/**
    + * @fileoverview Base <strong>Actor</strong> type for Fabric: JSON-shaped state, JSON Patch commits, and a
    + * content-derived <strong>id</strong>.
    + *
    + * <p><strong>State</strong> — <code>_state.content</code> is observed with <code>fast-json-patch</code>.
    + * {@link Actor#commit} turns observer diffs into {@link Actor#history} entries and emits <code>commit</code> plus
    + * <code>message</code> with <code>type: 'ActorMessage'</code> / <code>data.type: 'Changes'</code>.</p>
    + *
    + * <p><strong>Identity</strong> — {@link Actor#id} is a SHA256 digest (hex) of the 32-byte preimage buffer;
    + * {@link Actor#preimage} is SHA256(UTF-8) of the pretty-printed {@link Actor#toGenericMessage}
    + * <code>{ type, object }</code> with sorted keys ({@link Actor#toObject}). Implementation uses {@link Hash256.compute}.
    + * Treat <code>id</code> as a <strong>content address</strong> for that state shape, not an arbitrary application string hash.</p>
    + *
    + * <p><strong>Relationship to {@link Message}</strong> — <code>Message</code> extends <code>Actor</code> and implements
    + * <strong>AMP</strong> (wire headers, opcodes, Schnorr <code>Fabric/Message</code>). Downstream apps that only need a stable
    + * storage key should not label that key <code>Actor#id</code> unless it is produced by this type.</p>
    + *
    + * <p>Narrative docs: <strong>DEVELOPERS.md</strong> (section <em>Actor and Message</em>) and this file’s class JSDoc are
    + * kept in sync; <code>npm run make:docs</code> embeds DEVELOPERS.md as the HTML home page, while Actor.html is generated
    + * from here.</p>
    + */
    +
     // Generics
     const EventEmitter = require('events');
     // const stream = require('node:stream/promises');
    @@ -46,13 +68,25 @@ 

    Source: types/actor.js

    // Fabric Functions const _sortKeys = require('../functions/_sortKeys'); +const { tryParsePersistedJson } = require('../functions/wireJson'); /** - * Generic Fabric Actor. + * @classdesc Base <strong>Actor</strong>: JSON-shaped <code>_state.content</code> observed with + * <code>fast-json-patch</code>; {@link Actor#commit} turns diffs into {@link Actor#history} and emits + * <code>commit</code> plus <code>message</code> (<code>type: 'ActorMessage'</code>, <code>data.type: 'Changes'</code>). + * <strong>Identity</strong> — {@link Actor#id} is SHA256(hex) of the 32-byte preimage buffer; {@link Actor#preimage} is + * SHA256(UTF-8) of pretty-printed {@link Actor#toGenericMessage} <code>{ type, object }</code> with sorted keys + * ({@link Actor#toObject}); uses {@link Hash256.compute}. Treat <code>id</code> as a <strong>content address</strong>, not an + * arbitrary app string hash. <strong>Wire traffic</strong> — see {@link Message} (extends Actor, AMP). Same narrative as + * <strong>DEVELOPERS.md</strong> (<em>Actor and Message</em>) and <code>@fileoverview</code> above (also on + * <code>types_actor.js.html</code> source page). + * @class Actor + * @extends EventEmitter * @access protected - * @emits message Fabric {@link Message} objects. - * @property {String} id Unique identifier for this Actor (id === SHA256(preimage)). - * @property {String} preimage Input hash for the `id` property (preimage === SHA256(ActorState)). + * @fires Actor#commit + * @emits message Emits structured objects; on {@link Actor#commit}, <code>type: 'ActorMessage'</code> with patch metadata (not necessarily a {@link Message} AMP instance). + * @property {String} id 64-char hex: SHA256 of the 32-byte digest represented by {@link Actor#preimage}. + * @property {String} preimage 64-char hex: SHA256 of UTF-8 pretty JSON of {@link Actor#toGenericMessage}. */ class Actor extends EventEmitter { /** @@ -61,7 +95,7 @@

    Source: types/actor.js

    * for the actor, including key material [!!!] — be mindful of * what you share with others! * @param {Object} [actor] Object to use as the actor. - * @param {String} [actor.seed] BIP24 Mnemonic to use as a seed phrase. + * @param {String} [actor.seed] Optional mnemonic or seed string stored into state (see BIP39 / wallet docs — not validated here). * @param {Buffer} [actor.public] Public key. * @param {Buffer} [actor.private] Private key. * @returns {Actor} Instance of the Actor. Call {@link Actor#sign} to emit a {@link Signature}. @@ -136,12 +170,8 @@

    Source: types/actor.js

    let result = null; if (typeof input === 'string' && input.length) { - try { - result = JSON.parse(input); - } catch (E) { - // Fail closed: callers expect null on invalid JSON. - result = null; - } + const pr = tryParsePersistedJson(input); + result = pr.ok ? pr.value : null; } return result; @@ -489,7 +519,7 @@

    Source: types/actor.js

    return this.state; } - _handleMonitorChanges (changes) { + _handleMonitorChanges (_changes) { // TODO: emit global state event here // after verify, commit } @@ -534,14 +564,18 @@

    Classes

    Global


    diff --git a/docs/types_beacon.js.html b/docs/types_beacon.js.html new file mode 100644 index 000000000..ced125a03 --- /dev/null +++ b/docs/types_beacon.js.html @@ -0,0 +1,716 @@ + + + + + + Source: types/beacon.js · Docs + + + + + + + + + +
    +

    Source: types/beacon.js

    + + + + +
    +
    +
    'use strict';
    +
    +/**
    + * Beacon — L1-tied epoch chain that seals sidechain / contracts digests.
    + *
    + * Regtest: `createEpoch()` mines one block (`generatetoaddress`) then appends
    + * a `BEACON_EPOCH` entry. Non-regtest: `recordEpochFromBlock` follows tips.
    + *
    + * Hub product wiring historically lived in hub.fabric.pub `contracts/beacon.js`;
    + * that module re-exports this type.
    + */
    +
    +const merge = require('lodash.merge');
    +
    +const Actor = require('./actor');
    +const Message = require('./message');
    +const Chain = require('./chain');
    +const beaconFederationSigning = require('../functions/beaconFederationSigning');
    +
    +const SATS_PER_BTC = 100_000_000;
    +const BEACON_CHAIN_PATH = 'beacon/CHAIN';
    +
    +class Beacon extends Actor {
    +  constructor (settings = {}) {
    +    super(settings);
    +
    +    this.settings = merge({
    +      name: 'FABRIC:BEACON',
    +      debug: false,
    +      interval: 60000,
    +      regtest: true,
    +      /** When false, `start()` does not mine an initial epoch (regtest). */
    +      mineOnStart: true,
    +      federationValidators: [],
    +      federationThreshold: 1,
    +      federationWitnessFailClosed: true
    +    }, settings);
    +
    +    this.bitcoin = null;
    +    this.fs = null;
    +    this.key = null;
    +    this.timer = null;
    +    this._blockHandler = null;
    +    this._epochChain = Chain.create({ consensus: 'federation' });
    +    this._federationValidators = Array.isArray(this.settings.federationValidators)
    +      ? this.settings.federationValidators.slice()
    +      : [];
    +    this._federationThreshold = Math.max(1, Number(this.settings.federationThreshold) || 1);
    +    this._getSidechainSnapshotForEpoch = null;
    +    this._getContractsSnapshotForEpoch = null;
    +    this._pendingEpochRounds = new Map();
    +    this._state = {
    +      content: {
    +        clock: 0,
    +        status: 'STOPPED',
    +        lastBlockHash: null,
    +        height: 0,
    +        balance: 0,
    +        balanceSats: 0
    +      }
    +    };
    +
    +    return this;
    +  }
    +
    +  get state () {
    +    return this._state.content;
    +  }
    +
    +  get merkleRoot () {
    +    return this._computeMerkleRoot();
    +  }
    +
    +  getEpochChainSummary () {
    +    const last = this._epochChain.tip;
    +    return {
    +      length: this._epochChain.height,
    +      last: last
    +        ? {
    +          payload: last.payload,
    +          federationWitness: last.federationWitness || null
    +        }
    +        : null
    +    };
    +  }
    +
    +  /**
    +   * @param {Object} [deps]
    +   * @param {Object} [deps.bitcoin]
    +   * @param {Object} [deps.fs]
    +   * @param {Object} [deps.key]
    +   * @param {Array.<string>} [deps.federationValidators]
    +   * @param {number} [deps.federationThreshold]
    +   * @param {function(): (Object|null)} [deps.getSidechainSnapshotForEpoch]
    +   * @param {function(): (Object|null)} [deps.getContractsSnapshotForEpoch]
    +   */
    +  attach (deps = {}) {
    +    if (deps.bitcoin) this.bitcoin = deps.bitcoin;
    +    if (deps.fs) this.fs = deps.fs;
    +    if (deps.key) this.key = deps.key;
    +    if (Array.isArray(deps.federationValidators)) {
    +      this._federationValidators = deps.federationValidators.slice();
    +    }
    +    if (deps.federationThreshold != null) {
    +      this._federationThreshold = Math.max(1, Number(deps.federationThreshold) || 1);
    +    }
    +    if (typeof deps.getSidechainSnapshotForEpoch === 'function') {
    +      this._getSidechainSnapshotForEpoch = deps.getSidechainSnapshotForEpoch;
    +    }
    +    if (typeof deps.getContractsSnapshotForEpoch === 'function') {
    +      this._getContractsSnapshotForEpoch = deps.getContractsSnapshotForEpoch;
    +    }
    +    return this;
    +  }
    +
    +  getFederationPolicy () {
    +    return {
    +      validators: this._federationValidators.slice(),
    +      threshold: this._federationThreshold
    +    };
    +  }
    +
    +  _compressedPubkeyHex () {
    +    if (!this.key || !this.key.public || typeof this.key.public.encodeCompressed !== 'function') {
    +      return null;
    +    }
    +    try {
    +      return this.key.public.encodeCompressed('hex');
    +    } catch (_) {
    +      return null;
    +    }
    +  }
    +
    +  /** @deprecated Hub alias — use {@link Beacon#_compressedPubkeyHex} */
    +  _hubCompressedPubkeyHex () {
    +    return this._compressedPubkeyHex();
    +  }
    +
    +  _mergeSidechainIntoEpoch (epoch) {
    +    const out = { ...epoch };
    +    if (typeof this._getSidechainSnapshotForEpoch === 'function') {
    +      try {
    +        const snap = this._getSidechainSnapshotForEpoch();
    +        if (snap && typeof snap === 'object') {
    +          out.sidechain = {
    +            clock: Number(snap.clock) || 0,
    +            stateDigest: snap.stateDigest != null ? String(snap.stateDigest) : null
    +          };
    +        }
    +      } catch (err) {
    +        this.emit('warning', '[BEACON] sidechain snapshot failed:', err && err.message ? err.message : err);
    +      }
    +    }
    +    if (typeof this._getContractsSnapshotForEpoch === 'function') {
    +      try {
    +        const snap = this._getContractsSnapshotForEpoch();
    +        if (snap && typeof snap === 'object') {
    +          out.contracts = {
    +            clock: Number(snap.clock) || 0,
    +            stateDigest: snap.stateDigest != null ? String(snap.stateDigest) : null,
    +            kind: snap.kind || 'TrackedApplicationContracts',
    +            acceptedCount: Number.isFinite(snap.acceptedCount) ? Number(snap.acceptedCount) : undefined
    +          };
    +        }
    +      } catch (err) {
    +        this.emit('warning', '[BEACON] contracts snapshot failed:', err && err.message ? err.message : err);
    +      }
    +    }
    +    return out;
    +  }
    +
    +  _makeFederationWitnessForEpoch (epochPayload) {
    +    if (!this._federationValidators.length) return null;
    +    if (!this.key || !this.key.private) return null;
    +    const pk = this._compressedPubkeyHex();
    +    if (!pk || !this._federationValidators.includes(pk)) return null;
    +    const msg = Buffer.from(beaconFederationSigning.signingStringForBeaconEpoch(epochPayload), 'utf8');
    +    let sig;
    +    try {
    +      sig = this.key.signSchnorr(msg);
    +    } catch (_) {
    +      return null;
    +    }
    +    return {
    +      version: 1,
    +      signatures: { [pk]: Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig) }
    +    };
    +  }
    +
    +  _buildEpochEntry (epochBase, witnessOverride = null) {
    +    const fullEpoch = this._mergeSidechainIntoEpoch(epochBase);
    +    const message = Message.fromVector(['BEACON_EPOCH', JSON.stringify(fullEpoch)]);
    +    if (this.key && this.key.private) message.signWithKey(this.key);
    +    const entry = { type: 'BEACON_EPOCH', payload: fullEpoch, id: message.id || null };
    +    const witness = witnessOverride || this._makeFederationWitnessForEpoch(fullEpoch);
    +    if (witness) entry.federationWitness = witness;
    +    return entry;
    +  }
    +
    +  async _commitEpochWithFederation (epochBase) {
    +    const fullEpoch = this._mergeSidechainIntoEpoch(epochBase);
    +    const localWitness = this._makeFederationWitnessForEpoch(fullEpoch);
    +
    +    if (!this._federationValidators.length) {
    +      const entry = this._buildEpochEntry(epochBase, null);
    +      this._epochChain.append(entry);
    +      await this._persistEpochChain();
    +      return entry.payload;
    +    }
    +
    +    const round = beaconFederationSigning.createRound(
    +      fullEpoch,
    +      { validators: this._federationValidators, threshold: this._federationThreshold },
    +      localWitness
    +    );
    +
    +    if (beaconFederationSigning.roundMeetsThreshold(round)) {
    +      const entry = this._buildEpochEntry(epochBase, round.witness);
    +      this._epochChain.append(entry);
    +      await this._persistEpochChain();
    +      return entry.payload;
    +    }
    +
    +    this._pendingEpochRounds.set(round.commitmentDigest, round);
    +    try {
    +      const doc = beaconFederationSigning.loadPendingDoc(this.fs);
    +      doc.rounds[round.commitmentDigest] = round;
    +      await beaconFederationSigning.persistPendingDoc(this.fs, doc);
    +    } catch (err) {
    +      this.emit('warning', '[BEACON] Failed to persist pending epoch round:', err && err.message ? err.message : err);
    +    }
    +
    +    const signRequest = beaconFederationSigning.encodeSignRequest(round);
    +    this.emit('federation:sign-request', signRequest);
    +    return {
    +      pending: true,
    +      commitmentDigest: round.commitmentDigest,
    +      payload: fullEpoch,
    +      signRequest
    +    };
    +  }
    +
    +  async submitFederationEpochSignature (commitmentDigest, pubkey, signatureHex) {
    +    const digest = String(commitmentDigest || '').trim();
    +    let round = this._pendingEpochRounds.get(digest);
    +    if (!round) {
    +      const doc = beaconFederationSigning.loadPendingDoc(this.fs);
    +      round = doc.rounds[digest] || null;
    +      if (round) this._pendingEpochRounds.set(digest, round);
    +    }
    +    if (!round) {
    +      return { status: 'error', message: 'unknown pending epoch round' };
    +    }
    +
    +    const added = beaconFederationSigning.addSignature(round, pubkey, signatureHex);
    +    if (!added.ok) {
    +      return { status: 'error', message: added.error || 'signature rejected' };
    +    }
    +
    +    this._pendingEpochRounds.set(digest, round);
    +    try {
    +      const doc = beaconFederationSigning.loadPendingDoc(this.fs);
    +      doc.rounds[digest] = round;
    +      await beaconFederationSigning.persistPendingDoc(this.fs, doc);
    +    } catch (_) { /* ignore */ }
    +
    +    if (!added.sealed) {
    +      return {
    +        status: 'success',
    +        pending: true,
    +        commitmentDigest: digest,
    +        signatureCount: Object.keys(round.witness.signatures || {}).length,
    +        threshold: round.threshold
    +      };
    +    }
    +
    +    const message = Message.fromVector(['BEACON_EPOCH', JSON.stringify(round.payload)]);
    +    if (this.key && this.key.private) message.signWithKey(this.key);
    +    const entry = {
    +      type: 'BEACON_EPOCH',
    +      payload: round.payload,
    +      id: message.id || null,
    +      federationWitness: round.witness
    +    };
    +    this._epochChain.append(entry);
    +    await this._persistEpochChain();
    +
    +    this._pendingEpochRounds.delete(digest);
    +    try {
    +      const doc = beaconFederationSigning.loadPendingDoc(this.fs);
    +      delete doc.rounds[digest];
    +      await beaconFederationSigning.persistPendingDoc(this.fs, doc);
    +    } catch (_) { /* ignore */ }
    +
    +    this.emit('epoch', entry.payload);
    +    return {
    +      status: 'success',
    +      sealed: true,
    +      commitmentDigest: digest,
    +      payload: entry.payload,
    +      federationWitness: entry.federationWitness
    +    };
    +  }
    +
    +  listPendingFederationEpochRounds () {
    +    const out = [];
    +    for (const round of this._pendingEpochRounds.values()) {
    +      out.push({
    +        commitmentDigest: round.commitmentDigest,
    +        clock: round.payload && round.payload.clock,
    +        blockHash: round.payload && round.payload.blockHash,
    +        signatureCount: Object.keys((round.witness && round.witness.signatures) || {}).length,
    +        threshold: round.threshold,
    +        validators: round.validators,
    +        status: round.status,
    +        createdAt: round.createdAt
    +      });
    +    }
    +    return out;
    +  }
    +
    +  _verifyEpochWitnessesIfConfigured () {
    +    if (!this._federationValidators.length) return;
    +    const result = this._epochChain.verify(
    +      this._federationValidators,
    +      this._federationThreshold
    +    );
    +    for (const f of result.failures) {
    +      this.emit('warning', `[BEACON] Federation witness missing or invalid for epoch clock ${f.clock}`);
    +    }
    +    if (result.ok) return;
    +
    +    if (this.settings.federationWitnessFailClosed === false) return;
    +
    +    const firstBad = result.failures[0];
    +    const msgs = this._epochChain.toBeaconMessages();
    +    const idx = msgs.findIndex((e) =>
    +      e && e.payload && e.payload.clock === firstBad.clock
    +    );
    +    if (idx < 0) return;
    +    this._epochChain.truncateAt(idx);
    +    const last = this._epochChain.tip;
    +    if (last && last.payload) {
    +      this._state.content.clock = last.payload.clock != null ? last.payload.clock : 0;
    +      this._state.content.lastBlockHash = last.payload.blockHash || null;
    +      this._state.content.height = last.payload.height != null ? last.payload.height : 0;
    +      this._state.content.balance = last.payload.balance != null ? last.payload.balance : 0;
    +      this._state.content.balanceSats = last.payload.balanceSats != null ? last.payload.balanceSats : 0;
    +    } else {
    +      this._state.content.clock = 0;
    +      this._state.content.lastBlockHash = null;
    +      this._state.content.height = 0;
    +      this._state.content.balance = 0;
    +      this._state.content.balanceSats = 0;
    +    }
    +    this.emit('error', new Error(
    +      `[BEACON] Fail-closed: truncated epoch chain before clock ${firstBad.clock} (${result.failures.length} invalid witness(es))`
    +    ));
    +  }
    +
    +  async _loadEpochChainFromFilesystem () {
    +    if (!this.fs || typeof this.fs.readFile !== 'function') return;
    +    try {
    +      const raw = this.fs.readFile(BEACON_CHAIN_PATH);
    +      if (!raw) return;
    +      const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
    +      if (!parsed || !Array.isArray(parsed.messages)) return;
    +      this._epochChain = Chain.fromBeaconMessages(parsed.messages);
    +      const last = this._epochChain.tip;
    +      if (last && last.payload && Number.isFinite(last.payload.clock)) {
    +        this._state.content.clock = last.payload.clock;
    +        this._state.content.lastBlockHash = last.payload.blockHash || null;
    +        this._state.content.height = last.payload.height != null ? last.payload.height : 0;
    +        this._state.content.balance = last.payload.balance != null ? last.payload.balance : 0;
    +        this._state.content.balanceSats = last.payload.balanceSats != null ? last.payload.balanceSats : 0;
    +      }
    +      this._verifyEpochWitnessesIfConfigured();
    +    } catch (err) {
    +      this.emit('warning', '[BEACON] Failed to load epoch chain from filesystem:', err && err.message ? err.message : err);
    +    }
    +  }
    +
    +  _computeMerkleRoot () {
    +    return this._epochChain.digest();
    +  }
    +
    +  async _persistEpochChain () {
    +    if (!this.fs || typeof this.fs.publish !== 'function') return;
    +    try {
    +      const messages = this._epochChain.toBeaconMessages();
    +      const merkle = { root: this._computeMerkleRoot(), leaves: this._epochChain.height };
    +      await this.fs.publish(BEACON_CHAIN_PATH, { messages, merkle });
    +    } catch (err) {
    +      this.emit('warning', '[BEACON] Failed to persist epoch chain:', err && err.message ? err.message : err);
    +    }
    +  }
    +
    +  async createEpoch () {
    +    if (!this.bitcoin) throw new Error('Beacon has no Bitcoin service attached.');
    +
    +    const address = await this.bitcoin.getUnusedAddress();
    +    const generated = await this.bitcoin._makeRPCRequest('generatetoaddress', [1, address]);
    +    const blockHash = Array.isArray(generated) ? generated[0] : generated;
    +    const height = await this.bitcoin._makeRPCRequest('getblockcount', []);
    +    const balances = await this.bitcoin._makeRPCRequest('getbalances', []).catch(() => null);
    +    const trusted = (balances && balances.mine && balances.mine.trusted != null) ? Number(balances.mine.trusted) : 0;
    +    const balanceSats = Math.round(trusted * SATS_PER_BTC);
    +
    +    this._state.content.clock += 1;
    +    this._state.content.lastBlockHash = blockHash || null;
    +    this._state.content.height = Number(height || 0);
    +    this._state.content.balance = trusted;
    +    this._state.content.balanceSats = balanceSats;
    +
    +    const epoch = {
    +      clock: this._state.content.clock,
    +      blockHash: this._state.content.lastBlockHash,
    +      height: this._state.content.height,
    +      balance: trusted,
    +      balanceSats,
    +      timestamp: new Date().toISOString()
    +    };
    +
    +    let committedPayload = epoch;
    +    try {
    +      committedPayload = await this._commitEpochWithFederation(epoch);
    +    } catch (err) {
    +      this.emit('error', err);
    +    }
    +
    +    if (!(committedPayload && committedPayload.pending)) {
    +      this.emit('epoch', committedPayload);
    +    }
    +    return committedPayload;
    +  }
    +
    +  _pruneEpochChain (inclusiveMaxHeight) {
    +    const maxH = Number(inclusiveMaxHeight);
    +    if (!Number.isFinite(maxH)) return;
    +
    +    const { pruned, removedBeaconClocks } = this._epochChain.pruneByBeaconHeight(maxH);
    +    if (pruned === 0) return;
    +
    +    const last = this._epochChain.tip;
    +    if (last && last.payload) {
    +      this._state.content.clock = last.payload.clock != null ? last.payload.clock : 0;
    +      this._state.content.lastBlockHash = last.payload.blockHash || null;
    +      this._state.content.height = last.payload.height != null ? last.payload.height : 0;
    +      this._state.content.balance = last.payload.balance != null ? last.payload.balance : 0;
    +      this._state.content.balanceSats = last.payload.balanceSats != null ? last.payload.balanceSats : 0;
    +    } else {
    +      this._state.content.clock = 0;
    +      this._state.content.lastBlockHash = null;
    +      this._state.content.height = 0;
    +      this._state.content.balance = 0;
    +      this._state.content.balanceSats = 0;
    +    }
    +    this.emit('reorg', { pruned, inclusiveMaxHeight, removedBeaconClocks });
    +  }
    +
    +  async recordEpochFromBlock (payload = {}) {
    +    if (!this.bitcoin) throw new Error('Beacon has no Bitcoin service attached.');
    +
    +    const blockHash = payload.tip || null;
    +    const height = payload.height != null
    +      ? Number(payload.height)
    +      : (await this.bitcoin._makeRPCRequest('getblockcount', []));
    +    const balances = await this.bitcoin._makeRPCRequest('getbalances', []).catch(() => null);
    +    const trusted = (balances && balances.mine && balances.mine.trusted != null) ? Number(balances.mine.trusted) : 0;
    +    const balanceSats = Math.round(trusted * SATS_PER_BTC);
    +
    +    if (height <= this._state.content.height && blockHash === this._state.content.lastBlockHash) {
    +      return null;
    +    }
    +
    +    if (height < this._state.content.height) {
    +      this._pruneEpochChain(height);
    +    } else if (height === this._state.content.height && blockHash !== this._state.content.lastBlockHash) {
    +      const popped = this._epochChain.pop();
    +      const poppedClock = popped && popped.payload && popped.payload.clock != null
    +        ? Number(popped.payload.clock)
    +        : null;
    +      const last = this._epochChain.tip;
    +      if (last && last.payload) {
    +        this._state.content.clock = last.payload.clock != null ? last.payload.clock : 0;
    +        this._state.content.lastBlockHash = last.payload.blockHash || null;
    +        this._state.content.height = last.payload.height != null ? last.payload.height : 0;
    +        this._state.content.balance = last.payload.balance != null ? last.payload.balance : 0;
    +        this._state.content.balanceSats = last.payload.balanceSats != null ? last.payload.balanceSats : 0;
    +      } else if (!this._epochChain.height) {
    +        this._state.content.clock = 0;
    +        this._state.content.lastBlockHash = null;
    +        this._state.content.height = 0;
    +        this._state.content.balance = 0;
    +        this._state.content.balanceSats = 0;
    +      }
    +      this.emit('reorg', {
    +        pruned: 1,
    +        sameHeight: true,
    +        removedBeaconClocks: poppedClock != null && Number.isFinite(poppedClock) ? [poppedClock] : []
    +      });
    +    }
    +
    +    this._state.content.clock += 1;
    +    this._state.content.lastBlockHash = blockHash;
    +    this._state.content.height = height;
    +    this._state.content.balance = trusted;
    +    this._state.content.balanceSats = balanceSats;
    +
    +    const epoch = {
    +      clock: this._state.content.clock,
    +      blockHash: this._state.content.lastBlockHash,
    +      height: this._state.content.height,
    +      balance: trusted,
    +      balanceSats,
    +      timestamp: new Date().toISOString()
    +    };
    +
    +    let committedPayload = epoch;
    +    try {
    +      committedPayload = await this._commitEpochWithFederation(epoch);
    +    } catch (err) {
    +      this.emit('error', err);
    +    }
    +
    +    if (!(committedPayload && committedPayload.pending)) {
    +      this.emit('epoch', committedPayload);
    +    }
    +    return committedPayload;
    +  }
    +
    +  async start () {
    +    if (this._state.content.status === 'RUNNING') return this;
    +    this._state.content.status = 'RUNNING';
    +
    +    await this._loadEpochChainFromFilesystem();
    +
    +    const isRegtest = this.settings.regtest !== false;
    +
    +    if (isRegtest) {
    +      if (this.settings.mineOnStart !== false) {
    +        try {
    +          await this.createEpoch();
    +        } catch (err) {
    +          this.emit('error', err);
    +        }
    +      }
    +      const interval = Number(this.settings.interval);
    +      if (Number.isFinite(interval) && interval > 0) {
    +        this.timer = setInterval(() => {
    +          this.createEpoch().catch((err) => this.emit('error', err));
    +        }, interval);
    +      }
    +    } else {
    +      const prime = async () => {
    +        try {
    +          const tip = await this.bitcoin._makeRPCRequest('getbestblockhash', []);
    +          const height = await this.bitcoin._makeRPCRequest('getblockcount', []);
    +          await this.recordEpochFromBlock({ tip, height });
    +        } catch (err) {
    +          this.emit('error', err);
    +        }
    +      };
    +      await prime();
    +      this._blockHandler = (payload) => {
    +        this.recordEpochFromBlock(payload).catch((err) => this.emit('error', err));
    +      };
    +      if (this.bitcoin && typeof this.bitcoin.on === 'function') {
    +        this.bitcoin.on('block', this._blockHandler);
    +      }
    +    }
    +
    +    return this;
    +  }
    +
    +  async stop () {
    +    if (this.timer) {
    +      clearInterval(this.timer);
    +      this.timer = null;
    +    }
    +    if (this._blockHandler && this.bitcoin && typeof this.bitcoin.removeListener === 'function') {
    +      this.bitcoin.removeListener('block', this._blockHandler);
    +      this._blockHandler = null;
    +    }
    +    await this._persistEpochChain();
    +    this._state.content.status = 'STOPPED';
    +    return this;
    +  }
    +}
    +
    +Beacon.BEACON_CHAIN_PATH = BEACON_CHAIN_PATH;
    +Beacon.SATS_PER_BTC = SATS_PER_BTC;
    +
    +module.exports = Beacon;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_block.js.html b/docs/types_block.js.html new file mode 100644 index 000000000..0952956b3 --- /dev/null +++ b/docs/types_block.js.html @@ -0,0 +1,609 @@ + + + + + + Source: types/block.js · Docs + + + + + + + + + +
    +

    Source: types/block.js

    + + + + +
    +
    +
    'use strict';
    +
    +/**
    + * Bitcoin-shaped Block: parent-linked header + merkle of leaves, with optional
    + * PoW (`nonce`/`bits`), Elements-style federation signatures, and arbitrary `data`.
    + *
    + * @see docs/CHAIN.md
    + */
    +
    +const merge = require('lodash.merge');
    +const crypto = require('crypto');
    +
    +const Actor = require('./actor');
    +const Transaction = require('./transaction');
    +const Tree = require('./tree');
    +const Key = require('./key');
    +const fabricCanonicalJson = require('../functions/fabricCanonicalJson');
    +const {
    +  jsonSafe,
    +  stableStringify
    +} = fabricCanonicalJson;
    +const {
    +  signingStringForBeaconEpoch,
    +  verifyFederationWitnessOnMessage
    +} = require('../functions/beaconFederationSigning');
    +
    +const CONSENSUS_POW = 'pow';
    +const CONSENSUS_FEDERATION = 'federation';
    +const CONSENSUS_GOSSIP = 'gossip';
    +const BLOCK_SIGNING_KIND = 'FabricBlock';
    +
    +/**
    + * @param {object} input
    + * @returns {boolean}
    + */
    +function isStructuredBlockInput (input) {
    +  if (!input || typeof input !== 'object') return false;
    +  if (input instanceof Block) return true;
    +  const t = input.type;
    +  if (t === 'BEACON_EPOCH' || t === 'SCEvent' || t === 'FabricBlock') return true;
    +  if (input.data !== undefined || input.payload !== undefined) return true;
    +  if (input.federationWitness != null) return true;
    +  if (input.author != null && (input.parent !== undefined || input.height != null)) return true;
    +  return false;
    +}
    +
    +/**
    + * Canonical signing / digest body (excludes witness material).
    + * @param {object} header
    + * @returns {string}
    + */
    +function signingStringForBlock (header) {
    +  return stableStringify({
    +    version: 1,
    +    kind: BLOCK_SIGNING_KIND,
    +    id: header && header.id != null ? String(header.id) : '',
    +    parent: header && header.parent != null ? String(header.parent) : null,
    +    height: Number(header && header.height) || 0,
    +    type: header && header.type != null ? String(header.type) : 'Block',
    +    data: header && header.data != null ? jsonSafe(header.data) : {},
    +    transactions: header && header.transactions
    +      ? jsonSafe(header.transactions)
    +      : {},
    +    merkleRoot: header && header.merkleRoot != null ? String(header.merkleRoot) : null,
    +    timestamp: header && header.timestamp != null ? String(header.timestamp) : null,
    +    author: header && header.author != null ? String(header.author) : null,
    +    nonce: Number(header && header.nonce) || 0,
    +    bits: header && header.bits != null ? header.bits : null
    +  });
    +}
    +
    +/**
    + * Content digest for merkle leaves / chain digest (includes optional federationWitness).
    + * @param {object} header
    + * @returns {string}
    + */
    +function blockDigest (header) {
    +  const s = stableStringify({
    +    id: header && header.id != null ? String(header.id) : '',
    +    parent: header && header.parent != null ? String(header.parent) : null,
    +    height: Number(header && header.height) || 0,
    +    type: header && header.type != null ? String(header.type) : 'Block',
    +    data: header && header.data != null ? jsonSafe(header.data) : {},
    +    author: header && header.author != null ? String(header.author) : null,
    +    federationWitness: header && header.federationWitness
    +      ? jsonSafe(header.federationWitness)
    +      : null
    +  });
    +  return crypto.createHash('sha256').update(Buffer.from(s, 'utf8')).digest('hex');
    +}
    +
    +/**
    + * Soft playnet PoW: leading zero hex nibbles from `bits` (integer 0–64).
    + * @param {string} idHex
    + * @param {number|null} bits
    + * @returns {boolean}
    + */
    +function meetsProofOfWork (idHex, bits) {
    +  if (bits == null || bits === false) return true;
    +  const n = Math.max(0, Math.min(64, Number(bits) || 0));
    +  if (!n) return true;
    +  const id = String(idHex || '');
    +  return id.slice(0, n) === '0'.repeat(n);
    +}
    +
    +class Block extends Actor {
    +  /**
    +   * @param {object} [input]
    +   */
    +  constructor (input = {}) {
    +    const src = (input instanceof Block)
    +      ? input.toRecord()
    +      : (input && typeof input === 'object' ? input : {});
    +
    +    const structured = isStructuredBlockInput(src);
    +
    +    if (!structured) {
    +      // Playnet / legacy: preserve Actor content identity (known fixture hashes).
    +      super(src);
    +      this.settings = merge({ type: 'Block' }, src);
    +      this._ledgerId = null;
    +      this._state = {
    +        parent: src.parent != null ? src.parent : null,
    +        height: Number(src.height) || 0,
    +        transactions: (src.transactions && typeof src.transactions === 'object')
    +          ? src.transactions
    +          : {},
    +        signatures: Array.isArray(src.signatures) ? src.signatures.slice() : [],
    +        federationWitness: src.federationWitness || null,
    +        signature: src.signature || null,
    +        author: src.author != null ? String(src.author) : null,
    +        data: src.data !== undefined ? src.data : null,
    +        merkleRoot: src.merkleRoot != null ? src.merkleRoot : null,
    +        nonce: Number(src.nonce) || 0,
    +        bits: src.bits != null ? src.bits : null,
    +        timestamp: src.timestamp != null ? src.timestamp : null,
    +        blockType: src.type || 'Block',
    +        content: this.state || src
    +      };
    +    } else {
    +      const data = src.data !== undefined
    +        ? src.data
    +        : (src.payload !== undefined ? src.payload : {});
    +      const txs = (src.transactions && typeof src.transactions === 'object')
    +        ? src.transactions
    +        : {};
    +      const headerContent = {
    +        type: src.type || 'Block',
    +        parent: src.parent != null ? String(src.parent) : null,
    +        height: Number(src.height) || 0,
    +        data: data && typeof data === 'object' ? data : {},
    +        transactions: txs,
    +        author: src.author != null ? String(src.author) : null,
    +        timestamp: src.timestamp != null
    +          ? src.timestamp
    +          : (data && (data.timestamp || data.ts)) || null,
    +        nonce: Number(src.nonce) || 0,
    +        bits: src.bits != null ? src.bits : null,
    +        merkleRoot: src.merkleRoot != null ? src.merkleRoot : null
    +      };
    +      super(headerContent);
    +      this.settings = merge({ type: headerContent.type }, src);
    +      this._ledgerId = src.id != null ? String(src.id) : null;
    +      this._state = {
    +        parent: headerContent.parent,
    +        height: headerContent.height,
    +        transactions: txs,
    +        signatures: Array.isArray(src.signatures) ? src.signatures.slice() : [],
    +        federationWitness: src.federationWitness
    +          ? JSON.parse(JSON.stringify(src.federationWitness))
    +          : null,
    +        signature: src.signature != null ? String(src.signature) : null,
    +        author: headerContent.author,
    +        data: headerContent.data,
    +        merkleRoot: headerContent.merkleRoot,
    +        nonce: headerContent.nonce,
    +        bits: headerContent.bits,
    +        timestamp: headerContent.timestamp,
    +        blockType: headerContent.type,
    +        content: this.state || headerContent
    +      };
    +      if (!this._state.merkleRoot) {
    +        this._state.merkleRoot = this._computeMerkleRoot();
    +      }
    +      if (!this._ledgerId) {
    +        this._ledgerId = blockDigest({
    +          id: '',
    +          parent: this._state.parent,
    +          height: this._state.height,
    +          type: this._state.blockType,
    +          data: this._state.data,
    +          author: this._state.author,
    +          federationWitness: this._state.federationWitness
    +        }).slice(0, 32);
    +      }
    +    }
    +
    +    Object.defineProperty(this, '_events', { enumerable: false });
    +    Object.defineProperty(this, '_eventCount', { enumerable: false });
    +    Object.defineProperty(this, 'observer', { enumerable: false });
    +    Object.defineProperty(this, '_ledgerId', { enumerable: false, writable: true });
    +
    +    for (const [id, template] of Object.entries(this.transactions || {})) {
    +      try {
    +        const tx = new Transaction(template);
    +        if (id !== tx.id) throw new Error(`Transaction hash mismatch! ${id} != ${tx.id}`);
    +      } catch (err) {
    +        if (err && /hash mismatch/.test(err.message)) throw err;
    +      }
    +    }
    +
    +    return this;
    +  }
    +
    +  get id () {
    +    if (this._ledgerId) return this._ledgerId;
    +    const buffer = Buffer.from(this.preimage, 'hex');
    +    const Hash256 = require('./hash256');
    +    return Hash256.compute(buffer);
    +  }
    +
    +  get parent () {
    +    return this._state.parent != null ? this._state.parent : null;
    +  }
    +
    +  set parent (value) {
    +    this._state.parent = value != null ? String(value) : null;
    +  }
    +
    +  get height () {
    +    return Number(this._state.height) || 0;
    +  }
    +
    +  set height (value) {
    +    this._state.height = Number(value) || 0;
    +  }
    +
    +  /** Alias used by Beacon codecs / tests. */
    +  get clock () {
    +    return this.height;
    +  }
    +
    +  get blockType () {
    +    return this._state.blockType || 'Block';
    +  }
    +
    +  get type () {
    +    return this._state.blockType || this._state['@type'] || 'Block';
    +  }
    +
    +  get data () {
    +    return this._state.data != null ? this._state.data : {};
    +  }
    +
    +  /** Beacon / legacy entry alias for `data`. */
    +  get payload () {
    +    return this.data;
    +  }
    +
    +  get author () {
    +    return this._state.author != null ? this._state.author : null;
    +  }
    +
    +  get signature () {
    +    return this._state.signature != null ? this._state.signature : null;
    +  }
    +
    +  get signatures () {
    +    return Array.isArray(this._state.signatures) ? this._state.signatures : [];
    +  }
    +
    +  get federationWitness () {
    +    return this._state.federationWitness || null;
    +  }
    +
    +  get merkleRoot () {
    +    return this._state.merkleRoot != null ? this._state.merkleRoot : this._computeMerkleRoot();
    +  }
    +
    +  get nonce () {
    +    return Number(this._state.nonce) || 0;
    +  }
    +
    +  get bits () {
    +    return this._state.bits != null ? this._state.bits : null;
    +  }
    +
    +  get timestamp () {
    +    return this._state.timestamp != null ? this._state.timestamp : null;
    +  }
    +
    +  get tree () {
    +    const leaves = Object.keys(this.transactions || {});
    +    if (this._state.data && typeof this._state.data === 'object' && Object.keys(this._state.data).length) {
    +      leaves.push(blockDigest({ id: '', type: this.blockType, data: this._state.data }));
    +    }
    +    // Empty leaves → empty merkle root (Bitcoin-style playnet fixture).
    +    return new Tree({ leaves });
    +  }
    +
    +  get transactions () {
    +    return this._state.transactions || {};
    +  }
    +
    +  get transactionIDs () {
    +    return Object.keys(this.transactions || {});
    +  }
    +
    +  _computeMerkleRoot () {
    +    const tree = this.tree;
    +    const root = tree.root;
    +    if (!root) return null;
    +    return Buffer.isBuffer(root) ? root.toString('hex') : String(root);
    +  }
    +
    +  /**
    +   * JSON-safe ledger record.
    +   * @returns {object}
    +   */
    +  toRecord () {
    +    return {
    +      id: this.id,
    +      parent: this.parent,
    +      height: this.height,
    +      type: this.blockType,
    +      data: this._state.data != null
    +        ? JSON.parse(JSON.stringify(this._state.data))
    +        : {},
    +      transactions: this.transactions
    +        ? JSON.parse(JSON.stringify(this.transactions))
    +        : {},
    +      merkleRoot: this.merkleRoot,
    +      nonce: this.nonce,
    +      bits: this.bits,
    +      timestamp: this.timestamp,
    +      author: this.author,
    +      signature: this.signature,
    +      signatures: this.signatures.slice(),
    +      federationWitness: this.federationWitness
    +        ? JSON.parse(JSON.stringify(this.federationWitness))
    +        : null
    +    };
    +  }
    +
    +  /**
    +   * @param {object} record
    +   * @returns {Block}
    +   */
    +  static fromRecord (record) {
    +    return new Block(record || {});
    +  }
    +
    +  signingString () {
    +    return signingStringForBlock(this.toRecord());
    +  }
    +
    +  digest () {
    +    return blockDigest(this.toRecord());
    +  }
    +
    +  /**
    +   * Author Schnorr over signingString (gossip).
    +   * @param {Key} key
    +   * @returns {string} signature hex
    +   */
    +  sign (key) {
    +    if (key && key.private) {
    +      if (!this._state.author && key.pubkey) this._state.author = String(key.pubkey);
    +      const msg = Buffer.from(this.signingString(), 'utf8');
    +      const sig = key.signSchnorr(msg);
    +      this._state.signature = Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig);
    +      return this._state.signature;
    +    }
    +    // Legacy Actor-style sign via this.key
    +    const actor = new Actor(this._state);
    +    const data = actor.toString();
    +    const array = this.key._sign(data);
    +    this._state.signature = Buffer.from(array);
    +    return this._state.signature;
    +  }
    +
    +  /**
    +   * @param {string} pubkey
    +   * @param {string|Buffer} sig
    +   * @returns {Block}
    +   */
    +  addSignature (pubkey, sig) {
    +    const hex = Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig);
    +    const pk = String(pubkey || '');
    +    if (!this._state.federationWitness) {
    +      this._state.federationWitness = { signatures: {} };
    +    }
    +    if (!this._state.federationWitness.signatures) {
    +      this._state.federationWitness.signatures = {};
    +    }
    +    this._state.federationWitness.signatures[pk] = hex;
    +    this._state.signatures.push({ pubkey: pk, signature: hex });
    +    return this;
    +  }
    +
    +  /**
    +   * @param {Object} [opts]
    +   * @param {string} [opts.consensus]
    +   * @param {Array.<string>} [opts.validators]
    +   * @param {number} [opts.threshold]
    +   * @param {number|null} [opts.bits]
    +   * @param {string|null} [opts.requireParent]
    +   * @returns {{ok: boolean, reason: (string|undefined)}}
    +   */
    +  validate (opts = {}) {
    +    const consensus = opts.consensus || CONSENSUS_POW;
    +    if (opts.requireParent !== undefined) {
    +      const expected = opts.requireParent != null ? String(opts.requireParent) : null;
    +      const actual = this.parent != null ? String(this.parent) : null;
    +      if (expected !== actual) {
    +        return { ok: false, reason: 'parent must match tip' };
    +      }
    +    }
    +
    +    if (consensus === CONSENSUS_POW) {
    +      const bits = opts.bits !== undefined ? opts.bits : this.bits;
    +      if (!meetsProofOfWork(this.id, bits)) {
    +        return { ok: false, reason: 'proof of work not met' };
    +      }
    +      return { ok: true };
    +    }
    +
    +    if (consensus === CONSENSUS_FEDERATION) {
    +      const pubs = Array.isArray(opts.validators) ? opts.validators : [];
    +      if (!pubs.length) return { ok: true };
    +      const thr = Math.max(1, Number(opts.threshold) || 1);
    +      if (this.blockType === 'BEACON_EPOCH' && this.data) {
    +        const buf = Buffer.from(
    +          signingStringForBeaconEpoch(this.data),
    +          'utf8'
    +        );
    +        const ok = verifyFederationWitnessOnMessage(
    +          buf,
    +          this.federationWitness,
    +          pubs,
    +          thr
    +        );
    +        if (!ok) return { ok: false, reason: 'federationWitness missing or invalid' };
    +        return { ok: true };
    +      }
    +      const buf = Buffer.from(this.signingString(), 'utf8');
    +      const ok = verifyFederationWitnessOnMessage(
    +        buf,
    +        this.federationWitness,
    +        pubs,
    +        thr
    +      );
    +      if (!ok) return { ok: false, reason: 'federationWitness missing or invalid' };
    +      return { ok: true };
    +    }
    +
    +    if (consensus === CONSENSUS_GOSSIP) {
    +      if (!this.signature || !this.author) return { ok: true };
    +      try {
    +        const k = new Key({ pubkey: this.author });
    +        const msg = Buffer.from(this.signingString(), 'utf8');
    +        const sig = Buffer.from(this.signature, 'hex');
    +        if (!k.verifySchnorr(msg, sig)) {
    +          return { ok: false, reason: 'invalid author signature' };
    +        }
    +        return { ok: true };
    +      } catch (err) {
    +        return {
    +          ok: false,
    +          reason: err && err.message ? err.message : 'signature verify error'
    +        };
    +      }
    +    }
    +
    +    return { ok: true };
    +  }
    +}
    +
    +Block.CONSENSUS_POW = CONSENSUS_POW;
    +Block.CONSENSUS_FEDERATION = CONSENSUS_FEDERATION;
    +Block.CONSENSUS_GOSSIP = CONSENSUS_GOSSIP;
    +Block.signingStringForBlock = signingStringForBlock;
    +Block.blockDigest = blockDigest;
    +Block.meetsProofOfWork = meetsProofOfWork;
    +Block.isStructuredBlockInput = isStructuredBlockInput;
    +
    +module.exports = Block;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_bond.js.html b/docs/types_bond.js.html new file mode 100644 index 000000000..ae1a4190a --- /dev/null +++ b/docs/types_bond.js.html @@ -0,0 +1,153 @@ + + + + + + Source: types/bond.js · Docs + + + + + + + + + +
    +

    Source: types/bond.js

    + + + + +
    +
    +
    'use strict';
    +
    +const Contract = require('./contract');
    +
    +/**
    + * On-chain or logical bond / stake terms layered on {@link Contract}.
    + * @class Bond
    + * @extends Contract
    + */
    +class Bond extends Contract {
    +  constructor (settings = {}) {
    +    super(settings);
    +
    +    this.settings = Object.assign({
    +      amount: null,
    +      expiry: null,
    +      issuer: null
    +    }, this.settings);
    +
    +    return this;
    +  }
    +
    +  _getExpiry (type, time) {
    +    return {
    +      type, time,
    +      content: Buffer.alloc(4) // TODO: Bitcoin encoded values?
    +    };
    +  }
    +}
    +
    +module.exports = Bond;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_chain.js.html b/docs/types_chain.js.html index 33c20f7c0..c1c5cca41 100644 --- a/docs/types_chain.js.html +++ b/docs/types_chain.js.html @@ -33,6 +33,22 @@

    Source: types/chain.js

    'use strict';
     
    +/**
    + * Chain — ledger of Bitcoin-shaped Blocks with consensus policy:
    + *
    + * - `pow` (default) — parent-linked playnet / Bitcoin-style Block + mempool
    + * - `federation` — linear tip; Elements-style k-of-n block signatures (Beacon)
    + * - `gossip` — content-addressed data blocks; merge = union by block id
    + *
    + * Statechain document helpers (`functions/sidechainState`) hold the sealed JSON
    + * document. Digests feed that document / Beacon sidechain heads; raw gossip is
    + * never Beacon authority.
    + *
    + * @see docs/CHAIN.md
    + * @see docs/DISTRIBUTED_EXECUTION.md
    + */
    +
    +const crypto = require('crypto');
     const {
       MAX_TX_PER_BLOCK
     } = require('../constants');
    @@ -44,32 +60,186 @@ 

    Source: types/chain.js

    const Stack = require('./stack'); const State = require('./state'); const Transaction = require('./transaction'); +const Tree = require('./tree'); +const Key = require('./key'); +const fabricCanonicalJson = require('../functions/fabricCanonicalJson'); +const { stableStringify } = fabricCanonicalJson; + +const CONSENSUS_POW = Block.CONSENSUS_POW; +const CONSENSUS_FEDERATION = Block.CONSENSUS_FEDERATION; +const CONSENSUS_GOSSIP = Block.CONSENSUS_GOSSIP; + +/** @deprecated Use CONSENSUS_*; aliases for one release. */ +const SEAL_BLOCK = 'block'; +const SEAL_FEDERATION = CONSENSUS_FEDERATION; +const SEAL_GOSSIP = CONSENSUS_GOSSIP; + +function eventId (args = {}) { + const body = stableStringify({ + source: String(args.source || ''), + kind: String(args.kind || ''), + fields: args.fields && typeof args.fields === 'object' ? args.fields : {}, + timestamp: args.timestamp != null ? String(args.timestamp) : '' + }); + return crypto.createHash('sha256').update(Buffer.from(body, 'utf8')).digest('hex').slice(0, 32); +} + +function entryDigest (entry) { + return Block.blockDigest({ + id: entry && entry.id, + parent: entry && entry.parent, + height: entry && (entry.height != null ? entry.height : entry.clock), + type: entry && entry.type, + data: entry && (entry.data !== undefined ? entry.data : entry.payload), + author: entry && entry.author, + federationWitness: entry && entry.federationWitness + }); +} + +function signingStringForEntry (entry) { + return Block.signingStringForBlock({ + id: entry && entry.id, + parent: entry && entry.parent, + height: entry && (entry.height != null ? entry.height : entry.clock), + type: entry && entry.type, + data: entry && (entry.data !== undefined ? entry.data : entry.payload), + transactions: entry && entry.transactions, + merkleRoot: entry && entry.merkleRoot, + timestamp: entry && entry.timestamp, + author: entry && entry.author, + nonce: entry && entry.nonce, + bits: entry && entry.bits + }); +} + +function _normalizeConsensus (value) { + if (value === CONSENSUS_FEDERATION || value === SEAL_FEDERATION) return CONSENSUS_FEDERATION; + if (value === CONSENSUS_GOSSIP || value === SEAL_GOSSIP) return CONSENSUS_GOSSIP; + if (value === CONSENSUS_POW || value === SEAL_BLOCK || value === 'block') return CONSENSUS_POW; + return CONSENSUS_POW; +} + +function _cloneRecord (rec) { + return JSON.parse(JSON.stringify(rec)); +} + +function _recordFromInput (input, opts = {}) { + if (input instanceof Block) return input.toRecord(); + if (input && typeof input === 'object') { + const data = input.data !== undefined + ? input.data + : (input.payload !== undefined ? input.payload : ( + (input.type === 'BEACON_EPOCH' || input.type === 'SCEvent' || opts.type) + ? (input.payload || {}) + : undefined + )); + if (data !== undefined || input.type === 'BEACON_EPOCH' || input.type === 'SCEvent' || + opts.type || input.federationWitness != null || opts.signWith) { + const block = new Block({ + id: opts.id || input.id || null, + parent: input.parent !== undefined ? input.parent : opts.parent, + height: input.height != null ? input.height : (input.clock != null ? input.clock : opts.height), + type: opts.type || input.type || 'Block', + data: data !== undefined ? data : {}, + transactions: input.transactions || {}, + author: opts.author != null ? opts.author : input.author, + signature: opts.signature != null ? opts.signature : input.signature, + federationWitness: opts.federationWitness !== undefined + ? opts.federationWitness + : input.federationWitness, + timestamp: input.timestamp, + nonce: input.nonce, + bits: input.bits, + merkleRoot: input.merkleRoot + }); + if (opts.signWith) block.sign(opts.signWith); + return block.toRecord(); + } + } + const block = input instanceof Block ? input : new Block(input); + return block.toRecord ? block.toRecord() : { + id: block.id, + parent: block.parent, + height: block.height || 0, + type: 'Block', + data: {}, + transactions: block.transactions || {} + }; +} + +function _sortGossipRecords (records) { + return records.slice().sort((a, b) => { + const ta = (a.data && (a.data.timestamp || a.data.ts)) || a.timestamp || ''; + const tb = (b.data && (b.data.timestamp || b.data.ts)) || b.timestamp || ''; + if (ta !== tb) return String(ta).localeCompare(String(tb)); + const ca = Number(a.height) || 0; + const cb = Number(b.height) || 0; + if (ca !== cb) return ca - cb; + const aa = a.author || ''; + const ab = b.author || ''; + if (aa !== ab) return String(aa).localeCompare(String(ab)); + return String(a.id).localeCompare(String(b.id)); + }); +} + +function _relinkLinear (records) { + let parent = null; + const out = []; + for (let i = 0; i < records.length; i++) { + const e = _cloneRecord(records[i]); + e.parent = parent; + e.height = i + 1; + out.push(e); + parent = e.id; + } + return out; +} + +function _asTipView (rec) { + if (!rec) return null; + const view = _cloneRecord(rec); + view.clock = view.height; + view.payload = view.data; + return view; +} /** * Chain. - * @property {String} name Current name. - * @property {Map} indices - * @property {Storage} storage + * @property {String} consensus `pow` | `federation` | `gossip` */ class Chain extends Actor { /** - * Holds an immutable chain of events. - * @param {Vector} genesis Initial state for the chain of events. + * @param {Object} [origin] + * @param {String} [origin.consensus] `pow` (default), `federation`, or `gossip` + * @param {String} [origin.seal] Deprecated alias for consensus (`block`→`pow`) + * @param {Array} [origin.entries] Seed block records (federation/gossip) + * @param {Array} [origin.blocks] Seed block records */ constructor (origin = {}) { super(origin); this.name = (origin) ? origin.name : '@fabric/playnet'; + const consensus = _normalizeConsensus( + origin.consensus != null ? origin.consensus : origin.seal + ); + this.consensusMode = consensus; + /** @deprecated Use consensusMode */ + this.seal = consensus === CONSENSUS_POW ? SEAL_BLOCK : consensus; + this.settings = Object.assign({ name: this.name, + consensus: this.consensusMode, + seal: this.seal, type: 'sha256', genesis: null, mempool: [], transactions: {}, + bits: origin.bits != null ? origin.bits : null, + validators: Array.isArray(origin.validators) ? origin.validators : [], + threshold: origin.threshold != null ? origin.threshold : 1, validator: this.validate.bind(this) }, origin); - // Internal State this._state = { best: null, blocks: {}, @@ -81,32 +251,114 @@

    Source: types/chain.js

    mempool: [], tip: null }, - transactions: this.settings.transactions, - mempool: this.settings.mempool, + transactions: this.settings.transactions || {}, + mempool: this.settings.mempool || [], ledger: [] }; - for (let [key, value] of Object.entries(this._state.transactions)) { - const tx = new Transaction(value); - this._state.transactions[tx.id] = tx; - } - - for (let [key, value] of Object.entries(this._state.mempool)) { - this.proposeTransaction(value); + const seed = Array.isArray(origin.entries) + ? origin.entries + : (Array.isArray(origin.blocks) ? origin.blocks : null); + + if (seed && seed.length && this._isPolicyChain()) { + for (const row of seed) { + const rec = _recordFromInput(row); + this._state.blocks[rec.id] = rec; + this._state.ledger.push(rec.id); + this._state.consensus = rec.id; + this._state.content.blocks.push(rec.id); + this._state.content.actors[rec.id] = rec; + } + } else if (!this._isPolicyChain()) { + for (const [, value] of Object.entries(this._state.transactions)) { + const tx = new Transaction(value); + this._state.transactions[tx.id] = tx; + } + for (let [, value] of Object.entries(this._state.mempool)) { + this.proposeTransaction(value); + } } return this; } + _isPolicyChain () { + return this.consensusMode === CONSENSUS_FEDERATION || + this.consensusMode === CONSENSUS_GOSSIP; + } + + /** @deprecated */ + _isEntrySeal () { + return this._isPolicyChain(); + } + + /** + * @param {Object} [opts] + * @param {string} [opts.consensus] + * @param {string} [opts.seal] + * @param {Object} [opts.genesis] + * @param {Array.<Object>} [opts.entries] + * @param {Array.<Object>} [opts.blocks] + * @returns {Chain} + */ + static create (opts = {}) { + const consensus = _normalizeConsensus(opts.consensus != null ? opts.consensus : opts.seal); + const chain = new Chain({ + consensus, + entries: opts.entries || opts.blocks, + validators: opts.validators, + threshold: opts.threshold, + bits: opts.bits + }); + if (opts.genesis) { + chain.append(opts.genesis); + } + return chain; + } + static fromObject (data) { return new Chain(data); } + static fromJSON (obj) { + if (!obj || typeof obj !== 'object') return Chain.create(); + return new Chain({ + consensus: obj.consensus || obj.seal, + entries: Array.isArray(obj.entries) + ? obj.entries + : (Array.isArray(obj.blocks) ? obj.blocks : []) + }); + } + + toJSON () { + if (this._isPolicyChain()) { + return { + version: 1, + consensus: this.consensusMode, + seal: this.seal, + entries: this.entries + }; + } + return { + version: 1, + consensus: CONSENSUS_POW, + seal: SEAL_BLOCK, + name: this.name, + tip: this.tip, + height: this.height + }; + } + get consensus () { - return this.tip; + return this._state.consensus; } get tip () { + if (this._isPolicyChain()) { + if (!this._state.ledger.length) return null; + const id = this._state.ledger[this._state.ledger.length - 1]; + return _asTipView(this._state.blocks[id]); + } return this._state.consensus; } @@ -118,8 +370,13 @@

    Source: types/chain.js

    return this._state.ledger; } - get height () { + /** Cloned block records (federation/gossip). */ + get entries () { + return this._state.ledger.map((id) => _asTipView(this._state.blocks[id])); + } + get height () { + return this._state.ledger.length; } get leaves () { @@ -127,7 +384,7 @@

    Source: types/chain.js

    } get length () { - return this.blocks.length; + return this.height; } get subsidy () { @@ -147,17 +404,44 @@

    Source: types/chain.js

    return stack.asMerkleTree(); } + at (index) { + if (!this._isPolicyChain()) return null; + const id = this._state.ledger[index]; + if (!id) return null; + return _asTipView(this._state.blocks[id]); + } + + digest () { + if (!this._isPolicyChain()) { + if (!this._state.ledger.length) return null; + const leaves = this._state.ledger.map((id) => { + const rec = this._state.blocks[id]; + return rec ? Block.blockDigest(rec) : id; + }); + const tree = new Tree({ leaves }); + const root = tree.root; + if (!root) return null; + return Buffer.isBuffer(root) ? root.toString('hex') : String(root); + } + if (!this._state.ledger.length) return null; + const leaves = this._state.ledger.map((id) => Block.blockDigest(this._state.blocks[id])); + const tree = new Tree({ leaves }); + const root = tree.root; + if (!root) return null; + return Buffer.isBuffer(root) ? root.toString('hex') : String(root); + } + createSignedBlock (proposal = {}) { return { actor: proposal.actor || Actor.randomBytes(32).toString('hex'), changes: proposal.changes, mode: proposal.mode || 'NAIVE_SIGHASH_SINGLE', - object: Buffer.concat( - Buffer.alloc(32), // pubkey - Buffer.alloc(32), // parent - Buffer.alloc(32), // changes - Buffer.alloc(64), // signature - ), + object: Buffer.concat([ + Buffer.alloc(32), + Buffer.alloc(32), + Buffer.alloc(32), + Buffer.alloc(64) + ]), parent: this.id, signature: Buffer.alloc(64), state: this.state, @@ -168,7 +452,11 @@

    Source: types/chain.js

    proposeTransaction (transaction) { const actor = new Transaction(transaction); - // TODO: reject duplicate transactions + const prior = this._state.transactions[actor.id]; + if (prior) { + return prior; + } + this._state.transactions[actor.id] = actor; this._state.mempool.push(actor.id); @@ -185,7 +473,7 @@

    Source: types/chain.js

    super.trust(source, 'TIMECHAIN'); - source.on('message', function TODO (message) { + source.on('message', function onTrustedSourceMessage (message) { self.emit('debug', `Message from trusted source: ${message}`); }); @@ -194,13 +482,8 @@

    Source: types/chain.js

    async start () { const chain = this; - - // Monitor changes this.observer = monitor.observe(this._state.content); - - // before returning, ensure a commit await chain.commit(); - return chain; } @@ -215,7 +498,6 @@

    Source: types/chain.js

    } else { this.store = application.store; } - return this; } @@ -229,38 +511,396 @@

    Source: types/chain.js

    async _load () { const chain = this; - const query = await chain.storage.get('/blocks'); const response = new State(query); - this.log('query:', query); this.log('response:', response); this.log('response id:', response.id); - return chain; } - async append (block) { - if (!block) throw new Error('Must provide a block.'); + /** + * Append a Block (or Block-shaped object). + * Policy chains return the tip view synchronously; pow returns a Promise. + * @returns {Promise<Chain>|object} + */ + append (input, opts = {}) { + if (this._isPolicyChain()) { + return this._appendPolicyBlock(input, opts); + } + return this._appendPowBlock(input); + } + + async _appendPowBlock (input) { + if (!input) throw new Error('Must provide a block.'); + let block = input; if (!(block instanceof Block)) { block = new Block(block); } + const tipId = this._state.consensus; + if (tipId && block.parent != null && String(block.parent) !== String(tipId)) { + // Soft: playnet historically did not enforce parent; only enforce when parent set wrongly + if (String(block.parent) !== String(tipId) && block.parent !== tipId) { + /* allow legacy append without parent match when parent was unset in constructor */ + } + } + + const bits = this.settings.bits; + if (bits != null) { + const check = block.validate({ consensus: CONSENSUS_POW, bits }); + if (!check.ok) throw new Error(check.reason || 'invalid block'); + } + if (this.blocks.length <= 0) { this._state.genesis = block.id; } - this._state.blocks[block.id] = block; + const rec = (typeof block.toRecord === 'function') + ? block.toRecord() + : { id: block.id, parent: block.parent, height: this.height + 1, type: 'Block', data: {}, transactions: block.transactions || {} }; + + this._state.blocks[block.id] = rec; this._state.ledger.push(block.id); this._state.consensus = block.id; - this._state.content.actors[block.id] = block.generic.object; + this._state.content.actors[block.id] = block.generic ? block.generic.object : rec; this._state.content.blocks.push(block.id); this.commit(); - this.emit('block', block); + return this; + } + _appendPolicyBlock (input, opts = {}) { + const tipId = this._state.ledger.length + ? this._state.ledger[this._state.ledger.length - 1] + : null; + const tip = tipId ? this._state.blocks[tipId] : null; + const nextHeight = tip ? (Number(tip.height) || 0) + 1 : 1; + + let payloadInput = input; + if (!(input instanceof Block) && input && typeof input === 'object') { + if (input.payload !== undefined && input.data === undefined) { + payloadInput = { ...input, data: input.payload, type: input.type || opts.type || 'Block' }; + } else if (input.payload !== undefined && input.type) { + payloadInput = { ...input, data: input.data !== undefined ? input.data : input.payload }; + } + } + + const rec = _recordFromInput(payloadInput, { + parent: tip ? tip.id : null, + height: nextHeight, + type: opts.type, + author: opts.author, + signature: opts.signature, + federationWitness: opts.federationWitness, + id: opts.id, + signWith: opts.signWith + }); + + if (rec.parent === undefined || (opts.parent === undefined && payloadInput && payloadInput.parent === undefined)) { + rec.parent = tip ? tip.id : null; + } + if (payloadInput && payloadInput.parent !== undefined) { + rec.parent = payloadInput.parent != null ? String(payloadInput.parent) : null; + } + if (rec.height == null || rec.height === 0) rec.height = nextHeight; + + // Gossip: content-address by event fields unless caller supplied an explicit id + if (this.consensusMode === CONSENSUS_GOSSIP) { + const data = rec.data || {}; + const explicitId = opts.id != null + ? String(opts.id) + : (payloadInput && typeof payloadInput === 'object' && !(payloadInput instanceof Block) && + payloadInput.id != null + ? String(payloadInput.id) + : null); + rec.id = explicitId || eventId({ + source: rec.author || data.source || '', + kind: data.kind || rec.type, + fields: data.fields || data, + timestamp: data.timestamp || data.ts || '' + }); + } + + if (this.consensusMode === CONSENSUS_FEDERATION) { + if (tip && rec.parent !== tip.id) { + throw new Error('federation Chain: parent must match tip (no forks)'); + } + if (!tip && rec.parent != null) { + throw new Error('federation Chain: genesis parent must be null'); + } + } + + if (opts.signWith && opts.signWith.private) { + const block = Block.fromRecord(rec); + block.sign(opts.signWith); + const signed = block.toRecord(); + rec.author = signed.author; + rec.signature = signed.signature; + } + + if (this.consensusMode === CONSENSUS_GOSSIP) { + if (this._state.blocks[rec.id]) { + return _asTipView(this._state.blocks[rec.id]); + } + } + + this._state.blocks[rec.id] = _cloneRecord(rec); + this._state.ledger.push(rec.id); + this._state.consensus = rec.id; + this._state.content.blocks.push(rec.id); + this._state.content.actors[rec.id] = rec; + + const view = _asTipView(rec); + this.emit('block', view); + this.emit('entry', view); + return view; + } + + pop () { + if (!this._isPolicyChain()) return null; + if (!this._state.ledger.length) return null; + const id = this._state.ledger.pop(); + const rec = this._state.blocks[id]; + delete this._state.blocks[id]; + this._state.consensus = this._state.ledger.length + ? this._state.ledger[this._state.ledger.length - 1] + : null; + const idx = this._state.content.blocks.indexOf(id); + if (idx >= 0) this._state.content.blocks.splice(idx, 1); + return _asTipView(rec); + } + + replay (opts = {}) { + if (!this._isPolicyChain()) return []; + let list = this.entries; + if (opts.fromId) { + const idx = list.findIndex((e) => e.id === opts.fromId); + list = idx >= 0 ? list.slice(idx) : []; + } + if (opts.fromClock != null) { + const c = Number(opts.fromClock) || 0; + list = list.filter((e) => (e.height || e.clock) >= c); + } + if (typeof opts.filter === 'function') { + list = list.filter(opts.filter); + } + return list; + } + + split (opts = {}) { + if (!this._isPolicyChain()) { + return { + head: Chain.create({ consensus: this.consensusMode }), + tail: Chain.create({ consensus: this.consensusMode }) + }; + } + const by = opts.by || 'clock'; + let headEntries = []; + let tailEntries = []; + const all = this.entries; + + if (by === 'clock' || by === 'height') { + const at = Number(opts.at != null ? opts.at : opts.clock) || 0; + for (const e of all) { + if ((e.height || e.clock) <= at) headEntries.push(e); + else tailEntries.push(e); + } + } else if (by === 'time') { + const at = String(opts.at || ''); + for (const e of all) { + const ts = (e.data && (e.data.timestamp || e.data.ts)) || + (e.payload && (e.payload.timestamp || e.payload.ts)) || ''; + if (String(ts) <= at) headEntries.push(e); + else tailEntries.push(e); + } + } else if (by === 'author') { + const author = String(opts.author || opts.at || ''); + for (const e of all) { + if (String(e.author || '') === author) headEntries.push(e); + else tailEntries.push(e); + } + } else if (by === 'predicate' && typeof opts.predicate === 'function') { + for (const e of all) { + if (opts.predicate(e)) headEntries.push(e); + else tailEntries.push(e); + } + } else { + headEntries = all; + tailEntries = []; + } + + if (this.consensusMode === CONSENSUS_FEDERATION) { + headEntries = _relinkLinear(headEntries); + tailEntries = _relinkLinear(tailEntries); + } + + return { + head: new Chain({ consensus: this.consensusMode, entries: headEntries }), + tail: new Chain({ consensus: this.consensusMode, entries: tailEntries }) + }; + } + + merge (other) { + if (!this._isPolicyChain()) throw new Error('merge requires federation or gossip consensus'); + if (!other || !(other instanceof Chain) || !other._isPolicyChain()) { + throw new Error('merge requires a Chain with federation/gossip consensus'); + } + if (other.consensusMode !== this.consensusMode) { + throw new Error('merge consensus mode mismatch'); + } + + if (this.consensusMode === CONSENSUS_FEDERATION) { + const a = this._state.ledger.map((id) => this._state.blocks[id]); + const b = other._state.ledger.map((id) => other._state.blocks[id]); + let i = 0; + while (i < a.length && i < b.length) { + if (a[i].id !== b[i].id) { + throw new Error('federation Chain merge: fork detected'); + } + i++; + } + if (b.length < a.length) return this; + for (; i < b.length; i++) { + const tipId = this._state.ledger.length + ? this._state.ledger[this._state.ledger.length - 1] + : null; + const tip = tipId ? this._state.blocks[tipId] : null; + const next = _cloneRecord(b[i]); + const expectedParent = tip ? tip.id : null; + if (next.parent !== expectedParent && tip) { + next.parent = expectedParent; + next.height = (Number(tip.height) || 0) + 1; + } else if (!tip) { + next.parent = null; + next.height = 1; + } + if (tip && next.parent !== tip.id) { + throw new Error('federation Chain merge: extension parent mismatch'); + } + this._state.blocks[next.id] = next; + this._state.ledger.push(next.id); + this._state.consensus = next.id; + this._state.content.blocks.push(next.id); + } + return this; + } + + const byId = new Map(); + for (const id of this._state.ledger) byId.set(id, _cloneRecord(this._state.blocks[id])); + for (const id of other._state.ledger) { + if (!byId.has(id)) byId.set(id, _cloneRecord(other._state.blocks[id])); + } + const merged = _relinkLinear(_sortGossipRecords([...byId.values()])); + this._state.blocks = {}; + this._state.ledger = []; + this._state.content.blocks = []; + for (const rec of merged) { + this._state.blocks[rec.id] = rec; + this._state.ledger.push(rec.id); + this._state.content.blocks.push(rec.id); + this._state.consensus = rec.id; + } + return this; + } + + verify (validatorsOrLevel = 4, thresholdOrDepth = 6) { + if (this._isPolicyChain()) { + return this._verifyPolicy(validatorsOrLevel, thresholdOrDepth); + } + return this._verifyBlocks(validatorsOrLevel, thresholdOrDepth); + } + + async _verifyBlocks (level = 4, depth = 6) { + this.log(`Verification Level ${level} running from -${depth}...`); + console.log('root:', this.root); + return (this['@id'] === this.root); + } + + _verifyPolicy (validators, threshold) { + const failures = []; + const pubs = Array.isArray(validators) ? validators : this.settings.validators || []; + const thr = Math.max(1, Number(threshold != null ? threshold : this.settings.threshold) || 1); + + for (const id of this._state.ledger) { + const rec = this._state.blocks[id]; + if (!rec) continue; + const block = Block.fromRecord(rec); + const result = block.validate({ + consensus: this.consensusMode, + validators: pubs, + threshold: thr + }); + if (!result.ok) { + failures.push({ + id: rec.id, + clock: rec.data && rec.data.clock != null ? rec.data.clock : rec.height, + reason: result.reason || 'invalid' + }); + } + } + return { ok: failures.length === 0, failures }; + } + + replaceFromBeaconMessages (messages) { + const built = fromBeaconMessages(messages, { consensus: CONSENSUS_FEDERATION }); + this.consensusMode = CONSENSUS_FEDERATION; + this.seal = SEAL_FEDERATION; + this.settings.consensus = CONSENSUS_FEDERATION; + this.settings.seal = SEAL_FEDERATION; + this._state.blocks = built._state.blocks; + this._state.ledger = built._state.ledger.slice(); + this._state.consensus = built._state.consensus; + this._state.content.blocks = built._state.ledger.slice(); + return this; + } + + toBeaconMessages () { + return toBeaconMessages(this); + } + + pruneByBeaconHeight (inclusiveMaxHeight) { + if (!this._isPolicyChain()) return { pruned: 0, removedBeaconClocks: [] }; + const maxH = Number(inclusiveMaxHeight); + if (!Number.isFinite(maxH)) return { pruned: 0, removedBeaconClocks: [] }; + const removedBeaconClocks = []; + const kept = []; + for (const id of this._state.ledger) { + const e = this._state.blocks[id]; + const h = e.data && e.data.height != null ? Number(e.data.height) : 0; + if (h <= maxH) kept.push(_cloneRecord(e)); + else if (e.data && e.data.clock != null) removedBeaconClocks.push(Number(e.data.clock)); + } + const pruned = this._state.ledger.length - kept.length; + let parent = null; + this._state.blocks = {}; + this._state.ledger = []; + this._state.content.blocks = []; + for (const row of kept) { + row.parent = parent; + if (row.data && row.data.clock != null) row.height = Number(row.data.clock); + this._state.blocks[row.id] = row; + this._state.ledger.push(row.id); + this._state.content.blocks.push(row.id); + this._state.consensus = row.id; + parent = row.id; + } + if (!kept.length) this._state.consensus = null; + return { pruned, removedBeaconClocks }; + } + + truncateAt (idx) { + if (!this._isPolicyChain()) return this; + const i = Math.max(0, Number(idx) || 0); + const keep = this._state.ledger.slice(0, i); + const nextBlocks = {}; + for (const id of keep) nextBlocks[id] = this._state.blocks[id]; + this._state.blocks = nextBlocks; + this._state.ledger = keep; + this._state.content.blocks = keep.slice(); + this._state.consensus = keep.length ? keep[keep.length - 1] : null; return this; } @@ -269,23 +909,18 @@

    Source: types/chain.js

    } async generateBlock () { + // Playnet proposal shape stays `{ parent, transactions }` so Actor ids match fixtures. const proposal = { parent: this.consensus, transactions: {} }; - // TODO: _sortFees if (this.mempool.length) { for (let i = 0; i < MAX_TX_PER_BLOCK; i++) { try { - // Retrieve a transaction from the mempool const txid = this.mempool.shift(); const candidate = this._state.transactions[txid]; - - // Create a local transaction instance const tx = new Transaction(candidate); - - // Update the proposal proposal.transactions[tx.id] = candidate; } catch (exception) { console.error('Could not create block:', exception); @@ -294,62 +929,149 @@

    Source: types/chain.js

    } } + const bits = this.settings.bits; + if (bits != null && Number(bits) > 0) { + let nonce = 0; + let block = new Block({ ...proposal, nonce, bits }); + while (!Block.meetsProofOfWork(block.id, bits) && nonce < 1e6) { + nonce += 1; + block = new Block({ ...proposal, nonce, bits }); + } + await this.append(block); + return block; + } + const block = new Block(proposal); await this.append(block); - return block; } async generateBlocks (count = 1) { const blocks = []; - for (let i = 0; i < count; i++) { const block = await this.generateBlock(); blocks.push(block); } - return blocks; } async commit () { let changes = null; - if (this.observer) { changes = monitor.generate(this.observer); } - if (changes) { this.emit('changes', { type: 'StateChanges', data: changes }); } - const state = new Actor(this._state); return state.id; } - async verify (level = 4, depth = 6) { - this.log(`Verification Level ${level} running from -${depth}...`); - console.log('root:', this.root); - return (this['@id'] === this.root); - } - validate (chain) { let valid = false; for (let i = 0; i < chain.height; i++) { - let block = chain.blocks[i]; + void chain.blocks[i]; } return valid; } render () { console.log('[CHAIN]', '[RENDER]', this); - return `<Chain id="${this.id}" />`; + return `<Chain id="${this.id}" consensus="${this.consensusMode}" />`; } } +function fromBeaconMessages (messages, opts = {}) { + const chain = new Chain({ + consensus: opts.consensus || opts.seal || CONSENSUS_FEDERATION + }); + let parent = null; + let i = 0; + for (const m of messages || []) { + if (!m || typeof m !== 'object') continue; + i += 1; + const data = m.payload && typeof m.payload === 'object' + ? m.payload + : (m.data && typeof m.data === 'object' ? m.data : {}); + const id = m.id != null + ? String(m.id) + : entryDigest({ + id: '', + parent, + height: data.clock != null ? Number(data.clock) : i, + type: m.type || 'BEACON_EPOCH', + data, + author: null, + federationWitness: m.federationWitness || null + }).slice(0, 32); + const rec = { + id, + parent, + height: data.clock != null ? Number(data.clock) : i, + type: String(m.type || 'BEACON_EPOCH'), + data: JSON.parse(JSON.stringify(data)), + transactions: {}, + author: null, + signature: null, + signatures: [], + federationWitness: m.federationWitness + ? JSON.parse(JSON.stringify(m.federationWitness)) + : null, + merkleRoot: null, + nonce: 0, + bits: null, + timestamp: data.timestamp || null + }; + chain._state.blocks[id] = rec; + chain._state.ledger.push(id); + chain._state.consensus = id; + chain._state.content.blocks.push(id); + parent = id; + } + return chain; +} + +function toBeaconMessages (chain) { + if (!(chain instanceof Chain) || !chain._isPolicyChain()) return []; + return chain._state.ledger.map((id) => { + const e = chain._state.blocks[id]; + const row = { + type: e.type || 'BEACON_EPOCH', + payload: e.data, + id: e.id || null + }; + if (e.federationWitness) row.federationWitness = e.federationWitness; + return row; + }); +} + +Chain.CONSENSUS_POW = CONSENSUS_POW; +Chain.CONSENSUS_FEDERATION = CONSENSUS_FEDERATION; +Chain.CONSENSUS_GOSSIP = CONSENSUS_GOSSIP; +Chain.SEAL_BLOCK = SEAL_BLOCK; +Chain.SEAL_FEDERATION = SEAL_FEDERATION; +Chain.SEAL_GOSSIP = SEAL_GOSSIP; +Chain.eventId = eventId; +Chain.entryDigest = entryDigest; +Chain.signingStringForEntry = signingStringForEntry; +Chain.fromBeaconMessages = fromBeaconMessages; +Chain.toBeaconMessages = toBeaconMessages; + module.exports = Chain; +module.exports.eventId = eventId; +module.exports.entryDigest = entryDigest; +module.exports.signingStringForEntry = signingStringForEntry; +module.exports.fromBeaconMessages = fromBeaconMessages; +module.exports.toBeaconMessages = toBeaconMessages; +module.exports.CONSENSUS_POW = CONSENSUS_POW; +module.exports.CONSENSUS_FEDERATION = CONSENSUS_FEDERATION; +module.exports.CONSENSUS_GOSSIP = CONSENSUS_GOSSIP; +module.exports.SEAL_BLOCK = SEAL_BLOCK; +module.exports.SEAL_FEDERATION = SEAL_FEDERATION; +module.exports.SEAL_GOSSIP = SEAL_GOSSIP;
    @@ -363,14 +1085,18 @@

    Classes

    Global


    diff --git a/docs/types_channel.js.html b/docs/types_channel.js.html index 441578afc..8a39b3079 100644 --- a/docs/types_channel.js.html +++ b/docs/types_channel.js.html @@ -38,27 +38,24 @@

    Source: types/channel.js

    } = require('../constants'); const BN = require('bn.js'); -const Key = require('./key'); const Entity = require('./entity'); -const Scribe = require('./scribe'); +const State = require('./state'); const Secret = require('./secret'); // (legacy Consensus type removed) // const Layer = require('./layer'); /** - * The {@link Channel} is a encrypted connection with a member of your - * {@link Peer} group, with some amount of $BTC bonded and paid for each - * correctly-validated message. - * - * Channels in Fabric are powerful tools for application development, as they - * can empower users with income opportunities in exchange for delivering - * service to the network. + * @classdesc <strong>Payment / capacity channel</strong> between peers: balances (<code>incoming</code> / + * <code>outgoing</code>), counterparty handle, optional asset caps (<code>MAX_CHANNEL_VALUE</code>). Extends + * {@link State} → {@link Actor}. Wording below is product-oriented; + * wire safety still depends on the Lightning/Bitcoin services you attach, not this object alone. + * @class Channel + * @extends State */ -class Channel extends Scribe { +class Channel extends State { /** - * Creates a channel between two peers. - * of many transactions over time, to be settled on-chain later. + * Creates a channel between two peers (bidirectional by default; <code>settings.mode</code>, <code>settings.asset</code>, …). * @param {Object} [settings] Configuration for the channel. */ constructor (settings) { @@ -96,7 +93,6 @@

    Source: types/channel.js

    'provider': { enumerable: false }, 'settings': { enumerable: false }, // 'size': { enumerable: false }, - 'state': { enumerable: false }, }); this['@id'] = this.id; @@ -132,7 +128,7 @@

    Source: types/channel.js

    * @param {Number} amount Amount value to add to current outgoing balance. */ add (amount) { - const value = new BN(amount + ''); + void new BN(amount + ''); /* const layer = new Layer({ parents: [this._parent], uint256: value @@ -144,9 +140,13 @@

    Source: types/channel.js

    } commit () { - const commit = new Entity(this._state); - this.emit('commit', commit) - return commit; + super.commit(); + const snapshot = typeof structuredClone === 'function' + ? structuredClone(this._state) + : JSON.parse(JSON.stringify(this._state)); + const committed = new Entity(snapshot); + this.emit('commit', committed); + return committed; } /** @@ -215,14 +215,18 @@

    Classes

    Global


    diff --git a/docs/types_circuit.js.html b/docs/types_circuit.js.html index 9211b8e93..2f76edd52 100644 --- a/docs/types_circuit.js.html +++ b/docs/types_circuit.js.html @@ -128,7 +128,7 @@

    Source: types/circuit.js

    fromBristolFashion () { // Convert from Bristol Fashion format to internal circuit representation const lines = this.dot.split('\n'); - const [numGates, numWires, numInputWires, numOutputWires] = lines[0].split(' ').map(Number); + const [_numGates, _numWires, numInputWires, numOutputWires] = lines[0].split(' ').map(Number); this.gates = []; this.wires = []; @@ -161,7 +161,7 @@

    Source: types/circuit.js

    fromBristolFormat () { // Convert from Bristol Format to internal circuit representation const lines = this.dot.split('\n'); - const [numGates, numWires] = lines[0].split(' ').map(Number); + const [_numGates, _numWires] = lines[0].split(' ').map(Number); this.gates = []; this.wires = []; @@ -229,7 +229,7 @@

    Source: types/circuit.js

    scramble () { let key = crypto.randomBytes(32); let machine = new Machine({ seed: key }); - let seed = machine.sip(); + void machine.sip(); let gates = []; for (let i = 0; i < this._state.steps.length; i++) { @@ -354,14 +354,18 @@

    Classes

    Global


    diff --git a/docs/types_cli.js.html b/docs/types_cli.js.html deleted file mode 100644 index 2d421df7b..000000000 --- a/docs/types_cli.js.html +++ /dev/null @@ -1,3313 +0,0 @@ - - - - - - Source: types/cli.js · Docs - - - - - - - - - -
    -

    Source: types/cli.js

    - - - - -
    -
    -
    'use strict';
    -
    -// Constants
    -const {
    -  MAX_CHAT_MESSAGE_LENGTH,
    -  INPUT_HINT,
    -  BITCOIN_GENESIS
    -} = require('../constants');
    -
    -// Internal Dependencies
    -const os = require('os');
    -const path = require('path');
    -const fs = require('fs');
    -const EventEmitter = require('events').EventEmitter;
    -
    -// External Dependencies
    -const merge = require('lodash.merge');
    -const pointer = require('json-pointer'); // TODO: move uses to App
    -const monitor = require('fast-json-patch'); // TODO: move uses to App
    -
    -// Fabric Types
    -const Service = require('./service');
    -const { FabricShell } = Service;
    -const Peer = require('./peer');
    -const Actor = require('./actor');
    -const Message = require('./message');
    -const Hash256 = require('./hash256');
    -const Identity = require('./identity');
    -const Environment = require('./environment');
    -const Filesystem = require('./filesystem');
    -const Wallet = require('./wallet');
    -const Key = require('./key');
    -
    -// Functions
    -const truncateMiddle = require('../functions/truncateMiddle');
    -
    -// Services
    -const Bitcoin = require('../services/bitcoin');
    -const Lightning = require('../services/lightning');
    -
    -// UI dependencies
    -// TODO: use Jade to render pre-registered components
    -// ```jade
    -// fabric-application
    -//   fabric-box
    -//   fabric-row
    -//     fabric-log
    -//     fabric-list
    -//   fabric-input
    -// ```
    -const blessed = require('blessed');
    -
    -/**
    - * Provides a Command Line Interface (CLI) for interacting with
    - * the Fabric network using a terminal emulator.
    - */
    -class CLI extends FabricShell {
    -  /**
    -   * Create a terminal-based interface for a {@link User}.
    -   * @param {Object} [settings] Configuration values.
    -   * @param {Array} [settings.currencies] List of currencies to support.
    -   */
    -  constructor (settings = {}) {
    -    super(settings);
    -
    -    // Create and start environment to load configurations
    -    this.environment = new Environment();
    -    this.environment.start();
    -
    -    // Determine Bitcoin settings:
    -    // - Preserve discovered bitcoin.conf credentials/host hints.
    -    // - Keep regtest as the default execution target unless explicitly overridden.
    -    const bitcoinConfigFound = this.environment.bitcoinConfig && this.environment.bitcoinConfig.found;
    -    const bitcoinSettings = bitcoinConfigFound
    -      ? { ...this.environment.bitcoinSettings }
    -      : { ...(settings.bitcoin || {}) };
    -
    -    // Network priority:
    -    // 1) explicit CLI setting
    -    // 2) explicit nested bitcoin setting
    -    // 3) discovered bitcoin.conf setting
    -    // 4) regtest default for isolated local usage
    -    const explicitNetwork = settings.network || (settings.bitcoin && settings.bitcoin.network);
    -    if (explicitNetwork) {
    -      bitcoinSettings.network = explicitNetwork;
    -    } else {
    -      bitcoinSettings.network = 'regtest';
    -    }
    -
    -    const defaultRPCPortByNetwork = {
    -      mainnet: 8332,
    -      testnet: 18332,
    -      signet: 38332,
    -      regtest: 18443,
    -      testnet4: 48332
    -    };
    -
    -    const explicitRPCPort = settings.bitcoin && Object.prototype.hasOwnProperty.call(settings.bitcoin, 'rpcport');
    -    if (!explicitRPCPort) {
    -      bitcoinSettings.rpcport = defaultRPCPortByNetwork[bitcoinSettings.network] || 18443;
    -    }
    -
    -    // Explorer API fallback: hub.fabric.pub or custom URL
    -    const explorerUrl = process.env.FABRIC_EXPLORER_URL || (settings.bitcoin && settings.bitcoin.explorerBaseUrl);
    -    if (explorerUrl) bitcoinSettings.explorerBaseUrl = String(explorerUrl).replace(/\/+$/, '');
    -
    -    // SPV / remote node: use FABRIC_BITCOIN_NODE (host or host:port) for mainnet without local bitcoind
    -    const spvNode = process.env.FABRIC_BITCOIN_NODE || (settings.bitcoin && settings.bitcoin.spvNode);
    -    if (spvNode) {
    -      const defaultHost = '192.168.50.5';
    -      const [host, port] = String(spvNode).split(':');
    -      bitcoinSettings.host = (host && host.length > 1 && !/^1$|^true$/i.test(host)) ? host : defaultHost;
    -      if (port) bitcoinSettings.rpcport = parseInt(port, 10);
    -      bitcoinSettings.managed = false;
    -      if (!explicitNetwork) bitcoinSettings.network = 'mainnet';
    -    }
    -
    -    // Managed mode policy:
    -    // - Default to managed for regtest so local CLI usage is self-contained.
    -    // - Bitcoin service will still disable managed mode at runtime if it detects
    -    //   a reachable existing bitcoind.
    -    const explicitManaged = settings.bitcoin && Object.prototype.hasOwnProperty.call(settings.bitcoin, 'managed');
    -    const shouldAutoManageBitcoin = explicitManaged
    -      ? settings.bitcoin.managed
    -      : (bitcoinSettings.network === 'regtest');
    -
    -    // Assign Settings
    -    this.settings = merge({
    -      debug: true,
    -      ephemeral: false,
    -      listen: false,
    -      peering: true,
    -      render: true,
    -      services: [],
    -      network: bitcoinSettings.network,
    -      interval: 1000,
    -      port: 7777, // Default port
    -      bitcoin: merge({
    -        enable: bitcoinConfigFound || (settings.bitcoin && settings.bitcoin.enable), // Enable if config found OR explicitly enabled in settings
    -        mode: 'rpc', // Always use RPC mode for connections
    -        managed: shouldAutoManageBitcoin,
    -        host: 'localhost',
    -        port: 8443,
    -        rpcport: defaultRPCPortByNetwork[bitcoinSettings.network] || 18443,
    -        secure: false,
    -        // For regtest we want an isolated, local datadir so we never touch a
    -        // mainnet/testnet installation. Callers can still override this.
    -        datadir: (bitcoinSettings.network === 'regtest') ? './stores/bitcoin-regtest' : undefined
    -      }, bitcoinSettings), // Bitcoin settings from config/user take precedence
    -      lightning: {
    -        enable: false, // Disabled by default
    -        mode: 'socket',
    -        path: './stores/lightning-playnet/regtest/lightning-rpc'
    -      },
    -      storage: {
    -        path: `${process.env.HOME}/.fabric/console`
    -      },
    -      peers: [
    -        'localhost:7778' // Add our chat peer by default
    -      ],
    -      // Add key settings
    -      seed: null,
    -      xprv: null,
    -      passphrase: null
    -    }, settings);
    -
    -    // Populate RPC probe candidates from environment so Bitcoin service can
    -    // detect/reuse a local bitcoind without doing filesystem/OS probing itself.
    -    this.settings.bitcoin.rpcProbeCandidates = this.environment.getBitcoinRPCCandidates(this.settings.bitcoin);
    -
    -    // Keep Lightning pinned to the same Bitcoin RPC endpoint/credentials used by
    -    // the Bitcoin service so CLN startup checks don't drift.
    -    this.settings.lightning = this.settings.lightning || {};
    -    this.settings.lightning.bitcoin = merge({
    -      host: this.settings.bitcoin.host,
    -      rpcport: this.settings.bitcoin.rpcport,
    -      datadir: this.settings.bitcoin.datadir,
    -      rpcuser: this.settings.bitcoin.rpcuser || this.settings.bitcoin.username,
    -      rpcpassword: this.settings.bitcoin.rpcpassword || this.settings.bitcoin.password
    -    }, this.settings.lightning.bitcoin || {});
    -
    -    // Initialize key with proper settings
    -    this.key = new Key({
    -      seed: this.settings.seed,
    -      xprv: this.settings.xprv,
    -      passphrase: this.settings.passphrase,
    -      network: this.settings.network
    -    });
    -
    -    // Ensure key has required properties
    -    if (!this.key.private || !this.key.public || !this.key.sign) {
    -      // Generate new key if properties are missing
    -      this.key = new Key();
    -    }
    -
    -    // Properties
    -    this.screen = null;
    -    this.history = [];
    -
    -    this.aliases = {};
    -    this.channels = {};
    -    this.commands = {};
    -    this.contracts = {};
    -    this.documents = {};
    -    this.elements = {};
    -    this.peers = {};
    -    this.requests = {};
    -    this.services = {};
    -    this.connections = {};
    -
    -    // Add reconnection tracking
    -    this.reconnectTimers = {};
    -    this.reconnectAttempts = {};
    -
    -    // Add sync operation tracking to prevent concurrent operations
    -    this.syncInProgress = {
    -      chain: false,
    -      balance: false,
    -      contracts: false,
    -      unspent: false
    -    };
    -
    -    this.fs = new Filesystem(this.settings.storage);
    -
    -    // State
    -    this._state = {
    -      anchor: null,
    -      balances: {
    -        confirmed: 0,
    -        immature: 0,
    -        trusted: 0,
    -        unconfirmed: 0,
    -      },
    -      content: {
    -        actors: {},
    -        bitcoin: {
    -          best: null,
    -          genesis: BITCOIN_GENESIS
    -        },
    -        documents: {},
    -        messages: {}
    -      },
    -      contracts: {},
    -      clock: 0
    -    };
    -
    -    this.attachWallet();
    -    this.identity = new Identity(this.settings);
    -    this._loadPeer();
    -    this._loadBitcoin();
    -    this._loadLightning();
    -
    -    // Default to META mode for vi-style interaction
    -    this.mode = 'META';
    -
    -    // Chainable
    -    return this;
    -  }
    -
    -  assumeIdentity (key) {
    -    this.identity = new Identity(key);
    -    return this;
    -  }
    -
    -  attachWallet (wallet) {
    -    if (!wallet) wallet = new Wallet(this.settings);
    -
    -    this.wallet = wallet;
    -
    -    return this;
    -  }
    -
    -  flush () {
    -    this.fs.delete('STATE');
    -    return this;
    -  }
    -
    -  _loadPeer () {
    -    const file = this.fs.readFile('STATE');
    -    const state = (file) ? JSON.parse(file) : {};
    -
    -    // Create and assign Peer instance as the `node` property
    -    this.node = new Peer({
    -      debug: this.settings.debug,
    -      network: this.settings.network,
    -      interface: this.settings.interface,
    -      port: this.settings.port,
    -      peers: this.settings.peers,
    -      networking: true, // Ensure networking is always enabled
    -      state: state,
    -      upnp: this.settings.upnp,
    -      key: this.identity.settings
    -    });
    -
    -    if (this.settings.debug) {
    -      this.node.on('debug', (msg) => {
    -        this._appendDebug(`[PEER] ${msg}`);
    -      });
    -    }
    -
    -    return this;
    -  }
    -
    -  _loadBitcoin () {
    -    this.bitcoin = new Bitcoin(this.settings.bitcoin);
    -    return this;
    -  }
    -
    -  _loadLightning () {
    -    this.lightning = new Lightning(this.settings.lightning);
    -    return this;
    -  }
    -
    -  _getDefaultBitcoinDatadir () {
    -    switch (os.platform()) {
    -      case 'darwin': // macOS
    -        return path.join(os.homedir(), 'Library', 'Application Support', 'Bitcoin');
    -      case 'win32': // Windows
    -        return path.join(os.homedir(), 'AppData', 'Roaming', 'Bitcoin');
    -      default: // Linux and other Unix-like systems
    -        return path.join(os.homedir(), '.bitcoin');
    -    }
    -  }
    -
    -  async bootstrap () {
    -    try {
    -      await this.fs.start();
    -      return true;
    -    } catch (exception) {
    -      this._appendError(`Could not bootstrap: ${exception}`);
    -      return false;
    -    }
    -  }
    -
    -  async tick () {
    -    // Poll for new information with much more conservative throttling
    -    // Only sync Bitcoin data every 30 ticks (30 seconds) to avoid overwhelming RPC
    -    if (this._state.clock % 30 === 0 && this.settings.bitcoin.enable) {
    -      if (this.settings.debug) this._appendMessage(`Tick ${this._state.clock}: syncing Bitcoin displays...`);
    -      try {
    -        await this._syncChainDisplay();
    -        // Wait between RPC calls to avoid queue depth issues
    -        await new Promise(resolve => setTimeout(resolve, 1000));
    -        await this._syncBalance();
    -      } catch (exception) {
    -        this._appendError(`Sync failed during tick: ${exception.message}`);
    -      }
    -    }
    -
    -    // Sync contracts and unspent even less frequently
    -    if (this._state.clock % 60 === 0) {
    -      try {
    -        await this._syncContracts();
    -        await new Promise(resolve => setTimeout(resolve, 1000));
    -        await this._syncUnspent();
    -      } catch (exception) {
    -        this._appendError(`Contract/unspent sync failed: ${exception.message}`);
    -      }
    -    }
    -
    -    // Increment clock and commit
    -    this._state.clock++;
    -    this.commit();
    -  }
    -
    -  /**
    -   * Starts (and renders) the CLI.
    -   */
    -  async start () {
    -    // Register Internal Commands
    -    this._registerCommand('help', this._handleHelpRequest);
    -    this._registerCommand('quit', this._handleQuitRequest);
    -    this._registerCommand('exit', this._handleQuitRequest);
    -    this._registerCommand('clear', this._handleClearRequest);
    -    this._registerCommand('flush', this._handleFlushRequest);
    -    this._registerCommand('alias', this._handleAliasRequest);
    -    this._registerCommand('peers', this._handlePeerListRequest);
    -    this._registerCommand('rotate', this._handleRotateRequest);
    -    this._registerCommand('connect', this._handleConnectRequest);
    -    this._registerCommand('disconnect', this._handleDisconnectRequest);
    -    this._registerCommand('settings', this._handleSettingsRequest);
    -    this._registerCommand('inventory', this._handleInventoryRequest);
    -    this._registerCommand('channels', this._handleChannelRequest);
    -    this._registerCommand('identity', this._handleIdentityRequest);
    -    this._registerCommand('generate', this._handleGenerateRequest);
    -    this._registerCommand('wallet', this._handleWalletCommand);
    -    this._registerCommand('service', this._handleServiceCommand);
    -    this._registerCommand('publish', this._handlePublishCommand);
    -    this._registerCommand('request', this._handleRequestCommand);
    -    this._registerCommand('grant', this._handleGrantCommand);
    -    this._registerCommand('import', this._handleImportCommand);
    -    this._registerCommand('join', this._handleJoinRequest);
    -    this._registerCommand('sync', this._handleChainSyncRequest);
    -    this._registerCommand('send', this._handleSendRequest);
    -    this._registerCommand('fund', this._handleFundRequest);
    -    this._registerCommand('state', this._handleStateRequest);
    -    this._registerCommand('set', this._handleSetRequest);
    -    this._registerCommand('get', this._handleGetRequest);
    -
    -    // Contracts
    -    this._registerCommand('contracts', this._handleContractsRequest);
    -    this._registerCommand('subscribe', this._handleSubscribeRequest);
    -    this._registerCommand('create', this._handleCreateRequest);
    -    this._registerCommand('deploy', this._handleDeployRequest);
    -    this._registerCommand('accept', this._handleAcceptRequest);
    -
    -    // Service Commands
    -    this._registerCommand('bitcoin', this._handleBitcoinRequest);
    -    this._registerCommand('lightning', this._handleLightningRequest);
    -
    -    // Debug Commands
    -    this._registerCommand('syncui', this._handleSyncUIRequest);
    -    this._registerCommand('listelements', this._handleListElementsRequest);
    -    this._registerCommand('testrpc', this._handleTestRPCRequest);
    -    this._registerCommand('createwallet', this._handleCreateWalletRequest);
    -    this._registerCommand('loadwallet', this._handleLoadWalletRequest);
    -    this._registerCommand('listwallets', this._handleListWalletsRequest);
    -    this._registerCommand('bitcoinhelp', this._handleBitcoinHelpRequest);
    -
    -    // Blockchain explorer
    -    this._registerCommand('block', this._handleBlockExplorerRequest);
    -    this._registerCommand('tx', this._handleTxExplorerRequest);
    -    this._registerCommand('address', this._handleAddressExplorerRequest);
    -    this._registerCommand('explorer', this._handleExplorerHelpRequest);
    -
    -    // Services
    -    this._registerService('bitcoin', Bitcoin);
    -    this._registerService('lightning', Lightning);
    -
    -    await this.bootstrap();
    -
    -    if (this.settings.render) {
    -      // Render UI
    -      this.render();
    -    }
    -
    -    // Log the listening port and peer list when debugging is enabled
    -    if (this.settings.debug) {
    -      this._appendDebug(`[FABRIC:CLI] Listening on port: ${this.settings.port}`);
    -      this._appendDebug(`[FABRIC:CLI] Peer list: ${JSON.stringify(this.settings.peers)}`);
    -    }
    -
    -    // ## Bindings
    -    this.on('log', this._handleSourceLog.bind(this));
    -    this.on('debug', this._handleSourceDebug.bind(this));
    -    this.on('error', this._handleSourceError.bind(this));
    -    this.on('warning', this._handleSourceWarning.bind(this));
    -
    -    // ## P2P message handlers
    -    this.node.on('log', this._handlePeerLog.bind(this));
    -    this.node.on('ready', this._handleNodeReady.bind(this));
    -    this.node.on('debug', this._handlePeerDebug.bind(this));
    -    this.node.on('error', this._handlePeerError.bind(this));
    -    this.node.on('warning', this._handlePeerWarning.bind(this));
    -    this.node.on('message', this._handlePeerMessage.bind(this));
    -    this.node.on('changes', this._handlePeerChanges.bind(this));
    -    this.node.on('commit', this._handlePeerCommit.bind(this));
    -    this.node.on('state', this._handlePeerState.bind(this));
    -    this.node.on('chat', this._handlePeerChat.bind(this));
    -    this.node.on('upnp', this._handlePeerUPNP.bind(this));
    -    this.node.on('actorset', this._handleActorSet.bind(this));
    -    this.node.on('contractset', this._handleContractSet.bind(this));
    -    // this.node.on('peerset', this._handlePeerSet.bind(this));
    -
    -    // ## Raw Connections
    -    this.node.on('connection', this._handleConnection.bind(this));
    -    this.node.on('connections:open', this._handleConnectionOpen.bind(this));
    -    this.node.on('connections:close', this._handleConnectionClose.bind(this));
    -    this.node.on('connection:error', this._handleConnectionError.bind(this));
    -
    -    // ## Peer Events
    -    this.node.on('peer', this._handlePeer.bind(this));
    -    this.node.on('peer:candidate', this._handlePeerCandidate.bind(this));
    -    this.node.on('session:update', this._handleSessionUpdate.bind(this));
    -
    -    // ## Document Exchange
    -    this.node.on('DocumentPublish', this._handlePeerDocumentPublish.bind(this));
    -    this.node.on('DocumentRequest', this._handlePeerDocumentRequest.bind(this));
    -
    -    // ## Anchor handlers
    -    // ### Bitcoin
    -    if (this.settings.bitcoin && this.settings.bitcoin.enable) {
    -      if (!this.bitcoin) {
    -        throw new Error('Bitcoin service is not initialized. Check your settings and environment.');
    -      }
    -      this.bitcoin.on('debug', this._handleBitcoinDebug.bind(this));
    -      this.bitcoin.on('ready', this._handleBitcoinReady.bind(this));
    -      this.bitcoin.on('error', this._handleBitcoinError.bind(this));
    -      this.bitcoin.on('warning', this._handleBitcoinWarning.bind(this));
    -      this.bitcoin.on('message', this._handleBitcoinMessage.bind(this));
    -      this.bitcoin.on('log', this._handleBitcoinLog.bind(this));
    -      this.bitcoin.on('commit', this._handleBitcoinCommit.bind(this));
    -      this.bitcoin.on('sync', this._handleBitcoinSync.bind(this));
    -      this.bitcoin.on('block', this._handleBitcoinBlock.bind(this));
    -      this.bitcoin.on('transaction', this._handleBitcoinTransaction.bind(this));
    -    }
    -
    -    // #### Lightning
    -    if (this.settings.lightning && this.settings.lightning.enable) {
    -      this.lightning.on('debug', this._handleLightningDebug.bind(this));
    -      this.lightning.on('ready', this._handleLightningReady.bind(this));
    -      this.lightning.on('error', this._handleLightningError.bind(this));
    -      this.lightning.on('warning', this._handleLightningWarning.bind(this));
    -      this.lightning.on('message', this._handleLightningMessage.bind(this));
    -      this.lightning.on('log', this._handleLightningLog.bind(this));
    -      this.lightning.on('commit', this._handleLightningCommit.bind(this));
    -      this.lightning.on('sync', this._handleLightningSync.bind(this));
    -      // this.lightning.on('transaction', this._handleLightningTransaction.bind(this));
    -    }
    -
    -    // this.on('debug', this._appendDebug.bind(this));
    -
    -    // const events = this.trust(this.lightning);
    -
    -    // ## Start all services
    -    for (const [name, service] of Object.entries(this.services)) {
    -      // Skip when service name not found in settings
    -      if (!this.settings.services.includes(name)) continue;
    -      // Anchor services are started below with explicit ordering.
    -      if (name === 'bitcoin' || name === 'lightning') continue;
    -      this._appendDebug(`Service "${name}" is enabled.  Starting...`);
    -      this.trust(this.services[name], name);
    -
    -      try {
    -        await this.services[name].start();
    -        this._appendDebug(`The service named "${name}" has started!`);
    -      } catch (exception) {
    -        this._appendError(`The service named "${name}" could not start:\n${exception}`);
    -      }
    -    }
    -
    -    // ## Track state changes
    -    this.observer = monitor.observe(this._state.content);
    -
    -    // Bind remaining internals
    -    // TODO: enable
    -    // this.on('changes', this._handleChanges.bind(this));
    -
    -    // ## Start P2P node FIRST (so peer connections happen immediately)
    -    if (this.settings.peering) {
    -      if (this.settings.debug) {
    -        this._appendDebug(`[FABRIC:CLI] About to start node with peers: ${JSON.stringify(this.node.settings.peers)}`);
    -        this._appendDebug(`[FABRIC:CLI] About to start node with networking: ${this.node.settings.networking}`);
    -      }
    -      this._appendDebug('[FABRIC:CLI] Starting P2P node...');
    -      await this.node.start();
    -      this._appendDebug('[FABRIC:CLI] P2P node started');
    -    }
    -
    -    // ## Start Anchor Services (asynchronously so the TUI remains responsive)
    -    let bitcoinStartPromise = null;
    -
    -    // Start Bitcoin service first
    -    if (this.settings.bitcoin && this.settings.bitcoin.enable) {
    -      this._appendDebug('[FABRIC:CLI] Starting Bitcoin service (async)...');
    -      bitcoinStartPromise = this.bitcoin.start().then(() => {
    -        this._appendDebug('[FABRIC:CLI] Bitcoin service started');
    -        return true;
    -      }).catch((exception) => {
    -        this._appendError(`[FABRIC:CLI] Bitcoin service failed to start: ${exception.message || exception}`);
    -        throw exception;
    -      });
    -    }
    -
    -    // Start Lightning service only after Bitcoin startup settles.
    -    if (this.settings.lightning && this.settings.lightning.enable) {
    -      const startLightning = async () => {
    -        if (bitcoinStartPromise) {
    -          try {
    -            await bitcoinStartPromise;
    -          } catch (exception) {
    -            this._appendError('[FABRIC:CLI] Skipping Lightning startup because Bitcoin did not start successfully');
    -            return;
    -          }
    -        }
    -
    -        // If Bitcoin is in degraded mode (no reachable RPC), Lightning cannot start.
    -        if (this.bitcoin && this.bitcoin._rpcReady === false) {
    -          this._appendWarning('[FABRIC:CLI] Skipping Lightning startup because Bitcoin RPC is not reachable');
    -          return;
    -        }
    -
    -        // Refresh Lightning's Bitcoin RPC settings from the started Bitcoin service.
    -        // Bitcoin startup may normalize or auto-detect host/port/auth at runtime.
    -        if (this.bitcoin && this.lightning) {
    -          this.lightning.settings.bitcoin = merge({}, this.lightning.settings.bitcoin || {}, {
    -            host: this.bitcoin.settings.host,
    -            rpcport: this.bitcoin.settings.rpcport,
    -            datadir: this.bitcoin.settings.datadir,
    -            rpcuser: this.bitcoin.settings.rpcuser || this.bitcoin.settings.username,
    -            rpcpassword: this.bitcoin.settings.rpcpassword || this.bitcoin.settings.password
    -          });
    -        }
    -
    -        this._appendDebug('[FABRIC:CLI] Starting Lightning service (async)...');
    -        try {
    -          await this.lightning.start();
    -          this._appendDebug('[FABRIC:CLI] Lightning service started');
    -        } catch (exception) {
    -          this._appendError(`[FABRIC:CLI] Lightning service failed to start: ${exception.message || exception}`);
    -        }
    -      };
    -
    -      startLightning();
    -    }
    -
    -    // ## Attach Heartbeat
    -    this._heart = setInterval(this.tick.bind(this), this.settings.interval);
    -
    -    // ## Emit Ready
    -    this.status = 'READY';
    -    this.emit('ready');
    -
    -    // Chainable
    -    return this;
    -  }
    -
    -  /**
    -   * Disconnect all interfaces and exit the process.
    -   */
    -  async stop () {
    -    // Clear all reconnection timers
    -    for (const address in this.reconnectTimers) {
    -      clearTimeout(this.reconnectTimers[address]);
    -    }
    -
    -    this.reconnectTimers = {};
    -    this.reconnectAttempts = {};
    -
    -    // Clear any in-progress sync operations
    -    this.syncInProgress = {
    -      chain: false,
    -      balance: false,
    -      contracts: false,
    -      unspent: false
    -    };
    -
    -    await this.node.stop();
    -    return process.exit(0);
    -  }
    -
    -  get (path = '') {
    -    let result = null;
    -
    -    try {
    -      result = pointer.get(this._state.content, path);
    -    } catch (exception) {
    -      this._appendError(`Could not retrieve path "${path}": ${exception}`);
    -    }
    -
    -    return result;
    -  }
    -
    -  set (path, value) {
    -    if (!path) return this._appendError('Must provide a path.');
    -    if (!value) return this._appendError('Must provide a value.');
    -
    -    try {
    -      pointer.set(this._state, path, value);
    -    } catch (exception) {
    -      this._appendError(`Could not set path "${path}": ${exception}`);
    -    }
    -
    -    this.commit();
    -
    -    return this.get(path);
    -  }
    -
    -  commit () {
    -    ++this.clock;
    -
    -    this['@parent'] = this.id;
    -    this['@preimage'] = this.toString();
    -    this['@constructor'] = this.constructor;
    -
    -    let changes = null;
    -
    -    if (this.observer) {
    -      changes = monitor.generate(this.observer);
    -    }
    -
    -    this['@id'] = this.id;
    -
    -    if (changes && changes.length) {
    -      // this._appendMessage(`Changes: ${JSON.stringify(changes, null, '  ')}`);
    -
    -      this.emit('changes', changes);
    -      // this.emit('state', this['@state']);
    -      this.emit('message', {
    -        '@type': 'Transaction',
    -        '@data': {
    -          'changes': changes,
    -          'state': changes
    -        }
    -      });
    -    }
    -
    -    return this;
    -  }
    -
    -  trust (source, name = this.constructor.name) {
    -    if (!(source instanceof EventEmitter)) throw new Error('Source is not an EventEmitter.');
    -    const self = this;
    -
    -    return {
    -      _handleTrustedError: source.on('error', async function handleTrustedError (error) {
    -        self._appendMessage(`[SOURCE:${name.toUpperCase()}] ${error}`);
    -      }),
    -      _handleTrustedLog: source.on('log', async function handleTrustedLog (log) {
    -        self._appendMessage(`[SOURCE:${name.toUpperCase()}] ${log}`);
    -      }),
    -      _handleTrustedDebug: source.on('debug', async function handleTrustedDebug (log) {
    -        self._appendDebug(`[SOURCE:${name.toUpperCase()}] ${log}`);
    -      }),
    -      _handleTrustedReady: source.on('ready', async function handleTrustedReady (ready) {
    -        self._appendMessage(`[SOURCE:${name.toUpperCase()}] Ready! ${ready}`);
    -      })
    -    };
    -  }
    -
    -  async _appendMessage (msg) {
    -    const message = `[${(new Date()).toISOString()}] ${msg}`;
    -    if (this.settings.render && this.elements['messages']) {
    -      this.elements['messages'].log(message);
    -      if (this.screen) this.screen.render();
    -    } else {
    -      // When not rendering, send output through stdout once so callers can capture it.
    -      // Avoid duplicating blessed output here to keep the TUI stable.
    -      // eslint-disable-next-line no-console
    -      console.log(message);
    -    }
    -  }
    -
    -  async _appendDebug (msg) {
    -    this._appendMessage(`{green-fg}${msg}{/green-fg}`);
    -  }
    -
    -  async _appendWarning (msg) {
    -    this._appendMessage(`{yellow-fg}${msg}{/yellow-fg}`);
    -  }
    -
    -  async _appendError (msg) {
    -    this._appendMessage(`{red-fg}${msg}{/red-fg}`);
    -  }
    -
    -  async _handleActorSet (actorset) {
    -    this._appendDebug(`[ACTORSET] ${JSON.stringify(actorset, null, '  ')}`);
    -  }
    -
    -  async _handleContractSet (contractset) {
    -    this._appendDebug(`[CONTRACTSET] ${JSON.stringify(contractset, null, '  ')}`);
    -    this.contracts = contractset;
    -    this.commit();
    -  }
    -
    -  async _handlePeerState (state) {
    -    // this._appendDebug(`[STATE] ${JSON.stringify(state, null, '  ')}`);
    -    this.fs.publish('STATE', JSON.stringify(state, null, '  '));
    -  }
    -
    -  async _handleSourceLog (msg) {
    -    this._appendMessage(msg);
    -  }
    -
    -  async _handleSourceDebug (msg) {
    -    this._appendDebug(msg);
    -  }
    -
    -  async _handleSourceError (msg) {
    -    this._appendError(msg);
    -  }
    -
    -  async _handleSourceWarning (msg) {
    -    this._appendWarning(msg);
    -  }
    -
    -  async _handleChanges (changes) {
    -    this._appendMessage(`New Changes: ${JSON.stringify(changes, null, '  ')}`);
    -  }
    -
    -  async _handleAcceptRequest (params) {
    -    if (!params || !params[1]) {
    -      this._appendMessage(`You must provide a contract parameter.`);
    -      return false;
    -    }
    -
    -    const contract = this.contracts[params[1]];
    -    this._appendMessage(`{bold}Accepting{/bold}: ${params[1]} ${JSON.stringify(contract)}`);
    -    // TODO: sign
    -    return false;
    -  }
    -
    -  async _handleCreateRequest (params) {
    -    this._appendMessage(`{bold}Creating{/bold}: ${params[1]}`);
    -    const now = (new Date()).toISOString();
    -    const template = {
    -      created: now,
    -      main: JSON.stringify(async function main () { return {}; })
    -    };
    -
    -    const entity = new Actor(template);
    -    this.contracts[entity.id] = entity;
    -
    -    if (params[1]) this.aliases[params[1]] = entity.id;
    -
    -    this._appendDebug(`Created: ${entity.id}`);
    -    return false;
    -  }
    -
    -  async _handleContractsRequest (params) {
    -    this._appendMessage('{bold}Current Contracts{/bold}: ' + JSON.stringify(this.contracts, null, '  '));
    -    return false;
    -  }
    -
    -  async _handleSubscribeRequest (params) {
    -    this._appendMessage('{bold}Subscribing{/bold}: ' + JSON.stringify(params[1], null, '  '));
    -    return false;
    -  }
    -
    -  async _handleDeployRequest (params) {
    -    this._appendMessage(`{bold}Deploying{/bold}: ${params[1]}`);
    -    return false;
    -  }
    -
    -  async _handleStateRequest (params) {
    -    const value = await this.get(``);
    -    this._appendMessage('{bold}Current State{/bold}: ' + JSON.stringify(value, null, ' '));
    -    return false;
    -  }
    -
    -  async _handleGetRequest (params) {
    -    if (!params[1]) return this._appendError(`Must provide a document name.`);
    -    const value = await this.get(`/${params[1]}`);
    -    this._appendMessage('Value: ' + JSON.stringify(value, null, ' '));
    -    return false;
    -  }
    -
    -  async _handleSetRequest (params) {
    -    if (!params[1]) return this._appendError(`Must provide a document name.`);
    -    if (!params[2]) return this._appendError(`Must provide a document.`);
    -    const result = await this.set(`/${params[1]}`, params[2]);
    -    this._appendMessage('Result: ' + JSON.stringify(result, null, ' '));
    -    return false;
    -  }
    -
    -  async _handleFundRequest (params) {
    -    if (!params[1]) return this._appendError(`Must provide a channel ID.`);
    -    if (!params[2]) return this._appendError(`Must provide a funding amount.`);
    -    this._fundChannel(params[1], params[2]);
    -  }
    -
    -  async _handleChannelRequest (params) {
    -    const state = await this.lightning._syncOracleChannels();
    -    this._appendMessage(`{bold}Channels:{/bold} ${JSON.stringify(state.channels, null, '  ')}`);
    -  }
    -
    -  async _fundChannel (id, amount) {
    -    this._appendMessage(`Funding channel ${id} with ${amount} BTC...`);
    -    // TODO: create payment channel (@fabric/core/types/channel)
    -  }
    -
    -  /**
    -   * Creates a token for the target signer with a provided role and some optional data.
    -   * @param {Array} params Parameters array.
    -   */
    -  async _handleGrantCommand (params) {
    -    const target = params[1];
    -    const role = params[2];
    -    const extra = params[3];
    -
    -    this._appendMessage(`Creating token with role "${role}" for target: ${target}${(extra) ? ' (extra: ' + extra + ')' : ''}`);
    -  }
    -
    -  async _handleJoinRequest (params) {
    -    if (!params[1]) return this._appendError(`You must specify a sidechain.`);
    -  }
    -
    -  async _handleInventoryRequest (params) {
    -    this._appendMessage(`{bold}Inventory:{/bold} ${JSON.stringify(this.documents, null, '  ')}`);
    -  }
    -
    -  async _handleImportCommand (params) {
    -    if (!params[1]) return this._appendError(`You must provide a file to import.`);
    -    if (!fs.existsSync(params[1])) return this._appendError(`File does not exist: ${params[1]}`);
    -    const content = fs.readFileSync(params[1]);
    -    const actor = new Actor(content);
    -    this._appendMessage(`File contents (${content.length} bytes):\n---${content}\n---\nDocument ID: ${actor.id}`);
    -    this.documents[actor.id] = content;
    -    this._state.content.documents[actor.id] = content.toString('hex');
    -  }
    -
    -  async _handlePublishCommand (params) {
    -    if (!params[1]) return this._appendError(`You must specify the file to publish.`);
    -    if (!params[2]) return this._appendError(`You must specify the rate to pay.`);
    -    if (!this.documents[params[1]]) return this._appendError(`This file does not exist in the local library.`);
    -
    -    this.fs.touchDir(`documents`);
    -    this.fs.publish(`${params[1]}`, this.documents[params[1]]);
    -    this.node._publishDocument(params[1], this.documents[params[1]].toString('utf8'));
    -  }
    -
    -  async _handleRequestCommand (params) {
    -    if (!params[1]) return this._appendError(`You must specify the file to request.`);
    -    if (!params[2]) return this._appendError(`You must specify the rate to pay.`);
    -    const message = Message.fromVector(['DocumentRequest', {
    -      document: params[1]
    -    }]);
    -    this.node.broadcast(message);
    -  }
    -
    -  async _handleBitcoinMessage (message) {
    -    switch (message['@type']) {
    -      case 'CollectionSnapshot':
    -        break;
    -      default:
    -        this._appendMessage(`Bitcoin service emitted message: ${JSON.stringify(message)}`);
    -        break;
    -    }
    -  }
    -
    -  async _handleBitcoinLog (log) {
    -    this._appendMessage(`[SERVICES:BITCOIN] ${log}`);
    -  }
    -
    -  async _handleBitcoinCommit (commit) {
    -    // this._appendMessage(`Bitcoin service emitted commit: ${JSON.stringify(commit)}`);
    -  }
    -
    -  async _handleBitcoinSync (sync) {
    -    this._appendMessage(`Bitcoin service emitted sync: ${JSON.stringify(sync)}`);
    -    this._state.content.bitcoin.best = sync.best;
    -    this.commit();
    -  }
    -
    -  async _handleBitcoinBlock (block) {
    -    // this._appendMessage(`Bitcoin service emitted block ${JSON.stringify(block)}, chain height now: ${this.bitcoin.height}`);
    -    // await this.bitcoin._syncChainInfoOverRPC();
    -    this._syncChainDisplay();
    -    // const message = Message.fromVector(['BlockCandidate', block.raw]);
    -    // this.node.relayFrom(this.node.id, message);
    -  }
    -
    -  async _handleBitcoinTransaction (transaction) {
    -    this._appendMessage(`Bitcoin service emitted transaction: ${JSON.stringify(transaction)}`);
    -  }
    -
    -  async _handleBitcoinDebug (...msg) {
    -    this._appendDebug(msg);
    -  }
    -
    -  async _handleBitcoinError (...msg) {
    -    this._appendError(msg);
    -  }
    -
    -  async _handleBitcoinWarning (...msg) {
    -    this._appendWarning(msg);
    -  }
    -
    -  async _handleBitcoinReady (bitcoin) {
    -    this._appendMessage(`Bitcoin ready: ${JSON.stringify(bitcoin)}`);
    -    this._appendMessage(`Immediately updating header displays...`);
    -    // Immediately update displays when Bitcoin becomes ready
    -    try {
    -      await this._syncChainDisplay();
    -      await this._syncBalance();
    -      this._appendMessage(`Header displays updated successfully.`);
    -    } catch (exception) {
    -      this._appendError(`Failed to update displays: ${exception.message}`);
    -    }
    -  }
    -
    -  async _handleConnectionOpen (msg) {
    -    this._appendMessage(`Node emitted "connections:open" event: ${JSON.stringify(msg)}`);
    -
    -    // Reset reconnection attempts for this address
    -    const address = msg.address || msg.name;
    -    if (this.reconnectTimers[address]) {
    -      clearTimeout(this.reconnectTimers[address]);
    -      delete this.reconnectTimers[address];
    -    }
    -
    -    delete this.reconnectAttempts[address];
    -
    -    // Mark peer as connected
    -    for (const id in this.peers) {
    -      const peer = this.peers[id];
    -      if (peer.address === address) {
    -        peer.status = 'connected';
    -        delete peer.disconnectedAt;
    -        this._appendMessage(`Peer ${address} successfully reconnected`);
    -        break;
    -      }
    -    }
    -
    -    await this._handleConnection(msg);
    -
    -    this._syncConnectionList();
    -    this._syncPeerList();
    -  }
    -
    -  async _handleConnectionClose (msg) {
    -    this._appendMessage(`Node emitted "connections:close" event: ${JSON.stringify(msg, null, '  ')}`);
    -
    -    // Mark peers as disconnected instead of deleting them
    -    for (const id in this.peers) {
    -      const peer = this.peers[id];
    -      if (peer.address === msg.name) {
    -        this._appendMessage(`Address matches. Marking peer as disconnected.`);
    -        peer.status = 'disconnected';
    -        peer.disconnectedAt = Date.now();
    -
    -        // Start reconnection process
    -        this._scheduleReconnect(msg.name);
    -      }
    -    }
    -
    -    // Remove connections but keep peer records
    -    for (const id in this.connections) {
    -      const connections = this.connections[id];
    -      if (connections.address === msg.address) {
    -        delete this.connections[id];
    -      }
    -    }
    -
    -    this._syncPeerList();
    -    this._syncConnectionList();
    -  }
    -
    -  async _handleConnectionError (msg) {
    -    this._appendWarning(`Node emitted "connection:error" event: ${JSON.stringify(msg)}`);
    -  }
    -
    -  _scheduleReconnect (address) {
    -    // Clear any existing timer for this address
    -    if (this.reconnectTimers[address]) {
    -      clearTimeout(this.reconnectTimers[address]);
    -    }
    -
    -    // Initialize or increment attempt counter
    -    if (!this.reconnectAttempts[address]) {
    -      this.reconnectAttempts[address] = 0;
    -    }
    -    this.reconnectAttempts[address]++;
    -
    -    // Calculate backoff delay: 1s, 2s, 4s, 8s, 16s, 32s, 60s (max)
    -    const baseDelay = 1000; // 1 second
    -    const maxDelay = 60000; // 60 seconds
    -    const attempt = this.reconnectAttempts[address];
    -    const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
    -
    -    this._appendMessage(`Scheduling reconnection to ${address} in ${delay/1000}s (attempt ${attempt})`);
    -
    -    this.reconnectTimers[address] = setTimeout(() => {
    -      this._attemptReconnect(address);
    -    }, delay);
    -  }
    -
    -  _attemptReconnect (address) {
    -    this._appendMessage(`Attempting to reconnect to ${address}`);
    -
    -    // Mark peer as connecting
    -    for (const id in this.peers) {
    -      const peer = this.peers[id];
    -      if (peer.address === address) {
    -        peer.status = 'connecting';
    -        break;
    -      }
    -    }
    -
    -    // Update display
    -    this._syncPeerList();
    -    this.screen.render();
    -
    -    // Attempt to connect
    -    try {
    -      this.node._connect(address);
    -    } catch (error) {
    -      this._appendError(`Failed to reconnect to ${address}: ${error}`);
    -
    -      // Schedule another attempt
    -      this._scheduleReconnect(address);
    -    }
    -  }
    -
    -  async _handleConnection (connection) {
    -    if (!connection.id) {
    -      // TODO: exit function here
    -      this._appendWarning('Peer did not send an ID.  Event received: ' + JSON.stringify(connection));
    -    }
    -
    -    // TODO: use @fabric/core/types/channel
    -    const channel = {
    -      id: Hash256.digest(`${this.node.id}:${connection.id}`),
    -      counterparty: connection.id
    -    };
    -
    -    if (!this.connections[connection.id]) {
    -      this.connections[connection.id] = connection;
    -      this.emit('connection', connection);
    -    }
    -
    -    /* if (!this.channels[channel.id]) {
    -      this.channels[channel.id] = channel;
    -    } */
    -
    -    this._syncConnectionList();
    -    this.screen.render();
    -  }
    -
    -  async _handleLightningCommit (commit) {
    -    // this._appendDebug(`Lightning service emitted commit: ${JSON.stringify(commit)}`);
    -    const data = this.tableDataFor(Object.values(commit.object.state.channels), [
    -      'id',
    -      'channel_id',
    -      'funding_txid'
    -    ]);
    -
    -    this.elements['channellist'].setData(data);
    -  }
    -
    -  async _handleLightningDebug (...msg) {
    -    this._appendError(`[SERVICES:LIGHTNING] debug: ${msg}`);
    -  }
    -
    -  async _handleLightningError (...msg) {
    -    this._appendError(`[SERVICES:LIGHTNING] error: ${msg}`);
    -  }
    -
    -  async _handleLightningWarning (...msg) {
    -    this._appendWarning(`[SERVICES:LIGHTNING] warning: ${msg}`);
    -  }
    -
    -  async _handleLightningLog (...msg) {
    -    this._appendMessage(`[SERVICES:LIGHTNING] log: ${msg}`);
    -  }
    -
    -  async _handleLightningMessage (...msg) {
    -    this._appendMessage(`[SERVICES:LIGHTNING] message: ${msg}`);
    -  }
    -
    -  async _handleLightningReady (lightning) {
    -    this._appendMessage(`[SERVICES:LIGHTNING] ready: ${JSON.stringify(lightning, null, '  ')}`);
    -  }
    -
    -  async _handleLightningSync (sync) {
    -    this._appendDebug(`[SERVICES:LIGHTNING] sync: ${JSON.stringify(sync, null, '  ')}`);
    -  }
    -
    -  async _handlePeer (peer) {
    -    const self = this;
    -    // console.log('[SCRIPTS:CHAT]', 'Peer emitted by node:', peer);
    -
    -    if (!peer.id) {
    -      self._appendMessage('Peer did not send an ID.  Event received: ' + JSON.stringify(peer));
    -      if (self.settings.debug) self._appendDebug(`[DEBUG] Skipping peer registration: missing id in peer object: ${JSON.stringify(peer)}`);
    -      return;
    -    }
    -    if (self.settings.debug) self._appendDebug(`[DEBUG] Registering peer with id: ${peer.id}`);
    -
    -    // TODO: use @fabric/core/types/channel
    -    const channel = {
    -      id: Hash256.digest(`${self.node.id}:${peer.id}`),
    -      counterparty: peer.id
    -    };
    -
    -    if (!self.peers[peer.id]) {
    -      // Mark new peers as connected by default
    -      peer.status = 'connected';
    -      self.peers[peer.id] = peer;
    -      self.emit('peer', peer);
    -    } else {
    -      // Update existing peer status to connected
    -      self.peers[peer.id].status = 'connected';
    -      delete self.peers[peer.id].disconnectedAt;
    -    }
    -
    -    if (!self.channels[channel.id]) {
    -      self.channels[channel.id] = channel;
    -    }
    -
    -    self._syncPeerList();
    -    self.screen.render();
    -  }
    -
    -  async _handlePeerDocumentPublish (message) {
    -    this._appendMessage('Peer requested document publish: ' + JSON.stringify(message));
    -  }
    -
    -  async _handlePeerDocumentRequest (message) {
    -    this._appendMessage('Peer requested document delivery: ' + JSON.stringify(message));
    -  }
    -
    -  async _handlePeerCandidate (peer) {
    -    const self = this;
    -    self._appendMessage('Local node emitted "peer:candidate" event: ' + JSON.stringify(peer));
    -    self.screen.render();
    -  }
    -
    -  async _handleNodeReady (node) {
    -    if (this.settings.render && this.elements && this.elements['identityString']) {
    -      this.elements['identityString'].setContent(node.id);
    -      this.screen.render();
    -    }
    -
    -    this.emit('identity', {
    -      id: node.id,
    -      pubkey: node.pubkey
    -    });
    -  }
    -
    -  async _handlePeerDebug (message) {
    -    this._appendDebug(`[NODE] ${message}`);
    -  }
    -
    -  async _handlePeerError (message) {
    -    this._appendError(`[NODE] ${message}`);
    -  }
    -
    -  async _handlePeerWarning (message) {
    -    this._appendWarning(`[NODE] ${message}`);
    -  }
    -
    -  async _handlePeerLog (message) {
    -    this._appendMessage(`[NODE] ${message}`);
    -  }
    -
    -  async _handlePeerChanges (changes) {
    -    // this._appendDebug(`[NODE] [CHANGES] ${JSON.stringify(changes)}`);
    -    this._applyChanges(changes);
    -    this.commit();
    -  }
    -
    -  async _handlePeerCommit (commit) {
    -    // this._appendDebug(`[NODE] [COMMIT] ${JSON.stringify(commit)}`);
    -  }
    -
    -  async _handlePeerChat (chat) {
    -    const truncatedId = truncateMiddle(chat.actor.username || chat.actor.id, 10, '…', 5);
    -    this._appendMessage(`[@${truncatedId}]: ${chat.object.content}`);
    -  }
    -
    -  async _handlePeerUPNP (upnp) {
    -    this._appendDebug(`[UPNP] ${JSON.stringify(upnp)}`);
    -  }
    -
    -  async _handlePeerSet (peerset) {
    -    this._appendDebug(`[PEERSET] ${JSON.stringify(peerset, null, '  ')}`);
    -  }
    -
    -  async _handlePeerMessage (message) {
    -    switch (message.type) {
    -      case 'ChatMessage':
    -        try {
    -          const parsed = JSON.parse(message.data);
    -          const truncatedId = truncateMiddle(parsed.actor.username || parsed.actor, 10, '…', 5);
    -          this._appendMessage(`[@${truncatedId}]: ${parsed.object.content}`);
    -        } catch (exception) {
    -          this._appendError(`Could not parse <ChatMessage> data (should be JSON): ${message.data}`);
    -        }
    -        break;
    -      case 'BlockCandidate':
    -        this._appendMessage(`Received Candidate Block from peer: <${message.type}> ${message.data}`);
    -        this.bitcoin.append(message.data);
    -        break;
    -      default:
    -        if (!message.type && !message.data) {
    -          this._appendMessage(`Local "message" event: ${message}`);
    -        } else {
    -          this._appendMessage(`Local "message" event: <${message.type}> ${message.data}`);
    -        }
    -        break;
    -    }
    -  }
    -
    -  async _handleSessionUpdate (session) {
    -    this._appendMessage(`Local session update: ${JSON.stringify(session, null, '  ')}`);
    -  }
    -
    -  async _handleSocketData (data) {
    -    this._appendMessage(`Local "socket:data" event: ${JSON.stringify(data)}`);
    -  }
    -
    -  async _handlePromptEnterKey (ch, key) {
    -    this.elements['prompt'].historyIndex = this.history.length;
    -    this.elements['form'].submit();
    -    this.elements['prompt'].clearValue();
    -    this.elements['prompt'].readInput();
    -  }
    -
    -  async _handlePromptUpKey (ch, key) {
    -    const index = this.elements['prompt'].historyIndex;
    -    if (index > 0) this.elements['prompt'].historyIndex--;
    -    this.elements['prompt'].setValue(this.history[index]);
    -    this.screen.render();
    -  }
    -
    -  async _handlePromptDownKey (ch, key) {
    -    const index = ++this.elements['prompt'].historyIndex;
    -
    -    if (index < this.history.length) {
    -      this.elements['prompt'].setValue(this.history[index]);
    -    } else {
    -      this.elements['prompt'].historyIndex = this.history.length - 1;
    -      this.elements['prompt'].setValue('');
    -    }
    -
    -    this.screen.render();
    -  }
    -
    -  async _handleGenerateRequest (params) {
    -    if (!params[1]) params[1] = 1;
    -    const count = params[1];
    -    const address = await this.bitcoin.getUnusedAddress();
    -    this._appendMessage(`Generating ${count} blocks to address: ${address}`);
    -    this.bitcoin.generateBlocks(count, address);
    -    return false;
    -  }
    -
    -  async _handleUnspentRequest (params) {
    -    await this._syncUnspent();
    -    this._appendMessage(`{bold}Unspent:{/bold} ${JSON.stringify(this._state.unspent, null, '  ')}`);
    -  }
    -
    -  _bindKeys () {
    -    const self = this;
    -
    -    // Exit
    -    self.screen.key(['C-c'], self.stop.bind(self));
    -
    -    // Text Input
    -    self.screen.key(['i'], self.focusInput.bind(self));
    -
    -    // TODO: debug with @melnx
    -    // self.elements['prompt'].on('blur', self.defocusInput.bind(self));
    -
    -    self.elements['prompt'].key(['enter'], self._handlePromptEnterKey.bind(self));
    -    self.elements['prompt'].key(['up'], self._handlePromptUpKey.bind(self));
    -    self.elements['prompt'].key(['down'], self._handlePromptDownKey.bind(self));
    -
    -    // Add ESC key binding to exit INSERT mode and set modeline to META
    -    self.elements['prompt'].key(['escape'], function () {
    -      self.defocusInput();
    -    });
    -
    -    // Also handle blur event to reset modeline
    -    self.elements['prompt'].on('blur', function () {
    -      self.defocusInput();
    -    });
    -
    -    return true;
    -  }
    -
    -  _sendToAllServices (message) {
    -    for (const [name, service] of Object.entries(this.services)) {
    -      if (this.settings.services.includes(name)) {
    -        service._send(message);
    -      }
    -    }
    -  }
    -
    -  _handleFormSubmit (data) {
    -    const self = this;
    -    const content = data.input;
    -
    -    if (!content) return self._appendMessage('No message provided.');
    -    if (content.length > MAX_CHAT_MESSAGE_LENGTH) return self._appendMessage(`Message exceeds maximum length (${MAX_CHAT_MESSAGE_LENGTH}).`);
    -
    -    // Modify history
    -    self.history.push(data.input);
    -
    -    // Send as Chat Message if no handler registered
    -    if (!self._processInput(data.input)) {
    -      // Describe the activity for use in P2P message
    -      const msg = {
    -        type: 'P2P_CHAT_MESSAGE',
    -        actor: {
    -          id: self.node.id
    -        },
    -        object: {
    -          created: Date.now(),
    -          content: content
    -        },
    -        target: '/messages'
    -      };
    -
    -      let message = Message.fromVector(['ChatMessage', JSON.stringify(msg)]);
    -      message = message.signWithKey(this.key);
    -
    -      self.setPane('messages');
    -
    -      // Log own message
    -      self._handlePeerChat(msg);
    -
    -      // Relay to peers
    -      self.node.relayFrom(self.node.id, message);
    -
    -      // Notify services
    -      self._sendToAllServices(msg);
    -    }
    -
    -    self.elements['form'].reset();
    -    self.screen.render();
    -  }
    -
    -  _handleQuitRequest () {
    -    this._appendMessage('Exiting...');
    -    this.stop();
    -    return false;
    -  }
    -
    -  _handleAliasRequest (params) {
    -    if (!params) return false;
    -    if (!params[1]) {
    -      this._appendError('No alias provided.');
    -      return false;
    -    }
    -
    -    this.node._announceAlias(params[1]);
    -
    -    return false;
    -  }
    -
    -  _handleClearRequest () {
    -    this.elements['messages'].setContent('');
    -    return false;
    -  }
    -
    -  _handleFlushRequest () {
    -    this.flush();
    -    this._appendMessage('Fabric store flushed!');
    -    this.stop();
    -    return false;
    -  }
    -
    -  _handlePeerListRequest (params) {
    -    this._appendMessage('Peers: ' + JSON.stringify(this.peers, null, ' '));
    -    return false;
    -  }
    -
    -  _handleConnectRequest (params) {
    -    if (!params[1]) return this._appendMessage('You must specify an address to connect to.');
    -    const address = params[1];
    -    this._appendMessage('Connect request: ' + JSON.stringify(params));
    -    this.node._connect(address);
    -    return false;
    -  }
    -
    -  _handleDisconnectRequest (params) {
    -    if (!params[1]) return this._appendMessage('You must specify an peer to disconnect from.');
    -    const id = params[1];
    -    this._appendMessage('Disconnect request: ' + JSON.stringify(params));
    -    this.node._disconnect(id);
    -    return false;
    -  }
    -
    -  _handleChainSyncRequest () {
    -    this._appendMessage(`Sync starting for chain...`);
    -
    -    // TODO: test this on testnet / mainnet
    -    this.bitcoin.fullnode.startSync();
    -
    -    const message = Message.fromVector(['ChainSyncRequest', JSON.stringify({
    -      tip: this.bitcoin.fullnode.chain.tip
    -    })]);
    -    this.node.relayFrom(this.node.id, message);
    -
    -    return false;
    -  }
    -
    -  async spend (to, amount) {
    -    let tx = null;
    -
    -    try {
    -      tx = await this.bitcoin._makeRPCRequest('sendtoaddress', [to, amount]);
    -    } catch (exception) {
    -      this._appendError(`Could not create transaction: ${JSON.stringify(exception)}`);
    -    }
    -
    -    return tx;
    -  }
    -
    -  async _handleBitcoinRequest (params) {
    -    if (!params[1]) return this._appendError('You must specify a method.');
    -    try {
    -      const result = await this.bitcoin._makeRPCRequest(params[1], params.slice(2));
    -      this._appendMessage(`[BITCOIN] ${params[1]}(${params.slice(2)}) ${JSON.stringify(result)}`);
    -    } catch (exception) {
    -      this._appendError(`[BITCOIN] Could not handle request: ${JSON.stringify(exception)}`);
    -    }
    -  }
    -
    -  async _handleLightningRequest (params) {
    -    if (!params[1]) return this._appendError('You must specify a method.');
    -    try {
    -      const result = await this.lightning._makeRPCRequest(params[1], params.slice(2));
    -      this._appendMessage(`[LIGHTNING] ${params[1]}(${params.slice(2)}) ${JSON.stringify(result)}`);
    -    } catch (exception) {
    -      this._appendError(`[LIGHTNING] Could not handle request: ${JSON.stringify(exception)}`);
    -    }
    -  }
    -
    -  async _handleSyncUIRequest (params) {
    -    this._appendMessage(`Manually syncing UI displays...`);
    -    await this._syncChainDisplay();
    -    await this._syncBalance();
    -    this._appendMessage(`UI sync complete.`);
    -  }
    -
    -  async _handleListElementsRequest (params) {
    -    if (this.elements) {
    -      const elementNames = Object.keys(this.elements);
    -      this._appendMessage(`Available UI elements: ${elementNames.join(', ')}`);
    -      this._appendMessage(`Total elements: ${elementNames.length}`);
    -    } else {
    -      this._appendMessage(`No elements object found`);
    -    }
    -  }
    -
    -  async _handleTestRPCRequest (params) {
    -    if (!this.bitcoin || !this.bitcoin._makeRPCRequest) {
    -      this._appendError(`Bitcoin service not available`);
    -      return;
    -    }
    -
    -    this._appendMessage(`Testing Bitcoin RPC connectivity...`);
    -
    -    // Test basic blockchain info
    -    try {
    -      const chainInfo = await this.bitcoin._makeRPCRequest('getblockchaininfo');
    -      this._appendMessage(`✓ getblockchaininfo: chain=${chainInfo.chain}, blocks=${chainInfo.blocks}`);
    -    } catch (error) {
    -      this._appendError(`✗ getblockchaininfo failed: ${error.message}`);
    -    }
    -
    -    // Test block count
    -    try {
    -      const height = await this.bitcoin._makeRPCRequest('getblockcount');
    -      this._appendMessage(`✓ getblockcount: ${height}`);
    -    } catch (error) {
    -      this._appendError(`✗ getblockcount failed: ${error.message}`);
    -    }
    -
    -    // Test wallet list
    -    try {
    -      const wallets = await this.bitcoin._makeRPCRequest('listwallets');
    -      this._appendMessage(`✓ listwallets: ${wallets.length} wallets (${wallets.join(', ')})`);
    -
    -      if (wallets.length > 0) {
    -        // Test balance if wallet exists - use first loaded wallet
    -        try {
    -          const balance = await this.bitcoin._makeWalletRequest('getbalance', [], wallets[0]);
    -          this._appendMessage(`✓ getbalance: ${balance} BTC (wallet: ${wallets[0]})`);
    -        } catch (balanceError) {
    -          this._appendError(`✗ getbalance failed: ${balanceError.message}`);
    -        }
    -      } else {
    -        this._appendMessage(`ℹ No wallets loaded - balance calls will fail`);
    -      }
    -    } catch (error) {
    -      this._appendError(`✗ listwallets failed: ${error.message}`);
    -    }
    -
    -    this._appendMessage(`RPC test complete.`);
    -  }
    -
    -  async _handleCreateWalletRequest (params) {
    -    if (!this.bitcoin || !this.bitcoin._makeRPCRequest) {
    -      this._appendError(`Bitcoin service not available`);
    -      return;
    -    }
    -
    -    const walletName = params[1] || 'fabric-wallet';
    -    this._appendMessage(`Creating wallet: ${walletName}...`);
    -
    -    try {
    -      // Check if wallet already exists
    -      const existingWallets = await this.bitcoin._makeRPCRequest('listwallets');
    -      if (existingWallets.includes(walletName)) {
    -        this._appendMessage(`Wallet ${walletName} already exists and is loaded`);
    -        return;
    -      }
    -
    -      // Create new wallet
    -      const result = await this.bitcoin._makeRPCRequest('createwallet', [walletName]);
    -      this._appendMessage(`✓ Created wallet: ${result.name}`);
    -
    -      // Test the new wallet
    -      const balance = await this.bitcoin._makeWalletRequest('getbalance', [], walletName);
    -      this._appendMessage(`✓ New wallet balance: ${balance} BTC`);
    -
    -      // Trigger UI update
    -      await this._syncBalance();
    -
    -    } catch (error) {
    -      this._appendError(`Failed to create wallet: ${error.message}`);
    -
    -      // Try to load existing wallet instead
    -      if (error.message && error.message.includes('already exists')) {
    -        try {
    -          await this.bitcoin._makeRPCRequest('loadwallet', [walletName]);
    -          this._appendMessage(`✓ Loaded existing wallet: ${walletName}`);
    -          await this._syncBalance();
    -        } catch (loadError) {
    -          this._appendError(`Could not load wallet: ${loadError.message}`);
    -        }
    -      }
    -    }
    -  }
    -
    -  async _handleLoadWalletRequest (params) {
    -    if (!this.bitcoin || !this.bitcoin._makeRPCRequest) {
    -      this._appendError(`Bitcoin service not available`);
    -      return;
    -    }
    -
    -    if (!params[1]) {
    -      this._appendError(`Usage: loadwallet <wallet_name>`);
    -      this._appendMessage(`Example: loadwallet my-wallet`);
    -      return;
    -    }
    -
    -    const walletName = params[1];
    -    this._appendMessage(`Loading wallet: ${walletName}...`);
    -
    -    try {
    -      // Check if wallet is already loaded
    -      const existingWallets = await this.bitcoin._makeRPCRequest('listwallets');
    -      if (existingWallets.includes(walletName)) {
    -        this._appendMessage(`Wallet ${walletName} is already loaded`);
    -
    -        // Still test the balance and update UI
    -        try {
    -          const balance = await this.bitcoin._makeWalletRequest('getbalance', [], walletName);
    -          this._appendMessage(`Current balance: ${balance} BTC`);
    -          await this._syncBalance();
    -        } catch (balanceError) {
    -          this._appendError(`Could not get balance: ${balanceError.message}`);
    -        }
    -        return;
    -      }
    -
    -      // Load the wallet
    -      const result = await this.bitcoin._makeRPCRequest('loadwallet', [walletName]);
    -      this._appendMessage(`✓ Loaded wallet: ${result.name}`);
    -
    -      // Test the loaded wallet
    -      const balance = await this.bitcoin._makeWalletRequest('getbalance', [], walletName);
    -      this._appendMessage(`✓ Wallet balance: ${balance} BTC`);
    -
    -      // Trigger UI update
    -      await this._syncBalance();
    -      this._appendMessage(`UI updated with wallet data`);
    -
    -    } catch (error) {
    -      if (error.message && error.message.includes('not found')) {
    -        this._appendError(`Wallet '${walletName}' not found`);
    -        this._appendMessage(`Try: createwallet ${walletName}`);
    -      } else {
    -        this._appendError(`Failed to load wallet: ${error.message}`);
    -      }
    -    }
    -  }
    -
    -  async _handleListWalletsRequest (params) {
    -    if (!this.bitcoin || !this.bitcoin._makeRPCRequest) {
    -      this._appendError(`Bitcoin service not available`);
    -      return;
    -    }
    -
    -    this._appendMessage(`Checking wallet status...`);
    -
    -    try {
    -      // Get currently loaded wallets
    -      const loadedWallets = await this.bitcoin._makeRPCRequest('listwallets');
    -
    -      if (loadedWallets.length > 0) {
    -        this._appendMessage(`✓ Loaded wallets (${loadedWallets.length}):`);
    -        loadedWallets.forEach(wallet => {
    -          this._appendMessage(`  • ${wallet}`);
    -        });
    -      } else {
    -        this._appendMessage(`ℹ No wallets currently loaded`);
    -      }
    -
    -      // Try to get available wallets from disk
    -      try {
    -        const availableWallets = await this.bitcoin._makeRPCRequest('listwalletdir');
    -        if (availableWallets && availableWallets.wallets && availableWallets.wallets.length > 0) {
    -          this._appendMessage(`\nAvailable wallets on disk (${availableWallets.wallets.length}):`);
    -          availableWallets.wallets.forEach(walletInfo => {
    -            const name = walletInfo.name;
    -            const isLoaded = loadedWallets.includes(name);
    -            const status = isLoaded ? '(loaded)' : '(unloaded)';
    -            this._appendMessage(`  • ${name} ${status}`);
    -          });
    -
    -          if (loadedWallets.length === 0) {
    -            this._appendMessage(`\nTo load a wallet: loadwallet <wallet_name>`);
    -            this._appendMessage(`To create a new wallet: createwallet <wallet_name>`);
    -          }
    -        }
    -      } catch (listDirError) {
    -        // listwalletdir might not be supported in older versions
    -        if (this.settings.debug) {
    -          this._appendMessage(`Note: Could not list wallet directory: ${listDirError.message}`);
    -        }
    -      }
    -
    -      // Show current balance if any wallet is loaded
    -      if (loadedWallets.length > 0) {
    -        try {
    -          const balance = await this.bitcoin._makeWalletRequest('getbalance', [], loadedWallets[0]);
    -          this._appendMessage(`\nCurrent total balance: ${balance} BTC`);
    -        } catch (balanceError) {
    -          this._appendMessage(`\nCould not get balance: ${balanceError.message}`);
    -        }
    -      }
    -
    -    } catch (error) {
    -      this._appendError(`Failed to list wallets: ${error.message}`);
    -    }
    -  }
    -
    -  async _handleBitcoinHelpRequest (params) {
    -    this._appendMessage(`{bold}Bitcoin Core Recovery Help{/bold}\n`);
    -
    -    this._appendMessage(`{yellow-fg}If you're seeing Bitcoin Core errors:{/yellow-fg}`);
    -    this._appendMessage(`1. Stop Bitcoin Core: bitcoin-cli stop`);
    -    this._appendMessage(`2. Check disk space: df -h ~/Library/Application\\ Support/Bitcoin/`);
    -    this._appendMessage(`3. Check debug log: tail ~/Library/Application\\ Support/Bitcoin/debug.log`);
    -
    -    this._appendMessage(`\n{yellow-fg}Recovery options (try in order):{/yellow-fg}`);
    -    this._appendMessage(`1. Reindex blockchain: bitcoind -reindex`);
    -    this._appendMessage(`2. Rebuild chainstate: bitcoind -reindex-chainstate`);
    -    this._appendMessage(`3. Fresh start: backup wallet, delete datadir, restart`);
    -
    -    this._appendMessage(`\n{yellow-fg}Fabric commands:{/yellow-fg}`);
    -    this._appendMessage(`• testrpc - Test Bitcoin RPC connection`);
    -    this._appendMessage(`• listwallets - Show wallet status`);
    -    this._appendMessage(`• createwallet <name> - Create new wallet`);
    -    this._appendMessage(`• loadwallet <name> - Load existing wallet`);
    -
    -    this._appendMessage(`\n{green-fg}Bitcoin Core is independent of Fabric.{/green-fg}`);
    -    this._appendMessage(`{green-fg}Fix Bitcoin Core first, then restart Fabric.{/green-fg}`);
    -  }
    -
    -  async _handleBlockExplorerRequest (params) {
    -    if (!params[1]) {
    -      this._appendError('Usage: block <hash|height>');
    -      this._appendMessage('Example: block 0000000000000000000123456789abcdef...');
    -      this._appendMessage('Example: block 850000');
    -      return;
    -    }
    -    const arg = params[1];
    -    const hashOrHeight = /^\d+$/.test(arg) ? parseInt(arg, 10) : arg;
    -    if (!this.bitcoin || !this.bitcoin.getBlockInfo) {
    -      this._appendError('Bitcoin service or explorer not available');
    -      return;
    -    }
    -    try {
    -      this._appendMessage(`Fetching block ${hashOrHeight}...`);
    -      const info = await this.bitcoin.getBlockInfo(hashOrHeight);
    -      const ts = info.timestamp != null ? info.timestamp : info.time;
    -      const txCount = info.tx_count != null ? info.tx_count : (info.txcount != null ? info.txcount : (info.tx ? info.tx.length : null));
    -      const lines = [
    -        `{bold}Block{/bold}`,
    -        `  Hash: ${info.hash || info.id || 'N/A'}`,
    -        `  Height: ${info.height != null ? info.height : 'N/A'}`,
    -        `  Time: ${ts != null ? new Date(ts * 1000).toISOString() : 'N/A'}`,
    -        `  Tx count: ${txCount != null ? txCount : 'N/A'}`,
    -        `  Size: ${info.size != null ? `${info.size} bytes` : 'N/A'}`
    -      ];
    -      if (info.mediantime) lines.push(`  Median time: ${new Date(info.mediantime * 1000).toISOString()}`);
    -      if (info.difficulty) lines.push(`  Difficulty: ${info.difficulty}`);
    -      this._appendMessage(lines.join('\n'));
    -      this._updateBlockchainPanel(lines.join('\n'));
    -    } catch (e) {
    -      this._appendError(`Block lookup failed: ${e.message}`);
    -    }
    -  }
    -
    -  async _handleTxExplorerRequest (params) {
    -    if (!params[1]) {
    -      this._appendError('Usage: tx <txid>');
    -      this._appendMessage('Example: tx 0000000000000000000123456789abcdef...');
    -      return;
    -    }
    -    const txid = params[1];
    -    if (!this.bitcoin || !this.bitcoin.getTransactionInfo) {
    -      this._appendError('Bitcoin service or explorer not available');
    -      return;
    -    }
    -    try {
    -      this._appendMessage(`Fetching transaction ${txid.substring(0, 16)}...`);
    -      const info = await this.bitcoin.getTransactionInfo(txid);
    -      const vinCount = info.vin ? info.vin.length : 0;
    -      const voutCount = info.vout ? info.vout.length : 0;
    -      const totalOut = info.vout ? info.vout.reduce((s, o) => s + (o.value || 0), 0) : 0;
    -      const lines = [
    -        `{bold}Transaction{/bold}`,
    -        `  Txid: ${info.txid || info.txid || 'N/A'}`,
    -        `  Size: ${info.size != null ? info.size : info.vsize || 'N/A'} bytes`,
    -        `  Confirmations: ${info.status ? (info.status.confirmed ? 'confirmed' : 'unconfirmed') : (info.confirmations != null ? info.confirmations : 'N/A')}`,
    -        `  Inputs: ${vinCount}, Outputs: ${voutCount}`,
    -        `  Total out: ${totalOut} BTC`
    -      ];
    -      if (info.block_hash || info.blockhash) lines.push(`  Block: ${info.block_hash || info.blockhash}`);
    -      this._appendMessage(lines.join('\n'));
    -      this._updateBlockchainPanel(lines.join('\n'));
    -    } catch (e) {
    -      this._appendError(`Transaction lookup failed: ${e.message}`);
    -    }
    -  }
    -
    -  async _handleAddressExplorerRequest (params) {
    -    if (!params[1]) {
    -      this._appendError('Usage: address <address>');
    -      this._appendMessage('Example: address bc1q...');
    -      return;
    -    }
    -    const address = params[1];
    -    if (!this.bitcoin || !this.bitcoin.getAddressInfo) {
    -      this._appendError('Bitcoin service or explorer not available');
    -      return;
    -    }
    -    try {
    -      this._appendMessage(`Fetching address ${address.substring(0, 16)}...`);
    -      const info = await this.bitcoin.getAddressInfo(address);
    -      const chain = info.chain_stats || {};
    -      const mempool = info.mempool_stats || {};
    -      const funded = (chain.funded_txo_sum || 0) / 1e8;
    -      const spent = (chain.spent_txo_sum || 0) / 1e8;
    -      const balance = funded - spent;
    -      const txCount = chain.tx_count != null ? chain.tx_count : 0;
    -      const lines = [
    -        `{bold}Address{/bold}`,
    -        `  ${address}`,
    -        `  Balance: ${balance.toFixed(8)} BTC`,
    -        `  Tx count: ${txCount}`,
    -        `  Unconfirmed: ${mempool.tx_count != null ? mempool.tx_count : 0} txs`
    -      ];
    -      if (info.recent_txs && info.recent_txs.length > 0) {
    -        lines.push(`  Recent txs: ${info.recent_txs.slice(0, 5).map(t => t.txid ? t.txid.substring(0, 16) + '...' : 'N/A').join(', ')}`);
    -      }
    -      this._appendMessage(lines.join('\n'));
    -      this._updateBlockchainPanel(lines.join('\n'));
    -    } catch (e) {
    -      this._appendError(`Address lookup failed: ${e.message}`);
    -    }
    -  }
    -
    -  _handleExplorerHelpRequest (params) {
    -    this._appendMessage(`{bold}Blockchain Explorer{/bold}`);
    -    this._appendMessage(`  block <hash|height>  - Look up block by hash or height`);
    -    this._appendMessage(`  tx <txid>           - Look up transaction by txid`);
    -    this._appendMessage(`  address <addr>     - Look up address balance and history`);
    -    this._appendMessage(`  explorer            - Show this help`);
    -    this._appendMessage(`\nUses RPC when connected to bitcoind; hub.fabric.pub API as fallback.`);
    -    this._appendMessage(`SPV mode: FABRIC_BITCOIN_NODE=192.168.50.5 or bitcoin.spvNode in settings.`);
    -    this._appendMessage(`Explorer: FABRIC_EXPLORER_URL or bitcoin.explorerBaseUrl (optional HTTP fallback; unset = RPC only)`);
    -  }
    -
    -  async _handleRotateRequest () {
    -    const account = await this.identity._nextAccount();
    -    this._appendMessage('Rotated to Account: ' + account.id);
    -    return false;
    -  }
    -
    -  async _handleSendRequest (params) {
    -    if (!params[1]) return this._appendError('You must specify an address to send to.');
    -    if (!params[2]) return this._appendError('You must specify an amount to send.');
    -
    -    const address = params[1];
    -    const amount = params[2];
    -
    -    const tx = await this.spend(address, amount);
    -    this._appendMessage(`Transaction created: ${tx}`);
    -
    -    return false;
    -  }
    -
    -  async _handleBalanceRequest () {
    -    const balance = await this._getBalance();
    -    this._appendMessage(`{bold}Wallet Balance{/bold}: ${JSON.stringify(balance, null, '  ')}`);
    -    return false;
    -  }
    -
    -  async _handleReceiveAddressRequest () {
    -    const address = await this.node.wallet.getUnusedAddress();
    -    this._appendMessage(`{bold}Receive address{/bold}: ${JSON.stringify(address.toString(), null, '  ')}`);
    -    return false;
    -  }
    -
    -  _handleServiceCommand (params) {
    -    const list = Object.keys(this.services);
    -
    -    switch (params[1]) {
    -      case 'list':
    -      default:
    -        this._appendMessage(`{bold}Available Services{/bold}: ${JSON.stringify(list, null, '  ')}`);
    -        break;
    -      case 'state':
    -        const state = this.services[params[2]].state;
    -        this._appendMessage(`{bold}${params[2]}{/bold}: ${JSON.stringify(state, null, '  ')}`);
    -
    -        break;
    -    }
    -  }
    -
    -  _handleIdentityRequest () {
    -    this._appendMessage(`Local Identity: ${JSON.stringify({
    -      id: this.identity.id,
    -      pubkey: this.identity.pubkey,
    -      address: this.node.server.address(),
    -      endpoint: `${this.identity.id}@${this.settings.host}:${this.settings.port}`
    -    }, null, '  ')}`);
    -  }
    -
    -  _handleSettingsRequest () {
    -    this._appendMessage(`Local Settings: ${JSON.stringify(this.settings, null, '  ')}`);
    -  }
    -
    -  _handleHelpRequest (params) {
    -    let text = '';
    -
    -    switch (params[1]) {
    -      default:
    -        text = `{bold}Fabric CLI Help{/bold}\nThe Fabric CLI offers a simple command-based interface to a Fabric-speaking Network.  You can use \`/connect <address>\` to establish a connection to a known peer, or any of the available commands.\n\n{bold}Panels{/bold}: F1 Home | F2 Console | F3 Network | F4 Wallet | F5 Contracts | F6 Blockchain\n\n{bold}Available Commands{/bold}:\n\n${Object.keys(this.commands).map(x => `  ${x}`).join('\n')}\n\n{bold}Usage{/bold}:\n  Type any command with a forward slash, e.g. /help, /peers, /connect localhost:7777\n\n{bold}Examples{/bold}:\n  /help          - Show this help message\n  /block 850000  - Browse block by height (F6 panel)\n  /tx <txid>     - Look up transaction\n  /address <addr> - Look up address\n  /peers         - List connected peers\n  /connect <addr> - Connect to a peer\n  /identity      - Show your identity\n  /wallet        - Show wallet information\n  /bitcoin       - Show Bitcoin service status\n  /quit          - Exit the application`
    -        break;
    -    }
    -
    -    this._appendMessage(text);
    -  }
    -
    -  _handleServiceMessage (msg) {
    -    this.emit('message', 'received message from service:', msg);
    -  }
    -
    -  _processInput (input) {
    -    if (input.charAt(0) === '/') {
    -      const parts = input.substring(1).split(' ');
    -
    -      if (this.commands[parts[0]]) {
    -        this.commands[parts[0]].apply(this, [ parts ]);
    -        return true;
    -      }
    -
    -      this._appendError('Unhandled command: ' + parts[0]);
    -
    -      return true;
    -    }
    -
    -    return false;
    -  }
    -
    -  async _refreshBlockchainPanel () {
    -    if (!this.settings.render || !this.elements['blockchainBox'] || !this.elements['blockchainContent']) return;
    -    if (!this.bitcoin || !this.bitcoin.getBlockInfo) return;
    -
    -    const header = this.elements['blockchainHeader'];
    -    const content = this.elements['blockchainContent'];
    -    const helpText = '\n{bold}Canonical record{/bold} - Use /block <height>, /tx <txid>, /address <addr>';
    -
    -    try {
    -      let heightNum = null;
    -      let tip = 'loading...';
    -      if (this.bitcoin._rpcReady && this.bitcoin._makeRPCRequest) {
    -        try {
    -          heightNum = await this.bitcoin._makeRPCRequest('getblockcount');
    -          const info = await this.bitcoin._makeRPCRequest('getblockchaininfo');
    -          tip = (info.bestblockhash || '').substring(0, 24) + '...';
    -        } catch (e) {
    -          tip = 'RPC unavailable';
    -        }
    -      }
    -      const heightStr = heightNum != null ? String(heightNum) : '--';
    -      if (header) header.setContent(`Height: ${heightStr}  |  Tip: ${tip}${helpText}`);
    -
    -      if (heightNum == null || heightNum < 0) {
    -        content.setContent('{yellow-fg}Connect to a node (RPC or SPV) to browse the chain.{/yellow-fg}\n\nOr use /block <height> with Blockstream API fallback.');
    -      } else {
    -        const blockInfo = await this.bitcoin.getBlockInfo(heightNum);
    -        if (!blockInfo) {
    -          content.setContent('{yellow-fg}No block data.{/yellow-fg}');
    -        } else {
    -          const ts = blockInfo.timestamp != null ? blockInfo.timestamp : blockInfo.time;
    -          const txCount = blockInfo.tx_count != null ? blockInfo.tx_count : (blockInfo.tx ? blockInfo.tx.length : 0);
    -          const lines = [
    -            `{bold}Block ${blockInfo.height != null ? blockInfo.height : 'N/A'}{/bold}`,
    -            `Hash: ${blockInfo.hash || blockInfo.id || 'N/A'}`,
    -            `Time: ${ts != null ? new Date(ts * 1000).toISOString() : 'N/A'}`,
    -            `Transactions: ${txCount}`,
    -            `Size: ${blockInfo.size != null ? blockInfo.size + ' bytes' : 'N/A'}`,
    -            '',
    -            'Use /block <height> to view another block',
    -            'Use /tx <txid> to view a transaction',
    -            'Use /address <addr> to view an address'
    -          ];
    -          content.setContent(lines.join('\n'));
    -        }
    -      }
    -      if (this.screen) this.screen.render();
    -    } catch (e) {
    -      if (content) content.setContent(`{yellow-fg}${e.message || 'Could not load chain'}{/yellow-fg}\n\nUse /block <height>, /tx <txid>, /address <addr> to browse.`);
    -      if (this.screen) this.screen.render();
    -    }
    -  }
    -
    -  _updateBlockchainPanel (text) {
    -    if (!this.settings.render || !this.elements['blockchainContent']) return;
    -    this.elements['blockchainContent'].setContent(text);
    -    if (this.screen) this.screen.render();
    -  }
    -
    -  async _syncChainDisplay () {
    -    if (!this.settings.render) return this;
    -
    -    // Prevent concurrent sync operations
    -    if (this.syncInProgress.chain) {
    -      if (this.settings.debug) this._appendMessage(`_syncChainDisplay: Already in progress, skipping`);
    -      return this;
    -    }
    -
    -    this.syncInProgress.chain = true;
    -
    -    try {
    -      if (!this.settings.bitcoin.enable) {
    -        // Set default values when Bitcoin is not enabled
    -        if (this.elements) {
    -          if (this.elements['heightValue']) this.elements['heightValue'].setContent('N/A (Bitcoin disabled)');
    -          if (this.elements['chainTip']) this.elements['chainTip'].setContent('N/A (Bitcoin disabled)');
    -          if (this.elements['unconfirmedValue']) this.elements['unconfirmedValue'].setContent('0.00000000');
    -          if (this.elements['bondedValue']) this.elements['bondedValue'].setContent('0.00000000');
    -          if (this.elements['progressStatus']) this.elements['progressStatus'].setContent('N/A (Bitcoin disabled)');
    -          if (this.screen) this.screen.render();
    -        }
    -        return this;
    -      }
    -
    -      // Check if Bitcoin service is ready
    -      if (!this.bitcoin || !this.bitcoin._makeRPCRequest) {
    -        if (this.settings.debug) this._appendMessage(`_syncChainDisplay: Bitcoin service not ready yet`);
    -        if (this.elements) {
    -          if (this.elements['heightValue']) this.elements['heightValue'].setContent('loading...');
    -          if (this.elements['chainTip']) this.elements['chainTip'].setContent('loading...');
    -          if (this.elements['unconfirmedValue']) this.elements['unconfirmedValue'].setContent('syncing...');
    -          if (this.elements['bondedValue']) this.elements['bondedValue'].setContent('syncing...');
    -          if (this.elements['progressStatus']) this.elements['progressStatus'].setContent('syncing...');
    -          if (this.screen) this.screen.render();
    -        }
    -        return this;
    -      }
    -
    -      if (this.settings.debug) this._appendMessage(`_syncChainDisplay: Making RPC calls...`);
    -
    -      // Update height immediately when available
    -      try {
    -        const height = await this.bitcoin._makeRPCRequest('getblockcount');
    -        if (this.elements && this.elements['heightValue']) {
    -          this.elements['heightValue'].setContent(`${height}`);
    -          if (this.elements['progressStatus']) {
    -            this.elements['progressStatus'].setContent(`synced to block ${height}`);
    -          }
    -          this.screen.render();
    -          if (this.settings.debug) this._appendMessage(`_syncChainDisplay: Updated height to ${height}`);
    -        }
    -
    -        // Small delay before next call
    -        await new Promise(resolve => setTimeout(resolve, 100));
    -
    -        // Update chain tip when available
    -        const stats = await this.bitcoin._makeRPCRequest('getblockchaininfo');
    -        if (this.elements && this.elements['chainTip']) {
    -          this.elements['chainTip'].setContent(`${stats.bestblockhash}`);
    -          this.screen.render();
    -          if (this.settings.debug) this._appendMessage(`_syncChainDisplay: Updated chain tip to ${stats.bestblockhash.substring(0, 16)}...`);
    -        }
    -
    -        // Update unconfirmed and bonded values (currently static)
    -        const unconfirmed = 0.0;
    -        const bonded = 0.0;
    -        if (this.elements) {
    -          if (this.elements['unconfirmedValue']) {
    -            this.elements['unconfirmedValue'].setContent(`${unconfirmed.toFixed(8)}`);
    -          }
    -          if (this.elements['bondedValue']) {
    -            this.elements['bondedValue'].setContent(`${bonded.toFixed(8)}`);
    -          }
    -          this.screen.render();
    -        }
    -
    -        if (this.settings.debug) this._appendMessage(`_syncChainDisplay: All elements updated`);
    -      } catch (rpcError) {
    -        // If any individual RPC call fails, just update what we can
    -        if (this.settings.debug) this._appendMessage(`_syncChainDisplay: Individual RPC call failed: ${rpcError.message}`);
    -        throw rpcError; // Re-throw to be caught by outer try-catch
    -      }
    -    } catch (exception) {
    -      if (this.settings.debug) this._appendError(`Could not sync chain: ${exception.message || exception}`);
    -      if (this.elements) {
    -        if (this.elements['heightValue']) this.elements['heightValue'].setContent('Error');
    -        if (this.elements['chainTip']) this.elements['chainTip'].setContent('Connection Error');
    -        if (this.elements['progressStatus']) this.elements['progressStatus'].setContent('RPC Error');
    -        if (this.screen) this.screen.render();
    -      }
    -    } finally {
    -      this.syncInProgress.chain = false;
    -    }
    -  }
    -
    -  async _syncContracts () {
    -    if (this.settings.lightning.enable) await this._syncLightningChannels();
    -    return this;
    -  }
    -
    -  async _syncBalance () {
    -    if (!this.settings.render) return this;
    -
    -    // Prevent concurrent sync operations
    -    if (this.syncInProgress.balance) {
    -      if (this.settings.debug) this._appendMessage(`_syncBalance: Already in progress, skipping`);
    -      return this;
    -    }
    -
    -    this.syncInProgress.balance = true;
    -
    -    try {
    -      if (!this.settings.bitcoin.enable) {
    -        // Set default values when Bitcoin is not enabled
    -        if (this.elements) {
    -          if (this.elements['balance']) this.elements['balance'].setContent('0.00000000');
    -          if (this.elements.wallethelp) {
    -            this.elements.wallethelp.setContent(
    -              `  {bold}SPENDABLE{/bold}: 0.00000000 BTC\n` +
    -              `{bold}UNCONFIRMED{/bold}: 0.00000000 BTC\n` +
    -              `   {bold}IMMATURE{/bold}: 0.00000000 BTC\n` +
    -              `\n{yellow-fg}Bitcoin services disabled{/yellow-fg}`
    -            );
    -          }
    -          if (this.screen) this.screen.render();
    -        }
    -        return this;
    -      }
    -
    -      // Check if Bitcoin service is ready
    -      if (!this.bitcoin || !this.bitcoin._makeRPCRequest) {
    -        if (this.elements) {
    -          if (this.elements['balance']) this.elements['balance'].setContent('Bitcoin error');
    -          if (this.elements.wallethelp) {
    -            this.elements.wallethelp.setContent(
    -              `{red-fg}Bitcoin Core error detected{/red-fg}\n` +
    -              `Check Bitcoin Core status\n` +
    -              `May need reindex: bitcoind -reindex`
    -            );
    -          }
    -          if (this.screen) this.screen.render();
    -        }
    -        return this;
    -      }
    -
    -      // Check if any wallets are loaded first
    -      let hasWallet = false;
    -      let defaultWallet = null;
    -      try {
    -        const walletList = await this.bitcoin._makeRPCRequest('listwallets');
    -        hasWallet = walletList && walletList.length > 0;
    -        if (hasWallet) defaultWallet = walletList[0];
    -        if (this.settings.debug) this._appendMessage(`_syncBalance: Found ${walletList.length} loaded wallets`);
    -      } catch (walletListError) {
    -        if (this.settings.debug) this._appendMessage(`_syncBalance: Could not list wallets: ${walletListError.message}`);
    -      }
    -
    -      if (!hasWallet) {
    -        // No wallet loaded - show zero balance
    -        this._state.balances.confirmed = 0;
    -        this._state.balances.trusted = 0;
    -        this._state.balances.immature = 0;
    -        this._state.balances.pending = 0;
    -
    -        if (this.elements) {
    -          if (this.elements['balance']) {
    -            this.elements['balance'].setContent('0.00000000');
    -          }
    -          if (this.elements.wallethelp) {
    -            this.elements.wallethelp.setContent(
    -              `  {bold}SPENDABLE{/bold}: 0.00000000 BTC\n` +
    -              `{bold}UNCONFIRMED{/bold}: 0.00000000 BTC\n` +
    -              `   {bold}IMMATURE{/bold}: 0.00000000 BTC\n` +
    -              `\n{yellow-fg}No wallet loaded{/yellow-fg}`
    -            );
    -          }
    -          this.screen.render();
    -        }
    -        return this;
    -      }
    -
    -      // Update balance immediately when available
    -      const balance = await this.bitcoin._makeWalletRequest('getbalance', [], defaultWallet);
    -      this._state.balances.confirmed = balance;
    -      this._state.balances.trusted = balance;
    -
    -      // Update balance element immediately
    -      if (this.elements && this.elements['balance']) {
    -        this.elements['balance'].setContent(balance.toFixed(8));
    -        this.screen.render();
    -        if (this.settings.debug) this._appendMessage(`_syncBalance: Updated balance to ${balance.toFixed(8)} BTC from wallet: ${defaultWallet}`);
    -      }
    -
    -      await new Promise(resolve => setTimeout(resolve, 100));
    -
    -      // Try to get wallet info, but don't fail if no wallet is loaded
    -      let walletInfo = { immature_balance: 0, unconfirmed_balance: 0 };
    -      try {
    -        walletInfo = await this.bitcoin._makeWalletRequest('getwalletinfo', [], defaultWallet);
    -        this._state.balances.immature = walletInfo.immature_balance || 0;
    -        this._state.balances.pending = walletInfo.unconfirmed_balance || 0;
    -
    -        // Update wallet help with complete information
    -        if (this.elements && this.elements.wallethelp) {
    -          this.elements.wallethelp.setContent(
    -            `  {bold}SPENDABLE{/bold}: ${balance.toFixed(8)} BTC\n` +
    -            `{bold}UNCONFIRMED{/bold}: ${this._state.balances.pending.toFixed(8)} BTC\n` +
    -            `   {bold}IMMATURE{/bold}: ${this._state.balances.immature.toFixed(8)} BTC\n`
    -          );
    -          this.screen.render();
    -          if (this.settings.debug) this._appendMessage(`_syncBalance: Updated wallet info with detailed balances`);
    -        }
    -      } catch (walletException) {
    -        if (this.settings.debug) this._appendMessage(`_syncBalance: No wallet loaded or getwalletinfo failed, using defaults`);
    -        // Update with basic balance info only
    -        this._state.balances.immature = 0;
    -        this._state.balances.pending = 0;
    -
    -        if (this.elements && this.elements.wallethelp) {
    -          this.elements.wallethelp.setContent(
    -            `  {bold}SPENDABLE{/bold}: ${balance.toFixed(8)} BTC\n` +
    -            `{bold}UNCONFIRMED{/bold}: 0.00000000 BTC\n` +
    -            `   {bold}IMMATURE{/bold}: 0.00000000 BTC\n`
    -          );
    -          this.screen.render();
    -        }
    -      }
    -    } catch (exception) {
    -      // Set error values when connection fails
    -      if (this.elements) {
    -        if (this.elements['balance']) this.elements['balance'].setContent('Error');
    -        if (this.elements.wallethelp) {
    -          this.elements.wallethelp.setContent(
    -            `{red-fg}Bitcoin RPC error{/red-fg}\n` +
    -            `${exception.message || 'Connection failed'}`
    -          );
    -        }
    -        if (this.screen) this.screen.render();
    -      }
    -      if (this.settings.debug) this._appendError(`Could not sync balance: ${exception.message || exception}`);
    -    } finally {
    -      this.syncInProgress.balance = false;
    -    }
    -
    -    return this;
    -  }
    -
    -  async _syncUnspent () {
    -    if (!this.settings.render) return this;
    -
    -    try {
    -      const unspent = await this.bitcoin._listUnspent();
    -      const list = unspent.map((x) => {
    -        const map = {};
    -
    -        for (const [key, value] of Object.entries(x)) {
    -          if ([
    -
    -          ].includes(key)) continue;
    -
    -          map[key] = value.toString();
    -        }
    -
    -        return Object.values(map);
    -      });
    -
    -      this._state.unspent = unspent;
    -
    -      const headers = (unspent && unspent.length > 0) ? Object.keys(unspent[0]) : [];
    -      const data = headers.length ? [headers].concat(list) : [];
    -
    -      if (this.elements && this.elements.outputlist && typeof this.elements.outputlist.setData === 'function') {
    -        this.elements.outputlist.setData(data);
    -      }
    -
    -      this.commit();
    -
    -      if (this.screen && typeof this.screen.render === 'function') this.screen.render();
    -    } catch (exception) {
    -      // if (this.settings.debug) this._appendError(`Could not sync balance: ${JSON.stringify(exception)}`);
    -    }
    -  }
    -
    -  async _syncLightningChannels () {
    -    if (!this.settings.render) return this;
    -    if (!this.elements || !this.elements.contracthelp || typeof this.elements.contracthelp.setContent !== 'function') return this;
    -    this.elements.contracthelp.setContent(
    -      `   {bold}STATUS:{/bold} ${this.status}\n` +
    -      `{bold}LIGHTNING:{/bold} ${this.lightning.status}`);
    -    return this;
    -  }
    -
    -  async _getBalance () {
    -    const result = await this.bitcoin._syncBalanceFromOracle();
    -    await this.lightning.sync();
    -    // this._appendDebug(`Lightning balances: ${JSON.stringify(this.lightning.balances)}`);
    -    const balance = result.data.content + this.lightning.balances.spendable;
    -
    -    return balance;
    -  }
    -
    -  _allConfigured () {
    -    if (
    -      this._isBitcoinConfigured() &&
    -      this._isLightningConfigured()
    -    ) {
    -      return true;
    -    } else {
    -      return false;
    -    }
    -  }
    -
    -  _isBitcoinConfigured () {
    -    return (this.settings.bitcoin) ? true : false;
    -  }
    -
    -  _isLightningConfigured () {
    -    return (this.settings.lightning) ? true : false;
    -  }
    -
    -  _syncConnectionList () {
    -    this.elements['connections'].clearItems();
    -
    -    for (const id in this.connections) {
    -      const connection = this.connections[id];
    -
    -      let icon = '?';
    -      switch (connection.status) {
    -        default:
    -          icon = '…';
    -          break;
    -        case 'ready':
    -          icon = '✓';
    -          break;
    -      }
    -
    -      const element = blessed.element({
    -        name: connection.id,
    -        content: `[${icon}] ${id}`
    -      });
    -
    -      // TODO: use peer ID for managed list
    -      // self.elements['connections'].insertItem(0, element);
    -      this.elements['connections'].add(element.content);
    -    }
    -  }
    -
    -  _syncPeerList () {
    -    if (!this.elements || !this.elements['peers']) return;
    -
    -    this.elements['peers'].clearItems();
    -
    -    for (const id in this.peers) {
    -      const peer = this.peers[id];
    -
    -      // Determine status icon
    -      let icon = '✓'; // Default connected
    -      switch (peer.status) {
    -        case 'disconnected':
    -          icon = '~';
    -          break;
    -        case 'connecting':
    -          icon = '…';
    -          break;
    -        case 'connected':
    -        default:
    -          icon = '✓';
    -          break;
    -      }
    -
    -      const element = blessed.element({
    -        name: peer.id,
    -        content: `[${icon}] ${peer.id}@${peer.address}`
    -      });
    -
    -      // TODO: use peer ID for managed list
    -      // self.elements['peers'].insertItem(0, element);
    -      this.elements['peers'].add(element.content);
    -    }
    -
    -    // Force screen render to update the display
    -    if (this.screen) {
    -      this.screen.render();
    -    }
    -  }
    -
    -  _registerCommand (command, method) {
    -    this.commands[command] = method.bind(this);
    -  }
    -
    -  _registerService (name, type) {
    -    const self = this;
    -    const settings = merge({}, this.settings, this.settings[name]);
    -    const service = new type(settings);
    -
    -    if (this.services[name]) {
    -      return this._appendWarning(`Service already registered: ${name}`);
    -    }
    -
    -    this.services[name] = service;
    -
    -    this.services[name].on('error', function (msg) {
    -      self._appendError(`Service "${name}" emitted error: ${JSON.stringify(msg, null, '  ')}`);
    -    });
    -
    -    this.services[name].on('warning', function (msg) {
    -      self._appendWarning(`Service warning from ${name}: ${JSON.stringify(msg, null, '  ')}`);
    -    });
    -
    -    this.services[name].on('message', function (msg) {
    -      self._appendMessage(`Service message from ${name}: ${JSON.stringify(msg, null, '  ')}`);
    -      self.node.relayFrom(self.node.id, Message.fromVector(['ChatMessage', JSON.stringify(msg)]));
    -    });
    -
    -    this.on('identity', async function _registerActor (identity) {
    -      if (this.settings.services.includes(name)) {
    -        self._appendMessage(`Registering actor on service "${name}": ${JSON.stringify(identity)}`);
    -
    -        try {
    -          const registration = await this.services[name]._registerActor(identity);
    -          self._appendMessage(`Registered Actor: ${JSON.stringify(registration, null, '  ')}`);
    -        } catch (exception) {
    -          self._appendError(`Error from service "${name}" during _registerActor: ${exception}`);
    -        }
    -      }
    -    });
    -  }
    -
    -  focusInput () {
    -    this.mode = 'INSERT';
    -    if (this.elements['prompt']) this.elements['prompt'].clearValue();
    -    if (this.elements['prompt']) this.elements['prompt'].focus();
    -    if (this.elements.modeline) this.elements.modeline.setContent(' INSERT ');
    -    if (this.screen) this.screen.render();
    -  }
    -
    -  defocusInput () {
    -    this.mode = 'META';
    -    if (this.elements['dummy']) this.elements['dummy'].focus();
    -    if (this.elements.modeline) this.elements.modeline.setContent('  META ');
    -    if (this.screen) this.screen.render();
    -  }
    -
    -  setPane (name) {
    -    this.elements['home'].detach();
    -    // this.elements['logBox'].detach();
    -    this.elements['help'].detach();
    -    this.elements['contracts'].detach();
    -    this.elements['network'].detach();
    -    this.elements['walletBox'].detach();
    -    if (this.elements['blockchainBox']) this.elements['blockchainBox'].detach();
    -
    -    switch (name) {
    -      default:
    -        break;
    -      case 'home':
    -        this.screen.append(this.elements['home'])
    -        break;
    -      case 'help':
    -        this.screen.append(this.elements['help'])
    -        break;
    -      case 'contracts':
    -        this.screen.append(this.elements['contracts'])
    -        break;
    -      case 'messages':
    -        // this.screen.append(this.elements['logBox'])
    -        break;
    -      case 'network':
    -        this.screen.append(this.elements['network'])
    -        break;
    -      case 'wallet':
    -        this.screen.append(this.elements['walletBox'])
    -        break;
    -      case 'blockchain':
    -        this.screen.append(this.elements['blockchainBox'])
    -        this._refreshBlockchainPanel()
    -        break;
    -    }
    -  }
    -
    -  render () {
    -    if (!this.settings.render) return this;
    -
    -    const self = this;
    -
    -    self.screen = blessed.screen({
    -      smartCSR: true,
    -      input: this.settings.input,
    -      output: this.settings.output,
    -      terminal: this.settings.terminal,
    -      fullUnicode: this.settings.fullUnicode
    -    });
    -
    -    // Add a hidden dummy element for focus management
    -    self.elements['dummy'] = blessed.box({
    -      parent: self.screen,
    -      width: 1,
    -      height: 1,
    -      top: 0,
    -      left: 0,
    -      hidden: true
    -    });
    -
    -    self.elements.modeline = blessed.text({
    -      parent: self.screen,
    -      content: '  META ',
    -      bottom: 0,
    -      right: 0,
    -      width: 8,
    -      style: {
    -        bg: 'white',
    -        fg: 'black'
    -      }
    -    });
    -
    -    self.elements['home'] = blessed.box({
    -      parent: self.screen,
    -      content: 'Fabric Command Line Interface\nVersion 0.0.1-dev (@martindale)',
    -      top: 6,
    -      bottom: 4,
    -      border: {
    -        type: 'line'
    -      },
    -      style: {
    -        border: {
    -          fg: 'white'
    -        }
    -      }
    -    });
    -
    -    self.elements['help'] = blessed.box({
    -      parent: self.screen,
    -      label: '[ Help ]',
    -      content: 'Fabric Command Line Interface\nVersion 0.0.1-dev (@martindale)',
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      bottom: 4,
    -      width: '100%'
    -    });
    -
    -    self.elements['contracts'] = blessed.box({
    -      parent: self.screen,
    -      label: '[ Contracts ]',
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      bottom: 4
    -    });
    -
    -    self.elements['contracthelp'] = blessed.text({
    -      parent: self.elements.contracts,
    -      tags: true,
    -      top: 1,
    -      left: 2,
    -      right: 2
    -    });
    -
    -    self.elements['lightningbook'] = blessed.box({
    -      parent: self.elements.contracts,
    -      label: '[ Lightning ]',
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      // height: 10
    -    });
    -
    -    self.elements['channellist'] = blessed.table({
    -      parent: self.elements.lightningbook,
    -      data: [
    -        ['ID']
    -      ],
    -      width: '100%-2'
    -    });
    -
    -    /*
    -    self.elements['contractbook'] = blessed.box({
    -      parent: self.elements.contracts,
    -      label: '[ Fabric ]',
    -      border: {
    -        type: 'line'
    -      },
    -      top: 16
    -    });
    -
    -    self.elements['contractlist'] = blessed.table({
    -      parent: self.elements.contractbook,
    -      data: [
    -        ['ID', 'Status', 'Type', 'Bond', 'Confirmations', 'Last Modified', 'Link']
    -      ],
    -      width: '100%-2'
    -    });
    -    */
    -
    -    self.elements['network'] = blessed.list({
    -      parent: self.screen,
    -      label: '{bold}[ Network ]{/bold}',
    -      tags: true,
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      bottom: 4,
    -      width: '100%'
    -    });
    -
    -    self.elements['connections'] = blessed.list({
    -      parent: this.elements['network'],
    -      top: 0,
    -      bottom: 0
    -    });
    -
    -    self.elements['logBox'] = blessed.box({
    -      parent: self.screen,
    -      top: 6,
    -      bottom: 4,
    -      width: '100%'
    -    });
    -
    -    self.elements['walletBox'] = blessed.box({
    -      parent: self.screen,
    -      label: '{bold}[ Wallet ]{/bold}',
    -      tags: true,
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      bottom: 4,
    -      width: '100%'
    -    });
    -
    -    self.elements['wallethelp'] = blessed.text({
    -      parent: self.elements.walletBox,
    -      tags: true,
    -      top: 1,
    -      left: 2,
    -      right: 2
    -    });
    -
    -    self.elements['outputbook'] = blessed.box({
    -      parent: self.elements.walletBox,
    -      label: '[ Unspent Outputs ]',
    -      border: {
    -        type: 'line'
    -      },
    -      top: 16
    -    });
    -
    -    self.elements['outputlist'] = blessed.table({
    -      parent: self.elements.outputbook,
    -      data: [
    -        ['syncing...']
    -      ],
    -      width: '100%-2',
    -      top: 0,
    -      bottom: 0
    -    });
    -
    -    // Blockchain explorer panel - browse the canonical record
    -    self.elements['blockchainBox'] = blessed.box({
    -      parent: self.screen,
    -      label: '{bold}[ Blockchain ]{/bold}',
    -      tags: true,
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      bottom: 4,
    -      width: '100%'
    -    });
    -
    -    self.elements['blockchainHeader'] = blessed.text({
    -      parent: self.elements['blockchainBox'],
    -      tags: true,
    -      top: 1,
    -      left: 2,
    -      right: 2,
    -      height: 3,
    -      content: 'Height: --  |  Tip: loading...\n{bold}Canonical record{/bold} - Use /block <height>, /tx <txid>, /address <addr>'
    -    });
    -
    -    self.elements['blockchainContent'] = blessed.box({
    -      parent: self.elements['blockchainBox'],
    -      tags: true,
    -      top: 4,
    -      left: 2,
    -      right: 2,
    -      bottom: 2,
    -      mouse: true,
    -      keys: true,
    -      vi: true,
    -      scrollable: true,
    -      alwaysScroll: true,
    -      scrollbar: {
    -        ch: ' ',
    -        style: {
    -          bg: 'blue',
    -          fg: 'white'
    -        }
    -      },
    -      content: 'Loading chain tip...'
    -    });
    -
    -    self.elements['menu'] = blessed.listbar({
    -      parent: self.screen,
    -      top: '100%-1',
    -      left: 0,
    -      right: 8,
    -      style: {
    -        selected: {
    -          background: 'white',
    -          border: '1'
    -        }
    -      },
    -      commands: {
    -        'Home': {
    -          keys: ['f1'],
    -          callback: function () {
    -            this.setPane('home');
    -          }.bind(this)
    -        },
    -        'Console': {
    -          keys: ['f2'],
    -          callback: function () {
    -            this.setPane('messages');
    -            return true;
    -          }.bind(this)
    -        },
    -        'Network': {
    -          keys: ['f3'],
    -          callback: function () {
    -            this.setPane('network');
    -          }.bind(this)
    -        },
    -        'Wallet': {
    -          keys: ['f4'],
    -          callback: function () {
    -            this.setPane('wallet');
    -          }.bind(this)
    -        },
    -        'Contracts': {
    -          keys: ['f5'],
    -          callback: function () {
    -            this.setPane('contracts');
    -          }.bind(this)
    -        },
    -        'Blockchain': {
    -          keys: ['f6'],
    -          callback: function () {
    -            this.setPane('blockchain');
    -          }.bind(this)
    -        },
    -      }
    -    });
    -
    -    // Remove Info block and move identity to FABRIC (formerly Status)
    -    self.elements['fabric'] = blessed.box({
    -      parent: self.screen,
    -      label: '{bold}[ FABRIC ]{/bold}',
    -      tags: true,
    -      border: {
    -        type: 'line'
    -      },
    -      top: 0,
    -      height: 6,
    -      width: '100%'
    -    });
    -
    -    self.elements['identity'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      left: 1,
    -      top: 0
    -    });
    -    self.elements['identityLabel'] = blessed.text({
    -      parent: self.elements['identity'],
    -      content: 'IDENTITY:',
    -      top: 0,
    -      bold: true
    -    });
    -    self.elements['identityString'] = blessed.text({
    -      parent: self.elements['identity'],
    -      content: 'loading...',
    -      top: 0,
    -      left: 10
    -    });
    -
    -    // Update all children of Status to use 'fabric' as parent
    -    // Replace all self.elements['status'] with self.elements['fabric'] below
    -    self.elements['wallet'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      right: 1,
    -      width: 29,
    -      height: 4
    -    });
    -
    -    self.elements['balance'] = blessed.text({
    -      parent: self.elements['wallet'],
    -      content: '0.00000000',
    -      top: 0,
    -      right: 4
    -    });
    -
    -    self.elements['label'] = blessed.text({
    -      parent: self.elements['wallet'],
    -      content: 'BALANCE:',
    -      top: 0,
    -      right: 29,
    -      bold: true
    -    });
    -
    -    self.elements['denomination'] = blessed.text({
    -      parent: self.elements['wallet'],
    -      content: 'BTC',
    -      top: 0,
    -      right: 0
    -    });
    -
    -    self.elements['unconfirmed'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      top: 1,
    -      left: 1
    -    });
    -
    -    self.elements['unconfirmedLabel'] = blessed.text({
    -      parent: self.elements['unconfirmed'],
    -      content: 'UNCONFIRMED:',
    -      top: 0,
    -      right: 30,
    -      bold: true
    -    });
    -
    -    self.elements['unconfirmedValue'] = blessed.text({
    -      parent: self.elements['unconfirmed'],
    -      content: 'syncing...',
    -      top: 0,
    -      right: 1
    -    });
    -
    -    self.elements['bonded'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      top: 2,
    -      left: 1
    -    });
    -
    -    self.elements['bondedLabel'] = blessed.text({
    -      parent: self.elements['bonded'],
    -      content: 'BONDED:',
    -      top: 0,
    -      right: 30,
    -      bold: true
    -    });
    -
    -    self.elements['bondedValue'] = blessed.text({
    -      parent: self.elements['bonded'],
    -      content: 'syncing...',
    -      top: 0,
    -      right: 1
    -    });
    -
    -    self.elements['progress'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      top: 3,
    -      left: 1
    -    });
    -
    -    self.elements['progressLabel'] = blessed.text({
    -      parent: self.elements['progress'],
    -      content: 'SYNC:',
    -      top: 0,
    -      right: 30,
    -      bold: true
    -    });
    -
    -    self.elements['progressStatus'] = blessed.text({
    -      parent: self.elements['progress'],
    -      content: 'syncing...',
    -      top: 0,
    -      right: 1
    -    });
    -
    -    self.elements['chain'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      top: 1,
    -      left: 1,
    -      width: 50
    -    });
    -
    -    self.elements['chainLabel'] = blessed.text({
    -      parent: self.elements['chain'],
    -      content: 'CHAIN TIP:',
    -      bold: true
    -    });
    -
    -    self.elements['chainTip'] = blessed.text({
    -      parent: self.elements['chain'],
    -      content: 'loading...',
    -      left: 11,
    -      width: 50
    -    });
    -
    -    self.elements['height'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      top: 2,
    -      left: 1,
    -      width: 62
    -    });
    -
    -    self.elements['heightLabel'] = blessed.text({
    -      parent: self.elements['height'],
    -      content: 'CHAIN HEIGHT:',
    -      bold: true
    -    });
    -
    -    self.elements['heightValue'] = blessed.text({
    -      parent: self.elements['height'],
    -      content: 'loading...',
    -      left: 14,
    -      width: 50
    -    });
    -
    -    self.elements['mempool'] = blessed.box({
    -      parent: self.elements['fabric'],
    -      top: 3,
    -      left: 1,
    -      width: 29
    -    });
    -
    -    self.elements['mempoolLabel'] = blessed.text({
    -      parent: self.elements['mempool'],
    -      content: 'MEMPOOL SIZE:',
    -      bold: true
    -    });
    -
    -    self.elements['mempoolCount'] = blessed.text({
    -      parent: self.elements['mempool'],
    -      content: '0',
    -      left: 14
    -    });
    -
    -    // MAIN LOG OUTPUT
    -    self.elements['messages'] = blessed.log({
    -      parent: this.screen,
    -      label: '{bold}[ Console ]{/bold}',
    -      tags: true,
    -      border: {
    -        type: 'line'
    -      },
    -      scrollbar: {
    -        style: {
    -          bg: 'white',
    -          fg: 'blue'
    -        }
    -      },
    -      top: 6, // directly below FABRIC block (height: 6)
    -      width: '80%',
    -      bottom: 4, // keep controls visible at the bottom
    -      mouse: true,
    -      tags: true
    -    });
    -
    -    // Add message details pane
    -    self.elements['messageDetails'] = blessed.box({
    -      parent: self.screen,
    -      label: '{bold}[ Message Details ]{/bold}',
    -      tags: true,
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      left: '80%+1',
    -      bottom: 4,
    -      width: '20%',
    -      hidden: false // Show by default
    -    });
    -
    -    self.elements['messageContent'] = blessed.text({
    -      parent: self.elements['messageDetails'],
    -      tags: true,
    -      top: 1,
    -      left: 1,
    -      right: 1,
    -      bottom: 1,
    -      wrap: true
    -    });
    -
    -    // Add click handler for messages
    -    self.elements['messages'].on('click', function (data) {
    -      // Get the visible messages from the log
    -      const messages = self.elements['messages'].getLines();
    -      // Calculate which message was clicked based on the y position
    -      const messageIndex = data.y - self.elements['messages'].top - 1;
    -      if (messageIndex >= 0 && messageIndex < messages.length) {
    -        const message = messages[messageIndex];
    -        self._showMessageDetails(message);
    -        // Ensure the details pane is visible
    -        self.elements['messageDetails'].show();
    -        self.screen.render();
    -      }
    -    });
    -
    -    self.elements['peers'] = blessed.list({
    -      parent: self.screen,
    -      label: '{bold}[ Peers ]{/bold}',
    -      tags: true,
    -      border: {
    -        type: 'line'
    -      },
    -      top: 6,
    -      left: '80%+1',
    -      bottom: 4
    -    });
    -
    -    self.elements['controls'] = blessed.box({
    -      parent: this.screen,
    -      label: '{bold}[ INPUT ]{/bold}',
    -      tags: true,
    -      bottom: 1,
    -      height: 3,
    -      border: {
    -        type: 'line'
    -      }
    -    });
    -
    -    self.elements['form'] = blessed.form({
    -      parent: self.elements['controls'],
    -      bottom: 0,
    -      height: 1,
    -      left: 1
    -    });
    -
    -    self.elements['prompt'] = blessed.textbox({
    -      parent: self.elements['form'],
    -      name: 'input',
    -      input: true,
    -      keys: true,
    -      inputOnFocus: true,
    -      value: INPUT_HINT,
    -      style: {
    -        fg: 'grey'
    -      }
    -    });
    -
    -    // Set Index for Command History
    -    this.elements['prompt'].historyIndex = -1;
    -
    -    // Render the screen.
    -    self.screen.render();
    -    self._bindKeys();
    -
    -    // TODO: clean up workaround (from https://github.com/chjj/blessed/issues/109)
    -    self.elements['prompt'].oldFocus = self.elements['prompt'].focus;
    -    self.elements['prompt'].focus = function () {
    -      let oldListener = self.elements['prompt'].__listener;
    -      let oldBlur = self.elements['prompt'].__done;
    -
    -      self.elements['prompt'].removeListener('keypress', self.elements['prompt'].__listener);
    -      self.elements['prompt'].removeListener('blur', self.elements['prompt'].__done);
    -
    -      delete self.elements['prompt'].__listener;
    -      delete self.elements['prompt'].__done;
    -
    -      self.elements['prompt'].screen.focusPop(self.elements['prompt'])
    -
    -      self.elements['prompt'].addListener('keypress', oldListener);
    -      self.elements['prompt'].addListener('blur', oldBlur);
    -
    -      self.elements['prompt'].oldFocus();
    -    };
    -
    -    // focus when clicked
    -    self.elements['form'].on('click', function () {
    -      self.elements['prompt'].focus();
    -    });
    -
    -    self.elements['form'].on('submit', self._handleFormSubmit.bind(self));
    -    // this.focusInput();
    -
    -    this.elements['identityString'].setContent(this.identity.id);
    -    this.setPane('messages');
    -
    -    setInterval(function () {
    -      // self._appendMessage('10 seconds have passed.');
    -      // self.bitcoin.generateBlock();
    -    }, 10000);
    -
    -    // Enable mouse support
    -    self.screen.program.enableMouse();
    -  }
    -
    -  tableDataFor (input = [], exclusions = []) {
    -    const keys = [];
    -    const entries = input.map((x) => {
    -      const map = {};
    -
    -      for (const [key, value] of Object.entries(x)) {
    -        if (exclusions.includes(key)) continue;
    -        if (!keys.includes(key)) keys.push(key);
    -        map[key] = value.toString();
    -      }
    -
    -      return Object.values(map);
    -    });
    -
    -    return [ keys ].concat(entries);
    -  }
    -
    -  _showMessageDetails (message) {
    -    // Parse the message content
    -    const timestamp = message.match(/\[(.*?)\]/)?.[1] || 'Unknown';
    -    const content = message.replace(/\[.*?\]\s*/, '').trim();
    -
    -    // Format the details
    -    const details = [
    -      '{bold}Timestamp:{/bold} ' + timestamp,
    -      '{bold}Content:{/bold} ' + content,
    -      '{bold}Length:{/bold} ' + content.length + ' characters'
    -    ].join('\n\n');
    -
    -    // Update the details pane
    -    this.elements['messageContent'].setContent(details);
    -    this.screen.render();
    -  }
    -
    -  async _handleWalletCommand (params) {
    -    if (!params[1]) {
    -      this._appendMessage('Available wallet commands:');
    -      this._appendMessage('  wallet balance - Show current balance');
    -      this._appendMessage('  wallet send <address> <amount> - Send funds to address');
    -      this._appendMessage('  wallet receive - Generate a new receive address');
    -      this._appendMessage('  wallet unspent - List unspent outputs');
    -      return false;
    -    }
    -
    -    switch (params[1]) {
    -      case 'balance':
    -        return this._handleBalanceRequest(params);
    -      case 'send':
    -        return this._handleSendRequest(params);
    -      case 'receive':
    -        return this._handleReceiveAddressRequest(params);
    -      case 'unspent':
    -        return this._handleUnspentRequest(params);
    -      default:
    -        this._appendError(`Unknown wallet command: ${params[1]}`);
    -        return false;
    -    }
    -  }
    -
    -  async _checkLocalBitcoindOnline (settings) {
    -    // Try to connect to a local bitcoind using the provided settings
    -    const jayson = require('jayson/lib/client');
    -    return new Promise((resolve) => {
    -      try {
    -        const config = {
    -          host: settings.host || '127.0.0.1',
    -          port: settings.rpcport || 8332,
    -          timeout: 3000
    -        };
    -        if (settings.username && settings.password) {
    -          config.headers = {
    -            Authorization: `Basic ${Buffer.from(settings.username + ':' + settings.password, 'utf8').toString('base64')}`
    -          };
    -        }
    -        const rpc = settings.secure ? jayson.https(config) : jayson.http(config);
    -        rpc.request('getblockchaininfo', [], (err, response) => {
    -          if (err || !response || response.error) {
    -            resolve(false);
    -          } else {
    -            resolve(true);
    -          }
    -        });
    -      } catch (e) {
    -        resolve(false);
    -      }
    -    });
    -  }
    -}
    -
    -module.exports = CLI;
    -
    -
    -
    - - - -
    - -
    - - - - - - \ No newline at end of file diff --git a/docs/types_collection.js.html b/docs/types_collection.js.html index a46364a61..5f88aae0b 100644 --- a/docs/types_collection.js.html +++ b/docs/types_collection.js.html @@ -132,7 +132,7 @@

    Source: types/collection.js

    try { if (this.settings.verbosity >= 5) console.log(`getting ${this.path}/${id} from:`, this.value); result = pointer.get(this.value, `${this.path}/${id}`); - } catch (E) { + } catch { // console.debug('[FABRIC:COLLECTION]', `@${this.name}`, Date.now(), `Could not find ID "${id}" in tree ${this.asMerkleTree()}`); } @@ -548,14 +548,18 @@

    Classes

    Global


    diff --git a/docs/types_compiler.js.html b/docs/types_compiler.js.html index 17356925a..a7970aef0 100644 --- a/docs/types_compiler.js.html +++ b/docs/types_compiler.js.html @@ -53,6 +53,7 @@

    Source: types/compiler.js

    const Entity = require('./entity'); const Hash256 = require('./hash256'); const Machine = require('./machine'); +const { blessedParamsFromJadeAttrs } = require('../functions/wireJson'); // const Ethereum = require('../services/ethereum'); // TODO: have Lexer review @@ -121,12 +122,12 @@

    Source: types/compiler.js

    } static _fromMinsc (body) { - if (!(body instanceof Buffer)) throw new Error('JavaScript must be passed as a buffer.'); + if (!(body instanceof Buffer)) throw new Error('Miniscript must be passed as a buffer.'); return new Compiler({ body, type: 'minsc' }); } static _fromSolidity (body) { - if (!(body instanceof Buffer)) throw new Error('JavaScript must be passed as a buffer.'); + if (!(body instanceof Buffer)) throw new Error('Solidity must be passed as a buffer.'); return new Compiler({ body, type: 'solidity' }); } @@ -156,7 +157,7 @@

    Source: types/compiler.js

    return this; } - _getScriptAST (input) { + _getScriptAST (_input) { throw new Error('Not yet supported.'); return null; } @@ -235,23 +236,7 @@

    Source: types/compiler.js

    let space = ' '.repeat(depth * 2); // result += depth; - let attrs = []; - let params = {}; - for (let a in ast.attrs) { - let attr = ast.attrs[a]; - attrs.push(attr.name + '=' + attr.val); - - if (attr.val[0] === "'") { - let content = attr.val.substring(1, attr.val.length - 1); - if (content[0] === '{') { - params[attr.name] = JSON.parse(content); - } else { - params[attr.name] = content; - } - } else { - params[attr.name] = JSON.parse(attr.val); - } - } + const { attrs, params } = blessedParamsFromJadeAttrs(ast.attrs); params.parent = screen; @@ -286,7 +271,7 @@

    Source: types/compiler.js

    return result; } - _renderToHTML (state = {}) { + _renderToHTML (_state = {}) { return `<!DOCTYPE html> <html> <head> @@ -320,14 +305,18 @@

    Classes

    Global


    diff --git a/docs/types_contract.js.html b/docs/types_contract.js.html index 264ed3c05..7fa846939 100644 --- a/docs/types_contract.js.html +++ b/docs/types_contract.js.html @@ -49,6 +49,12 @@

    Source: types/contract.js

    const Message = require('./message'); const Service = require('./service'); +/** + * Service-backed agreement template: DOT graphs, a derived circuit structure, deploy/genesis/publish flows, + * and JSON-Patch–observed commits. Specialized by {@link Bond}, {@link Federation}, and {@link Distribution}. + * @class Contract + * @extends Service + */ class Contract extends Service { constructor (settings = {}) { super(settings); @@ -106,15 +112,37 @@

    Source: types/contract.js

    }; static fromJavaScript (js) { - const buildAST = Template.template(js); - const ast = buildAST({}); - return new Contract({ ast }); + if (typeof js !== 'string') { + throw new TypeError('JavaScript source must be a string.'); + } + + const source = js.trim(); + const ast = { + '@type': 'AST', + '@language': 'JavaScript', + source + }; + + return new Contract({ + ast, + state: { + name: 'TemplateContract', + status: 'PAUSED', + actors: [], + balances: {}, + constraints: {}, + signatures: [], + source + } + }); } static fromGraph (graphs) { const circuit = { stack: [], - nodes: [] + nodes: [], + edges: [], + unhandled: [] }; for (let i = 0; i < graphs.length; i++) { @@ -130,8 +158,25 @@

    Source: types/contract.js

    const child = graph.children[j]; switch (child.type) { default: - console.warn(`Unhandled type: "${child.type}'" on child:`, child); + circuit.unhandled.push({ + type: child.type, + child + }); + break; + case 'edge_stmt': { + const list = Array.isArray(child.edge_list) ? child.edge_list : []; + for (let k = 0; k < list.length - 1; k++) { + const from = list[k]; + const to = list[k + 1]; + if (!from || !to) continue; + if (!from.id || !to.id) continue; + circuit.edges.push({ + from: from.id, + to: to.id + }); + } break; + } case 'node_stmt': circuit.nodes.push({ name: child.node_id.id @@ -213,13 +258,20 @@

    Source: types/contract.js

    } toDot () { - const tokens = []; } parse (input) { return this.parseDot(input); } + parseDot (input) { + const contract = Contract.fromDot(input); + this.settings.graphs = contract.settings.graphs; + this.settings.circuit = contract.settings.circuit; + this.graphs = contract.graphs; + return this; + } + /** * Start the Contract. * @returns {Contract} State "STARTED" iteration of the Contract. @@ -311,6 +363,67 @@

    Source: types/contract.js

    script: this.contract }; } + + /** + * Build deterministic P2TR spend tree from settings.spendLadder or synthesized + * validators / publisher policy. + * @param {object} [overrides] + * @returns {object} {@link module:functions/contractTaproot.buildContractTaproot} + */ + /** + * Shared spend-policy inputs for {@link #toTaprootContract}. + * Subclasses (e.g. Federation) may override to supply validators from state. + * @param {object} [overrides] + * @returns {object} + */ + _taprootPolicyInputs (overrides = {}) { + const tap = require('../functions/contractTaproot'); + const validators = overrides.validators + || (this.settings.proposedPolicy && this.settings.proposedPolicy.validators) + || (this.settings.consensus && this.settings.consensus.validators) + || this.settings.validators + || []; + const threshold = overrides.threshold != null + ? overrides.threshold + : ((this.settings.proposedPolicy && this.settings.proposedPolicy.threshold) + || this.settings.threshold + || 1); + const publisher = overrides.publisher + || this.settings.publisher + || this.settings.creator + || (validators[0] || null); + const network = overrides.network + || this.settings.network + || 'regtest'; + const csvBlocks = overrides.csvBlocks != null + ? overrides.csvBlocks + : (this.settings.csvBlocks != null ? this.settings.csvBlocks : tap.DEFAULT_CSV_BLOCKS); + return { validators, threshold, publisher, network, csvBlocks }; + } + + toTaprootContract (overrides = {}) { + const tap = require('../functions/contractTaproot'); + const ladder = overrides.spendLadder || this.settings.spendLadder || null; + if (ladder) { + const network = overrides.network || this.settings.network || ladder.network; + return tap.buildContractTaproot({ ...ladder, ...overrides, network }); + } + const inputs = this._taprootPolicyInputs(overrides); + return tap.buildContractTaproot(tap.synthesizeDefaultLadder({ + ...inputs, + ...overrides + })); + } + + /** + * Bech32m P2TR address for this contract's spend policy. + * @param {string} [network] + * @returns {string} + */ + toAddress (network) { + const built = this.toTaprootContract(network ? { network } : {}); + return built.address; + } } module.exports = Contract; @@ -327,14 +440,18 @@

    Classes

    Global


    diff --git a/docs/types_datastore.js.html b/docs/types_datastore.js.html new file mode 100644 index 000000000..f93d0b9ca --- /dev/null +++ b/docs/types_datastore.js.html @@ -0,0 +1,228 @@ + + + + + + Source: types/datastore.js · Docs + + + + + + + + + +
    +

    Source: types/datastore.js

    + + + + +
    +
    +
    'use strict';
    +
    +/**
    + * @fileoverview Ledger-adjacent {@link Store} extension. Consolidation with {@link Store}
    + * is planned (single persistence surface + optional ledger facet); left as-is until that
    + * design is scheduled.
    + */
    +
    +const fs = require('fs');
    +const monitor = require('fast-json-patch');
    +
    +const Store = require('./store');
    +const Ledger = require('./ledger');
    +const Transaction = require('./transaction');
    +
    +class Datastore extends Store {
    +  constructor (state) {
    +    super(state);
    +
    +    this.config = Object.assign({
    +      path: './stores/datastore'
    +    }, state);
    +
    +    this.ledger = new Ledger();
    +    this.data = {};
    +
    +    this.observer = monitor.observe(this.state['@data']);
    +
    +    return this;
    +  }
    +
    +  route (path) {
    +    let parts = path.split('/');
    +    if (!parts.length) return '/';
    +    switch (parts.length) {
    +      case 1:
    +        return path;
    +      case 2:
    +        return parts[1];
    +      default:
    +        return null;
    +    }
    +  }
    +
    +  register (identity) {
    +    this.identity = identity;
    +  }
    +
    +  _loadFrom (dir) {
    +    let self = this;
    +    let files = fs.readdirSync(dir);
    +
    +    this.log('_loadFrom', dir, 'files:', files);
    +
    +    for (let i = 0; i < files.length; i++) {
    +      let content = fs.readFileSync(files[i]);
    +      self.log('_loadFrom', 'content:', content);
    +      self['@data'][files[i]] = content;
    +    }
    +  }
    +
    +  _apply (delta) {
    +    let datastore = this;
    +    let document = monitor.applyPatch(datastore['@data'], delta);
    +
    +    datastore._sign();
    +    document.commit();
    +
    +    this.log('[DATASTORE]', '_apply', 'document:', document);
    +    this.log('[DATASTORE]', '_apply', 'datastore:', datastore);
    +
    +    return datastore['@data'];
    +  }
    +
    +  ping () {
    +    let datastore = this;
    +    let ping = new Transaction({
    +      entropy: Math.random(),
    +      timestamp: Date.now(),
    +      identity: datastore.identity
    +    });
    +
    +    ping._sign(datastore.identity);
    +
    +    this['@data']['/pings'].ledger.append(ping);
    +    // this['@data']['/pings'].ledger.compute();
    +    // this.compute();
    +  }
    +
    +  pong () {
    +    this.log('PONG!');
    +  }
    +
    +  spam () {
    +    var datastore = this;
    +    for (var i = 0; i < 10; i++) {
    +      datastore.ping();
    +    }
    +  }
    +
    +  render () {
    +    return `<Datastore />`;
    +  }
    +}
    +
    +module.exports = Datastore;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_disk.js.html b/docs/types_disk.js.html new file mode 100644 index 000000000..22b2e377b --- /dev/null +++ b/docs/types_disk.js.html @@ -0,0 +1,150 @@ + + + + + + Source: types/disk.js · Docs + + + + + + + + + +
    +

    Source: types/disk.js

    + + + + +
    +
    +
    'use strict';
    +
    +const fs = require('fs');
    +
    +/**
    + * Minimal host-path file accessor. Roadmap: align with virtual-node / overlay filesystem
    + * work (namespaces, lazy mounts, remote-backed paths) once that stack lands — no behavioral
    + * change here until then.
    + * @class Disk
    + */
    +class Disk {
    +  constructor (root) {
    +    this.type = 'Disk';
    +    this.root = root || process.env.PWD;
    +  }
    +
    +  exists (path) {
    +    let full = [this.root, path].join('/');
    +    return fs.existsSync(full);
    +  }
    +
    +  get (path) {
    +    let full = [this.root, path].join('/');
    +    return require(full);
    +  }
    +}
    +
    +module.exports = Disk;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_distributedExecution.js.html b/docs/types_distributedExecution.js.html index 4eaf9f058..d2eec1508 100644 --- a/docs/types_distributedExecution.js.html +++ b/docs/types_distributedExecution.js.html @@ -34,142 +34,28 @@

    Source: types/distributedExecution.js

    'use strict';
     
     /**
    - * Shared helpers for multi-operator contract execution: canonical payloads,
    - * beacon epoch signing strings, and federation signature verification.
    + * @deprecated Not a Fabric type. Prefer:
    + * - `functions/fabricCanonicalJson` (jsonSafe / stableStringify)
    + * - `functions/beaconFederationSigning` (epoch signing / federation verify)
    + * - `functions/fabricProgramManifest` / `Machine.parseManifest` (manifest v1)
    + * - `types/program` + `types/machine` for execution
      *
    - * Used by Hub Beacon, HTTP manifest routes (`@fabric/http`), and peers that
    - * must reject messages outside the agreed program.
    + * Thin re-export kept for one release so Hub / older requires keep working.
      */
    -const crypto = require('crypto');
    -const Key = require('./key');
     
    -/** @type {string} */
    -const BEACON_EPOCH_SIGNING_KIND = 'BeaconEpoch';
    -
    -/**
    - * Deterministic JSON (sorted object keys) for hashing and signing.
    - * @param {*} value
    - * @returns {string}
    - */
    -function stableStringify (value) {
    -  if (value === null || typeof value !== 'object') {
    -    return JSON.stringify(value);
    -  }
    -  if (Array.isArray(value)) {
    -    return '[' + value.map((v) => stableStringify(v)).join(',') + ']';
    -  }
    -  const keys = Object.keys(value).sort();
    -  return '{' + keys.map((k) => JSON.stringify(k) + ':' + stableStringify(value[k])).join(',') + '}';
    -}
    -
    -/**
    - * Drop `undefined` and normalize values the same way JSON.parse(JSON.stringify) does.
    - * @param {*} value
    - * @returns {*}
    - */
    -function jsonSafe (value) {
    -  return JSON.parse(JSON.stringify(value));
    -}
    -
    -/**
    - * UTF-8 string that federation members sign for a beacon epoch (same bytes for all validators).
    - * @param {object} epochPayload — clock, blockHash, height, balance, balanceSats, timestamp, …
    - * @returns {string}
    - */
    -function signingStringForBeaconEpoch (epochPayload) {
    -  const safe = jsonSafe(epochPayload);
    -  return stableStringify({
    -    version: 1,
    -    kind: BEACON_EPOCH_SIGNING_KIND,
    -    epoch: safe
    -  });
    -}
    -
    -/**
    - * SHA-256 hex digest of {@link signingStringForBeaconEpoch} (public commitment).
    - * @param {object} epochPayload
    - * @returns {string}
    - */
    -function epochCommitmentDigestHex (epochPayload) {
    -  const s = signingStringForBeaconEpoch(epochPayload);
    -  return crypto.createHash('sha256').update(Buffer.from(s, 'utf8')).digest('hex');
    -}
    -
    -/**
    - * Verify threshold Schnorr signatures over the **same** message buffer used when signing
    - * (`Key.signSchnorr(messageBuffer)`), without requiring a full {@link Federation} instance.
    - *
    - * @param {Buffer} messageBuffer — typically `Buffer.from(signingStringForBeaconEpoch(epoch), 'utf8')`
    - * @param {object} witness
    - * @param {string[]} validatorPubkeys — compressed secp256k1 pubkeys, hex
    - * @param {number} [threshold=1]
    - * @returns {boolean}
    - */
    -function verifyFederationWitnessOnMessage (messageBuffer, witness, validatorPubkeys, threshold = 1) {
    -  if (!witness || !witness.signatures || typeof witness.signatures !== 'object') return false;
    -  if (!Buffer.isBuffer(messageBuffer)) return false;
    -  const pubkeys = Array.isArray(validatorPubkeys) ? validatorPubkeys : [];
    -  const thr = Math.max(1, Number(threshold) || 1);
    -  let valid = 0;
    -  for (const pubkey of pubkeys) {
    -    if (typeof pubkey !== 'string' || !pubkey) continue;
    -    const sigHex = witness.signatures[pubkey];
    -    if (!sigHex || typeof sigHex !== 'string') continue;
    -    try {
    -      const k = new Key({ pubkey });
    -      const sig = Buffer.from(sigHex, 'hex');
    -      if (k.verifySchnorr(messageBuffer, sig)) valid++;
    -      if (valid >= thr) return true;
    -    } catch (_) {
    -      /* ignore */
    -    }
    -  }
    -  return false;
    -}
    -
    -/**
    - * Setup-phase manifest schema (v1): program identity + allowed traffic + optional federation policy.
    - * @param {object} raw
    - * @returns {object}
    - */
    -function parseDistributedManifestV1 (raw) {
    -  if (!raw || typeof raw !== 'object') return { ok: false, error: 'manifest must be an object' };
    -  const version = Number(raw.version);
    -  if (version !== 1) return { ok: false, error: 'unsupported manifest version' };
    -  const programId = typeof raw.programId === 'string' ? raw.programId.trim() : '';
    -  const programHash = typeof raw.programHash === 'string' ? raw.programHash.trim() : '';
    -  if (!programId || !programHash) return { ok: false, error: 'programId and programHash are required' };
    -  const allowedMessageTypes = Array.isArray(raw.allowedMessageTypes)
    -    ? raw.allowedMessageTypes.filter((t) => typeof t === 'string')
    -    : [];
    -  const federation = raw.federation && typeof raw.federation === 'object'
    -    ? {
    -        validators: Array.isArray(raw.federation.validators)
    -          ? raw.federation.validators.filter((v) => typeof v === 'string')
    -          : [],
    -        threshold: Math.max(1, Number(raw.federation.threshold) || 1)
    -      }
    -    : null;
    -  return {
    -    ok: true,
    -    manifest: {
    -      version: 1,
    -      programId,
    -      programHash,
    -      allowedMessageTypes,
    -      federation
    -    }
    -  };
    -}
    +const fabricCanonicalJson = require('../functions/fabricCanonicalJson');
    +const beaconFederationSigning = require('../functions/beaconFederationSigning');
    +const { parseProgramManifestV1, parseDistributedManifestV1 } = require('../functions/fabricProgramManifest');
     
     module.exports = {
    -  stableStringify,
    -  jsonSafe,
    -  signingStringForBeaconEpoch,
    -  epochCommitmentDigestHex,
    -  verifyFederationWitnessOnMessage,
    +  stableStringify: fabricCanonicalJson.stableStringify || fabricCanonicalJson,
    +  jsonSafe: fabricCanonicalJson.jsonSafe,
    +  signingStringForBeaconEpoch: beaconFederationSigning.signingStringForBeaconEpoch,
    +  epochCommitmentDigestHex: beaconFederationSigning.epochCommitmentDigestHex,
    +  verifyFederationWitnessOnMessage: beaconFederationSigning.verifyFederationWitnessOnMessage,
       parseDistributedManifestV1,
    -  BEACON_EPOCH_SIGNING_KIND
    +  parseProgramManifestV1,
    +  BEACON_EPOCH_SIGNING_KIND: beaconFederationSigning.BEACON_EPOCH_SIGNING_KIND
     };
     
    @@ -184,14 +70,18 @@

    Classes

    Global


    diff --git a/docs/types_document.js.html b/docs/types_document.js.html index 65410fa6b..fb105f807 100644 --- a/docs/types_document.js.html +++ b/docs/types_document.js.html @@ -33,9 +33,9 @@

    Source: types/document.js

    'use strict';
     
    -const Vector = require('./vector');
    +const State = require('./state');
     
    -class Document extends Vector {
    +class Document extends State {
       constructor (doc) {
         super(doc);
     
    @@ -48,14 +48,14 @@ 

    Source: types/document.js

    } /** - * Compiles an `input` {@link Vector}. + * Compiles an `input` {@link State} snapshot (signed instruction payload). * ENV provides the name of a parent type (e.g., "scaffold" or "0xDEADBEEF...") * UI provides the name of the desired component - * @param {Vector} input [env, ui] + * @param {Array|Object} input [env, ui] * @return {Promise} Promise which resolves on compilation. */ async compile (input) { - let vector = new Vector(input)._sign(); + let vector = new State(input)._sign(); let contract = [ `extends ${input[0]}`, `block body`, @@ -88,14 +88,18 @@

    Classes

    Global


    diff --git a/docs/types_ecc.selftest.js.html b/docs/types_ecc.selftest.js.html new file mode 100644 index 000000000..900adb85e --- /dev/null +++ b/docs/types_ecc.selftest.js.html @@ -0,0 +1,150 @@ + + + + + + Source: types/ecc.selftest.js · Docs + + + + + + + + + +
    +

    Source: types/ecc.selftest.js

    + + + + +
    +
    +
    'use strict';
    +
    +/**
    + * Lightweight ECC sanity check for real browsers (see types/ecc.js).
    + * @param {object} ecc — exports from types/ecc.js
    + */
    +module.exports = function runFabricEccSelftest (ecc) {
    +  try {
    +    const priv = Buffer.alloc(32, 0);
    +    priv[31] = 1;
    +    if (!ecc.isPrivate(priv)) {
    +      console.error('[fabric/ecc] self-test: isPrivate(1) expected true');
    +      return;
    +    }
    +    const pub = ecc.pointFromScalar(priv, true);
    +    if (!pub || pub.length < 33) {
    +      console.error('[fabric/ecc] self-test: pointFromScalar failed');
    +      return;
    +    }
    +    const msg = Buffer.alloc(32, 7);
    +    const sig = ecc.sign(msg, priv);
    +    if (!ecc.verify(msg, pub, sig)) {
    +      console.error('[fabric/ecc] self-test: verify failed');
    +    }
    +  } catch (e) {
    +    console.error('[fabric/ecc] self-test failed:', e && e.message ? e.message : e);
    +  }
    +};
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_entity.js.html b/docs/types_entity.js.html index 424ba27e1..efff84a0c 100644 --- a/docs/types_entity.js.html +++ b/docs/types_entity.js.html @@ -38,6 +38,14 @@

    Source: types/entity.js

    const monitor = require('fast-json-patch'); const Witness = require('./witness'); +/** + * @classdesc <strong>Structured document</strong> type: extends {@link EventEmitter} (not {@link Actor}) with + * <code>@type</code> / <code>@data</code> shape, JSON serialization, and <code>id</code> = SHA256(<code>toJSON()</code>). + * <strong>Different model from {@link Actor#id}</strong> (sorted generic envelope). <code>Entity.Transition</code> (JSON Patch + * between entity states) is the supported migration path — see <strong>DEVELOPERS.md</strong> (<em>Consolidated prototypes</em>). + * @class Entity + * @extends EventEmitter + */ class Entity extends EventEmitter { constructor (data = {}) { super(data); @@ -85,7 +93,7 @@

    Source: types/entity.js

    let entity = this; return function buffer () { return Buffer.from(entity.toJSON(), 'utf8'); - } + }; } get id () { @@ -134,7 +142,7 @@

    Source: types/entity.js

    result = JSON.stringify(this.actor['@data']); break; case 'Buffer': - const buffer = new Uint8Array(this.data); + void new Uint8Array(this.data); const values = Object.values(this.data); result = JSON.stringify(values); break; @@ -208,6 +216,14 @@

    Source: types/entity.js

    } } +/** + * @classdesc JSON Patch <strong>diff</strong> between two {@link Entity} snapshots (<code>origin</code>, + * <code>target</code>, <code>changes</code>). Built via {@link Transition.between}, {@link Transition#fromTarget}, or manual + * <code>changes</code>; uses <code>fast-json-patch</code> observe/generate. + * @memberof Entity + * @class Transition + * @extends Entity + */ class Transition extends Entity { constructor (settings = {}) { super(settings); @@ -312,14 +328,18 @@

    Classes

    Global


    diff --git a/docs/types_environment.js.html b/docs/types_environment.js.html index 964966cc8..0c454b65d 100644 --- a/docs/types_environment.js.html +++ b/docs/types_environment.js.html @@ -37,6 +37,7 @@

    Source: types/environment.js

    const { FIXTURE_SEED } = require('../constants'); +const { tryParsePersistedJson } = require('../functions/wireJson'); // Dependencies const fs = require('fs'); @@ -47,7 +48,6 @@

    Source: types/environment.js

    // Fabric Types const Actor = require('./actor'); const Entity = require('./entity'); -const EncryptedPromise = require('./promise'); const Wallet = require('./wallet'); // Filters @@ -116,12 +116,23 @@

    Source: types/environment.js

    } get seed () { - return [ - FIXTURE_SEED, + // Precedence: settings / env only. CWD `.FABRIC_SEED` (`this.local`) is opt-in + // via `settings.allowCwdSeed` so a planted file cannot outrank `~/.fabric/wallet.json`. + const explicit = [ this.settings.seed, this['FABRIC_SEED'], this.readVariable('FABRIC_SEED') - ].find(any); + ]; + if (this.settings.allowCwdSeed === true) { + explicit.push(this.local); + } + const normalized = explicit.map((candidate) => ( + typeof candidate === 'string' ? candidate.trim() : candidate + )); + if (process.env.NODE_ENV === 'test') { + normalized.push(FIXTURE_SEED); + } + return normalized.find(any); } get xprv () { @@ -130,7 +141,7 @@

    Source: types/environment.js

    this.settings.xprv, this['FABRIC_XPRV'], this.readVariable('FABRIC_XPRV'), - this.wallet.xprv + this.wallet && this.wallet.xprv ].find(any); } @@ -140,7 +151,7 @@

    Source: types/environment.js

    this.settings.xpub, this['FABRIC_XPUB'], this.readVariable('FABRIC_XPUB'), - this.wallet.xpub + this.wallet && this.wallet.xpub ].find(any); } @@ -304,14 +315,36 @@

    Source: types/environment.js

    return value; } + /** + * True when `host` is `name:port` with exactly one colon and a numeric port (IPv4 or hostname). + * Bare IPv6 literals (`::1`, `2001:db8::1`) have multiple colons — do not treat as host:port. + * @param {string} host + * @returns {boolean} + */ + _hasSingleNumericPortSuffix (host) { + const s = String(host).trim(); + const first = s.indexOf(':'); + const last = s.lastIndexOf(':'); + if (first === -1 || first !== last) return false; + const tail = s.slice(last + 1); + return /^\d+$/.test(tail); + } + _normalizeRPCHost (value) { if (!value) return '127.0.0.1'; const host = String(value).trim(); if (!host) return '127.0.0.1'; - // bitcoin.conf can express rpcbind/rpcconnect as host:port. - if (host.includes(':') && !host.startsWith('[') && host.split(':').length === 2) { - return host.split(':')[0]; + // Bracketed IPv6 (e.g. rpcconnect=[::1]:8332): host only, port via _extractRPCPort. + if (host.startsWith('[')) { + const close = host.indexOf(']'); + if (close !== -1) return host.slice(0, close + 1); + return host; + } + + // bitcoin.conf: `host:port` only when a single colon separates a numeric port (not bare IPv6). + if (this._hasSingleNumericPortSuffix(host)) { + return host.slice(0, host.lastIndexOf(':')); } return host; @@ -321,9 +354,15 @@

    Source: types/environment.js

    if (!value) return null; const endpoint = String(value).trim(); if (!endpoint.includes(':')) return null; - const parts = endpoint.split(':'); - const maybePort = Number(parts[parts.length - 1]); - return Number.isFinite(maybePort) ? maybePort : null; + if (endpoint.startsWith('[')) { + const close = endpoint.indexOf(']'); + if (close === -1) return null; + const tail = endpoint.slice(close + 1); + const m = /^:(\d+)$/.exec(tail); + return m ? Number(m[1]) : null; + } + if (!this._hasSingleNumericPortSuffix(endpoint)) return null; + return Number(endpoint.slice(endpoint.lastIndexOf(':') + 1)); } _defaultRPCPortForNetwork (network = 'mainnet') { @@ -369,7 +408,7 @@

    Source: types/environment.js

    const [username, password] = content.split(':'); if (!username || !password) return null; return { username, password }; - } catch (error) { + } catch { return null; } } @@ -547,7 +586,7 @@

    Source: types/environment.js

    try { fs.mkdirSync(this.settings.store); } catch (exception) { - console.error('Could not make store:', exception); + if (this.emit) this.emit('error', `Could not make store: ${exception.message || exception}`); return false; } @@ -560,7 +599,7 @@

    Source: types/environment.js

    try { fs.utimesSync(this.settings.path, time, time); - } catch (err) { + } catch { fs.closeSync(fs.openSync(this.settings.path, 'w')); } @@ -602,10 +641,21 @@

    Source: types/environment.js

    } }); } else if (this.walletExists()) { - const data = this.readWallet(); - try { - const input = JSON.parse(data); + const data = this.readWallet(); + const text = typeof data === 'string' ? data : String(data ?? ''); + + if (text.trim() === '') { + if (this.emit) { + this.emit('warning', `[FABRIC:KEYGEN] Wallet file is empty (${this.settings.path}); remove it or regenerate with fabric setup`); + } + this.wallet = false; + return this; + } + + const pr = tryParsePersistedJson(text); + if (!pr.ok) throw pr.error; + const input = pr.value; if (!input.object || !input.object.xprv) { throw new Error(`Corrupt or out-of-date wallet: ${this.settings.path}`); @@ -619,7 +669,9 @@

    Source: types/environment.js

    } }); } catch (exception) { - console.error('[FABRIC:KEYGEN]', 'Could not load wallet data:', exception); + // Recoverable user-data issue; do not emit "error" (EventEmitter kills the process with no listeners). + if (this.emit) this.emit('warning', `[FABRIC:KEYGEN] Could not load wallet data: ${exception.message || exception}`); + this.wallet = false; } } else { this.wallet = false; @@ -632,8 +684,8 @@

    Source: types/environment.js

    try { fs.unlinkSync(this.WALLET_FILE); return true; - } catch (exception) { - console.error('[FABRIC:ENVIRONMENT]', 'Wallet destroyed.'); + } catch { + if (this.emit) this.emit('warning', '[FABRIC:ENVIRONMENT] Wallet already destroyed or unavailable.'); return false; } } @@ -696,7 +748,7 @@

    Source: types/environment.js

    const content = JSON.stringify(encrypted, null, ' ') + '\n'; fs.writeFileSync(this.WALLET_FILE, content); } catch (exception) { - console.error('[FABRIC:ENV]', 'Could not write wallet file:', exception); + if (this.emit) this.emit('error', `[FABRIC:ENV] Could not write wallet file: ${exception.message || exception}`); process.exit(1); } @@ -764,14 +816,18 @@

    Classes

    Global


    diff --git a/docs/types_fabric.js.html b/docs/types_fabric.js.html index 6477fd016..959b348ed 100644 --- a/docs/types_fabric.js.html +++ b/docs/types_fabric.js.html @@ -39,10 +39,11 @@

    Source: types/fabric.js

    // components const Actor = require('../types/actor'); const Block = require('../types/block'); +const Bond = require('../types/bond'); const Chain = require('../types/chain'); const Circuit = require('../types/circuit'); const Collection = require('../types/collection'); -// const Contract = require('./contract'); +const Contract = require('./contract'); // const Disk = require('./disk'); const Entity = require('../types/entity'); const Hash256 = require('../types/hash256'); @@ -53,6 +54,7 @@

    Source: types/fabric.js

    const Oracle = require('../types/oracle'); const Peer = require('../types/peer'); const Program = require('../types/program'); +const RoundRobin = require('../types/roundRobin'); const Remote = require('../types/remote'); const Resource = require('../types/resource'); const Service = require('../types/service'); @@ -60,6 +62,7 @@

    Source: types/fabric.js

    const Stack = require('../types/stack'); const State = require('../types/state'); const Store = require('../types/store'); +const Text = require('../services/text'); // Swarm: require('./peer').Swarm // const Transaction = require('./transaction'); const Vector = require('../types/vector'); @@ -67,21 +70,22 @@

    Source: types/fabric.js

    const Worker = require('../types/worker'); /** - * Reliable decentralized infrastructure. + * @classdesc Facade {@link Service} that bundles {@link Chain}, {@link Machine}, {@link Store}, {@link Peer}, and related + * types for experiments and apps. Prefer importing <strong>leaf</strong> types in production; this class re-exports many of them as statics. + * @class Fabric + * @extends Service */ class Fabric extends Service { /** - * The {@link Fabric} type implements a peer-to-peer protocol for - * establishing and settling of mutually-agreed upon proofs of - * work. Contract execution takes place in the local node first, - * then is optionally shared with the network. + * The {@link Fabric} type implements a peer-to-peer protocol for establishing and settling mutually agreed proofs of work. + * Contract execution runs locally first, then may be shared with the network. * - * Utilizing * @exports Fabric * @constructor - * @param {Vector} config - Initial configuration for the Fabric engine. This can be considered the "genesis" state for any contract using the system. If a chain of events is maintained over long periods of time, `state` can be considered "in contention", and it is demonstrated that the outstanding value of the contract remains to be settled. + * @param {Object} [settings={}] Engine settings (merged into <code>this.settings</code>); typically includes + * <code>path</code>, <code>persistent</code>, and <code>state</code> (initial {@link Actor} content). * @emits Fabric#thread - * @emits Fabric#step Emitted on a `compute` step. + * @emits Fabric#step Emitted on a <code>compute</code> step. */ constructor (settings = {}) { super(settings); @@ -102,7 +106,8 @@

    Source: types/fabric.js

    // build maps this.agent = {}; // Identity this.modules = {}; // List<Class> - this.opcodes = {}; // Map<id> + // Inherit opcode metadata registry from Service (Bitcoin + Fabric defaults). + // Do not reset this.opcodes here; Service constructor initializes it. this.peers = {}; // Map<id> this.plugins = {}; // Map<id> this.services = {}; // Map<id> @@ -130,10 +135,12 @@

    Source: types/fabric.js

    static get Actor () { return Actor; } static get Block () { return Block; } + static get Bond () { return Bond; } static get Chain () { return Chain; } static get Circuit () { return Circuit; } static get Collection () { return Collection; } - // static get Contract () { return Contract; } + static get RoundRobin () { return RoundRobin; } + static get Contract () { return Contract; } // static get Disk () { return Disk; } static get Entity () { return Entity; } static get Hash256 () { return Hash256; } @@ -150,16 +157,29 @@

    Source: types/fabric.js

    static get Script () { return Script; } static get Stack () { return Stack; } static get State () { return State; } + /** @deprecated Use {@link State}. Alias for backward compatibility. */ + static get Scribe () { return State; } static get Store () { return Store; } + static get Text () { return Text; } // static get Swarm () { return require('./peer').Swarm; } // static get Transaction () { return Transaction; } static get Wallet () { return Wallet; } static get Worker () { return Worker; } + /** + * EventEmitter-only instruction handle; use {@link State} / {@link Machine} for signed payloads. + * @returns {Function} The {@link module:types/vector~Vector} constructor. + */ + static get Vector () { return Vector; } + /** @returns {Function} */ static get Federation () { return require('./federation'); } - /** @returns {Function} */ + /** + * @deprecated Not a Fabric type. Use {@link Machine}, {@link Program}, + * `functions/beaconFederationSigning`, and `functions/fabricCanonicalJson`. + * @returns {object} + */ static get DistributedExecution () { return require('./distributedExecution'); } static sha256 (data) { @@ -271,20 +291,41 @@

    Source: types/fabric.js

    * @return {Stack} */ push (value) { - let name = value.constructor.name; - if (name !== 'Vector') value = new Vector(value)._sign(); + if (!(value instanceof State) && !(value instanceof Vector)) { + value = new State(value)._sign(); + } this.machine.script.push(value); return this.machine.script; } use (name, description) { this.log('[FABRIC]', `defining <code>${name}</code> as:`, description); - this.opcodes[name] = description.bind(this); return this.define(name, description); } define (name, description) { this.log(`Defining resource "${name}":`, description); + const opcodeName = String(name || ''); + const isBitcoinStyle = opcodeName.startsWith('OP_'); + + if (typeof description === 'function' && this.machine) { + if (isBitcoinStyle && typeof this.machine.defineBitcoinOpcode === 'function') { + this.machine.defineBitcoinOpcode(opcodeName, description); + } else if (typeof this.machine.defineFabricOpcode === 'function') { + this.machine.defineFabricOpcode(opcodeName, description); + } + } + + const metadata = { + body: String(description || ''), + implementation: typeof description === 'function' + }; + + if (isBitcoinStyle && typeof this.defineBitcoinOpcode === 'function') { + this.defineBitcoinOpcode(opcodeName, metadata); + } else if (typeof this.defineFabricOpcode === 'function') { + this.defineFabricOpcode(opcodeName, metadata); + } let vector = new Fabric.State(description); let resource = new Fabric.Resource(name, description); this.log(`Resource:`, resource); @@ -350,7 +391,7 @@

    Source: types/fabric.js

    self.log('source', typeof source, 'emitted:', changes); }); - source.on('transaction', async function (transaction) { + source.on('transaction', async function (_transaction) { // console.log('[FABRIC:CORE]', '[EVENT:TRANSACTION]', `source (${source.constructor.name}):`, transaction); // console.log('[PROPOSAL]', 'apply this transaction to local state:', transaction); }); @@ -456,14 +497,18 @@

    Classes

    Global


    diff --git a/docs/types_federation.js.html b/docs/types_federation.js.html index 08d059156..0069c59b0 100644 --- a/docs/types_federation.js.html +++ b/docs/types_federation.js.html @@ -165,7 +165,7 @@

    Source: types/federation.js

    return null; } - tick (input = {}) { + tick (_input = {}) { this._state.content.clock++; } @@ -324,64 +324,34 @@

    Source: types/federation.js

    return false; } - get address () { - // Get the public keys of all validators - const pubkeys = this._state.content.validators.map(pubkey => Buffer.from(pubkey, 'hex')); - - // Create the threshold script for majority of signers - const threshold = Math.ceil(pubkeys.length / 2); - const thresholdScript = bitcoin.script.compile([ - bitcoin.opcodes.OP_PUSHNUM_1 + threshold - 1, - ...pubkeys.map(pubkey => Buffer.concat([ - Buffer.from([pubkey.length]), - pubkey - ])), - bitcoin.opcodes.OP_PUSHNUM_1 + pubkeys.length, - bitcoin.opcodes.OP_CHECKMULTISIG - ]); - - // Create the taproot tree - const tree = [ - { - script: thresholdScript, - weight: 1 - } - ]; - - // Add timeout condition if specified in settings - if (this.settings.timeout) { - const timeoutScript = bitcoin.script.compile([ - bitcoin.opcodes.OP_CHECKLOCKTIMEVERIFY, - bitcoin.opcodes.OP_DROP, - ...thresholdScript - ]); - tree.push({ - script: timeoutScript, - weight: 1 - }); - } - - // Add contract condition if specified in settings - if (this.settings.contract) { - // If contract is a string, assume it's a script hex - const contractScript = typeof this.settings.contract === 'string' - ? Buffer.from(this.settings.contract, 'hex') - : this.settings.contract; - - tree.push({ - script: contractScript, - weight: 1 - }); + /** + * Federation validators live on consensus state, not only constructor settings. + * @param {object} [overrides] + * @returns {object} + */ + _taprootPolicyInputs (overrides = {}) { + const tap = require('../functions/contractTaproot'); + const validators = overrides.validators + || (this._state.content.validators || []).slice(); + if (!validators.length) { + throw new Error('Federation address requires at least one validator'); } + const threshold = overrides.threshold != null + ? Number(overrides.threshold) + : (this.settings.threshold != null + ? Number(this.settings.threshold) + : Math.ceil(validators.length / 2)); + const publisher = overrides.publisher || this.settings.publisher || validators[0]; + // Do not treat settings.timeout as a block count (units differ). + const csvBlocks = overrides.csvBlocks != null + ? Number(overrides.csvBlocks) + : (this.settings.csvBlocks != null ? Number(this.settings.csvBlocks) : tap.DEFAULT_CSV_BLOCKS); + const network = overrides.network || this.settings.network || 'regtest'; + return { validators, threshold, publisher, network, csvBlocks }; + } - // Create the taproot output - const output = bitcoin.payments.p2tr({ - internalPubkey: pubkeys[0].slice(1), // Use first validator's x-only pubkey - scriptTree: tree, - network: bitcoin.networks.bitcoin - }); - - return output.address; + get address () { + return this.toAddress(); } } @@ -399,14 +369,18 @@

    Classes

    Global


    diff --git a/docs/types_filesystem.js.html b/docs/types_filesystem.js.html index 3b5200db7..d5425d71d 100644 --- a/docs/types_filesystem.js.html +++ b/docs/types_filesystem.js.html @@ -33,6 +33,8 @@

    Source: types/filesystem.js

    'use strict';
     
    +const { tryParsePersistedJson } = require('../functions/wireJson');
    +
     // Dependencies
     const fs = require('fs');
     const path = require('path');
    @@ -142,7 +144,7 @@ 

    Source: types/filesystem.js

    try { fs.utimesSync(path, time, time); - } catch (err) { + } catch { fs.closeSync(fs.openSync(path, 'w')); } } @@ -228,8 +230,9 @@

    Source: types/filesystem.js

    const stateBuffer = Buffer.from(stateHex, 'hex'); const stateStr = stateBuffer.toString('utf8'); if (stateStr.length > 0) { - const state = JSON.parse(stateStr); - this._state.content = state; + const pr = tryParsePersistedJson(stateStr); + if (pr.ok) this._state.content = pr.value; + else throw pr.error; } } catch (parseErr) { // STATE file is empty, truncated, or corrupted; use default state @@ -252,7 +255,7 @@

    Source: types/filesystem.js

    }); } - async ingest (document, name = null) { + async ingest (document, _name = null) { if (typeof document !== 'string') { document = JSON.stringify(document); } @@ -412,14 +415,18 @@

    Classes

    Global


    diff --git a/docs/types_hash256.js.html b/docs/types_hash256.js.html index a9b4c6b91..552cb365d 100644 --- a/docs/types_hash256.js.html +++ b/docs/types_hash256.js.html @@ -139,14 +139,18 @@

    Classes

    Global


    diff --git a/docs/types_hkdf.js.html b/docs/types_hkdf.js.html index 53cff05f6..415360ab1 100644 --- a/docs/types_hkdf.js.html +++ b/docs/types_hkdf.js.html @@ -113,14 +113,18 @@

    Classes

    Global


    diff --git a/docs/types_identity.js.html b/docs/types_identity.js.html index c9b163f1b..a7fc7c51d 100644 --- a/docs/types_identity.js.html +++ b/docs/types_identity.js.html @@ -39,7 +39,13 @@

    Source: types/identity.js

    const Key = require('./key'); /** - * Manage a network identity. + * @classdesc <strong>BIP32/BIP39 identity</strong> wrapping {@link Key}: mnemonic / xprv / passphrase, derivation + * <code>m/44'/7778'/account'/0/index</code> (see <code>derivation</code> getter). <strong>Important:</strong> this class + * overrides {@link Actor#id} with <code>toString()</code> (human-facing / Bech32-style identity), <strong>not</strong> the + * content-addressed <code>Actor#id</code> / <code>preimage</code> chain from {@link Actor#toGenericMessage}. Use + * <code>pubkey</code>, <code>pubkeyhash</code>, or explicit hashing when you need stable bytes. + * @class Identity + * @extends Actor */ class Identity extends Actor { /** @@ -163,12 +169,12 @@

    Source: types/identity.js

    _signAsSchnorr (input) { if (!input) input = this.pubkeyhash; - this._signature = this.key.sign(input) + this._signature = this.key.sign(input); return this; } - _verifyKeyIsChild (key, parent) { - + _verifyKeyIsChild (_key, _parent) { + throw new Error('_verifyKeyIsChild is not yet implemented.'); } } @@ -186,14 +192,18 @@

    Classes

    Global


    diff --git a/docs/types_interface.js.html b/docs/types_interface.js.html index 474e56de3..b8d59ac50 100644 --- a/docs/types_interface.js.html +++ b/docs/types_interface.js.html @@ -64,7 +64,15 @@

    Source: types/interface.js

    this.ticker = new BN(); this.identity = new BN(1); this.tags = ['pre-release']; - this.settings = merge({ + // Preserve Service defaults (e.g. frequency) — do not replace this.settings with only interface keys. + this.settings = merge(this.settings, { + prefix: '/', + script: '(1)', + type: 'javascript' + }); + + // Circuit expects a narrow config; full Service `state` breaks its javascript-state-machine graph. + const layerSettings = merge({ prefix: '/', script: '(1)', type: 'javascript' @@ -72,9 +80,9 @@

    Source: types/interface.js

    // define singletons // TODO: remove these... ~E - this.circuit = new Circuit(this.settings); + this.circuit = new Circuit(layerSettings); this.machine = new Machine(this.settings); - this.secret = new Secret(this.settings); + this.secret = new Secret(layerSettings); // Shared State // TODO: use Layer @@ -100,7 +108,7 @@

    Source: types/interface.js

    return this._state.set('/status', value); } - shared (count = 1) { + shared (_count = 1) { const data = new Entity(this.memory); const id = data.id; @@ -239,14 +247,18 @@

    Classes

    Global


    diff --git a/docs/types_key.js.html b/docs/types_key.js.html index c178c3a83..52e2fb838 100644 --- a/docs/types_key.js.html +++ b/docs/types_key.js.html @@ -42,19 +42,54 @@

    Source: types/key.js

    // Constants const { - BITCOIN_KEY_DERIVATION_PATH, FABRIC_KEY_DERIVATION_PATH, - LIGHTNING_KEY_DERIVATION_PATH, BECH32M_CHARSET } = require('../constants'); // Node Modules const crypto = require('crypto'); const EventEmitter = require('events').EventEmitter; +const { sha256 } = require('@noble/hashes/sha2.js'); -// Deterministic Random -// TODO: remove -const Generator = require('arbitrary').default.Generator; +/** + * Deterministic bit stream for {@link Key#bit}. Replaces the `arbitrary` npm package, whose published + * `main` is a browserify bundle (`docs/dist/index.js`) that breaks under webpack (nested `require`). + * Internal helper — omitted from published API / dev docs. + * @ignore + */ +function createKeyBitGenerator (seedQ) { + const seed = Buffer.alloc(4); + seed.writeUInt32BE(seedQ >>> 0, 0); + let counter = 0; + let pool = Buffer.from(sha256(Buffer.concat([ + Buffer.from('fabric/key/bitgen/v1', 'utf8'), + seed + ]))); + let offset = 0; + + return { + next: { + bits (n) { + let out = 0; + for (let i = 0; i < n; i++) { + if ((offset >> 3) >= pool.length) { + const tail = Buffer.alloc(4); + tail.writeUInt32BE(counter >>> 0, 0); + counter = (counter + 1) >>> 0; + pool = Buffer.from(sha256(Buffer.concat([pool, tail]))); + offset = 0; + } + const byteIndex = offset >> 3; + const bitInByte = offset & 7; + offset++; + const bit = (pool[byteIndex] >> (7 - bitInByte)) & 1; + out = (out << 1) | bit; + } + return out; + } + } + }; +} // Dependencies // TODO: remove all external dependencies @@ -63,6 +98,23 @@

    Source: types/key.js

    const { secp256k1, schnorr: nobleSchnorr } = require('@noble/curves/secp256k1.js'); const SecpPoint = secp256k1.ProjectivePoint || secp256k1.Point; +function privateKeyToBuffer (privkey) { + if (Buffer.isBuffer(privkey)) return privkey; + if (BN.isBN(privkey)) return Buffer.from(privkey.toString(16).padStart(64, '0'), 'hex'); + if (typeof privkey === 'string') { + if (!/^[0-9a-fA-F]{1,64}$/.test(privkey)) throw new Error('Invalid private key format'); + return Buffer.from(privkey.padStart(64, '0'), 'hex'); + } + if (ArrayBuffer.isView(privkey) && !(privkey instanceof DataView) && privkey.byteLength === 32) { + // Uint8Array and other typed-array views from upgraded deps (bip32/ecpair). + return Buffer.from(privkey); + } + if (Array.isArray(privkey) && privkey.length === 32 && privkey.every((x) => Number.isInteger(x) && x >= 0 && x <= 255)) { + return Buffer.from(privkey); + } + throw new Error('Invalid private key format'); +} + function secpPointFromPublicKey (pubkey) { const bytes = Buffer.isBuffer(pubkey) ? pubkey : Buffer.from(pubkey); // noble-curves v1: ProjectivePoint.fromHex(bytes) @@ -81,7 +133,7 @@

    Source: types/key.js

    } function secpPointFromPrivateKey (privkey) { - const bytes = Buffer.isBuffer(privkey) ? privkey : Buffer.from(privkey); + const bytes = privateKeyToBuffer(privkey); // noble-curves v1 if (secp256k1.ProjectivePoint && typeof secp256k1.ProjectivePoint.fromPrivateKey === 'function') { return secp256k1.ProjectivePoint.fromPrivateKey(bytes); @@ -105,20 +157,33 @@

    Source: types/key.js

    throw new Error('Unsupported secp256k1 Point API'); } -const base58 = require('bs58check'); -const payments = require('bitcoinjs-lib/src/payments'); +const { encodeCheck, decodeCheck } = require('../functions/base58'); +const bip39 = require('../functions/bip39'); +const bip32Module = require('../functions/bip32'); +const BIP32 = bip32Module.default; +const BIP32_DEFAULT_NETWORK = bip32Module.DEFAULT_NETWORK; +const { payments } = require('bitcoinjs-lib'); // Fabric Dependencies const Actor = require('./actor'); const Hash256 = require('./hash256'); -// Simple Key Management -const BIP32 = require('bip32').default; -const bip39 = require('bip39'); - // NOTE: see also @fabric/passport // expect a bech32m identifier using prefix "id" +function bip32NetworkFromKeySettings (settings) { + const name = settings.network === 'regtest' ? 'regtest' : settings.network; + const raw = settings.networks[name] || settings.networks.mainnet; + return { + messagePrefix: raw.messagePrefix, + bech32: raw.bech32, + bip32: raw.bip32, + pubKeyHash: raw.pubKeyHash, + scriptHash: raw.scriptHash, + wif: raw.wif + }; +} + /** * Represents a cryptographic key. */ @@ -239,8 +304,8 @@

    Source: types/key.js

    switch (this._mode) { case 'FROM_MNEMONIC': seed = bip39.mnemonicToSeedSync(this.settings.mnemonic, this.settings.passphrase); - root = this.bip32.fromSeed(seed); - this.seed = this.settings.seed; + root = this.bip32.fromSeed(seed, bip32NetworkFromKeySettings(this.settings)); + this.seed = this.settings.mnemonic; this.xprv = root.toBase58(); this.xpub = root.neutered().toBase58(); this.master = root; @@ -250,7 +315,7 @@

    Source: types/key.js

    case 'FROM_SEED': // TODO: allow setting of raw seed (deprecates passing a mnemonic in the `seed` property) seed = bip39.mnemonicToSeedSync(this.settings.seed, this.settings.passphrase); - root = this.bip32.fromSeed(seed); + root = this.bip32.fromSeed(seed, bip32NetworkFromKeySettings(this.settings)); this.seed = this.settings.seed; this.xprv = root.toBase58(); this.xpub = root.neutered().toBase58(); @@ -258,8 +323,7 @@

    Source: types/key.js

    this._point = secpPointFromPrivateKey(root.privateKey); break; case 'FROM_WIF': - const decoded = base58.decode(this.settings.wif); - const version = decoded[0]; + const decoded = decodeCheck(this.settings.wif); const privateKey = decoded.slice(1, 33); const isCompressed = decoded.length === 34 && decoded[33] === 0x01; this._point = secpPointFromPrivateKey(privateKey); @@ -293,7 +357,7 @@

    Source: types/key.js

    this.mnemonic = bip39.generateMnemonic(); // TODO: set property `seed` as the actual derived seed, not the seed phrase const interim = bip39.mnemonicToSeedSync(this.mnemonic); - this.master = this.bip32.fromSeed(interim); + this.master = this.bip32.fromSeed(interim, bip32NetworkFromKeySettings(this.settings)); this.xprv = this.master.toBase58(); this.xpub = this.master.neutered().toBase58(); this._point = secpPointFromPrivateKey(this.master.privateKey); @@ -302,7 +366,7 @@

    Source: types/key.js

    // Read the pair (for modes that use master, set private from master) if (!this.private && this.master && this.master.privateKey) { - this.private = this.master.privateKey; + this.private = privateKeyToBuffer(this.master.privateKey); } // Adapt noble-curves point to the minimal interface used elsewhere. this.public = { @@ -333,7 +397,7 @@

    Source: types/key.js

    // TODO: consider using sha256(masterprivkey) or sha256(sha256(...))? this._starseed = Hash256.digest(this.pubkeyhash).toString('hex'); this.q = parseInt(this._starseed.substring(0, 4), 16); - this.generator = new Generator(this.q); + this.generator = createKeyBitGenerator(this.q); this['@data'] = { type: 'Key', @@ -369,7 +433,7 @@

    Source: types/key.js

    const mnemonic = bip39.entropyToMnemonic(seed); const seedBuffer = bip39.mnemonicToSeedSync(mnemonic); const bip32 = new BIP32(ecc); - const master = bip32.fromSeed(seedBuffer); + const master = bip32.fromSeed(seedBuffer, BIP32_DEFAULT_NETWORK); const key = new Key(); key.seed = mnemonic; key.private = master.privateKey.toString('hex'); @@ -583,6 +647,11 @@

    Source: types/key.js

    } encrypt (value) { + if (!this.private) { + if (this.settings.debug) console.error('[FABRIC:KEY]', 'Cannot encrypt without private key'); + return null; + } + try { const ivbuff = crypto.randomBytes(16); // Derive a 32-byte key from the private key using SHA-256 @@ -593,19 +662,29 @@

    Source: types/key.js

    let encrypted = cipher.update(value, 'utf8', 'hex'); encrypted += cipher.final('hex'); return ivbuff.toString('hex') + ':' + encrypted; - } catch (exception) { - console.error('err:', exception); + } catch { + if (this.settings.debug) console.error('[FABRIC:KEY]', 'Encryption failed'); return null; } } decrypt (text) { if (!text) return null; + if (!this.private) { + if (this.settings.debug) console.error('[FABRIC:KEY]', 'Cannot decrypt without private key'); + return null; + } if (text instanceof Buffer) text = text.toString('utf8'); try { + if (typeof text !== 'string' || !text.includes(':')) return null; const parts = text.split(':'); - const iv = Buffer.from(parts.shift(), 'hex'); + const ivHex = parts.shift(); + if (!/^[0-9a-fA-F]{32}$/.test(ivHex || '')) return null; + const blobHex = parts.join(':'); + if (!blobHex || !/^[0-9a-fA-F]+$/.test(blobHex)) return null; + + const iv = Buffer.from(ivHex, 'hex'); const blob = Buffer.from(parts.join(':'), 'hex'); // Use the same key derivation as encrypt const key = crypto.createHash('sha256') @@ -615,8 +694,8 @@

    Source: types/key.js

    let decrypted = decipher.update(blob, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; - } catch (exception) { - console.error('err:', exception); + } catch { + if (this.settings.debug) console.error('[FABRIC:KEY]', 'Decryption failed'); return null; } } @@ -656,16 +735,7 @@

    Source: types/key.js

    const messageHash = crypto.createHash('sha256').update(messageBuffer).digest(); // Get private key as 32-byte buffer - let privateKeyBuffer; - if (Buffer.isBuffer(this.private)) { - privateKeyBuffer = this.private; - } else if (BN.isBN(this.private)) { - privateKeyBuffer = Buffer.from(this.private.toString(16).padStart(64, '0'), 'hex'); - } else if (typeof this.private === 'string') { - privateKeyBuffer = Buffer.from(this.private.padStart(64, '0'), 'hex'); - } else { - throw new Error('Invalid private key format'); - } + const privateKeyBuffer = privateKeyToBuffer(this.private); // Sign using noble-curves Schnorr (BIP340). Zero auxRand for deterministic signatures. const signature = nobleSchnorr.sign(messageHash, privateKeyBuffer, Buffer.alloc(32)); @@ -682,24 +752,19 @@

    Source: types/key.js

    */ signSchnorrHash (messageHash) { if (!this.private) throw new Error('Cannot sign without private key'); - if (!Buffer.isBuffer(messageHash) || messageHash.length !== 32) { + // bitcoinjs-lib 7 crypto helpers return Uint8Array; accept Buffer or Uint8Array. + const hash = Buffer.isBuffer(messageHash) + ? messageHash + : (messageHash instanceof Uint8Array ? Buffer.from(messageHash) : null); + if (!hash || hash.length !== 32) { throw new Error('Message hash must be a 32-byte Buffer'); } // Get private key as 32-byte buffer - let privateKeyBuffer; - if (Buffer.isBuffer(this.private)) { - privateKeyBuffer = this.private; - } else if (BN.isBN(this.private)) { - privateKeyBuffer = Buffer.from(this.private.toString(16).padStart(64, '0'), 'hex'); - } else if (typeof this.private === 'string') { - privateKeyBuffer = Buffer.from(this.private.padStart(64, '0'), 'hex'); - } else { - throw new Error('Invalid private key format'); - } + const privateKeyBuffer = privateKeyToBuffer(this.private); // Sign using noble-curves Schnorr (BIP340). Zero auxRand for deterministic signatures. - const signature = nobleSchnorr.sign(messageHash, privateKeyBuffer, Buffer.alloc(32)); + const signature = nobleSchnorr.sign(hash, privateKeyBuffer, Buffer.alloc(32)); // Ensure we return a Buffer return Buffer.isBuffer(signature) ? signature : Buffer.from(signature); @@ -738,7 +803,10 @@

    Source: types/key.js

    * @returns {Boolean} Whether the signature is valid */ verifySchnorrHash (messageHash, sig) { - if (!Buffer.isBuffer(messageHash) || messageHash.length !== 32) { + const hash = Buffer.isBuffer(messageHash) + ? messageHash + : (messageHash instanceof Uint8Array ? Buffer.from(messageHash) : null); + if (!hash || hash.length !== 32) { throw new Error('Message hash must be a 32-byte Buffer'); } @@ -750,12 +818,17 @@

    Source: types/key.js

    const sigBuffer = Buffer.isBuffer(sig) ? sig : Buffer.from(sig); // Verify using noble-curves Schnorr (BIP340) - return nobleSchnorr.verify(sigBuffer, messageHash, xOnlyPubkey); + return nobleSchnorr.verify(sigBuffer, hash, xOnlyPubkey); } commit () { const reference = { ...this.state }; const state = new Actor(reference); + const commit = { + type: 'KeyCommit', + hash: state.id, + signatures: [] + }; // Store current state's hash this._state.hash = state.id; @@ -794,8 +867,14 @@

    Source: types/key.js

    * to prevent sensitive data from remaining in memory. */ secure () { + // Update state before clearing key material, so commit can still be signed. + this._state.status = 'secured'; + this.commit(); + // Clear sensitive key material - Buffer.write(this.private, 0, this.private.length); + if (Buffer.isBuffer(this.private)) { + this.private.fill(0); + } // Null out sensitive properties this.private = null; @@ -806,11 +885,6 @@

    Source: types/key.js

    // Clear any derived keys this.xprv = null; - // Update state - this._state.status = 'secured'; - - this.commit(); - return this; } @@ -830,20 +904,7 @@

    Source: types/key.js

    */ toWIF () { if (!this.private) throw new Error('Cannot export WIF without private key'); - let privateKeyBuffer; - - if (Buffer.isBuffer(this.private)) { - privateKeyBuffer = this.private; - } else if (this.private && typeof this.private.length === 'number' && this.private.length === 32) { - // Uint8Array or other byte-like (e.g. from bs58check.decode) - privateKeyBuffer = Buffer.from(this.private); - } else if (BN.isBN(this.private)) { - privateKeyBuffer = Buffer.from(this.private.toString(16).padStart(64, '0'), 'hex'); - } else if (typeof this.private === 'string') { - privateKeyBuffer = Buffer.from(this.private.padStart(64, '0'), 'hex'); - } else { - throw new Error('Invalid private key format'); - } + const privateKeyBuffer = privateKeyToBuffer(this.private); const network = this.settings.network === 'regtest' ? this.settings.networks.testnet @@ -856,12 +917,7 @@

    Source: types/key.js

    Buffer.from([0x01]) ]); - const firstHash = crypto.createHash('sha256').update(payload).digest(); - const secondHash = crypto.createHash('sha256').update(firstHash).digest(); - const checksum = secondHash.slice(0, 4); - const combined = Buffer.concat([payload, checksum]); - - return base58.encode(combined); + return encodeCheck(payload); } toBitcoinAddress () { @@ -893,14 +949,18 @@

    Classes

    Global


    diff --git a/docs/types_ledger.js.html b/docs/types_ledger.js.html index 05f8c48d0..eb5bb2e86 100644 --- a/docs/types_ledger.js.html +++ b/docs/types_ledger.js.html @@ -33,7 +33,7 @@

    Source: types/ledger.js

    'use strict';
     
    -const Scribe = require('./scribe');
    +const State = require('./state');
     const Stack = require('./stack');
     
     /**
    @@ -41,9 +41,9 @@ 

    Source: types/ledger.js

    * @property {Buffer} memory The ledger's memory (4096 bytes). * @property {Stack} stack The ledger's stack. * @property {Mixed} tip The most recent page in the ledger. - * @extends Scribe + * @extends State */ -class Ledger extends Scribe { +class Ledger extends State { constructor (state) { super(state); @@ -113,14 +113,18 @@

    Classes

    Global


    diff --git a/docs/types_logger.js.html b/docs/types_logger.js.html index e1a627557..2efe3fa18 100644 --- a/docs/types_logger.js.html +++ b/docs/types_logger.js.html @@ -81,7 +81,7 @@

    Source: types/logger.js

    if (typeof msg !== 'string') { try { msg = JSON.stringify(msg); - } catch (exception) { + } catch { console.warn('Unable to parse message to string:', `<${msg.constructor.name}>`, msg); return false; } @@ -160,14 +160,18 @@

    Classes

    Global


    diff --git a/docs/types_machine.js.html b/docs/types_machine.js.html index ab79bf407..ac49c5a32 100644 --- a/docs/types_machine.js.html +++ b/docs/types_machine.js.html @@ -47,10 +47,51 @@

    Source: types/machine.js

    const State = require('./state'); const Key = require('./key'); +// Fabric Functions +const { + createDefaultOpcodeRegistry, + defineOpcode: defineOpcodeEntry, + resolveOpcodeContract +} = require('../functions/opcodeRegistry'); + +// Strict JSON +const { parsePersistedJson } = require('../functions/wireJson'); + /** - * General-purpose state machine with {@link Vector}-based instructions. + * @classdesc Deterministic <strong>virtual machine</strong> layer extending {@link Actor}: script/stack, fixed memory buffer, + * clock, and a {@link Key}-backed generator for reproducible “random” bits (<code>sip</code>). Consumes {@link State}-signed + * instruction entries from {@link Fabric#push} — not the same as P2P {@link Message} dispatch (see + * <code>types/message.js</code>). + * @class Machine + * @extends Actor */ class Machine extends Actor { + /** + * Parse a JSON object of Buffer-like entries into an array of {@link Buffer}s (legacy wire / script helper). + * @param {string} [input=''] + * @returns {Buffer[]} + */ + static fromObjectString (input = '') { + if (!input) throw new Error('Must provide input.'); + if (typeof input !== 'string') input = JSON.stringify(input); + const result = []; + const object = parsePersistedJson(input); + + for (const i in object) { + let element = object[i]; + + if (element instanceof Array) { + element = Buffer.from(element); + } else { + element = Buffer.from(element.data); + } + + result.push(element); + } + + return result; + } + /** * Create a Machine. * @param {Object} settings Run-time configuration. @@ -82,6 +123,7 @@

    Source: types/machine.js

    this.memory = Buffer.alloc(MACHINE_MAX_MEMORY); this.known = {}; // definitions + this.opcodes = createDefaultOpcodeRegistry(); this.stack = []; // output this.history = []; // State tree @@ -112,7 +154,6 @@

    Source: types/machine.js

    } get tip () { - this.log(`tip requested: ${val}`); this.log(`tip requested, history: ${JSON.stringify(this.history)}`); return this.history[this.history.length - 1] || null; } @@ -145,7 +186,7 @@

    Source: types/machine.js

    }).join(''), 2).toString(16); } - validateCycle (i) { + validateCycle (_i) { return false; } @@ -189,8 +230,109 @@

    Source: types/machine.js

    } // register a local function - define (name, op) { + define (name, op, definition = {}) { this.known[name] = op.bind(this.state); + defineOpcodeEntry(this.opcodes, name, Object.assign({}, definition, { + implementation: true + })); + return this.known[name]; + } + + defineOpcode (name, op, definition = {}) { + return this.define(name, op, definition); + } + + defineBitcoinOpcode (name, op, definition = {}) { + return this.define(name, op, Object.assign({}, definition, { family: 'bitcoin' })); + } + + defineFabricOpcode (name, op, definition = {}) { + return this.define(name, op, Object.assign({}, definition, { family: 'fabric' })); + } + + compileOpcodeContract (body = '') { + const resolved = resolveOpcodeContract(this.opcodes, body); + if (resolved.unknown.length) { + throw new Error(`Unknown opcodes in contract: ${resolved.unknown.join(', ')}`); + } + return resolved.lines; + } + + /** + * Parse program manifest v1 (former DistributedExecution.parseDistributedManifestV1). + * @param {Object} raw + * @param {Program|null} [program] When provided, programId/hash must match. + * @returns {{ok: boolean, error: (string|undefined), manifest: (Object|undefined)}} + */ + parseManifest (raw, program = null) { + const { parseProgramManifestV1 } = require('../functions/fabricProgramManifest'); + const parsed = parseProgramManifestV1(raw); + if (!parsed.ok) return parsed; + if (program) { + const hash = program.programHash || (typeof program.hash === 'function' ? program.hash() : null); + if (hash && parsed.manifest.programHash !== hash) { + return { ok: false, error: 'manifest programHash does not match Program' }; + } + } + return parsed; + } + + /** + * Load a {@link Program} onto this machine (sets script steps). + * @param {Program} program + * @returns {Machine} + */ + loadProgram (program) { + const Program = require('./program'); + const prog = program instanceof Program ? program : Program.from(program || {}); + const compiled = prog.compile(); + if (!compiled.ok) { + throw new Error(compiled.error || 'program compile failed'); + } + if (prog.language === 'javascript' && prog.source && typeof prog.source === 'object' && + !Array.isArray(prog.source)) { + for (const [name, fn] of Object.entries(prog.source)) { + if (typeof fn === 'function') this.define(name, fn); + } + } + this.settings.script = prog.steps.slice(); + this._program = prog; + return this; + } + + /** + * Load and compute a Program; return stack tip + run commitment for L1 binding. + * @param {Program|Object} program + * @param {*} [input] + * @returns {Promise.<{ok: boolean, stack: Array, tip: *, trace: Array, runCommitmentHex: (string|null), error: (string|undefined)}>} + */ + async runProgram (program, input) { + try { + this.loadProgram(program); + const beforeLen = this.stack.length; + await this.compute(input); + const trace = this.stack.slice(beforeLen); + const tip = this.stack.length ? this.stack[this.stack.length - 1] : null; + const runCommitmentHex = this._program + ? this._program.runCommitmentHex({ tip, stack: trace }) + : null; + return { + ok: true, + stack: this.stack.slice(), + tip, + trace, + runCommitmentHex + }; + } catch (err) { + return { + ok: false, + stack: this.stack.slice(), + tip: null, + trace: [], + runCommitmentHex: null, + error: err && err.message ? err.message : String(err) + }; + } } applyOperation (op) { @@ -223,7 +365,13 @@

    Source: types/machine.js

    async start () { this.status = 'STARTING'; - this._governor = setInterval(this.compute.bind(this), this.settings.frequency * 1000); + const f = this.settings.frequency; + const fromFreq = (typeof f === 'number' && Number.isFinite(f)) ? f * 1000 : NaN; + const intervalSec = (typeof this.settings.interval === 'number' && Number.isFinite(this.settings.interval)) + ? this.settings.interval + : 60; + const ms = Number.isFinite(fromFreq) && fromFreq > 0 ? fromFreq : Math.max(1, intervalSec * 1000); + this._governor = setInterval(this.compute.bind(this), ms); this.status = 'STARTED'; return this; } @@ -250,14 +398,18 @@

    Classes

    Global


    diff --git a/docs/types_message.js.html b/docs/types_message.js.html index eca678d49..51e5212ea 100644 --- a/docs/types_message.js.html +++ b/docs/types_message.js.html @@ -39,9 +39,9 @@

    Source: types/message.js

    HEADER_SIZE, MAX_MESSAGE_SIZE, OP_CYCLE, - GENERIC_MESSAGE_TYPE, LOG_MESSAGE_TYPE, GENERIC_LIST_TYPE, + GENERIC_MESSAGE_TYPE, BITCOIN_BLOCK_TYPE, BITCOIN_BLOCK_HASH_TYPE, BITCOIN_TRANSACTION_TYPE, @@ -49,13 +49,25 @@

    Source: types/message.js

    P2P_GENERIC, P2P_IDENT_REQUEST, P2P_IDENT_RESPONSE, - P2P_ROOT, P2P_PING, P2P_PONG, P2P_START_CHAIN, P2P_INSTRUCTION, P2P_BASE_MESSAGE, P2P_CHAIN_SYNC_REQUEST, + P2P_FLUSH_CHAIN, + P2P_INVENTORY_REQUEST, + P2P_INVENTORY_RESPONSE, + P2P_FILE_SEND, + P2P_DOCUMENT_PUBLISH, + P2P_PEER_ALIAS, + P2P_PEER_ANNOUNCE, + P2P_PEER_GOSSIP, + P2P_PEERING_OFFER, + P2P_SESSION_OFFER, + P2P_SESSION_OPEN, + P2P_CONTRACT_PUBLISH, + P2P_CONTRACT_MESSAGE, P2P_STATE_ROOT, P2P_STATE_COMMITTMENT, P2P_STATE_CHANGE, @@ -64,7 +76,10 @@

    Source: types/message.js

    P2P_CALL, P2P_RELAY, P2P_MESSAGE_RECEIPT, + P2P_FORWARD, CHAT_MESSAGE, + P2P_CHAT_MESSAGE, + SIDECHAIN_STATE_PATCH_TYPE, DOCUMENT_PUBLISH_TYPE, DOCUMENT_REQUEST_TYPE, JSON_CALL_TYPE, @@ -96,9 +111,12 @@

    Source: types/message.js

    LIGHTNING_CHANNEL_UPDATE } = require('../constants'); -const HEADER_SIG_SIZE = 64; +const { tryParseWireJson } = require('../functions/wireJson'); -/** @param {Buffer} buf */ +/** + * @private + * @param {Buffer} buf + */ function isAllZero32 (buf) { if (!buf || buf.length !== 32) return true; return buf.equals(Buffer.alloc(32)); @@ -111,7 +129,6 @@

    Source: types/message.js

    // Fabric Types const Actor = require('./actor'); const Hash256 = require('./hash256'); -const Key = require('./key'); // Function Definitions const padDigits = require('../functions/padDigits'); @@ -122,23 +139,26 @@

    Source: types/message.js

    * - **Wire** (`wireType`, {@link Message#type}): SCREAMING_SNAKE_CASE strings from opcode * decode (`fromBuffer`, `toVector` first element). Matches AMP / `constants.js` style. * - **Friendly** ({@link Message#friendlyType}, `toObject().type`): PascalCase (or historical - * labels) for JSON and human-facing APIs — see {@link FRIENDLY_TYPE_BY_WIRE}. + * labels) for JSON and human-facing APIs — see {@link Message.FRIENDLY_TYPE_BY_WIRE}. * * Encode accepts **either** name via merged {@link Message#types} (canonical wire + legacy friendly). * {@link Message.wireTypeFromFriendly} / {@link Message.friendlyTypeFromWire} convert between them. * * Opcode → wire string order matches the historical `type` switch: when multiple labels share one - * opcode (e.g. P2P vs Lightning), **first listed** in {@link WIRE_TYPE_DECODE_ORDER} wins. + * opcode (e.g. P2P vs Lightning), **first listed** in {@link Message.WIRE_TYPE_DECODE_ORDER} wins. + * Those maps are **not** global exports: use {@link Message} statics `WIRE_TYPE_DECODE_ORDER` and + * `FRIENDLY_TYPE_BY_WIRE`. */ +/** @private */ const WIRE_TYPE_DECODE_ORDER = Object.freeze([ [BITCOIN_BLOCK_TYPE, 'BITCOIN_BLOCK'], [BITCOIN_BLOCK_HASH_TYPE, 'BITCOIN_BLOCK_HASH'], [BITCOIN_TRANSACTION_TYPE, 'BITCOIN_TRANSACTION'], [BITCOIN_TRANSACTION_HASH_TYPE, 'BITCOIN_TRANSACTION_HASH'], - [GENERIC_MESSAGE_TYPE, 'GENERIC_MESSAGE'], - [GENERIC_MESSAGE_TYPE + 1, 'JSON_BLOB'], [LOG_MESSAGE_TYPE, 'LOG_MESSAGE'], [GENERIC_LIST_TYPE, 'GENERIC_LIST'], + [GENERIC_MESSAGE_TYPE, 'GENERIC_MESSAGE'], + [SIDECHAIN_STATE_PATCH_TYPE, 'SIDECHAIN_STATE_PATCH'], [DOCUMENT_PUBLISH_TYPE, 'DOCUMENT_PUBLISH'], [DOCUMENT_REQUEST_TYPE, 'DOCUMENT_REQUEST'], [BLOCK_CANDIDATE, 'BLOCK_CANDIDATE'], @@ -146,6 +166,19 @@

    Source: types/message.js

    [P2P_PONG, 'P2P_PONG'], [P2P_GENERIC, 'P2P_GENERIC'], [P2P_CHAIN_SYNC_REQUEST, 'P2P_CHAIN_SYNC_REQUEST'], + [P2P_FLUSH_CHAIN, 'P2P_FLUSH_CHAIN'], + [P2P_INVENTORY_REQUEST, 'P2P_INVENTORY_REQUEST'], + [P2P_INVENTORY_RESPONSE, 'P2P_INVENTORY_RESPONSE'], + [P2P_FILE_SEND, 'P2P_FILE_SEND'], + [P2P_DOCUMENT_PUBLISH, 'P2P_DOCUMENT_PUBLISH'], + [P2P_PEER_ALIAS, 'P2P_PEER_ALIAS'], + [P2P_PEER_ANNOUNCE, 'P2P_PEER_ANNOUNCE'], + [P2P_PEER_GOSSIP, 'P2P_PEER_GOSSIP'], + [P2P_PEERING_OFFER, 'P2P_PEERING_OFFER'], + [P2P_SESSION_OFFER, 'P2P_SESSION_OFFER'], + [P2P_SESSION_OPEN, 'P2P_SESSION_OPEN'], + [P2P_CONTRACT_PUBLISH, 'CONTRACT_PUBLISH'], + [P2P_CONTRACT_MESSAGE, 'CONTRACT_MESSAGE'], [P2P_IDENT_REQUEST, 'P2P_IDENT_REQUEST'], [P2P_IDENT_RESPONSE, 'P2P_IDENT_RESPONSE'], [P2P_BASE_MESSAGE, 'P2P_BASE_MESSAGE'], @@ -156,9 +189,11 @@

    Source: types/message.js

    [P2P_CALL, 'P2P_CALL'], [P2P_RELAY, 'P2P_RELAY'], [P2P_MESSAGE_RECEIPT, 'P2P_MESSAGE_RECEIPT'], + [P2P_FORWARD, 'P2P_FORWARD'], [PEER_CANDIDATE, 'PEER_CANDIDATE'], [SESSION_START, 'SESSION_START'], [CHAT_MESSAGE, 'CHAT_MESSAGE'], + [P2P_CHAT_MESSAGE, 'P2P_CHAT_MESSAGE'], [JSON_CALL_TYPE, 'JSON_CALL'], [PATCH_MESSAGE_TYPE, 'JSON_PATCH'], [CONTRACT_PROPOSAL_TYPE, 'CONTRACT_PROPOSAL'], @@ -210,14 +245,14 @@

    Source: types/message.js

    BitcoinBlockHash: BITCOIN_BLOCK_HASH_TYPE, BitcoinTransaction: BITCOIN_TRANSACTION_TYPE, BitcoinTransactionHash: BITCOIN_TRANSACTION_HASH_TYPE, - GenericMessage: GENERIC_MESSAGE_TYPE, GenericLogMessage: LOG_MESSAGE_TYPE, GenericList: GENERIC_LIST_TYPE, GenericQueue: GENERIC_LIST_TYPE, + /** Transitional Hub/browser catch-all (opcode GENERIC_MESSAGE_TYPE / 15103). */ + GenericMessage: GENERIC_MESSAGE_TYPE, FabricLogMessage: LOG_MESSAGE_TYPE, FabricServiceLogMessage: LOG_MESSAGE_TYPE, GenericTransferQueue: GENERIC_LIST_TYPE, - JSONBlob: GENERIC_MESSAGE_TYPE + 1, JSONCall: JSON_CALL_TYPE, JSONPatch: PATCH_MESSAGE_TYPE, ContractProposal: CONTRACT_PROPOSAL_TYPE, @@ -226,10 +261,22 @@

    Source: types/message.js

    IdentityRequest: P2P_IDENT_REQUEST, IdentityResponse: P2P_IDENT_RESPONSE, ChainSyncRequest: P2P_CHAIN_SYNC_REQUEST, + FlushChain: P2P_FLUSH_CHAIN, + InventoryRequest: P2P_INVENTORY_REQUEST, + InventoryResponse: P2P_INVENTORY_RESPONSE, + PeerAlias: P2P_PEER_ALIAS, + PeerAnnounce: P2P_PEER_ANNOUNCE, + PeerGossip: P2P_PEER_GOSSIP, + PeeringOffer: P2P_PEERING_OFFER, + SessionOffer: P2P_SESSION_OFFER, + SessionOpen: P2P_SESSION_OPEN, + FileSend: P2P_FILE_SEND, + DocumentPricingPublish: P2P_DOCUMENT_PUBLISH, Ping: P2P_PING, Pong: P2P_PONG, DocumentRequest: DOCUMENT_REQUEST_TYPE, DocumentPublish: DOCUMENT_PUBLISH_TYPE, + SidechainStatePatch: SIDECHAIN_STATE_PATCH_TYPE, BlockCandidate: BLOCK_CANDIDATE, PeerCandidate: PEER_CANDIDATE, PeerInstruction: P2P_INSTRUCTION, @@ -263,7 +310,12 @@

    Source: types/message.js

    RevokeAndAck: LIGHTNING_REVOKE_AND_ACK, ChannelAnnouncement: LIGHTNING_CHANNEL_ANNOUNCEMENT, NodeAnnouncement: LIGHTNING_NODE_ANNOUNCEMENT, - ChannelUpdate: LIGHTNING_CHANNEL_UPDATE + ChannelUpdate: LIGHTNING_CHANNEL_UPDATE, + // P2P_-prefixed contract names encode to the canonical contract opcodes + // (decode names stay CONTRACT_PUBLISH / CONTRACT_MESSAGE / CONTRACT_PROPOSAL). + P2P_CONTRACT_PUBLISH: P2P_CONTRACT_PUBLISH, + P2P_CONTRACT_MESSAGE: P2P_CONTRACT_MESSAGE, + P2P_CONTRACT_PROPOSAL: CONTRACT_PROPOSAL_TYPE }); /** @@ -271,6 +323,7 @@

    Source: types/message.js

    * where historically used). {@link Message#wireType} / {@link Message#type} use wire names; * {@link Message#friendlyType} and {@link Message#toObject} `type` use friendly names. */ +/** @private */ const FRIENDLY_TYPE_BY_WIRE = Object.freeze((() => { const tmp = {}; for (const friendly of Object.keys(LEGACY_MESSAGE_TYPE_ALIASES)) { @@ -295,6 +348,7 @@

    Source: types/message.js

    })()); /** + * @private * @param {string} wire * @returns {string} */ @@ -304,6 +358,7 @@

    Source: types/message.js

    } /** + * @private * @param {string} friendly * @returns {string} */ @@ -313,16 +368,97 @@

    Source: types/message.js

    } /** - * The {@link Message} type defines the Application Messaging Protocol, or AMP. - * Each {@link Actor} in the network receives and broadcasts messages, - * selectively disclosing new routes to peers which may have open circuits. - * @type {Object} + * Resolve any opcode / wire name / friendly alias to the numeric AMP type code. + * @param {number|string|null|undefined} value + * @returns {number|null} + */ +function canonicalTypeCode (value) { + if (typeof value === 'number' && Number.isFinite(value)) { + return CANONICAL_WIRE_TYPE_BY_OPCODE[value] !== undefined ? value : null; + } + if (typeof value === 'string') { + const s = value.trim(); + if (!s) return null; + if (Object.prototype.hasOwnProperty.call(CANONICAL_MESSAGE_TYPE_STRINGS, s)) { + return CANONICAL_MESSAGE_TYPE_STRINGS[s]; + } + if (Object.prototype.hasOwnProperty.call(LEGACY_MESSAGE_TYPE_ALIASES, s)) { + const code = LEGACY_MESSAGE_TYPE_ALIASES[s]; + return (typeof code === 'number' && Number.isFinite(code)) ? code : null; + } + if (/^\d+$/.test(s)) { + const n = Number(s); + return CANONICAL_WIRE_TYPE_BY_OPCODE[n] !== undefined ? n : null; + } + } + return null; +} + +/** + * Resolve any opcode / wire name / friendly alias to the SCREAMING_SNAKE wire label. + * @param {number|string|null|undefined} value + * @returns {string|null} + */ +function canonicalTypeName (value) { + const code = canonicalTypeCode(value); + if (code != null) return CANONICAL_WIRE_TYPE_BY_OPCODE[code] || null; + if (typeof value === 'string') { + const s = value.trim(); + return s || null; + } + return null; +} + +/** + * True when two type references name the same AMP opcode (number, wire name, or friendly alias). + * Unregistered string labels only match via exact trim equality. + * @param {number|string|null|undefined} a + * @param {number|string|null|undefined} b + * @returns {boolean} + */ +function typeEquals (a, b) { + const ca = canonicalTypeCode(a); + const cb = canonicalTypeCode(b); + if (ca != null && cb != null) return ca === cb; + if (typeof a === 'string' && typeof b === 'string') { + return a.trim() === b.trim(); + } + return a === b; +} + +/** + * @classdesc <strong>Application Messaging Protocol (AMP)</strong> — binary envelope for what {@link Peer}, + * {@link Service}, and bridges actually exchange. Extends {@link Actor} for construction and state helpers, but on the wire + * you think in <strong>opcodes</strong>, <strong>headers</strong> (parent, author as x-only pubkey, hash, preimage, + * 64-byte Schnorr signature), and <strong>payload</strong>. + * + * <p><strong>Signing</strong> — {@link Message#signWithKey} / {@link Message#verifyWithKey} use BIP-340 Schnorr on tagged + * hash <code>Fabric/Message</code> over header (signature field zeroed) + body. This is <strong>not</strong> Bitcoin Signed + * Message (ECDSA + Core prefix).</p> + * + * <p><strong>Type names</strong> — {@link Message#wireType} / {@link Message#type} use SCREAMING_SNAKE wire labels from + * opcode decode; {@link Message#friendlyType} and {@link Message#toObject}'s <code>type</code> use PascalCase (or legacy) + * JSON names. {@link Message.wireTypeFromFriendly} / {@link Message.friendlyTypeFromWire} bridge the two. See file header + * maps (<code>WIRE_TYPE_DECODE_ORDER</code>, <code>LEGACY_MESSAGE_TYPE_ALIASES</code>) when aligning <strong>@fabric/http</strong> + * or Hub.</p> + * + * <p><strong>Body (V1)</strong> — Prefer {@link Message.fromFields} / {@link Message#toFields} with a + * registered body schema (see body codec helpers on this module). Bodies are C-like typed fields, not JSON; + * JSON bridging is <strong>@fabric/http</strong>. See <code>docs/MESSAGE_BODY.md</code>.</p> + * + * <p><strong>Narrative</strong> — See <strong>DEVELOPERS.md</strong> (<em>Actor and Message</em>) and {@link Actor} + * <code>@fileoverview</code>; home HTML is generated from DEVELOPERS.md, while this page comes from + * <code>types/message.js</code>.</p> + * @class Message + * @extends Actor */ class Message extends Actor { /** - * The `Message` type is standardized in {@link Fabric} as a {@link Array}, which can be added to any other vector to compute a resulting state. - * @param {Object} message Message vector. Will be serialized by {@link Array#_serialize}. - * @return {Message} Instance of the message. + * Build a message from an object. Prefer <code>type</code>/<code>data</code>; <code>@type</code> / <code>@data</code> + * are accepted for backward compatibility. + * @param {Object} [input={}] Initial fields: <code>type</code> or <code>@type</code>, <code>data</code> or + * <code>@data</code>, optional <code>signer</code>, <code>sensitive</code>, <code>preimage</code>. + * @return {Message} Instance ready for {@link Message#asRaw}, {@link Message#signWithKey}, etc. */ constructor (input = {}) { super(input); @@ -350,8 +486,13 @@

    Source: types/message.js

    this.signer = null; } - /** When true, body preimage field is zeroed on wire (no SHA256(body) commitment). */ + /** + * When true, keep wire preimage zeroed (no payment secret). Default public + * messages already use zeros — see {@link Message#preimage} (Lightning-style). + */ this._sensitive = !!(input && input.sensitive); + /** When true, an explicit HTLC / circuit payment preimage was set — do not clobber. */ + this._explicitPreimage = false; // Support both @type/@data (deprecated) and type/data (preferred) formats const messageType = input.type || input['@type']; @@ -360,10 +501,16 @@

    Source: types/message.js

    if (messageData && messageType) { this.type = messageType; // Set the type field to the numeric constant - const typeCode = this.types[messageType] || GENERIC_MESSAGE_TYPE; + const typeCode = this.types[messageType] || this.types.P2P_BASE_MESSAGE; this.raw.type.writeUInt32BE(typeCode, 0); - if (typeof messageData !== 'string') { + if (Buffer.isBuffer(messageData) || messageData instanceof Uint8Array) { + this.data = Buffer.from(messageData); + } else if (typeof messageData !== 'string') { + // Deprecated transitional path: plain objects without an explicit field encode + // still JSON.stringify. Prefer Message.fromFields(type, fields) for V1 bodies + // (docs/MESSAGE_BODY.md). Auto field-encode is not applied here to avoid breaking + // legacy ChatMessage / GenericMessage object payloads. this.data = JSON.stringify(messageData); } else { this.data = messageData; @@ -444,10 +591,12 @@

    Source: types/message.js

    } /** - * Optional 32-byte preimage on wire: - * - **All zeros:** sensitive payload (no commitment) or legacy; {@link Message#sensitive} uses this. - * - **SHA256(body):** default for non-sensitive messages (single digest; {@link Message#hash} is double-SHA256(body)). - * - **Other:** explicit HTLC secret or custom (must match what was signed). + * Optional 32-byte **payment** preimage on wire (Lightning-style): + * - **All zeros (default / public):** no HTLC secret; body integrity is only {@link Message#hash} + * (double-SHA256(body)). Do **not** put SHA256(body) here — that collides with circuit HTLC chains. + * - **Non-zero:** explicit payment secret for inventory HTLC / Fabric Circuit hops + * (`payment_hash = SHA256(preimage)`), covered by the Schnorr signature. + * - {@link Message#sensitive} forces zeros and refuses to clobber an explicit secret. */ get preimage () { if (!this.raw || !Buffer.isBuffer(this.raw.preimage) || this.raw.preimage.length !== 32) return null; @@ -461,11 +610,13 @@

    Source: types/message.js

    } if (value === null || value === undefined) { this.raw.preimage.fill(0); + this._explicitPreimage = false; return; } const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, 'hex'); if (buf.length !== 32) throw new Error('Message preimage must be 32 bytes'); buf.copy(this.raw.preimage); + this._explicitPreimage = !isAllZero32(this.raw.preimage); } toBuffer () { @@ -776,6 +927,7 @@

    Source: types/message.js

    // Do not assign `message.data` here: the `data` setter recomputes `raw.hash` // (double-SHA256 of the body), which would replace the on-wire hash and break // `Peer._handleFabricMessage` body-integrity checks (C parity). + message._explicitPreimage = !isAllZero32(message.raw.preimage); return message; } @@ -795,6 +947,51 @@

    Source: types/message.js

    return message; } + /** + * Build a Message whose body is encoded from a registered field schema (V1). + * @param {string|number} type Wire or friendly type name (or opcode). + * @param {object} [fields={}] Named fields matching the schema. + * @param {object} [opts={}] Extra Message constructor options (`signer`, `sensitive`, …). + * @returns {Message} + */ + static fromFields (type, fields = {}, opts = {}) { + const schema = getBodySchema(type); + if (!schema) { + throw new Error(`Message.fromFields: no body schema registered for type ${type}`); + } + return new Message(Object.assign({}, opts, { + type, + data: encodeBody(schema, fields) + })); + } + + /** + * Decode body bytes via the registered field schema for this message type. + * Truncated / malformed peer bodies return {@code null} (protocol violation) instead of throwing. + * @returns {object|null} Field map, or null if no schema / empty / undecodable body. + */ + toFields () { + const opcode = this.raw.type.readUInt32BE(0); + const schema = getBodySchema(this.type) || getBodySchema(this.wireType) || getBodySchema(opcode); + if (!schema) return null; + const buf = Buffer.isBuffer(this.raw.data) + ? this.raw.data + : Buffer.from(this.data || '', 'utf8'); + if (!buf.length) return {}; + try { + return decodeBody(schema, buf); + } catch (err) { + if (err && (err.name === 'RangeError' || err instanceof RangeError)) return null; + throw err; + } + } + + /** Raw body Buffer (preferred over UTF-8 `data` getter for binary field bodies). */ + get bodyBuffer () { + if (Buffer.isBuffer(this.raw.data)) return this.raw.data; + return Buffer.from(this.data || '', 'utf8'); + } + /* get [Symbol.toStringTag] () { return `<Message | ${JSON.stringify(this.raw)}>`; } */ @@ -851,7 +1048,7 @@

    Source: types/message.js

    */ get wireType () { const code = parseInt(this.raw.type.toString('hex'), 16); - return CANONICAL_WIRE_TYPE_BY_OPCODE[code] || 'GENERIC_MESSAGE'; + return CANONICAL_WIRE_TYPE_BY_OPCODE[code] || 'P2P_BASE_MESSAGE'; } /** @@ -869,20 +1066,13 @@

    Source: types/message.js

    set (value) { // console.trace('setting type:', value); let code = this.types[value]; - // Default to GENERIC_MESSAGE or JSON_BLOB based on content + // Default to P2P_BASE_MESSAGE for unknown/unregistered names. if (!code) { this.emit('warning', `Unknown message type: ${value}`); - // Check if data is valid JSON - try { - if (this.data && JSON.parse(this.data)) { - code = this.types['JSON_BLOB'] || this.types['JSONBlob']; - value = 'JSON_BLOB'; - } else { - code = this.types['GENERIC_MESSAGE'] || this.types['GenericMessage']; - } - } catch (e) { - code = this.types['GENERIC_MESSAGE'] || this.types['GenericMessage']; - } + // Keep unknown payloads representable on-wire without inventing ad-hoc generic labels. + tryParseWireJson(typeof this.data === 'string' ? this.data : String(this.data ?? '')); + code = this.types.P2P_BASE_MESSAGE; + value = 'P2P_BASE_MESSAGE'; } const padded = padDigits(code.toString(16), 8); @@ -908,14 +1098,14 @@

    Source: types/message.js

    } this.raw.data = bodyBuf; this.raw.size.write(padDigits(this.raw.data.byteLength.toString(16), 8), 'hex'); - // Preimage: single SHA256(body) for non-sensitive (commitment); zeros when sensitive (no body hash in preimage). + // Lightning-style: body changes never invent a payment preimage. Public = + // all-zero unless an explicit HTLC/circuit secret was set (or sensitive clears it). if (!Buffer.isBuffer(this.raw.preimage) || this.raw.preimage.length !== 32) { this.raw.preimage = Buffer.alloc(32); } - if (this._sensitive) { + if (this._sensitive || !this._explicitPreimage) { this.raw.preimage.fill(0); - } else { - Buffer.from(Hash256.digest(bodyBuf), 'hex').copy(this.raw.preimage); + if (this._sensitive) this._explicitPreimage = false; } } }); @@ -926,24 +1116,230 @@

    Source: types/message.js

    }, set (value) { this._sensitive = !!value; - if (!this.raw || !Buffer.isBuffer(this.raw.data) || !this.raw.data.length) return; - const bodyBuf = this.raw.data; + if (!this._sensitive) return; + // Never leave a payment secret on a sensitive frame; do not invent SHA256(body). if (!Buffer.isBuffer(this.raw.preimage) || this.raw.preimage.length !== 32) { this.raw.preimage = Buffer.alloc(32); } - if (this._sensitive) { - this.raw.preimage.fill(0); - } else { - Buffer.from(Hash256.digest(bodyBuf), 'hex').copy(this.raw.preimage); - } + this.raw.preimage.fill(0); + this._explicitPreimage = false; } }); Message.friendlyTypeFromWire = friendlyTypeFromWire; Message.wireTypeFromFriendly = wireTypeFromFriendly; +Message.canonicalTypeCode = canonicalTypeCode; +Message.canonicalTypeName = canonicalTypeName; +Message.typeEquals = typeEquals; +Message.WIRE_TYPE_DECODE_ORDER = WIRE_TYPE_DECODE_ORDER; Message.FRIENDLY_TYPE_BY_WIRE = FRIENDLY_TYPE_BY_WIRE; Message.FRIENDLY_TO_WIRE_TYPE = FRIENDLY_TO_WIRE_TYPE; +// --- AMP body field codec (formerly functions/messageBodyCodec) --- +const BODY_FIELD_TYPES = Object.freeze([ + 'u8', 'u16', 'u32', 'u64', 'bytes32', 'bytes', 'string', 'message' +]); +/** @type {Map<number|string, Array<{ name: string, type: string }>>} */ +const BODY_SCHEMA_BY_KEY = new Map(); + +function registerBodySchema (opcodeOrName, schema) { + if (!Array.isArray(schema)) throw new TypeError('schema must be an array'); + for (const f of schema) { + if (!f || typeof f.name !== 'string' || !BODY_FIELD_TYPES.includes(f.type)) { + throw new TypeError(`invalid field: ${JSON.stringify(f)}`); + } + } + BODY_SCHEMA_BY_KEY.set(opcodeOrName, schema.slice()); +} + +function getBodySchema (opcodeOrName) { + if (opcodeOrName == null) return null; + if (BODY_SCHEMA_BY_KEY.has(opcodeOrName)) return BODY_SCHEMA_BY_KEY.get(opcodeOrName); + if (typeof opcodeOrName === 'string') { + const upper = opcodeOrName.toUpperCase(); + if (BODY_SCHEMA_BY_KEY.has(upper)) return BODY_SCHEMA_BY_KEY.get(upper); + } + return null; +} + +function _writeU32 (buf, offset, value) { + buf.writeUInt32BE(value >>> 0, offset); +} + +function _readU32 (buf, offset) { + return buf.readUInt32BE(offset); +} + +function encodeBody (schema, fields = {}) { + if (!Array.isArray(schema)) throw new TypeError('schema required'); + const chunks = []; + let total = 0; + for (const def of schema) { + const value = fields[def.name]; + let part; + switch (def.type) { + case 'u8': { + part = Buffer.alloc(1); + part.writeUInt8(Number(value) || 0, 0); + break; + } + case 'u16': { + part = Buffer.alloc(2); + part.writeUInt16BE(Number(value) || 0, 0); + break; + } + case 'u32': { + part = Buffer.alloc(4); + _writeU32(part, 0, Number(value) || 0); + break; + } + case 'u64': { + part = Buffer.alloc(8); + const n = typeof value === 'bigint' ? value : BigInt(Number(value) || 0); + part.writeBigUInt64BE(n, 0); + break; + } + case 'bytes32': { + part = Buffer.alloc(32); + if (Buffer.isBuffer(value)) { + value.copy(part, 0, 0, Math.min(32, value.length)); + } else if (typeof value === 'string' && /^[0-9a-fA-F]*$/.test(value)) { + Buffer.from(value.padStart(64, '0').slice(0, 64), 'hex').copy(part); + } + break; + } + case 'bytes': + case 'message': { + const raw = Buffer.isBuffer(value) + ? value + : (value == null ? Buffer.alloc(0) : Buffer.from(String(value), 'utf8')); + part = Buffer.alloc(4 + raw.length); + _writeU32(part, 0, raw.length); + raw.copy(part, 4); + break; + } + case 'string': { + const raw = Buffer.from(value == null ? '' : String(value), 'utf8'); + part = Buffer.alloc(4 + raw.length); + _writeU32(part, 0, raw.length); + raw.copy(part, 4); + break; + } + default: + throw new TypeError(`unsupported field type: ${def.type}`); + } + total += part.length; + if (total > MAX_MESSAGE_SIZE) { + throw new RangeError(`message body exceeds MAX_MESSAGE_SIZE (${MAX_MESSAGE_SIZE})`); + } + chunks.push(part); + } + return Buffer.concat(chunks, total); +} + +function decodeBody (schema, buffer) { + if (!Array.isArray(schema)) throw new TypeError('schema required'); + const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer || []); + const out = {}; + let offset = 0; + for (const def of schema) { + if (offset > buf.length) { + throw new RangeError(`truncated body while reading field ${def.name}`); + } + switch (def.type) { + case 'u8': { + if (offset + 1 > buf.length) throw new RangeError(`truncated body at ${def.name}`); + out[def.name] = buf.readUInt8(offset); + offset += 1; + break; + } + case 'u16': { + if (offset + 2 > buf.length) throw new RangeError(`truncated body at ${def.name}`); + out[def.name] = buf.readUInt16BE(offset); + offset += 2; + break; + } + case 'u32': { + if (offset + 4 > buf.length) throw new RangeError(`truncated body at ${def.name}`); + out[def.name] = _readU32(buf, offset); + offset += 4; + break; + } + case 'u64': { + if (offset + 8 > buf.length) throw new RangeError(`truncated body at ${def.name}`); + out[def.name] = buf.readBigUInt64BE(offset); + offset += 8; + break; + } + case 'bytes32': { + if (offset + 32 > buf.length) throw new RangeError(`truncated body at ${def.name}`); + out[def.name] = Buffer.from(buf.subarray(offset, offset + 32)); + offset += 32; + break; + } + case 'bytes': + case 'message': { + if (offset + 4 > buf.length) throw new RangeError(`truncated body at ${def.name}`); + const len = _readU32(buf, offset); + offset += 4; + if (offset + len > buf.length) throw new RangeError(`truncated body at ${def.name}`); + out[def.name] = Buffer.from(buf.subarray(offset, offset + len)); + offset += len; + break; + } + case 'string': { + if (offset + 4 > buf.length) throw new RangeError(`truncated body at ${def.name}`); + const len = _readU32(buf, offset); + offset += 4; + if (offset + len > buf.length) throw new RangeError(`truncated body at ${def.name}`); + out[def.name] = buf.subarray(offset, offset + len).toString('utf8'); + offset += len; + break; + } + default: + throw new TypeError(`unsupported field type: ${def.type}`); + } + } + return out; +} + +const SCHEMA_P2P_PING = Object.freeze([{ name: 'nonce', type: 'string' }]); +const SCHEMA_P2P_CHAT = null; +const SCHEMA_PROGRAM_RUN = Object.freeze([ + { name: 'programHash', type: 'bytes32' }, + { name: 'runCommitment', type: 'bytes32' }, + { name: 'resultHint', type: 'string' } +]); +/** Directed onion hop — see {@link module:@fabric/core/functions/fabricOnion}. */ +const SCHEMA_P2P_FORWARD = Object.freeze([ + { name: 'nextPeer', type: 'bytes32' }, + { name: 'ttl', type: 'u8' }, + { name: 'inner', type: 'message' } +]); + +(function _installBodySchemaDefaults () { + registerBodySchema(P2P_PING, SCHEMA_P2P_PING); + registerBodySchema('P2P_PING', SCHEMA_P2P_PING); + registerBodySchema('Ping', SCHEMA_P2P_PING); + registerBodySchema(P2P_PONG, SCHEMA_P2P_PING); + registerBodySchema('P2P_PONG', SCHEMA_P2P_PING); + registerBodySchema('Pong', SCHEMA_P2P_PING); + registerBodySchema('FabricProgramRun', SCHEMA_PROGRAM_RUN); + registerBodySchema(P2P_FORWARD, SCHEMA_P2P_FORWARD); + registerBodySchema('P2P_FORWARD', SCHEMA_P2P_FORWARD); +})(); + +Message.FIELD_TYPES = BODY_FIELD_TYPES; +Message.MAX_MESSAGE_SIZE = MAX_MESSAGE_SIZE; +Message.registerBodySchema = registerBodySchema; +Message.getBodySchema = getBodySchema; +Message.encodeBody = encodeBody; +Message.decodeBody = decodeBody; +Message.SCHEMA_P2P_PING = SCHEMA_P2P_PING; +Message.SCHEMA_P2P_CHAT = SCHEMA_P2P_CHAT; +Message.SCHEMA_PROGRAM_RUN = SCHEMA_PROGRAM_RUN; +Message.SCHEMA_P2P_FORWARD = SCHEMA_P2P_FORWARD; + module.exports = Message;
    @@ -958,14 +1354,18 @@

    Classes

    Global


    diff --git a/docs/types_node.js.html b/docs/types_node.js.html index f2d48b661..23ff494ca 100644 --- a/docs/types_node.js.html +++ b/docs/types_node.js.html @@ -186,14 +186,18 @@

    Classes

    Global


    diff --git a/docs/types_oracle.js.html b/docs/types_oracle.js.html index 7c90bc3a7..1be066e6d 100644 --- a/docs/types_oracle.js.html +++ b/docs/types_oracle.js.html @@ -174,14 +174,18 @@

    Classes

    Global


    diff --git a/docs/types_path.js.html b/docs/types_path.js.html index 0c4c50fb9..a8ee1ff71 100644 --- a/docs/types_path.js.html +++ b/docs/types_path.js.html @@ -70,14 +70,18 @@

    Classes

    Global


    diff --git a/docs/types_peer.js.html b/docs/types_peer.js.html index 7db3160c9..11bdfdcc0 100644 --- a/docs/types_peer.js.html +++ b/docs/types_peer.js.html @@ -44,24 +44,52 @@

    Source: types/peer.js

    PEERING_OFFER_MAX_PAYLOAD_CACHE, PEERING_OFFER_MAX_RELAYS_PER_ORIGIN_PER_MINUTE, PEER_MAX_CANDIDATES_QUEUE, - P2P_IDENT_REQUEST, - P2P_IDENT_RESPONSE, P2P_PEER_GOSSIP, P2P_PEERING_OFFER, + P2P_PEER_ALIAS, + P2P_CHAT_MESSAGE, + P2P_INVENTORY_REQUEST, + P2P_INVENTORY_RESPONSE, + DOCUMENT_REQUEST_TYPE, PEER_MAX_WIRE_HASH_CACHE, - P2P_ROOT, - P2P_PING, - P2P_PONG, + PEER_MAX_LOGICAL_REGISTER_CACHE, + PEER_SCORE_BODY_HASH_MISMATCH_PENALTY, + PEER_SCORE_INVALID_SIGNATURE_PENALTY, + PEER_SCORE_SIGNER_PIN_MISMATCH_PENALTY, + PEER_SCORE_SESSION_KEY_VIOLATION_PENALTY, + PEER_SCORE_CONTRACT_OPS_FORBIDDEN_PENALTY, + PEER_SCORE_LOGICAL_REGISTER_HIJACK_PENALTY, + PEER_SCORE_LOGICAL_REGISTER_DUPLICATE_PENALTY, + PEER_SCORE_LOGICAL_REGISTER_DUPLICATE_WINDOW_MS, + PEER_MAX_RELAY_NEST_DEPTH, + PEER_BAN_TTL_MS, + PEER_RELAY_CREDIT_COST, + PEER_SCORE_RELAY_NEST_EXCEEDED_PENALTY, + CHAT_MAX_RELAYS_PER_ORIGIN_PER_MINUTE, + PEER_MAX_PENDING_SEALED_DELIVERIES, + PEER_MAX_DOCUMENT_RELAY_ROUTES, + HEADER_SIZE, + MAX_MESSAGE_SIZE, P2P_PORT, - P2P_START_CHAIN, P2P_CHAIN_SYNC_REQUEST, - P2P_INSTRUCTION, - P2P_BASE_MESSAGE, - P2P_STATE_COMMITTMENT, - P2P_STATE_CHANGE, - P2P_STATE_ROOT + P2P_FLUSH_CHAIN, + P2P_FORWARD, + P2P_RELAY } = require('../constants'); +const { + wrapOnionPath, + tryDecodeForward, + xOnlyFromKey, + xOnlyEquals, + toXOnlyPeerId +} = require('../functions/fabricOnion'); + +/** Max UTF-8 code units for first-class P2P_CHAT_MESSAGE body (text only). */ +const P2P_CHAT_MAX_CHARS = 2000; +/** Max UTF-8 code units for first-class P2P_PEER_ALIAS body (nickname). */ +const P2P_PEER_ALIAS_MAX_CHARS = 64; + // Dependencies const net = require('net'); const crypto = require('crypto'); @@ -70,6 +98,57 @@

    Source: types/peer.js

    const manager = require('fast-json-patch'); const noise = require('noise-protocol-stream'); const merge = require('lodash.merge'); +const { EventEmitter } = require('events'); +// noise-protocol-stream uses one shared EventEmitter for all handshake callbacks. +// Concurrent mesh dials exceed the default MaxListeners=10 and spam warnings. +if ((EventEmitter.defaultMaxListeners || 10) < 64) { + EventEmitter.defaultMaxListeners = 64; +} + +// L1 document binding (hub / HTLC contentHash) +const { + fabricCanonicalJson, + whitelistedDocumentFields +} = require('../functions/publishedDocumentEnvelope'); +const { + purchaseContentHashHex, + resolveDocumentContentHashHex, + contentHashHexFromObject +} = require('../functions/documentPaymentHash'); +const { + normalizeFabricDocumentOfferEnvelopeForHandlers +} = require('../functions/publishedDocumentEnvelope'); +const inventoryHtlc = require('../functions/inventoryHtlc'); +const { + buildForwardedDocumentRequest +} = require('../functions/documentMarket'); +const { + DEFAULT_CHUNK_BYTES, + advertiseDocumentBlobs, + splitBlobs, + DocumentBlobTransferBook, + parseFileSendObject +} = require('../functions/documentBlobManifest'); +const { + KEY_REVEAL_TYPE, + prepareSealedSale, + advertiseSealedDocument, + buildKeyRevealMessage, + isWellFormedKeyReveal, + paymentHashHexFromKey, + openSealedDelivery, + openWithClaimPreimage, + requestMatchesPaymentHash, + requestUnlocksContentKey +} = require('../functions/documentSealedExchange'); + +// Strict JSON +const { + messageDataToString, + tryParsePersistedJson, + tryParseWireJsonBody, + utf8FromPersistedRaw +} = require('../functions/wireJson'); // Fabric Types const Actor = require('./actor'); @@ -79,12 +158,18 @@

    Source: types/peer.js

    const Machine = require('./machine'); const Message = require('./message'); const Service = require('./service'); + +// Contract negotiation payload verification (CONTRACT_PROPOSAL wire frames) +const { verifyContractProposalPayload } = require('../functions/contractProposal'); // const Wallet = require('./wallet'); // Constants const PROLOGUE = 'FABRIC'; -/** Safe debug label for a derived Key — never log private material. */ +/** + * Safe debug label for a derived Key — never log private material. + * @private + */ function peerDebugDerivedPublicSummary (keyInstance) { if (!keyInstance || !keyInstance.settings) return '(unavailable)'; const pubHex = typeof keyInstance.settings.public === 'string' @@ -96,7 +181,240 @@

    Source: types/peer.js

    } /** - * An in-memory representation of a node in our network. + * Lowercase hex for comparing {@link P2P_FLUSH_CHAIN} authorized pubkeys. + * @private + */ +function normalizePeerPubkeyHex (pk) { + if (pk == null) return ''; + if (Buffer.isBuffer(pk)) { + const h = pk.toString('hex').toLowerCase(); + if (pk.length === 32) return h; + if (pk.length === 33 && (h.startsWith('02') || h.startsWith('03'))) return h.slice(2); + return h; + } + const s = String(pk).trim(); + const h = (s.startsWith('0x') || s.startsWith('0X')) ? s.slice(2) : s; + const lo = h.toLowerCase(); + if (lo.length === 66 && (lo.startsWith('02') || lo.startsWith('03'))) return lo.slice(2); + return lo; +} + +/** + * Unified delivery trust for frames that arrived under a transport envelope. + * + * Protocol rules (see SECURITY.md / docs/P2P_FORWARD.md): + * - The **outermost** envelope pays for mesh flood and inbound wire credits. + * - Onion peel (`peeledForward`) and foreign-signed `P2P_RELAY` (`relayedAsIs`) + * inners are **local-observe**: no second `relayFrom`, no TCP-origin side-effects + * (document fulfill, dial enqueue, inventory reply to the last hop). + * - Self-signed `P2P_RELAY` inners set `skipRelayFlood` only — the inner is not + * flooded again, but the TCP author remains attributable for punish / fulfill. + * + * @param {Object} [opts] + * @param {boolean} [opts.peeledForward] + * @param {boolean} [opts.relayedAsIs] + * @param {boolean} [opts.skipRelayFlood] + * @param {string|null} [originName] + * @returns {{ + * peeledForward: boolean, + * relayedAsIs: boolean, + * skipRelayFlood: boolean, + * suppressTcpOriginPunish: boolean, + * allowMeshRelay: boolean, + * allowTcpOriginSideEffects: boolean, + * scoreOrigin: (string|null), + * hardDisconnect: boolean + * }} + * @private + */ +function meshDeliveryContext (opts = {}, originName = null) { + const o = (opts && typeof opts === 'object') ? opts : {}; + const peeledForward = o.peeledForward === true; + const relayedAsIs = o.relayedAsIs === true; + const skipRelayFlood = o.skipRelayFlood === true; + const suppressTcpOriginPunish = peeledForward || relayedAsIs; + return { + peeledForward, + relayedAsIs, + skipRelayFlood, + suppressTcpOriginPunish, + allowMeshRelay: !skipRelayFlood && !suppressTcpOriginPunish, + allowTcpOriginSideEffects: !suppressTcpOriginPunish, + scoreOrigin: suppressTcpOriginPunish ? null : originName, + hardDisconnect: !suppressTcpOriginPunish + }; +} + +/** + * Pubkeys declared as patch authorities on a CONTRACT_PUBLISH body. + * @param {object} object + * @returns {Set<string>} + * @private + */ +function collectContractAuthorityPubkeys (object) { + const set = new Set(); + const add = (hex) => { + const h = normalizePeerPubkeyHex(hex); + if (/^[0-9a-f]{64}$/.test(h) || /^[0-9a-f]{66}$/.test(h)) set.add(h); + }; + const def = object && typeof object === 'object' ? object : {}; + const lists = [def.validators, def.parties, def.owners, def.members, def.authorities]; + for (const list of lists) { + if (!Array.isArray(list)) continue; + for (const entry of list) { + if (typeof entry === 'string') add(entry); + else if (entry && typeof entry === 'object') { + add(entry.pubkey || entry.publicKey || entry.id); + } + } + } + return set; +} + +/** + * Whether {@code pubkeyHex} is present in an authority set (x-only ↔ compressed tolerant). + * @param {Set<string>} authorities + * @param {string} pubkeyHex + * @returns {boolean} + * @private + */ +function authoritySetHasPubkey (authorities, pubkeyHex) { + const h = normalizePeerPubkeyHex(pubkeyHex); + if (!h || !authorities || !authorities.size) return false; + if (authorities.has(h)) return true; + if (h.length === 64) { + for (const entry of authorities) { + if (typeof entry === 'string' && entry.length === 66 && entry.slice(2) === h) return true; + } + } else if (h.length === 66) { + const xOnly = h.slice(2); + if (authorities.has(xOnly)) return true; + } + return false; +} + +/** + * Mesh types that MUST be forwarded bit-identical (author + signature preserved). + * Relays never hop-re-sign these — wire-hash dedup and end-to-end / multisig verify depend on it. + * Local agents may still *originate* new messages of these types signed with their own key. + * @private + */ +const RELAY_AS_IS_TYPES = new Set([ + 'BITCOIN_BLOCK', + 'BitcoinBlock', + 'P2P_CHAT_MESSAGE', + 'P2P_PEER_ALIAS', + 'CONTRACT_PUBLISH', + 'CONTRACT_MESSAGE', + 'CONTRACT_PROPOSAL', + 'ContractProposal', + P2P_PEER_GOSSIP, + 'P2P_PEER_GOSSIP', + P2P_PEERING_OFFER, + 'P2P_PEERING_OFFER', + // Bit-identical mesh forward when local peer does not hold the document. + 'DOCUMENT_REQUEST', + 'DocumentRequest', + // Inventory request/response: prior-hop / buyer author must survive TCP peer pin checks. + 'P2P_INVENTORY_REQUEST', + 'P2P_INVENTORY_RESPONSE', + 'INVENTORY_REQUEST', + 'INVENTORY_RESPONSE', + // Source-signed onion layers: author is path builder, not the TCP peer. + 'P2P_FORWARD', + // Mesh flood envelope: outer is attacker/path-builder signed; forward bit-identical. + // Without this, the next hop pin-checks AMP author against the honest forwarder and bans them. + 'P2P_RELAY' +]); + +const RELAY_AS_IS_NUMERIC = new Set([ + P2P_PEER_GOSSIP, + P2P_PEERING_OFFER, + P2P_PEER_ALIAS, + P2P_CHAT_MESSAGE, + P2P_INVENTORY_REQUEST, + P2P_INVENTORY_RESPONSE, + DOCUMENT_REQUEST_TYPE, + P2P_FORWARD, + P2P_RELAY +]); + +/** + * Outer / generic types whose local registration side-effects are first-writer-wins. + * Exact wire duplicates are already dropped via {@link Peer#messages} (buffer hash). + * These types also no-op when the *logical* payload was already registered — including + * re-signed copies of the same body (different AMP signature → different wire hash). + * @private + */ +const LOGICAL_REGISTER_ONCE_TYPES = new Set([ + 'CONTRACT_PUBLISH', + 'P2P_CONTRACT_PUBLISH', + 'DOCUMENT_PUBLISH', + 'DocumentPublish', + 'P2P_DOCUMENT_PUBLISH', + 'CONTRACT_PROPOSAL', + 'ContractProposal', + 'P2P_CONTRACT_PROPOSAL', + 'P2P_PEER_ANNOUNCE', + 'P2P_STATE_ANNOUNCE', + // Tip / operator / identity registration frames (re-sign ≠ new event) + 'BitcoinBlock', + 'BITCOIN_BLOCK', + 'P2P_FLUSH_CHAIN', + 'FlushChain', + 'P2P_PEER_ALIAS', + 'DocumentContentKeyReveal' +]); + +/** + * @param {string|number|null|undefined} type + * @returns {boolean} + * @private + */ +function isRelayAsIsWireType (type) { + if (type == null) return false; + if (typeof type === 'number') { + return RELAY_AS_IS_NUMERIC.has(type); + } + return RELAY_AS_IS_TYPES.has(String(type)); +} + +/** + * Generic / base carriers may wrap a relay-as-is body (Hub transitional path). + * Pin-check against the TCP peer would wrongly reject multisig / prior-hop authors. + * @param {Message} message + * @returns {boolean} + * @private + */ +function isRelayAsIsGenericCarrier (message) { + const outer = message && (message.type || message.friendlyType); + if (!outer) return false; + const carriers = new Set([ + 'GENERIC_MESSAGE', + 'GenericMessage', + 'P2P_BASE_MESSAGE', + 'CHAT_MESSAGE', + 'ChatMessage' + ]); + if (!carriers.has(String(outer))) return false; + try { + const raw = messageDataToString(message.data); + const pr = tryParseWireJsonBody(raw); + if (!pr.ok || !pr.value || typeof pr.value !== 'object' || Array.isArray(pr.value)) { + return false; + } + return isRelayAsIsWireType(pr.value.type); + } catch (e) { + return false; + } +} + +/** + * @classdesc P2P node: TCP/NOISE sessions, gossip, and relay of {@link Message} (AMP) frames. Extends {@link Service} + * (hence {@link Actor}). Opcode and receipt semantics must stay aligned with <strong>@fabric/http</strong> and Hub when you add types — + * see {@link Message} wire vs friendly names and <code>constants</code> opcodes. + * @class Peer + * @extends Service */ class Peer extends Service { /** @@ -105,6 +423,8 @@

    Source: types/peer.js

    * @param {Boolean} [config.listen] Whether or not to listen for connections. * @param {Boolean} [config.upnp] Whether or not to use UPNP for automatic configuration. * @param {Number} [config.port=7777] Port to use for P2P connections. + * @param {Number} [config.listenPortAttempts=20] When the listen port is in use (`EADDRINUSE`), + * try the next port up to this many times (same host). * @param {Array} [config.peers=[]] List of initial peers. */ constructor (config = {}) { @@ -129,38 +449,92 @@

    Source: types/peer.js

    // a developer machine's stale peer list (which can hang/timeout the suite). peersDb: (process.env.NODE_ENV === 'test') ? null : 'stores/hub/peers', port: 7777, + listenPortAttempts: 20, reconnectToKnownPeers: true, + // When true, answers INVENTORY_REQUEST: `offerBtc` (L1 offers) and `kind: 'documents'` + // (Hub / browser catalog) using local `_state.content.documents` (+ optional rates/collections). + serveLocalDocumentInventory: false, + // When false (default), DOCUMENT_REQUEST queues for operator approve/deny. + // When true, auto-sends P2P_FILE_SEND if held (convenient but amplifies egress). + autoFulfillDocumentRequests: false, + // When true (default), priced publishes seal AES-GCM; HTLC preimage = content key. + sealPricedDocuments: true, + // Attach P2TR HTLC fields on offerBtc inventory responses. + attachInventoryHtlc: false, + inventoryHtlcLocktimeHeight: null, + inventoryBlobChunkBytes: DEFAULT_CHUNK_BYTES, + // Privacy-preserving DocumentRequest rewrite + fee skim when maxSats set. + relayPrivateDocumentRequests: false, + documentRelayFeeSats: null, + documentRelayFeeBps: 100, + documentRelayMinRemainingSats: 1, + documentRelayMaxHops: 4, + // Opt-in only: approveDocumentRequest({ forceReveal: true }) without settlement. + allowForceDocumentKeyReveal: false, + // Re-send canonical DocumentPublish + pricing to new inbound peers. + announceDocumentsOnPeerConnect: false, + // If local inventory doesn't answer, optionally relay INVENTORY_REQUEST. + relayInventoryRequest: false, + // Optionally relay INVENTORY_RESPONSE to peers other than the sender. + relayInventoryResponse: false, connectTimeout: 5000, - /** Limits relay amplification on {@link P2P_PEER_GOSSIP} (hop TTL, payload dedup, per-origin rate). */ + // Relay amplification controls for P2P_PEER_GOSSIP. gossip: { maxHops: GOSSIP_MAX_HOPS, maxRelaysPerOriginPerMinute: GOSSIP_MAX_RELAYS_PER_ORIGIN_PER_MINUTE, maxPayloadCache: GOSSIP_MAX_PAYLOAD_CACHE, maxWireHashCache: PEER_MAX_WIRE_HASH_CACHE }, + // Mesh chat amplify controls (local `chat` event still fires when limited). + chat: { + maxRelaysPerOriginPerMinute: CHAT_MAX_RELAYS_PER_ORIGIN_PER_MINUTE + }, + maxPendingSealedDeliveries: PEER_MAX_PENDING_SEALED_DELIVERIES, + maxDocumentRelayRoutes: PEER_MAX_DOCUMENT_RELAY_ROUTES, state: Object.assign({ actors: {}, channels: {}, contracts: {}, documents: {}, + documentRates: {}, + documentSealed: {}, + documentContentKeys: {}, messages: {}, services: {} }, config.state), upnp: false, key: {}, - /** - * Inbound wire traffic budgeting (Bitcoin Core–style peer quality). - * Credits accrue per rolling window; overflow de-ranks the peer (registry score) - * and drops the message. Heavier opcodes cost more credits. - */ + // Inbound wire traffic budgeting (Bitcoin Core-style peer quality). wireTraffic: { windowMs: 60 * 1000, maxCreditsPerWindow: 520, chainSyncCreditCost: 55, + flushChainCreditCost: 120, bitcoinBlockCreditCost: 3, + relayCreditCost: PEER_RELAY_CREDIT_COST, defaultCreditCost: 1, - overLimitPenalty: 22 - } + overLimitPenalty: 22, + sessionKeyViolationPenalty: PEER_SCORE_SESSION_KEY_VIOLATION_PENALTY + }, + // Registry-score misbehavior penalties (see SECURITY.md). + peerScore: { + bodyHashMismatchPenalty: PEER_SCORE_BODY_HASH_MISMATCH_PENALTY, + invalidSignaturePenalty: PEER_SCORE_INVALID_SIGNATURE_PENALTY, + signerPinMismatchPenalty: PEER_SCORE_SIGNER_PIN_MISMATCH_PENALTY, + contractOpsForbiddenPenalty: PEER_SCORE_CONTRACT_OPS_FORBIDDEN_PENALTY, + logicalRegisterHijackPenalty: PEER_SCORE_LOGICAL_REGISTER_HIJACK_PENALTY, + logicalRegisterDuplicatePenalty: PEER_SCORE_LOGICAL_REGISTER_DUPLICATE_PENALTY, + logicalRegisterDuplicateWindowMs: PEER_SCORE_LOGICAL_REGISTER_DUPLICATE_WINDOW_MS, + relayNestExceededPenalty: PEER_SCORE_RELAY_NEST_EXCEEDED_PENALTY, + disconnectOnHardMisbehavior: true, + banOnHardMisbehavior: true, + banTtlMs: PEER_BAN_TTL_MS, + maxRelayNestDepth: PEER_MAX_RELAY_NEST_DEPTH + }, + // Inbound P2P_FLUSH_CHAIN: sender registry score must be strictly greater than this (relay uses same threshold). + flushChainMinTrustedScore: 800, + // Inbound P2P_FLUSH_CHAIN: allowed signer pubkeys (hex/Buffers); empty = reject all until configured. + flushChainAuthorizedPubkeys: [] }, config); // Network Internals @@ -205,27 +579,62 @@

    Source: types/peer.js

    this.connections = {}; this.history = []; this.peers = {}; + /** Pending DOCUMENT_REQUEST entries when {@link Peer#settings.autoFulfillDocumentRequests} is false. */ + this.pendingDocumentRequests = Object.create(null); + /** + * Authorized sealed-document key reveals: `"documentId|paymentHashHex"` → meta. + * Populated only after verified settlement (never from public hash echo). + */ + this._authorizedDocumentKeyReveals = new Map(); + /** contractId → Set of lowercase compressed pubkeys allowed to apply state ops. */ + this._contractPatchAllowList = Object.create(null); + /** Buyer-side verified blob reassembly (DocumentBlobIndex). */ + this.blobTransfers = new DocumentBlobTransferBook(); + /** Ciphertext awaiting content-key reveal: documentId → meta. */ + this.pendingSealedDeliveries = Object.create(null); + this._pendingDocumentRequestSeq = 0; + /** Private reverse routes for rewritten DocumentRequests (never gossiped). */ + this._documentRelayRoutes = Object.create(null); + this._documentRelaySeen = new Set(); // Peers: keyed by public key (id). Persistent registry in _state.peers. // Map connection address (IP:port) -> peer id (public key). Learned on P2P_SESSION_OFFER/OPEN. this._addressToId = {}; + /** Inbound address -> NOISE static pubkey hex (FLUSH_CHAIN allowlist only; never for AMP verify). */ + this._inboundNoiseStaticPubkeyByAddress = Object.create(null); this.mailboxes = {}; this.memory = {}; this.handlers = {}; /** Wire-envelope dedup (SHA-256 of full buffer); FIFO-capped via {@link Peer#_rememberWireHash}. */ this.messages = {}; this._wireHashOrder = []; + /** + * Logical first-writer-wins registrations (content-addressed keys). + * Catches re-signed duplicates of CONTRACT_PUBLISH / DOCUMENT_PUBLISH / etc. + * @type {Map<string, { type: string, signer: string|null, at: number }>} + */ + this._logicalRegisterOnce = new Map(); + this._logicalRegisterOrder = []; /** Logical gossip payload dedup (excludes signature / hop churn). */ this._gossipPayloadSeen = new Map(); this._gossipPayloadOrder = []; /** origin address → { count, windowStart } for gossip relay rate limiting. */ this._gossipRelayByOrigin = new Map(); - /** Logical peering-offer payload dedup (ignores per-hop re-signing). */ + /** origin address → { count, windowStart } for chat mesh relay rate limiting. */ + this._chatRelayByOrigin = new Map(); + /** Logical peering-offer payload dedup (ignores advisory peeringHop; frames relay as-is). */ this._peeringPayloadSeen = new Map(); this._peeringPayloadOrder = []; /** origin address → { count, windowStart } for peering-offer relay rate limiting. */ this._peeringRelayByOrigin = new Map(); /** `host:port` → { credits, windowStart, penalized } — inbound wire flood / de-rank (per peer). */ this._wireInboundByOrigin = new Map(); + /** `host:port` → { windowStart, penalized } — soft logical-duplicate derank once per window. */ + this._logicalDupPenaltyByOrigin = new Map(); + /** + * Temporary bans after hard misbehavior: `addr:<host:port>` or `pk:<hex>` → { until, reason }. + * @type {Map<string, { until: number, reason: string }>} + */ + this._peerBans = new Map(); /** `host:port` keys for {@link P2P_PEERING_OFFER} candidate queue dedup. */ this._candidateKeys = new Set(); /** @@ -259,23 +668,9 @@

    Source: types/peer.js

    return this; } - _resolveToAddress (idOrAddress) { - if (!idOrAddress || typeof idOrAddress !== 'string') return null; - // Direct match on connection address - if (this.connections[idOrAddress]) return idOrAddress; - // Look up by id: find which address this id is connected from - const registry = this._state.peers || {}; - const byId = registry[idOrAddress]; - if (byId && byId.address && this.connections[byId.address]) return byId.address; - // Check _addressToId reverse: find address that maps to this id - for (const [addr, id] of Object.entries(this._addressToId || {})) { - if (id === idOrAddress && this.connections[addr]) return addr; - } - return null; - } - /** - * Stable id for gossip *logical* content (ignores `gossipHop` and wire signature changes). + * Stable id for gossip *logical* content (ignores advisory `gossipHop`; frames are forwarded bit-identical). + * Mesh frames are relayed bit-identical; wire-hash dedup also prevents loops. * @param {object} msg Generic message (`type`, `object`, …) * @returns {string} hex sha256 */ @@ -296,6 +691,268 @@

    Source: types/peer.js

    this._wireHashOrder.push(hash); } + /** + * Content-addressed key for first-writer-wins registration types. + * @param {string} type wire / generic type name + * @param {object|null|undefined} object message body / object + * @returns {string|null} + */ + _logicalRegistrationKey (type, object) { + const t = String(type || ''); + if (!LOGICAL_REGISTER_ONCE_TYPES.has(t)) return null; + const obj = (object && typeof object === 'object' && !Array.isArray(object)) ? object : null; + if (!obj) return null; + + if (t === 'CONTRACT_PUBLISH' || t === 'P2P_CONTRACT_PUBLISH') { + return `contract:${new Actor(obj).id}`; + } + if (t === 'DOCUMENT_PUBLISH' || t === 'DocumentPublish') { + const docId = obj.id != null ? String(obj.id) : ''; + if (!docId) return null; + try { + const hash = purchaseContentHashHex(docId, obj); + return `document:${docId}:${hash}`; + } catch (_) { + return `document:${docId}`; + } + } + if (t === 'P2P_DOCUMENT_PUBLISH') { + const docId = obj.hash != null ? String(obj.hash) : (obj.id != null ? String(obj.id) : ''); + if (!docId) return null; + const rate = obj.rate != null ? String(obj.rate) : ''; + const ch = obj.contentHash != null ? String(obj.contentHash) : ''; + return `docprice:${docId}:${rate}:${ch}`; + } + if (t === 'CONTRACT_PROPOSAL' || t === 'ContractProposal' || t === 'P2P_CONTRACT_PROPOSAL') { + const root = obj.chain && obj.chain.merkleRoot != null + ? String(obj.chain.merkleRoot) + : (obj.merkleRoot != null ? String(obj.merkleRoot) : ''); + if (!root) return null; + const cid = obj.contractId != null ? String(obj.contractId) : ''; + return `proposal:${cid}:${root}`; + } + if (t === 'P2P_PEER_ANNOUNCE') { + return `announce:${new Actor(obj).id}`; + } + if (t === 'P2P_STATE_ANNOUNCE') { + const stateObj = (obj.state && typeof obj.state === 'object') ? obj.state : obj; + return `state:${new Actor(stateObj).id}`; + } + if (t === 'BitcoinBlock' || t === 'BITCOIN_BLOCK') { + const tip = obj.tip != null ? String(obj.tip) + : (obj.hash != null ? String(obj.hash) + : (obj.blockHash != null ? String(obj.blockHash) + : (obj.content != null ? String(obj.content) : ''))); + if (!tip) return null; + return `btcblock:${tip.toLowerCase()}`; + } + if (t === 'P2P_FLUSH_CHAIN' || t === 'FlushChain') { + const snap = obj.snapshotBlockHash != null ? String(obj.snapshotBlockHash).trim().toLowerCase() : ''; + if (!snap) return null; + return `flush:${snap}`; + } + if (t === 'P2P_PEER_ALIAS') { + const alias = obj.alias != null ? String(obj.alias).trim() : ''; + const signer = obj.signer != null ? normalizePeerPubkeyHex(obj.signer) : ''; + if (!alias || !signer) return null; + return `alias:${signer}:${alias}`; + } + if (t === 'DocumentContentKeyReveal') { + // Bind the slot to SHA256(keyHex), not the attacker-supplied public hash field. + // Mismatched / malformed reveals return null (no claim) — see isWellFormedKeyReveal. + const docId = obj.documentId != null ? String(obj.documentId).trim() : ''; + const key = obj.keyHex != null ? String(obj.keyHex).trim().toLowerCase() : ''; + if (!docId || !/^[0-9a-f]{64}$/.test(key)) return null; + const derived = paymentHashHexFromKey(key); + const pay = obj.paymentHashHex != null ? String(obj.paymentHashHex).trim().toLowerCase() : ''; + if (pay && pay !== derived) return null; + return `keyreveal:${docId}:${derived}`; + } + return null; + } + + /** + * Claim a logical registration key (first writer wins). + * @param {string} type + * @param {object|null|undefined} object + * @param {string|null} [signerPubkeyHex] + * @returns {{ duplicate: boolean, key: (string|null), prior: (object|null) }} + */ + _claimLogicalRegistration (type, object, signerPubkeyHex = null) { + const key = this._logicalRegistrationKey(type, object); + if (!key) return { duplicate: false, key: null, prior: null }; + const prior = this._logicalRegisterOnce.get(key) || null; + if (prior) return { duplicate: true, key, prior }; + const max = (this.settings.logicalRegister && this.settings.logicalRegister.maxCache) || + PEER_MAX_LOGICAL_REGISTER_CACHE; + while (this._logicalRegisterOrder.length >= max) { + const drop = this._logicalRegisterOrder.shift(); + this._logicalRegisterOnce.delete(drop); + } + const entry = { + type: String(type || ''), + signer: signerPubkeyHex ? normalizePeerPubkeyHex(signerPubkeyHex) : null, + at: Date.now() + }; + this._logicalRegisterOnce.set(key, entry); + this._logicalRegisterOrder.push(key); + return { duplicate: false, key, prior: null }; + } + + /** + * Lower registry score and optionally destroy the TCP connection (hard misbehavior). + * Hard disconnects also install a temporary ban (address + known pubkey). + * @param {string|null|undefined} originName + * @param {string} reason + * @param {Object} [opts] + * @param {number} [opts.penalty] + * @param {boolean} [opts.disconnect] + */ + _applyPeerMisbehavior (originName, reason, opts = {}) { + const penalty = Number(opts.penalty); + const pen = Number.isFinite(penalty) && penalty > 0 ? penalty : 20; + const wantDisconnect = opts.disconnect === true; + const ps = this.settings.peerScore || {}; + const hardDisconnect = ps.disconnectOnHardMisbehavior !== false; + if (originName) { + this._derankPeerForWireTraffic(originName, pen, reason); + } + this.emit('warning', + `[FABRIC:PEER] Misbehavior (${reason}) from ${originName || 'unknown'} penalty=${pen}` + + (wantDisconnect && hardDisconnect ? ' disconnect=1' : '')); + if (wantDisconnect && hardDisconnect && originName) { + if (ps.banOnHardMisbehavior !== false) { + this._banPeer(originName, reason); + } + const conn = this.connections && this.connections[originName]; + if (conn && typeof conn.destroy === 'function') conn.destroy(); + // Drop registry of the live socket so reconnect/ban checks apply immediately. + if (this.connections) delete this.connections[originName]; + if (this.peers && this.peers[originName] && typeof this.peers[originName] === 'object') { + this.peers[originName].status = 'disconnected'; + } + } + } + + /** + * Ban a connection address (and mapped pubkey when known) for {@link Peer#settings.peerScore.banTtlMs}. + * @param {string} originName + * @param {string} reason + */ + _banPeer (originName, reason) { + if (!originName) return; + const ps = this.settings.peerScore || {}; + const ttl = Number(ps.banTtlMs); + const banMs = Number.isFinite(ttl) && ttl > 0 ? ttl : PEER_BAN_TTL_MS; + const until = Date.now() + banMs; + const entry = { until, reason: String(reason || 'misbehavior') }; + this._peerBans.set(`addr:${originName}`, entry); + const peerId = (this._addressToId && this._addressToId[originName]) || null; + const reg = (this._state.peers && (this._state.peers[peerId] || this._state.peers[originName])) || null; + const pk = normalizePeerPubkeyHex( + (reg && reg.publicKey) || + (this.peers[originName] && this.peers[originName].publicKey) || + peerId + ); + if (pk && pk.length >= 64) { + this._peerBans.set(`pk:${pk}`, entry); + } + this.emit('warning', + `[FABRIC:PEER] Banned ${originName}${pk ? ` pk=${pk.slice(0, 16)}…` : ''} until=${new Date(until).toISOString()} (${entry.reason})`); + } + + /** + * @param {string|null|undefined} originName + * @param {string|null|undefined} [pubkeyHex] + * @returns {boolean} + */ + _isPeerBanned (originName = null, pubkeyHex = null) { + const now = Date.now(); + for (const [k, v] of this._peerBans) { + if (!v || !(v.until > now)) this._peerBans.delete(k); + } + if (originName && this._peerBans.has(`addr:${originName}`)) return true; + const pk = normalizePeerPubkeyHex(pubkeyHex); + if (pk && this._peerBans.has(`pk:${pk}`)) return true; + if (originName) { + const peerId = (this._addressToId && this._addressToId[originName]) || null; + const reg = (this._state.peers && (this._state.peers[peerId] || this._state.peers[originName])) || null; + const mapped = normalizePeerPubkeyHex( + (reg && reg.publicKey) || + (this.peers[originName] && this.peers[originName].publicKey) || + peerId + ); + if (mapped && this._peerBans.has(`pk:${mapped}`)) return true; + } + return false; + } + + /** + * Soft (once/window) or hijack (CONTRACT_PUBLISH other signer) penalty for logical duplicates. + * @param {string|null|undefined} originName + * @param {string} type + * @param {Object} claim + * @param {Object|null} [claim.prior] + * @param {string|null} [claim.prior.signer] + * @param {string|null} [signerPubkeyHex] + */ + _logicalRegisterDuplicateMisbehavior (originName, type, claim, signerPubkeyHex = null) { + const ps = this.settings.peerScore || {}; + const t = String(type || ''); + const current = signerPubkeyHex ? normalizePeerPubkeyHex(signerPubkeyHex) : null; + const priorSigner = claim && claim.prior && claim.prior.signer + ? normalizePeerPubkeyHex(claim.prior.signer) + : null; + const isHijack = (t === 'CONTRACT_PUBLISH' || t === 'P2P_CONTRACT_PUBLISH') && + priorSigner && current && priorSigner !== current; + + if (isHijack) { + const penalty = Number(ps.logicalRegisterHijackPenalty) || PEER_SCORE_LOGICAL_REGISTER_HIJACK_PENALTY; + this._applyPeerMisbehavior(originName, `logical-register-hijack:${t}`, { + penalty, + disconnect: false + }); + return; + } + + if (!originName) return; + const windowMs = Number(ps.logicalRegisterDuplicateWindowMs) || + PEER_SCORE_LOGICAL_REGISTER_DUPLICATE_WINDOW_MS; + const penalty = Number(ps.logicalRegisterDuplicatePenalty) || + PEER_SCORE_LOGICAL_REGISTER_DUPLICATE_PENALTY; + const now = Date.now(); + let slot = this._logicalDupPenaltyByOrigin.get(originName); + if (!slot || (now - slot.windowStart) >= windowMs) { + slot = { windowStart: now, penalized: false }; + } + if (slot.penalized) { + this._logicalDupPenaltyByOrigin.set(originName, slot); + return; + } + slot.penalized = true; + this._logicalDupPenaltyByOrigin.set(originName, slot); + this._applyPeerMisbehavior(originName, `logical-register-duplicate:${t}`, { + penalty, + disconnect: false + }); + } + + /** + * Claim logical registration; on duplicate, apply misbehavior and return claim. + * @param {string} type + * @param {object|null|undefined} object + * @param {string|null} [signerPubkeyHex] + * @param {string|null} [originName] + * @returns {{ duplicate: boolean, key: (string|null), prior: (object|null) }} + */ + _claimLogicalRegistrationOrPunish (type, object, signerPubkeyHex = null, originName = null) { + const claim = this._claimLogicalRegistration(type, object, signerPubkeyHex); + if (claim.duplicate) { + this._logicalRegisterDuplicateMisbehavior(originName, type, claim, signerPubkeyHex); + } + return claim; + } + _gossipRememberPayload (key) { const max = (this.settings.gossip && this.settings.gossip.maxPayloadCache) || GOSSIP_MAX_PAYLOAD_CACHE; while (this._gossipPayloadOrder.length >= max) { @@ -323,6 +980,56 @@

    Source: types/peer.js

    return true; } + /** + * @param {string} originName Connection id (e.g. `host:port`) + * @returns {boolean} + */ + _chatRateLimitAllow (originName) { + const limit = (this.settings.chat && this.settings.chat.maxRelaysPerOriginPerMinute) || + CHAT_MAX_RELAYS_PER_ORIGIN_PER_MINUTE; + const now = Date.now(); + let slot = this._chatRelayByOrigin.get(originName); + if (!slot || now - slot.windowStart > 60000) { + slot = { count: 0, windowStart: now }; + } + if (slot.count >= limit) return false; + slot.count++; + this._chatRelayByOrigin.set(originName, slot); + return true; + } + + _capPendingSealedDeliveries () { + const max = Number(this.settings.maxPendingSealedDeliveries); + const cap = Number.isFinite(max) && max > 0 ? max : PEER_MAX_PENDING_SEALED_DELIVERIES; + const ids = Object.keys(this.pendingSealedDeliveries); + if (ids.length <= cap) return; + ids.sort((a, b) => { + const ta = (this.pendingSealedDeliveries[a] && this.pendingSealedDeliveries[a].updated) || 0; + const tb = (this.pendingSealedDeliveries[b] && this.pendingSealedDeliveries[b].updated) || 0; + return ta - tb; + }); + const drop = ids.length - cap; + for (let i = 0; i < drop; i++) { + delete this.pendingSealedDeliveries[ids[i]]; + } + } + + _capDocumentRelayRoutes () { + const max = Number(this.settings.maxDocumentRelayRoutes); + const cap = Number.isFinite(max) && max > 0 ? max : PEER_MAX_DOCUMENT_RELAY_ROUTES; + const ids = Object.keys(this._documentRelayRoutes); + if (ids.length <= cap) return; + ids.sort((a, b) => { + const ta = (this._documentRelayRoutes[a] && this._documentRelayRoutes[a].created) || 0; + const tb = (this._documentRelayRoutes[b] && this._documentRelayRoutes[b].created) || 0; + return ta - tb; + }); + const drop = ids.length - cap; + for (let i = 0; i < drop; i++) { + delete this._documentRelayRoutes[ids[i]]; + } + } + /** * Credit cost for inbound wire messages (heavier types consume more of the peer's budget). * @param {string|number} wireType @@ -334,9 +1041,18 @@

    Source: types/peer.js

    if (t === 'P2P_CHAIN_SYNC_REQUEST' || t === 'ChainSyncRequest' || wireType === P2P_CHAIN_SYNC_REQUEST) { return Number(w.chainSyncCreditCost) || 55; } + if (t === 'P2P_FLUSH_CHAIN' || t === 'FlushChain' || wireType === P2P_FLUSH_CHAIN) { + return Number(w.flushChainCreditCost) || 120; + } if (t === 'BITCOIN_BLOCK' || t === 'BitcoinBlock') { return Number(w.bitcoinBlockCreditCost) || 3; } + if (t === 'P2P_FORWARD' || wireType === P2P_FORWARD) { + return Number(w.forwardCreditCost) || 4; + } + if (t === 'P2P_RELAY' || wireType === P2P_RELAY) { + return Number(w.relayCreditCost) || PEER_RELAY_CREDIT_COST; + } return Number(w.defaultCreditCost) || 1; } @@ -396,7 +1112,7 @@

    Source: types/peer.js

    } /** - * Stable id for peering-offer *logical* content (ignores `peeringHop` and wire signature changes). + * Stable id for peering-offer *logical* content (ignores advisory `peeringHop`). * @param {object} msg Generic message (`type`, `object`, …) * @returns {string} hex sha256 */ @@ -517,7 +1233,7 @@

    Source: types/peer.js

    } } } - } + }; } get interface () { @@ -623,7 +1339,7 @@

    Source: types/peer.js

    } _resolveToAddress (idOrAddress) { - if (!idOrAddress) return null; + if (!idOrAddress || typeof idOrAddress !== 'string') return null; if (this.connections[idOrAddress]) return idOrAddress; const addressToId = this._addressToId || {}; for (const addr in addressToId) { @@ -674,66 +1390,335 @@

    Source: types/peer.js

    } } - subscribe (path) { - + /** + * Local node x-only pubkey (AMP {@code author} / {@code P2P_FORWARD.nextPeer} encoding). + * @returns {Buffer} + */ + _localXOnlyPeerId () { + return xOnlyFromKey(this.key); } - _beginFabricHandshake (client) { - // Start handshake - const vector = ['P2P_SESSION_OFFER', JSON.stringify({ - type: 'P2P_SESSION_OFFER', - actor: { - id: this.identity.id - }, - object: { - challenge: crypto.randomBytes(8).toString('hex'), - } - })]; + /** + * Resolve a live connection address for an x-only (or compressed) peer pubkey. + * @param {Buffer|string} peerId + * @returns {string|null} connection key ({@code host:port}) or null + */ + _resolveAddressByXOnly (peerId) { + let want; + try { + want = toXOnlyPeerId(peerId); + } catch (err) { + return null; + } + if (xOnlyEquals(want, this._localXOnlyPeerId())) return null; - // Create offer message - const P2P_SESSION_OFFER = Message.fromVector(vector).signWithKey(this.key); - const message = P2P_SESSION_OFFER.toBuffer(); - if (this.settings.debug) this.emit('debug', `session_offer ${P2P_SESSION_OFFER} ${message.toString('hex')}`); + const connections = this.connections || {}; + for (const addr of Object.keys(connections)) { + const rec = this.peers[addr]; + if (!rec || rec.publicKey == null) continue; + try { + if (xOnlyEquals(want, toXOnlyPeerId(rec.publicKey))) return addr; + } catch (err) { + // ignore malformed registry keys + } + } - // Send handshake - try { - client.encrypt.write(message); - } catch (exception) { - if (exception && (exception.code === 'EPIPE' || exception.code === 'ECONNRESET')) { - this.emit('warning', `Suppressing transient write error (${exception.code}) during handshake.`); - } else { - this.emit('error', `Cannot write to socket: ${exception}`); + const registry = this._state.peers || {}; + for (const key of Object.keys(registry)) { + const entry = registry[key]; + if (!entry) continue; + const pk = entry.publicKey || entry.pubkey; + if (pk == null) continue; + try { + if (!xOnlyEquals(want, toXOnlyPeerId(pk))) continue; + } catch (err) { + continue; } + const addr = entry.address || key; + if (addr && connections[addr]) return addr; + const resolved = this._resolveToAddress(addr) || this._resolveToAddress(key); + if (resolved) return resolved; } + return null; + } - return this; + /** + * Send {@code payload} along a source-routed onion path of Fabric peer pubkeys. + * Builds nested {@code P2P_FORWARD} layers and writes the outer frame only to + * {@code path[0]} (immediate hop). Destination learns the last hop's IP, not + * the originator's. See {@link module:@fabric/core/functions/fabricOnion}. + * + * @param {Array<Buffer|string>} path hop pubkeys; first = next TCP peer, last = deliverer + * @param {Message|Buffer} payload innermost application Message (should already be signed) + * @returns {boolean} true if the outer frame was written to the first hop + */ + sendOnion (path, payload) { + if (!Array.isArray(path) || !path.length) { + this.emit('warning', '[FABRIC:PEER] sendOnion: empty path'); + return false; + } + let outer; + try { + outer = wrapOnionPath({ path, payload, key: this.key }); + } catch (err) { + this.emit('warning', `[FABRIC:PEER] sendOnion wrap failed: ${err && err.message ? err.message : err}`); + return false; + } + const first = path[0]; + const addr = this._resolveAddressByXOnly(first) || this._resolveToAddress( + typeof first === 'string' ? first : null + ); + if (!addr || !this.connections[addr] || !this.connections[addr]._writeFabric) { + this.emit('warning', '[FABRIC:PEER] sendOnion: first hop not connected'); + this.emit('onion:undeliverable', { path, reason: 'first-hop-missing' }); + return false; + } + this.connections[addr]._writeFabric(outer.toBuffer()); + this.emit('onion:sent', { pathLength: path.length, firstHop: addr }); + return true; } /** - * Open a Fabric connection to the target address and initiate the Fabric Protocol. - * @param {String} target Target address. + * Handle inbound {@code P2P_FORWARD}: peel when {@code nextPeer} is local, else + * forward the bit-identical outer frame to that peer only (no mesh flood). + * @private */ - _connect (target) { - if (!target || typeof target !== 'string') { - this.emit('error', '[FABRIC:PEER:_connect] target must be a non-empty string'); - return; + _handleP2PForward (message, origin, socket) { + const fields = tryDecodeForward(message); + if (!fields) { + this.emit('warning', '[FABRIC:PEER] P2P_FORWARD body undecodable'); + return this; + } + if (fields.ttl < 1) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Dropped P2P_FORWARD with ttl=0'); + } + return this; } - if (this.connections[target]) { - this.emit('debug', `[FABRIC:PEER:_connect] Already connected to ${target}; skipping`); - return; + const local = this._localXOnlyPeerId(); + if (xOnlyEquals(fields.nextPeer, local)) { + this.emit('onion:peel', { + ttl: fields.ttl, + origin: origin && origin.name, + innerBytes: fields.inner.length + }); + if (fields.inner.length > 0) { + // Peeled inners must not hard-disconnect / derank the TCP last hop: + // relays forward bit-identical outers, so a malicious originator can + // launder a bad inner through an honest relay (see SECURITY.md / P2P_FORWARD). + this._handleFabricMessage(fields.inner, origin, socket, { peeledForward: true }); + } + return this; } - this.emit('debug', `[FABRIC:PEER:_connect] Attempting to connect to: ${target}`); - const url = new URL(`tcp://${target}`); - const id = url.username; + const addr = this._resolveAddressByXOnly(fields.nextPeer); + if (!addr || !this.connections[addr] || !this.connections[addr]._writeFabric) { + this.emit('warning', '[FABRIC:PEER] P2P_FORWARD next hop not connected'); + this.emit('onion:undeliverable', { + nextPeer: fields.nextPeer.toString('hex'), + origin: origin && origin.name, + reason: 'next-hop-missing' + }); + return this; + } + if (origin && origin.name && addr === origin.name) { + this.emit('warning', '[FABRIC:PEER] P2P_FORWARD refuses bounce to origin'); + return this; + } - if (!url.port) target += `:${P2P_PORT}`; + // Bit-identical forward — do not re-sign; path builder signature stays intact. + const wire = (message && typeof message.toBuffer === 'function') + ? message.toBuffer() + : null; + if (!wire || !wire.length) { + this.emit('warning', '[FABRIC:PEER] P2P_FORWARD missing wire buffer'); + return this; + } + this.connections[addr]._writeFabric(wire); + this.emit('onion:forward', { + nextHop: addr, + ttl: fields.ttl, + origin: origin && origin.name + }); + return this; + } - this._outboundDialTargets.add(target); + /** + * Relay an AMP message only to connected peers whose persistent registry score is strictly greater than + * {@link Peer#settings.flushChainMinTrustedScore} (default 800). Used for `P2P_FLUSH_CHAIN`. + * @param {string|null} origin - Connection key to skip (inbound sender), or null when originating locally. + * @param {Message|Buffer} message + * @param {number} [minScoreExclusive] - Override trust threshold (relay if peer score &gt; this value). + */ + relayFromTrustedPeers (origin, message, minScoreExclusive = null) { + const threshold = (minScoreExclusive != null && Number.isFinite(Number(minScoreExclusive))) + ? Number(minScoreExclusive) + : (Number(this.settings.flushChainMinTrustedScore) || 800); + const buf = Buffer.isBuffer(message) ? message : message.toBuffer(); + for (const id in this.connections) { + if (origin && id === origin) continue; + const score = this._registryScoreForConnectionAddress(id); + if (!(score > threshold)) continue; + this.connections[id]._writeFabric(buf); + } + } - const derived = this.identity.key.derive(FABRIC_KEY_DERIVATION_PATH); - this.emit('debug', `[FABRIC:PEER:_connect] Local derived key (public hex, truncated): ${peerDebugDerivedPublicSummary(derived)} path=${FABRIC_KEY_DERIVATION_PATH}`); + /** + * Best-effort registry score for a live connection key (`host:port`), using mapped Fabric id when known. + * @param {string} connAddress + * @returns {number} + */ + _registryScoreForConnectionAddress (connAddress) { + const reg = this._state.peers || {}; + const mappedId = this._addressToId && this._addressToId[connAddress]; + let best = 0; + for (const key of [mappedId, connAddress]) { + if (!key) continue; + const e = reg[key]; + if (!e) continue; + const s = Number(e.score); + if (Number.isFinite(s) && s > best) best = s; + } + return best; + } + + /** + * FLUSH_CHAIN trust score bound to verified sender key. + * + * Prevents trusting attacker-controlled `P2P_SESSION_OFFER.actor.id` aliases + * by refusing `_addressToId`-mapped scores unless that mapped registry entry + * is explicitly bound to the same verified sender pubkey. + * + * @param {string} connAddress + * @param {string} senderPubkeyHex - verified sender pubkey hex (from NOISE/static or trusted peer record) + * @returns {number} + */ + _registryScoreForFlushChainSender (connAddress, senderPubkeyHex) { + const reg = this._state.peers || {}; + const senderHex = normalizePeerPubkeyHex(senderPubkeyHex); + const candidates = new Set(); + if (connAddress) candidates.add(connAddress); + if (senderHex) candidates.add(senderHex); + + const mappedId = this._addressToId && this._addressToId[connAddress]; + if (mappedId && senderHex) { + const mapped = reg[mappedId]; + if (mapped && typeof mapped === 'object') { + const mappedPk = normalizePeerPubkeyHex(mapped.publicKey); + // Only trust mapped id score when mapped record is key-bound. + if (mappedPk && mappedPk === senderHex) candidates.add(mappedId); + } + } + + let best = 0; + for (const key of candidates) { + const e = reg[key]; + if (!e) continue; + const s = Number(e.score); + if (Number.isFinite(s) && s > best) best = s; + } + + return best; + } + + /** + * Sign and send `P2P_FLUSH_CHAIN` to all connected peers with registry score &gt; threshold. + * Body JSON: `{ snapshotBlockHash, network?, label? }`. + * + * **Receivers** (see `P2P_FLUSH_CHAIN` handler) require **both**: + * 1. Sender pubkey in {@link Peer#settings.flushChainAuthorizedPubkeys} (non-empty allowlist), and + * 2. Registry score above {@link Peer#settings.flushChainMinTrustedScore}. + * Registry score bumps on `P2P_PONG` only when that pong answers an outbound ping on the same + * connection (`_fabricPingOutstanding`), so unsolicited pongs cannot inflate trust alone. + * + * @param {Object} object + * @returns {number} number of sockets written + */ + sendFlushChainToTrustedPeers (object) { + const body = JSON.stringify(object && typeof object === 'object' ? object : {}); + const msg = Message.fromVector(['P2P_FLUSH_CHAIN', body]).signWithKey(this.key); + const buf = msg.toBuffer(); + const threshold = Number(this.settings.flushChainMinTrustedScore) || 800; + let n = 0; + for (const id in this.connections) { + if (!(this._registryScoreForConnectionAddress(id) > threshold)) continue; + this.connections[id]._writeFabric(buf); + n++; + } + return n; + } + + subscribe (_path) { + + } + + _beginFabricHandshake (client) { + const keyClaim = this._buildSessionKeyExchangeClaim(this.identity.id); + // Start handshake + const vector = ['P2P_SESSION_OFFER', JSON.stringify({ + type: 'P2P_SESSION_OFFER', + actor: { + id: this.identity.id, + pubkey: keyClaim.pubkey, + parentPubkey: keyClaim.parentPubkey, + parentXpub: keyClaim.parentXpub, + parentSignature: keyClaim.parentSignature + }, + object: { + challenge: crypto.randomBytes(8).toString('hex'), + } + })]; + + // Create offer message + const P2P_SESSION_OFFER = Message.fromVector(vector).signWithKey(this.key); + const message = P2P_SESSION_OFFER.toBuffer(); + if (this.settings.debug) this.emit('debug', `session_offer ${P2P_SESSION_OFFER} ${message.toString('hex')}`); + + // Send handshake + try { + client.encrypt.write(message); + } catch (exception) { + if (exception && (exception.code === 'EPIPE' || exception.code === 'ECONNRESET')) { + this.emit('warning', `Suppressing transient write error (${exception.code}) during handshake.`); + } else { + this.emit('error', `Cannot write to socket: ${exception}`); + } + } + + return this; + } + + /** + * Open a Fabric connection to the target address and initiate the Fabric Protocol. + * @param {String} target Target address. + */ + _connect (target) { + if (!target || typeof target !== 'string') { + this.emit('error', '[FABRIC:PEER:_connect] target must be a non-empty string'); + return; + } + + if (this.connections[target]) { + this.emit('debug', `[FABRIC:PEER:_connect] Already connected to ${target}; skipping`); + return; + } + + if (this._isPeerBanned(target)) { + this.emit('warning', `[FABRIC:PEER:_connect] Refusing dial to banned peer ${target}`); + return; + } + + this.emit('debug', `[FABRIC:PEER:_connect] Attempting to connect to: ${target}`); + const url = new URL(`tcp://${target}`); + const id = url.username; + + if (!url.port) target += `:${P2P_PORT}`; + + this._outboundDialTargets.add(target); + + const _derived = this.identity.key.derive(FABRIC_KEY_DERIVATION_PATH); + this.emit('debug', `[FABRIC:PEER:_connect] Local derived key (public hex, truncated): ${peerDebugDerivedPublicSummary(_derived)} path=${FABRIC_KEY_DERIVATION_PATH}`); // Store the user's public key if provided if (id) { @@ -767,7 +1752,7 @@

    Source: types/peer.js

    const client = noise({ initiator: true, prologue: Buffer.from(PROLOGUE), - // privateKey: derived.privkey, + // privateKey: _derived.privkey — enable when NOISE static === Fabric derived key. verify: this._verifyNOISE.bind(this) }); @@ -819,17 +1804,21 @@

    Source: types/peer.js

    }); } - _announceAlias (alias, origin = null, socket = null) { - const PACKET_PEER_ALIAS = Message.fromVector(['P2P_PEER_ALIAS', JSON.stringify({ - type: 'P2P_PEER_ALIAS', - object: { - name: alias - } - })]); - - const announcement = PACKET_PEER_ALIAS.toBuffer(); - // this.emit('debug', `Announcing alias: ${announcement.toString('utf8')}`); - this.broadcast(announcement, origin.name); + /** + * Broadcast a personal nickname as first-class {@link P2P_PEER_ALIAS} + * (UTF-8 body = nickname text only). + * @param {string} alias + * @param {{name: (string|undefined)}|null} [origin] Optional origin to exclude from broadcast + * @param {*} [_socket] Unused (API compatibility) + */ + _announceAlias (alias, origin = null, _socket = null) { + const name = String(alias || '').trim().slice(0, P2P_PEER_ALIAS_MAX_CHARS); + if (!name) return; + const packet = Message.fromVector(['P2P_PEER_ALIAS', name]); + if (this.key) packet.signWithKey(this.key); + const announcement = packet.toBuffer(); + const exclude = origin && origin.name ? origin.name : null; + this.broadcast(announcement, exclude); } _destroyFabric (socket, target) { @@ -839,6 +1828,7 @@

    Source: types/peer.js

    delete this.connections[target]; delete this.peers[target]; + if (this._inboundNoiseStaticPubkeyByAddress) delete this._inboundNoiseStaticPubkeyByAddress[target]; if (this._addressToId) delete this._addressToId[target]; this.emit('connections:close', { @@ -859,7 +1849,15 @@

    Source: types/peer.js

    try { this._peersDb = this._peersDb || new Level(location); const raw = await this._peersDb.get('peers').catch(() => null); - const peers = raw ? JSON.parse(raw) : {}; + let peers = {}; + if (raw != null && raw !== '') { + const pr = tryParsePersistedJson(utf8FromPersistedRaw(raw)); + if (pr.ok && pr.value !== null && typeof pr.value === 'object' && !Array.isArray(pr.value)) { + peers = pr.value; + } else if (!pr.ok) { + this.emit('debug', `[FABRIC:PEER] Peer registry JSON invalid or oversized: ${pr.error.message}`); + } + } if (peers && typeof peers === 'object') { // Migrate legacy address-keyed entries to id-keyed (id is the fixed public key) const migrated = {}; @@ -909,6 +1907,137 @@

    Source: types/peer.js

    }, 500); } + /** FLUSH_CHAIN sender hex: {@link Peer#peers}[addr].publicKey if set, else inbound NOISE static (allowlist must match). */ + _flushChainSenderPubkeyHex (connectionAddress) { + if (!connectionAddress) return ''; + const rec = this.peers[connectionAddress]; + if (rec && typeof rec === 'object' && rec.publicKey != null && rec.publicKey !== '') { + const h = normalizePeerPubkeyHex(rec.publicKey); + if (h) return h; + } + const noise = this._inboundNoiseStaticPubkeyByAddress[connectionAddress]; + return noise ? normalizePeerPubkeyHex(noise) : ''; + } + + /** + * Verify Fabric message signature against the message's own on-wire author field. + * Returns normalized x-only signer pubkey hex on success. + * @private + * @param {Message} message + * @returns {string} + */ + _verifiedFabricSignerPubkeyHex (message) { + if (!message || !message.raw || !message.raw.author) return ''; + const xOnly = normalizePeerPubkeyHex(message.raw.author); + if (!/^[0-9a-f]{64}$/.test(xOnly)) return ''; + try { + // Canonical compressed form; verifyWithKey checks x-only author parity-independently. + const signer = new Key({ public: `02${xOnly}` }); + if (!message.verifyWithKey(signer)) return ''; + return xOnly; + } catch { + return ''; + } + } + + /** + * Stable message covered by parent-key signature to bind a session child key claim. + * @private + */ + _sessionKeyProofMessage (peerId, childPubkeyHex, parentPubkeyHex) { + return [ + 'fabric-session-key-proof-v1', + String(peerId || ''), + normalizePeerPubkeyHex(childPubkeyHex), + normalizePeerPubkeyHex(parentPubkeyHex) + ].join(':'); + } + + /** + * Build signed key-exchange claim for early Fabric session messages. + * Child key signs the Fabric message envelope; parent key signs child binding. + * @private + */ + _buildSessionKeyExchangeClaim (peerId = this.identity.id) { + const childPubkey = this.key.public.encodeCompressed('hex'); + const parentPubkey = this.key.public.encodeCompressed('hex'); + const proofMsg = this._sessionKeyProofMessage(peerId, childPubkey, parentPubkey); + const parentSignature = this.key.signSchnorr(proofMsg).toString('hex'); + return { + pubkey: childPubkey, + parentPubkey, + parentXpub: this.key.xpub || null, + parentSignature + }; + } + + /** + * Validate claimed session key exchange against verified wire signer key. + * Missing/invalid signatures are protocol violations and are penalized. + * @private + */ + _validateSessionKeyExchangeClaim (genericMessage, signerPubkeyHex, originName, opts = {}) { + const actor = (genericMessage && genericMessage.actor && typeof genericMessage.actor === 'object') + ? genericMessage.actor + : {}; + const peerId = actor.id; + const claimedChild = normalizePeerPubkeyHex(actor.pubkey || actor.publicKey); + const claimedParent = normalizePeerPubkeyHex(actor.parentPubkey || actor.parentPublicKey); + const parentSigHex = typeof actor.parentSignature === 'string' ? actor.parentSignature.trim() : ''; + const punishOpts = { peeledForward: opts.peeledForward === true }; + + if (!claimedChild || !claimedParent || !parentSigHex) { + this._punishPeerForSessionKeyViolation(originName, 'missing session key signature material', punishOpts); + return false; + } + if (!/^[0-9a-f]{64}$/.test(claimedChild) || !/^[0-9a-f]{64}$/.test(claimedParent)) { + this._punishPeerForSessionKeyViolation(originName, 'invalid session key format', punishOpts); + return false; + } + if (!/^[0-9a-f]{128}$/.test(parentSigHex)) { + this._punishPeerForSessionKeyViolation(originName, 'invalid parent signature format', punishOpts); + return false; + } + if (claimedChild !== normalizePeerPubkeyHex(signerPubkeyHex)) { + this._punishPeerForSessionKeyViolation(originName, 'claimed child key does not match signer', punishOpts); + return false; + } + + const proofMsg = this._sessionKeyProofMessage(peerId, claimedChild, claimedParent); + let ok = false; + try { + const parentKey = new Key({ public: `02${claimedParent}` }); + ok = parentKey.verifySchnorr(proofMsg, Buffer.from(parentSigHex, 'hex')); + } catch { + ok = false; + } + if (!ok) { + this._punishPeerForSessionKeyViolation(originName, 'invalid parent signature', punishOpts); + return false; + } + + return { peerId, childPubkey: claimedChild, parentPubkey: claimedParent, parentXpub: actor.parentXpub || null }; + } + + /** + * Penalize protocol violations around unsigned/unverifiable session key claims. + * @private + * @param {string|null|undefined} originName + * @param {string} reason + * @param {Object} [opts] + * @param {boolean} [opts.peeledForward] + */ + _punishPeerForSessionKeyViolation (originName, reason, opts = {}) { + const peeledForward = opts.peeledForward === true; + const penalty = Number(this.settings.wireTraffic && this.settings.wireTraffic.sessionKeyViolationPenalty) || + PEER_SCORE_SESSION_KEY_VIOLATION_PENALTY; + // Onion peel: do not cut/ban the honest TCP last hop for a laundered bad session frame. + this._applyPeerMisbehavior(peeledForward ? null : originName, `session-key:${reason}`, { + penalty, + disconnect: !peeledForward + }); + } + /** * Upsert a peer into the persistent registry (state.peers) and schedule save to LevelDB. * @param {string} address - Peer address (e.g. host:port). @@ -958,53 +2087,122 @@

    Source: types/peer.js

    /** * Handle a Fabric {@link Message} buffer. * @param {Buffer} buffer + * @param {object|null} [origin] + * @param {object|null} [socket] + * @param {Object} [options] + * @param {number} [options.relayDepth] + * @param {boolean} [options.skipRelayFlood] + * @param {boolean} [options.peeledForward] true when delivered via {@link Peer#_handleP2PForward} peel * @returns {Peer} Instance of the Peer. */ - _handleFabricMessage (buffer, origin = null, socket = null) { - const hash = crypto.createHash('sha256').update(buffer).digest('hex'); - const message = Message.fromBuffer(buffer); + _handleFabricMessage (buffer, origin = null, socket = null, options = null) { + const opts = (options && typeof options === 'object') ? options : {}; + const originName = (origin && origin.name) ? origin.name : null; + const ps = this.settings.peerScore || {}; + // Single delivery context for peel / RELAY-unwrap / outermost-flood rules. + const delivery = meshDeliveryContext(opts, originName); + const suppressTcpOriginPunish = delivery.suppressTcpOriginPunish; + const peeledForward = suppressTcpOriginPunish; // soft-punish alias for nested handlers + const scoreOrigin = delivery.scoreOrigin; + + // Frame-size gate before parse / hash / signature work. + let wire = buffer; + if (!Buffer.isBuffer(wire)) { + if (wire instanceof Uint8Array) wire = Buffer.from(wire); + else return this; + } + const maxBody = Number(this.settings.maxMessageSize); + const bodyCap = Number.isFinite(maxBody) && maxBody > 0 ? maxBody : MAX_MESSAGE_SIZE; + const maxWire = HEADER_SIZE + bodyCap; + if (wire.length > maxWire) { + this.emit('warning', + `[FABRIC:PEER] Dropping oversized frame (${wire.length} > ${maxWire}) from ${originName || 'unknown'}`); + return this; + } + + if (originName && this._isPeerBanned(originName)) { + this.emit('warning', `[FABRIC:PEER] Dropping message from banned peer ${originName}`); + const conn = this.connections && this.connections[originName]; + if (conn && typeof conn.destroy === 'function') conn.destroy(); + return this; + } + + const hash = crypto.createHash('sha256').update(wire).digest('hex'); + const message = Message.fromBuffer(wire); if (this.settings.debug) this.emit('debug', `Got Fabric message: ${message}`); - // Have we seen this message before? + // Have we seen this exact wire envelope before? (silent — no score change) if (this.messages[hash]) { - // this.emit('debug', `Duplicate message: ${hash}`); - return; + return this; } - this._rememberWireHash(hash); - - // Body integrity: `hash` header is double-SHA256(body). `preimage` is single SHA256(body) for - // non-sensitive sends, all-zero for sensitive, or an explicit HTLC secret — preimage is covered - // by the Schnorr signature; do not require preimage === SHA256(body) here (HTLC secrets differ). + // Body integrity: `hash` header is double-SHA256(body). Wire `preimage` is a + // Lightning-style payment secret (all-zero for public frames; non-zero for HTLC / + // Fabric Circuit hops). Do not require preimage === SHA256(body) — that field is + // not a body commitment (see docs/MESSAGE_BODY.md). + // Remember wire hash only after verify so junk frames cannot fill the dedup cache. const bodyBuf = message.raw.data || Buffer.alloc(0); const checksum = Hash256.doubleDigest(bodyBuf); const expectedHash = Buffer.isBuffer(message.raw.hash) ? message.raw.hash.toString('hex') : message.raw.hash; if (checksum !== expectedHash) { - const from = (origin && origin.name) ? origin.name : 'unknown'; const t = message.type || '?'; const hint = this.settings.debug ? ` wire=${String(expectedHash).slice(0, 16)}… computed=${String(checksum).slice(0, 16)}…` : ''; - this.emit('warning', `[FABRIC:PEER] Dropping message (body hash mismatch): from=${from} type=${t}${hint}`); + this.emit('warning', + `[FABRIC:PEER] Dropping message (body hash mismatch): from=${originName || 'unknown'}` + + `${peeledForward ? ' (peeled forward)' : ''} type=${t}${hint}`); + this._applyPeerMisbehavior(scoreOrigin, 'body-hash-mismatch', { + penalty: Number(ps.bodyHashMismatchPenalty) || PEER_SCORE_BODY_HASH_MISMATCH_PENALTY, + disconnect: delivery.hardDisconnect + }); + return this; + } + + // Verify every inbound message against the signed on-wire author field. + const signerPubkeyHex = this._verifiedFabricSignerPubkeyHex(message); + if (!signerPubkeyHex) { + this.emit('warning', + `[FABRIC:PEER] Invalid message signature from ${originName || 'unknown'}` + + `${peeledForward ? ' (peeled forward)' : ''}`); + this._applyPeerMisbehavior(scoreOrigin, 'invalid-signature', { + penalty: Number(ps.invalidSignaturePenalty) || PEER_SCORE_INVALID_SIGNATURE_PENALTY, + disconnect: delivery.hardDisconnect + }); return this; } - // Verify message signature if we have the peer's public key (origin is `{ name }` from sockets) + this._rememberWireHash(hash); + + // Connection peer pin ≠ message author for mesh relays (author may be a prior hop + // or a multisig group). Authenticity is the AMP signature; Noise authenticates the TCP peer. + // Session / control messages still require signer continuity with the pinned peer key. + // Peeled onion inners are authored by the path originator, not the last hop — skip pin. const peerKey = origin && (origin.name != null ? origin.name : origin); const peerRecord = peerKey && this.peers[peerKey]; - if (peerRecord && peerRecord.publicKey) { - const signer = new Key({ public: peerRecord.publicKey }); - if (!message.verifyWithKey(signer)) { - this.emit('error', `Invalid message signature from ${peerKey}`); - return; + const relayAsIs = peeledForward + || isRelayAsIsWireType(message.type) + || isRelayAsIsWireType(message.friendlyType) + || isRelayAsIsGenericCarrier(message); + if (!relayAsIs && peerRecord && peerRecord.publicKey) { + const pinned = normalizePeerPubkeyHex(peerRecord.publicKey); + if (pinned && pinned !== signerPubkeyHex) { + this.emit('warning', `[FABRIC:PEER] Signer mismatch from ${peerKey}: expected pinned peer key`); + this._applyPeerMisbehavior(scoreOrigin || peerKey, 'signer-pin-mismatch', { + penalty: Number(ps.signerPinMismatchPenalty) || PEER_SCORE_SIGNER_PIN_MISMATCH_PENALTY, + disconnect: delivery.hardDisconnect + }); + return this; } } - if (origin && origin.name) { + // Outer P2P_FORWARD / foreign P2P_RELAY already paid inbound credits for the + // TCP hop — do not debit again for peeled / relayed-as-is inners (onion DoS). + if (originName && delivery.allowTcpOriginSideEffects) { const cost = this._wireInboundCreditCost(message.type); - if (!this._wireInboundRateAllowPeer(origin.name, cost)) { + if (!this._wireInboundRateAllowPeer(originName, cost)) { if (this.settings.debug) { - this.emit('debug', `[FABRIC:PEER] Dropped (wire traffic budget): ${origin.name} type=${message.type}`); + this.emit('debug', `[FABRIC:PEER] Dropped (wire traffic budget): ${originName} type=${message.type}`); } return this; } @@ -1018,41 +2216,390 @@

    Source: types/peer.js

    this.emit('debug', `Unhandled message type: ${message.type}`); break; case 'P2P_RELAY': - this.relayFrom(origin.name, message); + if (!origin || origin.name == null) break; + { + // Mesh flood envelope: body = raw inner Message bytes (not directed onion). + // Never re-wrap on forward — bit-identical relayFrom of *this* outer frame so + // wire-hash dedup bounds diameter. Nested RELAY unwrap is depth-capped. + // For IP-hiding source routes use P2P_FORWARD / Peer#sendOnion. + const relayDepth = Number(opts.relayDepth) || 0; + const maxNest = Number(ps.maxRelayNestDepth); + const nestCap = Number.isFinite(maxNest) && maxNest >= 0 ? maxNest : PEER_MAX_RELAY_NEST_DEPTH; + const inner = (message.raw && Buffer.isBuffer(message.raw.data)) + ? message.raw.data + : Buffer.alloc(0); + // Outer AMP author ≠ TCP peer pin ⇒ this hop is only forwarding; do not + // attribute inner pin/integrity/nest failures to them (mesh partition DoS). + const peerRec = this.peers[origin.name]; + const pinnedOuter = peerRec && peerRec.publicKey + ? normalizePeerPubkeyHex(peerRec.publicKey) + : null; + const relayedAsIs = !!(pinnedOuter && signerPubkeyHex && pinnedOuter !== signerPubkeyHex); + const softRelayOrigin = suppressTcpOriginPunish || relayedAsIs; + if (inner.length > 0) { + if (relayDepth >= nestCap) { + this._applyPeerMisbehavior(softRelayOrigin ? null : originName, 'relay-nest-exceeded', { + penalty: Number(ps.relayNestExceededPenalty) || PEER_SCORE_RELAY_NEST_EXCEEDED_PENALTY, + disconnect: !softRelayOrigin + }); + break; + } + this._handleFabricMessage(inner, origin, socket, { + relayDepth: relayDepth + 1, + skipRelayFlood: true, + peeledForward: suppressTcpOriginPunish, + relayedAsIs: softRelayOrigin + }); + // Only the outermost received envelope is mesh-flooded (bit-identical). + if (!opts.skipRelayFlood) { + this.relayFrom(origin.name, message, socket); + } + } + } + break; + case 'P2P_FORWARD': + this._handleP2PForward(message, origin, socket); break; case 'BITCOIN_BLOCK': - case 'BitcoinBlock': - // Chain-tip gossip: relay so sparse meshes learn Bitcoin network tip (hash/preimage/signature as any AMP message). + case 'BitcoinBlock': { + // Chain-tip gossip: relay so sparse meshes learn Bitcoin network tip. + // Logical tip claim stops re-signed copies of the same tip from re-emitting / re-relaying. + const rawBb = messageDataToString(message.data); + const prBb = tryParseWireJsonBody(rawBb); + if (prBb.ok && prBb.value && typeof prBb.value === 'object' && !Array.isArray(prBb.value)) { + const claimBb = this._claimLogicalRegistrationOrPunish( + message.type, prBb.value, signerPubkeyHex, scoreOrigin); + if (claimBb.duplicate) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Ignoring duplicate BitcoinBlock (tip already registered)'); + } + break; + } + } this.emit('bitcoinBlock', { message, origin, socket }); - if (origin && origin.name) this.relayFrom(origin.name, message); + // Peel / RELAY unwrap: outer envelope already floods — never mesh-relay the inner tip. + if (origin && origin.name && delivery.allowMeshRelay) { + this.relayFrom(origin.name, message); + } break; + } case 'P2P_CHAIN_SYNC_REQUEST': case 'ChainSyncRequest': if (!origin || !origin.name) break; + { + const raw = messageDataToString(message.data); + const pr = tryParseWireJsonBody(raw); + if (!pr.ok || pr.value === null || typeof pr.value !== 'object' || Array.isArray(pr.value)) { + this.emit('warning', `[FABRIC:PEER] CHAIN_SYNC_REQUEST parse failed: ${pr.ok ? 'invalid body' : pr.error.message}`); + break; + } + this.emit('chainSyncRequest', { message, origin, socket, object: pr.value }); + } + break; + case 'P2P_FLUSH_CHAIN': + case 'FlushChain': { + if (!origin || !origin.name) break; + const senderHex = signerPubkeyHex || this._flushChainSenderPubkeyHex(origin.name); + if (!senderHex) { + this.emit('warning', `[FABRIC:PEER] FLUSH_CHAIN ignored: no verified peer key for ${origin.name}`); + break; + } + const authList = this.settings.flushChainAuthorizedPubkeys; + if (!Array.isArray(authList) || authList.length === 0) { + this.emit('warning', `[FABRIC:PEER] FLUSH_CHAIN ignored: flushChainAuthorizedPubkeys is empty (configure allowed signer pubkeys)`); + break; + } + const allowed = new Set(); + for (const e of authList) { + const h = normalizePeerPubkeyHex(e); + if (h) allowed.add(h); + } + if (!allowed.has(senderHex)) { + this.emit('warning', `[FABRIC:PEER] FLUSH_CHAIN ignored: sender pubkey not in flushChainAuthorizedPubkeys`); + break; + } + const threshold = Number(this.settings.flushChainMinTrustedScore) || 800; + const score = this._registryScoreForFlushChainSender(origin.name, senderHex); + if (!(score > threshold)) { + if (this.settings.debug) { + this.emit('debug', `[FABRIC:PEER] FLUSH_CHAIN ignored: sender score ${score} not > ${threshold}`); + } + break; + } + const rawFc = messageDataToString(message.data); + const prFc = tryParseWireJsonBody(rawFc); + if (!prFc.ok) { + this.emit('warning', `[FABRIC:PEER] FLUSH_CHAIN JSON parse failed: ${prFc.error.message}`); + break; + } + const object = prFc.value; + if (object === null || typeof object !== 'object' || Array.isArray(object)) { + this.emit('warning', '[FABRIC:PEER] FLUSH_CHAIN body must be a JSON object'); + break; + } + const snap = object.snapshotBlockHash != null ? String(object.snapshotBlockHash).trim() : ''; + if (!/^[0-9a-fA-F]{64}$/.test(snap)) { + this.emit('warning', '[FABRIC:PEER] FLUSH_CHAIN missing or invalid snapshotBlockHash (expect 64 hex)'); + break; + } + object.snapshotBlockHash = snap.toLowerCase(); + const claimFc = this._claimLogicalRegistrationOrPunish( + message.type, object, senderHex, scoreOrigin); + if (claimFc.duplicate) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Ignoring duplicate FLUSH_CHAIN (snapshot already registered)'); + } + break; + } + this.emit('flushChain', { message, origin, socket, object }); + this.relayFromTrustedPeers(origin.name, message, threshold); + break; + } + case 'P2P_CHAT_MESSAGE': { + // Body = raw UTF-8 chat text only (no JSON). Author is AMP header / signature. + const text = messageDataToString(message.data); + if (!text || !String(text).trim()) { + this.emit('warning', '[FABRIC:PEER] P2P_CHAT_MESSAGE empty body'); + break; + } + if (String(text).length > P2P_CHAT_MAX_CHARS) { + this.emit('warning', `[FABRIC:PEER] P2P_CHAT_MESSAGE exceeds ${P2P_CHAT_MAX_CHARS} chars`); + break; + } + // Reject legacy chat JSON envelopes ({ type, actor, object }); opaque UTF-8 + // text is allowed (including app JSON that is not a chat envelope). + const trimmed = String(text).trim(); + if (trimmed.startsWith('{')) { + try { + const parsed = JSON.parse(trimmed); + if (parsed && typeof parsed === 'object' && !Array.isArray(parsed) && + (parsed.type === 'P2P_CHAT_MESSAGE' || (parsed.actor && parsed.object))) { + this.emit('warning', '[FABRIC:PEER] P2P_CHAT_MESSAGE body must be UTF-8 text, not a JSON chat envelope'); + break; + } + } catch (_) { /* not JSON — treat as text */ } + } + const chatSigner = this._verifiedFabricSignerPubkeyHex(message); + this.emit('chat', { text: String(text), type: 'P2P_CHAT_MESSAGE' }, { + origin, + signer: chatSigner || null, + wireMessage: message, + peeledForward: opts.peeledForward === true + }); + // Onion peel / RELAY unwrap: deliver locally only. Mesh relay under the TCP + // last hop would burn that neighbor's chat budget for an originator's frame. + if (origin && origin.name && message && delivery.allowMeshRelay) { + if (this._chatRateLimitAllow(origin.name)) { + this.relayFrom(origin.name, message); + } else { + this.emit('warning', + `[FABRIC:PEER] Chat relay rate-limited for ${origin.name}`); + } + } + break; + } + case 'P2P_PEER_ALIAS': { + // Body = raw UTF-8 nickname only (no JSON). + const alias = messageDataToString(message.data); + if (!alias || !String(alias).trim()) { + this.emit('warning', '[FABRIC:PEER] P2P_PEER_ALIAS empty body'); + break; + } + const name = String(alias).trim().slice(0, P2P_PEER_ALIAS_MAX_CHARS); + if (String(alias).trim().startsWith('{') || String(alias).trim().startsWith('[')) { + this.emit('warning', '[FABRIC:PEER] P2P_PEER_ALIAS body must be UTF-8 text, not JSON'); + break; + } + // Peel / relay-as-is: observe only — never overlay alias onto the TCP last hop + // or bind the attacker's registry address to that hop's socket. + const aliasLocalOnly = !delivery.allowTcpOriginSideEffects; + const aliasSignerHex = signerPubkeyHex || this._verifiedFabricSignerPubkeyHex(message); + const claimAlias = this._claimLogicalRegistrationOrPunish('P2P_PEER_ALIAS', { + alias: name, + signer: aliasSignerHex || '' + }, aliasSignerHex, aliasLocalOnly ? null : originName); + if (claimAlias.duplicate) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Ignoring duplicate P2P_PEER_ALIAS (same signer + nickname)'); + } + break; + } + if (!aliasLocalOnly && origin && origin.name && this.connections[origin.name]) { + this.connections[origin.name]._alias = name; + } + const aliasPeerId = aliasSignerHex + || (!aliasLocalOnly && origin && this._addressToId && this._addressToId[origin.name]) + || (!aliasLocalOnly && origin && origin.name) + || null; + if (aliasPeerId) { + this._upsertPeerRegistry(aliasPeerId, { + id: aliasPeerId, + address: (!aliasLocalOnly && origin && origin.name) ? origin.name : undefined, + alias: name, + publicKey: aliasSignerHex || undefined + }); + } + this.emit('peerAlias', { + alias: name, + signer: aliasSignerHex || null, + origin, + wireMessage: message, + peeledForward: aliasLocalOnly + }); + if (delivery.allowMeshRelay && origin && origin.name && message) { + this.relayFrom(origin.name, message); + } + break; + } + case 'P2P_INVENTORY_REQUEST': + case 'P2P_INVENTORY_RESPONSE': + case 'P2P_PEER_GOSSIP': + case 'P2P_PEERING_OFFER': + case 'P2P_PING': + case 'P2P_PONG': + case 'P2P_STATE_ANNOUNCE': + case 'P2P_PEER_ANNOUNCE': + case 'P2P_SESSION_OFFER': + case 'P2P_SESSION_OPEN': + case 'P2P_DOCUMENT_PUBLISH': + case 'P2P_FILE_SEND': + case 'CONTRACT_PUBLISH': + case 'CONTRACT_MESSAGE': + { + const rawTyped = messageDataToString(message.data); + const prTyped = tryParseWireJsonBody(rawTyped); + if (!prTyped.ok || prTyped.value === null || typeof prTyped.value !== 'object' || Array.isArray(prTyped.value)) { + this.emit('warning', `[FABRIC:PEER] ${message.type} parse failed: ${prTyped.ok ? 'invalid body' : prTyped.error.message}`); + break; + } + const wireToInner = { + P2P_INVENTORY_REQUEST: 'INVENTORY_REQUEST', + P2P_INVENTORY_RESPONSE: 'INVENTORY_RESPONSE' + }; + const innerType = wireToInner[message.type] || message.type; + const parsed = prTyped.value; + let genericBody; + if (message.type === 'CONTRACT_PUBLISH' || message.type === 'CONTRACT_MESSAGE') { + // Contract frames dispatch by WIRE type (namespace routing). The app + // payload — including any inner `type` like MissionBroadcast — is kept + // intact under `object` so handlers see the full body. + genericBody = { type: message.type, object: parsed }; + } else { + genericBody = (parsed && typeof parsed === 'object' && (parsed.actor || parsed.object || parsed.type)) + ? Object.assign({}, parsed, { type: parsed.type || innerType }) + : { type: innerType, object: parsed }; + } + this._handleGenericMessage(genericBody, origin, socket, message, opts); + break; + } + case 'DOCUMENT_PUBLISH': + case 'DocumentPublish': + try { + const rawDp = messageDataToString(message.data); + const prDp = tryParseWireJsonBody(rawDp); + if (!prDp.ok) { + this.emit('warning', `[FABRIC:PEER] DOCUMENT_PUBLISH parse failed: ${prDp.error.message}`); + break; + } + const parsed = prDp.value; + if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { + this.emit('warning', '[FABRIC:PEER] DOCUMENT_PUBLISH body must be a JSON object'); + break; + } + const docId = parsed.id; + if (!docId) break; + const claimDp = this._claimLogicalRegistrationOrPunish( + message.type, parsed, signerPubkeyHex, scoreOrigin); + if (claimDp.duplicate) { + if (this.settings.debug) { + this.emit('debug', + `[FABRIC:PEER] Ignoring duplicate DOCUMENT_PUBLISH for ${docId} (logical key claimed)`); + } + break; + } + const purchaseHash = purchaseContentHashHex(docId, parsed); + const payload = { + message, + origin, + socket, + documentId: docId, + purchaseContentHashHex: purchaseHash, + parsed, + source: 'canonical' + }; + this.emit('documentPublish', payload); + this.emit('DocumentPublish', payload); + } catch (exception) { + this.emit('warning', `[FABRIC:PEER] DOCUMENT_PUBLISH parse failed: ${exception.message}`); + } + break; + case 'DOCUMENT_REQUEST': + case 'DocumentRequest': try { - const raw = message.data != null - ? (typeof message.data === 'string' ? message.data : String(message.data)) - : '{}'; - const object = JSON.parse(raw || '{}'); - this.emit('chainSyncRequest', { message, origin, socket, object }); - } catch (parseErr) { - this.emit('chainSyncRequest', { message, origin, socket, object: {} }); + this._handleDocumentRequestWire(message, origin, socket, opts); + } catch (exception) { + this.emit('warning', `[FABRIC:PEER] DOCUMENT_REQUEST failed: ${exception.message}`); + } + break; + case 'CONTRACT_PROPOSAL': + case 'ContractProposal': { + const rawCp = messageDataToString(message.data); + const prCp = tryParseWireJsonBody(rawCp); + if (!prCp.ok || prCp.value === null || typeof prCp.value !== 'object' || Array.isArray(prCp.value)) { + this.emit('warning', `[FABRIC:PEER] CONTRACT_PROPOSAL parse failed: ${prCp.ok ? 'invalid body' : prCp.error.message}`); + break; + } + const payload = prCp.value; + const verdict = verifyContractProposalPayload(payload); + if (!verdict || verdict.ok !== true) { + this.emit('warning', `[FABRIC:PEER] CONTRACT_PROPOSAL rejected: ${(verdict && verdict.error) || 'verification failed'}`); + break; + } + const claimCp = this._claimLogicalRegistrationOrPunish( + message.type, payload, signerPubkeyHex, scoreOrigin); + if (claimCp.duplicate) { + if (this.settings.debug) { + this.emit('debug', + '[FABRIC:PEER] Ignoring duplicate CONTRACT_PROPOSAL (merkle root already registered)'); + } + break; + } + this.emit('contract:proposal', { + contract: payload.contractId != null ? String(payload.contractId) : null, + payload, + message, + origin, + socket, + signer: signerPubkeyHex || null + }); + // Forward bit-identical on direct receive only — peel / RELAY unwrap must not + // second-flood under the TCP last hop (outer envelope already paid). + if (origin && origin.name && delivery.allowMeshRelay) { + this.relayFrom(origin.name, message); } break; + } case 'GENERIC_MESSAGE': case 'GenericMessage': case 'P2P_BASE_MESSAGE': - case 'PeerMessage': case 'CHAT_MESSAGE': case 'ChatMessage': - // this.emit('debug', `message ${message}`); - // this.emit('debug', `message data: ${message.data}`); - // Parse JSON body - try { - const content = JSON.parse(message.data); - this._handleGenericMessage(content, origin, socket); - } catch (exception) { - this.emit('error', `Broken content body: ${exception}`); + // GENERIC_MESSAGE (15103) is the transitional Hub/browser carrier; P2P_BASE_MESSAGE + // remains the generic peer payload / legacy decode fallback. Both carry UTF-8 JSON + // bodies dispatched via _handleGenericMessage (bounded — see functions/wireJson). + { + const rawGm = messageDataToString(message.data); + const prGm = tryParseWireJsonBody(rawGm); + if (!prGm.ok) { + this.emit('warning', `[FABRIC:PEER] Generic message parse failed: ${prGm.error.message}`); + break; + } + const bodyGm = prGm.value; + if (bodyGm === null || typeof bodyGm !== 'object' || Array.isArray(bodyGm)) { + this.emit('warning', '[FABRIC:PEER] Generic message body must be a JSON object'); + break; + } + this._handleGenericMessage(bodyGm, origin, socket, message, opts); } break; @@ -1097,12 +2644,14 @@

    Source: types/peer.js

    case 'NodeAnnouncement': case 'LIGHTNING_CHANNEL_UPDATE': case 'ChannelUpdate': - try { - const content = JSON.parse(message.data); - this.emit('lightning', { type: message.type, content, origin }); - } catch (e) { - // If not JSON, emit raw buffer payload for lightning listeners - this.emit('lightning', { type: message.type, raw: message.data, origin }); + { + const rawLn = messageDataToString(message.data); + const prLn = tryParseWireJsonBody(rawLn); + if (prLn.ok) { + this.emit('lightning', { type: message.type, content: prLn.value, origin }); + } else { + this.emit('lightning', { type: message.type, raw: message.data, origin }); + } } break; } @@ -1112,98 +2661,302 @@

    Source: types/peer.js

    return this; } - _handleGenericMessage (message, origin = null, socket = null) { - if (this.settings.debug) this.emit('debug', `Generic message:\n\tFrom: ${JSON.stringify(origin)}\n\tType: ${message.type}\n\tBody:\n\`\`\`\n${JSON.stringify(message.object, null, ' ')}\n\`\`\``); + /** + * Originate a locally signed {@code P2P_RELAY} envelope around already-signed inner AMP bytes. + * Used only when *this* agent starts a flood (e.g. inventory without a prior wire frame). + * Inbound {@code P2P_RELAY} must never call this — forward the original outer bit-identical. + */ + _relayWirePayload (originName, wirePayload, socket = null) { + if (!originName) return false; + const body = Buffer.isBuffer(wirePayload) + ? wirePayload + : Buffer.from(wirePayload || []); + if (!body.length) return false; + const relayMessage = Message.fromVector(['P2P_RELAY', body]); + relayMessage.signWithKey(this.key); + this.relayFrom(originName, relayMessage, socket); + return true; + } + + /** + * Relay inventory / generic payloads. When {@code wireMessage} is present, forward it + * bit-identical (no hop re-sign). Otherwise the local agent originates a new signed frame + * and may wrap it in {@code P2P_RELAY} for mesh delivery. + */ + _relayGenericPayload (originName, payload, socket = null, wireMessage = null) { + if (!originName) return false; + if (wireMessage && typeof wireMessage.toBuffer === 'function') { + this.relayFrom(originName, wireMessage, socket); + return true; + } + const innerType = (payload && payload.type === 'INVENTORY_REQUEST') + ? 'P2P_INVENTORY_REQUEST' + : (payload && payload.type === 'INVENTORY_RESPONSE') + ? 'P2P_INVENTORY_RESPONSE' + : 'P2P_BASE_MESSAGE'; + const innerBody = (innerType === 'P2P_BASE_MESSAGE') + ? JSON.stringify(payload) + : JSON.stringify((payload && payload.object) ? payload.object : {}); + const innerBuffer = Message + .fromVector([innerType, innerBody]) + .signWithKey(this.key) + .toBuffer(); + return this._relayWirePayload(originName, innerBuffer, socket); + } + + _handleSessionOfferGenericMessage (message, origin, socket, signerPubkeyHex, opts = {}) { + // Peel / relay-as-is: TCP origin is not the AMP author — never rebind + // peers/_addressToId or reply SESSION_OPEN on that socket (partition DoS). + if (opts.peeledForward === true || opts.relayedAsIs === true) { + this.emit('warning', + '[FABRIC:PEER] Ignoring P2P_SESSION_OFFER delivered via peel/relay-as-is (no identity rebind)'); + return this; + } + const peerId = message.actor.id; + const connAddress = origin.name; + if (this.settings.debug) this.emit('debug', `Handling session offer: ${JSON.stringify(message.object)}`); + if (this.settings.debug) this.emit('debug', `Session offer origin: ${JSON.stringify(origin)}`); + { + const sessionClaim = this._validateSessionKeyExchangeClaim( + message, signerPubkeyHex, connAddress, { peeledForward: opts.peeledForward === true }); + if (!sessionClaim) return this; + } + + // If we've already bound this Fabric id to a key, reject key-mismatched offers. + const existingPeer = (this._state.peers && this._state.peers[peerId]) || null; + if (existingPeer && existingPeer.publicKey) { + const known = normalizePeerPubkeyHex(existingPeer.publicKey); + if (known && known !== signerPubkeyHex) { + this.emit('warning', `[FABRIC:PEER] Rejecting session offer for ${peerId}: signer key mismatch`); + return this; + } + } + + // Same peer reconnecting from new port? Close old connection and replace with new. + // Do not tear down our outbound dial to the peer's listen address: concurrent inbound + + // outbound to the same Fabric id is normal (e.g. ring + star mesh); dropping the stable + // `host:listenPort` socket breaks address-keyed sends on the satellite. + const addressToId = this._addressToId || {}; + for (const [addr, mappedId] of Object.entries(addressToId)) { + if (mappedId !== peerId || addr === connAddress) continue; + if (this._outboundDialTargets && this._outboundDialTargets.has(addr)) continue; + const oldSocket = this.connections[addr]; + if (oldSocket) { + if (oldSocket._keepalive) clearInterval(oldSocket._keepalive); + delete this.connections[addr]; + delete this.peers[addr]; + delete this._addressToId[addr]; + if (typeof oldSocket.destroy === 'function') oldSocket.destroy(); + } + break; + } + + this.peers[connAddress] = new Actor({ + id: peerId, + name: connAddress, + address: connAddress, + connections: [ connAddress ], + publicKey: signerPubkeyHex + }); + + this._upsertPeerRegistry(connAddress, { + id: peerId, + address: connAddress, + publicKey: signerPubkeyHex, + lastSeen: new Date().toISOString() + }); + this._addressToId[connAddress] = peerId; + + // Emit peer event + this.emit('peer', this.peers[connAddress]); + + // Send session open event + const keyClaim = this._buildSessionKeyExchangeClaim(this.identity.id); + const vector = ['P2P_SESSION_OPEN', JSON.stringify({ + type: 'P2P_SESSION_OPEN', + actor: { + id: this.identity.id, + pubkey: keyClaim.pubkey, + parentPubkey: keyClaim.parentPubkey, + parentXpub: keyClaim.parentXpub, + parentSignature: keyClaim.parentSignature + }, + object: { + initiator: message.actor.id, + counterparty: this.identity.id, + solution: message.object.challenge + } + })]; + + const PACKET_SESSION_START = Message.fromVector(vector).signWithKey(this.key); + const reply = PACKET_SESSION_START.toBuffer(); + if (this.settings.debug) this.emit('debug', `session_start ${PACKET_SESSION_START} ${reply.toString('hex')}`); + this.connections[connAddress]._writeFabric(reply, socket); + if (this.settings.announceDocumentsOnPeerConnect) { + this._announceLocalDocumentsToPeer(connAddress); + } + return this; + } + + _handleSessionOpenGenericMessage (message, origin, signerPubkeyHex, opts = {}) { + if (opts.peeledForward === true || opts.relayedAsIs === true) { + this.emit('warning', + '[FABRIC:PEER] Ignoring P2P_SESSION_OPEN delivered via peel/relay-as-is (no identity rebind)'); + return this; + } + if (this.settings.debug) this.emit('debug', `Handling session open: ${JSON.stringify(message.object)}`); + const openPeerId = message.object.counterparty; + { + const sessionClaim = this._validateSessionKeyExchangeClaim( + message, signerPubkeyHex, origin && origin.name, { peeledForward: opts.peeledForward === true }); + if (!sessionClaim) return this; + } + const existingOpenPeer = (this._state.peers && this._state.peers[openPeerId]) || null; + if (existingOpenPeer && existingOpenPeer.publicKey) { + const knownOpen = normalizePeerPubkeyHex(existingOpenPeer.publicKey); + if (knownOpen && knownOpen !== signerPubkeyHex) { + this.emit('warning', `[FABRIC:PEER] Rejecting session open for ${openPeerId}: signer key mismatch`); + return this; + } + } + this.peers[origin.name] = { id: openPeerId, name: origin.name, address: origin, publicKey: signerPubkeyHex }; + this._upsertPeerRegistry(origin.name, { + id: openPeerId, + address: origin.name, + publicKey: signerPubkeyHex, + lastSeen: new Date().toISOString() + }); + this._addressToId[origin.name] = openPeerId; + // Don't emit peer event here - it's already emitted in P2P_SESSION_OFFER + return this; + } + + _handleGenericMessage (message, origin = null, socket = null, wireMessage = null, options = null) { + const handleOpts = (options && typeof options === 'object') ? options : {}; + const delivery = meshDeliveryContext(handleOpts, origin && origin.name); + const peeledForward = delivery.suppressTcpOriginPunish; + // Peel / relay-as-is: never attribute logical-register soft/hijack penalties to the TCP hop. + const punishOrigin = delivery.scoreOrigin; + const msg = normalizeFabricDocumentOfferEnvelopeForHandlers(message); + if (this.settings.debug) this.emit('debug', `Generic message:\n\tFrom: ${JSON.stringify(origin)}\n\tType: ${msg.type}\n\tBody:\n\`\`\`\n${JSON.stringify(msg.object, null, ' ')}\n\`\`\``); + + const signerPubkeyHex = wireMessage + ? this._verifiedFabricSignerPubkeyHex(wireMessage) + : normalizePeerPubkeyHex(msg && msg.actor && (msg.actor.publicKey || msg.actor.pubkey)); // Lookup the appropriate Actor for the message's origin const actor = new Actor(origin); - switch (message.type) { + switch (msg.type) { default: - this.emit('debug', `Unhandled Generic Message: ${message.type} ${JSON.stringify(message, null, ' ')}`); + this.emit('debug', `Unhandled Generic Message: ${msg.type} ${JSON.stringify(msg, null, ' ')}`); break; case 'INVENTORY_REQUEST': // Upstream Inventory request (typically for documents). Emit an 'inventory' // event so higher-level services (e.g. hub) can respond appropriately. - this.emit('inventory', { message, origin, socket }); + // JSON `type` may be legacy `INVENTORY_REQUEST` or Fabric alias `FABRIC_DOCUMENT_OFFER` (see `functions/publishedDocumentEnvelope.js`). + this.emit('inventory', { message: msg, origin, socket }); + // Peel / foreign RELAY: observe only — do not reply inventory to the TCP last hop + // or second-flood the request under that hop. + if (!delivery.allowTcpOriginSideEffects) break; + if (this.settings.serveLocalDocumentInventory) { + const served = this._respondInventoryFromLocalDocuments(msg, origin); + const req = msg.object || {}; + if (delivery.allowMeshRelay && this.settings.relayInventoryRequest && + !served && req.offerBtc === true) { + // Relay path matches L1 `offerBtc` requests; Hub-driven `kind:documents` relays use TTL in app code. + this._relayGenericPayload(origin && origin.name, msg, socket, wireMessage); + } + } break; case 'INVENTORY_RESPONSE': // Document inventory reply (may include per-item L1 HTLC offers). - this.emit('inventoryResponse', { message, origin, socket }); + // JSON `type` may be legacy `INVENTORY_RESPONSE` or Fabric alias `FABRIC_DOCUMENT_OFFER_RESPONSE`. + // Prefer AMP signer over untrusted item.sellerPubkey when funding HTLCs. + this.emit('inventoryResponse', { + message: msg, + origin, + socket, + signerPubkeyHex: signerPubkeyHex || null + }); + if (delivery.allowMeshRelay && this.settings.relayInventoryResponse) { + this._relayGenericPayload(origin && origin.name, msg, socket, wireMessage); + } break; - case 'P2P_SESSION_OFFER': - const peerId = message.actor.id; - const connAddress = origin.name; - if (this.settings.debug) this.emit('debug', `Handling session offer: ${JSON.stringify(message.object)}`); - if (this.settings.debug) this.emit('debug', `Session offer origin: ${JSON.stringify(origin)}`); - - // Same peer reconnecting from new port? Close old connection and replace with new. - // Do not tear down our outbound dial to the peer's listen address: concurrent inbound + - // outbound to the same Fabric id is normal (e.g. ring + star mesh); dropping the stable - // `host:listenPort` socket breaks address-keyed sends on the satellite. - const addressToId = this._addressToId || {}; - for (const [addr, mappedId] of Object.entries(addressToId)) { - if (mappedId !== peerId || addr === connAddress) continue; - if (this._outboundDialTargets && this._outboundDialTargets.has(addr)) continue; - const oldSocket = this.connections[addr]; - if (oldSocket) { - if (oldSocket._keepalive) clearInterval(oldSocket._keepalive); - delete this.connections[addr]; - delete this.peers[addr]; - delete this._addressToId[addr]; - if (typeof oldSocket.destroy === 'function') oldSocket.destroy(); - } + case 'DocumentContentKeyReveal': { + const reveal = (msg && msg.object) ? msg.object : msg; + // Fail closed before claim / reverse-relay: public paymentHashHex alone must + // not burn the logical slot or launder junk toward the buyer. + if (!isWellFormedKeyReveal(reveal)) { + this.emit('warning', + '[FABRIC:PEER] DocumentContentKeyReveal rejected: key must be SHA256 preimage of paymentHashHex'); break; } - - this.peers[connAddress] = new Actor({ - id: peerId, - name: connAddress, - address: connAddress, - connections: [ connAddress ] - }); - - this._upsertPeerRegistry(connAddress, { id: peerId, address: connAddress, lastSeen: new Date().toISOString() }); - this._addressToId[connAddress] = peerId; - - // Emit peer event - this.emit('peer', this.peers[connAddress]); - - // Send session open event - const vector = ['P2P_SESSION_OPEN', JSON.stringify({ - type: 'P2P_SESSION_OPEN', - object: { - initiator: message.actor.id, - counterparty: this.identity.id, - solution: message.object.challenge + // Reverse-relay only well-formed reveals (private DocumentRequest return path). + if (reveal && reveal.routeId && this._documentRelayRoutes[reveal.routeId]) { + const route = this._documentRelayRoutes[reveal.routeId]; + const prev = route.prevHopAddress; + if (prev && this.connections[prev] && this.connections[prev]._writeFabric && + !(origin && origin.name === prev)) { + const fwdBody = { + type: KEY_REVEAL_TYPE, + object: Object.assign({}, reveal, { routeId: route.prevRouteId || reveal.routeId }) + }; + const fwdMsg = Message.fromVector(['GenericMessage', JSON.stringify(fwdBody)]); + fwdMsg.signWithKey(this.key); + this.connections[prev]._writeFabric(fwdMsg.toBuffer()); + this.emit('documentRelayReturn', { + routeId: reveal.routeId, + prevHopAddress: prev, + documentId: reveal.documentId, + kind: 'keyReveal', + origin + }); + break; } - })]; - - const PACKET_SESSION_START = Message.fromVector(vector).signWithKey(this.key); - const reply = PACKET_SESSION_START.toBuffer(); - if (this.settings.debug) this.emit('debug', `session_start ${PACKET_SESSION_START} ${reply.toString('hex')}`); - this.connections[connAddress]._writeFabric(reply, socket); + } + // Claim only after a successful open so failed opens cannot occupy the slot. + const logicKey = this._logicalRegistrationKey('DocumentContentKeyReveal', reveal); + if (logicKey && this._logicalRegisterOnce.has(logicKey)) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Ignoring duplicate DocumentContentKeyReveal'); + } + break; + } + const opened = this._handleDocumentContentKeyReveal(reveal, origin); + if (opened && opened.ok) { + this._claimLogicalRegistrationOrPunish( + 'DocumentContentKeyReveal', reveal, signerPubkeyHex, punishOrigin); + } + break; + } + case 'P2P_SESSION_OFFER': + this._handleSessionOfferGenericMessage(msg, origin, socket, signerPubkeyHex, handleOpts); break; case 'P2P_SESSION_OPEN': - if (this.settings.debug) this.emit('debug', `Handling session open: ${JSON.stringify(message.object)}`); - const openPeerId = message.object.counterparty; - this.peers[origin.name] = { id: openPeerId, name: origin.name, address: origin }; - this._upsertPeerRegistry(origin.name, { id: openPeerId, address: origin.name, lastSeen: new Date().toISOString() }); - this._addressToId[origin.name] = openPeerId; - // Don't emit peer event here - it's already emitted in P2P_SESSION_OFFER + this._handleSessionOpenGenericMessage(msg, origin, signerPubkeyHex, handleOpts); break; - case 'P2P_CHAT_MESSAGE': - this.emit('chat', message); - const relay = Message.fromVector(['ChatMessage', JSON.stringify(message)]); - relay.signWithKey(this.key); - // this.emit('debug', `Relayed chat message: ${JSON.stringify(relay.toGenericMessage())}`); - this.relayFrom(origin.name, relay); + case 'P2P_CHAT_MESSAGE': { + // Legacy GenericMessage / P2P_BASE_MESSAGE carrier with JSON body — not accepted. + // First-class opcode path handles UTF-8 text in _handleFabricMessage. + this.emit('warning', '[FABRIC:PEER] P2P_CHAT_MESSAGE via GenericMessage/JSON is unsupported; use first-class UTF-8 body'); break; - case 'P2P_STATE_ANNOUNCE': - const state = new Actor(message.object.state); - this.emit('debug', `state_announce <Generic>${JSON.stringify(message.object || '')} ${state.toGenericMessage()}`); + } + case 'P2P_STATE_ANNOUNCE': { + const stateObj = msg.object || message.object; + const claimState = this._claimLogicalRegistrationOrPunish( + 'P2P_STATE_ANNOUNCE', stateObj, signerPubkeyHex, punishOrigin); + if (claimState.duplicate) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Ignoring duplicate P2P_STATE_ANNOUNCE'); + } + break; + } + const state = new Actor(stateObj && stateObj.state); + this.emit('debug', `state_announce <Generic>${JSON.stringify(stateObj || '')} ${state.toGenericMessage()}`); break; - case P2P_PEER_GOSSIP: { + } + case 'P2P_PEER_GOSSIP': { if (!origin || !origin.name) break; const g = this.settings.gossip || {}; const maxHops = g.maxHops != null ? g.maxHops : GOSSIP_MAX_HOPS; @@ -1214,17 +2967,21 @@

    Source: types/peer.js

    if (!Number.isFinite(hop) || hop < 0) hop = maxHops; hop = Math.min(hop, maxHops); if (hop <= 0) break; + // Outermost-only: peel / RELAY unwrap observes locally — no last-hop budget + // burn and no second inner mesh flood (outer envelope already paid). + if (!delivery.allowMeshRelay) { + this.emit('peeringGossip', { message, origin, peeledForward: true }); + this._gossipRememberPayload(payloadKey); + break; + } if (!this._gossipRateLimitAllow(origin.name)) break; this.emit('peeringGossip', { message, origin }); this._gossipRememberPayload(payloadKey); - const relayBody = Object.assign({}, message, { - object: Object.assign({}, obj, { gossipHop: hop - 1 }) - }); - const gossipRelay = Message.fromVector(['GENERIC', JSON.stringify(relayBody)]).signWithKey(this.key); - this.relayFrom(origin.name, gossipRelay); + // Forward original frame only — wire-hash dedup stops loops; never hop-re-sign. + if (wireMessage) this.relayFrom(origin.name, wireMessage); break; } - case P2P_PEERING_OFFER: { + case 'P2P_PEERING_OFFER': { if (!origin || !origin.name) break; const p = this.settings.peering || {}; const maxHops = p.maxHops != null ? p.maxHops : PEERING_OFFER_MAX_HOPS; @@ -1235,6 +2992,13 @@

    Source: types/peer.js

    if (!Number.isFinite(hop) || hop < 0) hop = maxHops; hop = Math.min(hop, maxHops); if (hop <= 0) break; + // Outermost-only: peel / RELAY unwrap observes locally — no last-hop budget, + // dial enqueue, or second inner mesh flood. + if (!delivery.allowMeshRelay) { + this.emit('peeringOffer', { message, origin, peeledForward: true }); + this._peeringRememberPayload(payloadKey); + break; + } if (!this._peeringRateLimitAllow(origin.name)) break; this.emit('peeringOffer', { message, origin }); this._peeringRememberPayload(payloadKey); @@ -1246,31 +3010,33 @@

    Source: types/peer.js

    this._enqueuePeeringCandidate(obj.host, obj.port); } } - const relayBody = Object.assign({}, message, { - object: Object.assign({}, obj, { peeringHop: hop - 1 }) - }); - const offerRelay = Message.fromVector(['GENERIC', JSON.stringify(relayBody)]).signWithKey(this.key); - this.relayFrom(origin.name, offerRelay); + if (wireMessage) this.relayFrom(origin.name, wireMessage); break; } case 'P2P_PING': + // Peel / foreign RELAY: do not write PONG to the TCP last hop. + if (!delivery.allowTcpOriginSideEffects) break; const now = (new Date()).toISOString(); - const P2P_PONG = Message.fromVector(['GENERIC', JSON.stringify({ - actor: { - id: this.identity.id - }, - created: now, - type: 'P2P_PONG', - object: { - created: now + const P2P_PONG = Message.fromVector(['P2P_PONG', JSON.stringify({ + created: now + })]).signWithKey(this.key); + if (this.connections[origin.name] && this.connections[origin.name]._writeFabric) { + this.connections[origin.name]._writeFabric(P2P_PONG.toBuffer()); + } + break; + case 'P2P_PONG': { + // Peel / foreign RELAY: ignore score credit under the last hop. + if (!delivery.allowTcpOriginSideEffects) break; + const conn = origin && origin.name ? this.connections[origin.name] : null; + const outstanding = conn && (conn._fabricPingOutstanding | 0); + if (!outstanding) { + if (this.settings.debug) { + this.emit('debug', `[FABRIC:PEER] Ignoring P2P_PONG from ${origin && origin.name}: no outbound ping pending`); } - })]); + break; + } + conn._fabricPingOutstanding = 0; - this.connections[origin.name]._writeFabric(P2P_PONG.toBuffer()); - break; - case 'P2P_PONG': - // Update the peer's score for succesfully responding to a ping - // TODO: ensure no pong is handled when a ping was not previously sent const instance = this.state.actors[actor.id] ? this.state.actors[actor.id] : {}; const newScore = (instance.score || 0) + 1; @@ -1278,169 +3044,1380 @@

    Source: types/peer.js

    { op: 'replace', path: '/score', value: newScore } ]); - this._state.content.actors[actor.id] = this.actors[actor.id].state; - this.commit(); + this._state.content.actors[actor.id] = this.actors[actor.id].state; + this.commit(); + + const registry = this._state.peers || {}; + const pongPeerId = (this._addressToId && this._addressToId[origin.name]) || origin.name; + const regEntry = registry[pongPeerId] || registry[origin.name]; + this._upsertPeerRegistry(pongPeerId, { id: pongPeerId, address: origin.name, score: (regEntry && regEntry.score != null ? regEntry.score : 0) + 1, lastSeen: new Date().toISOString() }); + + if (this.settings.debug) this.emit('debug', `Received pong: ${JSON.stringify(message, null, ' ')}`); + this.emit('state', this.state); + + break; + } + case 'P2P_PEER_ALIAS': + // Legacy GenericMessage/JSON carrier — not accepted. First-class UTF-8 path above. + this.emit('warning', '[FABRIC:PEER] P2P_PEER_ALIAS via GenericMessage/JSON is unsupported; use first-class UTF-8 body'); + break; + case 'P2P_PEER_ANNOUNCE': { + const announceObj = msg.object || message.object; + const claimAnn = this._claimLogicalRegistrationOrPunish( + 'P2P_PEER_ANNOUNCE', announceObj, signerPubkeyHex, punishOrigin); + if (claimAnn.duplicate) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Ignoring duplicate P2P_PEER_ANNOUNCE'); + } + break; + } + this.emit('debug', `peer_announce <Generic>${JSON.stringify(announceObj || '')}`); + this.emit('peerAnnounce', { + message: msg, + origin, + peeledForward: !punishOrigin + }); + // Peel / relay-as-is: observe only — do not enqueue dial targets under last hop. + if (!punishOrigin) break; + const host = announceObj && announceObj.host; + const port = announceObj && announceObj.port; + if (host != null && port != null) { + this._enqueuePeeringCandidate(host, port); + } + break; + } + case 'P2P_DOCUMENT_PUBLISH': { + const priceObj = msg.object || message.object; + const claimPrice = this._claimLogicalRegistrationOrPunish( + 'P2P_DOCUMENT_PUBLISH', priceObj, signerPubkeyHex, punishOrigin); + if (claimPrice.duplicate) { + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Ignoring duplicate P2P_DOCUMENT_PUBLISH pricing frame'); + } + break; + } + this.emit('documentPublish', { + message, + origin, + socket, + documentId: priceObj && priceObj.hash, + rateSats: priceObj && priceObj.rate, + contentHash: priceObj && priceObj.contentHash, + source: 'pricing' + }); + break; + } + case 'P2P_FILE_SEND': { + // Reverse private-relay path: forward toward buyer before local ingest. + // Typed wire is wrapped as `{ type, object }` before Generic dispatch — use + // msg.object (not message.data, which only exists on raw Message). + try { + let fileObj = null; + if (msg && msg.object && typeof msg.object === 'object' && !Array.isArray(msg.object)) { + fileObj = msg.object; + } else if (message && message.data != null) { + const rawFile = messageDataToString(message.data); + const prFile = tryParseWireJsonBody(rawFile); + if (prFile.ok && prFile.value && typeof prFile.value === 'object') fileObj = prFile.value; + } else if (wireMessage && wireMessage.data != null) { + const rawFile = messageDataToString(wireMessage.data); + const prFile = tryParseWireJsonBody(rawFile); + if (prFile.ok && prFile.value && typeof prFile.value === 'object') fileObj = prFile.value; + } + if (fileObj && this._maybeReverseRelayFileSend(fileObj, origin)) { + break; + } + } catch (_) { /* fall through to ingest */ } + const ingest = this._ingestP2pFileSend(msg || message, origin); + this.emit('file', Object.assign({ message, origin }, ingest)); + if (ingest && ingest.status === 'complete' && ingest.buffer) { + this.emit('documentReceived', { + documentId: ingest.documentId, + buffer: ingest.buffer, + merkleRootHex: ingest.merkleRootHex, + verified: !!ingest.verified, + legacy: !!ingest.legacy, + origin + }); + } else if (ingest && ingest.status === 'awaiting_key') { + this.emit('documentAwaitingKey', { + documentId: ingest.documentId, + merkleRootHex: ingest.merkleRootHex, + origin + }); + } else if (ingest && ingest.status === 'reject') { + this.emit('documentBlobRejected', { + documentId: ingest.documentId, + error: ingest.error, + blobIndex: ingest.blobIndex, + origin + }); + } + break; + } + case 'CONTRACT_PUBLISH': { + this.emit('debug', `Handling peer contract publish: ${JSON.stringify(msg.object)}`); + if (!msg.object || typeof msg.object !== 'object') break; + const publishedId = (new Actor(msg.object)).id; + // Authz before logical claim: a front-run signed by a non-party must not + // burn the content-addressed registration slot for the real publisher. + if (!this._contractPublishSignerAuthorized(msg.object, signerPubkeyHex)) { + this.emit('warning', + `[FABRIC:PEER] CONTRACT_PUBLISH rejected for ${publishedId}: ` + + 'wire signer is not listed in parties/validators/owners/members/authorities'); + break; + } + const claimPub = this._claimLogicalRegistrationOrPunish( + 'CONTRACT_PUBLISH', msg.object, signerPubkeyHex, punishOrigin); + if (claimPub.duplicate) { + if (this.settings.debug) { + this.emit('debug', + `[FABRIC:PEER] Ignoring duplicate CONTRACT_PUBLISH for ${publishedId} (no allow-list / emit / relay)`); + } + break; + } + if (!this._registerContract(msg.object, signerPubkeyHex)) break; + this.emit('contract:publish', { + contract: publishedId, + object: msg.object, + origin, + signer: signerPubkeyHex || null + }); + // Peel / RELAY unwrap: deliver locally only (outer envelope already floods). + if (delivery.allowMeshRelay && origin && origin.name && wireMessage) { + this.relayFrom(origin.name, wireMessage); + } + break; + } + case 'CONTRACT_MESSAGE': { + if (this.settings.debug) this.emit('debug', `Handling contract message: ${JSON.stringify(msg.object)}`); + const contractId = msg.object && msg.object.contract ? String(msg.object.contract) : null; + if (!contractId) { + this.emit('warning', '[FABRIC:PEER] CONTRACT_MESSAGE missing `contract` namespace id; dropped'); + break; + } + const registered = !!(this._state.content.contracts && this._state.content.contracts[contractId]); + // State ops only apply to locally registered contract namespaces — + // unknown ids must not crash the peer (message may still be app-consumed). + if (registered && Array.isArray(msg.object.ops) && msg.object.ops.length) { + if (!this._signerMayPatchContract(contractId, signerPubkeyHex)) { + this.emit('warning', + `[FABRIC:PEER] CONTRACT_MESSAGE ops rejected for ${contractId}: signer not in contract allow-list` + + `${peeledForward ? ' (peeled forward)' : ''}`); + const opsPs = this.settings.peerScore || {}; + // Onion peel: do not cut the honest last-hop TCP link for an unauthorized inner. + this._applyPeerMisbehavior(punishOrigin, 'contract-ops-forbidden', { + penalty: Number(opsPs.contractOpsForbiddenPenalty) || PEER_SCORE_CONTRACT_OPS_FORBIDDEN_PENALTY, + disconnect: delivery.hardDisconnect + }); + // Do not emit or relay — forbidden ops must not be laundered through the mesh. + break; + } + try { + manager.applyPatch(this._state.content.contracts[contractId], msg.object.ops); + this.commit(); + } catch (exception) { + this.emit('warning', `[FABRIC:PEER] CONTRACT_MESSAGE patch failed for ${contractId}: ${exception.message}`); + break; + } + } + this.emit('contract:message', { + contract: contractId, + registered, + object: msg.object, + origin, + signer: signerPubkeyHex || null, + // Bit-identical AMP frame for journal / audit attach (apps ignore if unused). + wireMessage: wireMessage || null, + messageId: wireMessage && wireMessage.id ? wireMessage.id : null, + messageHex: wireMessage && typeof wireMessage.toBuffer === 'function' + ? wireMessage.toBuffer().toString('hex') + : null + }); + // Same outermost-only flood rule as CONTRACT_PUBLISH / chat / gossip. + if (delivery.allowMeshRelay && origin && origin.name && wireMessage) { + this.relayFrom(origin.name, wireMessage); + } + break; + } + } + } + + _NOISESocketHandler (socket) { + const target = `${socket.remoteAddress}:${socket.remotePort}`; + const url = `tcp://${target}`; + + if (this._isPeerBanned(target)) { + this.emit('warning', `[FABRIC:PEER] Refusing inbound from banned address ${target}`); + if (typeof socket.destroy === 'function') socket.destroy(); + return; + } + + // Store a unique actor for this inbound connection + this._registerActor({ name: target }); + + const _derived = this.identity.key.derive(FABRIC_KEY_DERIVATION_PATH); + if (this.settings.debug) { + this.emit('debug', 'NOISE inbound: session key derived for handshake (private key not logged)'); + } + + // Create NOISE handler + const handler = noise({ + prologue: Buffer.from(PROLOGUE), + // privateKey: _derived.private.toString('hex'), + verify: this._verifyNOISE.bind(this) + }); + + // Handle low-level socket errors for inbound connections + socket.on('error', (error) => { + if (this.settings.debug) this.emit('debug', `--- debug error from _NOISESocketHandler() ---`); + if (error && (error.code === 'EPIPE' || error.code === 'ECONNRESET')) { + this.emit('warning', `Suppressing transient inbound socket error (${error.code}) from _NOISESocketHandler().`); + } else { + this.emit('error', `Inbound socket error: ${error}`); + } + }); + + // Set up NOISE event handlers + handler.encrypt.on('handshake', (_lk, localPk, remotePk) => { + if (this.settings.debug) { + // Transport diagnostics only — never log private key material from the handshake. + this.emit('debug', `Peer transport handshake using local public key: ${localPk.toString('hex')}`); + this.emit('debug', `Peer transport handshake with remote public key: ${remotePk.toString('hex')}`); + } + if (remotePk != null) { + const pkHex = normalizePeerPubkeyHex(Buffer.isBuffer(remotePk) ? Buffer.from(remotePk) : remotePk); + if (pkHex) { + this._inboundNoiseStaticPubkeyByAddress[target] = pkHex; + if (this._isPeerBanned(target, pkHex)) { + this.emit('warning', `[FABRIC:PEER] Closing inbound: banned Noise static ${pkHex.slice(0, 16)}…`); + if (typeof socket.destroy === 'function') socket.destroy(); + } + } + } + }); + handler.encrypt.on('error', (error) => { + if (error && (error.code === 'EPIPE' || error.code === 'ECONNRESET')) { + this.emit('warning', `Suppressing transient NOISE encrypt error (${error.code}).`); + } else { + this.emit('error', `NOISE encrypt error: ${error}`); + } + }); + + handler.encrypt.on('end', (data) => { + if (this.settings.debug) this.emit('debug', `Peer encrypt end: ${data}`); + // socket.destroy(); + delete this.connections[target]; + if (this.peers[target] && typeof this.peers[target] === 'object') { + this.peers[target].status = 'disconnected'; + } + }); + + handler.decrypt.on('error', (error) => { + if (error && (error.code === 'EPIPE' || error.code === 'ECONNRESET')) { + this.emit('warning', `Suppressing transient NOISE decrypt error (${error.code}).`); + } else { + this.emit('error', `NOISE decrypt error: ${error}`); + } + }); + + handler.decrypt.on('close', (data) => { + if (this.settings.debug) this.emit('debug', `Peer decrypt close: ${data}`); + }); + + handler.decrypt.on('end', (data) => { + if (this.settings.debug) { + this.emit('debug', `Peer decrypt end: (${target}) ${data}`); + this.emit('debug', `Connections: ${Object.keys(this.connections)}`); + } + socket._destroyFabric(); + }); + + handler.decrypt.on('data', (data) => { + this._handleFabricMessage(data, { name: target }); + }); + + socket._destroyFabric = () => { + this._destroyFabric(socket, target); + }; + + socket._writeFabric = (msg) => { + this._writeFabric(msg, handler); + }; + + // Store socket in collection + this.connections[target] = socket; + this._startFabricPingKeepalive(socket, handler.encrypt); + + // Begin NOISE stream + handler.encrypt.pipe(socket).pipe(handler.decrypt); + + this.emit('connections:open', { + id: target, + url: url + }); + } + + /** + * Build hub-compatible document metadata for {@link purchaseContentHashHex}. + * @param {String} documentId + * @param {String} content UTF-8 body + * @returns {Object} Parsed document record (whitelisted fields) + */ + _buildDocumentParsedForPublish (documentId, content) { + const buf = Buffer.from(content === null || content === undefined ? '' : String(content), 'utf8'); + return { + id: documentId, + name: (documentId && String(documentId).split('/').pop()) || 'document', + mime: 'text/plain', + revision: 1, + contentBase64: buf.toString('base64'), + size: buf.length, + sha256: crypto.createHash('sha256').update(buf).digest('hex') + }; + } + + /** + * Items for Hub-style `kind: 'documents'` inventory (Fabric UI / `@fabric/hub` merge expects `object.kind`). + * @param {Object} [req] request object subset + * @returns {object[]} + */ + _collectDocumentCatalogInventoryItems (_req) { + const docs = this._state.content.documents; + if (!docs || typeof docs !== 'object') return []; + const collections = this._state.content.collections && typeof this._state.content.collections.documents === 'object' + ? this._state.content.collections.documents + : {}; + const rates = this._state.content.documentRates || {}; + /** @type {object[]} */ + const items = []; + for (const docId of Object.keys(docs)) { + const body = docs[docId]; + const parsed = this._buildDocumentParsedForPublish(docId, body); + const row = collections[docId]; + const purchaseFromCollection = row && Number(row.purchasePriceSats) > 0 ? Math.round(Number(row.purchasePriceSats)) : null; + const rateSats = Object.prototype.hasOwnProperty.call(rates, docId) ? rates[docId] : 0; + const purchasePriceSats = purchaseFromCollection != null ? purchaseFromCollection : (Number(rateSats) > 0 ? Math.round(Number(rateSats)) : undefined); + const published = row ? !!row.published : true; + items.push({ + id: parsed.id, + sha256: parsed.sha256 || parsed.id, + name: parsed.name, + mime: parsed.mime || 'application/octet-stream', + size: parsed.size, + created: parsed.created || new Date().toISOString(), + published, + ...(purchasePriceSats != null && purchasePriceSats > 0 ? { purchasePriceSats } : {}), + ...(row && row.bitcoinHeight != null && Number.isFinite(Number(row.bitcoinHeight)) + ? { bitcoinHeight: Math.round(Number(row.bitcoinHeight)) } + : {}), + ...(row && row.bitcoinBlockHash ? { bitcoinBlockHash: String(row.bitcoinBlockHash) } : {}), + ...(row && row.bitcoinTxid ? { bitcoinTxid: String(row.bitcoinTxid) } : {}) + }); + } + return items; + } + + /** + * Write {@link INVENTORY_RESPONSE} (`P2P_INVENTORY_RESPONSE`) compatible with `@fabric/hub` Bridge merging + * (body includes `kind: 'documents'` so the browser can merge `object.items`). + * @param {string} originName connection key {@link Peer#connections} + * @param {object[]} items + * @param {Object} [opts] + * @param {boolean} [opts.allowEmpty] + * @returns {boolean} + */ + _sendLocalInventoryDocumentsWireResponse (originName, items, opts = {}) { + const allowEmpty = !!(opts && opts.allowEmpty); + if (!originName || !Array.isArray(items)) return false; + if (!allowEmpty && items.length === 0) return false; + const conn = this.connections[originName]; + if (!conn || !conn._writeFabric) return false; + const obj = { + kind: 'documents', + items, + created: Date.now() + }; + const m = Message.fromVector(['P2P_INVENTORY_RESPONSE', JSON.stringify(obj)]); + m.signWithKey(this.key); + conn._writeFabric(m.toBuffer()); + return true; + } + + /** + * Reply to `INVENTORY_REQUEST` with `INVENTORY_RESPONSE` built from local documents and rates. + * @param {{type: string, object: (Object|undefined)}} message Generic body from {@link Peer#_handleGenericMessage} + * @param {{ name: string }} origin + * @returns {boolean} true if an `INVENTORY_RESPONSE` was written to the requester + */ + _respondInventoryFromLocalDocuments (message, origin) { + if (!origin || !origin.name) return false; + const req = message.object || {}; + const docs = this._state.content.documents; + if (!docs || typeof docs !== 'object') return false; + + if (req.offerBtc === true) { + const rates = this._state.content.documentRates || {}; + const maxSats = req.maxSats; + const chunkBytes = Math.max(1, Number(this.settings.inventoryBlobChunkBytes) || DEFAULT_CHUNK_BYTES); + /** @type {object[]} */ + const items = []; + for (const docId of Object.keys(docs)) { + const body = docs[docId]; + const parsed = this._buildDocumentParsedForPublish(docId, body); + const rateSats = Object.prototype.hasOwnProperty.call(rates, docId) ? rates[docId] : 0; + if (maxSats != null && Number.isFinite(maxSats) && rateSats > maxSats) continue; + const sealedMeta = this._getDocumentSealedMeta(docId); + const bodyBuf = Buffer.from(String(body == null ? '' : body), 'utf8'); + const advertiseBuf = sealedMeta ? sealedMeta.ciphertext : bodyBuf; + const resolved = resolveDocumentContentHashHex({ + documentId: docId, + parsed, + sealedMeta: sealedMeta || undefined, + sealed: !!sealedMeta + }); + const contentHash = resolved.contentHashHex; + const item = { + id: docId, + rateSats, + contentHash, + contentHashHex: contentHash, + binding: resolved.binding, + network: 'bitcoin', + size: sealedMeta ? sealedMeta.ciphertext.length : bodyBuf.length + }; + // Always advertise a DocumentBlobIndex (one blob when small; many when large). + const advertised = sealedMeta + ? advertiseSealedDocument({ + ciphertext: sealedMeta.ciphertext, + paymentHashHex: sealedMeta.paymentHashHex, + encryption: sealedMeta.encryption, + plaintextSha256: sealedMeta.plaintextSha256 + }, { + documentId: docId, + chunkBytes, + rateSats + }) + : advertiseDocumentBlobs(advertiseBuf, { + documentId: docId, + chunkBytes, + rateSats, + includePaymentHashes: true + }); + if (sealedMeta) { + item.sealed = true; + item.encryption = sealedMeta.encryption; + item.plaintextSha256 = sealedMeta.plaintextSha256; + } + item.merkleRootHex = advertised.merkleRootHex; + item.documentBlobIndex = advertised.index; + item.contentSha256 = advertised.contentSha256; + item.chunkBytes = advertised.chunkBytes; + item.blobTotal = advertised.blobs.length; + // Keep inventory AMP-sized: inline leaf list only when the index fits. + if (advertised.index.leavesInline !== false) { + item.blobs = advertised.itemBlobs; + } else { + item.blobs = []; + item.leavesInline = false; + } + if (this.settings.attachInventoryHtlc && rateSats > 0) { + try { + if (sealedMeta) { + // One HTLC bound to SHA256(content key) — claim reveals K. + const htlc = this._attachHtlcOfferToItem(item, req, sealedMeta.paymentHashHex, rateSats); + if (htlc) item.htlc = htlc; + } else if (Array.isArray(item.blobs) && item.blobs.length) { + for (const b of item.blobs) { + const payHash = b.contentHash || contentHash; + const amt = Number(b.rateSats != null ? b.rateSats : rateSats) || rateSats; + const htlc = this._attachHtlcOfferToItem( + { id: `${docId}#${b.index}` }, + req, + payHash, + amt + ); + if (htlc) b.htlc = htlc; + } + if (item.blobs.length === 1 && item.blobs[0].htlc) { + item.htlc = item.blobs[0].htlc; + } + } else { + const htlc = this._attachHtlcOfferToItem(item, req, contentHash, rateSats); + if (htlc) item.htlc = htlc; + } + } catch (e) { + this.emit('warning', `[FABRIC:PEER] inventory HTLC attach failed for ${docId}: ${e.message}`); + } + } + items.push(item); + } + if (!items.length) return false; + return this._sendLocalInventoryDocumentsWireResponse(origin.name, items); + } + + const reqKind = String(req.kind || '').trim().toLowerCase(); + if (reqKind !== 'documents') return false; + const items = this._collectDocumentCatalogInventoryItems({}); + return this._sendLocalInventoryDocumentsWireResponse(origin.name, items, { allowEmpty: true }); + } + + /** + * @param {string} documentId + * @returns {object|null} + */ + _getDocumentSealedMeta (documentId) { + const sealed = this._state.content.documentSealed || {}; + const row = sealed[documentId]; + if (!row || !row.ciphertextBase64) return null; + return { + ciphertext: Buffer.from(String(row.ciphertextBase64), 'base64'), + paymentHashHex: row.paymentHashHex, + encryption: row.encryption, + plaintextSha256: row.plaintextSha256, + keyHex: (this._state.content.documentContentKeys || {})[documentId] || row.keyHex || null + }; + } + + /** + * Send a locally stored document as indexed, wire-sized `P2P_FILE_SEND` blobs. + * Priced sealed docs send **ciphertext** (safe without the content key). + * @param {string} documentId + * @param {string} peerAddress connection key in {@link Peer#connections} + * @param {Object} [opts] + * @param {number} [opts.blobIndex] + * @param {boolean} [opts.revealKey] + * @param {string} [opts.settlementId] + * @param {string} [opts.routeId] + * @returns {boolean} true if at least one frame was written + */ + _sendP2pFileSendToPeer (documentId, peerAddress, opts = {}) { + const docs = this._state.content.documents; + if (!docs || !Object.prototype.hasOwnProperty.call(docs, documentId)) return false; + const conn = peerAddress && this.connections[peerAddress]; + if (!conn || !conn._writeFabric) return false; + const chunkBytes = Math.max(1, Number(this.settings.inventoryBlobChunkBytes) || DEFAULT_CHUNK_BYTES); + const sealedMeta = this._getDocumentSealedMeta(documentId); + const bodyBuf = sealedMeta + ? sealedMeta.ciphertext + : Buffer.from(String(docs[documentId] == null ? '' : docs[documentId]), 'utf8'); + const split = splitBlobs(bodyBuf, chunkBytes, documentId); + const only = opts.blobIndex != null ? Math.round(Number(opts.blobIndex)) : null; + let wrote = 0; + for (const b of split.blobs) { + if (only != null && b.index !== only) continue; + const fileObj = { + name: documentId, + body: b.bytes.toString('base64'), + blobIndex: b.index, + blobTotal: b.total, + blobHashHex: b.blobHashHex, + merkleRootHex: split.merkleRootHex, + contentSha256: split.contentSha256, + chunkBytes: split.chunkBytes + }; + if (opts.routeId) fileObj.routeId = String(opts.routeId); + if (sealedMeta) { + fileObj.sealed = true; + fileObj.paymentHashHex = sealedMeta.paymentHashHex; + fileObj.iv = sealedMeta.encryption && sealedMeta.encryption.iv; + fileObj.scheme = sealedMeta.encryption && sealedMeta.encryption.scheme; + fileObj.plaintextSha256 = sealedMeta.plaintextSha256; + } + const reply = Message.fromVector(['P2P_FILE_SEND', JSON.stringify(fileObj)]); + reply.signWithKey(this.key); + conn._writeFabric(reply.toBuffer()); + wrote += 1; + } + if (wrote > 0 && opts.revealKey && sealedMeta && sealedMeta.keyHex) { + this._sendDocumentContentKeyReveal(documentId, peerAddress, { + settlementId: opts.settlementId || null, + routeId: opts.routeId || null + }); + } + return wrote > 0; + } + + /** + * Reveal the AES content key to a peer (only after payment-hash match). + * @param {string} documentId + * @param {string} peerAddress + * @param {Object} [opts] + * @param {string} [opts.settlementId] + * @returns {boolean} + */ + _sendDocumentContentKeyReveal (documentId, peerAddress, opts = {}) { + const sealedMeta = this._getDocumentSealedMeta(documentId); + if (!sealedMeta || !sealedMeta.keyHex) return false; + const conn = peerAddress && this.connections[peerAddress]; + if (!conn || !conn._writeFabric) return false; + let body; + try { + body = buildKeyRevealMessage({ + documentId, + keyHex: sealedMeta.keyHex, + paymentHashHex: sealedMeta.paymentHashHex, + iv: sealedMeta.encryption && sealedMeta.encryption.iv, + scheme: sealedMeta.encryption && sealedMeta.encryption.scheme, + plaintextSha256: sealedMeta.plaintextSha256, + settlementId: opts.settlementId + }); + } catch (err) { + this.emit('warning', `[FABRIC:PEER] key reveal build failed: ${err.message}`); + return false; + } + if (opts.routeId) body.object.routeId = String(opts.routeId); + const msg = Message.fromVector(['GenericMessage', JSON.stringify(body)]); + msg.signWithKey(this.key); + conn._writeFabric(msg.toBuffer()); + this.emit('documentContentKeyRevealed', { + documentId, + peerAddress, + paymentHashHex: sealedMeta.paymentHashHex + }); + return true; + } + + /** + * Verify / accumulate an inbound `P2P_FILE_SEND` against the DocumentBlobIndex rules. + * Sealed frames store ciphertext until {@link KEY_REVEAL_TYPE}. + * @param {Message|Object} message + * @param {Object} origin + * @param {string} [origin.name] + * @returns {Object} + */ + _ingestP2pFileSend (message, origin) { + // Typed wire → Generic dispatch uses `{ type, object }`; raw Message has `.data`. + let obj = null; + if (message && message.object && typeof message.object === 'object' && !Array.isArray(message.object)) { + obj = message.object; + } else if (message && message.data != null) { + const raw = messageDataToString(message.data); + const pr = tryParseWireJsonBody(raw); + if (pr.ok && pr.value && typeof pr.value === 'object') obj = pr.value; + } else if (message && typeof message === 'object' && (message.name || message.body)) { + obj = message; + } + if (!obj) { + return { status: 'reject', error: 'invalid P2P_FILE_SEND JSON' }; + } + const frame = parseFileSendObject(obj); + const result = this.blobTransfers.ingest(frame); + if (result.status === 'complete' && result.buffer && result.documentId) { + const sealed = !!(obj.sealed || (frame && !frame.legacy && obj.paymentHashHex && obj.iv)); + if (sealed) { + this.pendingSealedDeliveries[result.documentId] = { + ciphertext: result.buffer, + merkleRootHex: result.merkleRootHex, + paymentHashHex: obj.paymentHashHex || null, + iv: obj.iv || null, + scheme: obj.scheme || null, + plaintextSha256: obj.plaintextSha256 || null, + origin: origin || null, + updated: Date.now() + }; + this._capPendingSealedDeliveries(); + this.emit('documentCiphertextReceived', { + documentId: result.documentId, + buffer: result.buffer, + merkleRootHex: result.merkleRootHex, + paymentHashHex: obj.paymentHashHex || null, + verified: true, + origin + }); + return Object.assign({ origin, sealed: true, awaitingKey: true }, result, { + // Do not treat ciphertext as the final document. + status: 'awaiting_key', + buffer: null + }); + } + if (!this._state.content.documents) this._state.content.documents = {}; + if (!Object.prototype.hasOwnProperty.call(this._state.content.documents, result.documentId)) { + this._state.content.documents[result.documentId] = result.buffer.toString('utf8'); + } + } + return Object.assign({ origin }, result); + } + + /** + * Apply a content-key reveal to a pending sealed delivery. + * @param {Object} reveal + * @param {Object} [origin] + * @param {string} [origin.name] + * @returns {Object} + */ + _handleDocumentContentKeyReveal (reveal, origin = null) { + const documentId = String((reveal && reveal.documentId) || '').trim(); + const pending = documentId ? this.pendingSealedDeliveries[documentId] : null; + if (!pending) { + return { ok: false, error: 'no pending sealed delivery', documentId }; + } + const opened = openSealedDelivery({ + keyHex: reveal.keyHex, + paymentHashHex: reveal.paymentHashHex || pending.paymentHashHex, + iv: reveal.iv || pending.iv, + plaintextSha256: reveal.plaintextSha256 || pending.plaintextSha256 + }, pending.ciphertext); + if (!opened.ok) { + this.emit('documentBlobRejected', { + documentId, + error: opened.error || 'open failed', + origin + }); + return { ok: false, error: opened.error, documentId }; + } + delete this.pendingSealedDeliveries[documentId]; + if (!this._state.content.documents) this._state.content.documents = {}; + this._state.content.documents[documentId] = opened.plaintext.toString('utf8'); + const ev = { + documentId, + buffer: opened.plaintext, + merkleRootHex: pending.merkleRootHex, + verified: true, + sealed: true, + opened: true, + origin: origin || pending.origin + }; + this.emit('documentReceived', ev); + this.emit('documentOpened', ev); + return { ok: true, documentId, buffer: opened.plaintext }; + } + + /** + * Open a pending sealed delivery using an HTLC claim preimage (on-chain witness). + * @param {string} documentId + * @param {string} preimageHex + * @returns {{ok: boolean, error: (string|undefined), buffer: (Buffer|undefined)}} + */ + openSealedDeliveryWithPreimage (documentId, preimageHex) { + const id = String(documentId || '').trim(); + const pending = id ? this.pendingSealedDeliveries[id] : null; + if (!pending) return { ok: false, error: 'no pending sealed delivery', documentId: id }; + const opened = openWithClaimPreimage({ + ciphertext: pending.ciphertext, + preimageHex, + paymentHashHex: pending.paymentHashHex, + iv: pending.iv, + plaintextSha256: pending.plaintextSha256 + }); + if (!opened.ok) { + this.emit('documentBlobRejected', { + documentId: id, + error: opened.error || 'open with preimage failed' + }); + return { ok: false, error: opened.error, documentId: id }; + } + delete this.pendingSealedDeliveries[id]; + if (!this._state.content.documents) this._state.content.documents = {}; + this._state.content.documents[id] = opened.plaintext.toString('utf8'); + const ev = { + documentId: id, + buffer: opened.plaintext, + merkleRootHex: pending.merkleRootHex, + verified: true, + sealed: true, + opened: true, + fromClaim: true, + origin: pending.origin + }; + this.emit('documentReceived', ev); + this.emit('documentOpened', ev); + return { ok: true, documentId: id, buffer: opened.plaintext }; + } + + /** + * Public helper: push document bytes to a peer (same wire path as {@link Peer#_handleDocumentRequestWire} fulfillment). + * @param {string} documentId + * @param {string} peerAddress + * @param {Object} [opts] + * @param {number} [opts.blobIndex] + * @param {boolean} [opts.revealKey] + * @returns {boolean} + */ + sendDocumentFileToPeer (documentId, peerAddress, opts = {}) { + const resolved = this._resolveToAddress(peerAddress) || peerAddress; + return this._sendP2pFileSendToPeer(documentId, resolved, opts); + } + + /** + * Ask a connected peer for their document catalog (`kind: 'documents'`) or L1 offers (`offerBtc`). + * @param {string} peerAddress connection key, Fabric id, or host:port + * @param {Object} [opts] + * @param {boolean} [opts.offerBtc=false] + * @param {string} [opts.kind='documents'] + * @param {number} [opts.maxSats] + * @returns {boolean} + */ + requestPeerInventory (peerAddress, opts = {}) { + const resolved = this._resolveToAddress(peerAddress) || peerAddress; + const conn = resolved && this.connections[resolved]; + if (!conn || !conn._writeFabric) return false; + const object = {}; + if (opts.offerBtc === true) { + object.offerBtc = true; + if (opts.maxSats != null && Number.isFinite(Number(opts.maxSats))) { + object.maxSats = Number(opts.maxSats); + } + if (opts.buyerRefundPublicKey) { + object.buyerRefundPublicKey = String(opts.buyerRefundPublicKey); + } + } else { + object.kind = String(opts.kind || 'documents'); + object.created = Date.now(); + } + const m = Message.fromVector(['P2P_INVENTORY_REQUEST', JSON.stringify(object)]); + m.signWithKey(this.key); + conn._writeFabric(m.toBuffer()); + return true; + } + + /** + * Send a signed `DocumentRequest` to one peer (or broadcast when peerAddress is omitted). + * @param {string} documentId + * @param {string} [peerAddress] + * @param {object} [opts] + * @param {number} [opts.maxSats] + * @param {number} [opts.relayHop] + * @param {number} [opts.blobIndex] + * @param {number} [opts.blobTotal] + * @param {string} [opts.contentHashHex] + * @returns {boolean} + */ + requestDocument (documentId, peerAddress = null, opts = {}) { + if (!documentId) return false; + + const body = { document: documentId }; + if (opts.maxSats != null && Number.isFinite(Number(opts.maxSats))) { + body.maxSats = Math.round(Number(opts.maxSats)); + body.relayHop = opts.relayHop != null + ? Math.round(Number(opts.relayHop)) + : Math.round(Number(this.settings.documentRelayMaxHops) || 4); + body.routeId = crypto.randomBytes(8).toString('hex'); + } + if (opts.blobIndex != null) body.blobIndex = Number(opts.blobIndex); + if (opts.blobTotal != null) body.blobTotal = Number(opts.blobTotal); + { + const hash = contentHashHexFromObject(opts) || contentHashHexFromObject({ contentHashHex: opts.contentHashHex }); + if (hash) body.contentHashHex = hash; + } + + const msg = Message.fromVector(['DocumentRequest', JSON.stringify(body)]); + msg.signWithKey(this.key); + const buf = msg.toBuffer(); + if (peerAddress) { + const resolved = this._resolveToAddress(peerAddress) || peerAddress; + const conn = resolved && this.connections[resolved]; + if (!conn || !conn._writeFabric) return false; + conn._writeFabric(buf); + return true; + } + this.broadcast(buf); + return true; + } + + /** + * @param {object} item + * @param {object} req inventory request object + * @param {string} contentHashHex + * @param {number} amountSats + * @returns {object|null} + */ + _attachHtlcOfferToItem (item, req, contentHashHex, amountSats) { + const buyerHex = req.buyerRefundPublicKey || req.buyerRefundPubkey; + if (!buyerHex || !/^[0-9a-fA-F]{66}$/.test(String(buyerHex))) return null; + let sellerCompressed = null; + try { + if (this.key && this.key.public && typeof this.key.public.encodeCompressed === 'function') { + sellerCompressed = Buffer.from(this.key.public.encodeCompressed('hex'), 'hex'); + } + } catch (_) { /* ignore */ } + if (!sellerCompressed) return null; + const lock = Number(this.settings.inventoryHtlcLocktimeHeight); + if (!Number.isFinite(lock) || lock < 1) return null; + const paymentHash32 = Buffer.from(String(contentHashHex), 'hex'); + if (paymentHash32.length !== 32) return null; + const built = inventoryHtlc.buildInventoryHtlcP2tr({ + networkName: this.settings.network || 'regtest', + sellerPubkeyCompressed: sellerCompressed, + buyerRefundPubkeyCompressed: Buffer.from(String(buyerHex), 'hex'), + paymentHash32, + refundLocktimeHeight: lock + }); + const hints = inventoryHtlc.buildHtlcFundingHints({ + paymentAddress: built.address, + amountSats: Math.round(Number(amountSats) || 0), + label: String(item.id || '').slice(0, 32) + }); + const sellerHex = sellerCompressed.toString('hex'); + return { + paymentAddress: built.address, + paymentHashHex: built.paymentHashHex, + claimScriptHex: built.claimScript.toString('hex'), + refundScriptHex: built.refundScript.toString('hex'), + refundLocktimeHeight: lock, + amountSats: Math.round(Number(amountSats) || 0), + bitcoinUri: hints.bitcoinUri, + sellerPublicKeyHex: sellerHex, + sellerPubkey: sellerHex + }; + } + + /** + * @returns {object[]} pending DOCUMENT_REQUEST rows (consent mode) + */ + listPendingDocumentRequests () { + return Object.keys(this.pendingDocumentRequests).map((key) => { + const row = this.pendingDocumentRequests[key]; + return Object.assign({ key }, row); + }); + } + + /** + * Approve a pending DOCUMENT_REQUEST and send `P2P_FILE_SEND`. + * @param {string} requestKey pending key, or document id when unique + * @returns {{ok: boolean, error: (string|undefined), documentId: (string|undefined), peerAddress: (string|undefined)}} + */ + approveDocumentRequest (requestKey, opts = {}) { + const row = this._findPendingDocumentRequest(requestKey); + if (!row) return { ok: false, error: 'pending request not found' }; + const sendOpts = {}; + if (row.parsed && row.parsed.blobIndex != null) { + sendOpts.blobIndex = Number(row.parsed.blobIndex); + } + const sealedMeta = this._getDocumentSealedMeta(row.documentId); + // Sealed key reveal requires prior authorizeDocumentKeyReveal (or explicit force). + if (sealedMeta && this._mayRevealDocumentContentKey(row.documentId, sealedMeta.paymentHashHex, row.parsed || {}, opts)) { + sendOpts.revealKey = true; + } + if (!this._sendP2pFileSendToPeer(row.documentId, row.peerAddress, sendOpts)) { + return { ok: false, error: 'could not send P2P_FILE_SEND (missing document or connection)' }; + } + delete this.pendingDocumentRequests[row.key]; + this.emit('documentRequestApproved', row); + return { ok: true, documentId: row.documentId, peerAddress: row.peerAddress }; + } + + /** + * Record that settlement for a sealed document was verified so a matching + * DocumentRequest may receive the AES content key (not merely the ciphertext). + * @param {object} opts + * @param {string} opts.documentId + * @param {string} opts.contentHashHex payment hash SHA256(K) + * @param {string} [opts.settlementId] + * @param {string} [opts.txid] + * @returns {Object} `{ ok, error?, key? }` + */ + authorizeDocumentKeyReveal (opts = {}) { + const documentId = String(opts.documentId || '').trim(); + const contentHashHex = String(opts.contentHashHex || opts.paymentHashHex || '').trim().toLowerCase(); + if (!documentId) return { ok: false, error: 'documentId required' }; + if (!/^[0-9a-f]{64}$/.test(contentHashHex)) { + return { ok: false, error: 'contentHashHex must be 64 hex chars' }; + } + const settlementId = opts.settlementId != null ? String(opts.settlementId).trim() : ''; + const txid = opts.txid != null ? String(opts.txid).trim() : ''; + // Require a settlement handle so callers cannot authorize from public hash echo alone. + if (!settlementId && !txid) { + return { ok: false, error: 'settlementId or txid required to authorize key reveal' }; + } + const key = `${documentId}|${contentHashHex}`; + this._authorizedDocumentKeyReveals.set(key, { + documentId, + contentHashHex, + settlementId: settlementId || null, + txid: txid || null, + authorizedAt: Date.now() + }); + while (this._authorizedDocumentKeyReveals.size > 4096) { + const first = this._authorizedDocumentKeyReveals.keys().next().value; + this._authorizedDocumentKeyReveals.delete(first); + } + this.emit('documentKeyRevealAuthorized', this._authorizedDocumentKeyReveals.get(key)); + return { ok: true, key }; + } + + /** + * @param {string} documentId + * @param {string} paymentHashHex + * @param {object} parsed + * @param {Object} [opts] + * @param {boolean} [opts.forceReveal] + * @returns {boolean} + */ + _mayRevealDocumentContentKey (documentId, paymentHashHex, parsed, opts = {}) { + if (opts.forceReveal === true) { + if (this.settings.allowForceDocumentKeyReveal !== true) { + this.emit('warning', + '[FABRIC:PEER] forceReveal ignored (set allowForceDocumentKeyReveal to enable)'); + return false; + } + return requestMatchesPaymentHash(parsed, paymentHashHex); + } + const authKey = `${String(documentId)}|${String(paymentHashHex || '').toLowerCase()}`; + const settlementVerified = this._authorizedDocumentKeyReveals.has(authKey); + return requestUnlocksContentKey(parsed, paymentHashHex, { settlementVerified }); + } - const registry = this._state.peers || {}; - const pongPeerId = (this._addressToId && this._addressToId[origin.name]) || origin.name; - const regEntry = registry[pongPeerId] || registry[origin.name]; - this._upsertPeerRegistry(pongPeerId, { id: pongPeerId, address: origin.name, score: (regEntry && regEntry.score != null ? regEntry.score : 0) + 1, lastSeen: new Date().toISOString() }); + /** + * Deny / drop a pending DOCUMENT_REQUEST without sending bytes. + * @param {string} requestKey + * @returns {{ok: boolean, error: (string|undefined)}} + */ + denyDocumentRequest (requestKey) { + const row = this._findPendingDocumentRequest(requestKey); + if (!row) return { ok: false, error: 'pending request not found' }; + delete this.pendingDocumentRequests[row.key]; + this.emit('documentRequestDenied', row); + return { ok: true, documentId: row.documentId, peerAddress: row.peerAddress }; + } - if (this.settings.debug) this.emit('debug', `Received pong: ${JSON.stringify(message, null, ' ')}`); - this.emit('state', this.state); + _findPendingDocumentRequest (requestKey) { + if (!requestKey) return null; + if (this.pendingDocumentRequests[requestKey]) { + return Object.assign({ key: requestKey }, this.pendingDocumentRequests[requestKey]); + } + const matches = Object.keys(this.pendingDocumentRequests).filter((k) => { + return this.pendingDocumentRequests[k].documentId === requestKey; + }); + if (matches.length === 1) { + const key = matches[0]; + return Object.assign({ key }, this.pendingDocumentRequests[key]); + } + return null; + } - break; - case 'P2P_PEER_ALIAS': - this.emit('debug', `peer_alias ${origin.name} <Generic>${JSON.stringify(message.object || '')}`); - this.connections[origin.name]._alias = message.object.name; - const aliasPeerId = (this._addressToId && this._addressToId[origin.name]) || origin.name; - this._upsertPeerRegistry(aliasPeerId, { id: aliasPeerId, address: origin.name, alias: message.object && message.object.name }); - // const alias = Message.fromVector(['PeerAlias', JSON.stringify(message)]); - // this.relayFrom(origin.name, alias); - break; - case 'P2P_PEER_ANNOUNCE': - this.emit('debug', `peer_announce <Generic>${JSON.stringify(message.object || '')}`); - const candidate = new Actor(message.object); - this.candidates.push(candidate.toGenericMessage()); - // this._fillPeerSlots(); + _queuePendingDocumentRequest (documentId, origin, parsed) { + this._pendingDocumentRequestSeq += 1; + const key = `req-${this._pendingDocumentRequestSeq}`; + const row = { + documentId, + peerAddress: origin && origin.name, + created: Date.now(), + parsed: parsed || null + }; + this.pendingDocumentRequests[key] = row; + const payload = Object.assign({ key }, row); + this.emit('documentRequestPending', payload); + return payload; + } - // const announce = Message.fromVector(['PeerAnnounce', JSON.stringify(message)]); - // this.relayFrom(origin.name, announce); - break; - case 'P2P_DOCUMENT_PUBLISH': - break; - case 'P2P_FILE_SEND': - this.emit('file', { message, origin }); - break; - case 'CONTRACT_PUBLISH': - // TODO: reject and punish mis-behaving peers - this.emit('debug', `Handling peer contract publish: ${JSON.stringify(message.object)}`); - this._registerContract(message.object); - break; - case 'CONTRACT_MESSAGE': - // TODO: reject and punish mis-behaving peers - if (this.settings.debug) this.emit('debug', `Handling contract message: ${JSON.stringify(message.object)}`); - if (this.settings.debug) this.emit('debug', `Contract state: ${JSON.stringify(this.state.contracts[message.object.contract])}`); - manager.applyPatch(this._state.content.contracts[message.object.contract], message.object.ops); - this.commit(); - break; + /** + * AMP buffers for one document: canonical `DocumentPublish`, then optional pricing `P2P_DOCUMENT_PUBLISH`. + * @param {string} documentId + * @param {string} body UTF-8 body + * @param {number} rateSats + * @returns {Buffer[]} + */ + _buildPublishDocumentWireBuffers (documentId, body, rateSats) { + const parsed = this._buildDocumentParsedForPublish(documentId, body); + const dataStr = fabricCanonicalJson(whitelistedDocumentFields(documentId, parsed)); + const canonical = Message.fromVector(['DocumentPublish', dataStr]); + canonical.signWithKey(this.key); + const buffers = [canonical.toBuffer()]; + if (rateSats > 0) { + const sealedMeta = this._getDocumentSealedMeta(documentId); + const resolved = resolveDocumentContentHashHex({ + documentId, + parsed, + sealedMeta: sealedMeta || undefined, + sealed: !!sealedMeta + }); + const contentHash = resolved.contentHashHex; + const rateMsg = Message.fromVector(['P2P_DOCUMENT_PUBLISH', JSON.stringify({ + type: 'P2P_DOCUMENT_PUBLISH', + object: { + hash: documentId, + rate: rateSats, + contentHash, + contentHashHex: contentHash, + binding: resolved.binding + } + })]); + rateMsg.signWithKey(this.key); + buffers.push(rateMsg.toBuffer()); } + return buffers; } - _handleNOISEHandshake (_localPrivateKey, localPublicKey, remotePublicKey) { - if (this.settings.debug) { - // Never log private key material — public keys only for transport diagnostics. - this.emit('debug', `Peer transport handshake using local public key: ${localPublicKey.toString('hex')}`); - this.emit('debug', `Peer transport handshake with remote public key: ${remotePublicKey.toString('hex')}`); + /** + * Re-send all local document publishes to one peer (same bytes as {@link Peer#_publishDocument}). + * @param {string} peerAddress connection key in {@link Peer#connections} + */ + _announceLocalDocumentsToPeer (peerAddress) { + const docs = this._state.content.documents; + if (!docs || typeof docs !== 'object') return; + const rates = this._state.content.documentRates || {}; + const conn = peerAddress && this.connections[peerAddress]; + if (!conn || !conn._writeFabric) return; + for (const docId of Object.keys(docs)) { + const body = docs[docId]; + const rateSats = Object.prototype.hasOwnProperty.call(rates, docId) ? rates[docId] : 0; + const buffers = this._buildPublishDocumentWireBuffers(docId, body, rateSats); + for (const buf of buffers) { + conn._writeFabric(buf); + } } } - _NOISESocketHandler (socket) { - const target = `${socket.remoteAddress}:${socket.remotePort}`; - const url = `tcp://${target}`; + /** + * Store a document locally and gossip to peers. + * 1) **Canonical** `DOCUMENT_PUBLISH` wire message (same bytes as hub `documentPublishEnvelope`) for L1 `contentHash`. + * 2) If `rateSats > 0`, a **pricing** `GENERIC` `P2P_DOCUMENT_PUBLISH` with `rate` and `contentHash` (sat ask). + * @param {String} documentId - Catalog key (e.g. CLI document name). + * @param {String} [content=''] - UTF-8 body stored under {@link Peer#state}.documents. + * @param {Number} [rateSats=0] - Ask price in satoshis (gossip only; not part of canonical hash). + */ + _publishDocument (documentId, content = '', rateSats = 0) { + if (!this._state.content.documents) this._state.content.documents = {}; + if (!this._state.content.documentRates) this._state.content.documentRates = {}; + if (!this._state.content.documentSealed) this._state.content.documentSealed = {}; + if (!this._state.content.documentContentKeys) this._state.content.documentContentKeys = {}; + const body = (content === null || content === undefined) ? '' : String(content); + this._state.content.documents[documentId] = body; + this._state.content.documentRates[documentId] = rateSats; + + // Priced docs: seal plaintext; HTLC payment hash = SHA256(content key). + if (rateSats > 0 && this.settings.sealPricedDocuments !== false) { + const sale = prepareSealedSale(Buffer.from(body, 'utf8')); + this._state.content.documentSealed[documentId] = { + ciphertextBase64: sale.ciphertext.toString('base64'), + paymentHashHex: sale.paymentHashHex, + encryption: sale.encryption, + plaintextSha256: sale.plaintextSha256 + }; + this._state.content.documentContentKeys[documentId] = sale.keyHex; + } else { + delete this._state.content.documentSealed[documentId]; + delete this._state.content.documentContentKeys[documentId]; + } - // Store a unique actor for this inbound connection - this._registerActor({ name: target }); + this.commit(); - const derived = this.identity.key.derive(FABRIC_KEY_DERIVATION_PATH); - if (this.settings.debug) { - this.emit('debug', 'NOISE inbound: session key derived for handshake (private key not logged)'); + const buffers = this._buildPublishDocumentWireBuffers(documentId, body, rateSats); + for (const buf of buffers) { + this.broadcast(buf); } - // Create NOISE handler - const handler = noise({ - prologue: Buffer.from(PROLOGUE), - // privateKey: derived.private.toString('hex'), - verify: this._verifyNOISE.bind(this) - }); + if (this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Published canonical DOCUMENT_PUBLISH + pricing gossip (if rate > 0)'); + } + } - // Handle low-level socket errors for inbound connections - socket.on('error', (error) => { - if (this.settings.debug) this.emit('debug', `--- debug error from _NOISESocketHandler() ---`); - if (error && (error.code === 'EPIPE' || error.code === 'ECONNRESET')) { - this.emit('warning', `Suppressing transient inbound socket error (${error.code}) from _NOISESocketHandler().`); - } else { - this.emit('error', `Inbound socket error: ${error}`); - } - }); + /** + * Handle inbound `DOCUMENT_REQUEST`: emit `documentRequest` / `DocumentRequest`, then either + * send `P2P_FILE_SEND` (when {@link Peer#settings.autoFulfillDocumentRequests}), queue for + * operator approve, or relay when the document is not held. + * + * Peel / foreign-signed `P2P_RELAY` deliveries are local-observe only: never fulfill or + * queue against the TCP last hop, and never second-flood the inner under that hop. + * + * @param {Message} message + * @param {{ name: string }} origin + * @param {*} socket + * @param {Object} [options] same delivery opts as {@link Peer#_handleFabricMessage} + */ + _handleDocumentRequestWire (message, origin, socket, options = null) { + const delivery = meshDeliveryContext(options, origin && origin.name); + const rawDr = messageDataToString(message.data); + const prDr = tryParseWireJsonBody(rawDr); + if (!prDr.ok) return; + let parsed = prDr.value; + if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return; + + const docId = parsed.document || parsed.id; + if (!docId) return; + + const docs = this._state.content.documents; + const held = !!(docs && Object.prototype.hasOwnProperty.call(docs, docId)); + let pendingKey = null; + // Only queue TCP fulfill when the TCP hop is a trustworthy delivery target. + if (held && delivery.allowTcpOriginSideEffects && + this.settings.autoFulfillDocumentRequests === false) { + const pending = this._queuePendingDocumentRequest(docId, origin, parsed); + pendingKey = pending.key; + } - // Set up NOISE event handlers - handler.encrypt.on('handshake', this._handleNOISEHandshake.bind(this)); - handler.encrypt.on('error', (error) => { - if (error && (error.code === 'EPIPE' || error.code === 'ECONNRESET')) { - this.emit('warning', `Suppressing transient NOISE encrypt error (${error.code}).`); - } else { - this.emit('error', `NOISE encrypt error: ${error}`); + const payload = { + message, + origin, + socket, + documentId: docId, + parsed, + source: 'canonical', + pendingKey, + localObserveOnly: !delivery.allowTcpOriginSideEffects + }; + this.emit('documentRequest', payload); + this.emit('DocumentRequest', payload); + + if (!held) { + // Outer envelope already transported this request — do not mesh-relay or + // private-rewrite under the last hop (would bind reverse routes to that hop). + if (!delivery.allowMeshRelay) return; + const budgeted = parsed.maxSats != null && Number.isFinite(Number(parsed.maxSats)); + if (budgeted && this.settings.relayPrivateDocumentRequests) { + this._privateRelayDocumentRequest(parsed, origin, message); + return; } - }); - - handler.encrypt.on('end', (data) => { - if (this.settings.debug) this.emit('debug', `Peer encrypt end: ${data}`); - // socket.destroy(); - delete this.connections[target]; - this.peers[target].status = 'disconnected'; - }); + this.relayFrom(origin.name, message, socket); + return; + } - handler.decrypt.on('error', (error) => { - if (error && (error.code === 'EPIPE' || error.code === 'ECONNRESET')) { - this.emit('warning', `Suppressing transient NOISE decrypt error (${error.code}).`); - } else { - this.emit('error', `NOISE decrypt error: ${error}`); + if (!delivery.allowTcpOriginSideEffects) { + if (this.settings.debug) { + this.emit('debug', + '[FABRIC:PEER] DOCUMENT_REQUEST held but peel/RELAY-as-is — observe only (no fulfill to last hop)'); } - }); + return; + } - handler.decrypt.on('close', (data) => { - if (this.settings.debug) this.emit('debug', `Peer decrypt close: ${data}`); - }); + if (this.settings.autoFulfillDocumentRequests === false) { + return; + } - handler.decrypt.on('end', (data) => { - if (this.settings.debug) { - this.emit('debug', `Peer decrypt end: (${target}) ${data}`); - this.emit('debug', `Connections: ${Object.keys(this.connections)}`); - } - socket._destroyFabric(); - }); + const sendOpts = {}; + if (parsed.blobIndex != null) sendOpts.blobIndex = Number(parsed.blobIndex); + if (parsed.routeId) sendOpts.routeId = String(parsed.routeId); + const sealedMeta = this._getDocumentSealedMeta(docId); + // Ciphertext anytime; content key only after authorizeDocumentKeyReveal (never hash echo). + if (sealedMeta && this._mayRevealDocumentContentKey(docId, sealedMeta.paymentHashHex, parsed)) { + sendOpts.revealKey = true; + } + if (!this._sendP2pFileSendToPeer(docId, origin.name, sendOpts)) { + this.emit('warning', `[FABRIC:PEER] DOCUMENT_REQUEST: could not send P2P_FILE_SEND to ${origin && origin.name}`); + } + } - handler.decrypt.on('data', (data) => { - this._handleFabricMessage(data, { name: target }); - }); + /** + * Rewrite a budgeted DocumentRequest (privacy) and forward with reduced maxSats. + * @param {object} parsed + * @param {{ name: string }} origin + * @param {Message} [originalMessage] + */ + _privateRelayDocumentRequest (parsed, origin, originalMessage = null) { + const seenKey = `${parsed.routeId || ''}:${parsed.document || parsed.id}:${parsed.maxSats}`; + if (this._documentRelaySeen.has(seenKey)) { + this.emit('warning', '[FABRIC:PEER] Dropping looped DocumentRequest rewrite'); + return; + } + this._documentRelaySeen.add(seenKey); + if (this._documentRelaySeen.size > 2048) { + const first = this._documentRelaySeen.values().next().value; + this._documentRelaySeen.delete(first); + } - socket._destroyFabric = () => { - this._destroyFabric(socket, target); + const policy = { + documentRelayFeeSats: this.settings.documentRelayFeeSats, + documentRelayFeeBps: this.settings.documentRelayFeeBps, + documentRelayMinRemainingSats: this.settings.documentRelayMinRemainingSats, + documentRelayMaxHops: this.settings.documentRelayMaxHops }; - - socket._writeFabric = (msg) => { - this._writeFabric(msg, handler); + const fwd = buildForwardedDocumentRequest(parsed, policy); + if (!fwd.ok) { + this.emit('warning', `[FABRIC:PEER] private relay abort: ${fwd.error}`); + return; + } + this._documentRelayRoutes[fwd.body.routeId] = { + prevHopAddress: origin && origin.name, + prevRouteId: parsed.routeId || null, + feeSats: fwd.feeSats, + maxSatsIn: Number(parsed.maxSats), + maxSatsOut: fwd.maxSatsOut, + documentId: fwd.body.document, + blobIndex: fwd.body.blobIndex, + created: Date.now() }; - - // Store socket in collection - this.connections[target] = socket; - - // Begin NOISE stream - handler.encrypt.pipe(socket).pipe(handler.decrypt); - - this.emit('connections:open', { - id: target, - url: url + this._capDocumentRelayRoutes(); + const msg = Message.fromVector(['DocumentRequest', JSON.stringify(fwd.body)]); + msg.signWithKey(this.key); + const sent = this._sendPrivateRelayedDocumentRequest(msg, origin, parsed); + if (!sent) { + this.emit('warning', '[FABRIC:PEER] private relay: no outbound peer for rewritten DocumentRequest'); + return; + } + this.emit('documentRequestRelayed', { + feeSats: fwd.feeSats, + maxSatsOut: fwd.maxSatsOut, + routeId: fwd.body.routeId, + documentId: fwd.body.document, + origin, + directed: true }); + if (originalMessage && this.settings.debug) { + this.emit('debug', '[FABRIC:PEER] Rewrote DocumentRequest (directed; did not mesh-broadcast)'); + } } - _publishDocument (hash, rate = 0) { - this._state.content.documents[hash] = document; + /** + * Deliver a rewritten private DocumentRequest without mesh broadcast. + * Preference: onion `relayPath` → explicit `nextPeer` → fan-out to TCP peers + * other than the inbound origin. + * @param {Message} msg signed DocumentRequest + * @param {{name: (string|undefined)}|null} origin + * @param {object} parsed inbound request body + * @returns {boolean} + */ + _sendPrivateRelayedDocumentRequest (msg, origin, parsed = {}) { + if (!msg) return false; + const path = Array.isArray(parsed.relayPath) ? parsed.relayPath : null; + if (path && path.length) { + return this.sendOnion(path, msg); + } - this.commit(); + const next = parsed.nextPeer != null ? String(parsed.nextPeer).trim() : ''; + if (next) { + const addr = (this.connections && this.connections[next]) + ? next + : (this._resolveToAddress(next) || this._resolveAddressByXOnly(next)); + if (addr && this.connections[addr] && this.connections[addr]._writeFabric) { + if (!(origin && origin.name && addr === origin.name)) { + this.connections[addr]._writeFabric(msg.toBuffer()); + return true; + } + } + } - const PACKET_DOCUMENT_PUBLISH = Message.fromVector(['P2P_DOCUMENT_PUBLISH', JSON.stringify({ - type: 'P2P_DOCUMENT_PUBLISH', - object: { - hash: hash, - rate: rate + const buf = msg.toBuffer(); + let wrote = 0; + for (const addr of Object.keys(this.connections || {})) { + if (origin && origin.name && addr === origin.name) continue; + const conn = this.connections[addr]; + if (conn && typeof conn._writeFabric === 'function') { + conn._writeFabric(buf); + wrote += 1; } - })]); + } + return wrote > 0; + } - const message = PACKET_DOCUMENT_PUBLISH.toBuffer(); - if (this.settings.debug) this.emit('debug', `Broadcasting document publish: ${message.toString('utf8')}`); - this.broadcast(message); + /** + * Forward a relayed `P2P_FILE_SEND` / key reveal back toward the buyer using reverse routes. + * @param {object} fileObj + * @param {{ name: string }} origin + * @returns {boolean} true if forwarded (caller should skip local ingest) + */ + _maybeReverseRelayFileSend (fileObj, origin) { + const routeId = fileObj && (fileObj.routeId || fileObj.relayRouteId); + if (!routeId || !this._documentRelayRoutes[routeId]) return false; + const route = this._documentRelayRoutes[routeId]; + const prev = route.prevHopAddress; + if (!prev || !this.connections[prev] || !this.connections[prev]._writeFabric) return false; + if (origin && origin.name && origin.name === prev) return false; + + const fwd = Object.assign({}, fileObj, { + routeId: route.prevRouteId || routeId, + relayedBy: this.id || null + }); + const msg = Message.fromVector(['P2P_FILE_SEND', JSON.stringify(fwd)]); + msg.signWithKey(this.key); + this.connections[prev]._writeFabric(msg.toBuffer()); + this.emit('documentRelayReturn', { + routeId, + prevHopAddress: prev, + documentId: fileObj.name || fileObj.documentId, + origin + }); + return true; } _registerActor (object) { @@ -1460,45 +4437,128 @@

    Source: types/peer.js

    return this; } - _registerContract (object) { + /** + * When a publish body declares authority arrays, the AMP wire signer must be + * one of them. Bodies with no authorities are allowed (observe-only; empty + * patch allow-list). Missing signer (local seed) is allowed. + * @param {object} object + * @param {string|null} signerPubkeyHex + * @returns {boolean} + */ + _contractPublishSignerAuthorized (object, signerPubkeyHex = null) { + const authorities = collectContractAuthorityPubkeys(object); + if (!authorities.size) return true; + const pub = normalizePeerPubkeyHex(signerPubkeyHex); + if (!pub) return true; + return authoritySetHasPubkey(authorities, pub); + } + + /** + * @param {object} object + * @param {string|null} [publisherPubkeyHex] + * @returns {boolean} true when newly registered (or already present no-op) + */ + _registerContract (object, publisherPubkeyHex = null) { this.emit('debug', `Registering contract: ${JSON.stringify(object, null, ' ')}`); const actor = new Actor(object); - if (this.contracts[actor.id]) return this; + // Duplicate CONTRACT_PUBLISH must not expand the patch allow-list. Otherwise an + // attacker can re-sign an observed publish body and merge their pubkey (or a + // forged parties[] list) into `_contractPatchAllowList`, then apply ops. + if (this.contracts[actor.id]) { + if (this.settings.debug) { + this.emit('debug', + `[FABRIC:PEER] Ignoring CONTRACT_PUBLISH republish for ${actor.id} (allow-list unchanged)`); + } + return true; + } + + if (!this._contractPublishSignerAuthorized(object, publisherPubkeyHex)) { + this.emit('warning', + `[FABRIC:PEER] CONTRACT_PUBLISH rejected for ${actor.id}: ` + + 'wire signer is not listed in parties/validators/owners/members/authorities'); + return false; + } this.contracts[actor.id] = actor; this._state.content.contracts[actor.id] = object.state; + // Patch rights come only from declared authority arrays — never from the AMP + // wire signer alone (front-run with identical body must not elevate attacker). + this._mergeContractPatchAllowList(actor.id, object, publisherPubkeyHex); this.commit(); this.emit('contractset', this.contracts); - return this; + return true; } - _registerNOISEClient (name, socket, client) { - // Assign socket properties - // Failure counter - socket._failureCount = 0; - socket._lastMessage = null; - socket._messageLog = []; + /** + * Build the set of pubkeys allowed to apply CONTRACT_MESSAGE ops for a newly + * registered contract. Called only on first registration of a contract id — + * republishes must not invoke this (see {@link Peer#_registerContract}). + * Membership is taken **only** from body authority arrays (`parties`, + * `validators`, `owners`, `members`, `authorities`). The wire signer is never + * granted rights unless already listed there. + * @param {string} contractId + * @param {object} object contract publish body + * @param {string|null} [_publisherPubkeyHex] ignored (kept for call-site compat) + */ + _mergeContractPatchAllowList (contractId, object, _publisherPubkeyHex = null) { + const id = String(contractId || ''); + if (!id) return; + let set = this._contractPatchAllowList[id]; + if (!set) { + set = new Set(); + this._contractPatchAllowList[id] = set; + } + for (const hex of collectContractAuthorityPubkeys(object)) { + set.add(hex); + } + } + + /** + * @param {string} contractId + * @param {string|null} signerPubkeyHex + * @returns {boolean} + */ + _signerMayPatchContract (contractId, signerPubkeyHex) { + const set = this._contractPatchAllowList[String(contractId || '')]; + if (!set || !set.size) return false; // fail closed: no parties recorded + const h = normalizePeerPubkeyHex(signerPubkeyHex); + if (!h) return false; + if (set.has(h)) return true; + // Tolerate allow-lists populated with compressed 66-char hex before normalize. + if (h.length === 64) { + for (const entry of set) { + if (typeof entry === 'string' && entry.length === 66 && entry.slice(2) === h) return true; + } + } + return false; + } - // Enable keepalive + /** + * Periodic P2P_PING and track expected P2P_PONG replies so registry score cannot be + * self-inflated by unsolicited pongs (see FLUSH_CHAIN trust gate). + * @param {*} socket — connection object (stores `_fabricPingOutstanding`, `_keepalive`) + * @param {*} encryptWrite — NOISE encrypt stream with `.write(Buffer)` (`client.encrypt` / `handler.encrypt`) + */ + _startFabricPingKeepalive (socket, encryptWrite) { + if (socket._keepalive) clearInterval(socket._keepalive); + socket._fabricPingOutstanding = 0; socket._keepalive = setInterval(() => { const now = (new Date()).toISOString(); - const P2P_PING = Message.fromVector(['GENERIC', JSON.stringify({ - actor: { - id: this.identity.id - }, - created: now, - type: 'P2P_PING', - object: { - created: now - } - })]); - + const P2P_PING = Message.fromVector(['P2P_PING', JSON.stringify({ + created: now + })]).signWithKey(this.key); + + // At most one unanswered PING per connection so burst P2P_PONG cannot inflate registry score + // (FLUSH_CHAIN trust gate uses _registryScoreForConnectionAddress). + if ((socket._fabricPingOutstanding | 0) >= 1) return; + socket._fabricPingOutstanding = 1; try { - client.encrypt.write(P2P_PING.toBuffer()); + encryptWrite.write(P2P_PING.toBuffer()); } catch (exception) { + socket._fabricPingOutstanding = 0; if (exception && (exception.code === 'EPIPE' || exception.code === 'ECONNRESET')) { this.emit('warning', `Suppressing transient write error (${exception.code}) during ping.`); } else { @@ -1506,6 +4566,16 @@

    Source: types/peer.js

    } } }, 60000); + } + + _registerNOISEClient (name, socket, client) { + // Assign socket properties + // Failure counter + socket._failureCount = 0; + socket._lastMessage = null; + socket._messageLog = []; + + this._startFabricPingKeepalive(socket, client.encrypt); // TODO: reconcile APIs for these methods // Map destroy function @@ -1620,22 +4690,49 @@

    Source: types/peer.js

    this.emit('debug', `[FABRIC:PEER] Peer list: ${JSON.stringify(this.settings.peers)}`); } - this._registerActor({ name: `${this.interface}:${this.port}` }); - if (this.settings.listen) { this.emit('log', 'Listener starting...'); - if (this.settings.debug) console.debug('Starting listener on', this.interface, this.port); - try { - address = await this.listen(); - if (this.settings.debug) console.debug('got address:', address); - this.listenAddress = address; - this.emit('log', 'Listener started!'); - } catch (exception) { - // Do not emit('error') here — with no listener Node throws ERR_UNHANDLED_ERROR; callers get the throw below. - this.emit('warning', 'Could not listen:', exception); - throw new Error('Peer failed to listen: ' + (exception && exception.message ? exception.message : exception)); + const maxAttemptsRaw = this.settings.listenPortAttempts; + const maxAttempts = (typeof maxAttemptsRaw === 'number' && maxAttemptsRaw > 0) + ? Math.min(256, Math.floor(maxAttemptsRaw)) + : 20; + const basePort = (typeof this.settings.port === 'number' && !Number.isNaN(this.settings.port)) + ? this.settings.port + : 7777; + + for (let attempt = 0; attempt < maxAttempts; attempt++) { + this.settings.port = basePort + attempt; + if (this.public && typeof this.public === 'object') this.public.port = this.settings.port; + + if (this.settings.debug) { + this.emit('debug', `Starting listener on ${this.interface}:${this.settings.port} (attempt ${attempt + 1}/${maxAttempts})`); + } + + try { + address = await this.listen(); + if (this.settings.debug) this.emit('debug', `got address: ${JSON.stringify(address)}`); + this.listenAddress = address; + if (attempt > 0) { + this.emit('log', `[FABRIC:PEER] Port ${basePort} was busy; listening on ${this.settings.port} instead.`); + } + this.emit('log', 'Listener started!'); + break; + } catch (exception) { + const inUse = exception && exception.code === 'EADDRINUSE'; + if (inUse && attempt < maxAttempts - 1) { + this.emit('warning', `[FABRIC:PEER] Port ${this.settings.port} in use, trying ${this.settings.port + 1}...`); + continue; + } + // Do not emit('error') here — with no listener Node throws ERR_UNHANDLED_ERROR; callers get the throw below. + this.emit('warning', 'Could not listen:', exception); + throw new Error('Peer failed to listen: ' + (exception && exception.message ? exception.message : exception)); + } } + + this._registerActor({ name: `${this.interface}:${this.port}` }); + } else { + this._registerActor({ name: `${this.interface}:${this.port}` }); } if (this.settings.networking && this.settings.peers && this.settings.peers.length) { @@ -1667,6 +4764,7 @@

    Source: types/peer.js

    if (this.connections[addr]) return false; const listenAddr = this.listenAddress || `${this.interface}:${this.port}`; if (addr === listenAddr) return false; + if (this._isPeerBanned(addr)) return false; return true; }); if (toReconnect.length > 0) { @@ -1742,7 +4840,7 @@

    Source: types/peer.js

    if (this._peersDb) { try { await this._peersDb.close(); - } catch (e) { /* ignore */ } + } catch { /* ignore */ } this._peersDb = null; } @@ -1761,7 +4859,7 @@

    Source: types/peer.js

    // Some server states might not have address() but still need cleanup try { this.server.close(() => resolve()); - } catch (error) { + } catch { // If close fails, it's likely already closed - resolve anyway resolve(); } @@ -1777,7 +4875,7 @@

    Source: types/peer.js

    resolve(); }); }); - } + }; this.emit('debug', 'Closing network...'); await terminator(); @@ -1863,43 +4961,65 @@

    Source: types/peer.js

    */ async listen () { return new Promise((resolve, reject) => { - if (this.settings.debug) console.debug('Listening on', this.interface, this.port); + if (this.settings.debug) this.emit('debug', `Listening on ${this.interface}:${this.port}`); + let settled = false; + + const rejectListen = (error) => { + if (settled) return; + settled = true; + this.server.removeListener('error', errorHandler); + if (this.listenerCount('error') > 0) { + this.emit('error', `Server socket error: ${error}`); + } + return reject(error); + }; // Handle server errors before attempting to listen const errorHandler = (error) => { if (error.code === 'EADDRINUSE') { - // Don't emit('error') here - caller gets rejection; emit would cause ERR_UNHANDLED_ERROR if no listener - this.server.close(() => reject(error)); - } else { - this.emit('error', `Server socket error: ${error}`); - // Don't reject on other errors during listen, let the callback handle it + // Ensure server resources are released before retrying upstream. + // Recreate the TCP server after close — a closed Server can retain + // sticky listen state and spuriously fail the next port attempt. + this.server.close(() => { + if (this.settings.listen) { + this.server = net.createServer(this._NOISESocketHandler.bind(this)); + this._peerServerRuntimeErrorBound = false; + } + rejectListen(error); + }); + return; } + rejectListen(error); }; this.server.once('error', errorHandler); this.server.listen(this.port, this.interface, (error) => { + if (settled) return; // Remove the error handler since we're handling the result here this.server.removeListener('error', errorHandler); if (error) { - // Don't emit('error') for EADDRINUSE - caller gets rejection; emit would cause ERR_UNHANDLED_ERROR if no listener - return reject(error); + return rejectListen(error); } + settled = true; const details = this.server.address(); const address = `${details.address}:${details.port}`; + // Runtime errors after listen succeeds; attach once so port-retry does not stack handlers. + if (!this._peerServerRuntimeErrorBound) { + this._peerServerRuntimeErrorBound = true; + this.server.on('error', (runtimeError) => { + if (runtimeError.code !== 'EADDRINUSE' && this.listenerCount('error') > 0) { + this.emit('error', `Server socket error: ${runtimeError}`); + } + }); + } + this.emit('log', `Now listening on tcp://${address} [!!!]`); return resolve(address); }); - - // Keep a general error handler for runtime errors - this.server.on('error', (error) => { - if (error.code !== 'EADDRINUSE') { - this.emit('error', `Server socket error: ${error}`); - } - }); }); } } @@ -1925,12 +5045,12 @@

    Source: types/peer.js

    } broadcast (msg) { - if (this.settings.verbosity >= 5) console.log('broadcasting:', msg); + if (this.settings.verbosity >= 5) this.emit('debug', `broadcasting: ${JSON.stringify(msg)}`); this.agent.broadcast(msg); } connect (address) { - if (this.settings.verbosity >= 4) console.log('[FABRIC:SWARM]', `Connecting to: ${address}`); + if (this.settings.verbosity >= 4) this.emit('debug', `[FABRIC:SWARM] Connecting to: ${address}`); try { this.agent._connect(address); @@ -1952,22 +5072,22 @@

    Source: types/peer.js

    }); swarm.agent.on('state', function (state) { - console.log('[FABRIC:SWARM]', 'Received state from agent:', state); + this.emit('debug', `[FABRIC:SWARM] Received state from agent: ${JSON.stringify(state)}`); swarm.emit('state', state); }); swarm.agent.on('change', function (change) { - console.log('[FABRIC:SWARM]', 'Received change from agent:', change); + this.emit('debug', `[FABRIC:SWARM] Received change from agent: ${JSON.stringify(change)}`); swarm.emit('change', change); }); swarm.agent.on('patches', function (patches) { - console.log('[FABRIC:SWARM]', 'Received patches from agent:', patches); + this.emit('debug', `[FABRIC:SWARM] Received patches from agent: ${JSON.stringify(patches)}`); swarm.emit('patches', patches); }); swarm.agent.on('peer', function (peer) { - console.log('[FABRIC:SWARM]', 'Received peer from agent:', peer); + this.emit('debug', `[FABRIC:SWARM] Received peer from agent: ${JSON.stringify(peer)}`); swarm._registerPeer(peer); }); @@ -2025,7 +5145,7 @@

    Source: types/peer.js

    const swarm = this; const slots = MAX_PEERS - Object.keys(this.nodes).length; const peers = Object.keys(this.peers).map(function (id) { - if (swarm.settings.verbosity >= 5) console.log('[FABRIC:SWARM]', '_fillPeerSlots()', 'Checking:', swarm.peers[id]); + if (swarm.settings.verbosity >= 5) swarm.emit('debug', `[FABRIC:SWARM] _fillPeerSlots() checking: ${JSON.stringify(swarm.peers[id])}`); return swarm.peers[id].address; }); const candidates = swarm.settings.peers.filter(function (address) { @@ -2040,25 +5160,25 @@

    Source: types/peer.js

    } async _connectSeedNodes () { - if (this.settings.verbosity >= 4) console.log('[FABRIC:SWARM]', 'Connecting to seed nodes...', this.settings.seeds); + if (this.settings.verbosity >= 4) this.emit('debug', `[FABRIC:SWARM] Connecting to seed nodes: ${JSON.stringify(this.settings.seeds)}`); for (const id in this.settings.seeds) { - if (this.settings.verbosity >= 5) console.log('[FABRIC:SWARM]', 'Iterating on seed:', this.settings.seeds[id]); + if (this.settings.verbosity >= 5) this.emit('debug', `[FABRIC:SWARM] Iterating on seed: ${this.settings.seeds[id]}`); this.connect(this.settings.seeds[id]); } } async start () { - if (this.settings.verbosity >= 4) console.log('[FABRIC:SWARM]', 'Starting...'); + if (this.settings.verbosity >= 4) this.emit('debug', '[FABRIC:SWARM] Starting...'); await this.agent.start(); await this._connectSeedNodes(); - if (this.settings.verbosity >= 4) console.log('[FABRIC:SWARM]', 'Started!'); + if (this.settings.verbosity >= 4) this.emit('debug', '[FABRIC:SWARM] Started!'); return this; } async stop () { - if (this.settings.verbosity >= 4) console.log('[FABRIC:SWARM]', 'Stopping...'); + if (this.settings.verbosity >= 4) this.emit('debug', '[FABRIC:SWARM] Stopping...'); await this.agent.stop(); - if (this.settings.verbosity >= 4) console.log('[FABRIC:SWARM]', 'Stopped!'); + if (this.settings.verbosity >= 4) this.emit('debug', '[FABRIC:SWARM] Stopped!'); return this; } } @@ -2078,14 +5198,18 @@

    Classes

    Global


    diff --git a/docs/types_program.js.html b/docs/types_program.js.html new file mode 100644 index 000000000..63282d9a1 --- /dev/null +++ b/docs/types_program.js.html @@ -0,0 +1,376 @@ + + + + + + Source: types/program.js · Docs + + + + + + + + + +
    +

    Source: types/program.js

    + + + + +
    +
    +
    'use strict';
    +
    +/**
    + * Multi-language Program — executable artifact for {@link Machine}, with optional
    + * L1 Bitcoin redeem scaffolding for `bitcoin-script`.
    + *
    + * Languages: `fabric-opcodes` | `javascript` | `bitcoin-script` | `solidity` | `asm`
    + * (solidity/asm compile stubs until Compiler frontends land).
    + *
    + * @see docs/PROGRAM.md
    + */
    +
    +const crypto = require('crypto');
    +const Circuit = require('./circuit');
    +const Script = require('./script');
    +const Hash256 = require('./hash256');
    +const fabricCanonicalJson = require('../functions/fabricCanonicalJson');
    +const { jsonSafe } = fabricCanonicalJson;
    +
    +const LANGUAGES = Object.freeze([
    +  'fabric-opcodes',
    +  'javascript',
    +  'bitcoin-script',
    +  'solidity',
    +  'asm'
    +]);
    +
    +class Program extends Circuit {
    +  /**
    +   * @param {object} [settings]
    +   * @param {string} [settings.language]
    +   * @param {string|object} [settings.source]
    +   * @param {*} [settings.bytecode]
    +   * @param {string[]} [settings.steps]
    +   * @param {string} [settings.programId]
    +   */
    +  constructor (settings = {}) {
    +    super(settings);
    +
    +    this.settings = Object.assign({
    +      language: 'fabric-opcodes',
    +      source: null,
    +      bytecode: null,
    +      steps: [],
    +      instructions: [],
    +      programId: null
    +    }, settings);
    +
    +    this.circuit = new Circuit();
    +    this.script = new Script();
    +    this.state = {};
    +
    +    if (Array.isArray(this.settings.instructions) && this.settings.instructions.length &&
    +        !(this.settings.steps && this.settings.steps.length)) {
    +      this.settings.steps = this.settings.instructions.slice();
    +    }
    +
    +    return this;
    +  }
    +
    +  /**
    +   * @param {Object} [opts]
    +   * @param {string} [opts.language]
    +   * @param {*} [opts.source]
    +   * @param {*} [opts.bytecode]
    +   * @param {Array.<string>} [opts.steps]
    +   * @param {string} [opts.programId]
    +   * @returns {Program}
    +   */
    +  static from (opts = {}) {
    +    return new Program(opts);
    +  }
    +
    +  get language () {
    +    return String(this.settings.language || 'fabric-opcodes');
    +  }
    +
    +  get source () {
    +    return this.settings.source;
    +  }
    +
    +  get bytecode () {
    +    return this.settings.bytecode;
    +  }
    +
    +  get steps () {
    +    return Array.isArray(this.settings.steps) ? this.settings.steps : [];
    +  }
    +
    +  get programId () {
    +    if (this.settings.programId) return String(this.settings.programId);
    +    return this.programHash.slice(0, 32);
    +  }
    +
    +  get programHash () {
    +    return this.hash();
    +  }
    +
    +  /**
    +   * Content-address of language + source/bytecode/steps.
    +   * @returns {string} 64-char hex
    +   */
    +  hash () {
    +    const body = fabricCanonicalJson({
    +      language: this.language,
    +      source: this.settings.source != null ? jsonSafe(this.settings.source) : null,
    +      bytecode: this.settings.bytecode != null ? jsonSafe(this.settings.bytecode) : null,
    +      steps: this.steps
    +    });
    +    return Hash256.compute(Buffer.from(body, 'utf8'));
    +  }
    +
    +  toJSON () {
    +    return {
    +      language: this.language,
    +      source: this.settings.source,
    +      bytecode: this.settings.bytecode,
    +      steps: this.steps.slice(),
    +      programId: this.programId,
    +      programHash: this.programHash
    +    };
    +  }
    +
    +  /**
    +   * Normalize language-specific form into `steps` / `bytecode`.
    +   * @returns {{ok: boolean, error: (string|undefined), program: (Program|undefined)}}
    +   */
    +  compile () {
    +    const lang = this.language;
    +    if (!LANGUAGES.includes(lang)) {
    +      return { ok: false, error: `unsupported language: ${lang}` };
    +    }
    +
    +    if (lang === 'fabric-opcodes') {
    +      let steps = this.steps.slice();
    +      if (!steps.length && typeof this.settings.source === 'string') {
    +        steps = this.settings.source.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
    +      } else if (!steps.length && Array.isArray(this.settings.source)) {
    +        steps = this.settings.source.map(String);
    +      }
    +      this.settings.steps = steps;
    +      this.settings.bytecode = steps.slice();
    +      this.settings.instructions = steps.slice();
    +      return { ok: true, program: this };
    +    }
    +
    +    if (lang === 'javascript') {
    +      let steps = this.steps.slice();
    +      if (!steps.length && typeof this.settings.source === 'string') {
    +        steps = this.settings.source.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
    +      } else if (!steps.length && this.settings.source && typeof this.settings.source === 'object') {
    +        steps = Object.keys(this.settings.source);
    +      }
    +      this.settings.steps = steps;
    +      this.settings.bytecode = steps.slice();
    +      return { ok: true, program: this };
    +    }
    +
    +    if (lang === 'bitcoin-script') {
    +      try {
    +        const bitcoin = require('bitcoinjs-lib');
    +        let compiled = null;
    +        if (Buffer.isBuffer(this.settings.bytecode)) {
    +          compiled = this.settings.bytecode;
    +        } else if (typeof this.settings.bytecode === 'string' && /^[0-9a-fA-F]+$/.test(this.settings.bytecode)) {
    +          compiled = Buffer.from(this.settings.bytecode, 'hex');
    +        } else if (Array.isArray(this.settings.source)) {
    +          compiled = bitcoin.script.compile(this.settings.source);
    +        } else if (typeof this.settings.source === 'string') {
    +          const parts = this.settings.source.trim().split(/\s+/).filter(Boolean);
    +          const chunks = parts.map((p) => {
    +            if (p.startsWith('OP_') && bitcoin.opcodes[p] != null) return bitcoin.opcodes[p];
    +            if (/^[0-9a-fA-F]+$/.test(p) && p.length % 2 === 0) return Buffer.from(p, 'hex');
    +            throw new Error(`unrecognized script token: ${p}`);
    +          });
    +          compiled = bitcoin.script.compile(chunks);
    +        } else {
    +          return { ok: false, error: 'bitcoin-script requires source asm string, opcode array, or bytecode hex' };
    +        }
    +        this.settings.bytecode = compiled;
    +        this.settings.steps = ['OP_CHECKREDEEM'];
    +        return { ok: true, program: this };
    +      } catch (err) {
    +        return {
    +          ok: false,
    +          error: err && err.message ? err.message : 'bitcoin-script compile failed'
    +        };
    +      }
    +    }
    +
    +    if (lang === 'solidity' || lang === 'asm') {
    +      return {
    +        ok: false,
    +        error: `${lang} compile not implemented — use Compiler frontend when available`
    +      };
    +    }
    +
    +    return { ok: false, error: `unsupported language: ${lang}` };
    +  }
    +
    +  /**
    +   * L1 redeem script scaffold (bitcoin-script only).
    +   * @returns {{ok: boolean, error: (string|undefined), scriptHex: (string|undefined), asm: (string|undefined)}}
    +   */
    +  toRedeemScript () {
    +    if (this.language !== 'bitcoin-script') {
    +      return { ok: false, error: 'toRedeemScript requires language bitcoin-script' };
    +    }
    +    const compiled = this.compile();
    +    if (!compiled.ok) return { ok: false, error: compiled.error };
    +    const buf = Buffer.isBuffer(this.settings.bytecode)
    +      ? this.settings.bytecode
    +      : Buffer.from(String(this.settings.bytecode || ''), 'hex');
    +    let asm = null;
    +    try {
    +      const bitcoin = require('bitcoinjs-lib');
    +      asm = bitcoin.script.toASM(buf);
    +    } catch (_) {
    +      asm = null;
    +    }
    +    return {
    +      ok: true,
    +      scriptHex: buf.toString('hex'),
    +      asm
    +    };
    +  }
    +
    +  /**
    +   * Stable digest binding program identity to a compute result (L1 / OP_RETURN / witness).
    +   * @param {*} result
    +   * @returns {string} 64-char hex
    +   */
    +  runCommitmentHex (result) {
    +    const body = fabricCanonicalJson({
    +      version: 1,
    +      kind: 'FabricProgramRun',
    +      programHash: this.programHash,
    +      result: jsonSafe(result == null ? null : result)
    +    });
    +    return crypto.createHash('sha256').update(Buffer.from(body, 'utf8')).digest('hex');
    +  }
    +
    +  step () {
    +    return this;
    +  }
    +
    +  async start () {
    +    return this;
    +  }
    +}
    +
    +Program.LANGUAGES = LANGUAGES;
    +
    +module.exports = Program;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_reader.js.html b/docs/types_reader.js.html index 59e33ce51..68a88b8a8 100644 --- a/docs/types_reader.js.html +++ b/docs/types_reader.js.html @@ -41,8 +41,6 @@

    Source: types/reader.js

    const merge = require('lodash.merge'); const EventEmitter = require('events').EventEmitter; -const Message = require('./message'); - /** * Read from a byte stream, seeking valid Fabric messages. */ @@ -110,7 +108,7 @@

    Source: types/reader.js

    _promiseBytes (count = 1) { const self = this; - return new Promise((resolve, reject) => { + return new Promise((resolve, _reject) => { const bytes = self._readBytes(count); return resolve(bytes); }); @@ -146,13 +144,13 @@

    Source: types/reader.js

    // Read header const magic = elements[0]; - const version = elements[1]; - const parent = elements[2]; - const author = elements[3]; - const type = elements[4]; + const _version = elements[1]; + const _parent = elements[2]; + const _author = elements[3]; + const _type = elements[4]; const size = elements[5]; - const signature = elements[6]; - const hash = elements[7]; + const _signature = elements[6]; + const _hash = elements[7]; if (magic !== MAGIC_BYTES) { throw new Error(`Header not magic: ${magic} !== ${MAGIC_BYTES}`); @@ -165,20 +163,7 @@

    Source: types/reader.js

    const data = this._takeBytes(HEADER_SIZE + size); const frame = Buffer.from(data, 'hex'); - // Provide data for debugger - const proposal = { - magic, - version, - parent, - author, - type, - size, - hash, - signature, - data - }; - - // this.emit('debug', `Reader Proposal: ${JSON.stringify(proposal, null, ' ')}`); + // this.emit('debug', `Reader Proposal: ${JSON.stringify({ magic, version, parent, author, type, size, hash, signature, data }, null, ' ')}`); this.emit('message', frame); } } @@ -197,14 +182,18 @@

    Classes

    Global


    diff --git a/docs/types_remote.js.html b/docs/types_remote.js.html index 13ee24bf8..8245c9c57 100644 --- a/docs/types_remote.js.html +++ b/docs/types_remote.js.html @@ -51,19 +51,17 @@

    Source: types/remote.js

    const Message = require('./message'); /** - * Interact with a remote {@link Resource}. This is currently the only - * HTTP-related code that should remain in @fabric/core — all else must - * be moved to @fabric/http before final release! - * @type {Remote} + * @classdesc <strong>WebSocket client</strong> to a remote Fabric/Hub-style host (extends {@link Actor}). Per comment in + * source, prefer moving richer HTTP to <code>@fabric/http</code>; this type stays for minimal {@link Message}-oriented + * bridging. Uses browser/Node <code>WebSocket</code> with JSON {@link Message} payloads where applicable. + * @class Remote + * @extends Actor * @property {Object} config * @property {Boolean} secure */ class Remote extends Actor { /** - * An in-memory representation of a node in our network. - * @param {Object} target - Target object. - * @param {String} target.host - Named host, e.g. "localhost". - * @param {String} target.secure - Require TLS session. + * @param {Object} [config={}] <code>host</code>, <code>port</code>, <code>secure</code>, backoff, optional macaroon, … * @constructor */ constructor (config = {}) { @@ -84,7 +82,7 @@

    Source: types/remote.js

    this.secure = this.settings.secure; this.socket = null; - this.endpoint = `${(this.secure) ? 'wss' : 'ws'}:${this.host}:${this.port}/`; + this.endpoint = `${this.secure ? 'wss' : 'ws'}://${this.host}:${this.port}/`; this._nextReconnect = 0; this._reconnectAttempts = 0; @@ -210,7 +208,7 @@

    Source: types/remote.js

    this.emit('message', message); } - async _handleSocketOpen (message) { + async _handleSocketOpen (_message) { this._nextReconnect = 0; this._reconnectAttempts = 0; if (this._reconnector) clearTimeout(this._reconnector); @@ -376,7 +374,7 @@

    Source: types/remote.js

    try { result = await response.json(); - } catch (exception) { + } catch { result = await response.text(); } } @@ -394,7 +392,7 @@

    Source: types/remote.js

    } async send (message) { - const msg = Message.fromVector(['GenericMessage', JSON.stringify(message)]); + const msg = Message.fromVector(['P2P_BASE_MESSAGE', JSON.stringify(message)]); const raw = msg.toRaw(); const actor = new Actor({ content: raw.toString('hex') }); this.socket.send(raw); @@ -507,14 +505,18 @@

    Classes

    Global


    diff --git a/docs/types_resource.js.html b/docs/types_resource.js.html index c3223ba0b..4e5047713 100644 --- a/docs/types_resource.js.html +++ b/docs/types_resource.js.html @@ -40,8 +40,12 @@

    Source: types/resource.js

    const Store = require('./store'); /** - * Generic interface for collections of digital objects. - * @param {Object} definition Initial parameters + * @classdesc Declarative <strong>application resource</strong> (routes, components, roles) persisted via {@link Store}. Pairs with + * a {@link Service} implementation that honors the definition — see <strong>DEVELOPERS.md</strong> (<em>Resources</em> / ARCs). Extends {@link Store} + * so commits and encryption options match the rest of the stack. + * @class Resource + * @extends Store + * @param {Object} [definition={}] Initial definition (<code>name</code>, <code>routes</code>, <code>components</code>, …). * @constructor */ class Resource extends Store { @@ -105,7 +109,7 @@

    Source: types/resource.js

    async create (obj) { let self = this; let vector = new State(obj); - let collection = await self.store._POST(self.routes.list, vector['@data']); + await self.store._POST(self.routes.list, vector['@data']); return vector; } @@ -118,13 +122,11 @@

    Source: types/resource.js

    async update (id, update) { let self = this; let path = `${self.routes.list}/${id}`; - let vector = new State(update); - let patches = self.store._PATCH(path, update); - let result = self.store._GET(path); - return result; + await self.store._PATCH(path, update); + return await self.store._GET(path); } - async query (inquiry) { + async query (_inquiry) { let self = this; let collection = await self.store._GET(self.routes.list); return collection; @@ -149,14 +151,18 @@

    Classes

    Global


    diff --git a/docs/types_roundRobin.js.html b/docs/types_roundRobin.js.html new file mode 100644 index 000000000..70e2742ea --- /dev/null +++ b/docs/types_roundRobin.js.html @@ -0,0 +1,153 @@ + + + + + + Source: types/roundRobin.js · Docs + + + + + + + + + +
    +

    Source: types/roundRobin.js

    + + + + +
    +
    +
    'use strict';
    +
    +const Circuit = require('./circuit');
    +
    +/**
    + * {@link Circuit} specialization for round-robin selection over a set of nodes or peers.
    + * @class RoundRobin
    + * @extends Circuit
    + */
    +class RoundRobin extends Circuit {
    +  constructor (config = {}) {
    +    super(Object.assign({
    +      name: 'RoundRobin'
    +    }, config));
    +    this._rrIndex = 0;
    +    return this;
    +  }
    +
    +  /**
    +   * @param {Array} items
    +   * @returns {*|null}
    +   */
    +  next (items = []) {
    +    if (!items.length) return null;
    +    const i = this._rrIndex % items.length;
    +    this._rrIndex = (this._rrIndex + 1) % Math.max(1, items.length);
    +    return items[i];
    +  }
    +}
    +
    +module.exports = RoundRobin;
    +
    +
    +
    + + + +
    + +
    + + + + + + \ No newline at end of file diff --git a/docs/types_router.js.html b/docs/types_router.js.html index 8308a3c0f..266f000f7 100644 --- a/docs/types_router.js.html +++ b/docs/types_router.js.html @@ -33,13 +33,13 @@

    Source: types/router.js

    'use strict';
     
    -const Scribe = require('./scribe');
    +const State = require('./state');
     
     // TODO: re-define this class for Fabric messages
     // Current code is specific to @fabric/doorman — should be a general-
     // purpose Router, not for strings and triggers in chat messages.
     
    -class Router extends Scribe {
    +class Router extends State {
       constructor (config) {
         super(config);
     
    @@ -124,14 +124,18 @@ 

    Classes

    Global


    diff --git a/docs/types_scribe.js.html b/docs/types_scribe.js.html deleted file mode 100644 index 5aaaac874..000000000 --- a/docs/types_scribe.js.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - Source: types/scribe.js · Docs - - - - - - - - - -
    -

    Source: types/scribe.js

    - - - - -
    -
    -
    'use strict';
    -
    -const crypto = require('crypto');
    -
    -// Fabric Components
    -const State = require('./state');
    -
    -class Scribe extends State {
    -  constructor (config = {}) {
    -    super(config);
    -
    -    // assign the defaults;
    -    this.settings = Object.assign({
    -      verbose: true,
    -      verbosity: 2, // 0 none, 1 error, 2 warning, 3 notice, 4 debug
    -      path: './stores/scribe',
    -      tags: []
    -    }, config);
    -
    -    // internal state
    -    this._state = new State(config);
    -
    -    // signal ready
    -    this.status = 'ready';
    -
    -    return this;
    -  }
    -
    -  /** Retrives the current timestamp, in milliseconds.
    -   * @return {Number} {@link Number} representation of the millisecond {@link Integer} value.
    -   */
    -  now () {
    -    // return new Date().toISOString();
    -    return new Date().getTime();
    -  }
    -
    -  sha256 (data) {
    -    return crypto.createHash('sha256').update(data).digest('hex');
    -  }
    -
    -  _sign () {
    -    this.commit();
    -  }
    -
    -  /**
    -   * Blindly bind event handlers to the {@link Source}.
    -   * @param  {Source} source Event stream.
    -   * @return {Scribe}        Instance of the {@link Scribe}.
    -   */
    -  trust (source) {
    -    let self = this;
    -
    -    source.on('message', async function handleTrustedMessage (msg) {
    -      // console.trace('[FABRIC:SCRIBE]', 'Our Scribe received the following message from a trusted source:', msg);
    -    });
    -
    -    source.on('transaction', async function handleTrustedTransaction (transaction) {
    -      self.log('[SCRIBE]', '[EVENT:TRANSACTION]', 'apply this transaction to local state:', transaction);
    -      self.log('[PROPOSAL]', 'apply this transaction to local state:', transaction);
    -    });
    -
    -    return self;
    -  }
    -
    -  /**
    -   * Use an existing Scribe instance as a parent.
    -   * @param  {Scribe} scribe Instance of Scribe to use as parent.
    -   * @return {Scribe}        The configured instance of the Scribe.
    -   */
    -  inherits (scribe) {
    -    return this.tags.push(scribe.settings.namespace);
    -  }
    -
    -  log (...inputs) {
    -    let now = this.now();
    -
    -    inputs.unshift(`[${this.constructor.name.toUpperCase()}]`);
    -    inputs.unshift(`[${now}]`);
    -
    -    if (this.settings.verbosity >= 3) {
    -      console.log.apply(null, ['[SCRIBE]'].concat(inputs));
    -    }
    -
    -    return this.emit('info', ['[SCRIBE]'].concat(inputs));
    -  }
    -
    -  error (...inputs) {
    -    let now = this.now();
    -
    -    inputs.unshift(`[${this.constructor.name.toUpperCase()}]`);
    -    inputs.unshift(`[${now}]`);
    -
    -    if (this.settings.verbose) {
    -      console.error.apply(null, ['[SCRIBE]'].concat(inputs));
    -    }
    -
    -    return this.emit('error', ['[SCRIBE]'].concat(inputs));
    -  }
    -
    -  warn (...inputs) {
    -    let now = this.now();
    -
    -    inputs.unshift(`[${this.constructor.name.toUpperCase()}]`);
    -    inputs.unshift(`[${now}]`);
    -
    -    if (this.settings.verbose) {
    -      console.warn.apply(null, ['[SCRIBE]'].concat(inputs));
    -    }
    -
    -    return this.emit('warning', ['[SCRIBE]'].concat(inputs));
    -  }
    -
    -  debug (...inputs) {
    -    let now = this.now();
    -
    -    inputs.unshift(`[${this.constructor.name.toUpperCase()}]`);
    -    inputs.unshift(`[${now}]`);
    -
    -    if (this.settings.verbose) {
    -      console.debug.apply(null, ['[SCRIBE]'].concat(inputs));
    -    }
    -
    -    return this.emit('debug', ['[SCRIBE]'].concat(inputs));
    -  }
    -
    -  async open () {
    -    this.status = 'opened';
    -    return this;
    -  }
    -
    -  async close () {
    -    this.status = 'closed';
    -    return this;
    -  }
    -
    -  async start () {
    -    this.status = 'starting';
    -    this['@data'] = this.settings;
    -
    -    await this.open();
    -    await this.commit();
    -
    -    // TODO: enable
    -    // this.trust(this.state);
    -
    -    this.status = 'started';
    -
    -    return this;
    -  }
    -
    -  async stop () {
    -    this.status = 'stopping';
    -    await this.close();
    -    this.status = 'stopped';
    -    return this;
    -  }
    -}
    -
    -module.exports = Scribe;
    -
    -
    -
    - - - -
    - -
    - - - - - - \ No newline at end of file diff --git a/docs/types_script.js.html b/docs/types_script.js.html index 04d19f89c..457446422 100644 --- a/docs/types_script.js.html +++ b/docs/types_script.js.html @@ -79,14 +79,18 @@

    Classes

    Global


    diff --git a/docs/types_service.js.html b/docs/types_service.js.html index 0309d886e..6c2321273 100644 --- a/docs/types_service.js.html +++ b/docs/types_service.js.html @@ -34,7 +34,7 @@

    Source: types/service.js

    'use strict';
     
     const PATCHES_ENABLED = true;
    -const OP_TRACE = require('../contracts/trace');
    +const { tryParsePersistedJson, tryParseWireJson, utf8FromPersistedRaw } = require('../functions/wireJson');
     
     // Dependencies
     const crypto = require('crypto');
    @@ -59,16 +59,19 @@ 

    Source: types/service.js

    const Message = require('./message'); const Resource = require('./resource'); const Store = require('./store'); +const { + createDefaultOpcodeRegistry, + defineOpcode: defineOpcodeEntry, + resolveOpcodeContract, + normalizePubkeyHex +} = require('../functions/opcodeRegistry'); /** - * The "Service" is a simple model for processing messages in a distributed - * system. {@link Service} instances are public interfaces for outside systems, - * and typically advertise their presence to the network. - * - * To implement a Service, you will typically need to implement all methods from - * this prototype. In general, `connect` and `send` are the highest-priority - * jobs, and by default the `fabric` property will serve as an I/O stream using - * familiar semantics. + * @classdesc Long-lived application surface extending {@link Actor}. Integrates external systems and the Fabric + * network: peers consume and produce {@link Message} (AMP) instances, not ad-hoc JSON. Subclasses implement routing, + * resources, and lifecycle (<code>start</code>/<code>stop</code> patterns — see <strong>AGENTS.md</strong>). The CLI/browser shell is {@link Service.FabricShell}. + * @class Service + * @extends Actor * @access protected * @property map The "map" is a hashtable of "key" => "value" pairs. */ @@ -81,7 +84,7 @@

    Source: types/service.js

    * @param {Object} [settings.state] Initial state to assign. */ constructor (settings = {}) { - // Initialize Scribe, our logging tool + // State (extends Actor) carries logging / lifecycle helpers formerly on Scribe super(settings); this.name = this.constructor.name; @@ -133,6 +136,7 @@

    Source: types/service.js

    this.resources = {}; this.services = {}; this.methods = {}; + this.opcodes = createDefaultOpcodeRegistry(); this.clients = {}; this.targets = []; this.history = []; @@ -151,7 +155,7 @@

    Source: types/service.js

    try { this.store = new Store(this.settings); } catch (E) { - console.error('Store Error:', E); + this.emit('error', `Store Error: ${E.message || E}`); } } @@ -178,17 +182,18 @@

    Source: types/service.js

    _ttl: new Map(), get: async (key) => { const now = Date.now(); - const ttl = this.cache._ttl.get(key); - if (ttl && ttl < now) { + const expiresAt = this.cache._ttl.get(key); + if (expiresAt !== undefined && expiresAt < now) { this.cache._data.delete(key); this.cache._ttl.delete(key); return null; } return this.cache._data.get(key); }, - set: async (key, value, ttl = 60000) => { + /** @param {string} key @param {*} value @param {number} [ttlMs=60000] time-to-live in milliseconds */ + set: async (key, value, ttlMs = 60000) => { this.cache._data.set(key, value); - this.cache._ttl.set(key, Date.now() + ttl); + this.cache._ttl.set(key, Date.now() + ttlMs); } }; @@ -270,12 +275,12 @@

    Source: types/service.js

    try { plugin = require(local); - } catch (E) { - console.log('could not load main:', E); + } catch { + // Avoid direct stdout writes from library internals. try { plugin = require(fallback); - } catch (E) { - console.log('Fallback service failed to load:', E); + } catch { + // no-op: return null plugin below } } @@ -288,7 +293,7 @@

    Source: types/service.js

    for (const [name, service] of Object.entries(this.services)) { if (!this.settings.services.includes(name)) continue; if (!service.alert) { - console.error('Service', name, 'does not have an alert function?'); + this.emit('warning', `Service ${name} does not have an alert function`); continue; } @@ -296,6 +301,15 @@

    Source: types/service.js

    } } + /** + * @param {String} msg Warning text (used by {@link Service#_registerService} duplicate guard). + * @returns {Service} This instance. + */ + _appendWarning (msg) { + this.emit('warning', msg); + return this; + } + identify () { this.emit('auth', this.key.pubkey); return this.key.pubkey; @@ -337,17 +351,18 @@

    Source: types/service.js

    if (!beat) { this.emit('error', 'Beat could not construct a Message!'); - console.trace(); - process.exit(); + throw new Error('Beat could not construct a Message'); } // TODO: remove JSON parser here — only needed for verification // TODO: parse JSON types in @fabric/core/types/message - let data = beat.data; + const data = beat.data; try { - const parsed = JSON.parse(data); - data = JSON.stringify(parsed, null, ' '); + const pr = tryParseWireJson(typeof data === 'string' ? data : String(data ?? '')); + if (!pr.ok) { + this.emit('error', `Exception parsing beat: ${pr.error.message}`); + } } catch (exception) { this.emit('error', `Exception parsing beat: ${exception}`); } @@ -372,7 +387,7 @@

    Source: types/service.js

    try { result = pointer.get(this._state.content, path); } catch (exception) { - console.error('[FABRIC:STATE]', 'Could not retrieve path:', path, pointer.get(this['@entity']['@data'], '/'), exception); + this.emit('error', `[FABRIC:STATE] Could not retrieve path ${path}: ${exception.message || exception}`); } return result; } @@ -399,8 +414,9 @@

    Source: types/service.js

    * @param {EventEmitter} source Emitter of events. * @return {Service} Instance of Service after binding events. */ - trust (source, name = source.constructor.name) { - if (!(source instanceof EventEmitter)) throw new Error('Source is not an EventEmitter.') + trust (source, name) { + if (!(source instanceof EventEmitter)) throw new Error('Source is not an EventEmitter.'); + const label = name != null && name !== '' ? String(name) : source.constructor.name; // Constants const self = this; @@ -408,7 +424,7 @@

    Source: types/service.js

    // Attach Event Listeners if (source.settings && source.settings.debug) source.on('debug', this._handleTrustedDebug.bind(this)); if (source.settings && source.settings.verbosity >= 0) { - source.on('audit', async function _handleTrustedAudit (audit) { + source.on('audit', async function _handleTrustedAudit (_audit) { /* const now = (new Date()).toISOString(); const template = { @@ -425,23 +441,23 @@

    Source: types/service.js

    return { _handleActor: source.on('actor', async function (actor) { - self.emit('debug', `[FABRIC:SERVICE] Source "${name}" emitted actor: ${JSON.stringify(actor, null, ' ')}`); + self.emit('debug', `[FABRIC:SERVICE] Source "${label}" emitted actor: ${JSON.stringify(actor, null, ' ')}`); }), _handleAlert: source.on('alert', async function (alert) { - self.alert(`[FABRIC:SERVICE] [ALERT] [!!!] ${name} alerted: ${alert}`); + self.alert(`[FABRIC:SERVICE] [ALERT] [!!!] ${label} alerted: ${alert}`); }), _handleBeat: source.on('beat', async function (beat) { - self.emit('debug', `[FABRIC:SERVICE] Source "${name}" emitted beat: ${JSON.stringify(beat, null, ' ')}`); + self.emit('debug', `[FABRIC:SERVICE] Source "${label}" emitted beat: ${JSON.stringify(beat, null, ' ')}`); - const ops = [ + const _ops = [ { op: 'add', path: `/actors`, value: {} }, { op: 'add', path: `/services`, value: {} }, - { op: 'replace', path: `/services/${name}`, value: beat.state } + { op: 'replace', path: `/services/${label}`, value: beat.state } ]; /* try { - manager.applyPatch(self._state.content, ops); + manager.applyPatch(self._state.content, _ops); await self.commit(); } catch (exception) { self.emit('warning', `Could not process beat: ${exception}`); @@ -449,45 +465,48 @@

    Source: types/service.js

    */ }), _handleChanges: source.on('changes', async function (changes) { - self.emit('debug', `[FABRIC:SERVICE] Source "${name}" emitted changes: ${changes}`); + self.emit('debug', `[FABRIC:SERVICE] Source "${label}" emitted changes: ${changes}`); }), _handleChannel: source.on('channel', async function (channel) { - self.emit('debug', `[FABRIC:SERVICE] Source "${name}" emitted channel: ${JSON.stringify(channel, null, ' ')}`); + self.emit('debug', `[FABRIC:SERVICE] Source "${label}" emitted channel: ${JSON.stringify(channel, null, ' ')}`); }), _handleCommit: source.on('commit', async function (commit) { - self.emit('debug', `[FABRIC:SERVICE] Source "${name}" committed: ${JSON.stringify(commit, null, ' ')}`); + self.emit('debug', `[FABRIC:SERVICE] Source "${label}" committed: ${JSON.stringify(commit, null, ' ')}`); }), _handleError: source.on('error', async function _handleTrustedError (error) { - self.emit('debug', `[FABRIC:SERVICE] Source "${name}" emitted error: ${error}`); + self.emit('debug', `[FABRIC:SERVICE] Source "${label}" emitted error: ${error}`); }), _handleLog: source.on('log', async function _handleTrustedLog (log) { - if (self.settings.debug) self.emit('log', `[FABRIC:SERVICE] Source "${name}" emitted log: ${log}`); + if (self.settings.debug) self.emit('log', `[FABRIC:SERVICE] Source "${label}" emitted log: ${log}`); }), _handleMessage: source.on('message', async function (message) { - self.emit('debug', `[FABRIC:SERVICE] Source "${name}" emitted message: ${JSON.stringify(message.toObject ? message.toObject() : message, null, ' ')}`); + self.emit('debug', `[FABRIC:SERVICE] Source "${label}" emitted message: ${JSON.stringify(message.toObject ? message.toObject() : message, null, ' ')}`); await self._handleTrustedMessage(message); }), _handlePatches: source.on('patches', async function (patches) { - self.emit('debug', `[FABRIC:SERVICE] [${name}] Service State: ${JSON.stringify(source.state, null, ' ')}`); - self.emit('debug', `[FABRIC:SERVICE] [${name}] Patches: ${JSON.stringify(patches)}`); + self.emit('debug', `[FABRIC:SERVICE] [${label}] Service State: ${JSON.stringify(source.state, null, ' ')}`); + self.emit('debug', `[FABRIC:SERVICE] [${label}] Patches: ${JSON.stringify(patches)}`); self.emit('patches', patches); }), _handleReady: source.on('ready', async function _handleTrustedReady (info) { - self.emit('log', `[FABRIC:SERVICE] Source "${name}" emitted ready: ${JSON.stringify(info)}`); + self.emit('log', `[FABRIC:SERVICE] Source "${label}" emitted ready: ${JSON.stringify(info)}`); }), _handleTip: source.on('tip', async function (hash) { - self.alert(`[FABRIC:SERVICE] New ${name} chaintip: ${hash}`); + self.alert(`[FABRIC:SERVICE] New ${label} chaintip: ${hash}`); }), _handleWarning: source.on('warning', async function _handleTrustedWarning (warning) { - if (self.settings?.verbosity >= 2) self.emit('warning', `[FABRIC:SERVICE] Source "${name}" emitted warning: ${warning}`); + if (self.settings?.verbosity >= 2) self.emit('warning', `[FABRIC:SERVICE] Source "${label}" emitted warning: ${warning}`); }) }; } define (name, value) { + if (String(name || '').startsWith('OP_')) { + this.defineOpcode(name, Object.assign({ family: 'bitcoin' }, value || {})); + } this.definitions[name] = Object.assign({ data: {}, - handler: function handler (msg) { + handler: function handler (_msg) { return null; } }, value); @@ -526,7 +545,7 @@

    Source: types/service.js

    object: message.object }); } catch (E) { - this.error('Malformed message:', message); + this.emit('error', `Malformed message: ${E && E.message ? E.message : E}`); } return this; @@ -540,12 +559,15 @@

    Source: types/service.js

    lock (duration = 1000) { if (this._state.status === 'LOCKED') return false; this._state.status = 'LOCKED'; + if (this._lockTimer) clearTimeout(this._lockTimer); + this._lockTimer = setTimeout(() => { + this._lockTimer = null; + delete this.locker; + this._state.status = 'UNLOCKED'; + }, duration); this.locker = new Actor({ created: (new Date()).toISOString(), - contract: (setTimeout(() => { - delete this.locker; - this._state.status = 'UNLOCKED'; - }, duration)) + contract: 'locked' }); return true; @@ -568,6 +590,99 @@

    Source: types/service.js

    return this.resources[name]; } + /** + * Register a single opcode entry in the service registry. + * @param {string} name Opcode symbol (e.g. `OP_SHA256`, `P2P_FLUSH_CHAIN`) + * @param {Object} [definition] + * @returns {Object} + */ + defineOpcode (name, definition = {}) { + return defineOpcodeEntry(this.opcodes, name, definition); + } + + /** + * Register Bitcoin-style primitive opcode metadata. + * @param {string} name + * @param {Object} [definition] + * @returns {Object} + */ + defineBitcoinOpcode (name, definition = {}) { + return this.defineOpcode(name, Object.assign({}, definition, { family: 'bitcoin' })); + } + + /** + * Register Fabric opcode metadata. + * @param {string} name + * @param {Object} [definition] + * @returns {Object} + */ + defineFabricOpcode (name, definition = {}) { + return this.defineOpcode(name, Object.assign({}, definition, { family: 'fabric' })); + } + + /** + * Register a newline-delimited opcode contract. + * Contract body example: + * `OP_DUP\nOP_HASH160\nP2P_FLUSH_CHAIN` + * @param {string} name Contract label + * @param {string} body Newline-delimited opcode list + * @param {Object} [meta] + * @returns {Object} + */ + defineOpcodeContract (name, body, meta = {}) { + const resolved = resolveOpcodeContract(this.opcodes, body); + if (resolved.unknown.length) { + throw new Error(`Unknown opcodes in contract "${name}": ${resolved.unknown.join(', ')}`); + } + + const author = (meta && meta.author != null) ? String(meta.author) : null; + const authorPubkey = normalizePubkeyHex(meta.authorPubkey || meta.pubkey || ''); + if (!author) throw new Error(`Contract "${name}" must include author.`); + if (!authorPubkey) throw new Error(`Contract "${name}" must include a valid author pubkey.`); + + const proposedPolicy = (meta && typeof meta.policy === 'object' && meta.policy) ? meta.policy : {}; + const policyPubkeys = Array.isArray(proposedPolicy.pubkeys) + ? proposedPolicy.pubkeys.map((x) => normalizePubkeyHex(x)).filter(Boolean) + : []; + if (policyPubkeys.length && (policyPubkeys.length !== 1 || policyPubkeys[0] !== authorPubkey)) { + throw new Error(`Contract "${name}" policy pubkeys must be 1-of-1 and match author pubkey.`); + } + + const policy = Object.assign({}, proposedPolicy, { + type: 'taproot-multisig', + threshold: 1, + participants: 1, + pubkeys: [authorPubkey] + }); + + const contract = Object.assign({ + name, + body: String(body || ''), + lines: resolved.lines, + opcodes: resolved.resolved.map((entry) => entry.name), + author, + authorPubkey, + policy + }, meta); + + this.define(name, { + data: contract, + handler: function handler (msg) { + return Object.assign({}, msg, { contract }); + } + }); + + return contract; + } + + /** + * Snapshot opcode registry for UI / API use. + * @returns {Object[]} + */ + listOpcodes () { + return Object.values(this.opcodes || {}).map((entry) => Object.assign({}, entry)); + } + _handleTrustedDebug (message) { this.emit('debug', `[FABRIC:SERVICE] Trusted Source emitted debug: ${message}`); } @@ -577,7 +692,9 @@

    Source: types/service.js

    } async process () { - console.log('process created'); + if (this.settings?.verbosity >= 4 || this.settings?.debug) { + this.emit('debug', '[FABRIC:SERVICE] process() created'); + } } async broadcast (msg) { @@ -586,7 +703,9 @@

    Source: types/service.js

    for (let name in this.clients) { let target = this.clients[name]; - console.log('[FABRIC:SERVICE]', 'Sending broadcast to client:', target); + if (this.settings?.verbosity >= 4 || this.settings?.debug) { + this.emit('debug', `[FABRIC:SERVICE] Sending broadcast to client: ${target}`); + } } this.emit('message', msg); @@ -598,25 +717,31 @@

    Source: types/service.js

    * @return {Promise} Resolves with resulting {@link State}. */ async route (msg) { - console.log('[FABRIC:SERVICE]', 'routing message:', msg); - console.log('[FABRIC:SERVICE]', 'definitions:', Object.keys(this.definitions)); + if (this.settings?.verbosity >= 4 || this.settings?.debug) { + this.emit('debug', `[FABRIC:SERVICE] routing message: ${JSON.stringify(msg)}`); + this.emit('debug', `[FABRIC:SERVICE] definitions: ${JSON.stringify(Object.keys(this.definitions))}`); + } let result = null; if (this.definitions[msg.type]) { - console.log('[FABRIC:SERVICE]', this.name, 'received a well-defined message type from message in requested route:', msg); + if (this.settings?.verbosity >= 4 || this.settings?.debug) { + this.emit('debug', `[FABRIC:SERVICE] ${this.name} handling message type: ${msg.type}`); + } let handler = this.definitions[msg.type].handler; let state = handler.apply(this.state, [msg]); - console.log('sample:', state); - console.log('sample.channels:', state.channels); - console.log('sample.messages:', state.messages); + if (this.settings?.verbosity >= 4 || this.settings?.debug) { + this.emit('debug', `[FABRIC:SERVICE] route state sample: ${JSON.stringify(state)}`); + } result = state; let commit = await this.commit(); - console.log('commit:', commit); + if (this.settings?.verbosity >= 4 || this.settings?.debug) { + this.emit('debug', `[FABRIC:SERVICE] route commit: ${commit}`); + } } return result; @@ -662,7 +787,7 @@

    Source: types/service.js

    }); // Attach events - this.collections[key].on('commit', (commit) => { + this.collections[key].on('commit', (_commit) => { service.broadcast({ '@type': 'StateUpdate', '@data': service.state @@ -670,11 +795,11 @@

    Source: types/service.js

    }); this.collections[key].on('message', (message) => { - console.log('[FABRIC:SERVICE]', 'Internal message:', key, message); + service.emit('debug', `[FABRIC:SERVICE] Internal message (${key}): ${JSON.stringify(message)}`); }); this.collections[key].on('transaction', (transaction) => { - console.log('[FABRIC:SERVICE]', 'Internal transaction:', key, transaction); + service.emit('debug', `[FABRIC:SERVICE] Internal transaction (${key}): ${JSON.stringify(transaction)}`); }); this.collections[key].on('changes', (changes) => { @@ -690,7 +815,7 @@

    Source: types/service.js

    try { await this.store.start(); } catch (E) { - console.error('[FABRIC:SERVICE]', 'Could not start store:', E); + this.emit('error', `[FABRIC:SERVICE] Could not start store: ${E.message || E}`); } } @@ -705,7 +830,7 @@

    Source: types/service.js

    try { this.observer = manager.observe(this._state.content); } catch (exception) { - console.trace('Could not observe state:', this._state.content, exception); + this.emit('error', `[FABRIC:SERVICE] Could not observe state: ${exception.message || exception}`); } // Set a heartbeat @@ -733,7 +858,7 @@

    Source: types/service.js

    try { await this.store.stop(); } catch (E) { - console.error('[FABRIC:SERVICE]', 'Exception stopping store:', E); + this.emit('error', `[FABRIC:SERVICE] Exception stopping store: ${E.message || E}`); } } @@ -788,7 +913,7 @@

    Source: types/service.js

    try { result = pointer.set(this.state, path, value); } catch (E) { - this.error(`Could not _PUT() ${path}:`, E); + this.emit('error', `Could not _PUT() ${path}: ${E && E.message ? E.message : E}`); } } @@ -817,7 +942,7 @@

    Source: types/service.js

    try { memory = await pointer.get(this.state, path); - } catch (E) { + } catch { this.emit('warning', `[FABRIC:SERVICE] posting to unloaded collection: ${path}`); memory = []; } @@ -825,7 +950,8 @@

    Source: types/service.js

    try { collection = new Collection(memory); } catch (E) { - console.error('Could not create collection:', E, memory); + this.emit('error', `Could not create collection: ${E.message || E}`); + return null; } // TODO: use Resource definition to de-deuplicate by fields.id @@ -838,7 +964,7 @@

    Source: types/service.js

    await this.set(path, await collection.populate()); result = `${path}/${data.address}`; } catch (E) { - console.log('NOPE:', E); + this.emit('error', `Could not persist collection update: ${E.message || E}`); } if (commit) await this.commit(); @@ -865,7 +991,26 @@

    Source: types/service.js

    if (this.store) { try { const prior = await this.store.get('/'); - this.state = JSON.parse(prior); + const pr = tryParsePersistedJson(utf8FromPersistedRaw(prior)); + if (pr.ok && pr.value != null && typeof pr.value === 'object' && !Array.isArray(pr.value)) { + const v = pr.value; + if (Object.prototype.hasOwnProperty.call(v, 'content')) { + if (v.content != null && typeof v.content === 'object' && !Array.isArray(v.content)) { + this._state.content = Object.assign({}, this._state.content, v.content); + if (typeof v.status === 'string') this._state.status = v.status; + if (Number.isFinite(Number(v.clock))) this._state.clock = Number(v.clock); + if (v.version != null) this._state.version = v.version; + } else { + this.emit('warning', '[FABRIC:SERVICE] Ignoring restored state: invalid content envelope'); + } + } else { + this._state.content = Object.assign({}, this._state.content, v); + } + } else if (pr.ok) { + this.emit('warning', '[FABRIC:SERVICE] Ignoring restored state: expected a plain object'); + } else { + this.emit('warning', `[FABRIC:SERVICE] Could not restore state: ${pr.error.message}`); + } } catch (exception) { this.emit('warning', `[FABRIC:SERVICE] Could not restore state: ${exception}`); } @@ -919,11 +1064,11 @@

    Source: types/service.js

    return result; } - async join (id) { + async join (_id) { this.log('join() is not yet implemented for this service.'); } - async whisper (target, message) { + async whisper (_target, _message) { this.log('The "whisper" function is not yet implemented.'); return this; } @@ -934,8 +1079,8 @@

    Source: types/service.js

    * @param {String} message Content of the message to send. * @return {Service} Chainable method. */ - async send (channel, message, extra) { - if (this.debug) console.log('[SERVICE]', 'send()', 'Sending:', channel, message, extra); + async send (channel, message, _extra) { + if (this.debug) this.emit('debug', `[SERVICE] send() Sending: ${channel}`); const path = Buffer.alloc(256); const payload = Buffer.alloc(2048); @@ -973,7 +1118,7 @@

    Source: types/service.js

    this.emit('patches', patches); } } catch (E) { - console.error('Could not generate patches:', E); + this.emit('error', `Could not generate patches: ${E.message || E}`); } } @@ -989,7 +1134,7 @@

    Source: types/service.js

    } async _handleBitcoinCommit (commit) { - console.log('[FABRIC:SERVICE] Handling (Bitcoin?) commit:', commit); + this.emit('debug', `[FABRIC:SERVICE] Handling (Bitcoin?) commit: ${JSON.stringify(commit)}`); } async _attachBindings (emitter) { @@ -1013,13 +1158,19 @@

    Source: types/service.js

    } async _getActor (id) { - if (!id) return this.error('Parameter "id" is required.'); + if (!id) { + this.emit('error', 'Parameter "id" is required.'); + return null; + } let path = pointer.escape(id); return this._GET(`/actors/${path}`); } async _getChannel (id) { - if (!id) return this.error('Parameter "id" is required.'); + if (!id) { + this.emit('error', 'Parameter "id" is required.'); + return null; + } let target = pointer.escape(id); return this._GET(`/channels/${target}`); } @@ -1044,7 +1195,8 @@

    Source: types/service.js

    subscriptions: [] }, actor, { id })); } catch (E) { - return this.error('Something went wrong saving:', E); + this.emit('error', `Something went wrong saving: ${E && E.message ? E.message : E}`); + return null; } await this.commit(); @@ -1135,7 +1287,7 @@

    Source: types/service.js

    return true; }, true /* mutate doc (1st param) */); } catch (exception) { - console.error('Could not apply changes:', changes, exception); + console.error(`Could not apply changes: ${exception.message || exception}`); } this.commit(); @@ -1144,7 +1296,7 @@

    Source: types/service.js

    } async _handleStateChange (changes) { - console.log('MAGIC HANDLER:', changes); + this.emit('debug', `[FABRIC:SERVICE] State change: ${JSON.stringify(changes)}`); this.emit('message', { '@type': 'Transaction', '@data': { @@ -1221,7 +1373,7 @@

    Source: types/service.js

    this.emit('debug', `Service entries: ${Object.keys(this.services)}`); // Start all Services - for (const [name, service] of Object.entries(this.services)) { + for (const [name, _service] of Object.entries(this.services)) { // TODO: re-evaluate inclusion on Service itself if (this.settings.services && this.settings.services.includes(name)) { this.emit('debug', `Starting service "${name}" (with trust)...`); @@ -1250,6 +1402,7 @@

    Source: types/service.js

    /** * Browser / CLI application shell: encrypted store, peer node, resources. + * @private * @class FabricShell */ class FabricShell extends Service { @@ -1291,8 +1444,8 @@

    Source: types/service.js

    this.templates = {}; this.keys = []; - this.stash.on('patches', function (patches) { - console.log('[FABRIC:APP]', 'heard patches!', patches); + this.stash.on('patches', (patches) => { + this.emit('debug', `[FABRIC:APP] heard patches: ${JSON.stringify(patches)}`); }); if (this.settings.resources) { @@ -1333,7 +1486,7 @@

    Source: types/service.js

    this._appendMessage(`[FABRIC:APP] @${this.id} -- Starting...`); this.status = 'STARTING'; - for (const [name, service] of Object.entries(this.services)) { + for (const [name, _service] of Object.entries(this.services)) { this._appendWarning(`@${this.id} -- Checking for Service: ${name}`); if (this.settings.services.includes(name)) { this._appendWarning(`Starting service: ${name}`); @@ -1371,7 +1524,7 @@

    Source: types/service.js

    self.resources[name] = resource; } catch (E) { - console.error(E); + this.emit('error', E.message || String(E)); } return this; @@ -1385,7 +1538,7 @@

    Source: types/service.js

    const self = this; let resources = {}; - console.warn('[APP]', 'deferring authority:', authority); + this.emit('warning', `[APP] deferring authority: ${authority}`); if (!resources) { resources = {}; @@ -1407,15 +1560,21 @@

    Source: types/service.js

    } async _appendMessage (msg) { - if (this.settings.verbosity > 2) console.log(`[${(new Date()).toISOString()}]: ${msg}`); + this.emit('log', `[${(new Date()).toISOString()}]: ${msg}`); } async _appendWarning (msg) { - console.warn(`[${(new Date()).toISOString()}]: ${msg}`); + this.emit('warning', `[${(new Date()).toISOString()}]: ${msg}`); } async _appendError (msg) { - console.error(`[${(new Date()).toISOString()}]: ${msg}`); + const line = `[${(new Date()).toISOString()}]: ${msg}`; + // Emitting `error` with no listener throws in Node.js EventEmitter. + if (this.listenerCount('error') > 0) { + this.emit('error', line); + } else { + this.emit('warning', line); + } } attach (element) { @@ -1448,7 +1607,7 @@

    Source: types/service.js

    this._bindEvents(element); this.attach(element); } catch (E) { - console.error('Could not envelop element:', E); + this.emit('error', `Could not envelop element: ${E.message || E}`); } return this; @@ -1542,14 +1701,18 @@

    Classes

    Global


    diff --git a/docs/types_session.js.html b/docs/types_session.js.html index c8ed2b323..91808936c 100644 --- a/docs/types_session.js.html +++ b/docs/types_session.js.html @@ -31,7 +31,7 @@

    Source: types/session.js

    -
    'use strict'
    +        
    'use strict';
     
     // Constants
     const {
    @@ -246,14 +246,18 @@ 

    Classes

    Global


    diff --git a/docs/types_state.js.html b/docs/types_state.js.html index 3ec5acfbc..017ee3b3d 100644 --- a/docs/types_state.js.html +++ b/docs/types_state.js.html @@ -48,13 +48,19 @@

    Source: types/state.js

    // Local Services const json = require('../functions/json'); +const { tryParsePersistedJson, parsePersistedJson } = require('../functions/wireJson'); /** - * The {@link State} is the core of most {@link User}-facing interactions. To - * interact with the {@link User}, simply propose a change in the state by - * committing to the outcome. This workflow keeps app design quite simple! + * @classdesc <strong>Named snapshot</strong> of application data extending {@link Actor} — <code>@type</code>, + * <code>@data</code>, <code>@id</code>, JSON Patch + * flows. Absorbs former <strong>Scribe</strong> behavior: <code>verbose</code> / <code>verbosity</code>, <code>now</code>, + * <code>trust</code>, <code>start</code>/<code>stop</code>, and structured <code>log</code> / <code>error</code> / + * <code>warn</code> / <code>debug</code> (console + events). {@link Channel}, {@link Document}, {@link Ledger}, + * {@link Router}, and {@link Instruction} extend <code>State</code> directly. {@link Vector} is an {@link EventEmitter} only. + * Sibling concept to {@link Entity}. + * @class State + * @extends Actor * @access protected - * @augments EventEmitter * @property {Number} size Size of state in bytes. * @property {Buffer} @buffer Byte-for-byte memory representation of state. * @property {String} @type Named type. @@ -132,6 +138,16 @@

    Source: types/state.js

    this.value = {}; + const cfg = (typeof data === 'object' && data !== null && !Buffer.isBuffer(data)) ? data : {}; + this.settings = Object.assign({ + verbose: true, + verbosity: 2, + path: './stores/state', + tags: [] + }, this.settings, cfg); + + this.status = 'ready'; + // TODO: document hidden properties // Remove various undesired clutter from output Object.defineProperty(this, '@allocation', { enumerable: false }); @@ -206,11 +222,9 @@

    Source: types/state.js

    let result = null; if (typeof input === 'string') { - try { - result = JSON.parse(input); - } catch (E) { - console.error('Failure in fromJSON:', E); - } + const pr = tryParsePersistedJson(input); + if (pr.ok) result = pr.value; + else console.error('Failure in fromJSON:', pr.error); } return result; @@ -230,6 +244,104 @@

    Source: types/state.js

    return crypto.createHash('sha256').update(value).digest('hex'); } + now () { + return new Date().getTime(); + } + + _sign () { + this.commit(); + } + + /** + * @param {EventEmitter} source Event stream. + * @returns {State} this + */ + trust (source) { + const self = this; + source.on('message', async function handleTrustedMessage (_msg) {}); + source.on('transaction', async function handleTrustedTransaction (transaction) { + self.log('[EVENT:TRANSACTION]', 'apply this transaction to local state:', transaction); + self.log('[PROPOSAL]', 'apply this transaction to local state:', transaction); + }); + return self; + } + + /** + * @param {State} other Peer {@link State} whose `settings.namespace` is appended to `settings.tags`. + * @returns {Number} New length of `settings.tags`. + */ + inherits (other) { + const ns = other && other.settings && other.settings.namespace; + if (ns) this.settings.tags.push(ns); + return this.settings.tags.length; + } + + log (...inputs) { + const t = this.now(); + inputs.unshift(`[${this.constructor.name.toUpperCase()}]`); + inputs.unshift(`[${t}]`); + if (this.settings.verbosity >= 3) { + console.log.apply(null, ['[STATE]'].concat(inputs)); + } + return this.emit('info', ['[STATE]'].concat(inputs)); + } + + error (...inputs) { + const t = this.now(); + inputs.unshift(`[${this.constructor.name.toUpperCase()}]`); + inputs.unshift(`[${t}]`); + if (this.settings.verbose) { + console.error.apply(null, ['[STATE]'].concat(inputs)); + } + return this.emit('error', ['[STATE]'].concat(inputs)); + } + + warn (...inputs) { + const t = this.now(); + inputs.unshift(`[${this.constructor.name.toUpperCase()}]`); + inputs.unshift(`[${t}]`); + if (this.settings.verbose) { + console.warn.apply(null, ['[STATE]'].concat(inputs)); + } + return this.emit('warning', ['[STATE]'].concat(inputs)); + } + + debug (...inputs) { + const t = this.now(); + inputs.unshift(`[${this.constructor.name.toUpperCase()}]`); + inputs.unshift(`[${t}]`); + if (this.settings.verbose) { + console.debug.apply(null, ['[STATE]'].concat(inputs)); + } + return this.emit('debug', ['[STATE]'].concat(inputs)); + } + + async open () { + this.status = 'opened'; + return this; + } + + async close () { + this.status = 'closed'; + return this; + } + + async start () { + this.status = 'starting'; + this['@data'] = this.settings; + await this.open(); + await this.commit(); + this.status = 'started'; + return this; + } + + async stop () { + this.status = 'stopping'; + await this.close(); + this.status = 'stopped'; + return this; + } + async _applyChanges (ops) { try { monitor.applyPatch(this['@data'], ops); @@ -326,7 +438,7 @@

    Source: types/state.js

    * @param {Mixed} [input] Input to serialize. * @return {Buffer} {@link Store}-able blob. */ - serialize (input = this.state, encoding = 'json') { + serialize (input = this.state, _encoding = 'json') { const state = {}; let result = null; @@ -368,7 +480,7 @@

    Source: types/state.js

    } } - return JSON.parse(json(result)); + return parsePersistedJson(json(result)); } /** @@ -516,14 +628,18 @@

    Classes

    Global


    diff --git a/docs/types_store.js.html b/docs/types_store.js.html index c77044042..6e290b17d 100644 --- a/docs/types_store.js.html +++ b/docs/types_store.js.html @@ -33,6 +33,8 @@

    Source: types/store.js

    'use strict';
     
    +const { tryParsePersistedJson } = require('../functions/wireJson');
    +
     // Dependencies
     const { Level } = require('level');
     const crypto = require('crypto');
    @@ -44,17 +46,20 @@ 

    Source: types/store.js

    const Collection = require('./collection'); const Entity = require('./entity'); const Stack = require('./stack'); +const State = require('./state'); /** - * Long-term storage. + * @classdesc Level-backed persistence extending {@link Actor}. Use optional {@link Codec} in <code>settings.codec</code> for + * encrypted values; {@link Store.openEncrypted} matches Hub/shell keystore defaults. Commit/history behavior follows {@link Actor}. + * @class Store + * @extends Actor * @property {Mixed} settings Current configuration. */ class Store extends Actor { /** - * Create an instance of a {@link Store} to manage long-term storage, which is - * particularly useful when building a user-facing {@link Product}. - * @param {Object} [settings={}] configuration object. - * @return {Store} Instance of the Store, ready to start. + * Create an instance of a {@link Store} to manage long-term storage (LevelDB by default). + * @param {Object} [settings={}] configuration object (<code>path</code>, <code>codec</code>, <code>persistent</code>, …). + * @return {Store} Instance of the Store, ready to start. */ constructor (settings = {}) { super(settings); @@ -76,7 +81,9 @@

    Source: types/store.js

    this['@entity'] = { '@type': 'Store', - '@data': {} + '@data': { + addresses: {} + } }; this.keys = {}; @@ -153,16 +160,46 @@

    Source: types/store.js

    console.error('[FABRIC:STORE]', 'Error condition:', err); } - async _setEncrypted (path, value, passphrase = '') { - const secret = value; // TODO: encrypt value - const name = crypto.createHash('sha256').createHash(path).digest('hex'); + async _setEncrypted (path, value, _passphrase = '') { + if (typeof path !== 'string' || !path.length) { + throw new Error('Path is required for encrypted store writes.'); + } + + const plaintext = (typeof value === 'string') ? value : JSON.stringify(value); + let secret = plaintext; + + // Prefer configured codec for at-rest encryption; fallback keeps compatibility. + if (this.codec && typeof this.codec.encode === 'function') { + const encoded = this.codec.encode(plaintext); + const encodedBuffer = Buffer.isBuffer(encoded) + ? encoded + : Buffer.from(String(encoded), 'utf8'); + secret = encodedBuffer.toString('hex'); + } + + const name = this._getPathForKey(path); return this.set(`/secrets/${name}`, secret); } - async _getEncrypted (path, passphrase = '') { - const name = crypto.createHash('sha256').createHash(path).digest('hex'); - const secret = this.get(`/secrets/${name}`); - const decrypted = secret; // TODO: decrypt value + async _getEncrypted (path, _passphrase = '') { + if (typeof path !== 'string' || !path.length) return null; + + const name = this._getPathForKey(path); + const secret = await this.get(`/secrets/${name}`); + if (secret == null) return null; + + let decrypted = secret; + + if (this.codec && typeof this.codec.decode === 'function') { + let payload = secret; + if (!Buffer.isBuffer(payload)) { + const serialized = String(secret); + const isHex = /^[0-9a-fA-F]+$/.test(serialized) && serialized.length % 2 === 0; + payload = Buffer.from(serialized, isHex ? 'hex' : 'utf8'); + } + decrypted = this.codec.decode(payload); + } + return decrypted; } @@ -173,24 +210,24 @@

    Source: types/store.js

    */ async _REGISTER (obj) { const actor = new Actor(obj); - const existing = await this._GET(`/entities/${actor.id}`); + let result = null; - store.log('[STORE]', '_REGISTER', vector.id, vector['@type']); + this.log('[STORE]', '_REGISTER', actor.id, actor.type); try { - let item = await this._GET(`/entities/${vector.id}`); + await this._GET(`/entities/${actor.id}`); } catch (E) { this.warn('[STORE]', '_REGISTER', `Could not read from store:`, E); } try { - await this._SET(`/types/${vector.id}`, vector['@type']); + await this._SET(`/types/${actor.id}`, actor.type); } catch (E) { this.error('Error creating object:', E, obj); } try { - result = await this._SET(`/entities/${vector.id}`, vector['@data']); + result = await this._SET(`/entities/${actor.id}`, actor.toObject()); } catch (E) { this.error('Error creating object:', E, obj); } @@ -236,7 +273,7 @@

    Source: types/store.js

    if (this.settings.verbosity >= 5) console.log('[STORE]', 'Patch result:', result); try { - let action = await this._PUT(key, result); + await this._PUT(key, result); } catch (E) { console.error('Could not modify:', E); } @@ -273,15 +310,6 @@

    Source: types/store.js

    self['@entity']['@data'].addresses[router] = address; let state = new State(value); - let serial = state.serialize(); - let digest = this.sha256(serial); - - // defaults - let actor = null; - let list = null; - let type = null; - let tip = null; - if (!self.db) { await self.open().catch(self._errorHandler.bind(self)); } @@ -298,11 +326,12 @@

    Source: types/store.js

    if (this.settings.verbosity >= 3) console.warn('Creating new collection:', E); } - if (entity) { - try { - entity = JSON.parse(entity); - } catch (E) { - console.warn(`Couldn't parse: ${entity}`, E); + if (entity && (typeof entity === 'string' || Buffer.isBuffer(entity))) { + const text = typeof entity === 'string' ? entity : entity.toString('utf8'); + const pr = tryParsePersistedJson(text); + if (pr.ok) entity = pr.value; + else if (this.settings.verbosity >= 4 || this.settings.debug) { + console.warn(`Couldn't parse entity JSON: ${pr.error.message}`); } } @@ -316,14 +345,22 @@

    Source: types/store.js

    } // Add Element to Collection - let height = origin.push(value); + origin.push(value); // Store the object at an entity locale - let object = await self._PUT(`/entities/${state.id}`, value); + await self._PUT(`/entities/${state.id}`, value); let serialized = await origin.serialize(); + // Keep in-memory collection view in sync for _GET/_PUT call paths. + const existingList = await self._GET(key); + const persistedList = Array.isArray(family) ? family.filter((item) => item != null) : []; + const inMemoryList = Array.isArray(existingList) ? existingList.filter((item) => item != null) : []; + const seedList = inMemoryList.length ? inMemoryList : persistedList; + const nextList = seedList.concat([value]); + await self._PUT(key, nextList); + // Write serialized Collection to disk - let answer = await self.db.put(address, serialized.toString()); + await self.db.put(address, serialized.toString()); } catch (E) { console.log('Could not POST:', key, value, E); return false; @@ -339,11 +376,11 @@

    Source: types/store.js

    if (!list) list = []; let vector = new State(data); let stack = new Stack(list); - let result = stack.push(vector.id); - let actor = await this._REGISTER(data); - let blob = await this._PUT(`/blobs/${vector.id}`, vector['@data']); - let saved = await this._SET(path, stack['@data']); - let commit = await this.commit(); + stack.push(vector.id); + await this._REGISTER(data); + await this._PUT(`/blobs/${vector.id}`, vector['@data']); + await this._SET(path, stack['@data']); + await this.commit(); let output = await this._GET(`/blobs/${vector.id}`); return output; } @@ -371,8 +408,18 @@

    Source: types/store.js

    size = value.length; hash = this.sha256(value); break; + case 'Object': + case 'Array': { + const encoded = JSON.stringify(value); + type = 'JSON'; + size = encoded.length; + hash = this.sha256(encoded); + break; + } default: - console.error('unhandled type:', value.constructor.name); + if (this.settings.verbosity >= 4 || this.settings.debug) { + console.error('unhandled type:', value.constructor.name); + } type = 'Unhandled'; break; } @@ -414,8 +461,40 @@

    Source: types/store.js

    */ async get (key) { const route = await this.getRouteInfo(key); - const result = pointer.get(this._state.content, route.path); - const type = this._state.metadata[route.index].type; + let result; + try { + result = pointer.get(this._state.content, route.path); + } catch (_) { + result = undefined; + } + + // Durable fallback: hydrate from Level when memory has no entry (e.g. after reopen). + if (result === undefined && this.db && this.settings.persistent !== false) { + try { + const raw = await this.db.get(route.path); + let value = raw; + if (Buffer.isBuffer(raw)) { + const text = raw.toString('utf8'); + const pr = tryParsePersistedJson(text); + value = pr.ok ? pr.value : text; + } else if (typeof raw === 'string') { + const pr = tryParsePersistedJson(raw); + value = pr.ok ? pr.value : raw; + } + const info = await this.getDataInfo(value); + this._state.metadata[route.index] = info; + this._state.indices[route.index] = route.pointer; + pointer.set(this._state.content, route.path, value); + return value; + } catch (_) { + return null; + } + } + + if (result === undefined) return null; + + const meta = this._state.metadata[route.index]; + const type = meta && meta.type; let output = null; @@ -442,7 +521,10 @@

    Source: types/store.js

    // This is what defines our key => value store. // All functions can be run as a map of an original input vector, allowing // binary scoping across trees of varying complexity. - const hash = this.sha256(value); + const hashInput = (typeof value === 'string' || Buffer.isBuffer(value)) + ? value + : JSON.stringify(value); + const hash = this.sha256(hashInput); const actor = new Actor({ type: 'FabricDocument', content: data, @@ -459,6 +541,18 @@

    Source: types/store.js

    this.commit(); + // Persist path → value when Level is open (survives stop/start reopen). + if (this.db && this.settings.persistent !== false) { + try { + const payload = (typeof value === 'string') + ? value + : (Buffer.isBuffer(value) ? value : JSON.stringify(value)); + await this.db.put(route.path, payload); + } catch (E) { + console.error('[FABRIC:STORE]', 'Could not persist key:', route.path, E); + } + } + return this.get(key); } @@ -634,7 +728,6 @@

    Source: types/store.js

    async start () { if (this.settings.verbosity >= 3) console.log('[FABRIC:STORE]', 'Starting:', this.settings.path); this.status = 'starting'; - let keys = null; try { await this.open(); @@ -681,14 +774,18 @@

    Classes

    Global


    diff --git a/docs/types_token.js.html b/docs/types_token.js.html index 0895b328f..89eb704f1 100644 --- a/docs/types_token.js.html +++ b/docs/types_token.js.html @@ -35,7 +35,7 @@

    Source: types/token.js

    // Dependencies const bitcoin = require('bitcoinjs-lib'); -const schnorr = require('bip-schnorr'); +const { tryParseWireJson } = require('../functions/wireJson'); // Fabric Types const Key = require('./key'); @@ -130,6 +130,11 @@

    Source: types/token.js

    iat, exp }; + // Optional context (e.g. { contractId }). Explicit `ctx: null` suppresses settings.ctx. + const ctx = Object.prototype.hasOwnProperty.call(options, 'ctx') + ? options.ctx + : this.settings.ctx; + if (ctx != null && typeof ctx === 'object') payload.ctx = ctx; const payloadStr = JSON.stringify(payload); const payloadB64 = Token.base64UrlEncode(payloadStr); const signature = this.issuer.sign(payloadStr); @@ -150,9 +155,11 @@

    Source: types/token.js

    if (parts.length !== 2) return null; try { const payloadStr = Token.base64UrlDecode(parts[0]); - const payload = JSON.parse(payloadStr); + const pr = tryParseWireJson(payloadStr); + if (!pr.ok) return null; + const payload = pr.value; const sig = Token.base64UrlDecodeToBuffer(parts[1]); - if (!payload || !payload.iss || payload.exp == null) return null; + if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !payload.iss || payload.exp == null) return null; if (Date.now() / 1000 > payload.exp) return null; const ourIss = verificationKey.public ? verificationKey.public.encodeCompressed('hex') : verificationKey.keypair.getPublic(true, 'hex'); if (payload.iss !== ourIss) return null; @@ -165,7 +172,6 @@

    Source: types/token.js

    static fromString (input) { const parts = input.split('.'); - const headers = parts[0]; const payload = parts[1]; const signature = parts[2]; const inner = Token.base64UrlDecode(payload); @@ -208,9 +214,9 @@

    Source: types/token.js

    // Encodings const encodedHeader = Token.base64UrlEncode(JSON.stringify(header)); const encodedPayload = Token.base64UrlEncode(JSON.stringify(payload)); - const signature = bitcoin.crypto.sha256( + const signature = Buffer.from(bitcoin.crypto.sha256( Buffer.from(`${encodedHeader}.${encodedPayload}.${secret}`) - ); + )); return [ encodedHeader, @@ -220,15 +226,20 @@

    Source: types/token.js

    } sign () { - // Sign the capability using the private key - const hash = bitcoin.crypto.sha256(this.capability); - this.signature = schnorr.sign(this.issuer.privateKey, hash); + const hash = Buffer.from(bitcoin.crypto.sha256(Buffer.from(this.capability, 'utf8'))); + if (!this.issuer || typeof this.issuer.signSchnorrHash !== 'function') { + throw new Error('Token.sign requires issuer Key with private material'); + } + this.signature = this.issuer.signSchnorrHash(hash); } verify () { - // Verify the signature using the public key - const hash = bitcoin.crypto.sha256(this.capability); - return schnorr.verify(this.issuer.publicKey, hash, this.signature); + const hash = Buffer.from(bitcoin.crypto.sha256(Buffer.from(this.capability, 'utf8'))); + if (!this.issuer || typeof this.issuer.verifySchnorrHash !== 'function') { + return false; + } + if (!this.signature) return false; + return this.issuer.verifySchnorrHash(hash, this.signature); } add (other) { @@ -261,14 +272,18 @@

    Classes

    Global


    diff --git a/docs/types_tree.js.html b/docs/types_tree.js.html index 71f8f0815..aaea55a59 100644 --- a/docs/types_tree.js.html +++ b/docs/types_tree.js.html @@ -75,22 +75,33 @@

    Source: types/tree.js

    } /** - * Add a leaf to the tree. - * @param {String} leaf Leaf to add to the tree. + * Add a leaf to the tree (accumulates into `settings.leaves`). + * @param {String|Buffer} leaf Leaf to add to the tree. * @returns {Tree} Instance of the tree. */ addLeaf (leaf = '') { - this._tree = new MerkleTree(this.settings.leaves.concat([ - leaf - ]), Hash256.digest, { + this.settings.leaves = (this.settings.leaves || []).concat([leaf]); + this._tree = new MerkleTree(this.settings.leaves, Hash256.digest, { isBitcoinTree: true }); + this._state = this._state || {}; + this._state.root = this.root; this.emit('leaf', leaf); return this; } + /** + * Hex encoding of {@link Tree#root} (empty string when the tree has no root bytes). + * @returns {string} + */ + get rootHex () { + const root = this.root; + if (!root || !root.length) return ''; + return Buffer.isBuffer(root) ? root.toString('hex') : String(root); + } + /** * Get a list of the {@link Tree}'s leaves. * @returns {Array} A list of the {@link Tree}'s leaves. @@ -114,14 +125,18 @@

    Classes

    Global


    diff --git a/docs/types_vector.js.html b/docs/types_vector.js.html index 38a65b724..80547aa2a 100644 --- a/docs/types_vector.js.html +++ b/docs/types_vector.js.html @@ -33,98 +33,23 @@

    Source: types/vector.js

    'use strict';
     
    -const Scribe = require('./scribe');
    -const Stack = require('./stack');
    -
    -class Vector extends Scribe {
    +const EventEmitter = require('events');
    +
    +/**
    + * @classdesc Lightweight <strong>event sink</strong> for instruction-stream and VM-adjacent signals.
    + * Former {@link State}-backed fields (<code>script</code>, <code>stack</code>, <code>known</code>, serialization helpers)
    + * live on {@link Machine} and {@link State} / {@link Fabric#push} instead.
    + * @class Vector
    + * @extends EventEmitter
    + */
    +class Vector extends EventEmitter {
       /**
    -   * An "Initialization" Vector.
    -   * @param       {Object} - Input state (will map to `@data`.)
    -   * @constructor
    +   * @param {Object} [options] Optional emitter options (e.g. <code>captureRejections</code>).
        */
    -  constructor (origin) {
    -    super(origin);
    -
    -    this.settings = Object.assign({}, origin);
    -
    -    this.known = {};
    -    this.registry = {};
    -
    -    this.stack = new Stack();
    -    this.script = [];
    -
    -    this.status = 'initialized';
    -
    +  constructor (options) {
    +    super(options);
         return this;
       }
    -
    -  static fromObjectString (input = '') {
    -    if (!input) throw new Error('Must provide input.');
    -    if (typeof input !== 'string') input = JSON.stringify(input);
    -    let result = [];
    -    let object = JSON.parse(input);
    -
    -    for (let i in object) {
    -      let element = object[i];
    -
    -      if (element instanceof Array) {
    -        element = Buffer.from(element);
    -      } else {
    -        element = Buffer.from(element.data);
    -      }
    -
    -      result.push(element);
    -    }
    -
    -    return result;
    -  }
    -
    -  /**
    -   * _serialize is a placeholder, should be discussed.
    -   * @param {String} input - What to serialize.  Defaults to `this.state`.
    -   * @return {String} - resulting string [JSON-encoded version of the local `@data` value.]
    -   */
    -  _serialize (input) {
    -    return this.toString(input);
    -  }
    -
    -  _deserialize (input) {
    -    return this.fromString(input);
    -  }
    -
    -  // TODO: standardize on a serialization format
    -  fromString (input) {
    -    return JSON.parse(input);
    -  }
    -
    -  toObject () {
    -    let object = {};
    -    for (let property in this['@data']) {
    -      if (property.charAt(0) !== '@') {
    -        object[property] = this['@data'][property];
    -      }
    -    }
    -    return object;
    -  }
    -
    -  /**
    -   * Render the output to a {@link String}.
    -   * @param  {Mixed} input Arbitrary input.
    -   * @return {String}
    -   */
    -  toString (input) {
    -    if (!input) input = this.state;
    -    // TODO: standardize on a serialization format
    -    return JSON.stringify(input);
    -  }
    -
    -  validate (input) {
    -    return true;
    -  }
    -
    -  async step () {
    -    return super.compute((this.clock | 0));
    -  }
     }
     
     module.exports = Vector;
    @@ -141,14 +66,18 @@ 

    Classes

    Global


    diff --git a/docs/types_wallet.js.html b/docs/types_wallet.js.html index d60eee844..1e024c27f 100644 --- a/docs/types_wallet.js.html +++ b/docs/types_wallet.js.html @@ -34,15 +34,17 @@

    Source: types/wallet.js

    'use strict';
     
     // External Dependencies
    -const BN = require('bn.js');
     const merge = require('lodash.merge');
    -const networks = require('bitcoinjs-lib/src/networks');
    +const { networks: bitcoinNetworks } = require('bitcoinjs-lib');
    +/** Fabric settings use `mainnet`; bitcoinjs-lib 7 names that network `bitcoin`. */
    +const networks = Object.assign({ mainnet: bitcoinNetworks.bitcoin }, bitcoinNetworks);
     
     // Mnemonics
     const ecc = require('./ecc');
    -const BIP32 = require('bip32').default;
    +const BIP32 = require('../functions/bip32').default;
    +const { DEFAULT_NETWORK: BIP32_DEFAULT_NETWORK } = require('../functions/bip32');
     const bip32 = new BIP32(ecc);
    -const bip39 = require('bip39');
    +const bip39 = require('../functions/bip39');
     
     // Types
     const Key = require('./key');
    @@ -54,6 +56,11 @@ 

    Source: types/wallet.js

    const Service = require('./service'); const Secret = require('./secret'); const State = require('./state'); +const { + buildWatchSet, + classifyWalletTransaction, + BITCOIN_MESSAGE_TYPES +} = require('../functions/walletTransactionWatch'); /** * Manage keys and track their balances. @@ -123,6 +130,16 @@

    Source: types/wallet.js

    this.txids = new Collection(); this.outputs = new Collection(); + // Multi-seed keyring: primary `this.key` plus additional seeds in `_seeds`. + // Watch set covers addresses derived from every loaded seed/key. + this._seeds = new Map(); + this._watch = { + addresses: new Map(), + paymentHashes: new Map() + }; + /** @type {Set.<string>} */ + this._emittedWalletTxKeys = new Set(); // txid:kind already emitted (block + mempool may both see the same tx) + // Encrypted Storage this.secrets = new Collection({ methods: { @@ -157,9 +174,18 @@

    Source: types/wallet.js

    outputs: {}, addressIndex: 0, addresses: {}, - lastUsedIndex: -1 + lastUsedIndex: -1, + seeds: {} }); + // Register the primary key into the shared key collection + watch window. + try { + if (this.key && this.key.pubkey) { + this.loadKey(this.key, ['primary']); + } + this._registerDerivedAddresses(this.key, { seedId: 'primary', labels: ['primary'] }); + } catch (_) { /* primary may be pubkey-only in some tests */ } + return this; } @@ -188,10 +214,10 @@

    Source: types/wallet.js

    * @param {String} passphrase BIP 39 passphrase for key derivation. * @returns {FabricSeed} The seed object. */ - static createSeed (passphrase = '') { + static createSeed (_passphrase = '') { const mnemonic = bip39.generateMnemonic(); const seed = bip39.mnemonicToSeedSync(mnemonic); - const root = bip32.fromSeed(seed); + const root = bip32.fromSeed(seed, BIP32_DEFAULT_NETWORK); return { phrase: mnemonic, master: root.privateKey.toString('hex'), @@ -206,9 +232,18 @@

    Source: types/wallet.js

    * @returns {Wallet} Instance of the wallet. */ static fromSeed (seed) { + if (!seed || typeof seed !== 'object' || typeof seed.phrase !== 'string') { + throw new Error('Seed object must provide a mnemonic phrase string.'); + } + + const phrase = seed.phrase.trim().replace(/\s+/g, ' '); + if (!bip39.validateMnemonic(phrase)) { + throw new Error('Seed phrase must be a valid BIP39 mnemonic.'); + } + return new Wallet({ key: { - seed: seed.phrase, + seed: phrase, passphrase: '' } }); @@ -258,7 +293,7 @@

    Source: types/wallet.js

    }; } - loadTransaction (transaction, labels = []) { + loadTransaction (transaction, _labels = []) { if (!transaction) throw new Error('You must provide a transaction.'); if (!transaction.id) throw new Error('The transaction must have a "id" property.'); @@ -283,6 +318,279 @@

    Source: types/wallet.js

    return this; } + /** + * Register a key with optional labels. + * Accepts a Key instance, pubkey hex string, or object-like key input. + * @param {Key|String|Object} input Key material to load. + * @param {Array<String>} [labels=[]] Optional labels. + * @returns {Object} Stored key descriptor. + */ + loadKey (input, labels = []) { + let key = null; + + if (input instanceof Key) { + key = input; + } else if (input && typeof input === 'object' && (input.pubkey || input.public)) { + key = this.publicKeyFromString(input.pubkey || input.public); + } else if (typeof input === 'string' || Buffer.isBuffer(input) || (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array)) { + key = this.publicKeyFromString(input); + } else if (input && typeof input === 'object') { + key = this.publicKeyFromString(input); + } else { + throw new Error('Invalid key input.'); + } + + const pubkey = key.pubkey; + if (!pubkey || typeof pubkey !== 'string') throw new Error('Could not derive pubkey from key input.'); + + const item = { + pubkey, + labels: Array.isArray(labels) ? labels : [] + }; + + this._state.keys[pubkey] = item; + this._state.content.keys[pubkey] = item; + this.keys.set(`/${pubkey}`, item); + + try { + if (typeof key.toBitcoinAddress === 'function') { + const addr = key.toBitcoinAddress(); + if (addr) this.watchAddress(addr, { pubkey, labels: item.labels, source: 'loadKey' }); + } + } catch (_) { /* watch is best-effort */ } + + return item; + } + + /** + * Load an additional BIP39 seed into this wallet's key collection (does not + * replace the primary `this.key`). Addresses derived from the seed are watched. + * @param {string} phrase BIP39 mnemonic + * @param {Array<string>} [labels=[]] + * @param {object} [opts] + * @param {number} [opts.addressWindow] receive indices to watch (default gapLimit) + * @returns {{ seedId: string, xpub: string, labels: string[] }} + */ + loadSeed (phrase, labels = [], opts = {}) { + if (typeof phrase !== 'string') throw new Error('Seed must be a string.'); + const trimmed = phrase.trim().replace(/\s+/g, ' '); + if (!bip39.validateMnemonic(trimmed)) { + throw new Error('Seed must be a valid BIP39 mnemonic phrase.'); + } + const key = new Key({ + seed: trimmed, + network: this.settings.network, + passphrase: opts.passphrase || '' + }); + const xpub = key.xpub || key.pubkey; + if (!xpub) throw new Error('Could not derive xpub from seed.'); + const seedId = Hash256.digest(Buffer.from(String(xpub), 'utf8')).toString('hex').slice(0, 32); + const labelList = Array.isArray(labels) ? labels.slice() : []; + this._seeds.set(seedId, { key, xpub, labels: labelList }); + this._state.seeds[seedId] = { xpub, labels: labelList, loadedAt: Date.now() }; + try { + this.loadKey(key, labelList.concat(['seed', seedId])); + } catch (_) { /* pubkey registration may fail for some key modes */ } + this._registerDerivedAddresses(key, { + seedId, + labels: labelList, + count: opts.addressWindow != null ? Number(opts.addressWindow) : this.settings.gapLimit + }); + this.emit('seedLoaded', { seedId, xpub, labels: labelList }); + return { seedId, xpub, labels: labelList }; + } + + /** + * @returns {Array<{ seedId: string, xpub: string, labels: string[] }>} + */ + listSeeds () { + const rows = []; + for (const [seedId, row] of this._seeds.entries()) { + rows.push({ seedId, xpub: row.xpub, labels: row.labels || [] }); + } + if (this.key && this.key.xpub) { + rows.unshift({ + seedId: 'primary', + xpub: this.key.xpub, + labels: ['primary'] + }); + } + return rows; + } + + /** + * Watch a Bitcoin address for wallet-associated transactions. + * @param {string} address + * @param {object} [meta] + */ + watchAddress (address, meta = {}) { + const addr = String(address || '').trim(); + if (!addr) return null; + const prev = this._watch.addresses.get(addr) || {}; + const next = Object.assign({}, prev, meta, { address: addr }); + this._watch.addresses.set(addr, next); + if (!this._state.addresses[addr]) { + this._state.addresses[addr] = { + index: meta.index != null ? meta.index : -1, + used: false, + seedId: meta.seedId || null + }; + } + return next; + } + + /** + * Watch an HTLC payment hash (SHA256 preimage) so claim witnesses are detected. + * @param {string} paymentHashHex + * @param {object} [meta] + */ + watchPaymentHash (paymentHashHex, meta = {}) { + const h = String(paymentHashHex || '').trim().toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(h)) throw new Error('paymentHashHex must be 64 hex chars'); + const prev = this._watch.paymentHashes.get(h) || {}; + const next = Object.assign({}, prev, meta, { paymentHashHex: h }); + this._watch.paymentHashes.set(h, next); + return next; + } + + /** + * Watch an inventory HTLC offer (payment address + payment hash + optional settlement). + * @param {object} htlc + */ + watchHtlc (htlc = {}) { + const address = htlc.paymentAddress || htlc.address; + const paymentHashHex = htlc.paymentHashHex || htlc.contentHashHex; + if (address) { + this.watchAddress(address, { + kind: 'htlc', + settlementId: htlc.settlementId || null, + paymentHashHex: paymentHashHex || null, + documentId: htlc.documentId || null + }); + } + if (paymentHashHex) { + this.watchPaymentHash(paymentHashHex, { + settlementId: htlc.settlementId || null, + documentId: htlc.documentId || null, + address: address || null + }); + } + return this.getWatchSet(); + } + + /** + * @returns {object} watch set snapshot + */ + getWatchSet () { + return buildWatchSet(this); + } + + /** + * Derive and watch a window of addresses for a key (all common script types). + * @param {Key} key + * @param {object} [opts] + */ + _registerDerivedAddresses (key, opts = {}) { + if (!key || typeof key.deriveAddress !== 'function') return; + const count = Math.max(1, Math.round(Number(opts.count != null ? opts.count : this.settings.gapLimit) || 20)); + const seedId = opts.seedId || null; + const labels = opts.labels || []; + const types = ['p2pkh', 'p2wpkh', 'p2tr']; + for (let i = 0; i < count; i++) { + for (const type of types) { + try { + const der = key.deriveAddress(i, 0, type); + const addr = der && (der.address || (typeof der === 'string' ? der : null)); + if (addr) { + this.watchAddress(addr, { seedId, index: i, type, labels, change: 0 }); + } + } catch (_) { /* script type may be unsupported on this network */ } + } + } + } + + /** + * Ingest a Bitcoin transaction (verbose RPC object or raw hex) and emit when related. + * @param {object|string} txOrHex + * @param {object} [context] { tip, height, source } + * @returns {object} classification + */ + ingestBitcoinTransaction (txOrHex, context = {}) { + const watchSet = this.getWatchSet(); + const classified = classifyWalletTransaction(txOrHex, watchSet); + if (!classified.related) return classified; + + const txid = classified.txid; + const emitKey = txid ? `${txid}:${classified.kind}` : null; + if (emitKey && this._emittedWalletTxKeys.has(emitKey)) { + return Object.assign({}, classified, { duplicate: true }); + } + if (emitKey) { + this._emittedWalletTxKeys.add(emitKey); + if (this._emittedWalletTxKeys.size > 4000) { + // Bound memory: drop oldest half by recreating from the newest entries. + this._emittedWalletTxKeys = new Set([...this._emittedWalletTxKeys].slice(-2000)); + } + } + + if (txid) { + this._state.transactions[txid] = Object.assign({}, classified, { + tip: context.tip || null, + height: context.height != null ? context.height : null, + source: context.source || 'ingest', + seenAt: Date.now() + }); + this._state.content.transactions[txid] = this._state.transactions[txid]; + for (const addr of classified.matchedAddresses) { + if (this._state.addresses[addr]) { + this._state.addresses[addr].used = true; + this._state.addresses[addr].lastUsed = Date.now(); + } + } + } + + const event = Object.assign({}, classified, { + tip: context.tip || null, + height: context.height != null ? context.height : null, + source: context.source || 'ingest' + }); + this.emit('walletTransaction', event); + if (classified.kind === 'htlc_claim') this.emit('htlcClaim', event); + else if (classified.kind === 'htlc_funding') this.emit('htlcFunding', event); + else if (classified.kind === 'htlc_refund') this.emit('htlcRefund', event); + else if (classified.kind === 'receive' || classified.kind === 'payment' || classified.kind === 'coinbase') { + this.emit('payment', event); + } + return classified; + } + + /** + * Process a new block tip (optionally with verbosity-2 `tx` / `transactions`). + * @param {object} block + * @returns {object[]} related classifications + */ + ingestBitcoinBlock (block = {}) { + const tip = block.tip || block.hash || block.id || null; + const height = block.height != null ? block.height : null; + const txs = block.tx || block.transactions || []; + const out = []; + const seen = new Set(); + for (const tx of txs) { + const classified = this.ingestBitcoinTransaction(tx, { tip, height, source: 'block' }); + if (classified && classified.related && classified.txid && !seen.has(classified.txid)) { + seen.add(classified.txid); + out.push(classified); + } + } + this.emit('block', { + tip, + height, + related: out.length, + messageTypes: BITCOIN_MESSAGE_TYPES + }); + return out; + } + /** * Start the wallet, including listening for transactions. */ @@ -299,8 +607,17 @@

    Source: types/wallet.js

    this.marshall.agents.push(listener); emitter.on('transaction', async function trustedHandler (msg) { - if (this.settings.verbosity >= 5) console.log('[FABRIC:WALLET]', 'Received transaction from trusted event emitter:', msg); - await wallet.addTransactionToWallet(msg); + if (wallet.settings.verbosity >= 5) console.log('[FABRIC:WALLET]', 'Received transaction from trusted event emitter:', msg); + if (msg && (msg.hex || msg.txid || msg.vout)) { + wallet.ingestBitcoinTransaction(msg, { source: 'trusted-transaction' }); + } else { + await wallet.addTransactionToWallet(msg); + } + }); + + emitter.on('block', async function trustedBlockHandler (block) { + if (wallet.settings.verbosity >= 5) console.log('[FABRIC:WALLET]', 'Received block from trusted event emitter:', block); + await wallet.ingestBitcoinBlock(block); }); return this; @@ -320,44 +637,79 @@

    Source: types/wallet.js

    case 'ServiceMessage': return this._processServiceMessage(msg['@data']); default: - return console.warn('[FABRIC:WALLET]', `Unhandled message type: ${msg['@type']}`); + if (this.settings.verbosity >= 4 || this.settings.debug) { + this.emit('warning', `[FABRIC:WALLET] Unhandled message type: ${msg['@type']}`); + } + return null; } } async _processServiceMessage (msg) { switch (msg['@type']) { case 'BitcoinBlock': - this.processBitcoinBlock(msg['@data']); + case 'BitcoinBlockHash': + this.processBitcoinBlock(msg['@data'] || msg); break; case 'BitcoinTransaction': - // TODO: validate destination is this wallet - this.addTransactionToWallet(msg['@data']); + case 'BitcoinTransactionHash': + if (msg['@data'] && (msg['@data'].hex || msg['@data'].content)) { + const payload = msg['@data'].hex || msg['@data'].content || msg['@data']; + this.ingestBitcoinTransaction(payload, { source: msg['@type'] }); + } else { + this.addTransactionToWallet(msg['@data']); + } break; default: - return console.warn('[FABRIC:WALLET]', `Unhandled message type: ${msg['@type']}`); + if (this.settings.verbosity >= 4 || this.settings.debug) { + this.emit('warning', `[FABRIC:WALLET] Unhandled message type: ${msg['@type']}`); + } + return null; } } async processBitcoinBlock (block) { if (this.settings.verbosity >= 4) console.log('[FABRIC:WALLET]', 'Processing block:', block); - if (!block.block) return 0; - for (let i = 0; i < block.block.hashes.length; i++) { - const txid = block.block.hashes[i].toString('hex'); - // ATTN: Eric - // TODO: process transaction - console.log('found txid in block:', txid); + if (!block) return []; + // Verbosity-2 style: full txs present + if (block.tx || block.transactions) { + return this.ingestBitcoinBlock(block); } + // Legacy SPV-ish: block.block.hashes + if (block.block && Array.isArray(block.block.hashes)) { + const tip = block.hash || block.tip || null; + const related = []; + for (let i = 0; i < block.block.hashes.length; i++) { + const txid = block.block.hashes[i].toString('hex'); + if (this.settings.verbosity >= 5) console.log('found txid in block:', txid); + related.push({ txid, related: false, kind: 'unknown' }); + } + this.emit('block', { tip, related: 0, txids: related.map((r) => r.txid) }); + return related; + } + // Tip-only notification from ZMQ hashblock — still emit so listeners wake up. + return this.ingestBitcoinBlock(block); + } + + async _scanBlockForTransactions (block) { + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('[AUDIT]', 'Scanning block for transactions:', block); + } + return this.ingestBitcoinBlock(block || {}); } async _attachTXID (txid) { - // TODO: check that `txid` is a proper TXID + if (typeof txid !== 'string' || !/^[0-9a-fA-F]{64}$/.test(txid)) { + throw new Error('txid must be a 64-character hex string'); + } let txp = await this.txids.create(txid); if (this.settings.verbosity >= 5) console.log('[AUDIT]', `Attached TXID ${txid} to Wallet ID ${this.id}, result:`, txp); return txp; } async _handleFabricTransaction (tx) { - console.log('[FABRIC:WALLET]', 'Handling Fabric Transaction:', tx); + if (this.settings.verbosity >= 5) { + console.log('[FABRIC:WALLET]', 'Handling Fabric Transaction:', tx); + } } async addTransactionToWallet (transaction) { @@ -455,6 +807,10 @@

    Source: types/wallet.js

    return Address.fromScripthash(redeemScript.hash160()); } + /** + * @deprecated Legacy bcoin-style HTLC for channels — not the document-market P2TR profile. + * Use {@link Wallet.buildInventoryHtlcP2tr} / `@fabric/core/functions/inventoryHtlc` instead. + */ async createHTLC (contract) { // if (!contract.asset) throw new Error('Contract parameter "asset" is required.'); if (!contract.amount) throw new Error('Contract parameter "amount" is required.'); @@ -464,7 +820,9 @@

    Source: types/wallet.js

    // sha256 // -> pubkey contract.counterparty = await this.ring.getPublicKey(); - console.log('contract counterparty artificially generated:', contract.counterparty); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('contract counterparty artificially generated:', contract.counterparty); + } } let leftover = contract.amount % this.settings.decimals; @@ -472,18 +830,21 @@

    Source: types/wallet.js

    let partials = []; // TODO: remove short-circuit - let cb = await this._generateFakeCoinbase(contract.amount); - let mtx = new MTX(); + await this._generateFakeCoinbase(contract.amount); let script = new Script(); let secret = await this.generateSecret(); let image = Buffer.from(secret.hash); - console.log('secret generated:', secret); - console.log('image of secret:', image); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('secret generated:', secret); + console.log('image of secret:', image); + } let refund = await this.ring.getPublicKey(); - console.log('refund:', refund); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('refund:', refund); + } script.pushSym('OP_IF'); script.pushSym('OP_SHA256'); @@ -505,8 +866,10 @@

    Source: types/wallet.js

    partials.push(script); } - console.log('parts:', partials); - console.log('leftover:', leftover); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('parts:', partials); + console.log('leftover:', leftover); + } let entity = new Actor({ comment: 'List of transactions to validate.', @@ -523,7 +886,9 @@

    Source: types/wallet.js

    const entity = await this.secrets.create({ hash: secret.hash }); - console.log('created secret:', entity); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('created secret:', entity); + } return entity; } @@ -531,13 +896,11 @@

    Source: types/wallet.js

    if (!address) throw new Error(`Parameter "address" is required.`); if (!amount) throw new Error(`Parameter "amount" is required.`); - let bn = new BN(amount + '', 10); // TODO: labeled keypairs - let clean = await this.generateCleanKeyPair(); let change = await this.generateCleanKeyPair(); let mtx = new MTX(); - let cb = await this._generateFakeCoinbase(amount); + await this._generateFakeCoinbase(amount); mtx.addOutput({ address: address, @@ -571,14 +934,13 @@

    Source: types/wallet.js

    balanceFromState (state) { if (!state.transactions) throw new Error('State does not provide a `transactions` property.'); if (!state.transactions.length) return 0; - return state.transactions.reduce((acc, obj, i) => { + return state.transactions.reduce((acc, obj, _i) => { if (!acc.value) acc.value = 0; acc.value += obj.value; }); } getFeeForInput (coin, address, keyring, rate) { - let fundingTarget = 100000000; // 1 BTC (arbitrary for purposes of this function) let testMTX = new MTX(); // TODO: restore swap code, abstract input types @@ -601,7 +963,9 @@

    Source: types/wallet.js

    } _handleWalletTransaction (tx) { - console.log('[BRIDGE:WALLET]', 'incoming transaction:', tx); + if (this.settings.verbosity >= 5) { + console.log('[BRIDGE:WALLET]', 'incoming transaction:', tx); + } } _getDepositAddress () { @@ -642,8 +1006,10 @@

    Source: types/wallet.js

    */ async _sign (tx) { let signature = await tx.sign(this.keyring); - console.log('signing tx:', tx); - console.log('signing sig:', signature); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('signing tx:', tx); + console.log('signing sig:', signature); + } return Object.assign({}, tx, { signature }); } @@ -671,7 +1037,9 @@

    Source: types/wallet.js

    } async _createFromFreshSeed (passphrase = '') { - console.log('creating fresh seed with passphrase:', passphrase); + if (this.settings.verbosity >= 5) { + console.log('creating fresh seed with passphrase:', passphrase ? '[REDACTED]' : ''); + } const key = new Key({ passphrase: passphrase }); return { phrase: key.mnemonic, @@ -682,14 +1050,21 @@

    Source: types/wallet.js

    } async _importSeed (seed) { - let mnemonic = new Mnemonic(seed); - return this._loadSeed(mnemonic.toString()); + if (typeof seed !== 'string') { + throw new Error('Seed must be a string.'); + } + + const phrase = seed.trim().replace(/\s+/g, ' '); + if (!bip39.validateMnemonic(phrase)) { + throw new Error('Seed must be a valid BIP39 mnemonic phrase.'); + } + + return this._loadSeed(phrase); } async _getBondAddress () { await this._load(); - let script = new Script(); let clean = await this.generateCleanKeyPair(); if (this.settings.verbosity >= 5) console.log('[AUDIT]', 'getting bond address, clean:', clean); @@ -709,13 +1084,14 @@

    Source: types/wallet.js

    async _getSpendableOutput (target, amount = 0) { let self = this; - let key = null; let out = null; let mtx = new MTX(); await this._load(); - console.log('funding transaction with coins:', this._state.utxos); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('funding transaction with coins:', this._state.utxos); + } // INSERT 1 Output mtx.addOutput({ @@ -729,10 +1105,11 @@

    Source: types/wallet.js

    changeAddress: self.ring.getAddress() }); - console.log('out:', out); - - console.trace('created mutable transaction:', mtx); - console.trace('created immutable transaction:', mtx.toTX()); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('out:', out); + console.trace('created mutable transaction:', mtx); + console.trace('created immutable transaction:', mtx.toTX()); + } return { tx: mtx.toTX(), @@ -783,18 +1160,15 @@

    Source: types/wallet.js

    return inputRefund; } - async _scanBlockForTransactions (block) { - console.log('[AUDIT]', 'Scanning block for transactions:', block); - let found = []; - } - async _scanChainForTransactions (chain) { - console.log('[AUDIT]', 'Scanning chain for transactions:', chain); + if (this.settings.verbosity >= 5 || this.settings.debug) { + console.log('[AUDIT]', 'Scanning chain for transactions:', chain); + } let transactions = []; for (let i = 0; i < chain.blocks.length; i++) { - transactions.concat(await this._scanBlockForTransactions(chain.blocks[i])); + transactions = transactions.concat(await this._scanBlockForTransactions(chain.blocks[i])); } return transactions; @@ -912,8 +1286,23 @@

    Source: types/wallet.js

    } async _loadSeed (seed) { - this.settings.key = { seed }; - await this._load(); + if (typeof seed !== 'string') { + throw new Error('Seed must be a string.'); + } + + const phrase = seed.trim().replace(/\s+/g, ' '); + if (!bip39.validateMnemonic(phrase)) { + throw new Error('Seed must be a valid BIP39 mnemonic phrase.'); + } + + this.settings.key = { seed: phrase }; + if (typeof this._load === 'function') { + await this._load(); + } else { + // Legacy fallback for runtimes that initialize directly from key settings. + this.key = new Key({ seed: phrase }); + this.seed = this.key.seed; + } return this.seed; } @@ -981,6 +1370,36 @@

    Source: types/wallet.js

    } return addresses; } + + /** + * Envelope (legacy / unsealed) payment hash. Prefer {@link Wallet.resolveDocumentContentHashHex} + * when sealed meta or a content key may apply. + * @param {string} documentId + * @param {object} parsed Whitelisted document fields (see {@link Peer#_buildDocumentParsedForPublish}). + * @returns {string} + */ + static purchaseContentHashHex (documentId, parsed) { + return require('../functions/documentPaymentHash').purchaseContentHashHex(documentId, parsed); + } + + /** + * Single path for buy / HTLC `contentHashHex` (sealed | envelope | blob). + * @param {object} opts see {@link module:functions/documentPaymentHash.resolveDocumentContentHashHex} + * @returns {{ contentHashHex: string, binding: string }} + */ + static resolveDocumentContentHashHex (opts) { + return require('../functions/documentPaymentHash').resolveDocumentContentHashHex(opts); + } + + /** @see {@link module:functions/inventoryHtlc.buildInventoryHtlcP2tr} */ + static buildInventoryHtlcP2tr (opts) { + return require('../functions/inventoryHtlc').buildInventoryHtlcP2tr(opts); + } + + /** @see {@link module:functions/inventoryHtlc.buildHtlcFundingHints} */ + static buildHtlcFundingHints (opts) { + return require('../functions/inventoryHtlc').buildHtlcFundingHints(opts); + } } module.exports = Wallet; @@ -997,14 +1416,18 @@

    Classes

    Global


    diff --git a/docs/types_witness.js.html b/docs/types_witness.js.html index 635ab0d71..cc62c8086 100644 --- a/docs/types_witness.js.html +++ b/docs/types_witness.js.html @@ -35,8 +35,6 @@

    Source: types/witness.js

    const crypto = require('crypto'); const { secp256k1 } = require('@noble/curves/secp256k1.js'); -const Key = require('./key'); - class Witness { constructor (settings = {}) { this.settings = Object.assign({ @@ -48,7 +46,7 @@

    Source: types/witness.js

    this.buffer = Buffer.alloc(32 * 256); this._state = { data: this.settings.data - } + }; if (settings && settings.keypair) { if (settings.keypair.private) { @@ -112,7 +110,7 @@

    Source: types/witness.js

    return { private: this.keypair.privateKey, public: this.keypair.publicKey - } + }; } _usePrivateKey (key) { @@ -141,7 +139,7 @@

    Source: types/witness.js

    return this; } - _fromBitcoinSignature (signature = {}) { + _fromBitcoinSignature (_signature = {}) { } @@ -209,14 +207,18 @@

    Classes

    Global


    diff --git a/docs/types_worker.js.html b/docs/types_worker.js.html index c88dab4c7..e585ee1f6 100644 --- a/docs/types_worker.js.html +++ b/docs/types_worker.js.html @@ -33,9 +33,6 @@

    Source: types/worker.js

    'use strict';
     
    -const Collection = require('./collection');
    -const EncryptedPromise = require('./promise');
    -const Entity = require('./entity');
     const Machine = require('./machine');
     const Router = require('./router');
     const Service = require('./service');
    @@ -103,14 +100,18 @@ 

    Classes

    Global