orchestrator

package module
v0.0.0-...-ba872f6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 44 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultCoreVariantID = "patched"

DefaultCoreVariantID is the variant used when no settings file exists. "patched" is the only variant available on every network (including mainnet) so it's a safe default that won't get clamped to empty.

View Source
const DefaultTorProxy = "127.0.0.1:9050"

DefaultTorProxy is the SOCKS5 address of a standard local Tor daemon. Tor Browser exposes the same proxy on 127.0.0.1:9150.

View Source
const PresyncMessagePrefix = "Pre-synchronizing blockheaders"

PresyncMessagePrefix is the prefix used in the synthesized startup error when bitcoind is in the BIP324 headers-presync phase. The connection monitor's startup-pattern list includes this prefix so the message is classified as startupError rather than connectionError and the UI shows "Pre-synchronizing blockheaders" instead of a frozen 0/0 connected state.

Variables

This section is empty.

Functions

func ActiveCoreBinaryPath

func ActiveCoreBinaryPath(dataDir, bitwindowDir string, configs []BinaryConfig, binaryName string) string

ActiveCoreBinaryPath returns the on-disk path for the bitcoind variant currently selected in orchestrator_settings.json. Used by CLI commands and the testharness to find the active build without constructing a full Orchestrator. Non-bitcoind names always resolve via the legacy flat layout.

func AddDescriptorChecksum

func AddDescriptorChecksum(desc string) (string, error)

AddDescriptorChecksum adds a checksum to a descriptor string.

func BinDir

func BinDir(dataDir string) string

BinDir returns the directory where binaries are stored.

func BinaryPath

func BinaryPath(dataDir, binaryName string) string

BinaryPath returns the full path to a binary executable.

func CallBitcoindRPC

func CallBitcoindRPC(ctx context.Context, url, user, password, method string, params []interface{}) (json.RawMessage, error)

CallBitcoindRPC issues a single JSON-RPC call to bitcoind. Every orchestrator → bitcoind RPC goes through here so the concurrency cap is enforced at the one chokepoint — callers can't bypass it by reaching for http.DefaultClient. On overflow the goroutine waits for a free slot and a warning is logged with the method that hit it.

func ConfigFilePath

func ConfigFilePath(dir string) string

ConfigFilePath returns the path to chains_config.json in the given directory.

func CoreBinaryPath

func CoreBinaryPath(dataDir string, _ CoreVariantSpec, binaryName string) string

CoreBinaryPath returns the on-disk path for the active Bitcoin Core daemon. All variants share a single `bin/bitcoind` location — switching variants re-downloads and overwrites whatever was there.

func CoreVariantInstalled

func CoreVariantInstalled(dataDir string, v CoreVariantSpec, binaryName string) bool

CoreVariantInstalled reports whether the variant's binary exists on disk.

func DefaultBitwindowDir

func DefaultBitwindowDir() string

DefaultBitwindowDir returns the default BitWindow data directory. Uses the BitWindow binary directory config loaded from chains_config.json.

func DefaultDataDir

func DefaultDataDir() string

DefaultDataDir returns the default data directory for the orchestrator. This is the same as the BitWindow data directory, since orchestrator stores its assets (binaries, configs) alongside BitWindow.

func DescriptorChecksum

func DescriptorChecksum(desc string) (string, error)

DescriptorChecksum computes the checksum for a Bitcoin Core descriptor string.

func LogDir

func LogDir(dataDir string) string

LogDir returns the directory where logs are stored.

func PidDir

func PidDir(dataDir string) string

PidDir returns the directory where PID files are stored.

func SaveSettings

func SaveSettings(bitwindowDir string, s OrchestratorSettings) error

SaveSettings writes orchestrator_settings.json atomically. Bytes hit disk before the rename and the parent directory is fsync'd on POSIX so a crash can't leave the file half-written or replace a valid file with a tmp that isn't yet durable.

func SettingsPath

func SettingsPath(bitwindowDir string) string

SettingsPath returns the path to orchestrator_settings.json.

func StripPlatformSuffix

func StripPlatformSuffix(name string) string

StripPlatformSuffix removes platform/architecture and version suffixes from extracted filenames to produce a clean binary name.

Examples:

"thunder-orchard-0.1.0-x86_64-apple-darwin" -> "thunder-orchard"
"grpcurl_1.9.1_linux_x86_64" -> "grpcurl"
"bip300301-enforcer-latest-x86_64-unknown-linux-gnu" -> "bip300301-enforcer-latest"

func TestSidechainAppBundle

func TestSidechainAppBundle(dataDir, binaryName string) string

TestSidechainAppBundle returns the .app bundle path for a test sidechain build on macOS, or "" if not on macOS / no bundle present.

func TestSidechainBinaryPath

func TestSidechainBinaryPath(dataDir, binaryName string) string

TestSidechainBinaryPath resolves the launchable binary inside a test build's directory. Three shapes are supported:

  • Linux: `<dir>/<binaryName>` next to `lib/`+`data/`.
  • macOS: `<dir>/<TitleCase>.app/Contents/MacOS/<TitleCase>` (Flutter bundle). We walk the directory once to find the `.app` so the spec doesn't need to encode TitleCase mappings (`thunder` → `Thunder`, `bitassets` → `BitAssets`, etc.).
  • Windows: `<dir>/<binaryName>.exe` — single self-contained .exe.

func TestSidechainDir

func TestSidechainDir(dataDir, binaryName string) string

TestSidechainDir returns the per-binary directory that test/alternative sidechain builds extract into. Test builds are full Flutter app bundles with sibling lib/data trees (Linux) or `.app` packages (macOS); each sidechain therefore needs its own subfolder so libraries don't collide.

func WaitForHealthy

func WaitForHealthy(ctx context.Context, checker HealthChecker, interval time.Duration) error

WaitForHealthy polls the health checker until it succeeds or the context is canceled.

func WatchConfigFile

func WatchConfigFile(path string, onChange func([]BinaryConfig), log zerolog.Logger) (func(), error)

WatchConfigFile watches chains_config.json for changes and calls onChange.

Types

type AddressInfo

type AddressInfo struct {
	Address     string `json:"address"`
	IsMine      bool   `json:"ismine"`
	IsWatchOnly bool   `json:"iswatchonly"`
	IsScript    bool   `json:"isscript"`
	IsWitness   bool   `json:"iswitness"`
	HdKeyPath   string `json:"hdkeypath,omitempty"`
}

type BinaryConfig

type BinaryConfig struct {
	// Core identity — Dart: Binary constructor params (L61-72)
	Name        string // Dart: Binary.name (internal identifier, e.g. "bitcoind", "thunder")
	DisplayName string // Dart: Binary.name in subclass (e.g. "Bitcoin Core (Patched)")
	BinaryName  string // Dart: Binary.binary (executable name, from metadata.downloadConfig.binary)
	Version     string // Dart: Binary.version
	Description string // Dart: Binary.description
	RepoURL     string // Dart: Binary.repoUrl
	Port        int    // Dart: Binary.port
	Host        string // RPC host; empty = 127.0.0.1
	ChainLayer  int    // Dart: Binary.chainLayer (0=utility, 1=L1, 2=sidechain)
	Slot        int    // Dart: Sidechain.slot (0 for non-sidechains)

	// Directory configuration — Dart: DirectoryConfig class
	DataDir        map[string]string // os -> subdir under AppDir() (default for all networks)
	DataDirMainnet map[string]string // os -> subdir (mainnet override, empty = use DataDir)
	IsBitcoinCore  bool              // Linux appdir exception: ~/ instead of ~/.local/share

	// Flutter frontend directory — Dart: flutterFrontendDir() extension (L1306-1353)
	// Per-OS subdir under the platform app support dir. Empty = no frontend.
	FlutterFrontendDir map[string]string

	// Primary download configuration — Dart: MetadataConfig.downloadConfig
	DownloadSource   DownloadSource
	DownloadURLs     map[string]string // network -> base URL ("default", etc.)
	Files            map[string]string // os -> filename or regex pattern
	ExtractSubfolder map[string]string // os -> subfolder to extract from zip (empty = root)

	// Core variant configuration — only populated for the bitcoincore entry.
	// Keys are variant IDs ("core", "patched", "knots").
	Variants map[string]CoreVariantSpec

	// Alternative download configuration — Dart: MetadataConfig.alternativeDownloadConfig
	// Used for test chain builds when SettingsProvider selects it.
	AltDownloadURLs     map[string]string // network -> base URL
	AltBinaryName       string            // Dart: alternativeDownloadConfig.binary
	AltFiles            map[string]string // os -> filename
	AltExtractSubfolder map[string]string // os -> subfolder

	// Dart: MetadataConfig.updateable
	Updateable bool

	// Dart: Binary.startupLogPatterns (regex strings)
	StartupLogPatterns []string

	// Dart: Binary.extraBootArgs
	ExtraBootArgs []string

	// Health check (Go-specific, not in Dart Binary)
	HealthCheckType HealthCheckType
	HealthCheckRPC  string // JSON-RPC method for health check

	// Dependencies: names of binaries that must be running before this one
	Dependencies []string
}

