From c85165ccd4752a87a05105167383134109badde5 Mon Sep 17 00:00:00 2001 From: jholdstock Date: Wed, 29 Apr 2026 10:59:29 +0800 Subject: [PATCH] Consistent preconditions for SPV/RPC sync. In order to start a wallet sync over either RPC or SPV, the wallet must already be loaded and must not already have an associated syncer. RpcSync was missing the check for a loaded wallet (which could lead to a panic). SpvSync was missing the check for an already existing syncer. --- internal/rpc/rpcserver/server.go | 34 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/internal/rpc/rpcserver/server.go b/internal/rpc/rpcserver/server.go index b8ed7eb9b..ffb22f547 100644 --- a/internal/rpc/rpcserver/server.go +++ b/internal/rpc/rpcserver/server.go @@ -1,5 +1,5 @@ // Copyright (c) 2015-2016 The btcsuite developers -// Copyright (c) 2016-2025 The Decred developers +// Copyright (c) 2016-2026 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -2832,13 +2832,17 @@ func isLoopback(addr string) bool { func (s *loaderServer) RpcSync(req *pb.RpcSyncRequest, svr pb.WalletLoaderService_RpcSyncServer) error { defer zero(req.Password) - // Error if the wallet is already syncing with the network. wallet, walletLoaded := s.loader.LoadedWallet() - if walletLoaded { - _, err := wallet.NetworkBackend() - if err == nil { - return status.Errorf(codes.FailedPrecondition, "wallet is loaded and already synchronizing") - } + + // Wallet must be loaded before sync can start. + if !walletLoaded { + return status.Errorf(codes.FailedPrecondition, "Wallet has not been loaded") + } + + // Error if the wallet is already syncing with the network. + _, err := wallet.NetworkBackend() + if err == nil { + return status.Errorf(codes.FailedPrecondition, "wallet is loaded and already synchronizing") } if req.DiscoverAccounts && len(req.PrivatePassphrase) == 0 { @@ -2966,7 +2970,7 @@ func (s *loaderServer) RpcSync(req *pb.RpcSyncRequest, svr pb.WalletLoaderServic syncer.SetCallbacks(cbs) // Synchronize until error or RPC cancellation. - err := syncer.Run(svr.Context()) + err = syncer.Run(svr.Context()) if err != nil { if svr.Context().Err() != nil { return status.Errorf(codes.Canceled, "Wallet synchronization canceled: %v", err) @@ -2978,11 +2982,19 @@ func (s *loaderServer) RpcSync(req *pb.RpcSyncRequest, svr pb.WalletLoaderServic } func (s *loaderServer) SpvSync(req *pb.SpvSyncRequest, svr pb.WalletLoaderService_SpvSyncServer) error { - wallet, ok := s.loader.LoadedWallet() - if !ok { + wallet, walletLoaded := s.loader.LoadedWallet() + + // Wallet must be loaded before sync can start. + if !walletLoaded { return status.Errorf(codes.FailedPrecondition, "Wallet has not been loaded") } + // Error if the wallet is already syncing with the network. + _, err := wallet.NetworkBackend() + if err == nil { + return status.Errorf(codes.FailedPrecondition, "wallet is loaded and already synchronizing") + } + if req.DiscoverAccounts && len(req.PrivatePassphrase) == 0 { return status.Errorf(codes.InvalidArgument, "private passphrase is required for discovering accounts") } @@ -3133,7 +3145,7 @@ func (s *loaderServer) SpvSync(req *pb.SpvSyncRequest, svr pb.WalletLoaderServic syncer.SetPersistentPeers(spvConnects) } - err := syncer.Run(svr.Context()) + err = syncer.Run(svr.Context()) if err != nil { if errors.Is(err, context.Canceled) { return status.Errorf(codes.Canceled, "SPV synchronization canceled: %v", err)