Skip to content
Open
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
20 changes: 0 additions & 20 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,6 @@ jobs:
- name: Run unit tests
run: go test $(go list ./... | grep -v '/tests') -v

sn-manager-tests:
name: sn-manager-tests
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6.0.1
- name: Setup Go and system deps
uses: ./.github/actions/setup-env
with:
bust_lumera_retag: 'true'
- name: Verify nested module
working-directory: sn-manager
run: |
go mod tidy
git diff --exit-code -- go.mod go.sum
go vet ./...
go test ./...
go test -race ./internal/updater ./internal/utils ./internal/version

integration-tests:
name: integration-tests
runs-on: ubuntu-latest
Expand Down
40 changes: 0 additions & 40 deletions docs/evm-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,46 +80,6 @@ The migration is:
| `migration tx was not confirmed` | Tx was not included in a block within 60s | Restart to retry; the check is idempotent |
| `failed to save updated config` | Config file write error after successful migration | Manually update config as instructed in the error message |

### Recovery from the v2.6.1 automatic rollback

A v2.6.1 sn-manager can mistake the intentionally removed `evm_key_name` field
for an incomplete migration and select v2.5.0. The old manager evaluates that
preflight before self-update, so a normal combined release cannot reliably
self-deliver the fix. Do not run v2.5.x against a migrated EVM keyring.

Recover one node at a time:

1. Stop the sn-manager service so the old process cannot change the selected
version while recovery is in progress.
2. Replace the **sn-manager binary itself** out of band with the corrected
release (package/configuration management or a controlled manual binary
swap). Replacing only the managed SuperNode binary is insufficient.
3. Confirm the post-migration SuperNode config is canonical: `key_name` names a
local `eth_secp256k1` signing key, `identity` is that key's Lumera address,
and `evm_key_name` is absent. Do not recreate the transitional field after a
completed migration.
4. Confirm that the network-approved recovery release is also the **current
latest release in that network's channel** (`-testnet` for testnet, stable
for mainnet). Startup performs mandatory monotonic synchronization even when
periodic auto-upgrade is disabled; it may advance to a newer channel release
but will never downgrade. Then explicitly install and select the approved
EVM-capable release:

```bash
sn-manager get <v2.6-or-newer-network-tag>
sn-manager use <v2.6-or-newer-network-tag>
```

These are operator-explicit commands; automatic update paths never
downgrade and continue to block unprepared legacy nodes.
5. Start sn-manager and verify both axes independently:
- process: selected version, SuperNode startup/status endpoint, EVM-module
detection, and epoch-report submission;
- chain: expected SuperNode state after the normal epoch recovery predicate.

A historical `rolled-back.log` marker is diagnostic only. It is not proof that
key migration succeeded and is never used to bypass runtime key validation.

### Multisig legacy accounts

A supernode daemon holds a single signing key and cannot run the K-of-N signing
Expand Down
41 changes: 0 additions & 41 deletions pkg/configlock/lock.go

This file was deleted.

48 changes: 0 additions & 48 deletions pkg/configlock/lock_test.go

This file was deleted.

2 changes: 0 additions & 2 deletions pkg/github/testutil/fake_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ type FakeClient struct {

CallsLatestStable int
CallsListReleases int
CallsTarballURL int
}

