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
102 changes: 15 additions & 87 deletions cmd/blindbit-scan/main.go
Original file line number Diff line number Diff line change
@@ -1,103 +1,31 @@
package main

import (
"bytes"
"context"
"flag"
"os"
"os/signal"

"github.com/setavenger/blindbit-scan/internal/config"
"github.com/setavenger/blindbit-scan/internal/daemon"
nwcserver "github.com/setavenger/blindbit-scan/internal/nwc_server"
"github.com/setavenger/blindbit-scan/internal/server"
"github.com/setavenger/blindbit-scan/pkg/database"
"github.com/setavenger/blindbit-scan/pkg/logging"
"github.com/setavenger/blindbit-scan/pkg/networking/nwc"
"github.com/setavenger/blindbit-scan/internal/startup"
)

func init() {
// todo can this double reference work?
flag.StringVar(
&config.DirectoryPath,
"datadir",
config.DefaultDirectoryPath,
"Set the base directory for blindbit-scan. Default directory is ~/.blindbit-scan.",
)
flag.Parse()
}
// func init() {
// // todo can this double reference work?
// flag.StringVar(
// &config.DirectoryPath,
// "datadir",
// config.DefaultDirectoryPath,
// "Set the base directory for blindbit-scan. Default directory is ~/.blindbit-scan.",
// )

// flag.BoolVar(&config.PrivateMode, "private", false, "BlindBit Scan will run in private mode. All data on disk will be encrypted all data will only be decrypted in memory. Upon restart the unlock endpoint needs to be called to decrypt data and start the scanning.")

// flag.Parse()
// }