BinaryConfig is the 1:1 Go port of Dart's Binary abstract class + subclasses. Every field maps to a Dart property from binaries.dart / sidechains.dart.

func AllDefaults

func AllDefaults() []BinaryConfig

AllDefaults returns configs for every known binary, loaded from the embedded chains_config.json. This is the single source of truth.

func AllSidechains

func AllSidechains() []BinaryConfig

AllSidechains returns configs for all sidechain binaries (ChainLayer == 2). Dart: Sidechain.all (sidechains.dart L49-57)

func BinaryConfigByName

func BinaryConfigByName(name string) (BinaryConfig, bool)

BinaryConfigByName returns the default config for a binary by name. Dart: Sidechain.fromString (sidechains.dart L23-47)

func BinaryConfigBySlot

func BinaryConfigBySlot(slot int) (BinaryConfig, bool)

BinaryConfigBySlot returns the sidechain config for a given slot number. Dart: Sidechain.fromSlot (sidechains.dart L59-66)

func LoadConfigFile

func LoadConfigFile(path string, log zerolog.Logger) []BinaryConfig

LoadConfigFile loads binary configs from a chains_config.json file, overlaying it on top of the embedded defaults. Embedded fields fill in anything missing from the on-disk file — critical for fields the user has no business overriding (is_bitcoin_core, health_check) when their on-disk file pre-dates a schema bump.

Falls back to embedded only when the on-disk file is missing or unparseable.

func (BinaryConfig) AltBaseURL

func (c BinaryConfig) AltBaseURL(network string) string

AltBaseURL returns the alternative download base URL for a given network.

func (BinaryConfig) BaseURL

func (c BinaryConfig) BaseURL(network string) string

BaseURL returns the download base URL for a given network. Falls back to "default", then to the first available URL.

func (BinaryConfig) Downloadable

func (c BinaryConfig) Downloadable() bool

Downloadable returns true if this binary has download URLs configured.

func (BinaryConfig) FileForPlatform

func (c BinaryConfig) FileForPlatform() (string, error)

FileForPlatform returns the download filename for the current os-arch.

func (BinaryConfig) IsSidechain

func (c BinaryConfig) IsSidechain() bool

IsSidechain returns true if this binary is a sidechain (ChainLayer == 2).

func (BinaryConfig) RPCAddr

func (c BinaryConfig) RPCAddr() string

RPCAddr returns the host:port of the binary's RPC endpoint.

func (BinaryConfig) RPCHost

func (c BinaryConfig) RPCHost() string

RPCHost returns the host the binary's RPC is reached on.

func (BinaryConfig) RPCURL

func (c BinaryConfig) RPCURL() string

RPCURL returns the http URL of the binary's RPC endpoint.

type BinaryStatus

type BinaryStatus struct {
	Name            string
	DisplayName     string
	Running         bool
	Healthy         bool
	Pid             int
	Uptime          time.Duration
	ChainLayer      int
	Port            int
	Error           string
	Connected       bool   // from ConnectionMonitor
	StartupError    string // warmup message (e.g. "Loading block index...")
	ConnectionError string // real connection error
	Stopping        bool   // binary is being stopped
	Initializing    bool   // binary is starting up / restarting
	ConnectModeOnly bool   // willfully stopped, only watching for external restart
	Downloadable    bool   // binary has download URLs configured
	Description     string // short description of the binary
	Downloaded      bool   // binary file exists on disk
	BinaryPath      string // absolute path to the launchable binary (variant-aware), empty when not downloaded
	PortInUse       bool   // port is reachable (something is listening)
	Version         string // configured version string
	RepoURL         string // source code repository URL
	StartupLogs     []StartupLogLine
}

BinaryStatus represents the current state of a managed binary.

type BitcoindHealthCheck

type BitcoindHealthCheck struct {
	URL      string
	User     string
	Password string
	Timeout  time.Duration
}

BitcoindHealthCheck calls getblockchaininfo through the shared orchestrator → bitcoind RPC gate. When bitcoind is still in BIP324 headers-presync the RPC reports blocks=0/headers=0 cleanly; a vanilla success signal would freeze the UI at 0/0, so the checker synthesises a presync startup error in that one case. One RPC only.

func (*BitcoindHealthCheck) Check

func (h *BitcoindHealthCheck) Check(ctx context.Context) error

type BumpFeeResult

type BumpFeeResult struct {
	Txid        string   `json:"txid"`
	OriginalFee float64  `json:"origfee"`
	NewFee      float64  `json:"fee"`
	Errors      []string `json:"errors,omitempty"`
}

type CachedConnection

type CachedConnection[T any] struct {
	// contains filtered or unexported fields
}

CachedConnection wraps any Connection with TTL cache + single-flight + preserve-last-good-on-error. THIS is the only caching primitive in the orchestrator's chain-tip plumbing — every chain ends up here so the UI sees identical timing semantics across the board.

func (*CachedConnection[T]) Fetch

func (c *CachedConnection[T]) Fetch(ctx context.Context) (T, error)

type ChainSyncResult

type ChainSyncResult struct {
	Blocks  int64
	Headers int64
	Time    int64
	Error   string
}

ChainSyncResult is one chain's tip snapshot. Error is set on failure; the numeric fields are best-effort zero in that case.

type ConnectRPCHealthCheck

type ConnectRPCHealthCheck struct {
	URL     string // e.g. "http://localhost:50051/cusf.mainchain.v1.ValidatorService/GetChainTip"
	Timeout time.Duration
}

ConnectRPCHealthCheck POSTs an empty JSON body to a Connect-JSON endpoint and inspects the response. On Connect-JSON, success is HTTP 200 with a JSON body that has no `code` field; failure is HTTP 4xx/5xx with `{"code":"...","message":"..."}`. Warmup errors (daemon up, not ready) come back here as an error message that the connection monitor pattern-matches into startupError — e.g. the enforcer returning "Validator is not synced" while still catching up to the mainchain tip.

func (*ConnectRPCHealthCheck) Check

type Connection

type Connection[T any] interface {
	Fetch(ctx context.Context) (T, error)
}

Connection is the only thing that differs between chains: a pure RPC call that returns one typed value or an error. No caching, no single-flight, no error preservation. Implementations wrap their wire protocol and nothing else — the surrounding machinery (TTL cache, single-flight, last-good-on-error) lives in CachedConnection and applies uniformly to L1 and L2.

func Project

func Project[A, B any](inner Connection[A], fn func(A) B) Connection[B]

Project decorates a Connection[A] with a transform A→B.

type ConnectionMonitor

type ConnectionMonitor struct {
	Name    string // binary config name (e.g. "bitcoind")
	Checker HealthChecker
	// contains filtered or unexported fields
}

ConnectionMonitor is a 1:1 Go port of Dart's RPCConnection (rpc_connection.dart).

It manages:

  • A persistent 1-second health-check timer (Dart: connectionTimer)
  • A 500ms restart timer that auto-restarts crashed processes (Dart: restartTimer)
  • connectModeOnly: after a willful stop, the timer keeps pinging to detect externally started processes, but suppresses errors silently

Dart equivalents:

  • connectionTimer → connectionTicker
  • testConnection() → testConnection()
  • connectModeOnly → connectModeOnly
  • connected → connected
  • startConnectionTimer → StartConnectionTimer()
  • restartTimer → restartTicker
  • startRestartTimer → StartRestartTimer()
  • stop() → MarkStopped()
  • markDisconnected() → MarkDisconnected()
  • _pingEpoch → pingEpoch

func NewConnectionMonitor

func NewConnectionMonitor(name string, checker HealthChecker, startupPatterns []string, log zerolog.Logger) *ConnectionMonitor

NewConnectionMonitor creates a monitor for a binary. Does NOT start timers — call StartConnectionTimer() and StartRestartTimer().

func (*ConnectionMonitor) AddStartupLog

func (m *ConnectionMonitor) AddStartupLog(ts time.Time, msg string)

AddStartupLog appends a startup progress message. Keeps the last 20. Dart: Binary.addStartupLog

func (*ConnectionMonitor) ConnectModeOnly

func (m *ConnectionMonitor) ConnectModeOnly() bool

ConnectModeOnly returns whether the monitor is in connect-mode-only (willfully stopped, only watching for external restart).

func (*ConnectionMonitor) Connected

func (m *ConnectionMonitor) Connected() bool

Connected returns the current connection state. Dart: RPCConnection.connected

func (*ConnectionMonitor) ConnectionError

func (m *ConnectionMonitor) ConnectionError() string

ConnectionError returns the last connection error (empty if connected).

func (*ConnectionMonitor) InitializingBinary

func (m *ConnectionMonitor) InitializingBinary() bool

InitializingBinary returns whether the binary is currently starting up.

func (*ConnectionMonitor) MarkDisconnected

func (m *ConnectionMonitor) MarkDisconnected()

