From 465d309edc50909f64b89d8be5e31ae2d9bbb734 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Wed, 25 Aug 2021 11:02:34 -0700 Subject: [PATCH 01/14] Update API definition to include pool ID in balances --- protocols/horizon/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/horizon/main.go b/protocols/horizon/main.go index 3d315454fd..72d94103b3 100644 --- a/protocols/horizon/main.go +++ b/protocols/horizon/main.go @@ -197,9 +197,11 @@ type AssetStatAccounts struct { Unauthorized int32 `json:"unauthorized"` } -// Balance represents an account's holdings for a single currency type +// Balance represents an account's holdings for either a single currency type or +// shares in a liquidity pool. type Balance struct { Balance string `json:"balance"` + LiquidityPoolId string `json:"liquidity_pool_id,omitempty"` Limit string `json:"limit,omitempty"` BuyingLiabilities string `json:"buying_liabilities,omitempty"` SellingLiabilities string `json:"selling_liabilities,omitempty"` From 16bed52cd8758bdfb75d1873ee00916e2ae9b6d6 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Wed, 25 Aug 2021 12:31:20 -0700 Subject: [PATCH 02/14] Add skeleton code enabling filtering accounts by pool participation --- services/horizon/internal/actions/account.go | 14 ++++++++++---- services/horizon/internal/db2/history/accounts.go | 5 +++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/services/horizon/internal/actions/account.go b/services/horizon/internal/actions/account.go index a6d4278fc6..a82f11cbed 100644 --- a/services/horizon/internal/actions/account.go +++ b/services/horizon/internal/actions/account.go @@ -69,9 +69,10 @@ func AccountInfo(ctx context.Context, hq *history.Q, addr string) (*protocol.Acc // AccountsQuery query struct for accounts end-point type AccountsQuery struct { - Signer string `schema:"signer" valid:"accountID,optional"` - Sponsor string `schema:"sponsor" valid:"accountID,optional"` - AssetFilter string `schema:"asset" valid:"asset,optional"` + Signer string `schema:"signer" valid:"accountID,optional"` + Sponsor string `schema:"sponsor" valid:"accountID,optional"` + AssetFilter string `schema:"asset" valid:"asset,optional"` + LiquidityPool string `schema:"liquidity_pool" valid:"liquidity_pool,optional"` } // URITemplate returns a rfc6570 URI template the query struct @@ -83,7 +84,7 @@ var invalidAccountsParams = problem.P{ Type: "invalid_accounts_params", Title: "Invalid Accounts Parameters", Status: http.StatusBadRequest, - Detail: "Exactly one filter is required. Please ensure that you are including a signer, an asset, or a sponsor filter.", + Detail: "Exactly one filter is required. Please ensure that you are including a signer, a sponsor, an asset, or a liquidity pool filter.", } // Validate runs custom validations. @@ -158,6 +159,11 @@ func (handler GetAccountsHandler) GetResourcePage( if err != nil { return nil, errors.Wrap(err, "loading account records") } + } else if len(qp.LiquidityPool) > 0 { + records, err = historyQ.AccountEntriesForLiquidityPool(ctx, qp.LiquidityPool, pq) + if err != nil { + return nil, errors.Wrap(err, "loading account records") + } } else { records, err = historyQ.AccountsForAsset(ctx, *qp.Asset(), pq) if err != nil { diff --git a/services/horizon/internal/db2/history/accounts.go b/services/horizon/internal/db2/history/accounts.go index 3e08064a53..8c4ed18b72 100644 --- a/services/horizon/internal/db2/history/accounts.go +++ b/services/horizon/internal/db2/history/accounts.go @@ -366,6 +366,11 @@ func (q *Q) AccountEntriesForSigner(ctx context.Context, signer string, page db2 return results, nil } +// AccountEntriesForLiquidityPool returns a list of `AccountEntry` rows for a given liquidity pool ID. +func (q *Q) AccountEntriesForLiquidityPool(ctx context.Context, poolId string, page db2.PageQuery) ([]AccountEntry, error) { + return nil, errors.New("filtering by liquidity pool is not implemented") +} + var selectAccounts = sq.Select(` account_id, balance, From b0d3e42fec290cfa29faa3d120d3f23bd487997e Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Wed, 25 Aug 2021 12:47:33 -0700 Subject: [PATCH 03/14] Add query for selecting accounts by liquidity pool --- .../horizon/internal/db2/history/accounts.go | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/services/horizon/internal/db2/history/accounts.go b/services/horizon/internal/db2/history/accounts.go index 8c4ed18b72..33a67130a4 100644 --- a/services/horizon/internal/db2/history/accounts.go +++ b/services/horizon/internal/db2/history/accounts.go @@ -366,9 +366,30 @@ func (q *Q) AccountEntriesForSigner(ctx context.Context, signer string, page db2 return results, nil } -// AccountEntriesForLiquidityPool returns a list of `AccountEntry` rows for a given liquidity pool ID. +// AccountEntriesForLiquidityPool returns a list of `AccountEntry` rows that +// have trustlines established with a given liquidity pool ID. func (q *Q) AccountEntriesForLiquidityPool(ctx context.Context, poolId string, page db2.PageQuery) ([]AccountEntry, error) { return nil, errors.New("filtering by liquidity pool is not implemented") + + sql := sq. + Select("accounts.*"). + From("accounts"). + Join("trust_lines ON accounts.account_id = trust_lines.account_id"). + Where(map[string]interface{}{ + "trust_lines.liquidity_pool_id": poolId, + }) + + sql, err := page.ApplyToUsingCursor(sql, "trust_lines.liquidity_pool_id", page.Cursor) + if err != nil { + return nil, errors.Wrap(err, "could not apply query to page") + } + + var results []AccountEntry + if err := q.Select(ctx, &results, sql); err != nil { + return nil, errors.Wrap(err, "could not run select query") + } + + return results, nil } var selectAccounts = sq.Select(` From 50153515667b1eb3045e3b39e2aea45d73f03494 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 13:38:52 -0700 Subject: [PATCH 04/14] Add changelog entry --- services/horizon/CHANGELOG.md | 8 +++++++- services/horizon/internal/actions/account.go | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/services/horizon/CHANGELOG.md b/services/horizon/CHANGELOG.md index 53cc8ab211..73fb9647c4 100644 --- a/services/horizon/CHANGELOG.md +++ b/services/horizon/CHANGELOG.md @@ -5,9 +5,15 @@ file. This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased -* The `--ingest` flag is set by default. If `--captive-core-config-path` is not set, the config file is generated based on network passhprase. ([3783](https://github.com/stellar/go/pull/3783)) +### Breaking +* The `--ingest` flag is set by default. If `--captive-core-config-path` is not set, the config file is generated based on network passhprase ([3783](https://github.com/stellar/go/pull/3783)). + +### Add * Add a feature flag `--captive-core-reuse-storage-path`/`CAPTIVE_CORE_REUSE_STORAGE_PATH` that will reuse Captive Core's storage path for bucket files when applicable for better performance ([3750](https://github.com/stellar/go/pull/3750)). +* Add the ability to filter accounts by their participation in a particular liquidity pool ([TODO](https://github.com/stellar/go/pull/TODO)). + + ## v2.6.1 **Upgrading to this version from <= v2.1.1 will trigger a state rebuild. During this process (which will take at least 10 minutes), Horizon will not ingest new ledgers.** diff --git a/services/horizon/internal/actions/account.go b/services/horizon/internal/actions/account.go index a82f11cbed..024d0626fb 100644 --- a/services/horizon/internal/actions/account.go +++ b/services/horizon/internal/actions/account.go @@ -84,7 +84,7 @@ var invalidAccountsParams = problem.P{ Type: "invalid_accounts_params", Title: "Invalid Accounts Parameters", Status: http.StatusBadRequest, - Detail: "Exactly one filter is required. Please ensure that you are including a signer, a sponsor, an asset, or a liquidity pool filter.", + Detail: "Exactly one filter is required. Please ensure that you are including a signer, sponsor, asset, or liquidity pool filter.", } // Validate runs custom validations. From 3a260bdccd4becedaca4094888bf7c40c940ec3b Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 14:09:26 -0700 Subject: [PATCH 05/14] Populate account balances with pool shares --- services/horizon/internal/actions/account.go | 3 +- services/horizon/internal/assets/main.go | 13 ++--- .../internal/resourceadapter/account_entry.go | 20 +++++++- .../internal/resourceadapter/balance.go | 49 ++++++++++++++----- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/services/horizon/internal/actions/account.go b/services/horizon/internal/actions/account.go index 024d0626fb..3c82ac74cc 100644 --- a/services/horizon/internal/actions/account.go +++ b/services/horizon/internal/actions/account.go @@ -125,7 +125,8 @@ type GetAccountsHandler struct { } // GetResourcePage returns a page containing the account records that have -// `signer` as a signer or have a trustline to the given asset. +// `signer` as a signer, `sponsor` as a sponsor, a trustline to the given +// `asset`, or participate in a particular `liquidity_pool`. func (handler GetAccountsHandler) GetResourcePage( w HeaderWriter, r *http.Request, diff --git a/services/horizon/internal/assets/main.go b/services/horizon/internal/assets/main.go index c2cc57089b..9aeb80d7c5 100644 --- a/services/horizon/internal/assets/main.go +++ b/services/horizon/internal/assets/main.go @@ -7,17 +7,18 @@ import ( ) // ErrInvalidString gets returns when the string form of the asset type is invalid -var ErrInvalidString = errors.New("invalid asset type: was not one of 'native', 'credit_alphanum4', 'credit_alphanum12'") +var ErrInvalidString = errors.New("invalid asset type: was not one of 'native', 'credit_alphanum4', 'credit_alphanum12', 'liquidity_pool_shares'") //ErrInvalidValue gets returned when the xdr.AssetType int value is not one of the valid enum values var ErrInvalidValue = errors.New("unknown asset type, cannot convert to string") -// AssetTypeMap is the read-only (i.e. don't modify it) map from string names to xdr.AssetType -// values +// AssetTypeMap is the read-only (i.e. don't modify it) map from string names to +// xdr.AssetType values var AssetTypeMap = map[string]xdr.AssetType{ - "native": xdr.AssetTypeAssetTypeNative, - "credit_alphanum4": xdr.AssetTypeAssetTypeCreditAlphanum4, - "credit_alphanum12": xdr.AssetTypeAssetTypeCreditAlphanum12, + "native": xdr.AssetTypeAssetTypeNative, + "credit_alphanum4": xdr.AssetTypeAssetTypeCreditAlphanum4, + "credit_alphanum12": xdr.AssetTypeAssetTypeCreditAlphanum12, + "liquidity_pool_shares": xdr.AssetTypeAssetTypePoolShare, } //Parse creates an asset from the provided strings. See AssetTypeMap for valid strings for aType. diff --git a/services/horizon/internal/resourceadapter/account_entry.go b/services/horizon/internal/resourceadapter/account_entry.go index d70dacc882..252f0c7b13 100644 --- a/services/horizon/internal/resourceadapter/account_entry.go +++ b/services/horizon/internal/resourceadapter/account_entry.go @@ -6,6 +6,7 @@ import ( "strconv" protocol "github.com/stellar/go/protocols/horizon" + "github.com/stellar/go/services/horizon/internal/assets" horizonContext "github.com/stellar/go/services/horizon/internal/context" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/support/errors" @@ -47,7 +48,24 @@ func PopulateAccountEntry( // populate balances dest.Balances = make([]protocol.Balance, len(trustLines)+1) for i, tl := range trustLines { - err := PopulateBalance(&dest.Balances[i], tl) + var err error + + switch tl.AssetType { + case xdr.AssetTypeAssetTypeCreditAlphanum4: + fallthrough + case xdr.AssetTypeAssetTypeCreditAlphanum12: + err = PopulateAssetBalance(&dest.Balances[i], tl) + case xdr.AssetTypeAssetTypePoolShare: + err = PopulatePoolShareBalance(&dest.Balances[i], tl) + default: + assetType, innerErr := assets.String(tl.AssetType) + if innerErr != nil { + err = innerErr + } else { + err = errors.New("unknown asset in balance:" + assetType) + } + } + if err != nil { return errors.Wrap(err, "populating balance") } diff --git a/services/horizon/internal/resourceadapter/balance.go b/services/horizon/internal/resourceadapter/balance.go index 7bc4883caf..acb9241509 100644 --- a/services/horizon/internal/resourceadapter/balance.go +++ b/services/horizon/internal/resourceadapter/balance.go @@ -9,10 +9,27 @@ import ( "github.com/stellar/go/xdr" ) -func PopulateBalance(dest *protocol.Balance, row history.TrustLine) (err error) { +func PopulatePoolShareBalance(dest *protocol.Balance, row history.TrustLine) (err error) { dest.Type, err = assets.String(row.AssetType) if err != nil { - return errors.Wrap(err, "getting the string representation from the provided xdr asset type") + return err + } + if dest.Type != "liquidity_pool_shares" { + return PopulateAssetBalance(dest, row) + } + + dest.Balance = amount.StringFromInt64(row.Balance) + dest.Limit = amount.StringFromInt64(row.Limit) + dest.LastModifiedLedger = row.LastModifiedLedger + fillAuthorizationFlags(dest, row) + + return +} + +func PopulateAssetBalance(dest *protocol.Balance, row history.TrustLine) (err error) { + dest.Type, err = assets.String(row.AssetType) + if err != nil { + return err } dest.Balance = amount.StringFromInt64(row.Balance) @@ -22,20 +39,11 @@ func PopulateBalance(dest *protocol.Balance, row history.TrustLine) (err error) dest.Issuer = row.AssetIssuer dest.Code = row.AssetCode dest.LastModifiedLedger = row.LastModifiedLedger - isAuthorized := row.IsAuthorized() - dest.IsAuthorized = &isAuthorized - dest.IsAuthorizedToMaintainLiabilities = &isAuthorized - isAuthorizedToMaintainLiabilities := row.IsAuthorizedToMaintainLiabilities() - if isAuthorizedToMaintainLiabilities { - dest.IsAuthorizedToMaintainLiabilities = &isAuthorizedToMaintainLiabilities - } - isClawbackEnabled := row.IsClawbackEnabled() - if isClawbackEnabled { - dest.IsClawbackEnabled = &isClawbackEnabled - } + fillAuthorizationFlags(dest, row) if row.Sponsor.Valid { dest.Sponsor = row.Sponsor.String } + return } @@ -54,5 +62,20 @@ func PopulateNativeBalance(dest *protocol.Balance, stroops, buyingLiabilities, s dest.Code = "" dest.IsAuthorized = nil dest.IsAuthorizedToMaintainLiabilities = nil + dest.IsClawbackEnabled = nil return } + +func fillAuthorizationFlags(dest *protocol.Balance, row history.TrustLine) { + isAuthorized := row.IsAuthorized() + dest.IsAuthorized = &isAuthorized + dest.IsAuthorizedToMaintainLiabilities = &isAuthorized + isAuthorizedToMaintainLiabilities := row.IsAuthorizedToMaintainLiabilities() + if isAuthorizedToMaintainLiabilities { + dest.IsAuthorizedToMaintainLiabilities = &isAuthorizedToMaintainLiabilities + } + isClawbackEnabled := row.IsClawbackEnabled() + if isClawbackEnabled { + dest.IsClawbackEnabled = &isClawbackEnabled + } +} From 1f6c2b655c7d7db3c65817f001ad48e309afa99f Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 14:12:27 -0700 Subject: [PATCH 06/14] Separate changelog entries --- services/horizon/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/horizon/CHANGELOG.md b/services/horizon/CHANGELOG.md index 73fb9647c4..fb4c0851eb 100644 --- a/services/horizon/CHANGELOG.md +++ b/services/horizon/CHANGELOG.md @@ -11,8 +11,10 @@ file. This project adheres to [Semantic Versioning](http://semver.org/). ### Add * Add a feature flag `--captive-core-reuse-storage-path`/`CAPTIVE_CORE_REUSE_STORAGE_PATH` that will reuse Captive Core's storage path for bucket files when applicable for better performance ([3750](https://github.com/stellar/go/pull/3750)). -* Add the ability to filter accounts by their participation in a particular liquidity pool ([TODO](https://github.com/stellar/go/pull/TODO)). +* Add the ability to filter accounts by their participation in a particular liquidity pool ([3873](https://github.com/stellar/go/pull/3873)). +### Update +* Include pool shares in account balances ([3873](https://github.com/stellar/go/pull/3873)). ## v2.6.1 From 5d893dcd3404fca97bb3d944f557a010bd9552f6 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 14:19:45 -0700 Subject: [PATCH 07/14] Add tests for filling out account balances --- .../resourceadapter/account_entry_test.go | 19 +++++++++++---- .../internal/resourceadapter/balance_test.go | 24 ++++++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/services/horizon/internal/resourceadapter/account_entry_test.go b/services/horizon/internal/resourceadapter/account_entry_test.go index ebe0e112b9..03da789e20 100644 --- a/services/horizon/internal/resourceadapter/account_entry_test.go +++ b/services/horizon/internal/resourceadapter/account_entry_test.go @@ -3,11 +3,12 @@ package resourceadapter import ( "encoding/base64" "encoding/json" - "github.com/guregu/null" "strconv" "testing" "time" + "github.com/guregu/null" + "github.com/stellar/go/amount" . "github.com/stellar/go/protocols/horizon" protocol "github.com/stellar/go/protocols/horizon" @@ -75,7 +76,7 @@ var ( AccountID: accountID.Address(), AssetCode: "EUR", AssetIssuer: trustLineIssuer.Address(), - AssetType: 1, + AssetType: xdr.AssetTypeAssetTypeCreditAlphanum4, Balance: 20000, Limit: 223456789, Flags: 1, @@ -85,9 +86,9 @@ var ( }, { AccountID: accountID.Address(), - AssetCode: "USD", + AssetCode: "USDDDDDDDDDD", AssetIssuer: trustLineIssuer.Address(), - AssetType: 1, + AssetType: xdr.AssetTypeAssetTypeCreditAlphanum12, Balance: 10000, Limit: 123456789, Flags: 0, @@ -95,6 +96,14 @@ var ( BuyingLiabilities: 1, LastModifiedLedger: 900, }, + { + AccountID: accountID.Address(), + AssetType: xdr.AssetTypeAssetTypePoolShare, + Balance: 10000, + Limit: 123456789, + Flags: 0, + LastModifiedLedger: 900, + }, } signers = []history.AccountSigner{ @@ -166,7 +175,7 @@ func TestPopulateAccountEntry(t *testing.T) { tt.Equal(d.Value, history.AccountDataValue(want)) } - tt.Len(hAccount.Balances, 3) + tt.Len(hAccount.Balances, 4) for i, t := range trustLines { ht := hAccount.Balances[i] diff --git a/services/horizon/internal/resourceadapter/balance_test.go b/services/horizon/internal/resourceadapter/balance_test.go index e3ab93763a..0e9f743627 100644 --- a/services/horizon/internal/resourceadapter/balance_test.go +++ b/services/horizon/internal/resourceadapter/balance_test.go @@ -39,9 +39,16 @@ func TestPopulateBalance(t *testing.T) { Balance: 10, Flags: 0, } + poolshareTrustline := history.TrustLine{ + AccountID: "testID", + AssetType: xdr.AssetTypeAssetTypePoolShare, + Limit: 100, + Balance: 10, + Flags: 0, + } want := Balance{} - err := PopulateBalance(&want, authorizedTrustline) + err := PopulateAssetBalance(&want, authorizedTrustline) assert.NoError(t, err) assert.Equal(t, "credit_alphanum12", want.Type) assert.Equal(t, "0.0000010", want.Balance) @@ -52,7 +59,7 @@ func TestPopulateBalance(t *testing.T) { assert.Equal(t, true, *want.IsAuthorizedToMaintainLiabilities) want = Balance{} - err = PopulateBalance(&want, authorizedToMaintainLiabilitiesTrustline) + err = PopulateAssetBalance(&want, authorizedToMaintainLiabilitiesTrustline) assert.NoError(t, err) assert.Equal(t, "credit_alphanum12", want.Type) assert.Equal(t, "0.0000010", want.Balance) @@ -63,7 +70,7 @@ func TestPopulateBalance(t *testing.T) { assert.Equal(t, true, *want.IsAuthorizedToMaintainLiabilities) want = Balance{} - err = PopulateBalance(&want, unauthorizedTrustline) + err = PopulateAssetBalance(&want, unauthorizedTrustline) assert.NoError(t, err) assert.Equal(t, "credit_alphanum12", want.Type) assert.Equal(t, "0.0000010", want.Balance) @@ -72,6 +79,17 @@ func TestPopulateBalance(t *testing.T) { assert.Equal(t, testAssetCode2, want.Code) assert.Equal(t, false, *want.IsAuthorized) assert.Equal(t, false, *want.IsAuthorizedToMaintainLiabilities) + + want = Balance{} + err = PopulatePoolShareBalance(&want, poolshareTrustline) + assert.NoError(t, err) + assert.Equal(t, "liquidity_pool_shares", want.Type) + assert.Equal(t, "0.0000010", want.Balance) + assert.Equal(t, "0.0000100", want.Limit) + assert.Equal(t, "", want.Issuer) + assert.Equal(t, "", want.Code) + assert.Equal(t, false, *want.IsAuthorized) + assert.Equal(t, false, *want.IsAuthorizedToMaintainLiabilities) } func TestPopulateNativeBalance(t *testing.T) { From 583b943b92abb9707bd14ba6ec701d4980ccca32 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 14:35:45 -0700 Subject: [PATCH 08/14] Test non-existent liabilities properly --- .../internal/resourceadapter/account_entry_test.go | 12 ++++++++++-- services/horizon/internal/resourceadapter/balance.go | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/services/horizon/internal/resourceadapter/account_entry_test.go b/services/horizon/internal/resourceadapter/account_entry_test.go index 03da789e20..c0d0fa32e7 100644 --- a/services/horizon/internal/resourceadapter/account_entry_test.go +++ b/services/horizon/internal/resourceadapter/account_entry_test.go @@ -186,8 +186,16 @@ func TestPopulateAccountEntry(t *testing.T) { tt.Equal(wantType, ht.Type) tt.Equal(amount.StringFromInt64(t.Balance), ht.Balance) - tt.Equal(amount.StringFromInt64(t.BuyingLiabilities), ht.BuyingLiabilities) - tt.Equal(amount.StringFromInt64(t.SellingLiabilities), ht.SellingLiabilities) + if t.BuyingLiabilities != 0 { + tt.Equal(amount.StringFromInt64(t.BuyingLiabilities), ht.BuyingLiabilities) + } else { + tt.Equal("", ht.BuyingLiabilities) + } + if t.SellingLiabilities != 0 { + tt.Equal(amount.StringFromInt64(t.SellingLiabilities), ht.SellingLiabilities) + } else { + tt.Equal("", ht.SellingLiabilities) + } tt.Equal(amount.StringFromInt64(t.Limit), ht.Limit) tt.Equal(t.LastModifiedLedger, ht.LastModifiedLedger) tt.Equal(t.IsAuthorized(), *ht.IsAuthorized) diff --git a/services/horizon/internal/resourceadapter/balance.go b/services/horizon/internal/resourceadapter/balance.go index acb9241509..f03e1af6e7 100644 --- a/services/horizon/internal/resourceadapter/balance.go +++ b/services/horizon/internal/resourceadapter/balance.go @@ -69,11 +69,15 @@ func PopulateNativeBalance(dest *protocol.Balance, stroops, buyingLiabilities, s func fillAuthorizationFlags(dest *protocol.Balance, row history.TrustLine) { isAuthorized := row.IsAuthorized() dest.IsAuthorized = &isAuthorized + + // After CAP-18, isAuth => isAuthToMaintain, so the following code does this + // in a backwards compatible manner. dest.IsAuthorizedToMaintainLiabilities = &isAuthorized isAuthorizedToMaintainLiabilities := row.IsAuthorizedToMaintainLiabilities() if isAuthorizedToMaintainLiabilities { dest.IsAuthorizedToMaintainLiabilities = &isAuthorizedToMaintainLiabilities } + isClawbackEnabled := row.IsClawbackEnabled() if isClawbackEnabled { dest.IsClawbackEnabled = &isClawbackEnabled From c0df7143d2427f178ae0d9b0e3c405b451db8207 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 14:37:50 -0700 Subject: [PATCH 09/14] Simplify liabilities check --- .../resourceadapter/account_entry_test.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/services/horizon/internal/resourceadapter/account_entry_test.go b/services/horizon/internal/resourceadapter/account_entry_test.go index c0d0fa32e7..4c5f7af5cb 100644 --- a/services/horizon/internal/resourceadapter/account_entry_test.go +++ b/services/horizon/internal/resourceadapter/account_entry_test.go @@ -186,16 +186,19 @@ func TestPopulateAccountEntry(t *testing.T) { tt.Equal(wantType, ht.Type) tt.Equal(amount.StringFromInt64(t.Balance), ht.Balance) + + wantBuy := "" if t.BuyingLiabilities != 0 { - tt.Equal(amount.StringFromInt64(t.BuyingLiabilities), ht.BuyingLiabilities) - } else { - tt.Equal("", ht.BuyingLiabilities) + wantBuy = amount.StringFromInt64(t.BuyingLiabilities) } + tt.Equal(wantBuy, ht.BuyingLiabilities) + + wantSell := "" if t.SellingLiabilities != 0 { - tt.Equal(amount.StringFromInt64(t.SellingLiabilities), ht.SellingLiabilities) - } else { - tt.Equal("", ht.SellingLiabilities) + wantSell = amount.StringFromInt64(t.SellingLiabilities) } + tt.Equal(wantSell, ht.SellingLiabilities) + tt.Equal(amount.StringFromInt64(t.Limit), ht.Limit) tt.Equal(t.LastModifiedLedger, ht.LastModifiedLedger) tt.Equal(t.IsAuthorized(), *ht.IsAuthorized) From ebcdf9cd5bae0af17680c6e22f4f28b13ea00feb Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 15:40:32 -0700 Subject: [PATCH 10/14] Amend query parameter tests to include liquidity_pool filter --- services/horizon/internal/actions/account_test.go | 5 +++-- services/horizon/internal/actions_root_test.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/services/horizon/internal/actions/account_test.go b/services/horizon/internal/actions/account_test.go index 0287e4d07d..2590e379c0 100644 --- a/services/horizon/internal/actions/account_test.go +++ b/services/horizon/internal/actions/account_test.go @@ -1,11 +1,12 @@ package actions import ( - "github.com/guregu/null" "net/http/httptest" "testing" "time" + "github.com/guregu/null" + "github.com/stretchr/testify/assert" protocol "github.com/stellar/go/protocols/horizon" @@ -622,7 +623,7 @@ func TestGetAccountsHandlerInvalidParams(t *testing.T) { func TestAccountQueryURLTemplate(t *testing.T) { tt := assert.New(t) - expected := "/accounts{?signer,sponsor,asset,cursor,limit,order}" + expected := "/accounts{?signer,sponsor,asset,liquidity_pool,cursor,limit,order}" accountsQuery := AccountsQuery{} tt.Equal(expected, accountsQuery.URITemplate()) } diff --git a/services/horizon/internal/actions_root_test.go b/services/horizon/internal/actions_root_test.go index f4d521627e..f77cf02da5 100644 --- a/services/horizon/internal/actions_root_test.go +++ b/services/horizon/internal/actions_root_test.go @@ -47,7 +47,7 @@ func TestRootAction(t *testing.T) { err = json.Unmarshal(w.Body.Bytes(), &actual) ht.Require.NoError(err) ht.Assert.Equal( - "http://localhost/accounts{?signer,sponsor,asset,cursor,limit,order}", + "http://localhost/accounts{?signer,sponsor,asset,liquidity_pool,cursor,limit,order}", actual.Links.Accounts.Href, ) ht.Assert.Equal( From f42e1f2218e08e0e2477cdbd4fc1cad5d19788d2 Mon Sep 17 00:00:00 2001 From: George Kudrayvtsev Date: Mon, 30 Aug 2021 15:41:43 -0700 Subject: [PATCH 11/14] Simplify asset type parsing for filling balance entries While this is technically less defensive, it's still strictly correct, because it covers all of the possible cases for this code block: only pool shares / asset4 / asset12 can be a part of the switch statement. --- .../internal/resourceadapter/account_entry.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/services/horizon/internal/resourceadapter/account_entry.go b/services/horizon/internal/resourceadapter/account_entry.go index 252f0c7b13..e18528be5b 100644 --- a/services/horizon/internal/resourceadapter/account_entry.go +++ b/services/horizon/internal/resourceadapter/account_entry.go @@ -6,7 +6,6 @@ import ( "strconv" protocol "github.com/stellar/go/protocols/horizon" - "github.com/stellar/go/services/horizon/internal/assets" horizonContext "github.com/stellar/go/services/horizon/internal/context" "github.com/stellar/go/services/horizon/internal/db2/history" "github.com/stellar/go/support/errors" @@ -51,19 +50,10 @@ func PopulateAccountEntry( var err error switch tl.AssetType { - case xdr.AssetTypeAssetTypeCreditAlphanum4: - fallthrough - case xdr.AssetTypeAssetTypeCreditAlphanum12: - err = PopulateAssetBalance(&dest.Balances[i], tl) case xdr.AssetTypeAssetTypePoolShare: err = PopulatePoolShareBalance(&dest.Balances[i], tl) default: - assetType, innerErr := assets.String(tl.AssetType) - if innerErr != nil { - err = innerErr - } else { - err = errors.New("unknown asset in balance:" + assetType) - } + err = PopulateAssetBalance(&dest.Balances[i], tl) } if err != nil { From 1b8eeb9144266545e3e218ec8fe40b12586909a2 Mon Sep 17 00:00:00 2001 From: Bartek Nowotarski Date: Tue, 31 Aug 2021 14:17:20 +0200 Subject: [PATCH 12/14] review feedback --- services/horizon/internal/actions/account.go | 4 +-- services/horizon/internal/assets/main.go | 7 +++-- .../horizon/internal/db2/history/accounts.go | 26 ------------------- 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/services/horizon/internal/actions/account.go b/services/horizon/internal/actions/account.go index 3c82ac74cc..41bff28834 100644 --- a/services/horizon/internal/actions/account.go +++ b/services/horizon/internal/actions/account.go @@ -72,7 +72,7 @@ type AccountsQuery struct { Signer string `schema:"signer" valid:"accountID,optional"` Sponsor string `schema:"sponsor" valid:"accountID,optional"` AssetFilter string `schema:"asset" valid:"asset,optional"` - LiquidityPool string `schema:"liquidity_pool" valid:"liquidity_pool,optional"` + LiquidityPool string `schema:"liquidity_pool" valid:"sha256,optional"` } // URITemplate returns a rfc6570 URI template the query struct @@ -161,7 +161,7 @@ func (handler GetAccountsHandler) GetResourcePage( return nil, errors.Wrap(err, "loading account records") } } else if len(qp.LiquidityPool) > 0 { - records, err = historyQ.AccountEntriesForLiquidityPool(ctx, qp.LiquidityPool, pq) + records, err = historyQ.AccountsForLiquidityPool(ctx, qp.LiquidityPool, pq) if err != nil { return nil, errors.Wrap(err, "loading account records") } diff --git a/services/horizon/internal/assets/main.go b/services/horizon/internal/assets/main.go index 9aeb80d7c5..6613f62bab 100644 --- a/services/horizon/internal/assets/main.go +++ b/services/horizon/internal/assets/main.go @@ -15,10 +15,9 @@ var ErrInvalidValue = errors.New("unknown asset type, cannot convert to string") // AssetTypeMap is the read-only (i.e. don't modify it) map from string names to // xdr.AssetType values var AssetTypeMap = map[string]xdr.AssetType{ - "native": xdr.AssetTypeAssetTypeNative, - "credit_alphanum4": xdr.AssetTypeAssetTypeCreditAlphanum4, - "credit_alphanum12": xdr.AssetTypeAssetTypeCreditAlphanum12, - "liquidity_pool_shares": xdr.AssetTypeAssetTypePoolShare, + "native": xdr.AssetTypeAssetTypeNative, + "credit_alphanum4": xdr.AssetTypeAssetTypeCreditAlphanum4, + "credit_alphanum12": xdr.AssetTypeAssetTypeCreditAlphanum12, } //Parse creates an asset from the provided strings. See AssetTypeMap for valid strings for aType. diff --git a/services/horizon/internal/db2/history/accounts.go b/services/horizon/internal/db2/history/accounts.go index 33a67130a4..3e08064a53 100644 --- a/services/horizon/internal/db2/history/accounts.go +++ b/services/horizon/internal/db2/history/accounts.go @@ -366,32 +366,6 @@ func (q *Q) AccountEntriesForSigner(ctx context.Context, signer string, page db2 return results, nil } -// AccountEntriesForLiquidityPool returns a list of `AccountEntry` rows that -// have trustlines established with a given liquidity pool ID. -func (q *Q) AccountEntriesForLiquidityPool(ctx context.Context, poolId string, page db2.PageQuery) ([]AccountEntry, error) { - return nil, errors.New("filtering by liquidity pool is not implemented") - - sql := sq. - Select("accounts.*"). - From("accounts"). - Join("trust_lines ON accounts.account_id = trust_lines.account_id"). - Where(map[string]interface{}{ - "trust_lines.liquidity_pool_id": poolId, - }) - - sql, err := page.ApplyToUsingCursor(sql, "trust_lines.liquidity_pool_id", page.Cursor) - if err != nil { - return nil, errors.Wrap(err, "could not apply query to page") - } - - var results []AccountEntry - if err := q.Select(ctx, &results, sql); err != nil { - return nil, errors.Wrap(err, "could not run select query") - } - - return results, nil -} - var selectAccounts = sq.Select(` account_id, balance, From 7b119c472dbb8c99bf0c78888cb19b0f18b70d7a Mon Sep 17 00:00:00 2001 From: Bartek Nowotarski Date: Tue, 31 Aug 2021 14:24:13 +0200 Subject: [PATCH 13/14] fix tests --- .../resourceadapter/account_entry_test.go | 10 ++++++++-- .../horizon/internal/resourceadapter/balance.go | 17 +++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/services/horizon/internal/resourceadapter/account_entry_test.go b/services/horizon/internal/resourceadapter/account_entry_test.go index 4c5f7af5cb..0d53b31459 100644 --- a/services/horizon/internal/resourceadapter/account_entry_test.go +++ b/services/horizon/internal/resourceadapter/account_entry_test.go @@ -181,8 +181,14 @@ func TestPopulateAccountEntry(t *testing.T) { ht := hAccount.Balances[i] tt.Equal(t.AssetIssuer, ht.Issuer) tt.Equal(t.AssetCode, ht.Code) - wantType, e := assets.String(t.AssetType) - tt.NoError(e) + var wantType string + if t.AssetType == xdr.AssetTypeAssetTypePoolShare { + wantType = "liquidity_pool_shares" + } else { + var err error + wantType, err = assets.String(t.AssetType) + tt.NoError(err) + } tt.Equal(wantType, ht.Type) tt.Equal(amount.StringFromInt64(t.Balance), ht.Balance) diff --git a/services/horizon/internal/resourceadapter/balance.go b/services/horizon/internal/resourceadapter/balance.go index f03e1af6e7..e336eac732 100644 --- a/services/horizon/internal/resourceadapter/balance.go +++ b/services/horizon/internal/resourceadapter/balance.go @@ -10,12 +10,17 @@ import ( ) func PopulatePoolShareBalance(dest *protocol.Balance, row history.TrustLine) (err error) { - dest.Type, err = assets.String(row.AssetType) - if err != nil { - return err - } - if dest.Type != "liquidity_pool_shares" { - return PopulateAssetBalance(dest, row) + if row.AssetType == xdr.AssetTypeAssetTypePoolShare { + dest.Type = "liquidity_pool_shares" + } else { + dest.Type, err = assets.String(row.AssetType) + if err != nil { + return err + } + + if dest.Type != "liquidity_pool_shares" { + return PopulateAssetBalance(dest, row) + } } dest.Balance = amount.StringFromInt64(row.Balance) From 12b52db01424a561466f44f31743439946ba14df Mon Sep 17 00:00:00 2001 From: Bartek Nowotarski Date: Tue, 31 Aug 2021 14:27:06 +0200 Subject: [PATCH 14/14] fix shadow --- services/horizon/internal/resourceadapter/account_entry_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/services/horizon/internal/resourceadapter/account_entry_test.go b/services/horizon/internal/resourceadapter/account_entry_test.go index 0d53b31459..f74884edc4 100644 --- a/services/horizon/internal/resourceadapter/account_entry_test.go +++ b/services/horizon/internal/resourceadapter/account_entry_test.go @@ -185,7 +185,6 @@ func TestPopulateAccountEntry(t *testing.T) { if t.AssetType == xdr.AssetTypeAssetTypePoolShare { wantType = "liquidity_pool_shares" } else { - var err error wantType, err = assets.String(t.AssetType) tt.NoError(err) }