How Key Resharing Works in MPCium When Nodes Join or Leave the Cluster

Key resharing in MPCium updates existing MPC key shares by creating versioned resharing sessions that reconcile old and new committees, running the TSS resharing protocol, and persisting new shares with incremented version tags.

When the committee composition of an MPC cluster changes—whether nodes are added or removed—the fystack/mpcium repository performs key resharing to redistribute key material securely without exposing the underlying secret. This process is orchestrated by the Node type in pkg/mpc/node.go and implemented through scheme-specific sessions that wrap the underlying TSS library protocols.

Entry Point and Committee Validation

The resharing process begins at Node.CreateReshareSession in pkg/mpc/node.go. This method acts as the factory and validator for all resharing operations, ensuring the cluster can safely transition to a new committee structure.

Prerequisites and Peer Validation

Before constructing a session, the node performs strict validation checks (lines 60-88):

  1. Threshold check – Verifies at least newThreshold+1 peers are currently ready in the cluster
  2. Peer list validation – Confirms len(newPeerIDs) >= newThreshold+1 and that every proposed peer ID corresponds to a ready node
  3. Key metadata retrieval – Loads the existing KeyInfo using the wallet ID and determines the signature scheme prefix (ecdsa or eddsa)

Role Determination: Old vs. New Committee

The function determines node participation by checking two boolean conditions (lines 104-115):

  • isInOldCommittee – Whether the node holds an existing share from the current committee
  • isInNewCommittee – Whether the node appears in the newPeerIDs list

If the node participates in neither committee, the function returns nil and the node ignores the operation. This filtering ensures only relevant nodes engage in the protocol, reducing network overhead and attack surface.

Session Construction and TSS Parameters

Once validated, the node instantiates either an ecdsaReshareSession or eddsaReshareSession based on the SessionType. Both constructors follow an identical pattern defined in pkg/mpc/ecdsa_resharing_session.go (lines 57-84) and pkg/mpc/eddsa_resharing_session.go (lines 46-74).

Building the Resharing Context

The constructor performs four critical setup steps:

  • Party ID selection – Chooses newPartyIDs if the node is joining, or retains oldPartyIDs if continuing
  • Topic composition – Creates broadcast topics (reshare:broadcast:<scheme>:<walletID>) and direct message topics (reshare:direct:<scheme>:<from>:<to>:<walletID>)
  • TSS parameter generation – Invokes tss.NewReSharingParameters with old and new committee sizes, respective thresholds, and the local PartyID
  • Legacy peer recording – Stores oldPeerIDs to calculate departing nodes later

Legacy Committee Handling

When nodes leave the cluster, they form the "legacy committee." Both session types expose GetLegacyCommitteePeers() (ECDSA lines 22-38, EdDSA lines 12-27) which computes the set difference between oldPeerIDs and newPeerIDs. These departing nodes must remain active during the protocol to forward their final share data to the incoming committee, ensuring no share material is lost during transition.

The Resharing Protocol Lifecycle

The session implements a three-phase lifecycle managed through the ReshareSession interface.

Initialization Phase (Init)

During session.Init(), nodes prepare their cryptographic state based on committee membership:

  • New nodes generate empty LocalPartySaveData using keygen.NewLocalPartySaveData (they possess no prior share material)
  • Existing nodes load their current shares via loadOldShareDataGeneric
  • Party creation – Both types instantiate resharing.NewLocalParty(s.reshareParams, share, s.outCh, s.endCh) to bind the TSS protocol to the session's communication channels

Execution Phase (Reshare/Run)

The session.Reshare() method launches the protocol in a dedicated goroutine by calling s.party.Start(). During execution:

  • Incoming messages from peers route through s.handleTssMessage
  • The TSS library coordinates the multi-round protocol between old and new committees
  • Upon completion, the final share material emits on s.endCh as saveData

Finalization and Storage

When the protocol completes (ECDSA lines 70-98, EdDSA lines 70-100), the session:

  1. Marshals the new share to JSON and stores it under a versioned key (walletID:version)
  2. Creates updated KeyInfo containing the new participant list and newThreshold
  3. Extracts and encodes the public key (using encoding.EncodeS256PubKey for ECDSA)
  4. Publishes a resharing-success event to the resultQueue