MarkDisconnected marks the connection as disconnected without sending a stop. Dart: RPCConnection.markDisconnected() (rpc_connection.dart L387-396) Used when the binary has already been stopped externally.

func (*ConnectionMonitor) MarkStopped

func (m *ConnectionMonitor) MarkStopped()

MarkStopped marks the connection as stopped. Dart: RPCConnection.stop() (rpc_connection.dart L356-383) Timer keeps running in connect-mode-only: it keeps pinging to detect externally started processes but suppresses errors silently.

func (*ConnectionMonitor) SetConnectionError

func (m *ConnectionMonitor) SetConnectionError(errMsg string)

SetConnectionError sets the connection error from an external source (e.g. process crash). This is how process exit errors flow into the UI. The error "sticks" — it won't be overwritten by generic "connection refused" from the next health check ping. It's only cleared when the process reconnects successfully.

func (*ConnectionMonitor) SetInitializing

func (m *ConnectionMonitor) SetInitializing(v bool)

SetInitializing flips the initializing flag and fires onChange so the frontend sees the transition immediately (before the next 1s ping). Orchestrator calls this around process.Start so the UI shows an "initializing" spinner instead of a red X during the fresh-boot window where testConnection is still failing.

func (*ConnectionMonitor) SetOnChange

func (m *ConnectionMonitor) SetOnChange(fn func())

SetOnChange sets the callback fired whenever connection state changes.

func (*ConnectionMonitor) SetStopping

func (m *ConnectionMonitor) SetStopping(v bool)

SetStopping flips the stopping flag and fires onChange so the frontend sees the transition immediately. Orchestrator calls SetStopping(true) right before signalling the process, so the UI can badge the binary as "stopping" during the shutdown window (between signal and exit). MarkStopped resets the flag to false once the process is actually gone.

func (*ConnectionMonitor) StartConnectionTimer

func (m *ConnectionMonitor) StartConnectionTimer(ctx context.Context)

StartConnectionTimer starts the 1-second periodic health check. Dart: RPCConnection.startConnectionTimer() (rpc_connection.dart L299-319)

1. Pings once immediately 2. Starts a 1-second periodic timer

Returns after the first ping completes (so caller knows if already connected).

func (*ConnectionMonitor) StartRestartTimer

func (m *ConnectionMonitor) StartRestartTimer(ctx context.Context, restartFunc func(ctx context.Context) error, exitedFunc func() (int, bool))

StartRestartTimer starts the 500ms restart timer that auto-restarts crashed processes. Dart: RPCConnection.startRestartTimer() (rpc_connection.dart L236-286)

Only call this if we STARTED the process ourselves (not for adopted processes). Dart: "only start restart timer if this process starts the binary!"

func (*ConnectionMonitor) StartupError

func (m *ConnectionMonitor) StartupError() string

StartupError returns the current startup/warmup message (e.g. "Loading block index..."). Dart: RPCConnection.startupError

func (*ConnectionMonitor) StartupLogs

func (m *ConnectionMonitor) StartupLogs() []StartupLogLine

StartupLogs returns a copy of the recent startup progress messages.

func (*ConnectionMonitor) StopAllTimers

func (m *ConnectionMonitor) StopAllTimers()

StopAllTimers stops both timers.

func (*ConnectionMonitor) StopConnectionTimer

func (m *ConnectionMonitor) StopConnectionTimer()

StopConnectionTimer stops the periodic health check timer.

func (*ConnectionMonitor) StopRestartTimer

func (m *ConnectionMonitor) StopRestartTimer()

StopRestartTimer stops the restart timer. Dart: restartTimer?.cancel() in stop()

func (*ConnectionMonitor) StoppingBinary

func (m *ConnectionMonitor) StoppingBinary() bool

StoppingBinary returns whether the binary is currently being stopped.

func (*ConnectionMonitor) WaitForConnected

func (m *ConnectionMonitor) WaitForConnected(ctx context.Context) error

WaitForConnected blocks until connected or context is cancelled. Dart: RPCConnection.waitForConnected() (rpc_connection.dart L293-296)

type CoreBalances

type CoreBalances struct {
	Mine struct {
		Trusted          float64 `json:"trusted"`
		UntrustedPending float64 `json:"untrusted_pending"`
		Immature         float64 `json:"immature"`
	} `json:"mine"`
	Watchonly *struct {
		Trusted          float64 `json:"trusted"`
		UntrustedPending float64 `json:"untrusted_pending"`
		Immature         float64 `json:"immature"`
	} `json:"watchonly,omitempty"`
}

type CoreStatusClient

type CoreStatusClient struct {
	// contains filtered or unexported fields
}

CoreStatusClient is a minimal Bitcoin Core JSON-RPC client for startup sequencing checks. It only implements the 2-3 calls needed to check wallet unlock and IBD status.

func NewCoreStatusClient

func NewCoreStatusClient(host string, port int, user, password string) *CoreStatusClient

func (*CoreStatusClient) BumpFee

func (c *CoreStatusClient) BumpFee(ctx context.Context, walletName, txid string) (*BumpFeeResult, error)

func (*CoreStatusClient) CreateRawTransaction

func (c *CoreStatusClient) CreateRawTransaction(ctx context.Context, inputs []map[string]interface{}, outputs []map[string]interface{}) (string, error)

func (*CoreStatusClient) GetAddressInfo

func (c *CoreStatusClient) GetAddressInfo(ctx context.Context, walletName, address string) (*AddressInfo, error)

func (*CoreStatusClient) GetBalances

func (c *CoreStatusClient) GetBalances(ctx context.Context, walletName string) (*CoreBalances, error)

func (*CoreStatusClient) GetBlockCount

func (c *CoreStatusClient) GetBlockCount(ctx context.Context) (int64, error)

GetBlockCount returns the current block height.

func (*CoreStatusClient) GetNewAddress

func (c *CoreStatusClient) GetNewAddress(ctx context.Context, walletName string) (string, error)

func (*CoreStatusClient) GetRawTransaction

func (c *CoreStatusClient) GetRawTransaction(ctx context.Context, txid string, verbose bool) (*RawTransaction, error)

func (*CoreStatusClient) GetTransaction

func (c *CoreStatusClient) GetTransaction(ctx context.Context, walletName, txid string) (*CoreTransactionDetail, error)

func (*CoreStatusClient) GetWalletInfo

func (c *CoreStatusClient) GetWalletInfo(ctx context.Context, walletName string) (*WalletInfo, error)

func (*CoreStatusClient) IsHeaderSyncComplete

func (c *CoreStatusClient) IsHeaderSyncComplete(ctx context.Context) (bool, error)

IsHeaderSyncComplete reports whether Bitcoin Core has finished downloading headers — block IBD may still be in progress. Enforcer only needs headers to start validating BIP300/301 activity (it syncs blocks alongside Core), so gating on IBD forces users to wait for the full chain when they don't have to. Signal: headers > 0 AND headers >= blocks AND we're past the "still connecting / no chain" bootstrap phase (headers > 10).

func (*CoreStatusClient) IsWalletLoaded

func (c *CoreStatusClient) IsWalletLoaded(ctx context.Context) (bool, error)

IsWalletLoaded checks if a wallet is loaded in Bitcoin Core.

func (*CoreStatusClient) ListDescriptors

func (c *CoreStatusClient) ListDescriptors(ctx context.Context, walletName string) (*ListDescriptorsResult, error)

func (*CoreStatusClient) ListTransactionsWallet

func (c *CoreStatusClient) ListTransactionsWallet(ctx context.Context, walletName string, count int) ([]CoreTransaction, error)

func (*CoreStatusClient) ListUnspentForAddresses

func (c *CoreStatusClient) ListUnspentForAddresses(ctx context.Context, walletName string, minConf int, addresses []string) ([]CoreUnspent, error)

func (*CoreStatusClient) ListUnspentWallet

func (c *CoreStatusClient) ListUnspentWallet(ctx context.Context, walletName string) ([]CoreUnspent, error)

func (*CoreStatusClient) Send

func (c *CoreStatusClient) Send(ctx context.Context, walletName string, destinations map[string]float64, feeRate float64) (string, error)

Send uses the "send" RPC (Core 22+) with multiple destinations.

func (*CoreStatusClient) SendRawTransaction

func (c *CoreStatusClient) SendRawTransaction(ctx context.Context, hexString string) (string, error)

func (*CoreStatusClient) SendToAddress

func (c *CoreStatusClient) SendToAddress(ctx context.Context, walletName, address string, amountBTC float64) (string, error)

SendToAddress sends BTC from the given wallet. Returns txid.

func (*CoreStatusClient) SignRawTransactionWithWallet

func (c *CoreStatusClient) SignRawTransactionWithWallet(ctx context.Context, walletName, hexString string) (*SignRawResult, error)

func (*CoreStatusClient) Stop

func (c *CoreStatusClient) Stop(ctx context.Context) error

Stop acks immediately; the caller must wait for process exit for the flush.

type CoreTransaction