func (f *FakeClient) GetLatestRelease() (*github.Release, error) {
Expand Down Expand Up @@ -49,6 +48,5 @@ func (f *FakeClient) GetRelease(tag string) (*github.Release, error) {
}

func (f *FakeClient) GetReleaseTarballURL(version string) (string, error) {
f.CallsTarballURL++
return "", errors.New("not implemented")
}
33 changes: 12 additions & 21 deletions sn-manager/cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/config"
"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/updater"
"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/utils"
"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/version"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -36,23 +35,14 @@ func runCheck(cmd *cobra.Command, args []string) error {
// Create GitHub client
client := github.NewClient(config.GitHubRepo)

// Select the same release channel as automatic updates.
snapshot, err := utils.ReadSupernodeUpdateSnapshot()
// Get latest stable release
release, err := client.GetLatestStableRelease()
if err != nil {
return fmt.Errorf("failed to read SuperNode update configuration: %w", err)
}
release, err := utils.LatestReleaseForChainID(client, snapshot.ChainID)
if err != nil {
return fmt.Errorf("failed to check updates for chain %q: %w", snapshot.ChainID, err)
}
managerHome := config.GetManagerHome()
currentVersion, err := version.NewManager(managerHome).GetCurrentVersion()
if err != nil {
return fmt.Errorf("failed to read active SuperNode version: %w", err)
return fmt.Errorf("failed to check for stable updates: %w", err)
}

fmt.Printf("\nLatest release: %s\n", release.TagName)
fmt.Printf("Current version: %s\n", currentVersion)
fmt.Printf("Current version: %s\n", cfg.Updates.CurrentVersion)
// Report manager version and if it would update under the same policy
mv := strings.TrimSpace(appVersion)
if mv != "" && mv != "dev" && !strings.EqualFold(mv, "unknown") {
Expand All @@ -63,20 +53,21 @@ func runCheck(cmd *cobra.Command, args []string) error {
}

// Compare versions
cmp := utils.CompareVersions(currentVersion, release.TagName)
cmp := utils.CompareVersions(cfg.Updates.CurrentVersion, release.TagName)

if cmp < 0 {
// Use the same logic as auto-updater to determine update eligibility.
// Use the same logic as auto-updater to determine update eligibility
managerHome := config.GetManagerHome()
autoUpdater := updater.New(managerHome, cfg, appVersion, nil)
wouldAutoUpdate := autoUpdater.ShouldUpdate(currentVersion, release.TagName)
wouldAutoUpdate := autoUpdater.ShouldUpdate(cfg.Updates.CurrentVersion, release.TagName)

if wouldAutoUpdate {
fmt.Printf("\n✓ Update available: %s → %s\n", currentVersion, release.TagName)
fmt.Printf("\n✓ Update available: %s → %s\n", cfg.Updates.CurrentVersion, release.TagName)
fmt.Printf("Published: %s\n", release.PublishedAt.Format("2006-01-02 15:04:05"))
fmt.Println("\n✓ This update will be applied automatically if auto-upgrade is enabled")
fmt.Println(" Or manually with: sn-manager get")
} else {
fmt.Printf("\n⚠ Major update available: %s → %s\n", currentVersion, release.TagName)
fmt.Printf("\n⚠ Major update available: %s → %s\n", cfg.Updates.CurrentVersion, release.TagName)
fmt.Printf("Published: %s\n", release.PublishedAt.Format("2006-01-02 15:04:05"))
fmt.Println("\n⚠ Major version updates require manual installation:")
fmt.Printf(" sn-manager get %s\n", release.TagName)
Expand All @@ -89,9 +80,9 @@ func runCheck(cmd *cobra.Command, args []string) error {
fmt.Println(release.Body)
}
} else if cmp == 0 {
fmt.Println("\n✓ You are running the latest release for this chain")
fmt.Println("\n✓ You are running the latest stable version")
} else {
fmt.Printf("\n⚠ You are running a newer version than the latest release for this chain\n")
fmt.Printf("\n⚠ You are running a newer version than the latest stable release\n")
}

return nil
Expand Down
18 changes: 2 additions & 16 deletions sn-manager/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,9 @@ func runGet(cmd *cobra.Command, args []string) error {

var targetVersion string
if len(args) == 0 {
snapshot, err := utils.ReadSupernodeUpdateSnapshot()
release, err := client.GetLatestStableRelease()
if err != nil {
return fmt.Errorf("failed to read SuperNode update configuration: %w", err)
}
release, err := utils.LatestReleaseForChainID(client, snapshot.ChainID)
if err != nil {
return fmt.Errorf("failed to get latest release for chain %q: %w", snapshot.ChainID, err)
return fmt.Errorf("failed to get latest release: %w", err)
}
targetVersion = release.TagName
} else {
Expand All @@ -46,16 +42,6 @@ func runGet(cmd *cobra.Command, args []string) error {

fmt.Printf("Target version: %s\n", targetVersion)

releaseInstallLock, err := versionMgr.AcquireInstallLock()
if err != nil {
return fmt.Errorf("failed to lock release installation: %w", err)
}
defer func() {
if releaseErr := releaseInstallLock(); releaseErr != nil {
log.Printf("Warning: failed to release installation lock: %v", releaseErr)
}
}()

if versionMgr.IsVersionInstalled(targetVersion) {
fmt.Printf("Already installed\n")
return nil
Expand Down
Loading
Loading