Skip to content

Commit 32e9d69

Browse files
author
jeffyanta
authored
Report the balance source for CalculateFromBlockchain (#48)
1 parent 3a5a705 commit 32e9d69

File tree

3 files changed

+53
-10
lines changed

3 files changed

+53
-10
lines changed

pkg/code/balance/calculator.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ import (
1717
timelock_token "github.com/code-payments/code-server/pkg/solana/timelock/v1"
1818
)
1919

20+
type Source uint8
21+
22+
const (
23+
UnknownSource Source = iota
24+
CacheSource
25+
BlockchainSource
26+
)
27+
2028
const (
2129
metricsPackageName = "balance"
2230
)
@@ -51,6 +59,9 @@ type State struct {
5159

5260
// CalculateFromCache is the default and recommended strategy for reliably estimating
5361
// a token account's balance using cached values.
62+
//
63+
// Note: Use this method when calculating balances for accounts that are managed by
64+
// Code (ie. Timelock account) and operate within the L2 system.
5465
func CalculateFromCache(ctx context.Context, data code_data.Provider, tokenAccount *common.Account) (uint64, error) {
5566
tracer := metrics.TraceMethodCall(ctx, metricsPackageName, "CalculateFromCache")
5667
tracer.AddAttribute("account", tokenAccount.PublicKey().ToBase58())
@@ -106,32 +117,37 @@ func CalculateFromCache(ctx context.Context, data code_data.Provider, tokenAccou
106117
}
107118

108119
// CalculateFromBlockchain is the default and recommended strategy for reliably
109-
// estimating a token account's balance from the blockchain.
120+
// estimating a token account's balance from the blockchain. This strategy is
121+
// resistant to various RPC failure nodes, and may return a cached value. The
122+
// source of the balance calculation is returned.
123+
//
124+
// Note: Use this method when calculating token account balances that are external
125+
// and not managed by Code and outside the L2 system.
110126
//
111127
// todo: add a batching variant
112-
func CalculateFromBlockchain(ctx context.Context, data code_data.Provider, tokenAccount *common.Account) (uint64, error) {
128+
func CalculateFromBlockchain(ctx context.Context, data code_data.Provider, tokenAccount *common.Account) (uint64, Source, error) {
113129
var cachedQuarks uint64
114130
var cachedSlot uint64
115131
checkpointRecord, err := data.GetBalanceCheckpoint(ctx, tokenAccount.PublicKey().ToBase58())
116132
if err == nil {
117133
cachedQuarks = checkpointRecord.Quarks
118134
cachedSlot = checkpointRecord.SlotCheckpoint
119135
} else if err != balance.ErrCheckpointNotFound {
120-
return 0, err
136+
return 0, UnknownSource, err
121137
}
122138

123139
// todo: we may need something that's more resistant to RPC nodes with stale account state
124140
quarks, slot, err := data.GetBlockchainBalance(ctx, tokenAccount.PublicKey().ToBase58())
125141
if err == solana.ErrNoBalance {
126-
return 0, nil
142+
return 0, BlockchainSource, nil
127143
} else if err != nil {
128144
// RPC node threw an error. Return the cached balance
129-
return cachedQuarks, nil
145+
return cachedQuarks, CacheSource, nil
130146
}
131147

132148
// RPC node is behind, use cached balance
133149
if cachedSlot > slot {
134-
return cachedQuarks, nil
150+
return cachedQuarks, CacheSource, nil
135151
}
136152

137153
// Observed a balance that's more recent. Best-effort update the checkpoint.
@@ -144,7 +160,7 @@ func CalculateFromBlockchain(ctx context.Context, data code_data.Provider, token
144160
data.SaveBalanceCheckpoint(ctx, newCheckpointRecord)
145161
}
146162

147-
return quarks, nil
163+
return quarks, BlockchainSource, nil
148164
}
149165

150166
// Calculate calculates a token account's balance using a starting point and a set
@@ -262,6 +278,9 @@ type BatchState struct {
262278
// or reliably estimating a set of token accounts' balance when common.AccountRecords are
263279
// available.
264280
//
281+
// Note: Use this method when calculating balances for accounts that are managed by
282+
// Code (ie. Timelock account) and operate within the L2 system.
283+
//
265284
// Note: This only supports post-privacy accounts. Use CalculateFromCache instead.
266285
func BatchCalculateFromCacheWithAccountRecords(ctx context.Context, data code_data.Provider, accountRecordsBatch ...*common.AccountRecords) (map[string]uint64, error) {
267286
tracer := metrics.TraceMethodCall(ctx, metricsPackageName, "BatchCalculateFromCacheWithAccountRecords")
@@ -289,6 +308,9 @@ func BatchCalculateFromCacheWithAccountRecords(ctx context.Context, data code_da
289308
// or reliably estimating a set of token accounts' balance when common.Account are
290309
// available.
291310
//
311+
// Note: Use this method when calculating balances for accounts that are managed by
312+
// Code (ie. Timelock account) and operate within the L2 system.
313+
//
292314
// Note: This only supports post-privacy accounts. Use CalculateFromCache instead.
293315
func BatchCalculateFromCacheWithTokenAccounts(ctx context.Context, data code_data.Provider, tokenAccounts ...*common.Account) (map[string]uint64, error) {
294316
tracer := metrics.TraceMethodCall(ctx, metricsPackageName, "BatchCalculateFromCacheWithTokenAccounts")
@@ -469,3 +491,15 @@ func GetPrivateBalance(ctx context.Context, data code_data.Provider, owner *comm
469491
}
470492
return total, nil
471493
}
494+
495+
func (s Source) String() string {
496+
switch s {
497+
case UnknownSource:
498+
return "unknown"
499+
case CacheSource:
500+
return "cache"
501+
case BlockchainSource:
502+
return "blockchain"
503+
}
504+
return "unknown"
505+
}

pkg/code/server/grpc/account/server.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,13 +371,22 @@ func (s *server) fetchBalances(ctx context.Context, recordsByType map[commonpb.A
371371
return nil, err
372372
}
373373

374-
quarks, err := balance.CalculateFromBlockchain(ctx, s.data, tokenAccount)
374+
quarks, balanceSource, err := balance.CalculateFromBlockchain(ctx, s.data, tokenAccount)
375375
if err != nil {
376376
return nil, err
377377
}
378+
var protoBalanceSource accountpb.TokenAccountInfo_BalanceSource
379+
switch balanceSource {
380+
case balance.BlockchainSource:
381+
protoBalanceSource = accountpb.TokenAccountInfo_BALANCE_SOURCE_BLOCKCHAIN
382+
case balance.CacheSource:
383+
protoBalanceSource = accountpb.TokenAccountInfo_BALANCE_SOURCE_CACHE
384+
default:
385+
protoBalanceSource = accountpb.TokenAccountInfo_BALANCE_SOURCE_UNKNOWN
386+
}
378387
balanceMetadataByTokenAccount[tokenAccount.PublicKey().ToBase58()] = &balanceMetadata{
379388
value: quarks,
380-
source: accountpb.TokenAccountInfo_BALANCE_SOURCE_BLOCKCHAIN,
389+
source: protoBalanceSource,
381390
}
382391
}
383392
}

pkg/code/server/grpc/transaction/v2/swap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func (s *transactionServer) Swap(streamer transactionpb.Transaction_SwapServer)
139139
}
140140
log = log.WithField("swap_destination", swapDestination.PublicKey().ToBase58())
141141

142-
swapSourceBalance, err := balance.CalculateFromBlockchain(ctx, s.data, swapSource)
142+
swapSourceBalance, _, err := balance.CalculateFromBlockchain(ctx, s.data, swapSource)
143143
if err != nil {
144144
log.WithError(err).Warn("failure getting swap source account balance")
145145
return handleSwapError(streamer, err)

0 commit comments

Comments
 (0)