The version is always oldVersion + 1, enabling backward-compatible key lookups and preventing replay attacks.

Version Management and Security Considerations

MPCium implements strict version tracking through the walletIDWithVersion helper in pkg/types/tss.go. Each resharing operation increments the version counter, which serves dual purposes:

  • Storage isolation – New shares live under distinct KV-store keys, preventing overwrite of old shares during failed resharing attempts
  • Pre-parameter rotation – New nodes select pre-parameters using p.ecdsaPreParams[version%2] (lines 55-59 in node.go), ensuring fresh randomness for each committee generation and mitigating collision attacks

Implementation Example

The following example demonstrates initiating a resharing operation when a new node joins the cluster:

// Node instance already running and connected
walletID     := "corporate-wallet-001"
newThreshold := 2
newPeers     := []string{"node1", "node2", "node3"} // includes existing and new nodes
isNewPeer    := true // This node is joining the new committee

// Setup result queue for completion notification
resultQueue, _ := manager.NewMessageQueue("mpc_reshare_result")

// Create session via the orchestrator
session, err := node.CreateReshareSession(
    SessionTypeECDSA,
    walletID,
    newThreshold,
    newPeers,
    isNewPeer,
    resultQueue,
)
if err != nil {
    log.Fatalf("Resharing session creation failed: %v", err)
}

// Initialize cryptographic state
if err = session.Init(); err != nil {
    log.Fatalf("Failed to initialize TSS state: %v", err)
}

// Execute protocol with callback
session.Reshare(func() {
    log.Println("Key resharing completed successfully")
})

To handle departing nodes, the event consumer can retrieve legacy peers:

legacyPeers := session.GetLegacyCommitteePeers()
for _, peer := range legacyPeers {
    log.Printf("Routing final share from departing node: %s", peer)
}

Summary

  • Key resharing in MPCium is triggered through Node.CreateReshareSession, which validates that the new committee satisfies threshold requirements (newThreshold+1) and identifies whether the local node belongs to the old committee, new committee, or both.
  • Scheme-specific sessions (ecdsaReshareSession and eddsaReshareSession) wrap the TSS library's resharing.NewLocalParty, handling the cryptographic protocol while managing messaging topics and peer routing.
  • Legacy committee members (nodes leaving the cluster) remain active during resharing to forward their share contributions, calculated via GetLegacyCommitteePeers() as the set difference between old and new peer IDs.
  • Versioned storage ensures atomic upgrades by persisting shares under walletID:version keys and incrementing the version counter in KeyInfo, preventing conflicts and enabling rollback capabilities.
  • Pre-parameter rotation uses the version number to select fresh cryptographic parameters for new nodes, protecting against replay attacks and ensuring unique share generation per committee epoch.

Frequently Asked Questions

What happens if a node is neither in the old nor new committee?

The CreateReshareSession method returns nil and the node skips the operation entirely (lines 108-115 in pkg/mpc/node.go). This prevents irrelevant nodes from consuming resources or accidentally interfering with the protocol.

How does MPCium prevent old shares from being overwritten during resharing?

The system uses versioned storage keys constructed via walletIDWithVersion in pkg/types/tss.go. New shares are stored under keys incorporating oldVersion + 1, ensuring distinct storage locations for each committee generation and enabling recovery from failed resharing attempts.

Can the threshold change during a resharing operation?

Yes. The newThreshold parameter in CreateReshareSession allows the security policy to change when the committee updates. The function validates that len(newPeerIDs) >= newThreshold+1 and that at least newThreshold+1 peers are ready before allowing the session to proceed.

What is the role of the TSS library in the resharing process?

MPCium delegates the cryptographic heavy lifting to the TSS library through tss.NewReSharingParameters and resharing.NewLocalParty. These constructs manage the secure multi-party computation protocol that redistributes key shares between the old and new committees without reconstructing the master secret.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →