[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FJ0YR1SFMNNG5M1TT40SDS":3,"skill-same:01M6FJ0YR1SFMNNG5M1TT40SDS":48},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":21,"linkOnly":22,"markdownContent":23,"markdownBody":23,"frontMatter":24,"refName":25,"commitSha":26,"skillDirectory":27,"forksCount":28,"repositoryHtmlUrl":29,"repositoryHomepageUrl":30,"ownerHtmlUrl":31,"ownerBlogUrl":6,"ownerDescription":32,"updatedAt":33,"install":34,"resources":47},"01M6FJ0YR1SFMNNG5M1TT40SDS","subsystem-summary-of-scp",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fstellar\u002Fstellar-core\u002Fsubsystem-summary-of-scp\u002FSKILL.md",[],[18,19,20],"scp","summary","subsystem summary of scp","2026-11-10 10:12:30",false,"# SCP Subsystem — Technical Summary\n\n## Overview\n\nThe SCP (Stellar Consensus Protocol) subsystem implements the federated Byzantine agreement protocol used by stellar-core to reach consensus on ledger values. It is a self-contained library with a clean driver interface (`SCPDriver`) that decouples protocol logic from networking, persistence, and application-specific validation. The protocol operates in two main stages: **nomination** (proposing and filtering candidate values) and **balloting** (converging on a single value through prepare\u002Fconfirm\u002Fexternalize phases).\n\n## Key Files\n\n- **SCP.h \u002F SCP.cpp** — Top-level `SCP` class; entry point for receiving envelopes, nominating values, managing slots.\n- **SCPDriver.h \u002F SCPDriver.cpp** — Abstract driver interface that the application implements; handles signing, validation, timers, quorum set retrieval, hashing, and event callbacks.\n- **Slot.h \u002F Slot.cpp** — Per-slot state container; owns `NominationProtocol` and `BallotProtocol` instances.\n- **NominationProtocol.h \u002F NominationProtocol.cpp** — Nomination phase logic: voting for values, federated acceptance, candidate promotion.\n- **BallotProtocol.h \u002F BallotProtocol.cpp** — Ballot phase logic: prepare\u002Fconfirm\u002Fexternalize state machine, ballot bumping, timer management.\n- **LocalNode.h \u002F LocalNode.cpp** — Represents the local node; owns quorum set, provides quorum slice \u002F v-blocking set checks.\n- **QuorumSetUtils.h \u002F QuorumSetUtils.cpp** — Quorum set sanity checking and normalization utilities.\n\n---\n\n## Key Classes and Data Structures\n\n### `SCP`\n\nThe top-level protocol object. One instance per node.\n\n**Members:**\n- `mDriver` (`SCPDriver&`) — Reference to the application-provided driver.\n- `mLocalNode` (`shared_ptr\u003CLocalNode>`) — The local node descriptor with its quorum set.\n- `mKnownSlots` (`map\u003Cuint64, shared_ptr\u003CSlot>>`) — Map from slot index to `Slot` objects. Slots are created lazily on first access via `getSlot()`.\n\n**Key Methods:**\n- `receiveEnvelope(SCPEnvelopeWrapperPtr)` — Main entry point. Routes envelope to the appropriate `Slot::processEnvelope()`.\n- `nominate(slotIndex, value, previousValue)` — Initiates nomination for a slot (must be validator).\n- `stopNomination(slotIndex)` — Stops nomination for a slot.\n- `purgeSlots(maxSlotIndex, slotToKeep)` — Removes old slots below `maxSlotIndex` except `slotToKeep`.\n- `getSlot(slotIndex, create)` — Lazily creates and retrieves `Slot` from `mKnownSlots`.\n- `setStateFromEnvelope(slotIndex, e)` — Restores state from a previously emitted envelope (crash recovery).\n- `processCurrentState(slotIndex, f, forceSelf)` — Iterates over latest messages for a slot.\n- `processSlotsAscendingFrom \u002F processSlotsDescendingFrom` — Iteration helpers over known slots.\n- `getExternalizingState(slotIndex)` — Returns envelopes that contributed to externalization.\n- `getState(node, slotIndex)` — Computes `QuorumInfoNodeState` for a node, checking up to `NUM_SLOTS_TO_CHECK_FOR_REPORTING` (2) recent slots.\n- `getJsonQuorumInfo(id, summary, fullKeys, index)` — JSON diagnostic info categorizing quorum nodes as AGREE\u002FMISSING\u002FDELAYED\u002FDISAGREE.\n- `envToStr(envelope\u002Fstatement)` — Formats SCP envelopes\u002Fstatements as human-readable strings for logging.\n\n### `SCPDriver` (Abstract)\n\nThe application-facing interface. Stellar-core's `HerderSCPDriver` implements this.\n\n**Pure Virtual Methods (must be implemented):**\n- `signEnvelope(SCPEnvelope&)` — Sign an outgoing envelope.\n- `getQSet(Hash)` — Retrieve a quorum set by hash (return `nullptr` for unknown\u002Finvalid).\n- `emitEnvelope(SCPEnvelope)` — Broadcast an envelope to the network.\n- `getHashOf(vector\u003Copaque_vec\u003C>>)` — Compute a hash of serialized data.\n- `combineCandidates(slotIndex, candidates)` — Produce a composite value from candidate set (used when transitioning from nomination to balloting).\n- `hasUpgrades(Value)` — Check if a value contains protocol upgrades.\n- `stripAllUpgrades(Value)` — Remove all upgrades from a value.\n- `getUpgradeNominationTimeoutLimit()` — Max nomination timeouts before stripping upgrades.\n- `setupTimer(slotIndex, timerID, timeout, cb)` \u002F `stopTimer(slotIndex, timerID)` — Timer management.\n- `computeTimeout(roundNumber, isNomination)` — Compute timeout for a round.\n\n**Virtual Methods with Defaults:**\n- `validateValue(slotIndex, value, nomination)` — Returns `kMaybeValidValue` by default. Three levels: `kInvalidValue`, `kMaybeValidValue`, `kFullyValidatedValue`.\n- `extractValidValue(slotIndex, value)` — Extract a valid variant from an invalid value (returns `nullptr` by default).\n- `wrapEnvelope(e)` \u002F `wrapValue(v)` — Factory methods for `SCPEnvelopeWrapper` \u002F `ValueWrapper` (allow subclasses to add metadata).\n- `computeHashNode(slotIndex, prev, isPriority, roundNumber, nodeID)` — Hash for nomination leader election.\n- `computeValueHash(slotIndex, prev, roundNumber, value)` — Hash for value ordering during nomination.\n- `getNodeWeight(nodeID, qset, isLocalNode)` — Compute weight of a node within a quorum set (normalized 0–UINT64_MAX). Local node always gets `UINT64_MAX`. For other nodes, weight is `threshold\u002Ftotal * leafWeight` recursively through inner sets.\n- Event callbacks: `valueExternalized`, `nominatingValue`, `updatedCandidateValue`, `startedBallotProtocol`, `acceptedBallotPrepared`, `confirmedBallotPrepared`, `acceptedCommit`, `ballotDidHearFromQuorum`.\n\n### `Slot`\n\nPer-slot state container. Each slot tracks one consensus round (one ledger sequence number).\n\n**Members:**\n- `mSlotIndex` (`uint64 const`) — The slot\u002Fledger index.\n- `mSCP` (`SCP&`) — Back-reference to the owning `SCP` instance.\n- `mBallotProtocol` (`BallotProtocol`) — Owns ballot protocol state (value, not pointer).\n- `mNominationProtocol` (`NominationProtocol`) — Owns nomination protocol state (value, not pointer).\n- `mStatementsHistory` (`vector\u003CHistoricalStatement>`) — Debug log of all statements seen.\n- `mFullyValidated` (`bool`) — True if all values processed by this slot have been fully validated.\n- `mGotVBlocking` (`bool`) — True once messages from a v-blocking set have been received.\n\n**Key Methods:**\n- `processEnvelope(envelope, self)` — Dispatches to `NominationProtocol` or `BallotProtocol` based on statement type (`SCP_ST_NOMINATE` vs ballot types).\n- `nominate(value, previousValue, timedout)` — Delegates to `NominationProtocol::nominate()`.\n- `bumpState(value, force)` — Delegates to `BallotProtocol::bumpState()`.\n- `federatedAccept(voted, accepted, envs)` — Checks if a statement should be accepted: true if either (a) a v-blocking set accepted it, or (b) a quorum voted-or-accepted it.\n- `federatedRatify(voted, envs)` — Checks if a statement is ratified: true if a quorum voted for it.\n- `getQuorumSetFromStatement(st)` — Retrieves quorum set for a statement; for `EXTERNALIZE` statements, returns the singleton `{nodeID}` set.\n- `createEnvelope(statement)` — Wraps a statement into a signed envelope.\n- `getCompanionQuorumSetHashFromStatement(st)` — Static; extracts the quorum set hash from any statement type (note: `EXTERNALIZE` uses `commitQuorumSetHash`).\n- `maybeSetGotVBlocking()` — Checks if messages from a v-blocking set have been received.\n\n### `NominationProtocol`\n\nImplements the nomination phase of SCP. Votes for values, promotes them through federated accept and ratify, and produces candidate values.\n\n**Members:**\n- `mSlot` (`Slot&`) — Back-reference.\n- `mRoundNumber` (`int32`) — Current nomination round (incremented on each `nominate()` call).\n- `mVotes` (`ValueWrapperPtrSet`, paper variable X) — Values this node has voted to nominate.\n- `mAccepted` (`ValueWrapperPtrSet`, paper variable Y) — Values accepted as nominated.\n- `mCandidates` (`ValueWrapperPtrSet`, paper variable Z) — Values confirmed nominated (candidates).\n- `mLatestNominations` (`map\u003CNodeID, SCPEnvelopeWrapperPtr>`, paper variable N) — Latest nomination envelope per node.\n- `mLastEnvelope` (`SCPEnvelopeWrapperPtr`) — Last envelope emitted by this node.\n- `mRoundLeaders` (`set\u003CNodeID>`) — Nodes with highest priority this round.\n- `mNominationStarted` (`bool`) — Whether `nominate()` has been called.\n- `mLatestCompositeCandidate` (`ValueWrapperPtr`) — Latest composite candidate value (from `combineCandidates`).\n- `mPreviousValue` (`Value`) — Value from the previous slot (used for leader hashing).\n- `mTimerExpCount` (`uint32_t`) — Number of timer expirations (used for reporting and upgrade timeout logic).\n\n**Key Methods:**\n- `nominate(value, previousValue, timedout)` — Main entry. Increments round, updates leaders, adds votes from leaders, optionally adds own value (if self is leader). Strips upgrades after exceeding `getUpgradeNominationTimeoutLimit()`. Arms a timer to re-invoke itself on timeout. Stops nominating once candidates exist.\n- `processEnvelope(envelope)` — Processes a nomination message from another node. For each voted value, checks `federatedAccept(voted, accepted)`; if accepted, adds to `mAccepted`. For each accepted value, checks `federatedRatify(accepted)`; if ratified, promotes to `mCandidates`. When candidates found, stops timer, calls `combineCandidates`, and triggers `bumpState` on the ballot protocol.\n- `updateRoundLeaders()` — Computes which nodes have priority this round using `hashNode(priority)`. Includes self. Fast-forwards rounds if no node has priority.\n- `getNewValueFromNomination(nom)` — Extracts the highest-hash value from a nomination that the local node doesn't already have, preferring accepted values. Validates or extracts valid values.\n- `emitNomination()` — Creates and emits a `SCP_ST_NOMINATE` statement containing current votes and accepted values.\n- `hashNode(isPriority, nodeID)` \u002F `hashValue(value)` — Delegate to `SCPDriver::computeHashNode` \u002F `computeValueHash`.\n- `getNodePriority(nodeID, qset)` — Computes priority: if `hashNode(N, nodeID) \u003C= weight`, returns `hashNode(P, nodeID)`, else 0.\n- `stripUpgrades(value)` — Calls `SCPDriver::stripAllUpgrades` to remove upgrades from a value when timeouts exceed threshold.\n- `stopNomination()` — Sets `mNominationStarted = false`.\n- `getState(node, selfAlreadyMovedOn)` — Categorizes node as AGREE\u002FDELAYED\u002FDISAGREE\u002FMISSING based on accepted value comparison.\n- `isNewerStatement(old, new)` — A nomination statement is newer if its votes and accepted sets are (non-strict) supersets with at least one being strictly larger.\n\n### `BallotProtocol`\n\nImplements the ballot phase of SCP with three sub-phases: PREPARE, CONFIRM, EXTERNALIZE.\n\n**Members:**\n- `mSlot` (`Slot&`) — Back-reference.\n- `mHeardFromQuorum` (`bool`) — Whether a quorum at the current ballot counter has been heard.\n- `mPhase` (`SCPPhase`) — Current phase: `SCP_PHASE_PREPARE`, `SCP_PHASE_CONFIRM`, or `SCP_PHASE_EXTERNALIZE`.\n- `mCurrentBallot` (`SCPBallotWrapperUPtr`, paper variable b) — Current ballot.\n- `mPrepared` (`SCPBallotWrapperUPtr`, paper variable p) — Highest accepted-prepared ballot.\n- `mPreparedPrime` (`SCPBallotWrapperUPtr`, paper variable p') — Second-highest accepted-prepared ballot, incompatible with p.\n- `mHighBallot` (`SCPBallotWrapperUPtr`, paper variable h) — Highest confirmed-prepared ballot.\n- `mCommit` (`SCPBallotWrapperUPtr`, paper variable c) — Commit ballot.\n- `mLatestEnvelopes` (`map\u003CNodeID, SCPEnvelopeWrapperPtr>`, paper variable M) — Latest ballot envelope per node.\n- `mValueOverride` (`ValueWrapperPtr`, paper variable z) — Value override set when h is confirmed prepared; ensures this value is used for subsequent ballots.\n- `mCurrentMessageLevel` (`int`) — Recursion depth counter for `advanceSlot`, capped at `MAX_ADVANCE_SLOT_RECURSION` (50).\n- `mTimerExpCount` (`uint32_t`) — Number of ballot timer expirations.\n- `mLastEnvelope` \u002F `mLastEnvelopeEmit` — Track last generated and last emitted envelopes.\n\n**Inner Class: `SCPBallotWrapper`**\nPairs a `ValueWrapperPtr` with an `SCPBallot` to keep shared ownership of the value. Used via `unique_ptr\u003CSCPBallotWrapper>` (`SCPBallotWrapperUPtr`).\n\n**Key Methods — State Machine (`advanceSlot`):**\n- `advanceSlot(hint)` — The core state machine driver. Called after each envelope is recorded. Sequentially attempts each progression step:\n  1. `attemptAcceptPrepared(hint)` — Steps 1,5: Check if any ballot can be federatedAccept-ed as prepared.\n  2. `attemptConfirmPrepared(hint)` — Steps 2,3,8: Check if any prepared ballot can be federatedRatify-ed (confirmed prepared). If so, sets h, c, and the value override.\n  3. `attemptAcceptCommit(hint)` — Steps 4,6,8: Check if commit can be federatedAccept-ed. Transitions PREPARE→CONFIRM phase.\n  4. `attemptConfirmCommit(hint)` — Steps 7,8: Check if commit can be federatedRatify-ed. Transitions to EXTERNALIZE phase, calls `valueExternalized`.\n  5. `attemptBump()` — Step 9: If a v-blocking subset has higher counters, bump local counter to the minimum counter that eliminates this condition.\n  After all attempts complete, calls `checkHeardFromQuorum()` and `sendLatestEnvelope()`.\n\n**Key Methods — State Setters:**\n- `setAcceptPrepared(ballot)` — Updates p\u002Fp', clears c if p\u002Fp' conflict with h.\n- `setConfirmPrepared(newC, newH)` — Sets h, optionally c; sets `mValueOverride`; updates current ballot if needed.\n- `setAcceptCommit(c, h)` — Sets c\u002Fh; transitions phase to CONFIRM if in PREPARE; updates current ballot.\n- `setConfirmCommit(c, h)` — Transitions to EXTERNALIZE phase; calls `valueExternalized`; stops nomination.\n\n**Key Methods — Ballot Management:**\n- `bumpState(value, force)` — Creates a new ballot at counter+1 (or 1 if no current ballot). Uses `mValueOverride` if set.\n- `bumpToBallot(ballot, check)` — Low-level ballot update; resets h\u002Fc if incompatible; resets `mHeardFromQuorum`.\n- `updateCurrentValue(ballot)` — Updates current ballot with checks; calls `bumpToBallot`.\n- `abandonBallot(n)` — Bumps to ballot counter n (or counter+1 if n=0) using latest composite candidate value.\n- `ballotProtocolTimerExpired()` — Increments timer count, calls `abandonBallot(0)`.\n- `startBallotProtocolTimer()` \u002F `stopBallotProtocolTimer()` — Manage the ballot protocol timer.\n\n**Key Methods — Predicates and Helpers:**\n- `isNewerStatement(old, new)` — Total ordering: PREPARE \u003C CONFIRM \u003C EXTERNALIZE; within same type, lexicographic on (b, p, p', h).\n- `isStatementSane(st, self)` — Validates structural invariants of each statement type (counter > 0, c ≤ h ≤ b, etc.).\n- `hasPreparedBallot(ballot, st)` — Checks if a statement implies `ballot` is prepared.\n- `commitPredicate(ballot, interval, st)` — Checks if a statement commits `ballot` within `[interval.first, interval.second]`.\n- `compareBallots(b1, b2)` — Orders ballots by (counter, value). Returns -1\u002F0\u002F1.\n- `areBallotsCompatible(b1, b2)` — True if `b1.value == b2.value`.\n- `areBallotsLessAndCompatible\u002FIncompatible` — Combined comparisons.\n- `getPrepareCandidates(hint)` — Collects ballots from all known envelopes that might be prepared.\n- `getCommitBoundariesFromStatements(ballot)` — Collects counter boundaries for commit interval search.\n- `findExtendedInterval(candidate, boundaries, pred)` — Scans boundaries top-down to find the widest [low,high] interval satisfying the predicate.\n- `validateValues(st)` — Validates all values in a statement; returns the minimum validation level.\n- `checkHeardFromQuorum()` — Checks if a quorum at the current ballot counter has been heard; starts\u002Fstops timer accordingly; invokes `ballotDidHearFromQuorum` callback.\n- `emitCurrentStateStatement()` — Creates statement for current phase, processes it self, emits if newer.\n- `checkInvariants()` — Debug assertions: in CONFIRM\u002FEXTERNALIZE, b\u002Fp\u002Fc\u002Fh must all be set; p' \u003C p and incompatible; h ≤ b and compatible; c ≤ h.\n- `createStatement(type)` — Constructs `SCPStatement` from local state for the given phase type.\n\n### `LocalNode`\n\nRepresents the local node in the SCP network. Holds the node's identity, quorum set, and provides static methods for quorum\u002Fv-blocking checks.\n\n**Members:**\n- `mNodeID` (`NodeID const`) — This node's public key.\n- `mIsValidator` (`bool const`) — Whether this node is a validator.\n- `mQSet` (`SCPQuorumSet`) — This node's quorum set (normalized on construction).\n- `mQSetHash` (`Hash`) — Hash of the quorum set.\n- `mSingleQSet` (`shared_ptr\u003CSCPQuorumSet>`) — Singleton quorum set `{{mNodeID}}`, used during EXTERNALIZE.\n- `gSingleQSetHash` (`Hash`) — Hash of the singleton quorum set.\n- `mDriver` (`SCPDriver&`) — Back-reference.\n\n**Key Static Methods:**\n- `forAllNodes(qset, proc)` — Recursively iterates all nodes in a quorum set; short-circuits on `proc` returning false.\n- `isQuorumSlice(qSet, nodeSet)` — Tests if `nodeSet` contains a quorum slice for this quorum set (threshold validators + inner sets satisfied).\n- `isVBlocking(qSet, nodeSet\u002Fmap, filter)` — Tests if a set of nodes forms a v-blocking set. Condition: `nodeSet` size ≥ `total - threshold + 1` (enough to block every quorum slice).\n- `isQuorum(qSet, map, qfun, filter)` — Iterative quorum check with transitivity: filters nodes, then repeatedly removes nodes whose quorum slice isn't satisfied until fixpoint, then checks if local node's slice is still satisfied.\n- `findClosestVBlocking(qset, nodes, excluded)` — Finds the minimum set of nodes from `nodes` needed to form a v-blocking set (used for failure analysis in diagnostics).\n- `getSingletonQSet(nodeID)` — Returns `{threshold:1, validators:[nodeID]}`.\n- `toJson \u002F fromJson` — Serialize\u002Fdeserialize quorum sets to\u002Ffrom JSON.\n\n### `QuorumSetUtils`\n\n**Functions:**\n- `isQuorumSetSane(qSet, extraChecks, errString)` — Validates a quorum set: nesting depth ≤ 4, threshold ≥ 1, threshold ≤ entries, no duplicate nodes, total nodes 1–1000. With `extraChecks`, also validates threshold ≥ v-blocking size (≥51% effective).\n- `normalizeQSet(qSet, idToRemove)` — Normalizes a quorum set: removes `idToRemove` (adjusting threshold), merges singleton inner sets into parent's validators, simplifies `{t:1, {inner}}` to `inner`, then lexicographically sorts validators and inner sets.\n\n### Wrapper Types\n\n- `ValueWrapper` — Immutable wrapper around `Value` (XDR opaque byte vector). Non-copyable, non-movable. Shared via `ValueWrapperPtr` (`shared_ptr\u003CValueWrapper>`).\n- `SCPEnvelopeWrapper` — Immutable wrapper around `SCPEnvelope`. Non-copyable, non-movable. Shared via `SCPEnvelopeWrapperPtr`.\n- `ValueWrapperPtrSet` — `set\u003CValueWrapperPtr, WrappedValuePtrComparator>` ordered by underlying value bytes.\n\n---\n\n## SCP Protocol Phases\n\n### 1. Nomination Phase\n\n**Goal:** Agree on a set of candidate values to propose for balloting.\n\n**Flow:**\n1. `SCP::nominate()` is called by the application with a proposed value.\n2. `NominationProtocol::nominate()` increments round, computes round leaders via `updateRoundLeaders()`, and adds values from leaders' latest nominations to votes (X).\n3. If self is a leader, adds own proposed value to X (stripping upgrades if timeout limit exceeded).\n4. A `NOMINATION_TIMER` is armed to re-invoke `nominate()` with `timedout=true` for the next round.\n5. When messages arrive via `processEnvelope()`:\n   - For each voted value: if `federatedAccept(voted_for, accepted)` holds, move to Y (accepted).\n   - For each accepted value: if `federatedRatify(accepted)` holds, move to Z (candidates). Stops the nomination timer.\n6. When new candidates appear, `combineCandidates()` produces a composite value and `bumpState()` initiates the ballot protocol.\n\n**Leader Election:** Uses `hashNode(priority, nodeID)` with node weight from quorum set. The node(s) with highest priority hash value are leaders. Self is always included. Rounds fast-forward if no node has priority.\n\n### 2. Ballot Phase — PREPARE\n\n**Goal:** Converge on a prepared ballot.\n\n**State:** `mPhase = SCP_PHASE_PREPARE`. Working variables: b (current ballot), p (highest accepted-prepared), p' (second highest, incompatible with p).\n\n**Transitions:**\n- **Accept Prepared** (`attemptAcceptPrepared`): If `federatedAccept(vote_to_prepare(ballot), accept_prepared(ballot))`, update p (and p' if needed). If p\u002Fp' conflict with h, clear c.\n- **Confirm Prepared** (`attemptConfirmPrepared`): If `federatedRatify(prepared(ballot))`, set h (highest confirmed-prepared) and optionally c (lowest confirmed-prepared). Set `mValueOverride` to lock in h's value. Transition to CONFIRM if commit is also accepted.\n\n### 3. Ballot Phase — CONFIRM\n\n**Goal:** Converge on a committed ballot.\n\n**State:** `mPhase = SCP_PHASE_CONFIRM`. Requires b, p, c, h all set. p' is cleared.\n\n**Transitions:**\n- **Accept Commit** (`attemptAcceptCommit`): If `federatedAccept(vote_to_commit, accept_commit)` for an interval [c, h], set c\u002Fh, transition PREPARE→CONFIRM.\n- **Confirm Commit** (`attemptConfirmCommit`): If `federatedRatify(commit(ballot, [c,h]))`, transition to EXTERNALIZE.\n- **Bump** (`attemptBump`): If v-blocking set has higher ballot counters, bump to lowest counter that eliminates this condition.\n- Accept-prepared can still update p (for reporting purposes) in CONFIRM phase.\n\n### 4. Ballot Phase — EXTERNALIZE\n\n**Goal:** Finalize — consensus is reached.\n\n**State:** `mPhase = SCP_PHASE_EXTERNALIZE`. Set by `setConfirmCommit()`.\n\n**Actions:**\n- Emits `EXTERNALIZE` statement.\n- Stops nomination via `Slot::stopNomination()`.\n- Invokes `SCPDriver::valueExternalized(slotIndex, value)`.\n- Incoming envelopes are still processed (recorded) if their value matches the committed value, but no further state transitions occur.\n\n---\n\n## Key Control Flow and Timers\n\n### Nomination Timer (`NOMINATION_TIMER = 0`)\n- Armed in `NominationProtocol::nominate()` after each round.\n- Timeout computed by `SCPDriver::computeTimeout(roundNumber, isNomination=true)`.\n- On expiry: re-invokes `nominate()` with `timedout=true`, advancing to next round with new leaders.\n- Stopped when candidates are found (federated ratify of an accepted value).\n\n### Ballot Protocol Timer (`BALLOT_PROTOCOL_TIMER = 1`)\n- Armed in `checkHeardFromQuorum()` when a quorum is first heard at the current ballot counter.\n- Timeout computed by `SCPDriver::computeTimeout(ballotCounter, isNomination=false)`.\n- On expiry: `ballotProtocolTimerExpired()` increments `mTimerExpCount` and calls `abandonBallot(0)`, which bumps to the next ballot counter.\n- Stopped when quorum is no longer heard at current counter, or when externalized.\n\n### `advanceSlot` Recursion\n- Processing an envelope triggers `advanceSlot(hint)`, which may emit new statements that re-enter `advanceSlot`.\n- Recursion is capped at `MAX_ADVANCE_SLOT_RECURSION = 50` to prevent infinite loops.\n- Envelope emission (`sendLatestEnvelope`) is deferred to the outermost `advanceSlot` call (`mCurrentMessageLevel == 0`).\n\n---\n\n## Ownership Relationships\n\n```\nSCP\n ├── mLocalNode: shared_ptr\u003CLocalNode>\n └── mKnownSlots: map\u003Cuint64, shared_ptr\u003CSlot>>\n       └── Slot\n            ├── mBallotProtocol: BallotProtocol (value member)\n            │    ├── mCurrentBallot: unique_ptr\u003CSCPBallotWrapper>\n            │    ├── mPrepared: unique_ptr\u003CSCPBallotWrapper>\n            │    ├── mPreparedPrime: unique_ptr\u003CSCPBallotWrapper>\n            │    ├── mHighBallot: unique_ptr\u003CSCPBallotWrapper>\n            │    ├── mCommit: unique_ptr\u003CSCPBallotWrapper>\n            │    ├── mLatestEnvelopes: map\u003CNodeID, SCPEnvelopeWrapperPtr>\n            │    └── mValueOverride: ValueWrapperPtr\n            └── mNominationProtocol: NominationProtocol (value member)\n                 ├── mVotes: ValueWrapperPtrSet (X)\n                 ├── mAccepted: ValueWrapperPtrSet (Y)\n                 ├── mCandidates: ValueWrapperPtrSet (Z)\n                 ├── mLatestNominations: map\u003CNodeID, SCPEnvelopeWrapperPtr>\n                 └── mLatestCompositeCandidate: ValueWrapperPtr\n```\n\n`SCPDriver` is referenced (not owned) by `SCP` and `LocalNode`. The application owns the `SCPDriver` and `SCP` instances.\n\n---\n\n## Key Data Flows\n\n### Inbound Envelope Processing\n1. `SCP::receiveEnvelope(envelope)` → `Slot::processEnvelope(envelope, self=false)`\n2. Slot dispatches based on statement type:\n   - `SCP_ST_NOMINATE` → `NominationProtocol::processEnvelope()`\n   - `SCP_ST_PREPARE\u002FCONFIRM\u002FEXTERNALIZE` → `BallotProtocol::processEnvelope()`\n3. Protocol validates sanity, checks newness, validates values via `SCPDriver::validateValue()`.\n4. Records envelope, then:\n   - Nomination: checks federated accept\u002Fratify on values\n   - Ballot: calls `advanceSlot()` which attempts all state transitions\n\n### Outbound Envelope Emission\n1. Protocol state change triggers `emitCurrentStateStatement()` (ballot) or `emitNomination()` (nomination).\n2. Statement is created from current local state, wrapped in an envelope, signed via `SCPDriver::signEnvelope()`.\n3. The envelope is self-processed (fed back through `processEnvelope` with `self=true`) to ensure consistency.\n4. If valid and newer than last emitted, `SCPDriver::emitEnvelope()` is called to broadcast.\n\n### Nomination → Ballot Transition\n1. When `NominationProtocol` confirms candidates (Z non-empty), it calls `combineCandidates()` to produce a composite value.\n2. It then calls `Slot::bumpState(compositeValue, force=false)` which delegates to `BallotProtocol::bumpState()`.\n3. This creates the first ballot `(1, compositeValue)` and emits a PREPARE statement.\n\n### Federated Agreement Primitives\nBoth protocols use two primitives provided by `Slot`:\n- `federatedAccept(voted, accepted, envs)` — True if: (a) a v-blocking set of nodes accepted the statement, OR (b) a quorum of nodes voted-or-accepted it.\n- `federatedRatify(voted, envs)` — True if a quorum of nodes voted for the statement.\n\nThese delegate to `LocalNode::isVBlocking()` and `LocalNode::isQuorum()` with the local node's quorum set and the relevant envelope maps.\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fstellar\u002Fstellar-core\u002Fsubsystem-summary-of-scp",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill subsystem-summary-of-scp",{"tool":40,"command":41},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill subsystem-summary-of-scp",{"tool":43,"command":44},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill subsystem-summary-of-scp","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fstellar\u002Fstellar-core\u002Fsubsystem-summary-of-scp\u002FSKILL.md",true,[],[49,63,73,82,88,95],{"skillNo":50,"name":51,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":52,"categories":53,"keywords":59,"publishedAt":21},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[54],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[60,61,62],"model","model hierarchy","model hierarchy skill",{"skillNo":64,"name":65,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":69,"publishedAt":21},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[68],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},[70,71,72],"MCP","zoom","zoom mcp",{"skillNo":74,"name":75,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":78,"publishedAt":21},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[79,80,81,71],"sdk","zoom apps sdk","apps",{"skillNo":83,"name":84,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":21},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[84],{"skillNo":89,"name":90,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":94,"publishedAt":21},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[93],{"categoryNo":55,"categoryName":56,"taxonomyType":57,"taxonomyTypeDesc":58},[90],{"skillNo":96,"name":97,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":98,"categories":99,"keywords":100,"publishedAt":21},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[97]]