fix(node): surface corrupted persisted state during load
All checks were successful
Security Scan / security (push) Successful in 9s
Test / test (push) Successful in 1m27s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-30 21:17:43 +01:00
parent 4c8bced1e7
commit 6a70c6f234
4 changed files with 50 additions and 6 deletions

View file

@ -104,6 +104,9 @@ func NewNodeManager() (*NodeManager, error) {
// NewNodeManagerFromPaths loads or creates a node identity store at explicit paths.
//
// Missing files are treated as a fresh install; malformed or partial identity
// state is returned as an error so callers can handle it explicitly.
//
// nodeManager, err := NewNodeManagerFromPaths("/srv/p2p/private.key", "/srv/p2p/node.json")
func NewNodeManagerFromPaths(keyPath, configPath string) (*NodeManager, error) {
nm := &NodeManager{
@ -111,12 +114,15 @@ func NewNodeManagerFromPaths(keyPath, configPath string) (*NodeManager, error) {
configPath: configPath,
}
// Try to load existing identity
if err := nm.loadIdentity(); err != nil {
// Identity doesn't exist yet, that's ok
// Missing files indicate a first run; anything else is a load failure.
if !fsExists(keyPath) && !fsExists(configPath) {
return nm, nil
}
if err := nm.loadIdentity(); err != nil {
return nil, err
}
return nm, nil
}

View file

@ -170,6 +170,22 @@ func TestIdentity_NodeIdentity_Good(t *testing.T) {
})
}
func TestIdentity_NodeManagerFromPaths_CorruptIdentity_Bad(t *testing.T) {
tmpDir := t.TempDir()
keyPath, configPath := testNodeManagerPaths(tmpDir)
testWriteFile(t, configPath, []byte(`{"id":"node-1","name":"broken","publicKey":"not-json"`), 0o600)
nm, err := NewNodeManagerFromPaths(keyPath, configPath)
if err == nil {
t.Fatal("expected error when loading a corrupted node identity")
}
if nm != nil {
t.Fatal("expected nil node manager when identity data is corrupted")
}
}
func TestIdentity_NodeRoles_Good(t *testing.T) {
tests := []struct {
role NodeRole

View file

@ -136,6 +136,9 @@ func NewPeerRegistry() (*PeerRegistry, error) {
// NewPeerRegistryFromPath loads or creates a peer registry at an explicit path.
//
// Missing files are treated as an empty registry; malformed registry files are
// returned as errors so callers can repair the persisted state.
//
// peerRegistry, err := NewPeerRegistryFromPath("/srv/p2p/peers.json")
func NewPeerRegistryFromPath(peersPath string) (*PeerRegistry, error) {
pr := &PeerRegistry{
@ -146,13 +149,16 @@ func NewPeerRegistryFromPath(peersPath string) (*PeerRegistry, error) {
allowedPublicKeys: make(map[string]bool),
}
// Try to load existing peers
if err := pr.load(); err != nil {
// No existing peers, that's ok
// Missing files indicate a first run; any existing file must parse cleanly.
if !fsExists(peersPath) {
pr.rebuildKDTree()
return pr, nil
}
if err := pr.load(); err != nil {
return nil, err
}
pr.rebuildKDTree()
return pr, nil
}

View file

@ -27,6 +27,22 @@ func TestPeer_Registry_NewPeerRegistry_Good(t *testing.T) {
}
}
func TestPeer_Registry_NewPeerRegistryFromPath_CorruptFile_Bad(t *testing.T) {
tmpDir := t.TempDir()
peersPath := testJoinPath(tmpDir, "peers.json")
testWriteFile(t, peersPath, []byte(`{"id":"peer-1"`), 0o600)
pr, err := NewPeerRegistryFromPath(peersPath)
if err == nil {
t.Fatal("expected error when loading a corrupted peer registry")
}
if pr != nil {
t.Fatal("expected nil peer registry when persisted data is corrupted")
}
}
func TestPeer_Registry_AddPeer_Good(t *testing.T) {
pr, cleanup := setupTestPeerRegistry(t)
defer cleanup()