type CoreTransaction struct {
	Txid          string  `json:"txid"`
	Amount        float64 `json:"amount"`
	Fee           float64 `json:"fee"`
	Confirmations int64   `json:"confirmations"`
	BlockHash     string  `json:"blockhash,omitempty"`
	BlockTime     int64   `json:"blocktime,omitempty"`
	Time          int64   `json:"time"`
	TimeReceived  int64   `json:"timereceived"`
	Category      string  `json:"category"` // send, receive, generate, immature, orphan
	Address       string  `json:"address,omitempty"`
	Label         string  `json:"label,omitempty"`
	Vout          uint32  `json:"vout"`
}

type CoreTransactionDetail

type CoreTransactionDetail struct {
	Txid          string  `json:"txid"`
	Amount        float64 `json:"amount"`
	Fee           float64 `json:"fee"`
	Confirmations int64   `json:"confirmations"`
	BlockHash     string  `json:"blockhash,omitempty"`
	BlockTime     int64   `json:"blocktime,omitempty"`
	Time          int64   `json:"time"`
	TimeReceived  int64   `json:"timereceived"`
	Hex           string  `json:"hex"`
	Details       []struct {
		Address  string  `json:"address"`
		Category string  `json:"category"`
		Amount   float64 `json:"amount"`
		Vout     uint32  `json:"vout"`
		Fee      float64 `json:"fee,omitempty"`
	} `json:"details"`
}

type CoreUnspent

type CoreUnspent struct {
	Txid          string  `json:"txid"`
	Vout          uint32  `json:"vout"`
	Address       string  `json:"address"`
	Amount        float64 `json:"amount"`
	Confirmations int64   `json:"confirmations"`
	ScriptPubKey  string  `json:"scriptPubKey"`
	Spendable     bool    `json:"spendable"`
	Solvable      bool    `json:"solvable"`
	Safe          bool    `json:"safe"`
}

type CoreVariantSpec

type CoreVariantSpec struct {
	ID                string
	Subfolder         string
	BaseURL           string
	Files             map[string]string // os -> filename
	AvailableNetworks []string
}

CoreVariantSpec describes a single Bitcoin Core build variant.

func FilterVariantsForNetwork

func FilterVariantsForNetwork(variants map[string]CoreVariantSpec, network string) []CoreVariantSpec

FilterVariantsForNetwork returns variants available for the given network. "patched" is available on every chain — including mainnet — so the dropdown always has at least one item the user can pick.

func (CoreVariantSpec) AvailableOn

func (v CoreVariantSpec) AvailableOn(network string) bool

AvailableOn reports whether the variant is offered for the given network.

func (CoreVariantSpec) FileForPlatform

func (v CoreVariantSpec) FileForPlatform() (string, error)

FileForPlatform returns the variant's download filename for the current os-arch.

type DeleteEvent

type DeleteEvent struct {
	Path  string
	Error string
}

DeleteEvent is emitted for each path during DeleteFiles. Error is empty when the path was removed (or moved to backup) successfully.

type DownloadManager

type DownloadManager struct {

	// CoreVariant returns the active Bitcoin Core variant spec to use for
	// download/extract. It is consulted only when config.IsBitcoinCore. May
	// be left nil — in that case the download falls back to the legacy
	// per-network file selection (default vs. variant).
	CoreVariant func() (CoreVariantSpec, bool)

	// SidechainVariant resolves the test/alternative download spec for a
	// layer-2 binary. ok=false means use the production fields. The "test"
	// suffix is also used to namespace the in-flight key and the on-disk
	// extract dir so prod and test builds can coexist without clobbering.
	SidechainVariant func(BinaryConfig) (sidechainVariantSpec, bool)
	// contains filtered or unexported fields
}

func NewDownloadManager

func NewDownloadManager(dataDir, configFilePath string, log zerolog.Logger) *DownloadManager

func (*DownloadManager) Download

func (d *DownloadManager) Download(ctx context.Context, config BinaryConfig, network string, force bool) (<-chan DownloadProgress, error)

Download downloads a binary to the bin directory with progress reporting. It determines the download strategy based on config (GitHub vs direct).

func (*DownloadManager) DownloadWithOptions

func (d *DownloadManager) DownloadWithOptions(ctx context.Context, config BinaryConfig, network string, force bool, opts DownloadOptions) (<-chan DownloadProgress, error)

DownloadWithOptions is Download with per-call overrides (see DownloadOptions).

func (*DownloadManager) State

func (d *DownloadManager) State(binaryName string) (DownloadState, bool)

State returns the latest download progress snapshot for a binary, keyed by its logical name (e.g. "bitcoind", "thunder"). ok=false means no download is in flight for that name. Read by Orchestrator.GetSyncStatus so the polled API can report download progress without callers needing to subscribe to the DownloadBinary / StartWithL1 streams.

func (*DownloadManager) States

func (d *DownloadManager) States() map[string]DownloadState

States returns a snapshot of every download currently in flight, keyed by the binary's logical name. Entries are inserted when a download starts and deleted when its goroutine returns (Done or Error), so the snapshot is naturally restricted to live downloads — no need for a Running check.

type DownloadOptions

type DownloadOptions struct {
	// ForceBackend skips the SidechainVariant resolver so the prod-download
	// URL/path is used even when UseTestSidechains is on. Set by sidechain
	// Flutter frontends self-booting their backend.
	ForceBackend bool
}

DownloadOptions tweaks Download behaviour per-call. Defaults are the equivalent of the original Download(ctx, config, network, force) call.

type DownloadProgress

type DownloadProgress struct {
	// Progress in megabytes — bytes are converted at the source (downloadFile)
	// so every consumer (UI, logs, tests) sees the same units.
	MBDownloaded int64
	MBTotal      int64 // -1 if unknown
	Message      string
	Done         bool
	Error        error
}

type DownloadSource

type DownloadSource int
const (
	DownloadSourceDirect DownloadSource = iota // Direct URL from releases.drivechain.info
	DownloadSourceGitHub                       // GitHub releases API (regex matching)
)

type DownloadState

type DownloadState struct {
	MBDownloaded int64
	MBTotal      int64
	Message      string
	Running      bool
}

DownloadState is the in-memory snapshot of the latest progress event for a binary. The orchestrator's GetSyncStatus reads from here so the polled API can carry download progress without a separate stream — frontends never need to subscribe to DownloadBinary / StartWithL1 just to draw a progress bar.

type GatherSpec

type GatherSpec struct {
	Binary     ResetBinary
	Categories []ResetCategory
}

GatherSpec is one binary plus the categories of its data to gather.

type HealthCheckOpts

type HealthCheckOpts struct {
	User     string
	Password string
}

HealthCheckOpts provides optional configuration for health checkers.

type HealthCheckType

type HealthCheckType int
const (
	HealthCheckTCP        HealthCheckType = iota
	HealthCheckJSONRPC                    // JSON-RPC call (e.g. getblockcount)
	HealthCheckConnectRPC                 // Connect-JSON POST (e.g. cusf.mainchain.v1.ValidatorService/GetChainTip)
)

type HealthChecker

type HealthChecker interface {
	Check(ctx context.Context) error
}

HealthChecker checks if a binary is healthy.

func NewHealthChecker

func NewHealthChecker(config BinaryConfig, opts ...HealthCheckOpts) HealthChecker

NewHealthChecker creates the appropriate health checker for a binary config.

type JSONRPCHealthCheck

type JSONRPCHealthCheck struct {
	URL      string
	Method   string
	User     string
	Password string
	Timeout  time.Duration
}

JSONRPCHealthCheck sends a JSON-RPC request to verify the service is responding.

func (*JSONRPCHealthCheck) Check

func (h *JSONRPCHealthCheck) Check(ctx context.Context) error

type ListDescriptorsResult

type ListDescriptorsResult struct {
	WalletName  string `json:"wallet_name"`
	Descriptors []struct {
		Desc   string `json:"desc"`
		Active bool   `json:"active"`
		Range  []int  `json:"range,omitempty"`
	} `json:"descriptors"`
}

type LogEntry

type LogEntry struct {
	Timestamp time.Time
	Stream    string // "stdout" or "stderr"
	Line      string
}

LogEntry represents a line of output from a managed process.

type MainchainBalance

type MainchainBalance struct {
	Confirmed   float64
	Unconfirmed float64
}

MainchainBalance holds confirmed + unconfirmed balances from bitcoind.

type MainchainBlockchainInfo

type MainchainBlockchainInfo struct {
	Chain                string  `json:"chain"`
	Blocks               int     `json:"blocks"`
	Headers              int     `json:"headers"`
	BestBlockHash        string  `json:"bestblockhash"`
	Difficulty           float64 `json:"difficulty"`
	Time                 int64   `json:"time"`
	MedianTime           int64   `json:"mediantime"`
	VerificationProgress float64 `json:"verificationprogress"`
	InitialBlockDownload bool    `json:"initialblockdownload"`
	ChainWork            string  `json:"chainwork"`
	SizeOnDisk           int64   `json:"size_on_disk"`
	Pruned               bool    `json:"pruned"`
}

MainchainBlockchainInfo holds the result of bitcoind's getblockchaininfo.

