// New returns a new BtcdNotifier instance. This function assumes the btcd node // detailed in the passed configuration is already running, and // willing to accept new websockets clients. func New(config *btcrpcclient.ConnConfig) (*BtcdNotifier, error) { notifier := &BtcdNotifier{ notificationRegistry: make(chan interface{}), spendNotifications: make(map[wire.OutPoint]*spendNotification), confNotifications: make(map[wire.ShaHash][]*confirmationsNotification), confHeap: newConfirmationHeap(), disconnectedBlockHashes: make(chan *blockNtfn, 20), chainUpdateSignal: make(chan struct{}), txUpdateSignal: make(chan struct{}), quit: make(chan struct{}), } ntfnCallbacks := &btcrpcclient.NotificationHandlers{ OnBlockConnected: notifier.onBlockConnected, OnBlockDisconnected: notifier.onBlockDisconnected, OnRedeemingTx: notifier.onRedeemingTx, } // Disable connecting to btcd within the btcrpcclient.New method. We // defer establishing the connection to our .Start() method. config.DisableConnectOnNew = true config.DisableAutoReconnect = false chainConn, err := btcrpcclient.New(config, ntfnCallbacks) if err != nil { return nil, err } notifier.chainConn = chainConn return notifier, nil }
// NewRPCClient creates a client connection to the server described by the // connect string. If disableTLS is false, the remote RPC certificate must be // provided in the certs slice. The connection is not established immediately, // but must be done using the Start method. If the remote server does not // operate on the same bitcoin network as described by the passed chain // parameters, the connection will be disconnected. func NewRPCClient(chainParams *chaincfg.Params, connect, user, pass string, certs []byte, disableTLS bool, reconnectAttempts int) (*RPCClient, error) { if reconnectAttempts < 0 { return nil, errors.New("reconnectAttempts must be positive") } client := &RPCClient{ connConfig: &btcrpcclient.ConnConfig{ Host: connect, Endpoint: "ws", User: user, Pass: pass, Certificates: certs, DisableAutoReconnect: true, DisableConnectOnNew: true, DisableTLS: disableTLS, }, chainParams: chainParams, reconnectAttempts: reconnectAttempts, enqueueNotification: make(chan interface{}), dequeueNotification: make(chan interface{}), currentBlock: make(chan *waddrmgr.BlockStamp), quit: make(chan struct{}), } ntfnCallbacks := &btcrpcclient.NotificationHandlers{ OnClientConnected: client.onClientConnect, OnBlockConnected: client.onBlockConnected, OnBlockDisconnected: client.onBlockDisconnected, OnRecvTx: client.onRecvTx, OnRedeemingTx: client.onRedeemingTx, OnRescanFinished: client.onRescanFinished, OnRescanProgress: client.onRescanProgress, } rpcClient, err := btcrpcclient.New(client.connConfig, ntfnCallbacks) if err != nil { return nil, err } client.Client = rpcClient return client, nil }
// connectRPCClient attempts to establish an RPC connection to the created btcd // process belonging to this Harness instance. If the initial connection // attempt fails, this function will retry h.maxConnRetries times, backing off // the time between subsequent attempts. If after h.maxConnRetries attempts, // we're not able to establish a connection, this function returns with an // error. func (h *Harness) connectRPCClient() error { var client *btcrpcclient.Client var err error rpcConf := h.node.config.rpcConnConfig() for i := 0; i < h.maxConnRetries; i++ { if client, err = btcrpcclient.New(&rpcConf, h.handlers); err != nil { time.Sleep(time.Duration(i) * 50 * time.Millisecond) continue } break } if client == nil { return fmt.Errorf("connection timeout") } h.Node = client h.wallet.SetRPCClient(client) return nil }
func sweep() error { rpcPassword, err := promptSecret("Wallet RPC password") if err != nil { return errContext(err, "failed to read RPC password") } // Open RPC client. rpcCertificate, err := ioutil.ReadFile(opts.RPCCertificateFile) if err != nil { return errContext(err, "failed to read RPC certificate") } rpcClient, err := btcrpcclient.New(&btcrpcclient.ConnConfig{ Host: opts.RPCConnect, User: opts.RPCUsername, Pass: rpcPassword, Certificates: rpcCertificate, HTTPPostMode: true, }, nil) if err != nil { return errContext(err, "failed to create RPC client") } defer rpcClient.Shutdown() // Fetch all unspent outputs, ignore those not from the source // account, and group by their destination address. Each grouping of // outputs will be used as inputs for a single transaction sending to a // new destination account address. unspentOutputs, err := rpcClient.ListUnspent() if err != nil { return errContext(err, "failed to fetch unspent outputs") } sourceOutputs := make(map[string][]btcjson.ListUnspentResult) for _, unspentOutput := range unspentOutputs { if !unspentOutput.Spendable { continue } if unspentOutput.Confirmations < opts.RequiredConfirmations { continue } if unspentOutput.Account != opts.SourceAccount { continue } sourceAddressOutputs := sourceOutputs[unspentOutput.Address] sourceOutputs[unspentOutput.Address] = append(sourceAddressOutputs, unspentOutput) } var privatePassphrase string if len(sourceOutputs) != 0 { privatePassphrase, err = promptSecret("Wallet private passphrase") if err != nil { return errContext(err, "failed to read private passphrase") } } var totalSwept btcutil.Amount var numErrors int var reportError = func(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, format, args...) os.Stderr.Write(newlineBytes) numErrors++ } for _, previousOutputs := range sourceOutputs { inputSource := makeInputSource(previousOutputs) destinationSource := makeDestinationScriptSource(rpcClient, opts.DestinationAccount) tx, err := txauthor.NewUnsignedTransaction(nil, opts.FeeRate.Amount, inputSource, destinationSource) if err != nil { if err != (noInputValue{}) { reportError("Failed to create unsigned transaction: %v", err) } continue } // Unlock the wallet, sign the transaction, and immediately lock. err = rpcClient.WalletPassphrase(privatePassphrase, 60) if err != nil { reportError("Failed to unlock wallet: %v", err) continue } signedTransaction, complete, err := rpcClient.SignRawTransaction(tx.Tx) _ = rpcClient.WalletLock() if err != nil { reportError("Failed to sign transaction: %v", err) continue } if !complete { reportError("Failed to sign every input") continue } // Publish the signed sweep transaction. txHash, err := rpcClient.SendRawTransaction(signedTransaction, false) if err != nil { reportError("Failed to publish transaction: %v", err) continue } outputAmount := btcutil.Amount(tx.Tx.TxOut[0].Value) fmt.Printf("Swept %v to destination account with transaction %v\n", outputAmount, txHash) totalSwept += outputAmount } numPublished := len(sourceOutputs) - numErrors transactionNoun := pickNoun(numErrors, "transaction", "transactions") if numPublished != 0 { fmt.Printf("Swept %v to destination account across %d %s\n", totalSwept, numPublished, transactionNoun) } if numErrors > 0 { return fmt.Errorf("Failed to publish %d %s", numErrors, transactionNoun) } return nil }
// POSTClient creates the equivalent HTTP POST btcrpcclient.Client. func (c *RPCClient) POSTClient() (*btcrpcclient.Client, error) { configCopy := *c.connConfig configCopy.HTTPPostMode = true return btcrpcclient.New(&configCopy, nil) }