wallet: bitcoind support with RPC polling (#254)

* support bitcoind connection

* fix: wallet should be the varibale, not s.wallet

* block cache: up to 2M blocks

* WithBitcoind wait for sync

* WithBitcoind rename

* switch based on the ENV VARs

* Add make targets
This commit is contained in:
Marco Argentieri
2024-08-15 20:02:11 +02:00
committed by GitHub
parent c840aac431
commit 94cd222004
6 changed files with 136 additions and 21 deletions

2
server/.gitignore vendored
View File

@@ -25,4 +25,4 @@ go.work.sum
.vscode/ .vscode/
tmp/ data/

View File

@@ -1,4 +1,4 @@
.PHONY: build clean cov help intergrationtest lint run run-signet test vet proto proto-lint .PHONY: build clean cov help intergrationtest lint run run-neutrino run-neutrino-signet test vet proto proto-lint
## build: build for all platforms ## build: build for all platforms
build: build:
@@ -30,26 +30,43 @@ lint:
@echo "Linting code..." @echo "Linting code..."
@golangci-lint run --fix @golangci-lint run --fix
## run: run in regtest mode ## run: run in regtest mode with bitcoind
run: clean run: clean
@echo "Running arkd in dev mode..." @echo "Running arkd with Bitcoin Core in regtest mode ..."
@export ARK_NEUTRINO_PEER=localhost:18444; \ @export ARK_ROUND_INTERVAL=10; \
export ARK_ROUND_INTERVAL=10; \
export ARK_LOG_LEVEL=5; \ export ARK_LOG_LEVEL=5; \
export ARK_NETWORK=regtest; \ export ARK_NETWORK=regtest; \
export ARK_PORT=7070; \ export ARK_PORT=7070; \
export ARK_NO_TLS=true; \ export ARK_NO_TLS=true; \
export ARK_NO_MACAROONS=true; \ export ARK_NO_MACAROONS=true; \
export ARK_MIN_RELAY_FEE=200; \
export ARK_TX_BUILDER_TYPE=covenantless; \ export ARK_TX_BUILDER_TYPE=covenantless; \
export ARK_ESPLORA_URL=http://localhost:3000; \ export ARK_ESPLORA_URL=http://localhost:3000; \
export ARK_MIN_RELAY_FEE=200; \ export ARK_BITCOIND_RPC_USER=admin1; \
export ARK_BITCOIND_RPC_PASS=123; \
export ARK_BITCOIND_RPC_HOST=localhost:18443; \
export ARK_DATADIR=./data/regtest; \
go run ./cmd/arkd go run ./cmd/arkd
## export ARK_NEUTRINO_PEER=44.240.54.180:38333 ; \ ## run-neutrino: run in regtest mode with neutrino
run-neutrino: clean
@echo "Running arkd with Neutrino in regtest mode ..."
@export ARK_ROUND_INTERVAL=10; \
export ARK_LOG_LEVEL=5; \
export ARK_NETWORK=regtest; \
export ARK_PORT=7070; \
export ARK_NO_TLS=true; \
export ARK_NO_MACAROONS=true; \
export ARK_MIN_RELAY_FEE=200; \
export ARK_TX_BUILDER_TYPE=covenantless; \
export ARK_ESPLORA_URL=http://localhost:3000; \
export ARK_NEUTRINO_PEER=localhost:18444; \
export ARK_DATADIR=./data/regtest; \
go run ./cmd/arkd
## run: run in signet mode ## run-neutrino-signet: run in signet mode
run-signet: clean run-neutrino-signet: clean
@echo "Running arkd in signet mode..." @echo "Running arkd with Neutrino in signet mode ..."
@export ARK_ROUND_INTERVAL=10; \ @export ARK_ROUND_INTERVAL=10; \
export ARK_LOG_LEVEL=5; \ export ARK_LOG_LEVEL=5; \
export ARK_NETWORK=signet; \ export ARK_NETWORK=signet; \
@@ -60,7 +77,7 @@ run-signet: clean
export ARK_ESPLORA_URL=https://mutinynet.com/api; \ export ARK_ESPLORA_URL=https://mutinynet.com/api; \
export ARK_NEUTRINO_PEER=45.79.52.207:38333; \ export ARK_NEUTRINO_PEER=45.79.52.207:38333; \
export ARK_MIN_RELAY_FEE=200; \ export ARK_MIN_RELAY_FEE=200; \
export ARK_DATADIR=./tmp/signet/arkd-data; \ export ARK_DATADIR=./data/signet; \
go run ./cmd/arkd go run ./cmd/arkd
## test: runs unit and component tests ## test: runs unit and component tests

View File

@@ -76,6 +76,9 @@ func mainAction(_ *cli.Context) error {
UnilateralExitDelay: cfg.UnilateralExitDelay, UnilateralExitDelay: cfg.UnilateralExitDelay,
EsploraURL: cfg.EsploraURL, EsploraURL: cfg.EsploraURL,
NeutrinoPeer: cfg.NeutrinoPeer, NeutrinoPeer: cfg.NeutrinoPeer,
BitcoindRpcUser: cfg.BitcoindRpcUser,
BitcoindRpcPass: cfg.BitcoindRpcPass,
BitcoindRpcHost: cfg.BitcoindRpcHost,
} }
svc, err := grpcservice.NewService(svcConfig, appConfig) svc, err := grpcservice.NewService(svcConfig, appConfig)
if err != nil { if err != nil {

View File

@@ -64,8 +64,11 @@ type Config struct {
RoundLifetime int64 RoundLifetime int64
UnilateralExitDelay int64 UnilateralExitDelay int64
EsploraURL string EsploraURL string
NeutrinoPeer string NeutrinoPeer string
BitcoindRpcUser string
BitcoindRpcPass string
BitcoindRpcHost string
repo ports.RepoManager repo ports.RepoManager
svc application.Service svc application.Service
@@ -230,13 +233,34 @@ func (c *Config) walletService() error {
return fmt.Errorf("missing esplora url, covenant-less ark requires ARK_ESPLORA_URL to be set") return fmt.Errorf("missing esplora url, covenant-less ark requires ARK_ESPLORA_URL to be set")
} }
svc, err := btcwallet.NewService(btcwallet.WalletConfig{ // Check if both Neutrino peer and Bitcoind RPC credentials are provided
Datadir: c.DbDir, if c.NeutrinoPeer != "" && (c.BitcoindRpcUser != "" || c.BitcoindRpcPass != "") {
Network: c.Network, return fmt.Errorf("cannot use both Neutrino peer and Bitcoind RPC credentials")
EsploraURL: c.EsploraURL, }
},
btcwallet.WithNeutrino(c.NeutrinoPeer), var svc ports.WalletService
) var err error
switch {
case c.NeutrinoPeer != "":
svc, err = btcwallet.NewService(btcwallet.WalletConfig{
Datadir: c.DbDir,
Network: c.Network,
EsploraURL: c.EsploraURL,
}, btcwallet.WithNeutrino(c.NeutrinoPeer))
case c.BitcoindRpcUser != "" && c.BitcoindRpcPass != "":
svc, err = btcwallet.NewService(btcwallet.WalletConfig{
Datadir: c.DbDir,
Network: c.Network,
EsploraURL: c.EsploraURL,
}, btcwallet.WithPollingBitcoind(c.BitcoindRpcHost, c.BitcoindRpcUser, c.BitcoindRpcPass))
// Placeholder for future initializers like WithBitcoindZMQ
default:
return fmt.Errorf("either Neutrino peer or Bitcoind RPC credentials must be provided")
}
if err != nil { if err != nil {
return err return err
} }

View File

@@ -31,6 +31,9 @@ type Config struct {
UnilateralExitDelay int64 UnilateralExitDelay int64
EsploraURL string EsploraURL string
NeutrinoPeer string NeutrinoPeer string
BitcoindRpcUser string
BitcoindRpcPass string
BitcoindRpcHost string
TLSExtraIPs []string TLSExtraIPs []string
TLSExtraDomains []string TLSExtraDomains []string
} }
@@ -53,6 +56,9 @@ var (
UnilateralExitDelay = "UNILATERAL_EXIT_DELAY" UnilateralExitDelay = "UNILATERAL_EXIT_DELAY"
EsploraURL = "ESPLORA_URL" EsploraURL = "ESPLORA_URL"
NeutrinoPeer = "NEUTRINO_PEER" NeutrinoPeer = "NEUTRINO_PEER"
BitcoindRpcUser = "BITCOIND_RPC_USER"
BitcoindRpcPass = "BITCOIND_RPC_PASS"
BitcoindRpcHost = "BITCOIND_RPC_HOST"
NoMacaroons = "NO_MACAROONS" NoMacaroons = "NO_MACAROONS"
NoTLS = "NO_TLS" NoTLS = "NO_TLS"
TLSExtraIP = "TLS_EXTRA_IP" TLSExtraIP = "TLS_EXTRA_IP"
@@ -128,6 +134,9 @@ func LoadConfig() (*Config, error) {
UnilateralExitDelay: viper.GetInt64(UnilateralExitDelay), UnilateralExitDelay: viper.GetInt64(UnilateralExitDelay),
EsploraURL: viper.GetString(EsploraURL), EsploraURL: viper.GetString(EsploraURL),
NeutrinoPeer: viper.GetString(NeutrinoPeer), NeutrinoPeer: viper.GetString(NeutrinoPeer),
BitcoindRpcUser: viper.GetString(BitcoindRpcUser),
BitcoindRpcPass: viper.GetString(BitcoindRpcPass),
BitcoindRpcHost: viper.GetString(BitcoindRpcHost),
NoMacaroons: viper.GetBool(NoMacaroons), NoMacaroons: viper.GetBool(NoMacaroons),
TLSExtraIPs: viper.GetStringSlice(TLSExtraIP), TLSExtraIPs: viper.GetStringSlice(TLSExtraIP),
TLSExtraDomains: viper.GetStringSlice(TLSExtraDomain), TLSExtraDomains: viper.GetStringSlice(TLSExtraDomain),

View File

@@ -138,6 +138,68 @@ func WithNeutrino(initialPeer string) WalletOption {
} }
} }
func WithPollingBitcoind(host, user, pass string) WalletOption {
return func(s *service) error {
netParams := s.cfg.chainParams()
// Create a new bitcoind configuration
bitcoindConfig := &chain.BitcoindConfig{
ChainParams: netParams,
Host: host,
User: user,
Pass: pass,
PollingConfig: &chain.PollingConfig{
BlockPollingInterval: 10 * time.Second,
TxPollingInterval: 5 * time.Second,
TxPollingIntervalJitter: 0.1,
RPCBatchSize: 20,
RPCBatchInterval: 1 * time.Second,
},
}
chain.UseLogger(logger("chain"))
// Create the BitcoindConn first
bitcoindConn, err := chain.NewBitcoindConn(bitcoindConfig)
if err != nil {
return fmt.Errorf("failed to create bitcoind connection: %w", err)
}
// Start the bitcoind connection
if err := bitcoindConn.Start(); err != nil {
return fmt.Errorf("failed to start bitcoind connection: %w", err)
}
// Now create the BitcoindClient using the connection
chainClient := bitcoindConn.NewBitcoindClient()
// Start the chain client
if err := chainClient.Start(); err != nil {
bitcoindConn.Stop()
return fmt.Errorf("failed to start bitcoind client: %w", err)
}
// wait for bitcoind to sync
for !chainClient.IsCurrent() {
time.Sleep(1 * time.Second)
}
// Set up the wallet as chain source and scanner
if err := withChainSource(chainClient)(s); err != nil {
chainClient.Stop()
bitcoindConn.Stop()
return fmt.Errorf("failed to set chain source: %w", err)
}
if err := withScanner(chainClient)(s); err != nil {
chainClient.Stop()
bitcoindConn.Stop()
return fmt.Errorf("failed to set scanner: %w", err)
}
return nil
}
}
// NewService creates the wallet service, an option must be set to configure the chain source. // NewService creates the wallet service, an option must be set to configure the chain source.
func NewService(cfg WalletConfig, options ...WalletOption) (ports.WalletService, error) { func NewService(cfg WalletConfig, options ...WalletOption) (ports.WalletService, error) {
wallet.UseLogger(logger("wallet")) wallet.UseLogger(logger("wallet"))