Exemple #1
0
func (w *Wallet) handleChainVotingNotifications(chainClient *chain.RPCClient) {
	for n := range chainClient.NotificationsVoting() {
		var err error
		strErrType := ""

		switch n := n.(type) {
		case chain.WinningTickets:
			err = walletdb.Update(w.db, func(dbtx walletdb.ReadWriteTx) error {
				return w.handleWinningTickets(dbtx, n.BlockHash, n.BlockHeight, n.Tickets)
			})
			strErrType = "WinningTickets"
		case chain.MissedTickets:
			err = walletdb.Update(w.db, func(dbtx walletdb.ReadWriteTx) error {
				return w.handleMissedTickets(dbtx, n.BlockHash, n.BlockHeight, n.Tickets)
			})
			strErrType = "MissedTickets"
		default:
			err = fmt.Errorf("voting handler received unknown ntfn type")
		}
		if err != nil {
			log.Errorf("Cannot handle chain server voting "+
				"notification %v: %v", strErrType, err)
		}
	}
	w.wg.Done()
}
Exemple #2
0
func (w *Wallet) handleConsensusRPCNotifications(chainClient *chain.RPCClient) {
	for n := range chainClient.Notifications() {
		var notificationName string
		var err error
		switch n := n.(type) {
		case chain.ClientConnected:
			log.Infof("The client has successfully connected to dcrd and " +
				"is now handling websocket notifications")
		case chain.BlockConnected:
			notificationName = "blockconnected"
			err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				return w.onBlockConnected(tx, n.BlockHeader, n.Transactions)
			})
		case chain.Reorganization:
			notificationName = "reorganizing"
			err = w.handleReorganizing(n.OldHash, n.NewHash, n.OldHeight, n.NewHeight)
		case chain.RelevantTxAccepted:
			notificationName = "relevanttxaccepted"
			err = walletdb.Update(w.db, func(dbtx walletdb.ReadWriteTx) error {
				return w.processTransaction(dbtx, n.Transaction, nil, nil)
			})
		}
		if err != nil {
			log.Errorf("Failed to process consensus server notification "+
				"(name: `%s`, detail: `%v`)", notificationName, err)
		}
	}
}
Exemple #3
0
// scanAddressRange scans backwards from end to start many addresses in the
// account branch, and return the first index that is found on the blockchain.
// If the address doesn't exist, false is returned as the first argument.
func (w *Wallet) scanAddressRange(account uint32, branch uint32, start int,
	end int, chainClient *chain.RPCClient) (bool, int, error) {

	var addresses []dcrutil.Address
	err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
		addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
		var err error
		addresses, err = w.Manager.AddressesDerivedFromDbAcct(addrmgrNs,
			uint32(start), uint32(end+1), account, branch)
		if err != nil {
			return errDerivation
		}
		return nil
	})
	if err != nil {
		return false, 0, err
	}

	// Whether or not the addresses exist is encoded as a binary
	// bitset.
	exists, err := chainClient.ExistsAddresses(addresses)
	if err != nil {
		return false, 0, err
	}
	existsB, err := hex.DecodeString(exists)
	if err != nil {
		return false, 0, err
	}
	set := bitset.Bytes(existsB)

	// Prevent a panic when an empty message is passed as a response.
	if len(set) == 0 {
		return false, 0, nil
	}

	// Scan backwards and return if we find an address exists.
	idx := end
	itr := len(addresses) - 1
	for idx >= start {
		// If the address exists in the mempool or blockchain according
		// to the bit set returned, return this index.
		if set.Get(itr) {
			return true, idx, nil
		}

		itr--
		idx--
	}

	return false, 0, nil
}
Exemple #4
0
// accountIsUsed checks if an account has ever been used by scanning the
// first acctSeekWidth many addresses for usage.
func (w *Wallet) accountIsUsed(account uint32, chainClient *chain.RPCClient) bool {
	// Search external branch then internal branch for a used
	// address. We need to set the address function to use based
	// on whether or not this is the initial sync. The function
	// AddressDerivedFromCointype is able to see addresses that
	// exists in accounts that have not yet been created, while
	// AddressDerivedFromDbAcct can not.
	addrFunc := w.Manager.AddressDerivedFromDbAcct
	if w.initiallyUnlocked {
		addrFunc = w.Manager.AddressDerivedFromCointype
	}

	for branch := uint32(0); branch < 2; branch++ {
		for i := uint32(0); i < acctSeekWidth; i++ {
			var addr dcrutil.Address
			err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
				addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
				var err error
				addr, err = addrFunc(addrmgrNs, i, account, branch)
				return err
			})
			if err != nil {
				// Skip erroneous keys, which happen rarely.
				continue
			}

			exists, err := chainClient.ExistsAddress(addr)
			if err != nil {
				return false
			}
			if exists {
				return true
			}
		}
	}

	return false
}
Exemple #5
0
// debugAccountAddrGapsString is a debug function that prints a graphical outlook
// of address usage to a string, from the perspective of the daemon.
func debugAccountAddrGapsString(chainClient *chain.RPCClient, scanBackFrom uint32,
	account uint32, branch uint32, w *Wallet) (string, error) {

	var buf bytes.Buffer
	str := fmt.Sprintf("Begin debug address scan scanning backwards from "+
		"idx %v, account %v, branch %v\n", scanBackFrom, account, branch)
	buf.WriteString(str)
	var firstUsedIndex uint32
	for i := scanBackFrom; i > 0; i-- {
		var addr dcrutil.Address
		err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
			addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
			var err error
			addr, err = w.Manager.AddressDerivedFromDbAcct(addrmgrNs,
				i, account, branch)
			return err
		})
		// Skip erroneous keys.
		if err != nil {
			continue
		}

		exists, err := chainClient.ExistsAddress(addr)
		if err != nil {
			return "", fmt.Errorf("failed to access chain server: %v",
				err)
		}

		if exists {
			firstUsedIndex = i
			break
		}
	}

	str = fmt.Sprintf("Last used index found: %v\n", firstUsedIndex)
	buf.WriteString(str)

	var batchSize uint32 = 50
	batches := (firstUsedIndex / batchSize) + 1
	var lastBatchSize uint32
	if firstUsedIndex%batchSize != 0 {
		lastBatchSize = firstUsedIndex - ((batches - 1) * batchSize)
	}

	for i := uint32(0); i < batches; i++ {
		str = fmt.Sprintf("%8v", i*batchSize)
		buf.WriteString(str)

		start := i * batchSize
		end := (i + 1) * batchSize
		if i == batches-1 {
			// Nothing to do because last batch empty.
			if lastBatchSize == 0 {
				break
			}
			end = (i*batchSize + lastBatchSize) + 1
		}

		for j := start; j < end; j++ {
			if j%10 == 0 {
				buf.WriteString("  ")
			}

			char := "_"
			var addr dcrutil.Address
			err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
				addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
				var err error
				addr, err = w.Manager.AddressDerivedFromDbAcct(addrmgrNs,
					j, account, branch)
				return err
			})
			if err != nil {
				char = "X"
			}

			exists, err := chainClient.ExistsAddress(addr)
			if err != nil {
				return "", fmt.Errorf("failed to access chain server: %v",
					err)
			}
			if exists {
				char = "#"
			}

			buf.WriteString(char)
		}

		buf.WriteString("\n")
	}

	return buf.String(), nil
}
Exemple #6
0
// LiveTicketHashes returns the hashes of live tickets that have been purchased
// by the wallet.
func (w *Wallet) LiveTicketHashes(rpcClient *chain.RPCClient, includeImmature bool) ([]chainhash.Hash, error) {
	// This was mostly copied from an older version of the legacy RPC server
	// implementation, hence the overall weirdness, inefficiencies, and the
	// direct dependency on the consensus server RPC client.

	var blk waddrmgr.BlockStamp
	var ticketHashes []chainhash.Hash
	var stakeMgrTickets []chainhash.Hash
	err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
		txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)

		blk = w.Manager.SyncedTo()

		// UnspentTickets collects all the tickets that pay out to a
		// public key hash for a public key owned by this wallet.
		var err error
		ticketHashes, err = w.TxStore.UnspentTickets(txmgrNs, blk.Height,
			includeImmature)
		if err != nil {
			return err
		}

		// Access the stake manager and see if there are any extra tickets
		// there. Likely they were either pruned because they failed to get
		// into the blockchain or they are P2SH for some script we own.
		stakeMgrTickets, err = w.StakeMgr.DumpSStxHashes()
		return err
	})
	if err != nil {
		return nil, err
	}

	for _, h := range stakeMgrTickets {
		if sliceContainsHash(ticketHashes, h) {
			continue
		}

		// Get the raw transaction information from daemon and add
		// any relevant tickets. The ticket output is always the
		// zeroeth output.
		spent, err := rpcClient.GetTxOut(&h, 0, true)
		if err != nil {
			continue
		}
		// This returns nil if the output is spent.
		if spent == nil {
			continue
		}

		ticketTx, err := rpcClient.GetRawTransactionVerbose(&h)
		if err != nil {
			continue
		}

		txHeight := ticketTx.BlockHeight
		unconfirmed := (txHeight == 0)
		immature := (blk.Height-int32(txHeight) <
			int32(w.ChainParams().TicketMaturity))
		if includeImmature {
			ticketHashes = append(ticketHashes, h)
		} else {
			if !(unconfirmed || immature) {
				ticketHashes = append(ticketHashes, h)
			}
		}
	}

	return ticketHashes, nil
}
Exemple #7
0
// rescan synchronously scans over all blocks on the main chain starting at
// startHash and height up through the recorded main chain tip block.  The
// progress channel, if non-nil, is sent non-error progress notifications with
// the heights the rescan has completed through, starting with the start height.
func (w *Wallet) rescan(chainClient *chain.RPCClient, startHash *chainhash.Hash, height int32,
	p chan<- RescanProgress, cancel <-chan struct{}) error {

	blockHashStorage := make([]chainhash.Hash, maxBlocksPerRescan)
	rescanFrom := *startHash
	inclusive := true
	for {
		select {
		case <-cancel:
			return nil
		default:
		}

		var rescanBlocks []chainhash.Hash
		err := walletdb.View(w.db, func(dbtx walletdb.ReadTx) error {
			txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
			var err error
			rescanBlocks, err = w.TxStore.GetMainChainBlockHashes(txmgrNs,
				&rescanFrom, inclusive, blockHashStorage)
			return err
		})
		if err != nil {
			return err
		}
		if len(rescanBlocks) == 0 {
			return nil
		}

		scanningThrough := height + int32(len(rescanBlocks)) - 1
		log.Infof("Rescanning blocks %v-%v...", height,
			scanningThrough)
		rescanResults, err := chainClient.Rescan(rescanBlocks)
		if err != nil {
			return err
		}
		var rawBlockHeader wtxmgr.RawBlockHeader
		err = walletdb.Update(w.db, func(dbtx walletdb.ReadWriteTx) error {
			txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)
			for _, r := range rescanResults.DiscoveredData {
				blockHash, err := chainhash.NewHashFromStr(r.Hash)
				if err != nil {
					return err
				}
				blockMeta, err := w.TxStore.GetBlockMetaForHash(txmgrNs, blockHash)
				if err != nil {
					return err
				}
				serHeader, err := w.TxStore.GetSerializedBlockHeader(txmgrNs,
					blockHash)
				if err != nil {
					return err
				}
				err = copyHeaderSliceToArray(&rawBlockHeader, serHeader)
				if err != nil {
					return err
				}

				for _, hexTx := range r.Transactions {
					serTx, err := hex.DecodeString(hexTx)
					if err != nil {
						return err
					}
					err = w.processTransaction(dbtx, serTx, &rawBlockHeader,
						&blockMeta)
					if err != nil {
						return err
					}
				}
			}
			return nil
		})
		if err != nil {
			return err
		}
		if p != nil {
			p <- RescanProgress{ScannedThrough: scanningThrough}
		}
		rescanFrom = rescanBlocks[len(rescanBlocks)-1]
		height += int32(len(rescanBlocks))
		inclusive = false
	}
}