Example #1
0
// connectRPCClient attempts to establish an RPC connection to the created
// dcrd 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 *rpc.Client
	var err error

	rpcConf := h.node.config.rpcConnConfig()
	for i := 0; i < h.maxConnRetries; i++ {
		if client, err = rpc.New(&rpcConf, h.handlers); err != nil {
			time.Sleep(time.Duration(math.Log(float64(i+3))) * 50 * time.Millisecond)
			continue
		}
		break
	}

	if client == nil {
		return fmt.Errorf("connection timed out: %v", err)
	}

	err = client.NotifyBlocks()
	if err != nil {
		return err
	}

	h.Node = client
	return nil
}
Example #2
0
func main() {
	// Only override the handlers for notifications you care about.
	// Also note most of these handlers will only be called if you register
	// for notifications.  See the documentation of the dcrrpcclient
	// NotificationHandlers type for more details about each handler.
	ntfnHandlers := dcrrpcclient.NotificationHandlers{
		OnBlockConnected: func(hash *chainhash.Hash, height int32, time time.Time, vb uint16) {
			log.Printf("Block connected: %v (%d) %v %v", hash, height, time, vb)
		},
		OnBlockDisconnected: func(hash *chainhash.Hash, height int32, time time.Time, vb uint16) {
			log.Printf("Block disconnected: %v (%d) %v %v", hash, height, time, vb)
		},
	}

	// Connect to local dcrd RPC server using websockets.
	dcrdHomeDir := dcrutil.AppDataDir("dcrd", false)
	certs, err := ioutil.ReadFile(filepath.Join(dcrdHomeDir, "rpc.cert"))
	if err != nil {
		log.Fatal(err)
	}
	connCfg := &dcrrpcclient.ConnConfig{
		Host:         "localhost:8334",
		Endpoint:     "ws",
		User:         "******",
		Pass:         "******",
		Certificates: certs,
	}
	client, err := dcrrpcclient.New(connCfg, &ntfnHandlers)
	if err != nil {
		log.Fatal(err)
	}

	// Register for block connect and disconnect notifications.
	if err := client.NotifyBlocks(); err != nil {
		log.Fatal(err)
	}
	log.Println("NotifyBlocks: Registration Complete")

	// Get the current block count.
	blockCount, err := client.GetBlockCount()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Block count: %d", blockCount)

	// For this example gracefully shutdown the client after 10 seconds.
	// Ordinarily when to shutdown the client is highly application
	// specific.
	log.Println("Client shutdown in 10 seconds...")
	time.AfterFunc(time.Second*10, func() {
		log.Println("Client shutting down...")
		client.Shutdown()
		log.Println("Client shutdown complete.")
	})

	// Wait until the client either shuts down gracefully (or the user
	// terminates the process with Ctrl+C).
	client.WaitForShutdown()
}
Example #3
0
func main() {
	// Only override the handlers for notifications you care about.
	// Also note most of the handlers will only be called if you register
	// for notifications.  See the documentation of the dcrrpcclient
	// NotificationHandlers type for more details about each handler.
	ntfnHandlers := dcrrpcclient.NotificationHandlers{
		OnAccountBalance: func(account string, balance dcrutil.Amount, confirmed bool) {
			log.Printf("New balance for account %s: %v", account,
				balance)
		},
	}

	// Connect to local dcrwallet RPC server using websockets.
	certHomeDir := dcrutil.AppDataDir("dcrwallet", false)
	certs, err := ioutil.ReadFile(filepath.Join(certHomeDir, "rpc.cert"))
	if err != nil {
		log.Fatal(err)
	}
	connCfg := &dcrrpcclient.ConnConfig{
		Host:         "localhost:18332",
		Endpoint:     "ws",
		User:         "******",
		Pass:         "******",
		Certificates: certs,
	}
	client, err := dcrrpcclient.New(connCfg, &ntfnHandlers)
	if err != nil {
		log.Fatal(err)
	}

	// Get the list of unspent transaction outputs (utxos) that the
	// connected wallet has at least one private key for.
	unspent, err := client.ListUnspent()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Num unspent outputs (utxos): %d", len(unspent))
	if len(unspent) > 0 {
		log.Printf("First utxo:\n%v", spew.Sdump(unspent[0]))
	}

	// For this example gracefully shutdown the client after 10 seconds.
	// Ordinarily when to shutdown the client is highly application
	// specific.
	log.Println("Client shutdown in 10 seconds...")
	time.AfterFunc(time.Second*10, func() {
		log.Println("Client shutting down...")
		client.Shutdown()
		log.Println("Client shutdown complete.")
	})

	// Wait until the client either shuts down gracefully (or the user
	// terminates the process with Ctrl+C).
	client.WaitForShutdown()
}
Example #4
0
// NewClient 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 decred network as described by the passed chain parameters, the
// connection will be disconnected.
func NewClient(chainParams *chaincfg.Params, connect, user, pass string,
	certs []byte, disableTLS bool) (*Client, error) {
	client := Client{
		reorganizeToHash:          chainhash.Hash{},
		reorganizing:              false,
		chainParams:               chainParams,
		enqueueNotification:       make(chan interface{}),
		dequeueNotification:       make(chan interface{}),
		enqueueVotingNotification: make(chan interface{}),
		dequeueVotingNotification: make(chan interface{}),
		currentBlock:              make(chan *waddrmgr.BlockStamp),
		quit:                      make(chan struct{}),
	}
	ntfnCallbacks := dcrrpcclient.NotificationHandlers{
		OnClientConnected:       client.onClientConnect,
		OnBlockConnected:        client.onBlockConnected,
		OnBlockDisconnected:     client.onBlockDisconnected,
		OnReorganization:        client.onReorganization,
		OnWinningTickets:        client.onWinningTickets,
		OnSpentAndMissedTickets: client.onSpentAndMissedTickets,
		OnStakeDifficulty:       client.onStakeDifficulty,
		OnRecvTx:                client.onRecvTx,
		OnRedeemingTx:           client.onRedeemingTx,
		OnRescanFinished:        client.onRescanFinished,
		OnRescanProgress:        client.onRescanProgress,
	}
	conf := dcrrpcclient.ConnConfig{
		Host:                 connect,
		Endpoint:             "ws",
		User:                 user,
		Pass:                 pass,
		Certificates:         certs,
		DisableAutoReconnect: true,
		DisableConnectOnNew:  true,
		DisableTLS:           disableTLS,
	}
	c, err := dcrrpcclient.New(&conf, &ntfnCallbacks)
	if err != nil {
		return nil, err
	}
	client.Client = c
	return &client, nil
}
Example #5
0
// 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: &dcrrpcclient.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{}),
		enqueueVotingNotification: make(chan interface{}),
		dequeueVotingNotification: make(chan interface{}),
		quit: make(chan struct{}),
	}
	ntfnCallbacks := &dcrrpcclient.NotificationHandlers{
		OnClientConnected:       client.onClientConnect,
		OnBlockConnected:        client.onBlockConnected,
		OnBlockDisconnected:     client.onBlockDisconnected,
		OnRelevantTxAccepted:    client.onRelevantTxAccepted,
		OnReorganization:        client.onReorganization,
		OnWinningTickets:        client.onWinningTickets,
		OnSpentAndMissedTickets: client.onSpentAndMissedTickets,
		OnStakeDifficulty:       client.onStakeDifficulty,
	}
	rpcClient, err := dcrrpcclient.New(client.connConfig, ntfnCallbacks)
	if err != nil {
		return nil, err
	}
	client.Client = rpcClient
	return client, nil
}
Example #6
0
// SetUp initializes the rpc test state. Initialization includes: starting up a
// simnet node, creating a websocket client and connecting to the started node,
// and finally: optionally generating and submitting a testchain with a configurable
// number of mature coinbase outputs coinbase outputs.
func (h *Harness) SetUp(createTestChain bool, numMatureOutputs uint32) error {
	var err error

	// Start the dcrd node itself. This spawns a new process which will be
	// managed
	if err = h.node.Start(); err != nil {
		return err
	}
	time.Sleep(200 * time.Millisecond)
	if err = h.connectRPCClient(); err != nil {
		return err
	}
	fmt.Println("Node RPC client connected.")

	// Start dcrwallet. This spawns a new process which will be managed
	if err = h.wallet.Start(); err != nil {
		return err
	}
	time.Sleep(1 * time.Second)

	// Connect walletClient so we can get the mining address
	var walletClient *rpc.Client
	walletRPCConf := h.wallet.config.rpcConnConfig()
	for i := 0; i < 400; i++ {
		if walletClient, err = rpc.New(&walletRPCConf, nil); err != nil {
			time.Sleep(time.Duration(math.Log(float64(i+3))) * 50 * time.Millisecond)
			continue
		}
		break
	}
	if walletClient == nil {
		return fmt.Errorf("walletClient connection timedout")
	}
	fmt.Println("Wallet RPC client connected.")
	h.WalletRPC = walletClient

	// Get a new address from the wallet to be set with dcrd's --miningaddr
	time.Sleep(5 * time.Second)
	var miningAddr dcrutil.Address
	for i := 0; i < 100; i++ {
		if miningAddr, err = walletClient.GetNewAddress("default"); err != nil {
			time.Sleep(time.Duration(math.Log(float64(i+3))) * 50 * time.Millisecond)
			continue
		}
		break
	}
	if miningAddr == nil {
		return fmt.Errorf("RPC not up for mining addr %v %v", h.testNodeDir,
			h.testWalletDir)
	}
	h.miningAddr = miningAddr

	var extraArgs []string
	miningArg := fmt.Sprintf("--miningaddr=%s", miningAddr)
	extraArgs = append(extraArgs, miningArg)

	// Stop node so we can restart it with --miningaddr
	if err = h.node.Stop(); err != nil {
		return err
	}

	config, err := newConfig(h.node.config.appDataDir, h.node.config.certFile,
		h.node.config.keyFile, extraArgs)
	if err != nil {
		return err
	}
	config.listen = h.node.config.listen
	config.rpcListen = h.node.config.rpcListen

	// Create the testing node bounded to the simnet.
	node, err := newNode(config, h.testNodeDir)
	if err != nil {
		return err
	}
	h.node = node

	// Restart node with mining address set
	if err = h.node.Start(); err != nil {
		return err
	}
	time.Sleep(1 * time.Second)
	if err := h.connectRPCClient(); err != nil {
		return err
	}
	fmt.Printf("Node RPC client connected, miningaddr: %v.\n", miningAddr)

	// Create a test chain with the desired number of mature coinbase outputs
	if createTestChain {
		numToGenerate := uint32(h.ActiveNet.CoinbaseMaturity) + numMatureOutputs
		fmt.Printf("Generating %v blocks...\n", numToGenerate)
		_, err := h.Node.Generate(numToGenerate)
		if err != nil {
			return err
		}
		fmt.Println("Block generation complete.")
	}

	// Wait for the wallet to sync up to the current height.
	// TODO: Figure out why this is the longest wait, about 60 sec, when it
	// should be almost immediate.
	fmt.Println("Waiting for wallet to sync to current height.")
	ticker := time.NewTicker(time.Millisecond * 500)
	desiredHeight := int64(numMatureOutputs + uint32(h.ActiveNet.CoinbaseMaturity))
out:
	for range ticker.C {
		count, err := h.WalletRPC.GetBlockCount()
		if err != nil {
			return err
		}
		if count == desiredHeight {
			break out
		}
	}
	ticker.Stop()

	fmt.Println("Wallet sync complete.")

	return nil
}
Example #7
0
// POSTClient creates the equivalent HTTP POST dcrrpcclient.Client.
func (c *RPCClient) POSTClient() (*dcrrpcclient.Client, error) {
	configCopy := *c.connConfig
	configCopy.HTTPPostMode = true
	return dcrrpcclient.New(&configCopy, nil)
}
Example #8
0
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 := dcrrpcclient.New(&dcrrpcclient.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][]dcrjson.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 dcrutil.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 := dcrutil.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
}
Example #9
0
File: main.go Project: decred/dcrd
// realMain is the real main function for the utility.  It is necessary to work
// around the fact that deferred functions do not run when os.Exit() is called.
func realMain() int {
	// Load configuration and parse command line.
	cfg, args, err := loadConfig()
	if err != nil {
		return rcError
	}

	// Ensure the user specified a single argument.
	if len(args) < 1 {
		usage("Transaction hash not specified")
		return rcError
	}
	if len(args) > 1 {
		usage("Too many arguments specified")
		return rcError
	}

	// Read the argument from a stdin pipe when it is '-'.
	arg0 := args[0]
	if arg0 == "-" {
		bio := bufio.NewReader(os.Stdin)
		param, err := bio.ReadString('\n')
		if err != nil && err != io.EOF {
			fmt.Fprintf(os.Stderr, "Failed to read data from "+
				"stdin: %v\n", err)
			return rcError
		}
		if err == io.EOF && len(param) == 0 {
			fmt.Fprintln(os.Stderr, "Not enough lines provided on "+
				"stdin")
			return rcError
		}
		arg0 = param
	}
	arg0 = strings.TrimSpace(arg0)

	// Attempt to unmarshal the parameter as a JSON array of strings if it
	// looks like JSON input.  This allows multiple transactions to be
	// specified via the argument.  Treat the argument as a single hash if
	// it fails to unmarshal.
	var txHashes []*chainhash.Hash
	if strings.Contains(arg0, "[") && strings.Contains(arg0, "]") {
		var txHashStrs []string
		if err := json.Unmarshal([]byte(arg0), &txHashStrs); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to unmarshal JSON "+
				"string array of transaction hashes: %v\n", err)
			return rcError
		}
		for _, txHashStr := range txHashStrs {
			txHash, err := chainhash.NewHashFromStr(txHashStr)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Unable to parse "+
					"transaction hash %q: %v\n", txHashStr, err)
				return rcError
			}
			txHashes = append(txHashes, txHash)
		}
	} else {
		// Parse the provided transaction hash string.
		arg0 = strings.Trim(arg0, `"`)
		txHash, err := chainhash.NewHashFromStr(arg0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to parse transaction "+
				"hash %q: %v\n", arg0, err)
			return rcError
		}
		txHashes = append(txHashes, txHash)
	}

	// Connect to dcrd RPC server using websockets.
	certs, err := ioutil.ReadFile(cfg.RPCCert)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read RPC server TLS cert: %v\n",
			err)
		return rcError
	}
	connCfg := &dcrrpcclient.ConnConfig{
		Host:         cfg.RPCServer,
		Endpoint:     "ws",
		User:         cfg.RPCUser,
		Pass:         cfg.RPCPassword,
		Certificates: certs,
	}
	client, err := dcrrpcclient.New(connCfg, nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to connect to dcrd RPC server: "+
			"%v\n", err)
		return rcError
	}
	defer client.Shutdown()

	// Check all of the provided transactions.
	var hasDevPremineOuts bool
	for _, txHash := range txHashes {
		// Get a list of all dev premine outpoints the are ancestors of
		// all inputs to the provided transaction.
		devPremineOuts, err := traceDevPremineOuts(client, txHash)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return rcError
		}

		// List outputs which are dev premine outputs.
		if len(devPremineOuts) > 0 {
			hasDevPremineOuts = true

			// Don't print anything in quiet mode.
			if cfg.Quiet {
				continue
			}
			fmt.Printf("Transaction %v contains inputs which "+
				"trace back to the following original dev "+
				"premine outpoints:\n", txHash)
			for _, out := range devPremineOuts {
				fmt.Println(out)
			}
		}
	}

	// Return the approriate code depending on whether or not any of the
	// inputs trace back to a dev premine outpoint.
	if hasDevPremineOuts {
		return rcDevPremineInputs
	}
	return rcNoDevPremineInputs
}