type ManagedProcess

type ManagedProcess struct {
	Config       BinaryConfig
	Pid          int
	Cmd          *exec.Cmd
	BinPath      string // resolved executable path used for this process
	PidName      string // PID-file basename used for this process
	Started      time.Time
	Adopted      bool // true if this process was found from a previous session
	ForceBackend bool // true if this sidechain was launched with --force-backend (skips flutter_frontend variant)
	// contains filtered or unexported fields
}

ManagedProcess represents a running process managed by the orchestrator.

func (*ManagedProcess) ExitCh

func (p *ManagedProcess) ExitCh() <-chan struct{}

ExitCh returns a channel that is closed when the process exits.

func (*ManagedProcess) ExitCode

func (p *ManagedProcess) ExitCode() int

ExitCode returns the process exit code (only valid after exitCh is closed).

func (*ManagedProcess) ExitDetails

func (p *ManagedProcess) ExitDetails() string

ExitDetails returns the rich crash message extracted from stderr / last error log lines. Empty if the process is still running or exited cleanly. Prefer this over ExitErr() in user-facing surfaces — ExitErr only carries the Go cmd.Wait status (e.g. "exit status 1"), while ExitDetails carries the actual reason the binary printed before dying.

func (*ManagedProcess) ExitErr

func (p *ManagedProcess) ExitErr() string

ExitErr returns the exit error string (only valid after exitCh is closed).

func (*ManagedProcess) RecentLogs

func (p *ManagedProcess) RecentLogs(n int) []LogEntry

RecentLogs returns the most recent log entries.

func (*ManagedProcess) Subscribe

func (p *ManagedProcess) Subscribe() (<-chan LogEntry, func())

Subscribe returns a channel that receives new log entries and a cancel function.

type Orchestrator

type Orchestrator struct {
	DataDir      string
	Network      string
	BitwindowDir string

	// Catalog is the resolved network catalog (service endpoints, explorer
	// templates, and the live drynet generation id).
	Catalog netcatalog.Catalog

	BitcoinConf    *config.BitcoinConfManager
	EnforcerConf   *config.EnforcerConfManager
	SidechainConfs map[string]*config.SidechainConfManager
	WalletSvc      *wallet.Service // for seed injection into sidechain/enforcer args
	Settings       *SettingsStore
	// contains filtered or unexported fields
}

Orchestrator coordinates binary download, process management, and health checking.

func New

func New(dataDir, network, bitwindowDir string, configs []BinaryConfig, log zerolog.Logger) *Orchestrator

New creates a new Orchestrator.

func (*Orchestrator) AdoptOrphans

func (o *Orchestrator) AdoptOrphans(ctx context.Context) error

AdoptOrphans reads PID files from a previous session and adopts any processes that are still alive. Layer-2 PID files are namespaced by mode (e.g. thunder.pid vs thunder-test.pid) so we can attribute the right owner when the user has flipped the test-sidechains toggle between runs.

func (*Orchestrator) ApplyUserSnapshot

func (o *Orchestrator) ApplyUserSnapshot(ctx context.Context, src SnapshotSource) (<-chan StartupProgress, error)

ApplyUserSnapshot loads a snapshot the user supplied and streams progress. loadtxoutset is an online RPC, so this applies against the running node and does not stop, restart or wipe anything.

Core refuses a snapshot whose base block the active chain has already passed, and refuses a second snapshot in the same datadir. Both come back as errors from loadtxoutset and are relayed verbatim rather than pre-empted here.

func (*Orchestrator) AwaitShutdownIdle

func (o *Orchestrator) AwaitShutdownIdle(ctx context.Context) error

AwaitShutdownIdle blocks until any in-flight drain completes. Returns immediately if no drain is active. Respects context cancellation.

func (*Orchestrator) BeginShutdown

func (o *Orchestrator) BeginShutdown() bool

BeginShutdown kicks off the orchestratord shutdown sequence. Idempotent: subsequent calls while a drain is in flight are no-ops. Returns true iff this call initiated a fresh drain.

func (*Orchestrator) BinaryVersion

func (o *Orchestrator) BinaryVersion(name string, forceBackend bool) (version, binPath string, isTest bool, err error)

BinaryVersion resolves the binary the same way the launcher does (variant- and test-build aware, honoring forceBackend) and returns its --version output. isTest is true when the resolved path is a Flutter test build, which has no --version — callers should show "Test Sidechain" rather than running it. Mirrors the former Dart Binary.binaryVersion so the frontend can drop its own path-guessing.

func (*Orchestrator) BinaryWalletPaths

func (o *Orchestrator) BinaryWalletPaths() []string

BinaryWalletPaths returns the per-binary on-disk wallet locations that the wallet service's pre-deletion sweep removes. Mirrors the path enumeration GatherFilesToDelete uses so the sweep agrees with the file-by-file pass.

func (*Orchestrator) CancelShutdownExit

func (o *Orchestrator) CancelShutdownExit() bool

CancelShutdownExit flips the will-exit bit off if a drain is in progress and currently set to exit. Returns true iff the bit was flipped. In-flight binary stops continue regardless — see plan: in-flight stops are uncancellable by design.

func (*Orchestrator) Configs

func (o *Orchestrator) Configs() map[string]BinaryConfig

Configs returns the binary configs.

func (*Orchestrator) CoreStatusClient

func (o *Orchestrator) CoreStatusClient() (*CoreStatusClient, error)

CoreStatusClient builds a CoreStatusClient from the current config.

func (*Orchestrator) CoreVariant

func (o *Orchestrator) CoreVariant() string

CoreVariant returns the currently selected Bitcoin Core variant ID.

func (*Orchestrator) DefaultElectrumServerURL

func (o *Orchestrator) DefaultElectrumServerURL() string

DefaultElectrumServerURL returns the built-in Esplora endpoint for the current network, the value used when the user has set no override.

func (*Orchestrator) DeleteFiles

func (o *Orchestrator) DeleteFiles(ctx context.Context, paths []string, specs ...[]GatherSpec) (<-chan DeleteEvent, error)

DeleteFiles stops the binaries implicated by specs, then removes each path. Wallet paths are moved to wallet_backups/ instead of being removed — keys are irreplaceable. Each path is reported on the returned channel; an empty Error means success. A returned error means deletion couldn't start at all (e.g. shutdown failed).

Callers must pass only paths from GatherFilesToDelete; the RPC handler enforces that by resolving them server-side. Passing specs scopes shutdown to the selected binaries; omitting specs preserves the legacy "stop all" behavior for direct internal callers.

func (*Orchestrator) Download

func (o *Orchestrator) Download(ctx context.Context, name string, force bool) (<-chan DownloadProgress, error)

Download downloads a binary if missing (or forces re-download).

func (*Orchestrator) DownloadStateForTest

func (o *Orchestrator) DownloadStateForTest(name string) (DownloadState, bool)

DownloadStateForTest exposes the DownloadManager's per-binary state to tests in sibling packages (e.g. api/) so they can poll for completion without subscribing to a stream. Returns ok=true while the download is in flight.

func (*Orchestrator) DownloadStates

func (o *Orchestrator) DownloadStates() map[string]DownloadState

DownloadStates returns the live download snapshot for every binary the manager is currently fetching. Empty map means no in-flight downloads. Source of truth for the GetDownloadStatus RPC.

func (*Orchestrator) ElectrumServerOverride

func (o *Orchestrator) ElectrumServerOverride() string

ElectrumServerOverride returns the persisted user Esplora override, or "" when none is set (the network default applies).

func (*Orchestrator) ForkState

func (o *Orchestrator) ForkState(ctx context.Context) (*fork.ForkState, error)

ForkState returns the canonical fork snapshot, or a zero state if the fork engine isn't wired yet (no Core RPC).

func (*Orchestrator) ForkTip

func (o *Orchestrator) ForkTip(ctx context.Context) (fork.Tip, error)

ForkTip implements fork.TipSource off the cached getblockchaininfo.

func (*Orchestrator) GatherFilesToDelete

func (o *Orchestrator) GatherFilesToDelete(specs []GatherSpec) ([]ResetFileInfo, error)

GatherFilesToDelete resolves, per binary, the on-disk paths for each requested category. No side effects. The returned list is deduplicated by path so a file shared between two categories (e.g. a frontend wallet.json that shows up under both settings and wallet) is only listed once.

func (*Orchestrator) GetBTCPrice

func (o *Orchestrator) GetBTCPrice() (float64, time.Time, error)

GetBTCPrice returns the current BTC/USD price, caching for 10 seconds.

func (*Orchestrator) GetMainchainBalance

func (o *Orchestrator) GetMainchainBalance(ctx context.Context) (*MainchainBalance, error)

GetMainchainBalance proxies getbalance + getunconfirmedbalance from bitcoind.

func (*Orchestrator) GetMainchainBlockchainInfo

func (o *Orchestrator) GetMainchainBlockchainInfo(ctx context.Context) (*MainchainBlockchainInfo, error)