func main() {
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
var err error

// todo move to a go routine to avoid blocking
err = config.SetupConfigs(config.DirectoryPath)
if err != nil {
logging.L.Panic().Err(err).
Msg("startup failed, could not setup configs")
}

var d *daemon.Daemon

d, err = daemon.SetupDaemonNoWallet()
if err != nil {
logging.L.Panic().Err(err).
Msg("startup failed, could produce daemon hull")
}
w, err := database.TryLoadWalletFromDisk(config.PathDbWallet)
if err != nil {
logging.L.Warn().Err(err).
Msg("startup failed, could setup full daemon")
}
d.Wallet = w

// Setup BlindBit Nostr Wallet Connect
nwcServer := nwcserver.NewNwcServer(d)

logging.L.Info().Msg("attempting to load NWC apps from disk")
controller, err := database.TryLoadingControllerFromDisk(context.Background(), config.PathDbNWC)
if err != nil {
logging.L.Panic().Err(err).Msg("failed to create new controller")
}
logging.L.Trace().Any("apps", controller.Apps()).Msg("controller data")

controller.RegisterHandler(nwc.GET_INFO_METHOD, nwcServer.GetInfoHandler())
controller.RegisterHandler(nwc.GET_BALANCE_METHOD, nwcServer.GetBalanceHandler())
controller.RegisterHandler(nwc.LIST_UTXOS_METHOD, nwcServer.ListUtxosHandler())

err = controller.ConnectRelay()
if err != nil {
logging.L.Panic().Err(err).Msg("failed to connect to relay")
}
go controller.StartListening()

// http server
go func() {
err = server.StartNewServer(d, controller)
if err != nil {
logging.L.Panic().Err(err).
Msg("startup failed, could start server")
}
}()

// when we exit we still flush the last state
defer d.SaveWalletToDB()

go func() {
// if the keys are not setup we wait
if d.Wallet == nil || bytes.Equal(d.Wallet.SecretKeyScan[:], make([]byte, 32)) || bytes.Equal(d.Wallet.PubKeySpend[:], make([]byte, 33)) {
logging.L.Info().Msg("waiting for keys")
<-config.KeysReadyChan
d, err = daemon.SetupDaemon(config.PathDbWallet)
if err != nil {
logging.L.Panic().Err(err).
Msg("startup failed, could setup full daemon")
}
}
go d.ContinuousScan()
}()
startup.RunProgram()

// wait for program stop signal
<-interrupt
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ require (
github.com/btcsuite/btcd/btcutil v1.1.6
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0
github.com/btcsuite/goleveldb v1.0.0
github.com/dustinkirkland/golang-petname v0.0.0-20240428194347-eebcea082ee0
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0
github.com/nbd-wtf/go-nostr v0.50.0
github.com/rs/zerolog v1.33.0
github.com/setavenger/blindbitd v0.0.0-20240602183715-c4e971bba3e4
github.com/setavenger/go-bip352 v0.1.7
github.com/setavenger/go-electrum v1.1.1
github.com/spf13/viper v1.19.0
Expand Down Expand Up @@ -69,7 +69,7 @@ require (
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/text v0.24.0 // indirect
google.golang.org/protobuf v1.36.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/dustinkirkland/golang-petname v0.0.0-20240428194347-eebcea082ee0 h1:aYo8nnk3ojoQkP5iErif5Xxv0Mo0Ga/FR5+ffl/7+Nk=
github.com/dustinkirkland/golang-petname v0.0.0-20240428194347-eebcea082ee0/go.mod h1:8AuBTZBRSFqEYBPYULd+NN474/zZBLP+6WeT5S9xlAc=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
Expand Down Expand Up @@ -160,8 +162,6 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/setavenger/blindbitd v0.0.0-20240602183715-c4e971bba3e4 h1:mKbdSQiWncNMS2AExIYQXvBjxziNMeo2oE1J0YCd0qs=
github.com/setavenger/blindbitd v0.0.0-20240602183715-c4e971bba3e4/go.mod h1:+L9bcVMbECVSqYjj/LhBKWNM4uVNRayN86EcVCrnFBI=
github.com/setavenger/go-bip352 v0.1.7 h1:XckUqK+MadzOnfersbjXZuR9kLy40mW+BJGbWzYAt0A=
github.com/setavenger/go-bip352 v0.1.7/go.mod h1:ajjkB64QrjbF0+MEUjeeBlBxDaJk7VmYUN8XbOK+EKo=
github.com/setavenger/go-electrum v1.1.1 h1:2rowPjZE9BGLBHHenVwq6JWP0RKlqY2A5Bd6omprPjk=
Expand Down Expand Up @@ -242,8 +242,8 @@ golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
36 changes: 36 additions & 0 deletions internal/config/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// internal/config/auth.go
package config

import (
"crypto/rand"

"github.com/btcsuite/btcd/btcutil/base58"
petname "github.com/dustinkirkland/golang-petname"
"github.com/setavenger/blindbit-scan/pkg/types"
)

var (
authCredentials *types.AuthCredentials
)

func GenerateAuthCredentials() *types.AuthCredentials {
username := petname.Generate(2, "-")
randomBytes := make([]byte, 16)

rand.Read(randomBytes) // never returns an error

password := base58.Encode(randomBytes)

return &types.AuthCredentials{
Username: username,
Password: password,
}
}

func SetAuthCredentials(creds *types.AuthCredentials) {
authCredentials = creds
}

func GetAuthCredentials() *types.AuthCredentials {
return authCredentials
}
14 changes: 8 additions & 6 deletions internal/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package config
import (
"encoding/hex"
"log"
"log/slog"
"strings"
"time"

Expand All @@ -27,8 +26,7 @@ func LoadConfigs(pathToConfig string) (err error) {

// Handle errors reading the config file
if err = viper.ReadInConfig(); err != nil {
logging.L.Err(err).Msg("Error reading config file")
return
logging.L.Warn().Err(err).Msg("Error reading config file")
}

// map ENV var names
Expand All @@ -49,6 +47,7 @@ func LoadConfigs(pathToConfig string) (err error) {
viper.BindEnv("auth.pass", "AUTH_PASS")

viper.BindEnv("log_level", "LOG_LEVEL")
viper.BindEnv("privacy_mode", "PRIVACY_MODE")

// app seed is for umbrel inputs
// viper.BindEnv("external_app_seed", "EXTERNAL_APP_SEED")
Expand All @@ -68,6 +67,7 @@ func LoadConfigs(pathToConfig string) (err error) {
viper.SetDefault("wallet.birth_height", 840000)

viper.SetDefault("log_level", "info")
viper.SetDefault("privacy_mode", false)

// app seed
// viper.SetDefault("external_app_seed", "") // we normally don't use it
Expand All @@ -90,13 +90,15 @@ func LoadConfigs(pathToConfig string) (err error) {
AutomaticScanInterval = 1 * time.Minute
}

PrivateMode = viper.GetBool("privacy_mode")

// Basic Auth Data
AuthUser = viper.GetString("auth.user")
AuthPass = viper.GetString("auth.pass")

if AuthUser == "" || AuthPass == "" {
err := errors.New("config is missing auth settings")
slog.Error(err.Error())
if (AuthUser == "" || AuthPass == "") && !PrivateMode {
err = errors.New("config is missing auth settings")
logging.L.Err(err).Msg("")
return err
}

Expand Down
6 changes: 4 additions & 2 deletions internal/config/paths.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package config

// outsource to a common package as this is used across several blindbit programs
import "github.com/setavenger/blindbitd/src/utils"
import "github.com/setavenger/blindbit-scan/pkg/utils"

var (
DirectoryPath = "~/.blindbit-scan"
Expand All @@ -10,6 +10,7 @@ var (
PathConfig string
PathDbWallet string
PathDbNWC string
PathDbAuth string
)

// needed for the flag default
Expand All @@ -20,6 +21,7 @@ const PathEndingConfig = "/blindbit.toml"
const PathEndingWallet = dataPath + "/wallet"
const PathEndingNWC = dataPath + "/nwc"
const PathEndingKeys = dataPath + "/keys"
const PathEndingBasicAuth = dataPath + "/basic_auth"

func SetPaths(baseDirectory string) {
if baseDirectory != "" {
Expand All @@ -34,7 +36,7 @@ func SetPaths(baseDirectory string) {
PathConfig = DirectoryPath + PathEndingConfig
PathDbWallet = DirectoryPath + PathEndingWallet
PathDbNWC = DirectoryPath + PathEndingNWC

PathDbAuth = DirectoryPath + PathEndingBasicAuth
// create the directories
utils.TryCreateDirectoryPanic(DirectoryPath)

Expand Down
13 changes: 13 additions & 0 deletions internal/config/privatemode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package config

var (
UnlockChan = make(chan string)
)

func init() {
UnlockChan = make(chan string)
}

func Unlock() {
close(UnlockChan)
}
19 changes: 18 additions & 1 deletion internal/config/vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,24 @@ import (
"github.com/btcsuite/btcd/chaincfg"
)

type PrivateModeSetup struct {
ScanSecretKey [32]byte
SpendPubKey [33]byte
BirthHeight uint64
LabelCount int
Password string
BasicAuthUser string
BasicAuthPass string
}

func init() {
KeysReadyChan = make(chan struct{})
PrivateModeSetupChan = make(chan PrivateModeSetup)
}

var (
PrivateMode bool

// ExposeHttpHost if set gRPC will be exposed via http and not unix socket. This variable also defines the where it will be exposed.
ExposeHttpHost string

Expand Down Expand Up @@ -41,7 +54,9 @@ var (

SpendPubKey [33]byte

BirthHeight uint64
// A reasonable default birth height
// Silent Payments was not used a lot before that
BirthHeight uint64 = 890000

LabelCount int

Expand All @@ -52,4 +67,6 @@ var (

// keys are ready chan
KeysReadyChan chan struct{}

PrivateModeSetupChan chan PrivateModeSetup
)
Loading