# How Version Management Works for Wallet Keys Across Reshares in mpcium

> Learn how mpcium ensures secure wallet key management across reshares using semantic versioning. Discover automatic version increments for distinct key layouts and backward compatibility.

- Repository: [Fystack Labs/mpcium](https://github.com/fystack/mpcium)
- Tags: internals
- Published: 2026-03-02

---

**Mpcium implements semantic versioning for wallet secret shares, automatically incrementing version numbers during resharing operations to distinguish key layouts while maintaining backward compatibility for legacy nodes.**

The **mpcium** repository provides a threshold MPC (Multi-Party Computation) wallet infrastructure where cryptographic keys undergo periodic resharing to update committee membership. Understanding **version management for wallet keys across reshares** is essential for developers integrating with the system or operating nodes that must handle both current and legacy wallet states.

## The Semantic Versioning Architecture

The system defines two fundamental version constants in [`pkg/mpc/node.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/node.go):

- **DefaultVersion (1)**: Assigned to all newly generated wallets
- **BackwardCompatibleVersion (0)**: Used by legacy nodes or pre-resharing operations

This semantic versioning serves three critical purposes: distinguishing old and new key-share layouts, tracking the number of resharing operations a wallet has undergone, and enabling correct key-share selection when nodes rejoin reshared committees.

## Initial Key Generation and Storage

When a node creates a new wallet via `CreateKeyGenSession`, the system establishes the initial version state. In [`pkg/mpc/node.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/node.go) (lines 96-100), the function calls either `createECDSAKeyGenSession` or `createEDDSAKeyGenSession`, passing `DefaultVersion` as the initial value.

The system persists two distinct data structures:

1. **KeyInfo records**: Stored in the key-info store ([`pkg/keyinfo/keyinfo.go`](https://github.com/fystack/mpcium/blob/main/pkg/keyinfo/keyinfo.go)), containing `ParticipantPeerIDs`, `Threshold`, and `Version`
2. **Raw share data**: Marshaled `LocalPartySaveData` stored in the KV store under the plain `walletID` key (unversioned) for the initial generation

This unversioned storage for the first generation ensures backward compatibility while establishing the baseline version state.

## The Resharing Flow: Version Increment

When a resharing operation begins, `Node.CreateReshareSession` (in [`pkg/mpc/node.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/node.go), lines 60-68) loads the current `KeyInfo.Version` and passes it to the session constructor. The critical version increment occurs inside the resharing execution.

In [`pkg/mpc/ecdsa_resharing_session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/ecdsa_resharing_session.go) (lines 85-90), the `Reshare` method implements the version bump:

```go
newVersion := s.GetVersion() + 1
key := s.composeKey(walletIDWithVersion(s.walletID, newVersion))

```

The new share data is then marshaled and stored under this **versioned key** following the pattern `walletID_vN`. Simultaneously, the code updates the `KeyInfo` record (lines 92-99) to reflect the new version and revised participant list.

An identical flow exists in [`pkg/mpc/eddsa_resharing_session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/eddsa_resharing_session.go) for EdDSA keys.

## Party ID Generation and Collision Prevention

To prevent cryptographic collisions during resharing, party IDs embed the wallet version in their key field. The `createPartyID` logic in [`pkg/mpc/party_id.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/party_id.go) (lines 45-50) implements this distinction:

- For **BackwardCompatibleVersion (0)**: The party ID key is simply the node ID
- For all other versions: The party ID key follows the format `<nodeID>:<version>`

This ensures that different generations of the same wallet produce unique party identifiers, preventing interference between old and new committees during the resharing transition.

## Retrieving Shares with Version Awareness

When loading wallet data for signing or further resharing, the system uses `loadOldShareDataGeneric` in [`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go). This function implements a hierarchical lookup strategy:

1. If the requested `version > 0`, it composes the key using `walletIDWithVersion` (e.g., `walletID_v2`)
2. If the versioned key is missing or `version == 0`, it gracefully falls back to the unversioned `walletID` key

This logic appears in [`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go) (lines 49-57 and 77-84), ensuring that nodes can always locate their share data regardless of when they last participated in the wallet's evolution.

The `Node.getVersion` method (lines 54-69 in [`pkg/mpc/node.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/node.go)) provides the entry point for determining a wallet's current version, returning `DefaultVersion` when no `KeyInfo` record exists.

## Practical Implementation Examples

### Retrieving the Current Wallet Version

```go
// p is a *Node
walletID := "my-wallet"
ver := p.getVersion(mpc.SessionTypeECDSA, walletID)
// ver will be DefaultVersion (1) for a freshly generated wallet

```

### Loading Shares Across Versions

```go
// s is a *session (created by key-gen, sign, or resharing)
var share keygen.LocalPartySaveData
// Load using the session's known version
if err := s.loadOldShareDataGeneric(walletID, s.version, &share); err != nil {
    return fmt.Errorf("cannot load share: %w", err)
}

```

### Executing a Version Bump During Resharing

```go
// Inside ecdsaReshareSession.Reshare
newVersion := s.GetVersion() + 1
kvKey := s.composeKey(walletIDWithVersion(s.walletID, newVersion))

bytes, _ := json.Marshal(newShareData)
if err := s.kvstore.Put(kvKey, bytes); err != nil {
    return fmt.Errorf("failed to persist reshared share: %w", err)
}

// Update KeyInfo with new participants and version
info := keyinfo.KeyInfo{
    ParticipantPeerIDs: s.newPeerIDs,
    Threshold:          s.reshareParams.NewThreshold(),
    Version:            newVersion,
}
if err := s.keyinfoStore.Save(s.composeKey(s.walletID), &info); err != nil {
    return fmt.Errorf("failed to update keyinfo: %w", err)
}

```

### Generating Version-Aware Party IDs

```go
// node is *Node, readyPeers is []string
selfID, allIDs := node.generatePartyIDs(
    mpc.PurposeKeygen,
    readyPeers,
    node.getVersion(mpc.SessionTypeECDSA, walletID),
)

```

## Summary

- **Semantic versioning** distinguishes wallet generations through `DefaultVersion` (1) and `BackwardCompatibleVersion` (0) constants defined in [`pkg/mpc/node.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/node.go)
- **Version incrementation** occurs atomically during resharing in [`ecdsa_resharing_session.go`](https://github.com/fystack/mpcium/blob/main/ecdsa_resharing_session.go) and [`eddsa_resharing_session.go`](https://github.com/fystack/mpcium/blob/main/eddsa_resharing_session.go), storing new shares under `walletID_vN` keys
- **KeyInfo records** track the current version alongside participant lists and thresholds in the dedicated key-info store
- **Collision prevention** is achieved by embedding version numbers into party ID keys in [`pkg/mpc/party_id.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/party_id.go)
- **Backward compatibility** is maintained through graceful fallback to unversioned keys when `version == 0` in [`pkg/mpc/session.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/session.go)

## Frequently Asked Questions

### What happens if a node tries to load a wallet version it doesn't recognize?

The node calls `loadOldShareDataGeneric` which implements a fallback mechanism. If the versioned key is unavailable, the system attempts to load the unversioned key, allowing legacy nodes to operate on pre-reshared wallets while newer nodes access versioned data.

### How does the system prevent version collisions during active resharing?

Party IDs embed the version number in their key field (as `<nodeID>:<version>` for non-zero versions) through the `createPartyID` function in [`pkg/mpc/party_id.go`](https://github.com/fystack/mpcium/blob/main/pkg/mpc/party_id.go). This ensures that old and new committees generate distinct cryptographic identities even when processing the same wallet ID.

### Where is the version number physically stored?

The version is stored in two locations: the `KeyInfo` struct in the key-info store (alongside participants and threshold) and implicitly in the KV store key name (as `walletID_vN` suffixes) for the actual share data. The `KeyInfo` record serves as the authoritative source of truth.

### Can the version number decrease during a resharing operation?

No. The resharing logic explicitly increments the version using `newVersion := s.GetVersion() + 1` in both ECDSA and EdDSA resharing sessions. This monotonic increase ensures that wallet evolution is strictly forward-moving and audit trails remain intact.