GetMainchainBlockchainInfo proxies getblockchaininfo from bitcoind through the shared cache. Signature is load-bearing — the public Connect RPC at api/orchestrator_handler.go:216 returns this rich type directly.

func (*Orchestrator) GetSyncStatus

func (o *Orchestrator) GetSyncStatus(ctx context.Context) (*SyncStatus, error)

GetSyncStatus fans out concurrent probes — mainchain bitcoind, enforcer ValidatorService, plus every known sidechain — and returns them as one atomic snapshot. For each slot, an in-flight download takes precedence: if DownloadManager.State reports Running, the slot is filled with MB downloaded / MB total and IsDownloading=true; otherwise the live RPC is queried for the chain tip.

Per-chain errors are surfaced inline on ChainSyncResult.Error — the overall call only errors out when no probe could even be dispatched.

func (*Orchestrator) InitForkEngine

func (o *Orchestrator) InitForkEngine(we *wallet.WalletEngine)

InitForkEngine wires the fork engine once the wallet engine (Core RPC) is available — called from main after NewWalletEngine. The fork.Engine is the single source of truth for fork state; ForkState is a thin pass-through.

func (*Orchestrator) ListAll

func (o *Orchestrator) ListAll() []BinaryStatus

ListAll returns the status of every configured binary, sorted by chain layer (L1 first) then name.

func (*Orchestrator) ListCoreVariants

func (o *Orchestrator) ListCoreVariants() []CoreVariantSpec

ListCoreVariants returns the variants offered for the current network. On mainnet the slice is empty (the UI hides the picker entirely).

func (*Orchestrator) Logs

func (o *Orchestrator) Logs(name string) (<-chan LogEntry, func(), error)

Logs returns a channel of log entries for a binary and a cancel function.

func (*Orchestrator) PersistElectrumServerURL

func (o *Orchestrator) PersistElectrumServerURL(url string) error

PersistElectrumServerURL stores a runtime Esplora endpoint override so it survives restart. An empty url clears the override (reset to default).

func (*Orchestrator) PersistTorConfig

func (o *Orchestrator) PersistTorConfig(enabled bool, proxy string) error

PersistTorConfig stores the Tor routing preference so it survives restart.

func (*Orchestrator) ProcessManager

func (o *Orchestrator) ProcessManager() *ProcessManager

ProcessManager returns the underlying process manager (for direct access if needed).

func (*Orchestrator) RecentLogs

func (o *Orchestrator) RecentLogs(name string, n int) ([]LogEntry, error)

RecentLogs returns the most recent log entries for a binary.

func (*Orchestrator) ResolveNetworkCatalog

func (o *Orchestrator) ResolveNetworkCatalog(ctx context.Context)

ResolveNetworkCatalog loads the catalog persisted by the previous run, then refreshes it from the published document. A failed refresh is never fatal: the persisted copy stays in force, which is also what makes it a safe baseline for spotting a drynet generation change — an offline boot compares the old values against themselves and so can never wipe anything.

func (*Orchestrator) RestartDaemon

func (o *Orchestrator) RestartDaemon(ctx context.Context, name string) (<-chan StartupProgress, error)

RestartDaemon stops the named binary and starts it again — single-daemon scope. Unlike StartWithL1, this never touches sibling daemons: restarting "enforcer" only restarts the enforcer; it never tries to spawn or adopt bitcoind. Use it for the "Restart" button on per-daemon UI cards.

The returned channel emits StartupProgress events the same way StartWithL1 does and is closed when the restart completes (or fails).

func (*Orchestrator) RestartL1

func (o *Orchestrator) RestartL1(ctx context.Context) error

RestartL1 stops the L1 stack (enforcer + bitcoind) and boots it again on the current config. Running sidechains are left alone — they reconnect once the enforcer is back. This is the single server-side entry point for the "Restart Bitcoin Core and Enforcer" UI flow; the frontend must not hand-orchestrate stop/start itself. Stops are guarded by IsRunning, so a not-running daemon is skipped rather than treated as an error.

func (*Orchestrator) SetCoreVariant

func (o *Orchestrator) SetCoreVariant(ctx context.Context, id string) error

SetCoreVariant stops bitcoind, persists the new variant, ensures the binary is on disk for it, and restarts bitcoind. The whole sequence is serialised behind coreVariantMu so concurrent callers can't race the on-disk state. On stop failure we escalate to SIGKILL; if even that fails we abort before touching settings.

func (*Orchestrator) SetForkEnforcerWallet

func (o *Orchestrator) SetForkEnforcerWallet(client enforcerrpc.WalletServiceClient)

SetForkEnforcerWallet attaches the enforcer wallet client to the fork scan so the enforcer wallet's pre-fork coins are claimable too. Called from main once the enforcer client exists (which is after InitForkEngine), so it's read dynamically by the scanner rather than captured at construction.

func (*Orchestrator) SetPendingSnapshot

func (o *Orchestrator) SetPendingSnapshot(src *SnapshotSource)

SetPendingSnapshot records a snapshot to apply the next time bitcoind comes up. Applying is deferred rather than done inline because loadtxoutset needs a chainstate that has not passed the snapshot height: the caller wipes the existing chain and restarts bitcoind, and the snapshot is applied against the fresh node on the way back up.

func (*Orchestrator) SetTestSidechains

func (o *Orchestrator) SetTestSidechains(ctx context.Context, enabled bool) error

SetTestSidechains flips the persisted test-sidechains toggle. The flow is:

  1. Stop every running layer-2 (sidechain) binary, escalating to SIGKILL on graceful failure.
  2. Persist the new value before any wipe so a crash leaves coherent state.
  3. Wipe on-disk binaries for both production and test layouts so the next launch redownloads from the correct source.

We don't auto-restart anything: the frontend triggers redownload + start on the user's next StartWithL1.

func (*Orchestrator) ShutdownAll

func (o *Orchestrator) ShutdownAll(ctx context.Context, force bool) (<-chan ShutdownProgress, error)

ShutdownAll stops all running binaries in reverse dependency order.

func (*Orchestrator) ShutdownDraining

func (o *Orchestrator) ShutdownDraining() (draining, willExit bool)

ShutdownDraining reports whether a drain is currently in progress and, if so, whether the daemon will os.Exit when it completes.

func (*Orchestrator) Start

func (o *Orchestrator) Start(ctx context.Context, name string, args []string, env map[string]string) (int, error)

Start starts a binary with the given args and env.

func (*Orchestrator) StartWithL1

func (o *Orchestrator) StartWithL1(ctx context.Context, target string, opts StartOpts) (<-chan StartupProgress, error)

StartWithL1 starts a binary along with its dependency chain: Bitcoin Core -> wait for wallet/IBD -> Enforcer -> target binary.

func (*Orchestrator) Status

func (o *Orchestrator) Status(name string) BinaryStatus

Status returns the current status of a binary.

func (*Orchestrator) Stop

func (o *Orchestrator) Stop(ctx context.Context, name string, force bool) error

Stop stops a running binary and marks its monitor as stopped so the restart timer won't automatically bring it back.

func (*Orchestrator) StopAllMonitors

func (o *Orchestrator) StopAllMonitors()

StopAllMonitors stops all connection monitor timers.

func (*Orchestrator) SwapNetwork

func (o *Orchestrator) SwapNetwork(ctx context.Context, n config.Network) error

SwapNetwork performs an atomic Bitcoin network swap: stop running L2 sidechains + enforcer + bitcoind in reverse-dependency order, persist the new network to bitwindow-bitcoin.conf, refresh in-memory state, then restart the L1 stack if bitcoind/enforcer was running. Sidechains are intentionally not auto-restarted — the user re-launches them when they want to.

func (*Orchestrator) TorConfigOverride

func (o *Orchestrator) TorConfigOverride() (bool, string)

TorConfigOverride returns the persisted Tor routing preference (enabled, proxy address), or (false, "") when settings are unavailable.

func (*Orchestrator) UpdateConfigs

func (o *Orchestrator) UpdateConfigs(configs []BinaryConfig)

UpdateConfigs replaces the binary configs with new ones (e.g. from a reloaded JSON file). Preserves Go-specific runtime state (running processes, health checks).

func (*Orchestrator) UseTestSidechains

func (o *Orchestrator) UseTestSidechains() bool

UseTestSidechains reports the persisted test-sidechains preference.

type OrchestratorSettings

type OrchestratorSettings struct {
	CoreVariant       string `json:"core_variant"`
	UseTestSidechains bool   `json:"use_test_sidechains"`
	// ElectrumServerURL overrides the network's default Esplora endpoint for
	// electrum wallets. Empty means "use the network default".
	ElectrumServerURL string `json:"electrum_server_url"`
	// TorEnabled routes the electrum wallet's chain connections through TorProxy
	// when true. Default false means direct connection.
	TorEnabled bool `json:"tor_enabled"`
	// TorProxy is the SOCKS5 proxy address (host:port) used when TorEnabled.
	TorProxy string `json:"tor_proxy"`
}

OrchestratorSettings is the on-disk shape of orchestrator_settings.json.

