OSDN Git Service

fix api
authorwz <mars@bytom.io>
Tue, 26 Nov 2019 09:04:37 +0000 (17:04 +0800)
committerwz <mars@bytom.io>
Tue, 26 Nov 2019 09:04:37 +0000 (17:04 +0800)
account/accounts.go
api/query.go
database/leveldb/db.go
database/leveldb/go_level_db.go
database/leveldb/mem_db.go
wallet/indexer.go
wallet/unconfirmed.go

index ab9bd9e..7170e89 100644 (file)
@@ -19,11 +19,11 @@ import (
        "github.com/bytom/crypto"
        "github.com/bytom/crypto/ed25519/chainkd"
        "github.com/bytom/crypto/sha3pool"
+       dbm "github.com/bytom/database/leveldb"
        "github.com/bytom/errors"
        "github.com/bytom/protocol"
        "github.com/bytom/protocol/bc"
        "github.com/bytom/protocol/vm/vmutil"
-       dbm "github.com/bytom/database/leveldb"
 )
 
 const (
@@ -57,6 +57,7 @@ var (
        ErrContractIndex   = errors.New("Exceeded maximum addresses per account")
        ErrAccountIndex    = errors.New("Exceeded maximum accounts per xpub")
        ErrFindTransaction = errors.New("No transaction")
+       ErrAccountIDEmpty  = errors.New("account_id is empty")
 )
 
 // ContractKey account control promgram store prefix
index 5265475..00328d2 100644 (file)
@@ -129,48 +129,37 @@ func (a *API) getTransaction(ctx context.Context, txInfo struct {
 
 // POST /list-transactions
 func (a *API) listTransactions(ctx context.Context, filter struct {
-       ID          string `json:"id"`
-       AccountID   string `json:"account_id"`
-       Detail      bool   `json:"detail"`
-       Unconfirmed bool   `json:"unconfirmed"`
-       From        uint   `json:"from"`
-       Count       uint   `json:"count"`
+       AccountID    string `json:"account_id"`
+       AccountAlias string `json:"account_alias"`
+       StartTxID    string `json:"start_tx_id"`
+       Detail       bool   `json:"detail"`
+       Unconfirmed  bool   `json:"unconfirmed"`
+       Count        uint   `json:"count"`
 }) Response {
-       transactions := []*query.AnnotatedTx{}
-       var err error
-       var transaction *query.AnnotatedTx
-
-       if filter.ID != "" {
-               transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
-               if err != nil && filter.Unconfirmed {
-                       transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
-                       if err != nil {
-                               return NewErrorResponse(err)
-                       }
-               }
-               transactions = []*query.AnnotatedTx{transaction}
-       } else {
-               transactions, err = a.wallet.GetTransactions(filter.AccountID)
+       accountID := filter.AccountID
+       if filter.AccountAlias != "" {
+               acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
                if err != nil {
                        return NewErrorResponse(err)
                }
+               accountID = acc.ID
+       }
 
-               if filter.Unconfirmed {
-                       unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
-                       if err != nil {
-                               return NewErrorResponse(err)
-                       }
-                       transactions = append(unconfirmedTxs, transactions...)
-               }
+       if accountID == "" {
+               return NewErrorResponse(account.ErrAccountIDEmpty)
+       }
+
+       transactions, err := a.wallet.GetTransactions(accountID, filter.StartTxID, filter.Count, filter.Unconfirmed)
+       if err != nil {
+               return NewErrorResponse(err)
        }
 
        if filter.Detail == false {
                txSummary := a.wallet.GetTransactionsSummary(transactions)
-               start, end := getPageRange(len(txSummary), filter.From, filter.Count)
-               return NewSuccessResponse(txSummary[start:end])
+               return NewSuccessResponse(txSummary)
        }
-       start, end := getPageRange(len(transactions), filter.From, filter.Count)
-       return NewSuccessResponse(transactions[start:end])
+
+       return NewSuccessResponse(transactions)
 }
 
 // POST /get-unconfirmed-transaction
index 38cab5b..aca9586 100644 (file)
@@ -12,6 +12,7 @@ type DB interface {
        NewBatch() Batch
        Iterator() Iterator
        IteratorPrefix([]byte) Iterator
+       IteratorPrefixWithStart(Prefix, start []byte, isReverse bool) Iterator
 
        // For debugging
        Print()
index e9e8d3d..174540e 100644 (file)
@@ -118,7 +118,28 @@ func (db *GoLevelDB) Stats() map[string]string {
 }
 
 type goLevelDBIterator struct {
-       source iterator.Iterator
+       source    iterator.Iterator
+       start     []byte
+       isReverse bool
+}
+
+func newGoLevelDBIterator(source iterator.Iterator, start []byte, isReverse bool) *goLevelDBIterator {
+       if start != nil {
+               valid := source.Seek(start)
+               if !valid && isReverse {
+                       source.Last()
+                       source.Next()
+               }
+       } else if isReverse {
+               source.Last()
+               source.Next()
+       }
+
+       return &goLevelDBIterator{
+               source:    source,
+               start:     start,
+               isReverse: isReverse,
+       }
 }
 
 // Key returns a copy of the current key.
@@ -148,19 +169,34 @@ func (it *goLevelDBIterator) Error() error {
 }
 
 func (it *goLevelDBIterator) Next() bool {
+       it.assertNoError()
+       if it.isReverse {
+               return it.source.Prev()
+       }
        return it.source.Next()
 }
 
+func (it *goLevelDBIterator) assertNoError() {
+       if err := it.source.Error(); err != nil {
+               panic(err)
+       }
+}
+
 func (it *goLevelDBIterator) Release() {
        it.source.Release()
 }
 
 func (db *GoLevelDB) Iterator() Iterator {
-       return &goLevelDBIterator{db.db.NewIterator(nil, nil)}
+       return &goLevelDBIterator{source: db.db.NewIterator(nil, nil)}
 }
 
 func (db *GoLevelDB) IteratorPrefix(prefix []byte) Iterator {
-       return &goLevelDBIterator{db.db.NewIterator(util.BytesPrefix(prefix), nil)}
+       return &goLevelDBIterator{source: db.db.NewIterator(util.BytesPrefix(prefix), nil)}
+}
+
+func (db *GoLevelDB) IteratorPrefixWithStart(Prefix, start []byte, isReverse bool) Iterator {
+       itr := db.db.NewIterator(util.BytesPrefix(Prefix), nil)
+       return newGoLevelDBIterator(itr, start, isReverse)
 }
 
 func (db *GoLevelDB) NewBatch() Batch {
index 62f40fc..e70aa9d 100644 (file)
@@ -1,6 +1,7 @@
 package db
 
 import (
+       "bytes"
        "fmt"
        "sort"
        "strings"
@@ -78,13 +79,29 @@ func (db *MemDB) Stats() map[string]string {
 type memDBIterator struct {
        last int
        keys []string
-       db   *MemDB
+       db   DB
+
+       start []byte
 }
 
 func newMemDBIterator() *memDBIterator {
        return &memDBIterator{}
 }
 
+// Keys is expected to be in reverse order for reverse iterators.
+func newMemDBIteratorWithArgs(db DB, keys []string, start []byte) *memDBIterator {
+       itr := &memDBIterator{
+               db:    db,
+               keys:  keys,
+               start: start,
+               last:  -1,
+       }
+       if start != nil {
+               itr.Seek(start)
+       }
+       return itr
+}
+
 func (it *memDBIterator) Next() bool {
        if it.last >= len(it.keys)-1 {
                return false
@@ -143,10 +160,38 @@ func (db *MemDB) IteratorPrefix(prefix []byte) Iterator {
        return it
 }
 
+func (db *MemDB) IteratorPrefixWithStart(Prefix, start []byte, isReverse bool) Iterator {
+       db.mtx.Lock()
+       defer db.mtx.Unlock()
+
+       keys := db.getSortedKeys(start, isReverse)
+       return newMemDBIteratorWithArgs(db, keys, start)
+}
+
 func (db *MemDB) NewBatch() Batch {
        return &memDBBatch{db, nil}
 }
 
+func (db *MemDB) getSortedKeys(start []byte, reverse bool) []string {
+       keys := []string{}
+       for key := range db.db {
+               if bytes.Compare([]byte(key), start) < 0 {
+                       continue
+               }
+               keys = append(keys, key)
+       }
+       sort.Strings(keys)
+       if reverse {
+               nkeys := len(keys)
+               for i := 0; i < nkeys/2; i++ {
+                       temp := keys[i]
+                       keys[i] = keys[nkeys-i-1]
+                       keys[nkeys-i-1] = temp
+               }
+       }
+       return keys
+}
+
 //--------------------------------------------------------------------------------
 
 type memDBBatch struct {
index 04d9643..9cf3392 100644 (file)
@@ -26,7 +26,7 @@ const (
        GlobalTxIndexPrefix = "GTID:"
 )
 
-var errAccntTxIDNotFound = errors.New("account TXID not found")
+var ErrAccntTxIDNotFound = errors.New("account TXID not found")
 
 func formatKey(blockHeight uint64, position uint32) string {
        return fmt.Sprintf("%016x%08x", blockHeight, position)
@@ -158,7 +158,7 @@ func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) {
        annotatedTx := &query.AnnotatedTx{}
        formatKey := w.DB.Get(calcTxIndexKey(txID))
        if formatKey == nil {
-               return nil, errAccntTxIDNotFound
+               return nil, ErrAccntTxIDNotFound
        }
 
        txInfo := w.DB.Get(calcAnnotatedKey(string(formatKey)))
@@ -174,7 +174,7 @@ func (w *Wallet) getGlobalTxByIndex(index string) (*query.AnnotatedTx, error) {
        annotatedTx := &query.AnnotatedTx{}
        formatKey := w.DB.Get(calcGlobalTxIndexKey(index))
        if formatKey == nil {
-               return nil, errAccntTxIDNotFound
+               return nil, ErrAccntTxIDNotFound
        }
 
        txInfo := w.DB.Get(calcAnnotatedKey(string(formatKey)))
@@ -238,24 +238,50 @@ func findTransactionsByAccount(annotatedTx *query.AnnotatedTx, accountID string)
        return false
 }
 
-// GetTransactions get all walletDB transactions, and filter transactions by accountID optional
-func (w *Wallet) GetTransactions(accountID string) ([]*query.AnnotatedTx, error) {
+// GetTransactions get all walletDB transactions or unconfirmed transactions, and filter transactions by accountID and StartTxID optional
+func (w *Wallet) GetTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
        annotatedTxs := []*query.AnnotatedTx{}
+       var startKey []byte
+       preFix := TxPrefix
 
-       txIter := w.DB.IteratorPrefix([]byte(TxPrefix))
-       defer txIter.Release()
-       for txIter.Next() {
+       if StartTxID != "" {
+               if unconfirmed {
+                       startKey = calcUnconfirmedTxKey(StartTxID)
+               } else {
+                       formatKey := w.DB.Get(calcTxIndexKey(StartTxID))
+                       if formatKey == nil {
+                               return nil, ErrAccntTxIDNotFound
+                       }
+                       startKey = calcAnnotatedKey(string(formatKey))
+               }
+       }
+
+       if unconfirmed {
+               preFix = UnconfirmedTxPrefix
+       }
+
+       itr := w.DB.IteratorPrefixWithStart([]byte(preFix), startKey, true)
+       defer itr.Release()
+
+       for txNum := count; itr.Next() && txNum > 0; {
                annotatedTx := &query.AnnotatedTx{}
-               if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
+               if err := json.Unmarshal(itr.Value(), &annotatedTx); err != nil {
                        return nil, err
                }
 
                if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
                        annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
                        annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
+                       txNum--
                }
        }
 
+       if unconfirmed {
+               sort.Sort(SortByTimestamp(annotatedTxs))
+       } else {
+               sort.Sort(SortByHeight(annotatedTxs))
+       }
+
        return annotatedTxs, nil
 }
 
index b616518..5b04b1b 100644 (file)
@@ -33,6 +33,13 @@ func (a SortByTimestamp) Len() int           { return len(a) }
 func (a SortByTimestamp) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 func (a SortByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestamp }
 
+// SortByHeight implements sort.Interface for AnnotatedTx slices
+type SortByHeight []*query.AnnotatedTx
+
+func (a SortByHeight) Len() int           { return len(a) }
+func (a SortByHeight) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a SortByHeight) Less(i, j int) bool { return a[i].BlockHeight > a[j].BlockHeight }
+
 // AddUnconfirmedTx handle wallet status update when tx add into txpool
 func (w *Wallet) AddUnconfirmedTx(txD *protocol.TxDesc) {
        if err := w.saveUnconfirmedTx(txD.Tx); err != nil {