Files
chains/mpcvm/block.go
T
zeekayandHanzo Dev bbce4b6975 chains: rename thresholdvm → mpcvm (LP-134 T-Chain retirement finished)
Under LP-0130's canonical roster, chains/thresholdvm/ hosts the M-Chain
VM (MPC threshold signing). The package name "thresholdvm" was the last
piece of T-Chain nomenclature still shipping in code — legacy from the
pre-LP-134 shared-substrate design. Renamed to mpcvm/ so the package
name matches the M-Chain letter and LP-7100 spec.

- chains/thresholdvm/ → chains/mpcvm/ (dir + inner files + package decl)
- Sed-replace thresholdvm → mpcvm across .go / .md / .yml / .json / .sh
- Fee/settlement primitive references now say mpcvm
- LLM.md canonical chain roster (LP-0130) already points at mpcvm/

F-Chain runtime pieces (fhe/, protocol/tfhe_keygen/,
runtime/f_chain_adapter.go) stay under mpcvm/ for now — they consume
the same shared threshold primitives. A follow-up extract to
chains/fhevm/ can happen once F-Chain runtime graduates.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 01:22:30 -07:00

192 lines
4.2 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package mpcvm
import (
"context"
"crypto/sha256"
"time"
"github.com/luxfi/consensus/core/choices"
"github.com/luxfi/ids"
"github.com/luxfi/log"
)
// Operation types
const (
OpTypeKeygen = "keygen"
OpTypeSign = "sign"
OpTypeReshare = "reshare"
OpTypeRefresh = "refresh"
)
// Operation represents an MPC operation recorded in a block
type Operation struct {
Type string `json:"type"` // keygen, sign, reshare, refresh
SessionID string `json:"sessionId"`
KeyID string `json:"keyId"`
Protocol string `json:"protocol,omitempty"`
RequestingChain string `json:"requestingChain,omitempty"`
MessageHash []byte `json:"messageHash,omitempty"`
Signature []byte `json:"signature,omitempty"`
Timestamp int64 `json:"timestamp"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}
// Block represents a T-Chain block
type Block struct {
ID_ ids.ID `json:"id"`
ParentID_ ids.ID `json:"parentId"`
BlockHeight uint64 `json:"height"`
BlockTimestamp int64 `json:"timestamp"`
Operations []*Operation `json:"operations"`
vm *VM
status choices.Status
}
// ID returns the block's ID
func (b *Block) ID() ids.ID {
return b.ID_
}
// Parent returns the parent block's ID (legacy)
func (b *Block) Parent() ids.ID {
return b.ParentID_
}
// ParentID returns the parent block's ID (implements block.Block interface)
func (b *Block) ParentID() ids.ID {
return b.ParentID_
}
// Height returns the block's height
func (b *Block) Height() uint64 {
return b.BlockHeight
}
// Timestamp returns the block's timestamp
func (b *Block) Timestamp() time.Time {
return time.Unix(b.BlockTimestamp, 0)
}
// Status returns the block's status as uint8 (implements block.Block interface)
func (b *Block) Status() uint8 {
return uint8(b.status)
}
// ChoicesStatus returns the block's status as choices.Status
func (b *Block) ChoicesStatus() choices.Status {
return b.status
}
// Bytes returns the block's serialized bytes
func (b *Block) Bytes() []byte {
bytes, _ := Codec.Marshal(codecVersion, b)
return bytes
}
// Verify verifies the block is valid
func (b *Block) Verify(ctx context.Context) error {
// Verify parent exists
if b.ParentID_ != ids.Empty {
_, err := b.vm.getBlock(b.ParentID_)
if err != nil {
return err
}
}
// Verify operations are valid
for _, op := range b.Operations {
if op.SessionID == "" {
return ErrInvalidOperation
}
if op.KeyID == "" {
return ErrInvalidOperation
}
}
return nil
}
// Accept marks the block as accepted
func (b *Block) Accept(ctx context.Context) error {
b.status = choices.Accepted
b.vm.mu.Lock()
defer b.vm.mu.Unlock()
// Remove from pending
delete(b.vm.pendingBlocks, b.ID())
// Update last accepted
b.vm.lastAcceptedID = b.ID()
// Persist block
if err := b.vm.putBlock(b); err != nil {
return err
}
// Update height index
b.vm.heightIndex[b.BlockHeight] = b.ID()
// Clean up completed sessions
for _, op := range b.Operations {
switch op.Type {
case OpTypeSign:
delete(b.vm.signingSessions, op.SessionID)
case OpTypeKeygen, OpTypeReshare, OpTypeRefresh:
delete(b.vm.keygenSessions, op.SessionID)
}
}
b.vm.log.Info("accepted threshold block",
log.Stringer("blockID", b.ID()),
log.Uint64("height", b.BlockHeight),
log.Int("operations", len(b.Operations)),
)
return nil
}
// Reject marks the block as rejected
func (b *Block) Reject(ctx context.Context) error {
b.status = choices.Rejected
b.vm.mu.Lock()
defer b.vm.mu.Unlock()
// Remove from pending
delete(b.vm.pendingBlocks, b.ID())
b.vm.log.Info("rejected threshold block",
log.Stringer("blockID", b.ID()),
)
return nil
}
// SetStatus sets the block's status
func (b *Block) SetStatus(status choices.Status) {
b.status = status
}
// computeID computes the block's ID
func (b *Block) computeID() ids.ID {
bytes, _ := Codec.Marshal(codecVersion, b)
hash := sha256.Sum256(bytes)
return ids.ID(hash)
}
var ErrInvalidOperation = &BlockError{Message: "invalid operation"}
type BlockError struct {
Message string
}
func (e *BlockError) Error() string {
return e.Message
}