소스 정보
- 저장소
- stellar/stellar-core
- 최근 소스 활동
- 2026년 2월 11일 01:44
- 감지된 SKILL.md 언어
- 영어
- 스타
- 3,297
- 포크
- 1,070
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/stellar/stellar-core --skill subsystem-summary-of-scp명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
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
SOC 직업 분류 기준
SKILL.md 표시 중
| 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.