Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/gean/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func main() {
logger.Error("failed to initialize node", "err", err)
os.Exit(1)
}
defer n.Close()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down
14 changes: 14 additions & 0 deletions node/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ func loadValidatorKeys(log *slog.Logger, cfg Config) (map[uint64]forkchoice.Sign

kp, err := leansig.LoadKeypair(pkPath, skPath)
if err != nil {
// Clean up previously loaded keypairs to prevent Rust memory leaks.
// Modeled after zeam's errdefer keypair.deinit() pattern
// (cli/src/node.zig:433-469).
freeLoadedKeys(keys)
return nil, fmt.Errorf("failed to load keypair for validator %d: %w", idx, err)
}
keys[idx] = kp
Expand All @@ -307,6 +311,16 @@ func loadValidatorKeys(log *slog.Logger, cfg Config) (map[uint64]forkchoice.Sign
return keys, nil
}

// freeLoadedKeys releases Rust-allocated XMSS keypairs from a partially
// loaded key map. Called on error during loadValidatorKeys to prevent leaks.
func freeLoadedKeys(keys map[uint64]forkchoice.Signer) {
for _, key := range keys {
if f, ok := key.(interface{ Free() }); ok {
f.Free()
}
}
}

func startMetrics(log *slog.Logger, cfg Config) {
if cfg.MetricsPort <= 0 {
return
Expand Down
8 changes: 8 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
dbCloser io.Closer
log *slog.Logger

ctx context.Context

Check failure on line 62 in node/node.go

View workflow job for this annotation

GitHub Actions / Go Lint & Format

field ctx is unused (U1000)
cancel context.CancelFunc
}

Expand All @@ -74,7 +74,7 @@
const (
// maxBlocksPerRequest is the maximum number of block roots to request
// in a single BlocksByRoot RPC call. Matches leanSpec MAX_BLOCKS_PER_REQUEST.
maxBlocksPerRequest = 10

Check failure on line 77 in node/node.go

View workflow job for this annotation

GitHub Actions / Go Lint & Format

const maxBlocksPerRequest is unused (U1000)

// maxBackfillDepth is the maximum depth for backward chain walks.
// Matches leanSpec MAX_BACKFILL_DEPTH and zeam MAX_BLOCK_FETCH_DEPTH.
Expand Down Expand Up @@ -104,6 +104,14 @@
if n.API != nil {
n.API.Stop()
}
// Free Rust-allocated XMSS keypairs.
if n.Validator != nil {
for _, key := range n.Validator.Keys {
if f, ok := key.(interface{ Free() }); ok {
f.Free()
}
}
}
if n.dbCloser != nil {
n.dbCloser.Close()
}
Expand Down
4 changes: 4 additions & 0 deletions xmss/leanmultisig/leanmultisig.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package leanmultisig
import "C"
import (
"fmt"
"runtime"
"sync"
"unsafe"
)
Expand Down Expand Up @@ -82,6 +83,7 @@ func Aggregate(pubkeys, signatures [][]byte, messageHash [MessageHashLength]byte
&outData,
&outLen,
)
runtime.KeepAlive(messageHash)
if result != ResultOK {
return nil, resultError("leanmultisig_aggregate", result)
}
Expand Down Expand Up @@ -118,6 +120,8 @@ func VerifyAggregated(pubkeys [][]byte, messageHash [MessageHashLength]byte, pro
C.size_t(len(proofData)),
C.uint32_t(epoch),
)
runtime.KeepAlive(messageHash)
runtime.KeepAlive(proofData)
if result != ResultOK {
return resultError("leanmultisig_verify_aggregated", result)
}
Expand Down
9 changes: 9 additions & 0 deletions xmss/leansig/leansig.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package leansig
import "C"
import (
"fmt"
"runtime"
"unsafe"
)

Expand Down Expand Up @@ -75,6 +76,8 @@ func RestoreKeypair(pkBytes []byte, skBytes []byte) (*Keypair, error) {
skLen := C.size_t(len(skBytes))

result := C.leansig_keypair_restore(pkPtr, pkLen, skPtr, skLen, &kpPtr)
runtime.KeepAlive(pkBytes)
runtime.KeepAlive(skBytes)
if result != ResultOK {
return nil, fmt.Errorf("leansig_keypair_restore failed with code %d", result)
}
Expand Down Expand Up @@ -184,6 +187,7 @@ func (kp *Keypair) Sign(epoch uint32, message [MessageLength]byte) ([]byte, erro
&sigData,
&sigLen,
)
runtime.KeepAlive(message)
if result != ResultOK {
return nil, fmt.Errorf("leansig_sign failed with code %d", result)
}
Expand All @@ -206,6 +210,9 @@ func Verify(pubkeyBytes []byte, epoch uint32, message [MessageLength]byte, sigBy
(*C.uint8_t)(unsafe.Pointer(&sigBytes[0])),
C.size_t(len(sigBytes)),
)
runtime.KeepAlive(pubkeyBytes)
runtime.KeepAlive(message)
runtime.KeepAlive(sigBytes)
if result == ResultOK {
return nil
}
Expand All @@ -231,6 +238,8 @@ func (kp *Keypair) VerifyWithKeypair(epoch uint32, message [MessageLength]byte,
(*C.uint8_t)(unsafe.Pointer(&sigBytes[0])),
C.size_t(len(sigBytes)),
)
runtime.KeepAlive(message)
runtime.KeepAlive(sigBytes)
if result == ResultOK {
return nil
}
Expand Down
Loading