func LoadSettings

func LoadSettings(bitwindowDir string) (OrchestratorSettings, error)

LoadSettings reads orchestrator_settings.json, returning defaults if absent.

type PidFileManager

type PidFileManager struct {
	// contains filtered or unexported fields
}

PidFileManager handles PID file read/write/validate operations. PID files are stored at {dataDir}/pids/{binaryName}.pid

func NewPidFileManager

func NewPidFileManager(dataDir string, log zerolog.Logger) *PidFileManager

func (*PidFileManager) DeletePidFile

func (m *PidFileManager) DeletePidFile(binaryName string) error

func (*PidFileManager) ListPidFiles

func (m *PidFileManager) ListPidFiles() map[string]int

ListPidFiles returns all PID files and their PIDs.

func (*PidFileManager) ReadPidFile

func (m *PidFileManager) ReadPidFile(binaryName string) (int, error)

func (*PidFileManager) ValidatePid

func (m *PidFileManager) ValidatePid(pid int, binaryName string) bool

ValidatePid checks if a PID is alive and belongs to the expected binary. Returns true only if the process is alive AND the process name matches.

func (*PidFileManager) WritePidFile

func (m *PidFileManager) WritePidFile(binaryName string, pid int) error

type ProcessExitInfo

type ProcessExitInfo struct {
	Name     string
	ExitCode int
	ErrMsg   string // stderr-based error message, empty if clean exit
}

ProcessExitInfo is passed to the onExit callback when a process dies.

type ProcessManager

type ProcessManager struct {

	// CoreVariant resolves the binary path for Bitcoin Core when set. If nil
	// (or it returns ok=false), the default flat BinaryPath is used.
	CoreVariant func(config BinaryConfig) (CoreVariantSpec, bool)

	// SidechainVariant resolves the on-disk binary name for layer-2 test
	// builds. ok=false means use the production BinaryName + flat BinaryPath.
	SidechainVariant func(config BinaryConfig) (sidechainVariantSpec, bool)

	// OnExit is called when a process exits. The orchestrator uses this to
	// pipe exit errors into ConnectionMonitor.connectionError for the UI.
	OnExit func(ProcessExitInfo)

	// OnStartupLog is called when a process line matches one of the binary's
	// startup_log_patterns. The orchestrator pushes these to the UI so users
	// see a timeline of startup progress messages.
	// Dart: ProcessManager._captureStartupLog (process_manager.dart L380-395)
	OnStartupLog func(StartupLogEntry)
	// contains filtered or unexported fields
}

ProcessManager handles spawning, monitoring, and killing processes.

func NewProcessManager

func NewProcessManager(dataDir string, pidManager *PidFileManager, log zerolog.Logger) *ProcessManager

func (*ProcessManager) AdoptProcess

func (pm *ProcessManager) AdoptProcess(config BinaryConfig, pid int)

AdoptProcess registers an externally-found process (from a PID file).

func (*ProcessManager) AdoptProcessResolved

func (pm *ProcessManager) AdoptProcessResolved(config BinaryConfig, pid int, binPath, pidName string, forceBackend bool)

AdoptProcessResolved registers an externally-found process when the PID file name already tells us which on-disk variant it belongs to.

func (*ProcessManager) AdoptProcessWithOptions

func (pm *ProcessManager) AdoptProcessWithOptions(config BinaryConfig, pid int, opts ProcessStartOptions)

AdoptProcessWithOptions registers an externally-found process using the same path resolution overrides as StartWithOptions.

func (*ProcessManager) ForceBackendFor

func (pm *ProcessManager) ForceBackendFor(name string) bool

ForceBackendFor returns the ForceBackend flag the named process was started with. False when the process is unknown — same default as a fresh StartOpts.

func (*ProcessManager) Get

func (pm *ProcessManager) Get(name string) *ManagedProcess

Get returns the managed process for a binary, or nil if not running.

func (*ProcessManager) IsAdopted

func (pm *ProcessManager) IsAdopted(name string) bool

IsAdopted returns true if the named process was adopted (not started by us).

func (*ProcessManager) IsRunning

func (pm *ProcessManager) IsRunning(name string) bool

IsRunning checks if a binary is currently running.

func (*ProcessManager) ListRunning

func (pm *ProcessManager) ListRunning() []string

ListRunning returns the names of all running processes.

func (*ProcessManager) Remove

func (pm *ProcessManager) Remove(name string)

Remove removes a process from tracking without stopping it. Used for adopted processes that we don't own.

func (*ProcessManager) Start

func (pm *ProcessManager) Start(ctx context.Context, config BinaryConfig, args []string, env map[string]string) (int, error)

Start launches a binary and returns its PID.

func (*ProcessManager) StartWithOptions

func (pm *ProcessManager) StartWithOptions(_ context.Context, config BinaryConfig, args []string, env map[string]string, opts ProcessStartOptions) (int, error)

StartWithOptions is Start with per-call overrides (see ProcessStartOptions).

func (*ProcessManager) Stop

func (pm *ProcessManager) Stop(_ context.Context, name string, force bool) error

func (*ProcessManager) StopAll

func (pm *ProcessManager) StopAll(ctx context.Context, force bool) error

StopAll stops all running processes.

func (*ProcessManager) WaitForExit

func (pm *ProcessManager) WaitForExit(name string, timeout time.Duration) bool

type ProcessStartOptions

type ProcessStartOptions struct {
	// ForceBackend skips the SidechainVariant resolver so the prod-download
	// binary is launched even when UseTestSidechains is on. Set by sidechain
	// Flutter frontends self-booting their backend.
	ForceBackend bool

	// ProcessName overrides the in-memory process slot. Used for managed GUI
	// companions so they don't occupy the backend daemon's slot.
	ProcessName string

	// PidName overrides the PID-file name. Keep GUI companion PID files
	// distinct from backend/test-daemon PID files so orphan adoption cannot
	// confuse the frontend with the daemon.
	PidName string

	// WorkDir overrides the child process working directory. Flutter GUI
	// bundles expect to run beside their lib/data trees.
	WorkDir string
}

ProcessStartOptions tweaks process.Start behaviour per-call.

type RawTransaction

type RawTransaction struct {
	Txid     string `json:"txid"`
	Hash     string `json:"hash"`
	Size     int64  `json:"size"`
	Vsize    int64  `json:"vsize"`
	Weight   int64  `json:"weight"`
	Version  int32  `json:"version"`
	Locktime uint32 `json:"locktime"`
	Vin      []struct {
		Txid      string `json:"txid"`
		Vout      uint32 `json:"vout"`
		Coinbase  string `json:"coinbase,omitempty"`
		ScriptSig *struct {
			Asm string `json:"asm"`
			Hex string `json:"hex"`
		} `json:"scriptSig,omitempty"`
		Witness  []string `json:"txinwitness,omitempty"`
		Sequence uint32   `json:"sequence"`
	} `json:"vin"`
	Vout []struct {
		Value        float64 `json:"value"`
		N            uint32  `json:"n"`
		ScriptPubKey struct {
			Asm     string `json:"asm"`
			Hex     string `json:"hex"`
			Type    string `json:"type"`
			Address string `json:"address,omitempty"`
		} `json:"scriptPubKey"`
	} `json:"vout"`
	Blockhash     string `json:"blockhash,omitempty"`
	Confirmations int64  `json:"confirmations"`
	BlockTime     int64  `json:"blocktime,omitempty"`
}

type ResetBinary

type ResetBinary int

ResetBinary is the typed reset graph. Keep these values hardcoded and small: they are safer to reason about than free-form process names in reset logic.

const (
	ResetBinaryUnknown ResetBinary = iota
	ResetBinaryBitcoind
	ResetBinaryEnforcer
	ResetBinaryBitwindowd
	ResetBinaryThunder
	ResetBinaryZSide
	ResetBinaryBitNames
	ResetBinaryBitAssets
	ResetBinaryTruthcoin
	ResetBinaryPhoton
	ResetBinaryCoinShift
	ResetBinaryGRPCurl
	ResetBinaryOrchestratord
	ResetBinaryZSided
)

type ResetCategory

type ResetCategory int

ResetCategory maps 1:1 to the proto DeletionType enum and selects which getter on config.BinaryDirConfig is used.

const (
	ResetCategoryData ResetCategory = iota
	ResetCategorySoftware
	ResetCategoryLogs
	ResetCategorySettings
	ResetCategoryWallet
)

type ResetFileInfo

type ResetFileInfo struct {
	Path        string
	Category    ResetCategory
	Binary      ResetBinary
	SizeBytes   int64
	IsDirectory bool
}

ResetFileInfo describes a single file/directory that a reset would affect.

type SettingsStore

type SettingsStore struct {
	// contains filtered or unexported fields
}

SettingsStore is a thread-safe in-memory cache around orchestrator_settings.json.

func NewSettingsStore

func NewSettingsStore(bitwindowDir string) (*SettingsStore, error)

NewSettingsStore loads (or initialises) the on-disk settings.

func (*SettingsStore) CoreVariant

func (s *SettingsStore) CoreVariant() string

func (*SettingsStore) ElectrumServerURL

func (s *SettingsStore) ElectrumServerURL() string

ElectrumServerURL returns the user's Esplora endpoint override, or "" when the network default should be used.

func (*SettingsStore) Get

func (*SettingsStore) SetCoreVariant

func (s *SettingsStore) SetCoreVariant(id string) (string, error)

SetCoreVariant persists a new variant ID and returns the previous value.

func (*SettingsStore) SetElectrumServerURL

func (s *SettingsStore) SetElectrumServerURL(url string) (string, error)

SetElectrumServerURL persists a new Esplora endpoint override and returns the previous value. An empty url clears the override.

func (*SettingsStore) SetTorConfig

func (s *SettingsStore) SetTorConfig(enabled bool, proxy string) (bool, string, error)

SetTorConfig persists the Tor routing preference and returns the previous values, so a failed apply can be rolled back.

func (*SettingsStore) SetUseTestSidechains

func (s *SettingsStore) SetUseTestSidechains(v bool) (bool, error)

SetUseTestSidechains persists the new value and returns the previous one.

func (*SettingsStore) TorConfig

func (s *SettingsStore) TorConfig() (bool, string)

TorConfig returns the persisted Tor routing preference: whether it is enabled and the SOCKS5 proxy address to use.

func (*SettingsStore) UseTestSidechains

func (s *SettingsStore) UseTestSidechains() bool

type ShutdownProgress

type ShutdownProgress struct {
	TotalCount     int
	CompletedCount int
	CurrentBinary  string
	Done           bool
	Error          error
}

ShutdownProgress reports progress during ShutdownAll.

type SignRawResult

type SignRawResult struct {
	Hex      string `json:"hex"`
	Complete bool   `json:"complete"`
}

type SnapshotSource

type SnapshotSource struct {
	URL  string
	Path string
	// SHA256 is the expected digest. Empty skips verification, which is the
	// normal case for a file the user supplied themselves.
	SHA256 string
	// Height is the block the snapshot commits to. Zero when unknown, which
	// disables the already-synced check.
	Height int64
	// Label names the source in logs and progress messages.
	Label string
	// Requested marks a snapshot the user explicitly asked for. Those failures
	// are reported as errors; the automatic drynet one stays non-fatal and
	// falls back to a normal sync.
	Requested bool
}

SnapshotSource describes where a UTXO snapshot comes from. Exactly one of URL or Path is set: URL is downloaded, Path is an existing file on disk.

type StartOpts

type StartOpts struct {
	TargetArgs   []string
	TargetEnv    map[string]string
	CoreArgs     []string
	EnforcerArgs []string
	Immediate    bool // start target without waiting for L1
	// ForceBackend bypasses UseTestSidechains for the target binary. Set by
	// sidechain Flutter frontends when self-booting their backend so the
	// toggle doesn't swap in another Flutter bundle inside them.
	ForceBackend bool
}

StartOpts configures a StartWithL1 call.

type StartupLogEntry

type StartupLogEntry struct {
	Name      string
	Timestamp time.Time
	Message   string
}

StartupLogEntry is passed to the OnStartupLog callback when a process line matches one of the binary's startup_log_patterns. Used by the UI to show a timeline of startup progress messages.

type StartupLogLine

type StartupLogLine struct {
	Timestamp time.Time
	Message   string
}

StartupLogLine is a timestamped startup progress message.

type StartupProgress

type StartupProgress struct {
	Stage        string // e.g. "downloading-bitcoind", "starting-bitcoind", "waiting-ibd"
	Message      string
	Done         bool
	Error        error
	MBDownloaded int64
	MBTotal      int64
}

StartupProgress reports progress during StartWithL1. Download fields are in megabytes (matches DownloadProgress).

type SyncStatus

type SyncStatus struct {
	Mainchain  *ChainSyncResult
	Enforcer   *ChainSyncResult
	Sidechains map[string]*ChainSyncResult
}

SyncStatus is the atomic snapshot returned by GetSyncStatus. Mainchain + enforcer are always populated; Sidechains carries one entry per orchestrator-managed L2 sidechain binary, keyed by the binary's logical name. Frontends that aren't sidechains (e.g. bitwindow's own bitwindowd daemon) are NOT in this map — the orchestrator knows nothing about them.

type TCPHealthCheck

type TCPHealthCheck struct {
	Host    string
	Port    int
	Timeout time.Duration
}

TCPHealthCheck verifies a port is accepting connections.

func (*TCPHealthCheck) Check

func (h *TCPHealthCheck) Check(ctx context.Context) error

type WalletInfo

type WalletInfo struct {
	WalletName string `json:"walletname"`
	Format     string `json:"format"`
	TxCount    int64  `json:"txcount"`
}

Directories

Path Synopsis
cmd
orchestratorctl command
orchestratord command
netcatalog
Package netcatalog resolves the network catalog published at https://drivechain.dev/config: the per-network service endpoints, explorer URL templates and — for the eCash family — the live drynet generation id ("drynet2", "drynet3", ...).
Package netcatalog resolves the network catalog published at https://drivechain.dev/config: the per-network service endpoints, explorer URL templates and — for the eCash family — the live drynet generation id ("drynet2", "drynet3", ...).
Package datasource abstracts every read-only chain/drivechain data fetch that bitwindow + the orchestrator make from Bitcoin Core and the BIP300301 enforcer, behind a single interface.
Package datasource abstracts every read-only chain/drivechain data fetch that bitwindow + the orchestrator make from Bitcoin Core and the BIP300301 enforcer, behind a single interface.
Package enforcerproxy forwards enforcer traffic for the daemons that front it (orchestratord for sidechain apps, bitwindowd for bitwindow): Connect/gRPC service calls and the JSON-RPC mining endpoint.
Package enforcerproxy forwards enforcer traffic for the daemons that front it (orchestratord for sidechain apps, bitwindowd for bitwindow): Connect/gRPC service calls and the JSON-RPC mining endpoint.
Package engines hosts long-running background workers driven by the orchestrator.
Package engines hosts long-running background workers driven by the orchestrator.
Package fork is the single source of truth for eCash fork state.
Package fork is the single source of truth for eCash fork state.
gen
Package localauth is a bitcoin-cookie-style local auth shared by orchestratord and bitwindowd.
Package localauth is a bitcoin-cookie-style local auth shared by orchestratord and bitwindowd.
localauthtest
Package localauthtest provides test helpers for localauth, mirroring the bbtest.AuthContext pattern: build a context carrying a valid local-auth token so a test can make authenticated client calls.
Package localauthtest provides test helpers for localauth, mirroring the bbtest.AuthContext pattern: build a context carrying a valid local-auth token so a test can make authenticated client calls.
Package replay applies replay protection via a magic nLockTime.
Package replay applies replay protection via a magic nLockTime.
Package rpcmeter provides a Connect interceptor that meters RPC traffic through a handler — call counts, latency, and error counts per method — and logs a periodic per-method summary.
Package rpcmeter provides a Connect interceptor that meters RPC traffic through a handler — call counts, latency, and error counts per method — and logs a periodic per-method summary.
bitassets
Package bitassets provides a JSON-RPC client for the Bitassets sidechain.
Package bitassets provides a JSON-RPC client for the Bitassets sidechain.
bitnames
Package bitnames provides a JSON-RPC client for the BitNames sidechain.
Package bitnames provides a JSON-RPC client for the BitNames sidechain.
coinshift
Package coinshift provides a JSON-RPC client for the Coinshift sidechain.
Package coinshift provides a JSON-RPC client for the Coinshift sidechain.
elements
Package elements provides a JSON-RPC client for the Elements/Liquid sidechain.
Package elements provides a JSON-RPC client for the Elements/Liquid sidechain.
photon
Package photon provides a JSON-RPC client for the Photon sidechain.
Package photon provides a JSON-RPC client for the Photon sidechain.
thunder
Package thunder provides a JSON-RPC client for the Thunder sidechain.
Package thunder provides a JSON-RPC client for the Thunder sidechain.
truthcoin
Package truthcoin provides a JSON-RPC client for the Truthcoin sidechain.
Package truthcoin provides a JSON-RPC client for the Truthcoin sidechain.
zside
Package zside provides a JSON-RPC client for the Zside sidechain.
Package zside provides a JSON-RPC client for the Zside sidechain.
Package testharness provides a reusable integration test harness for the wallet stack.
Package testharness provides a reusable integration test harness for the wallet stack.
bip47send
Package bip47send drives the orchestrator-side BIP47 send flow for bitcoinCore wallets: per-payment address derivation and notification transaction assembly.
Package bip47send drives the orchestrator-side BIP47 send flow for bitcoinCore wallets: per-payment address derivation and notification transaction assembly.
bip47state
Package bip47state persists the per-recipient BIP47 send state (notification txid, next per-payment derivation index) used by the orchestrator's send path.
Package bip47state persists the per-recipient BIP47 send state (notification txid, next per-payment derivation index) used by the orchestrator's send path.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL