// addrToKey converts known address types to an addrindex key. An error is // returned for unsupported types. func addrToKey(addr dcrutil.Address, params *chaincfg.Params) ([addrKeySize]byte, error) { switch addr := addr.(type) { case *dcrutil.AddressPubKeyHash: switch addr.DSA(params) { case chainec.ECTypeSecp256k1: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHash copy(result[1:], addr.Hash160()[:]) return result, nil case chainec.ECTypeEdwards: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHashEdwards copy(result[1:], addr.Hash160()[:]) return result, nil case chainec.ECTypeSecSchnorr: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHashSchnorr copy(result[1:], addr.Hash160()[:]) return result, nil } case *dcrutil.AddressScriptHash: var result [addrKeySize]byte result[0] = addrKeyTypeScriptHash copy(result[1:], addr.Hash160()[:]) return result, nil case *dcrutil.AddressSecpPubKey: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHash copy(result[1:], addr.AddressPubKeyHash().Hash160()[:]) return result, nil case *dcrutil.AddressEdwardsPubKey: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHashEdwards copy(result[1:], addr.AddressPubKeyHash().Hash160()[:]) return result, nil case *dcrutil.AddressSecSchnorrPubKey: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHashSchnorr copy(result[1:], addr.AddressPubKeyHash().Hash160()[:]) return result, nil } return [addrKeySize]byte{}, errUnsupportedAddressType }
// initialize initializes an address pool for usage by loading the latest // unused address from the blockchain itself. func (a *addressPool) initialize(branch uint32, w *Wallet) error { a.addresses = make([]string, 0) a.mutex = new(sync.Mutex) a.wallet = w a.branch = branch var err error // Retrieve the last addresses from wallet closing and storing. lastExtAddr, lastIntAddr, err := w.Manager.LastUsedAddresses() if err != nil { return err } var lastSavedAddr dcrutil.Address // Get the last managed address for the account and branch. var lastIndex uint32 if branch == waddrmgr.InternalBranch { _, lastIndex, err = a.wallet.Manager.LastInternalAddress(waddrmgr.DefaultAccountNum) if err != nil { return err } lastSavedAddr = lastIntAddr } if branch == waddrmgr.ExternalBranch { _, lastIndex, err = a.wallet.Manager.LastExternalAddress(waddrmgr.DefaultAccountNum) if err != nil { return err } lastSavedAddr = lastExtAddr } // Get the actual last index as recorded in the blockchain. traversed := 0 actualLastIndex := lastIndex for actualLastIndex != 0 && traversed != addressPoolBuffer { addr, err := a.wallet.Manager.GetAddress(actualLastIndex, waddrmgr.DefaultAccountNum, branch) if err != nil { return err } // Start with the address on tip if address reuse is disabled. if !w.addressReuse { // If address reuse is disabled, we compare to the last // stored address. if lastSavedAddr != nil { lsaH160 := lastSavedAddr.Hash160() thisH160 := addr.Hash160() if *lsaH160 == *thisH160 { // We actually append this address because the // LastUsedAddresses function in Manager actually // stores the next to-be-used address rather than // the last used address. See Close below. a.addresses = append([]string{addr.EncodeAddress()}, a.addresses...) break } } } else { // Otherwise, search the blockchain for the last actually used // address. exists, err := a.wallet.existsAddressOnChain(addr) if err != nil { return err } if exists { break } } // Insert this unused address into the cache. a.addresses = append([]string{addr.EncodeAddress()}, a.addresses...) actualLastIndex-- traversed++ } a.cursor = 0 return nil }
// FetchTxsForAddr looks up and returns all transactions which either // spend from a previously created output of the passed address, or // create a new output locked to the passed address. The, `limit` parameter // should be the max number of transactions to be returned. Additionally, if the // caller wishes to seek forward in the results some amount, the 'seek' // represents how many results to skip. func (db *LevelDb) FetchTxsForAddr(addr dcrutil.Address, skip int, limit int) ([]*database.TxListReply, error) { db.dbLock.Lock() defer db.dbLock.Unlock() // Enforce constraints for skip and limit. if skip < 0 { return nil, errors.New("offset for skip must be positive") } if limit < 1 { return nil, errors.New("value for limit must be positive") } // Parse address type, bailing on an unknown type. var addrKey []byte switch addr := addr.(type) { case *dcrutil.AddressPubKeyHash: hash160 := addr.Hash160() addrKey = hash160[:] case *dcrutil.AddressScriptHash: hash160 := addr.Hash160() addrKey = hash160[:] case *dcrutil.AddressSecpPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] case *dcrutil.AddressEdwardsPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] case *dcrutil.AddressSecSchnorrPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] default: return nil, database.ErrUnsupportedAddressType } // Create the prefix for our search. addrPrefix := make([]byte, 23, 23) copy(addrPrefix[0:3], addrIndexKeyPrefix) copy(addrPrefix[3:23], addrKey) iter := db.lDb.NewIterator(bytesPrefix(addrPrefix), nil) for skip != 0 { iter.Next() skip-- } // Iterate through all address indexes that match the targeted prefix. var replies []*database.TxListReply var rawIndex [database.AddrIndexKeySize]byte for iter.Next() { if limit == 0 { break } copy(rawIndex[:], iter.Key()) addrIndex := unpackTxIndex(rawIndex) tx, blkSha, blkHeight, _, err := db.fetchTxDataByLoc( int64(addrIndex.Height), int(addrIndex.TxOffset), int(addrIndex.TxLen), []byte{}) if err != nil { log.Warnf("tx listed in addrindex record not found, height: %v"+ " offset: %v, len: %v", addrIndex.Height, addrIndex.TxOffset, addrIndex.TxLen) limit-- continue } var txSha chainhash.Hash if tx != nil { txSha = tx.TxSha() } txReply := &database.TxListReply{Sha: &txSha, Tx: tx, BlkSha: blkSha, Height: blkHeight, TxSpent: []bool{}, Err: err} replies = append(replies, txReply) limit-- } iter.Release() if err := iter.Error(); err != nil { return nil, err } return replies, nil }
// FetchTxsForAddr looks up and returns all transactions which either // spend from a previously created output of the passed address, or // create a new output locked to the passed address. The, `limit` parameter // should be the max number of transactions to be returned. Additionally, if the // caller wishes to seek forward in the results some amount, the 'seek' // represents how many results to skip. func (db *LevelDb) FetchTxsForAddr(addr dcrutil.Address, skip int, limit int, reverse bool) ([]*database.TxListReply, int, error) { db.dbLock.Lock() defer db.dbLock.Unlock() // Enforce constraints for skip and limit. if skip < 0 { return nil, 0, errors.New("offset for skip must be positive") } if limit < 0 { return nil, 0, errors.New("value for limit must be positive") } // Parse address type, bailing on an unknown type. var addrKey []byte switch addr := addr.(type) { case *dcrutil.AddressPubKeyHash: hash160 := addr.Hash160() addrKey = hash160[:] case *dcrutil.AddressScriptHash: hash160 := addr.Hash160() addrKey = hash160[:] case *dcrutil.AddressSecpPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] case *dcrutil.AddressEdwardsPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] case *dcrutil.AddressSecSchnorrPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] default: return nil, 0, database.ErrUnsupportedAddressType } // Create the prefix for our search. addrPrefix := make([]byte, 23, 23) copy(addrPrefix[0:3], addrIndexKeyPrefix) copy(addrPrefix[3:23], addrKey) iter := db.lDb.NewIterator(bytesPrefix(addrPrefix), nil) skipped := 0 if reverse { // Go to the last element if reverse iterating. iter.Last() // Skip "one past" the last element so the loops below don't // miss the last element due to Prev() being called first. // We can safely ignore iterator exhaustion since the loops // below will see there's no keys anyway. iter.Next() } for skip != 0 && advanceIterator(iter, reverse) { skip-- skipped++ } // Iterate through all address indexes that match the targeted prefix. var replies []*database.TxListReply var rawIndex [database.AddrIndexKeySize]byte for advanceIterator(iter, reverse) && limit != 0 { copy(rawIndex[:], iter.Key()) addrIndex := unpackTxIndex(rawIndex) tx, blkSha, blkHeight, _, err := db.fetchTxDataByLoc( int64(addrIndex.Height), int(addrIndex.TxOffset), int(addrIndex.TxLen), []byte{}) if err != nil { log.Warnf("tx listed in addrindex record not found, height: %v"+ " offset: %v, len: %v", addrIndex.Height, addrIndex.TxOffset, addrIndex.TxLen) limit-- continue } var txSha chainhash.Hash if tx != nil { txSha = tx.TxSha() } txReply := &database.TxListReply{Sha: &txSha, Tx: tx, BlkSha: blkSha, Height: blkHeight, TxSpent: []bool{}, Err: err} replies = append(replies, txReply) limit-- } iter.Release() if err := iter.Error(); err != nil { return nil, 0, err } return replies, skipped, nil }