diff --git a/lib/block_view_nft.go b/lib/block_view_nft.go index 0892c2a26..771653759 100644 --- a/lib/block_view_nft.go +++ b/lib/block_view_nft.go @@ -94,15 +94,22 @@ func (bav *UtxoView) GetNFTEntriesForPostHash(nftPostHash *BlockHash) []*NFTEntr return nftEntries } -func (bav *UtxoView) GetNFTEntriesForPKID(ownerPKID *PKID) []*NFTEntry { +func (bav *UtxoView) GetNFTEntriesForPKID( + ownerPKID *PKID, + limit int, + lastKeyBytes []byte, + isForSale *bool, + isPending *bool, +) ([]*NFTEntry, []byte) { var dbNFTEntries []*NFTEntry + var lastSeenKey []byte if bav.Postgres != nil { nfts := bav.Postgres.GetNFTsForPKID(ownerPKID) for _, nft := range nfts { dbNFTEntries = append(dbNFTEntries, nft.NewNFTEntry()) } } else { - dbNFTEntries = DBGetNFTEntriesForPKID(bav.Handle, ownerPKID) + dbNFTEntries, lastSeenKey = DBGetNFTEntriesForPKID(bav.Handle, ownerPKID, limit, lastKeyBytes, isForSale, isPending) } // Make sure all of the DB entries are loaded in the view. @@ -118,11 +125,11 @@ func (bav *UtxoView) GetNFTEntriesForPKID(ownerPKID *PKID) []*NFTEntry { // Loop over the view and build the final set of NFTEntries to return. nftEntries := []*NFTEntry{} for _, nftEntry := range bav.NFTKeyToNFTEntry { - if !nftEntry.isDeleted && reflect.DeepEqual(nftEntry.OwnerPKID, ownerPKID) { + if !nftEntry.isDeleted && nftEntry.OwnerPKID.Eq(ownerPKID) { nftEntries = append(nftEntries, nftEntry) } } - return nftEntries + return nftEntries, lastSeenKey } func (bav *UtxoView) GetNFTBidEntriesForPKID(bidderPKID *PKID) (_nftBidEntries []*NFTBidEntry) { diff --git a/lib/db_utils.go b/lib/db_utils.go index d1d8bfac7..acfa83e82 100644 --- a/lib/db_utils.go +++ b/lib/db_utils.go @@ -8754,18 +8754,75 @@ func DBGetNFTEntryByNFTOwnershipDetails(db *badger.DB, snap *Snapshot, ownerPKID } // DBGetNFTEntriesForPKID gets NFT Entries *from the DB*. Does not include mempool txns. -func DBGetNFTEntriesForPKID(handle *badger.DB, ownerPKID *PKID) (_nftEntries []*NFTEntry) { +func DBGetNFTEntriesForPKID( + handle *badger.DB, + ownerPKID *PKID, + limit int, + lastKeyBytes []byte, + isForSale *bool, + isPending *bool, +) ( + _nftEntries []*NFTEntry, + _lastKeyBytes []byte, +) { var nftEntries []*NFTEntry prefix := append([]byte{}, Prefixes.PrefixPKIDIsForSaleBidAmountNanosPostHashSerialNumberToNFTEntry...) keyPrefix := append(prefix, ownerPKID[:]...) - _, entryByteStringsFound := _enumerateKeysForPrefix(handle, keyPrefix, false, false) - for _, byteString := range entryByteStringsFound { - currentEntry := &NFTEntry{} - rr := bytes.NewReader(byteString) - DecodeFromBytes(currentEntry, rr) - nftEntries = append(nftEntries, currentEntry) + if isForSale != nil { + keyPrefix = append(keyPrefix, BoolToByte(*isForSale)) } - return nftEntries + lastSeenKey := keyPrefix + haveSeenLastSeenKey := true + if len(lastKeyBytes) > 0 { + lastSeenKey = lastKeyBytes + if limit > 0 { + limit += 1 + } + } + + opts := badger.DefaultIteratorOptions + opts.Prefix = keyPrefix + var lastSeenKeyBytes []byte + dbErr := handle.View(func(txn *badger.Txn) error { + nodeIterator := txn.NewIterator(opts) + defer nodeIterator.Close() + for nodeIterator.Seek(lastSeenKey); nodeIterator.ValidForPrefix(keyPrefix); nodeIterator.Next() { + // Break if at or beyond limit. + if limit > 0 && len(nftEntries) >= limit { + break + } + key := nodeIterator.Item().Key() + // Skip if key is before the last seen key. The caller + // needs to filter out the lastSeenKey in the view as + // we return any key >= the lastSeenKey. + if !haveSeenLastSeenKey { + if !bytes.Equal(key, lastSeenKey) { + continue + } + haveSeenLastSeenKey = true + } + + val, err := nodeIterator.Item().ValueCopy(nil) + if err != nil { + return err + } + currentEntry := &NFTEntry{} + rr := bytes.NewReader(val) + DecodeFromBytes(currentEntry, rr) + // If isPending is specified, filter by it. + if isPending != nil && currentEntry.IsPending != *isPending { + continue + } + nftEntries = append(nftEntries, currentEntry) + lastSeenKeyBytes = append([]byte{}, key...) + } + return nil + }) + if dbErr != nil { + glog.Errorf("DBGetNFTEntriesForPKID: Problem reading NFTEntry, error: (%v)", dbErr) + } + + return nftEntries, lastSeenKeyBytes } // =======================================================================================