بنقرة واحدة
subsystem-summary-of-scp
read this skill for a token-efficient summary of the scp subsystem
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
read this skill for a token-efficient summary of the scp subsystem
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
| name | subsystem-summary-of-scp |
| description | read this skill for a token-efficient summary of the scp subsystem |
The 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/confirm/externalize phases).
SCP class; entry point for receiving envelopes, nominating values, managing slots.NominationProtocol and BallotProtocol instances.SCPThe top-level protocol object. One instance per node.
Members:
mDriver (SCPDriver&) — Reference to the application-provided driver.mLocalNode (shared_ptr<LocalNode>) — The local node descriptor with its quorum set.mKnownSlots (map<uint64, shared_ptr<Slot>>) — Map from slot index to Slot objects. Slots are created lazily on first access via getSlot().Key Methods:
receiveEnvelope(SCPEnvelopeWrapperPtr) — Main entry point. Routes envelope to the appropriate Slot::processEnvelope().nominate(slotIndex, value, previousValue) — Initiates nomination for a slot (must be validator).stopNomination(slotIndex) — Stops nomination for a slot.purgeSlots(maxSlotIndex, slotToKeep) — Removes old slots below maxSlotIndex except slotToKeep.getSlot(slotIndex, create) — Lazily creates and retrieves Slot from mKnownSlots.setStateFromEnvelope(slotIndex, e) — Restores state from a previously emitted envelope (crash recovery).processCurrentState(slotIndex, f, forceSelf) — Iterates over latest messages for a slot.processSlotsAscendingFrom / processSlotsDescendingFrom — Iteration helpers over known slots.getExternalizingState(slotIndex) — Returns envelopes that contributed to externalization.getState(node, slotIndex) — Computes QuorumInfoNodeState for a node, checking up to NUM_SLOTS_TO_CHECK_FOR_REPORTING (2) recent slots.getJsonQuorumInfo(id, summary, fullKeys, index) — JSON diagnostic info categorizing quorum nodes as AGREE/MISSING/DELAYED/DISAGREE.envToStr(envelope/statement) — Formats SCP envelopes/statements as human-readable strings for logging.SCPDriver (Abstract)The application-facing interface. Stellar-core's HerderSCPDriver implements this.
Pure Virtual Methods (must be implemented):
signEnvelope(SCPEnvelope&) — Sign an outgoing envelope.getQSet(Hash) — Retrieve a quorum set by hash (return nullptr for unknown/invalid).emitEnvelope(SCPEnvelope) — Broadcast an envelope to the network.getHashOf(vector<opaque_vec<>>) — Compute a hash of serialized data.combineCandidates(slotIndex, candidates) — Produce a composite value from candidate set (used when transitioning from nomination to balloting).hasUpgrades(Value) — Check if a value contains protocol upgrades.stripAllUpgrades(Value) — Remove all upgrades from a value.getUpgradeNominationTimeoutLimit() — Max nomination timeouts before stripping upgrades.setupTimer(slotIndex, timerID, timeout, cb) / stopTimer(slotIndex, timerID) — Timer management.computeTimeout(roundNumber, isNomination) — Compute timeout for a round.Virtual Methods with Defaults:
validateValue(slotIndex, value, nomination) — Returns kMaybeValidValue by default. Three levels: kInvalidValue, kMaybeValidValue, kFullyValidatedValue.extractValidValue(slotIndex, value) — Extract a valid variant from an invalid value (returns nullptr by default).wrapEnvelope(e) / wrapValue(v) — Factory methods for SCPEnvelopeWrapper / ValueWrapper (allow subclasses to add metadata).computeHashNode(slotIndex, prev, isPriority, roundNumber, nodeID) — Hash for nomination leader election.computeValueHash(slotIndex, prev, roundNumber, value) — Hash for value ordering during nomination.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/total * leafWeight recursively through inner sets.valueExternalized, nominatingValue, updatedCandidateValue, startedBallotProtocol, acceptedBallotPrepared, confirmedBallotPrepared, acceptedCommit, ballotDidHearFromQuorum.SlotPer-slot state container. Each slot tracks one consensus round (one ledger sequence number).
Members:
mSlotIndex (uint64 const) — The slot/ledger index.mSCP (SCP&) — Back-reference to the owning SCP instance.mBallotProtocol (BallotProtocol) — Owns ballot protocol state (value, not pointer).mNominationProtocol (NominationProtocol) — Owns nomination protocol state (value, not pointer).mStatementsHistory (vector<HistoricalStatement>) — Debug log of all statements seen.mFullyValidated (bool) — True if all values processed by this slot have been fully validated.mGotVBlocking (bool) — True once messages from a v-blocking set have been received.Key Methods:
processEnvelope(envelope, self) — Dispatches to NominationProtocol or BallotProtocol based on statement type (SCP_ST_NOMINATE vs ballot types).nominate(value, previousValue, timedout) — Delegates to NominationProtocol::nominate().bumpState(value, force) — Delegates to BallotProtocol::bumpState().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.federatedRatify(voted, envs) — Checks if a statement is ratified: true if a quorum voted for it.getQuorumSetFromStatement(st) — Retrieves quorum set for a statement; for EXTERNALIZE statements, returns the singleton {nodeID} set.createEnvelope(statement) — Wraps a statement into a signed envelope.getCompanionQuorumSetHashFromStatement(st) — Static; extracts the quorum set hash from any statement type (note: EXTERNALIZE uses commitQuorumSetHash).maybeSetGotVBlocking() — Checks if messages from a v-blocking set have been received.NominationProtocolImplements the nomination phase of SCP. Votes for values, promotes them through federated accept and ratify, and produces candidate values.
Members:
mSlot (Slot&) — Back-reference.mRoundNumber (int32) — Current nomination round (incremented on each nominate() call).mVotes (ValueWrapperPtrSet, paper variable X) — Values this node has voted to nominate.mAccepted (ValueWrapperPtrSet, paper variable Y) — Values accepted as nominated.mCandidates (ValueWrapperPtrSet, paper variable Z) — Values confirmed nominated (candidates).mLatestNominations (map<NodeID, SCPEnvelopeWrapperPtr>, paper variable N) — Latest nomination envelope per node.mLastEnvelope (SCPEnvelopeWrapperPtr) — Last envelope emitted by this node.mRoundLeaders (set<NodeID>) — Nodes with highest priority this round.mNominationStarted (bool) — Whether nominate() has been called.mLatestCompositeCandidate (ValueWrapperPtr) — Latest composite candidate value (from combineCandidates).mPreviousValue (Value) — Value from the previous slot (used for leader hashing).mTimerExpCount (uint32_t) — Number of timer expirations (used for reporting and upgrade timeout logic).Key Methods:
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.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.updateRoundLeaders() — Computes which nodes have priority this round using hashNode(priority). Includes self. Fast-forwards rounds if no node has priority.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.emitNomination() — Creates and emits a SCP_ST_NOMINATE statement containing current votes and accepted values.hashNode(isPriority, nodeID) / hashValue(value) — Delegate to SCPDriver::computeHashNode / computeValueHash.getNodePriority(nodeID, qset) — Computes priority: if hashNode(N, nodeID) <= weight, returns hashNode(P, nodeID), else 0.stripUpgrades(value) — Calls SCPDriver::stripAllUpgrades to remove upgrades from a value when timeouts exceed threshold.stopNomination() — Sets mNominationStarted = false.getState(node, selfAlreadyMovedOn) — Categorizes node as AGREE/DELAYED/DISAGREE/MISSING based on accepted value comparison.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.BallotProtocolImplements the ballot phase of SCP with three sub-phases: PREPARE, CONFIRM, EXTERNALIZE.
Members:
mSlot (Slot&) — Back-reference.mHeardFromQuorum (bool) — Whether a quorum at the current ballot counter has been heard.mPhase (SCPPhase) — Current phase: SCP_PHASE_PREPARE, SCP_PHASE_CONFIRM, or SCP_PHASE_EXTERNALIZE.mCurrentBallot (SCPBallotWrapperUPtr, paper variable b) — Current ballot.mPrepared (SCPBallotWrapperUPtr, paper variable p) — Highest accepted-prepared ballot.mPreparedPrime (SCPBallotWrapperUPtr, paper variable p') — Second-highest accepted-prepared ballot, incompatible with p.mHighBallot (SCPBallotWrapperUPtr, paper variable h) — Highest confirmed-prepared ballot.mCommit (SCPBallotWrapperUPtr, paper variable c) — Commit ballot.mLatestEnvelopes (map<NodeID, SCPEnvelopeWrapperPtr>, paper variable M) — Latest ballot envelope per node.mValueOverride (ValueWrapperPtr, paper variable z) — Value override set when h is confirmed prepared; ensures this value is used for subsequent ballots.mCurrentMessageLevel (int) — Recursion depth counter for advanceSlot, capped at MAX_ADVANCE_SLOT_RECURSION (50).mTimerExpCount (uint32_t) — Number of ballot timer expirations.mLastEnvelope / mLastEnvelopeEmit — Track last generated and last emitted envelopes.Inner Class: SCPBallotWrapper
Pairs a ValueWrapperPtr with an SCPBallot to keep shared ownership of the value. Used via unique_ptr<SCPBallotWrapper> (SCPBallotWrapperUPtr).
Key Methods — State Machine (advanceSlot):
advanceSlot(hint) — The core state machine driver. Called after each envelope is recorded. Sequentially attempts each progression step:
attemptAcceptPrepared(hint) — Steps 1,5: Check if any ballot can be federatedAccept-ed as prepared.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.attemptAcceptCommit(hint) — Steps 4,6,8: Check if commit can be federatedAccept-ed. Transitions PREPARE→CONFIRM phase.attemptConfirmCommit(hint) — Steps 7,8: Check if commit can be federatedRatify-ed. Transitions to EXTERNALIZE phase, calls valueExternalized.attemptBump() — Step 9: If a v-blocking subset has higher counters, bump local counter to the minimum counter that eliminates this condition.
After all attempts complete, calls checkHeardFromQuorum() and sendLatestEnvelope().Key Methods — State Setters:
setAcceptPrepared(ballot) — Updates p/p', clears c if p/p' conflict with h.setConfirmPrepared(newC, newH) — Sets h, optionally c; sets mValueOverride; updates current ballot if needed.setAcceptCommit(c, h) — Sets c/h; transitions phase to CONFIRM if in PREPARE; updates current ballot.setConfirmCommit(c, h) — Transitions to EXTERNALIZE phase; calls valueExternalized; stops nomination.Key Methods — Ballot Management:
bumpState(value, force) — Creates a new ballot at counter+1 (or 1 if no current ballot). Uses mValueOverride if set.bumpToBallot(ballot, check) — Low-level ballot update; resets h/c if incompatible; resets mHeardFromQuorum.updateCurrentValue(ballot) — Updates current ballot with checks; calls bumpToBallot.abandonBallot(n) — Bumps to ballot counter n (or counter+1 if n=0) using latest composite candidate value.ballotProtocolTimerExpired() — Increments timer count, calls abandonBallot(0).startBallotProtocolTimer() / stopBallotProtocolTimer() — Manage the ballot protocol timer.Key Methods — Predicates and Helpers:
isNewerStatement(old, new) — Total ordering: PREPARE < CONFIRM < EXTERNALIZE; within same type, lexicographic on (b, p, p', h).isStatementSane(st, self) — Validates structural invariants of each statement type (counter > 0, c ≤ h ≤ b, etc.).hasPreparedBallot(ballot, st) — Checks if a statement implies ballot is prepared.commitPredicate(ballot, interval, st) — Checks if a statement commits ballot within [interval.first, interval.second].compareBallots(b1, b2) — Orders ballots by (counter, value). Returns -1/0/1.areBallotsCompatible(b1, b2) — True if b1.value == b2.value.areBallotsLessAndCompatible/Incompatible — Combined comparisons.getPrepareCandidates(hint) — Collects ballots from all known envelopes that might be prepared.getCommitBoundariesFromStatements(ballot) — Collects counter boundaries for commit interval search.findExtendedInterval(candidate, boundaries, pred) — Scans boundaries top-down to find the widest [low,high] interval satisfying the predicate.validateValues(st) — Validates all values in a statement; returns the minimum validation level.checkHeardFromQuorum() — Checks if a quorum at the current ballot counter has been heard; starts/stops timer accordingly; invokes ballotDidHearFromQuorum callback.emitCurrentStateStatement() — Creates statement for current phase, processes it self, emits if newer.checkInvariants() — Debug assertions: in CONFIRM/EXTERNALIZE, b/p/c/h must all be set; p' < p and incompatible; h ≤ b and compatible; c ≤ h.createStatement(type) — Constructs SCPStatement from local state for the given phase type.LocalNodeRepresents the local node in the SCP network. Holds the node's identity, quorum set, and provides static methods for quorum/v-blocking checks.
Members:
mNodeID (NodeID const) — This node's public key.mIsValidator (bool const) — Whether this node is a validator.mQSet (SCPQuorumSet) — This node's quorum set (normalized on construction).mQSetHash (Hash) — Hash of the quorum set.mSingleQSet (shared_ptr<SCPQuorumSet>) — Singleton quorum set {{mNodeID}}, used during EXTERNALIZE.gSingleQSetHash (Hash) — Hash of the singleton quorum set.mDriver (SCPDriver&) — Back-reference.Key Static Methods:
forAllNodes(qset, proc) — Recursively iterates all nodes in a quorum set; short-circuits on proc returning false.isQuorumSlice(qSet, nodeSet) — Tests if nodeSet contains a quorum slice for this quorum set (threshold validators + inner sets satisfied).isVBlocking(qSet, nodeSet/map, filter) — Tests if a set of nodes forms a v-blocking set. Condition: nodeSet size ≥ total - threshold + 1 (enough to block every quorum slice).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.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).getSingletonQSet(nodeID) — Returns {threshold:1, validators:[nodeID]}.toJson / fromJson — Serialize/deserialize quorum sets to/from JSON.QuorumSetUtilsFunctions:
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).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.ValueWrapper — Immutable wrapper around Value (XDR opaque byte vector). Non-copyable, non-movable. Shared via ValueWrapperPtr (shared_ptr<ValueWrapper>).SCPEnvelopeWrapper — Immutable wrapper around SCPEnvelope. Non-copyable, non-movable. Shared via SCPEnvelopeWrapperPtr.ValueWrapperPtrSet — set<ValueWrapperPtr, WrappedValuePtrComparator> ordered by underlying value bytes.Goal: Agree on a set of candidate values to propose for balloting.
Flow:
SCP::nominate() is called by the application with a proposed value.NominationProtocol::nominate() increments round, computes round leaders via updateRoundLeaders(), and adds values from leaders' latest nominations to votes (X).NOMINATION_TIMER is armed to re-invoke nominate() with timedout=true for the next round.processEnvelope():
federatedAccept(voted_for, accepted) holds, move to Y (accepted).federatedRatify(accepted) holds, move to Z (candidates). Stops the nomination timer.combineCandidates() produces a composite value and bumpState() initiates the ballot protocol.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.
Goal: Converge on a prepared ballot.
State: mPhase = SCP_PHASE_PREPARE. Working variables: b (current ballot), p (highest accepted-prepared), p' (second highest, incompatible with p).
Transitions:
attemptAcceptPrepared): If federatedAccept(vote_to_prepare(ballot), accept_prepared(ballot)), update p (and p' if needed). If p/p' conflict with h, clear c.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.Goal: Converge on a committed ballot.
State: mPhase = SCP_PHASE_CONFIRM. Requires b, p, c, h all set. p' is cleared.
Transitions:
attemptAcceptCommit): If federatedAccept(vote_to_commit, accept_commit) for an interval [c, h], set c/h, transition PREPARE→CONFIRM.attemptConfirmCommit): If federatedRatify(commit(ballot, [c,h])), transition to EXTERNALIZE.attemptBump): If v-blocking set has higher ballot counters, bump to lowest counter that eliminates this condition.Goal: Finalize — consensus is reached.
State: mPhase = SCP_PHASE_EXTERNALIZE. Set by setConfirmCommit().
Actions:
EXTERNALIZE statement.Slot::stopNomination().SCPDriver::valueExternalized(slotIndex, value).NOMINATION_TIMER = 0)NominationProtocol::nominate() after each round.SCPDriver::computeTimeout(roundNumber, isNomination=true).nominate() with timedout=true, advancing to next round with new leaders.BALLOT_PROTOCOL_TIMER = 1)checkHeardFromQuorum() when a quorum is first heard at the current ballot counter.SCPDriver::computeTimeout(ballotCounter, isNomination=false).ballotProtocolTimerExpired() increments mTimerExpCount and calls abandonBallot(0), which bumps to the next ballot counter.advanceSlot RecursionadvanceSlot(hint), which may emit new statements that re-enter advanceSlot.MAX_ADVANCE_SLOT_RECURSION = 50 to prevent infinite loops.sendLatestEnvelope) is deferred to the outermost advanceSlot call (mCurrentMessageLevel == 0).SCP
├── mLocalNode: shared_ptr<LocalNode>
└── mKnownSlots: map<uint64, shared_ptr<Slot>>
└── Slot
├── mBallotProtocol: BallotProtocol (value member)
│ ├── mCurrentBallot: unique_ptr<SCPBallotWrapper>
│ ├── mPrepared: unique_ptr<SCPBallotWrapper>
│ ├── mPreparedPrime: unique_ptr<SCPBallotWrapper>
│ ├── mHighBallot: unique_ptr<SCPBallotWrapper>
│ ├── mCommit: unique_ptr<SCPBallotWrapper>
│ ├── mLatestEnvelopes: map<NodeID, SCPEnvelopeWrapperPtr>
│ └── mValueOverride: ValueWrapperPtr
└── mNominationProtocol: NominationProtocol (value member)
├── mVotes: ValueWrapperPtrSet (X)
├── mAccepted: ValueWrapperPtrSet (Y)
├── mCandidates: ValueWrapperPtrSet (Z)
├── mLatestNominations: map<NodeID, SCPEnvelopeWrapperPtr>
└── mLatestCompositeCandidate: ValueWrapperPtr
SCPDriver is referenced (not owned) by SCP and LocalNode. The application owns the SCPDriver and SCP instances.
SCP::receiveEnvelope(envelope) → Slot::processEnvelope(envelope, self=false)SCP_ST_NOMINATE → NominationProtocol::processEnvelope()SCP_ST_PREPARE/CONFIRM/EXTERNALIZE → BallotProtocol::processEnvelope()SCPDriver::validateValue().advanceSlot() which attempts all state transitionsemitCurrentStateStatement() (ballot) or emitNomination() (nomination).SCPDriver::signEnvelope().processEnvelope with self=true) to ensure consistency.SCPDriver::emitEnvelope() is called to broadcast.NominationProtocol confirms candidates (Z non-empty), it calls combineCandidates() to produce a composite value.Slot::bumpState(compositeValue, force=false) which delegates to BallotProtocol::bumpState().(1, compositeValue) and emits a PREPARE statement.Both protocols use two primitives provided by Slot:
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.federatedRatify(voted, envs) — True if a quorum of nodes voted for the statement.These delegate to LocalNode::isVBlocking() and LocalNode::isQuorum() with the local node's quorum set and the relevant envelope maps.
extending yourself with a new reusable skill by interviewing the user
analyzing a change to determine what tests are needed and adding them to the test suite
modifying build configuration to enable/disable variants, switch compilers or flags, or otherwise prepare for a build
reviewing a change for semantic correctness, simplicity, design consistency, and completeness
reviewing a git diff for small localized coding mistakes that can be fixed without high-level understanding
how to run make correctly to get a good build, and otherwise understand the build system