Skip to content

Commit 4753b31

Browse files
committed
add address utils endpoints
1 parent 494739d commit 4753b31

File tree

4 files changed

+115
-0
lines changed

4 files changed

+115
-0
lines changed

api/server.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type (
5959
V2Transactions(ids []types.TransactionID) ([]explorer.V2Transaction, error)
6060
V2TransactionChainIndices(id types.TransactionID, offset, limit uint64) ([]types.ChainIndex, error)
6161
Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error)
62+
TopSiacoinAddresses(limit, offset int) (result []explorer.TopSiacoin, err error)
63+
TopSiafundAddresses(limit, offset int) (result []explorer.TopSiafund, err error)
6264
SiacoinElements(ids []types.SiacoinOutputID) (result []explorer.SiacoinOutput, err error)
6365
SiafundElements(ids []types.SiafundOutputID) (result []explorer.SiafundOutput, err error)
6466
UnspentSiacoinOutputs(address types.Address, offset, limit uint64) ([]explorer.SiacoinOutput, error)
@@ -838,6 +840,39 @@ func (s *server) healthHandler(jc jape.Context) {
838840
jc.Encode(nil)
839841
}
840842

843+
func (s *server) topAddressesSiacoinsHandler(jc jape.Context) {
844+
limit := defaultLimit
845+
offset := uint64(0)
846+
if jc.DecodeForm("limit", &limit) != nil || jc.DecodeForm("offset", &offset) != nil {
847+
return
848+
}
849+
if limit > maxLimit {
850+
limit = maxLimit
851+
}
852+
853+
topAddresses, err := s.e.TopSiacoinAddresses(int(limit), int(offset))
854+
if jc.Check("failed to get top siacoin addresses", err) != nil {
855+
return
856+
}
857+
jc.Encode(topAddresses)
858+
}
859+
func (s *server) topAddressesSiafundsHandler(jc jape.Context) {
860+
limit := defaultLimit
861+
offset := uint64(0)
862+
if jc.DecodeForm("limit", &limit) != nil || jc.DecodeForm("offset", &offset) != nil {
863+
return
864+
}
865+
if limit > maxLimit {
866+
limit = maxLimit
867+
}
868+
869+
topAddresses, err := s.e.TopSiafundAddresses(int(limit), int(offset))
870+
if jc.Check("failed to get top siacoin addresses", err) != nil {
871+
return
872+
}
873+
jc.Encode(topAddresses)
874+
}
875+
841876
// NewServer returns an HTTP handler that serves the explored API.
842877
func NewServer(e Explorer, cm ChainManager, s Syncer, ex exchangerates.Source, apiPassword string) http.Handler {
843878
srv := server{
@@ -883,6 +918,9 @@ func NewServer(e Explorer, cm ChainManager, s Syncer, ex exchangerates.Source, a
883918
"GET /addresses/:address/events/unconfirmed": srv.addressessAddressEventsUnconfirmedHandler,
884919
"GET /addresses/:address/balance": srv.addressessAddressBalanceHandler,
885920

921+
"GET /top/addresses/siacoins": srv.topAddressesSiacoinsHandler,
922+
"GET /top/addresses/siafunds": srv.topAddressesSiafundsHandler,
923+
886924
"GET /events/:id": srv.eventsIDHandler,
887925

888926
"GET /outputs/siacoin/:id": srv.outputsSiacoinHandler,

explorer/explorer.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ type Store interface {
113113

114114
QueryHosts(params HostQuery, sortBy HostSortColumn, dir HostSortDir, offset, limit uint64) ([]Host, error)
115115
HostsForScanning(minLastAnnouncement time.Time, limit uint64) ([]UnscannedHost, error)
116+
117+
TopSiacoinAddresses(limit, offset int) ([]TopSiacoin, error)
118+
TopSiafundAddresses(limit, offset int) ([]TopSiafund, error)
116119
}
117120

118121
// Explorer implements a Sia explorer.
@@ -162,6 +165,18 @@ func (e *Explorer) syncStore(index types.ChainIndex, batchSize int) error {
162165
return nil
163166
}
164167

168+
// TopSiacoinAddresses returns a paginated list of Siacoin addresses ordered
169+
// by balance.
170+
func (e *Explorer) TopSiacoinAddresses(limit, offset int) ([]TopSiacoin, error) {
171+
return e.s.TopSiacoinAddresses(limit, offset)
172+
}
173+
174+
// TopSiafundAddresses returns a paginated list of Siafund addresses ordered
175+
// by balance.
176+
func (e *Explorer) TopSiafundAddresses(limit, offset int) ([]TopSiafund, error) {
177+
return e.s.TopSiafundAddresses(limit, offset)
178+
}
179+
165180
// NewExplorer returns a Sia explorer.
166181
func NewExplorer(cm ChainManager, store Store, indexCfg config.Index, scanCfg config.Scanner, log *zap.Logger) (*Explorer, error) {
167182
e := &Explorer{

explorer/types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,3 +619,15 @@ type DifficultyMetrics struct {
619619
Difficulties []consensus.Work `json:"difficulties"`
620620
BlockTimes []time.Duration `json:"blockTimes"`
621621
}
622+
623+
// TopSiacoin pairs an address with its Siacoin balance
624+
type TopSiacoin struct {
625+
Address types.Address `json:"address"`
626+
Amount types.Currency `json:"amount"`
627+
}
628+
629+
// TopSiafund pairs an address with its Siafund balance
630+
type TopSiafund struct {
631+
Address types.Address `json:"address"`
632+
Amount uint64 `json:"amount"`
633+
}

persist/sqlite/addresses.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,53 @@ func (s *Store) Balance(address types.Address) (sc types.Currency, immatureSC ty
271271
})
272272
return
273273
}
274+
275+
// TopSiacoinAddresses returns a paginated list of Siacoin addresses ordered
276+
// by balance.
277+
func (s *Store) TopSiacoinAddresses(limit, offset int) (result []explorer.TopSiacoin, err error) {
278+
err = s.transaction(func(tx *txn) error {
279+
rows, err := tx.Query(`SELECT address, siacoin_balance FROM address_balance ORDER BY siacoin_balance DESC LIMIT $1 OFFSET $2`, limit, offset)
280+
if err != nil {
281+
return fmt.Errorf("failed to query top siacoin addresses: %w", err)
282+
}
283+
defer rows.Close()
284+
285+
for rows.Next() {
286+
var address explorer.TopSiacoin
287+
if err := rows.Scan(decode(&address.Address), decode(&address.Amount)); err != nil {
288+
return fmt.Errorf("failed to scan top siacoin address: %w", err)
289+
}
290+
result = append(result, address)
291+
}
292+
if err := rows.Err(); err != nil {
293+
return fmt.Errorf("failed to retrieve top siacoin address rows: %w", err)
294+
}
295+
return nil
296+
})
297+
return
298+
}
299+
300+
// TopSiafundAddresses returns a paginated list of Siafund addresses ordered
301+
// by balance.
302+
func (s *Store) TopSiafundAddresses(limit, offset int) (result []explorer.TopSiafund, err error) {
303+
err = s.transaction(func(tx *txn) error {
304+
rows, err := tx.Query(`SELECT address, siafund_balance FROM address_balance ORDER BY siafund_balance DESC LIMIT $1 OFFSET $2`, limit, offset)
305+
if err != nil {
306+
return fmt.Errorf("failed to query top siafund addresses: %w", err)
307+
}
308+
defer rows.Close()
309+
310+
for rows.Next() {
311+
var address explorer.TopSiafund
312+
if err := rows.Scan(decode(&address.Address), decode(&address.Amount)); err != nil {
313+
return fmt.Errorf("failed to scan top siafund address: %w", err)
314+
}
315+
result = append(result, address)
316+
}
317+
if err := rows.Err(); err != nil {
318+
return fmt.Errorf("failed to retrieve top siafund address rows: %w", err)
319+
}
320+
return nil
321+
})
322+
return
323+
}

0 commit comments

Comments
 (0)