darepod: add client daemon scaffold with actor system - #123
Conversation
Summary of ChangesHello @Roasbeef, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces the initial scaffold for the Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a significant amount of new functionality by scaffolding the darepod client daemon. The changes are well-structured, including a new darepod main package, configuration management with Cobra and Viper, RPC definitions for daemon and indexer services, and the core server logic with an actor system. The code quality is high, with clear separation of concerns and good use of interfaces for testability.
I've found a couple of issues. A redundant command in the Makefile's clean target, and a potential nil pointer dereference in indexer/sync_client.go due to incorrect nil checks, which I've flagged as high severity.
| if c == nil || c.backend == nil { | ||
| return nil, fmt.Errorf("missing sync backend") | ||
| } | ||
| if c.cursors == nil { | ||
| return nil, fmt.Errorf("missing sync cursor store") | ||
| } |
There was a problem hiding this comment.
The check c == nil || c.backend == nil will cause a panic if c is nil due to the access to c.backend. The checks for c being nil and its fields being nil should be separated to prevent this panic.
| if c == nil || c.backend == nil { | |
| return nil, fmt.Errorf("missing sync backend") | |
| } | |
| if c.cursors == nil { | |
| return nil, fmt.Errorf("missing sync cursor store") | |
| } | |
| if c == nil { | |
| return nil, fmt.Errorf("SyncClient is nil") | |
| } | |
| if c.backend == nil { | |
| return nil, fmt.Errorf("missing sync backend") | |
| } | |
| if c.cursors == nil { | |
| return nil, fmt.Errorf("missing sync cursor store") | |
| } |
| if c == nil || c.backend == nil { | ||
| return nil, fmt.Errorf("missing sync backend") | ||
| } | ||
| if c.cursors == nil { | ||
| return nil, fmt.Errorf("missing sync cursor store") | ||
| } |
There was a problem hiding this comment.
Similar to the other sync method, the check c == nil || c.backend == nil will panic if c is nil. The checks should be separated to handle the nil receiver case gracefully before accessing its fields.
| if c == nil || c.backend == nil { | |
| return nil, fmt.Errorf("missing sync backend") | |
| } | |
| if c.cursors == nil { | |
| return nil, fmt.Errorf("missing sync cursor store") | |
| } | |
| if c == nil { | |
| return nil, fmt.Errorf("SyncClient is nil") | |
| } | |
| if c.backend == nil { | |
| return nil, fmt.Errorf("missing sync backend") | |
| } | |
| if c.cursors == nil { | |
| return nil, fmt.Errorf("missing sync cursor store") | |
| } |
| clean: #? Remove build artifacts | ||
| @$(call print, "Cleaning build artifacts.") | ||
| $(RM) ./merge-sql-schemas | ||
| $(RM) -r ./bin/darepod |
bf71d8c to
d5de844
Compare
💡 Codex Reviewhttps://github.com/lightninglabs/darepo-client/blob/bf71d8cffa56cb4ad2e29ed0f0a8ad0bee38d1c1/darepod/config.go#L200 When https://github.com/lightninglabs/darepo-client/blob/bf71d8cffa56cb4ad2e29ed0f0a8ad0bee38d1c1/indexer/client.go#L355
https://github.com/lightninglabs/darepo-client/blob/bf71d8cffa56cb4ad2e29ed0f0a8ad0bee38d1c1/cmd/darepod/main.go#L103-L106 This enables ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@claude review this |
|
Claude finished @Roasbeef's task in 3m 27s —— View job Code ReviewGood overall structure — the numbered startup steps, lifecycle pattern mirroring lnd/tapd, and the clean separation between Bugs / Correctness1. Viper env var overrides broken for nested config keys —
Fix: add 2. Zero ExpiresAtUnixS: uint64(expiresAt.Unix()),The proto comment says var expiresAtUnixS uint64
if !expiresAt.IsZero() {
expiresAtUnixS = uint64(expiresAt.Unix())
}
ExpiresAtUnixS: expiresAtUnixS,3. The config struct exposes TLS paths for the daemon's own gRPC listener, but False Positives in Prior Review Comments
Minor Issues4. Makefile
5. Per project guidelines every function (including unexported) must have a doc comment starting with the function name. 6. Per guidelines, exported struct fields need GoDoc starting with the field name: // PkScript is the P2TR output script.
PkScript []byte
// SigningKey is the P2TR output key corresponding to PkScript.
SigningKey *btcec.PrivateKey7. Unexported functions still require a comment per project style. None present. 8. The helper is defined but never called in the current scaffold. Fine to keep as a forward stub, but worth a 9. Field 7 ( 10. gRPC server error log after graceful stop —
SummaryThe scaffold is well-structured and follows the lnd/tapd lifecycle pattern cleanly. The main actionable items before merge are: the env-var key replacer (item 1, silent misconfiguration that will confuse operators), the zero-time wrapping in |
|
Addressed the actionable items from the review in 85c49a6:
Re item 3 (unused Re item 10 (gRPC error log after graceful stop): |
caac373 to
fece1d3
Compare
|
@claude review this |
|
Claude finished @Roasbeef's task in 2m 8s —— View job Code ReviewThe follow-up commit (85c49a6) addressed all the items from the prior review. The code is in good shape overall — the numbered startup steps are clear, lifecycle management is clean, and the actor wiring reads well. Two new issues below from this pass. Bugs1. The msgBytes, err := encodeRegistrationProofTLV(
c.serverID, c.principal, pkScript, nonce,
uint64(now.Unix()), uint64(expiresAt.Unix()), // ← wraps for zero time
)
// ...
var expiresAtUnixS uint64
if !expiresAt.IsZero() {
expiresAtUnixS = uint64(expiresAt.Unix())
}When var expiresAtUnixS uint64
if !expiresAt.IsZero() {
expiresAtUnixS = uint64(expiresAt.Unix())
}
msgBytes, err := encodeRegistrationProofTLV(
c.serverID, c.principal, pkScript, nonce,
uint64(now.Unix()), expiresAtUnixS,
)2. Nil dereference if There's a guard for if env.Body == nil { // ← guards Body
return fmt.Errorf(...)
}
// env.Rpc is not guarded
respMsg, err := s.mailboxMux.ServeRPC(
ctx, env.Rpc.Service, env.Rpc.Method, // ← panics if Rpc == nil
env.Body.Value,
)Add a corresponding nil check for Minor3. Makefile
4.
5. Field 7 ( Notes on Prior Review Comments
|
40d7b4d to
e0e7484
Compare
fece1d3 to
636a08b
Compare
|
@claude review this |
|
Claude finished @Roasbeef's task in 3m 15s —— View job Code Review
All items from the previous two rounds of review have been addressed cleanly. The code is in good shape. A few items on the new Previously Raised ItemsAll prior action items are resolved:
New Issues1. The in-code comment acknowledges that MuSig2Version100RC2+ expects 32-byte x-only keys, but the loop always calls signerBytes[i] = pk.SerializeCompressed()If 2. pubKey, err := btcec.ParsePubKey(resp.Pubkey)If the server returns an empty or absent if len(resp.Pubkey) == 0 {
return nil, fmt.Errorf("operator pubkey is missing")
}
pubKey, err := btcec.ParsePubKey(resp.Pubkey)Minor3. The dispatcher map currently has one entry ( 4. Compile-time interface check is partial — var _ input.Signer = (*ClientWallet)(nil)The comment explains the cycle prevents checking against 5. Field 7 ( SummaryThe changes in this round ( |
636a08b to
558ec50
Compare
|
Addressed all actionable items from both Claude reviews, the Codex review, and Gemini comments: From Claude review #2 (latest):
From Codex review: Lint fixes:
All fixups squashed into their target commits, branch force-pushed. |
558ec50 to
59b4f45
Compare
|
@claude review the latest push addressing your review comments: (1) MuSig2 pubkey serialization now uses schnorr.SerializePubKey for RC2+ and SerializeCompressed for v040, (2) added resp.Pubkey length guard in fetchOperatorTerms, (3) added TODO for future dispatcher methods, (4) improved compile-time interface check comment with NOTE about import cycle. |
|
Claude finished @Roasbeef's task in 2m 4s —— View job Code ReviewAll four items from the previous round are addressed correctly. Detailed verification below. Verified Fixes1. MuSig2 pubkey serialization — case input.MuSig2Version100RC2:
signerBytes[i] = schnorr.SerializePubKey(pk)
default:
signerBytes[i] = pk.SerializeCompressed()Correct. RC2+ gets 32-byte x-only keys; v040 and any other version gets 33-byte compressed. The comment above the switch documents the format contract clearly. 2. if len(resp.Pubkey) == 0 {
return nil, fmt.Errorf("operator pubkey is missing")
}Clean early return before 3. TODO for future dispatcher methods — The TODO comment explicitly names the future services and warns that missing entries cause silent drops in the ingress loop. This makes the failure mode visible to the next person adding a service. 4. Compile-time interface check comment — The One Minor Observation
The This is not an issue today since only Not a blocker; noting it for awareness. SummaryAll action items from the previous three review rounds are resolved. The |
sputn1ck
left a comment
There was a problem hiding this comment.
LGTM! Started with some glueing between server and this and it seems to work, but would be very helpful to have a good master/base branch to start from
f7a0c53 to
59b4f45
Compare
3468f97 to
c356dae
Compare
59b4f45 to
44b9984
Compare
c356dae to
7fdcca2
Compare
Introduce the darepod package and cmd/darepod entry point for the Ark client daemon. The daemon uses cobra for CLI flag parsing with viper for config file and environment variable overrides. Key components: - Config struct with nested LndConfig, ServerConfig, and RPCConfig sections, each with sensible defaults and validation. - Main entry point that wires signal interception (lnd signal package), config validation, and the server lifecycle. - Server struct with RunUntilShutdown that connects to lnd via lndclient (blocking until chain-synced and unlocked), starts a gRPC server for the daemon's own API, and blocks until SIGINT/SIGTERM. - Placeholder RPCServer with GetInfo returning version, commit, network, and lnd identity pubkey. The proto-generated stubs will replace the hand-written types in a follow-up commit. - Makefile updated to build darepod binary into ./bin/.
Introduce the daemonrpc package with a DaemonService proto defining the client daemon's own gRPC API. The initial service exposes a single GetInfo RPC that returns version, commit hash, network, lnd identity pubkey, lnd alias, block height, and server connection status. The gen_protos.sh script is updated to generate stubs for the new daemonrpc package while excluding it from mailboxrpc generation since this is a local gRPC service, not a mailbox-transported one. The RPCServer and Server types are updated to use the generated proto types instead of the hand-written placeholders.
Wire the ark server gRPC connection into the daemon startup flow. The dialServer method supports three TLS modes: custom certificate, system certificate pool, and insecure (for regtest/development). The newMailboxEdge helper creates a MailboxServiceClient from the established gRPC connection, ready to be passed into serverconn.ConnectorConfig once the DeliveryStore is available. GetInfo now calls lnd's ChainKit.GetBestBlock to populate the block_height field instead of leaving it as a TODO. The gen_protos.sh exclusion for daemonrpc is removed so mailboxrpc stubs are generated for the DaemonService as well.
Initialize the actor system during daemon startup and register the chain source actor backed by lndclient. The chain backend adapter bridges lndclient's chain notifier, fee estimator, and wallet kit into the unified ChainBackend interface consumed by the chainsource actor, which provides fee estimation, block queries, and notification subscription via dedicated sub-actors. The actor system is gracefully shut down with a bounded timeout on daemon exit. The chain backend lifecycle is also managed via deferred Start/Stop calls. ServerConfig gains LocalMailboxID and RemoteMailboxID fields (with corresponding CLI flags) that will feed into the serverconn runtime once the DeliveryStore is wired.
Register the RPCServer as a DaemonServiceMailboxServer on a ServeMux so the ark server can invoke client-side RPCs (like GetInfo) through the mailbox transport. The same RPCServer implementation serves both the local gRPC interface and the mailbox RPC interface. The ServeMux is stored on the Server struct and will be plugged into the ConnectorConfig.Dispatchers map once the serverconn runtime is wired with a persistent DeliveryStore.
In this commit, we replace the manual pointer-based flag bindings (f.StringVar(&cfg.Field, ...)) with viper's BindPFlags + Unmarshal pattern. Flags are declared with f.String/f.Bool (no pointer target), bound to viper in bulk via BindPFlags, and then merged into the Config struct in a PreRunE hook via v.Unmarshal(cfg). This lets viper handle the full precedence chain (flags > env > config file > defaults) in one place, and avoids the repetitive boilerplate of threading each field through a pointer.
In this commit, we add MinVersion: tls.VersionTLS12 to both TLS config paths in dialServer (custom cert pool and system cert pool). Go's defaults already negotiate TLS 1.2+, but setting this explicitly makes the floor visible in the code and prevents accidental downgrade if defaults ever change.
In this commit, we fix several items flagged by the automated review: - Add v.SetEnvKeyReplacer so nested viper keys like lnd.host map to DAREPOD_LND_HOST instead of the invalid DAREPOD_LND.HOST. - Guard the expiresAt→uint64 cast in RegisterReceiveScriptTaproot against zero time.Time values that would wrap to a huge uint64. - Add missing function comments on randomNonceHex and newTaprootScope. - Add GoDoc field comments on TaprootScriptScope exported fields. - Add TODO comments for the unused gRPC TLS config paths and the forward-declared newMailboxEdge helper.
In this commit, we add a ClientWallet adapter that bridges lndclient's remote signing interfaces to the round.ClientWallet interface (input.Signer + DeriveNextKey). This allows the round actor's FSM to sign VTXO tree branches and forfeit transactions through lnd's remote signer without requiring a local wallet. The adapter wraps all MuSig2 session management methods (CreateSession, RegisterNonces, Sign, CombineSig, Cleanup) and handles the type conversions between lndclient's wire types and the musig2 package types. SignOutputRaw and ComputeInputScript forward to lnd's remote signer with proper prevout extraction for taproot sighash computation. Because input.Signer does not carry context, the adapter uses a background context for all RPC calls. The underlying gRPC deadline from the lndclient dial options still applies.
In this commit, we extend the ArkService.GetInfo response with the full set of operator terms needed for round participation. The client daemon needs these parameters before the round actor can start, as they govern boarding script construction, VTXO exit paths, forfeit penalties, and sweep timelocks. New fields: boarding_exit_delay, vtxo_exit_delay, forfeit_script, sweep_key, sweep_delay, dust_limit, min/max_boarding_amount, fee_rate, and min_confirmations.
In this commit, we complete the daemon's startup sequence by wiring the boarding wallet and round client actors into the server lifecycle. The RunUntilShutdown method now follows an 11-step startup: lnd connect, server dial, actor system, chain source, database, gRPC server, mailbox runtime, RPC clients, wallet actor, round actor, and finally blocking until shutdown. We extract the heavier initialization sequences into dedicated helper methods (initDatabase, initRPCClients, initWalletActor, initRoundActor) to keep RunUntilShutdown readable. The wallet actor uses lndbackend.NewBoardingBackend for key derivation and the db.Store convenience wrappers for persistence. The round actor uses the new lndbackend.ClientWallet for MuSig2 signing and fetches operator terms from the server via ArkService.GetInfo before starting.
In this commit, we rewrap the comments, interface method signatures, and const docstrings in sync_client.go to stay within the 80-column line limit enforced by the linter.
44b9984 to
f77f9b6
Compare
* darepod: add daemon skeleton with config, CLI, and server Introduce the darepod package and cmd/darepod entry point for the Ark client daemon. The daemon uses cobra for CLI flag parsing with viper for config file and environment variable overrides. Key components: - Config struct with nested LndConfig, ServerConfig, and RPCConfig sections, each with sensible defaults and validation. - Main entry point that wires signal interception (lnd signal package), config validation, and the server lifecycle. - Server struct with RunUntilShutdown that connects to lnd via lndclient (blocking until chain-synced and unlocked), starts a gRPC server for the daemon's own API, and blocks until SIGINT/SIGTERM. - Placeholder RPCServer with GetInfo returning version, commit, network, and lnd identity pubkey. The proto-generated stubs will replace the hand-written types in a follow-up commit. - Makefile updated to build darepod binary into ./bin/. * daemonrpc: add daemon proto and GetInfo implementation Introduce the daemonrpc package with a DaemonService proto defining the client daemon's own gRPC API. The initial service exposes a single GetInfo RPC that returns version, commit hash, network, lnd identity pubkey, lnd alias, block height, and server connection status. The gen_protos.sh script is updated to generate stubs for the new daemonrpc package while excluding it from mailboxrpc generation since this is a local gRPC service, not a mailbox-transported one. The RPCServer and Server types are updated to use the generated proto types instead of the hand-written placeholders. * darepod: add server connection and lndclient wiring Wire the ark server gRPC connection into the daemon startup flow. The dialServer method supports three TLS modes: custom certificate, system certificate pool, and insecure (for regtest/development). The newMailboxEdge helper creates a MailboxServiceClient from the established gRPC connection, ready to be passed into serverconn.ConnectorConfig once the DeliveryStore is available. GetInfo now calls lnd's ChainKit.GetBestBlock to populate the block_height field instead of leaving it as a TODO. The gen_protos.sh exclusion for daemonrpc is removed so mailboxrpc stubs are generated for the DaemonService as well. * darepod: wire actor system and chain source into startup Initialize the actor system during daemon startup and register the chain source actor backed by lndclient. The chain backend adapter bridges lndclient's chain notifier, fee estimator, and wallet kit into the unified ChainBackend interface consumed by the chainsource actor, which provides fee estimation, block queries, and notification subscription via dedicated sub-actors. The actor system is gracefully shut down with a bounded timeout on daemon exit. The chain backend lifecycle is also managed via deferred Start/Stop calls. ServerConfig gains LocalMailboxID and RemoteMailboxID fields (with corresponding CLI flags) that will feed into the serverconn runtime once the DeliveryStore is wired. * darepod: register DaemonService on mailbox RPC mux Register the RPCServer as a DaemonServiceMailboxServer on a ServeMux so the ark server can invoke client-side RPCs (like GetInfo) through the mailbox transport. The same RPCServer implementation serves both the local gRPC interface and the mailbox RPC interface. The ServeMux is stored on the Server struct and will be plugged into the ConnectorConfig.Dispatchers map once the serverconn runtime is wired with a persistent DeliveryStore. * darepod: include cert path in TLS parse error * cmd/darepod: use viper struct binding instead of manual flag vars In this commit, we replace the manual pointer-based flag bindings (f.StringVar(&cfg.Field, ...)) with viper's BindPFlags + Unmarshal pattern. Flags are declared with f.String/f.Bool (no pointer target), bound to viper in bulk via BindPFlags, and then merged into the Config struct in a PreRunE hook via v.Unmarshal(cfg). This lets viper handle the full precedence chain (flags > env > config file > defaults) in one place, and avoids the repetitive boilerplate of threading each field through a pointer. * darepod: set TLS minimum version to 1.2 for server dial In this commit, we add MinVersion: tls.VersionTLS12 to both TLS config paths in dialServer (custom cert pool and system cert pool). Go's defaults already negotiate TLS 1.2+, but setting this explicitly makes the floor visible in the code and prevents accidental downgrade if defaults ever change. * multi: address review feedback from Claude bot In this commit, we fix several items flagged by the automated review: - Add v.SetEnvKeyReplacer so nested viper keys like lnd.host map to DAREPOD_LND_HOST instead of the invalid DAREPOD_LND.HOST. - Guard the expiresAt→uint64 cast in RegisterReceiveScriptTaproot against zero time.Time values that would wrap to a huge uint64. - Add missing function comments on randomNonceHex and newTaprootScope. - Add GoDoc field comments on TaprootScriptScope exported fields. - Add TODO comments for the unused gRPC TLS config paths and the forward-declared newMailboxEdge helper. * lndbackend: add ClientWallet adapter for round actor signing In this commit, we add a ClientWallet adapter that bridges lndclient's remote signing interfaces to the round.ClientWallet interface (input.Signer + DeriveNextKey). This allows the round actor's FSM to sign VTXO tree branches and forfeit transactions through lnd's remote signer without requiring a local wallet. The adapter wraps all MuSig2 session management methods (CreateSession, RegisterNonces, Sign, CombineSig, Cleanup) and handles the type conversions between lndclient's wire types and the musig2 package types. SignOutputRaw and ComputeInputScript forward to lnd's remote signer with proper prevout extraction for taproot sighash computation. Because input.Signer does not carry context, the adapter uses a background context for all RPC calls. The underlying gRPC deadline from the lndclient dial options still applies. * arkrpc: add operator terms fields to GetInfo proto In this commit, we extend the ArkService.GetInfo response with the full set of operator terms needed for round participation. The client daemon needs these parameters before the round actor can start, as they govern boarding script construction, VTXO exit paths, forfeit penalties, and sweep timelocks. New fields: boarding_exit_delay, vtxo_exit_delay, forfeit_script, sweep_key, sweep_delay, dust_limit, min/max_boarding_amount, fee_rate, and min_confirmations. * darepod: wire wallet and round actors into daemon startup In this commit, we complete the daemon's startup sequence by wiring the boarding wallet and round client actors into the server lifecycle. The RunUntilShutdown method now follows an 11-step startup: lnd connect, server dial, actor system, chain source, database, gRPC server, mailbox runtime, RPC clients, wallet actor, round actor, and finally blocking until shutdown. We extract the heavier initialization sequences into dedicated helper methods (initDatabase, initRPCClients, initWalletActor, initRoundActor) to keep RunUntilShutdown readable. The wallet actor uses lndbackend.NewBoardingBackend for key derivation and the db.Store convenience wrappers for persistence. The round actor uses the new lndbackend.ClientWallet for MuSig2 signing and fetches operator terms from the server via ArkService.GetInfo before starting. * indexer: wrap sync_client comments and signatures to 80 cols In this commit, we rewrap the comments, interface method signatures, and const docstrings in sync_client.go to stay within the 80-column line limit enforced by the linter.
* darepod: add daemon skeleton with config, CLI, and server Introduce the darepod package and cmd/darepod entry point for the Ark client daemon. The daemon uses cobra for CLI flag parsing with viper for config file and environment variable overrides. Key components: - Config struct with nested LndConfig, ServerConfig, and RPCConfig sections, each with sensible defaults and validation. - Main entry point that wires signal interception (lnd signal package), config validation, and the server lifecycle. - Server struct with RunUntilShutdown that connects to lnd via lndclient (blocking until chain-synced and unlocked), starts a gRPC server for the daemon's own API, and blocks until SIGINT/SIGTERM. - Placeholder RPCServer with GetInfo returning version, commit, network, and lnd identity pubkey. The proto-generated stubs will replace the hand-written types in a follow-up commit. - Makefile updated to build darepod binary into ./bin/. * daemonrpc: add daemon proto and GetInfo implementation Introduce the daemonrpc package with a DaemonService proto defining the client daemon's own gRPC API. The initial service exposes a single GetInfo RPC that returns version, commit hash, network, lnd identity pubkey, lnd alias, block height, and server connection status. The gen_protos.sh script is updated to generate stubs for the new daemonrpc package while excluding it from mailboxrpc generation since this is a local gRPC service, not a mailbox-transported one. The RPCServer and Server types are updated to use the generated proto types instead of the hand-written placeholders. * darepod: add server connection and lndclient wiring Wire the ark server gRPC connection into the daemon startup flow. The dialServer method supports three TLS modes: custom certificate, system certificate pool, and insecure (for regtest/development). The newMailboxEdge helper creates a MailboxServiceClient from the established gRPC connection, ready to be passed into serverconn.ConnectorConfig once the DeliveryStore is available. GetInfo now calls lnd's ChainKit.GetBestBlock to populate the block_height field instead of leaving it as a TODO. The gen_protos.sh exclusion for daemonrpc is removed so mailboxrpc stubs are generated for the DaemonService as well. * darepod: wire actor system and chain source into startup Initialize the actor system during daemon startup and register the chain source actor backed by lndclient. The chain backend adapter bridges lndclient's chain notifier, fee estimator, and wallet kit into the unified ChainBackend interface consumed by the chainsource actor, which provides fee estimation, block queries, and notification subscription via dedicated sub-actors. The actor system is gracefully shut down with a bounded timeout on daemon exit. The chain backend lifecycle is also managed via deferred Start/Stop calls. ServerConfig gains LocalMailboxID and RemoteMailboxID fields (with corresponding CLI flags) that will feed into the serverconn runtime once the DeliveryStore is wired. * darepod: register DaemonService on mailbox RPC mux Register the RPCServer as a DaemonServiceMailboxServer on a ServeMux so the ark server can invoke client-side RPCs (like GetInfo) through the mailbox transport. The same RPCServer implementation serves both the local gRPC interface and the mailbox RPC interface. The ServeMux is stored on the Server struct and will be plugged into the ConnectorConfig.Dispatchers map once the serverconn runtime is wired with a persistent DeliveryStore. * darepod: include cert path in TLS parse error * cmd/darepod: use viper struct binding instead of manual flag vars In this commit, we replace the manual pointer-based flag bindings (f.StringVar(&cfg.Field, ...)) with viper's BindPFlags + Unmarshal pattern. Flags are declared with f.String/f.Bool (no pointer target), bound to viper in bulk via BindPFlags, and then merged into the Config struct in a PreRunE hook via v.Unmarshal(cfg). This lets viper handle the full precedence chain (flags > env > config file > defaults) in one place, and avoids the repetitive boilerplate of threading each field through a pointer. * darepod: set TLS minimum version to 1.2 for server dial In this commit, we add MinVersion: tls.VersionTLS12 to both TLS config paths in dialServer (custom cert pool and system cert pool). Go's defaults already negotiate TLS 1.2+, but setting this explicitly makes the floor visible in the code and prevents accidental downgrade if defaults ever change. * multi: address review feedback from Claude bot In this commit, we fix several items flagged by the automated review: - Add v.SetEnvKeyReplacer so nested viper keys like lnd.host map to DAREPOD_LND_HOST instead of the invalid DAREPOD_LND.HOST. - Guard the expiresAt→uint64 cast in RegisterReceiveScriptTaproot against zero time.Time values that would wrap to a huge uint64. - Add missing function comments on randomNonceHex and newTaprootScope. - Add GoDoc field comments on TaprootScriptScope exported fields. - Add TODO comments for the unused gRPC TLS config paths and the forward-declared newMailboxEdge helper. * lndbackend: add ClientWallet adapter for round actor signing In this commit, we add a ClientWallet adapter that bridges lndclient's remote signing interfaces to the round.ClientWallet interface (input.Signer + DeriveNextKey). This allows the round actor's FSM to sign VTXO tree branches and forfeit transactions through lnd's remote signer without requiring a local wallet. The adapter wraps all MuSig2 session management methods (CreateSession, RegisterNonces, Sign, CombineSig, Cleanup) and handles the type conversions between lndclient's wire types and the musig2 package types. SignOutputRaw and ComputeInputScript forward to lnd's remote signer with proper prevout extraction for taproot sighash computation. Because input.Signer does not carry context, the adapter uses a background context for all RPC calls. The underlying gRPC deadline from the lndclient dial options still applies. * arkrpc: add operator terms fields to GetInfo proto In this commit, we extend the ArkService.GetInfo response with the full set of operator terms needed for round participation. The client daemon needs these parameters before the round actor can start, as they govern boarding script construction, VTXO exit paths, forfeit penalties, and sweep timelocks. New fields: boarding_exit_delay, vtxo_exit_delay, forfeit_script, sweep_key, sweep_delay, dust_limit, min/max_boarding_amount, fee_rate, and min_confirmations. * darepod: wire wallet and round actors into daemon startup In this commit, we complete the daemon's startup sequence by wiring the boarding wallet and round client actors into the server lifecycle. The RunUntilShutdown method now follows an 11-step startup: lnd connect, server dial, actor system, chain source, database, gRPC server, mailbox runtime, RPC clients, wallet actor, round actor, and finally blocking until shutdown. We extract the heavier initialization sequences into dedicated helper methods (initDatabase, initRPCClients, initWalletActor, initRoundActor) to keep RunUntilShutdown readable. The wallet actor uses lndbackend.NewBoardingBackend for key derivation and the db.Store convenience wrappers for persistence. The round actor uses the new lndbackend.ClientWallet for MuSig2 signing and fetches operator terms from the server via ArkService.GetInfo before starting. * indexer: wrap sync_client comments and signatures to 80 cols In this commit, we rewrap the comments, interface method signatures, and const docstrings in sync_client.go to stay within the 80-column line limit enforced by the linter.
systest: adapt for client IntentPackage refactor
In this PR, we add the initial
darepodclient daemon -- the main entrypoint that boots up, connects to lnd and the ark server, initializes the
actor system, and exposes a gRPC API for external tooling (CLI, GUI, etc).
The daemon follows the same lifecycle pattern used by lnd and tapd: cobra
for CLI parsing, viper for config/env overrides, lndclient for the lnd
connection (blocking until chain-synced and wallet-unlocked), and a signal
interceptor for graceful shutdown. The startup flow is sequential and
numbered for readability -- connect lnd, dial the ark server, init the
actor system, start the chain source, then fire up the gRPC listener.
Indexer RPC Schema and Client
The first four commits (carried over from
indexer-draft-oor-lite) add thearkrpc proto definitions for the indexer service and a mailbox-backed Go
client in
indexer/. The indexer client takes anRPCClientinterface(satisfied by the serverconn
UnaryFacade) and wraps the generatedIndexerServiceMailboxClientstubs. We also add taproot script-scope authhelpers for proof-of-control signing, and a cursor-based sync client with
full test coverage.
Daemon Scaffold
In
cmd/darepod/main.go, we wire up cobra with flags for--network,--lnd.host,--lnd.tlspath,--lnd.macaroonpath,--server.host,--server.insecure,--rpc.listenaddr, and the mailbox ID pair(
--server.localmailboxid,--server.remotemailboxid). Config structslive in
darepod/config.gowith sensible defaults and aValidate()method.
darepod/server.gois the main orchestrator. TheRunUntilShutdownmethodwalks through the numbered startup steps:
lndclient.NewLndServicespool, or insecure for regtest)
actor.NewActorSystem()with deferred graceful shutdownchainbackends.NewLNDBackendFromLndClientand register a
ChainSourceActorunderchainsource.ChainSourceKeyRuntime(blocked onDeliveryStore,a.k.a the DB layer)
DaemonServiceregistered on both localgRPC and a mailbox
ServeMuxfor dual-transport accessDaemonService RPC
We add a
daemonrpc/daemon.protowith a singleGetInfoRPC that returnsversion, commit hash, network, lnd pubkey, lnd alias, and current block
height (fetched via
ChainKit.GetBestBlock). TheRPCServerimplementsboth the gRPC server interface and the
DaemonServiceMailboxServerinterface, so the same handler serves both transports. The mailbox stubs
are generated by
protoc-gen-mailboxrpcas part ofmake rpc.What's Left
The serverconn
Runtimeneeds a persistentDeliveryStorebefore it canbe wired -- that's the DB layer work. Once that lands, the mailbox
ServeMuxplugs into
ConnectorConfig.Dispatchersand the indexer client getsinitialized via
indexer.New(runtime.Unary(), ...). The remaining actors(round, OOR, VTXO, wallet) similarly depend on the store.
See each commit message for a detailed description w.r.t the incremental
changes.