github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/stellar/bpc.go (about) 1 package stellar 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 "github.com/keybase/client/go/libkb" 9 "github.com/keybase/client/go/protocol/stellar1" 10 "github.com/keybase/client/go/stellar/remote" 11 "github.com/keybase/client/go/stellar/stellarcommon" 12 ) 13 14 // BuildPaymentCache has helpers for getting information quickly when building a payment. 15 // Methods should err on the side of performance rather at the cost of serialization. 16 // CORE-8119: But they don't yet. 17 type BuildPaymentCache interface { 18 PrimaryAccount(libkb.MetaContext) (stellar1.AccountID, error) 19 // AccountSeqno should be cached _but_ it should also be busted asap. 20 // Because it is used to prevent users from sending payments twice in a row. 21 AccountSeqno(libkb.MetaContext, stellar1.AccountID) (string, error) 22 IsAccountFunded(libkb.MetaContext, stellar1.AccountID, stellar1.BuildPaymentID) (bool, error) 23 LookupRecipient(libkb.MetaContext, stellarcommon.RecipientInput) (stellarcommon.Recipient, error) 24 GetOutsideExchangeRate(libkb.MetaContext, stellar1.OutsideCurrencyCode) (stellar1.OutsideExchangeRate, error) 25 AvailableXLMToSend(libkb.MetaContext, stellar1.AccountID) (string, error) 26 GetOutsideCurrencyPreference(libkb.MetaContext, stellar1.AccountID, stellar1.BuildPaymentID) (stellar1.OutsideCurrencyCode, error) 27 ShouldOfferAdvancedSend(mctx libkb.MetaContext, from, to stellar1.AccountID) (stellar1.AdvancedBanner, error) 28 InformDefaultCurrencyChange(mctx libkb.MetaContext) 29 } 30 31 // Each instance is tied to a UV login. Must be discarded when switching users. 32 // Threadsafe. 33 // CORE-8119: Make all of these methods hit caches when called repeatedly. 34 type buildPaymentCache struct { 35 sync.Mutex 36 remoter remote.Remoter 37 38 accountFundedCache *TimeCache 39 lookupRecipientCache *TimeCache 40 shouldOfferAdvancedSendCache *TimeCache 41 currencyPreferenceCache *TimeCache 42 currencyPreferenceForeverCache *TimeCache 43 } 44 45 func newBuildPaymentCache(remoter remote.Remoter) *buildPaymentCache { 46 return &buildPaymentCache{ 47 remoter: remoter, 48 accountFundedCache: NewTimeCache("accountFundedCache", 20, 0 /*forever*/), 49 lookupRecipientCache: NewTimeCache("lookupRecipient", 20, time.Minute), 50 shouldOfferAdvancedSendCache: NewTimeCache("shouldOfferAdvancedSend", 20, time.Minute), 51 currencyPreferenceCache: NewTimeCache("currencyPreference", 20, 5*time.Minute), 52 currencyPreferenceForeverCache: NewTimeCache("currencyPreferenceForever", 20, 0 /*forever*/), 53 } 54 } 55 56 func (c *buildPaymentCache) PrimaryAccount(mctx libkb.MetaContext) (stellar1.AccountID, error) { 57 return GetOwnPrimaryAccountID(mctx) 58 } 59 60 func (c *buildPaymentCache) AccountSeqno(mctx libkb.MetaContext, 61 accountID stellar1.AccountID) (string, error) { 62 seqno, err := c.remoter.AccountSeqno(mctx.Ctx(), accountID) 63 return fmt.Sprintf("%v", seqno), err 64 } 65 66 func (c *buildPaymentCache) IsAccountFunded(mctx libkb.MetaContext, 67 accountID stellar1.AccountID, bid stellar1.BuildPaymentID) (res bool, err error) { 68 fill := func() (interface{}, error) { 69 funded, err := isAccountFunded(mctx.Ctx(), c.remoter, accountID) 70 res = funded 71 return funded, err 72 } 73 if !bid.IsNil() { 74 key := fmt.Sprintf("%v:%v", accountID, bid) 75 err = c.accountFundedCache.GetWithFill(mctx, key, &res, fill) 76 return res, err 77 } 78 _, err = fill() 79 return res, err 80 } 81 82 func (c *buildPaymentCache) LookupRecipient(mctx libkb.MetaContext, 83 to stellarcommon.RecipientInput) (res stellarcommon.Recipient, err error) { 84 fill := func() (interface{}, error) { 85 return LookupRecipient(mctx, to, false /* isCLI */) 86 } 87 err = c.lookupRecipientCache.GetWithFill(mctx, string(to), &res, fill) 88 return res, err 89 } 90 91 func (c *buildPaymentCache) ShouldOfferAdvancedSend(mctx libkb.MetaContext, from, to stellar1.AccountID) (res stellar1.AdvancedBanner, err error) { 92 key := from.String() + ":" + to.String() 93 fill := func() (interface{}, error) { 94 return ShouldOfferAdvancedSend(mctx, c.remoter, from, to) 95 } 96 err = c.shouldOfferAdvancedSendCache.GetWithFill(mctx, key, &res, fill) 97 return res, err 98 } 99 100 func (c *buildPaymentCache) GetOutsideExchangeRate(mctx libkb.MetaContext, 101 currency stellar1.OutsideCurrencyCode) (rate stellar1.OutsideExchangeRate, err error) { 102 return c.remoter.ExchangeRate(mctx.Ctx(), string(currency)) 103 } 104 105 func (c *buildPaymentCache) AvailableXLMToSend(mctx libkb.MetaContext, 106 accountID stellar1.AccountID) (string, error) { 107 details, err := c.remoter.Details(mctx.Ctx(), accountID) 108 if err != nil { 109 return "", err 110 } 111 if details.Available == "" { 112 return "0", nil 113 } 114 return details.Available, nil 115 } 116 117 func (c *buildPaymentCache) GetOutsideCurrencyPreference(mctx libkb.MetaContext, 118 accountID stellar1.AccountID, bid stellar1.BuildPaymentID) (res stellar1.OutsideCurrencyCode, err error) { 119 fillInner := func() (interface{}, error) { 120 cr, err := GetCurrencySetting(mctx, accountID) 121 return cr.Code, err 122 } 123 fillOuter := func() (interface{}, error) { 124 err := c.currencyPreferenceCache.GetWithFill(mctx, accountID.String(), &res, fillInner) 125 return res, err 126 } 127 if !bid.IsNil() { 128 foreverKey := fmt.Sprintf("%v:%v", accountID, bid) 129 err = c.currencyPreferenceForeverCache.GetWithFill(mctx, foreverKey, &res, fillOuter) 130 return res, err 131 } 132 _, err = fillOuter() 133 return res, err 134 } 135 136 func (c *buildPaymentCache) InformDefaultCurrencyChange(mctx libkb.MetaContext) { 137 c.currencyPreferenceCache.Clear() 138 c.currencyPreferenceForeverCache.Clear() 139 }