pass errors up rather than logging directly
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-09-30 23:02:07 -06:00
parent 5f11b45dd4
commit e9466bebc5
2 changed files with 12 additions and 28 deletions

View File

@@ -3,7 +3,7 @@ package bitcoinElectrum
import ( import (
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"log" "fmt"
"sync/atomic" "sync/atomic"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
@@ -43,13 +43,10 @@ func addressToScriptHash(address btcutil.Address) (string, error) {
} }
// Scans a derivation branch (m/0 or m/1) for balances until the gap limit is reached. // Scans a derivation branch (m/0 or m/1) for balances until the gap limit is reached.
func scanBranch(masterKey *hdkeychain.ExtendedKey, branch uint32, gapLimit int, spvServer string) int64 { func scanBranch(masterKey *hdkeychain.ExtendedKey, branch uint32, gapLimit int, spvServer string) (int64, error) {
log.Printf("Scanning branch m/%d/k...", branch)
branchKey, err := masterKey.Derive(branch) branchKey, err := masterKey.Derive(branch)
if err != nil { if err != nil {
log.Printf("Error deriving branch %d: %v", branch, err) return 0, fmt.Errorf("error deriving branch %d: %v", branch, err)
return 0
} }
var branchTotalBalance int64 var branchTotalBalance int64
@@ -57,35 +54,28 @@ func scanBranch(masterKey *hdkeychain.ExtendedKey, branch uint32, gapLimit int,
for i := uint32(0); ; i++ { for i := uint32(0); ; i++ {
if unusedAddressCount >= gapLimit { if unusedAddressCount >= gapLimit {
log.Printf("Gap limit of %d reached on branch m/%d. Stopping scan.", gapLimit, branch)
break break
} }
// 3. Derive child address // 3. Derive child address
address, err := deriveAddress(branchKey, i) address, err := deriveAddress(branchKey, i)
if err != nil { if err != nil {
log.Printf("Could not derive address at index %d on branch %d: %v", i, branch, err) return 0, fmt.Errorf("could not derive address at index %d on branch %d: %v", i, branch, err)
continue
} }
// 4. Get balance from Electrum server // 4. Get balance from Electrum server
balance, err := getAddressBalance(spvServer, address) balance, err := getAddressBalance(spvServer, address)
if err != nil { if err != nil {
log.Printf("Error getting balance for %s: %v", address.EncodeAddress(), err) return 0, fmt.Errorf("error getting balance for %s: %v", address.EncodeAddress(), err)
// On error, we can't know if it's used, so we continue scanning.
unusedAddressCount = 0
continue
} }
total := balance.Result.Confirmed + balance.Result.Unconfirmed total := balance.Result.Confirmed + balance.Result.Unconfirmed
if total > 0 { if total > 0 {
log.Printf("Found balance for m/%d/%d (%s): %.8f BTC", branch, i, address.EncodeAddress(), btcutil.Amount(total).ToBTC())
atomic.AddInt64(&branchTotalBalance, total) atomic.AddInt64(&branchTotalBalance, total)
unusedAddressCount = 0 // Reset gap counter on finding a used address unusedAddressCount = 0 // Reset gap counter on finding a used address
} else { } else {
log.Printf("No balance for m/%d/%d (%s)", branch, i, address.EncodeAddress())
unusedAddressCount++ unusedAddressCount++
} }
} }
return branchTotalBalance return branchTotalBalance, nil
} }

View File

@@ -3,8 +3,6 @@ package bitcoinElectrum
import ( import (
"fmt" "fmt"
"os" "os"
"sync"
"sync/atomic"
"github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/btcutil/hdkeychain"
) )
@@ -27,7 +25,7 @@ func (p *Provider) Configure() error {
p.ynabAccountID = os.Getenv("bitcoin_ynab_account") p.ynabAccountID = os.Getenv("bitcoin_ynab_account")
p.spvServer = os.Getenv("bitcoin_spv_server") p.spvServer = os.Getenv("bitcoin_spv_server")
p.coinGeckoApiKey = os.Getenv("bitcoin_coingecko_api_key") p.coinGeckoApiKey = os.Getenv("bitcoin_coingecko_api_key")
p.gapLimit = 20 p.gapLimit = 10
if zpub == "" || p.ynabAccountID == "" || p.spvServer == "" || p.coinGeckoApiKey == "" { if zpub == "" || p.ynabAccountID == "" || p.spvServer == "" || p.coinGeckoApiKey == "" {
return fmt.Errorf("this account provider is not configured") return fmt.Errorf("this account provider is not configured")
} }
@@ -43,21 +41,17 @@ func (p *Provider) Configure() error {
// Returns slices of account balances and mapped YNAB account IDs, along with an error // Returns slices of account balances and mapped YNAB account IDs, along with an error
func (p *Provider) GetBalances() ([]int, []string, error) { func (p *Provider) GetBalances() ([]int, []string, error) {
var totalSats int64 var totalSats int64
var wg sync.WaitGroup
// branch 0 = regular // branch 0 = regular
// branch 1 = change addresses // branch 1 = change addresses
for _, branch := range []uint32{0, 1} { for _, branch := range []uint32{0, 1} {
wg.Add(1) branchSats, err := scanBranch(p.masterKey, branch, p.gapLimit, p.spvServer)
go func(branch uint32) { if err != nil {
defer wg.Done() return nil, nil, fmt.Errorf("failed to scan branch '%v': %v", branch, err)
branchSats := scanBranch(p.masterKey, branch, p.gapLimit, p.spvServer) }
atomic.AddInt64(&branchSats, totalSats) totalSats += branchSats
}(branch)
} }
wg.Wait()
cad, err := convertBTCToCAD(totalSats, p.coinGeckoApiKey) cad, err := convertBTCToCAD(totalSats, p.coinGeckoApiKey)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to convert sats to CAD: %v", err) return nil, nil, fmt.Errorf("failed to convert sats to CAD: %v", err)