diff --git a/.github/workflows/galexie-release.yml b/.github/workflows/galexie-release.yml index 7fb93792d0..875c8b087d 100644 --- a/.github/workflows/galexie-release.yml +++ b/.github/workflows/galexie-release.yml @@ -16,7 +16,7 @@ jobs: # this is the multi-arch index sha, get it by 'docker buildx imagetools inspect stellar/quickstart:testing' GALEXIE_INTEGRATION_TESTS_QUICKSTART_IMAGE: docker.io/stellar/quickstart:testing@sha256:5333ec87069efd7bb61f6654a801dc093bf0aad91f43a5ba84806d3efe4a6322 GALEXIE_INTEGRATION_TESTS_QUICKSTART_IMAGE_PULL: "false" - STELLAR_CORE_VERSION: 22.1.0-2194.0241e79f7.focal + STELLAR_CORE_VERSION: 22.1.1-2291.a50f3f919.focal~do~not~use~in~prd steps: - name: Set VERSION run: | diff --git a/.github/workflows/galexie.yml b/.github/workflows/galexie.yml index 4ed163e52e..837fc3dfe1 100644 --- a/.github/workflows/galexie.yml +++ b/.github/workflows/galexie.yml @@ -10,7 +10,7 @@ jobs: name: Test runs-on: ubuntu-22.04 env: - CAPTIVE_CORE_DEBIAN_PKG_VERSION: 22.1.0-2194.0241e79f7.focal + CAPTIVE_CORE_DEBIAN_PKG_VERSION: 22.1.1-2291.a50f3f919.focal~do~not~use~in~prd GALEXIE_INTEGRATION_TESTS_ENABLED: "true" GALEXIE_INTEGRATION_TESTS_CAPTIVE_CORE_BIN: /usr/bin/stellar-core # this pins to a version of quickstart:testing that has the same version as GALEXIE_INTEGRATION_TESTS_CAPTIVE_CORE_BIN diff --git a/.github/workflows/horizon.yml b/.github/workflows/horizon.yml index 0ab7cc7807..17da9dffe9 100644 --- a/.github/workflows/horizon.yml +++ b/.github/workflows/horizon.yml @@ -33,8 +33,8 @@ jobs: HORIZON_INTEGRATION_TESTS_ENABLED: true HORIZON_INTEGRATION_TESTS_CORE_MAX_SUPPORTED_PROTOCOL: ${{ matrix.protocol-version }} HORIZON_INTEGRATION_TESTS_CAPTIVE_CORE_USE_DB: true - PROTOCOL_22_CORE_DEBIAN_PKG_VERSION: 22.1.0-2194.0241e79f7.focal - PROTOCOL_22_CORE_DOCKER_IMG: stellar/stellar-core:22.1.0-2194.0241e79f7.focal + PROTOCOL_22_CORE_DEBIAN_PKG_VERSION: 22.1.1-2291.a50f3f919.focal~do~not~use~in~prd + PROTOCOL_22_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:22.1.1-2291.a50f3f919.focal-do-not-use-in-prd PROTOCOL_22_STELLAR_RPC_DOCKER_IMG: stellar/stellar-rpc:22.1.2 PGHOST: localhost PGPORT: 5432 @@ -124,7 +124,7 @@ jobs: runs-on: ubuntu-22.04 env: GO_VERSION: 1.23.4 - STELLAR_CORE_VERSION: 22.1.0-2194.0241e79f7.focal + STELLAR_CORE_VERSION: 22.1.1-2291.a50f3f919.jammy~do~not~use~in~prd CAPTIVE_CORE_STORAGE_PATH: /tmp steps: - uses: actions/checkout@v3 @@ -134,7 +134,7 @@ jobs: - name: Build and test the Verify Range Docker image run: | - docker build --build-arg="GO_VERSION=$GO_VERSION" -f services/horizon/docker/verify-range/Dockerfile -t stellar/horizon-verify-range services/horizon/docker/verify-range/ + docker build --build-arg="GO_VERSION=$GO_VERSION" --build-arg="STELLAR_CORE_VERSION=$STELLAR_CORE_VERSION" -f services/horizon/docker/verify-range/Dockerfile -t stellar/horizon-verify-range services/horizon/docker/verify-range/ # Any range should do for basic testing, this range was chosen pretty early in history so that it only takes a few mins to run docker run -e BRANCH=$(git rev-parse HEAD) -e FROM=10000063 -e TO=10000127 stellar/horizon-verify-range diff --git a/ingest/ledgerbackend/captive_core_backend.go b/ingest/ledgerbackend/captive_core_backend.go index dc5365809b..eda2d11775 100644 --- a/ingest/ledgerbackend/captive_core_backend.go +++ b/ingest/ledgerbackend/captive_core_backend.go @@ -20,7 +20,7 @@ import ( "github.com/stellar/go/xdr" ) -const minProtocolVersionSupported uint = 21 +const minProtocolVersionSupported uint = 23 // Ensure CaptiveStellarCore implements LedgerBackend var _ LedgerBackend = (*CaptiveStellarCore)(nil) diff --git a/ingest/ledgerbackend/captive_core_backend_test.go b/ingest/ledgerbackend/captive_core_backend_test.go index cb7e8dba7e..483e2e7c8c 100644 --- a/ingest/ledgerbackend/captive_core_backend_test.go +++ b/ingest/ledgerbackend/captive_core_backend_test.go @@ -160,7 +160,7 @@ func TestCaptiveNew(t *testing.T) { HistoryArchiveURLs: historyURLs, StoragePath: storagePath, UserAgent: "uatest", - CoreProtocolVersionFn: func(string) (uint, error) { return 21, nil }, + CoreProtocolVersionFn: func(string) (uint, error) { return 23, nil }, }, ) @@ -193,11 +193,11 @@ func TestCaptiveNewUnsupportedProtocolVersion(t *testing.T) { HistoryArchiveURLs: historyURLs, StoragePath: storagePath, UserAgent: "uatest", - CoreProtocolVersionFn: func(string) (uint, error) { return 20, nil }, + CoreProtocolVersionFn: func(string) (uint, error) { return 22, nil }, }, ) - assert.EqualError(t, err, "stellar-core version not supported. Installed stellar-core version is at protocol 20, but minimum required version is 21. Please upgrade stellar-core to a version that supports protocol version 21 or higher") + assert.EqualError(t, err, "stellar-core version not supported. Installed stellar-core version is at protocol 22, but minimum required version is 23. Please upgrade stellar-core to a version that supports protocol version 23 or higher") } func TestCaptivePrepareRange(t *testing.T) { @@ -1041,7 +1041,7 @@ func TestCaptiveStellarCore_PrepareRangeAfterClose(t *testing.T) { HistoryArchiveURLs: historyURLs, Toml: captiveCoreToml, StoragePath: storagePath, - CoreProtocolVersionFn: func(string) (uint, error) { return 21, nil }, + CoreProtocolVersionFn: func(string) (uint, error) { return 23, nil }, }, ) assert.NoError(t, err) diff --git a/ingest/processors/asset_processor/asset_test.go b/ingest/processors/asset_processor/asset_test.go index 8160f5473b..a9fbb2aeec 100644 --- a/ingest/processors/asset_processor/asset_test.go +++ b/ingest/processors/asset_processor/asset_test.go @@ -31,9 +31,6 @@ var genericBumpOperationEnvelope = xdr.TransactionV1Envelope{ Ext: xdr.TransactionExt{ V: 0, SorobanData: &xdr.SorobanTransactionData{ - Ext: xdr.ExtensionPoint{ - V: 0, - }, Resources: xdr.SorobanResources{ Footprint: xdr.LedgerFootprint{ ReadOnly: []xdr.LedgerKey{}, diff --git a/ingest/processors/operation_processor/operation_test.go b/ingest/processors/operation_processor/operation_test.go index 4609e779d8..ee1127f672 100644 --- a/ingest/processors/operation_processor/operation_test.go +++ b/ingest/processors/operation_processor/operation_test.go @@ -70,9 +70,6 @@ var genericBumpOperationEnvelope = xdr.TransactionV1Envelope{ Ext: xdr.TransactionExt{ V: 0, SorobanData: &xdr.SorobanTransactionData{ - Ext: xdr.ExtensionPoint{ - V: 0, - }, Resources: xdr.SorobanResources{ Footprint: xdr.LedgerFootprint{ ReadOnly: []xdr.LedgerKey{}, diff --git a/ingest/processors/trade_processor/trade_test.go b/ingest/processors/trade_processor/trade_test.go index 8d55f70d5a..d38ae06737 100644 --- a/ingest/processors/trade_processor/trade_test.go +++ b/ingest/processors/trade_processor/trade_test.go @@ -33,9 +33,6 @@ var genericBumpOperationEnvelope = xdr.TransactionV1Envelope{ Ext: xdr.TransactionExt{ V: 0, SorobanData: &xdr.SorobanTransactionData{ - Ext: xdr.ExtensionPoint{ - V: 0, - }, Resources: xdr.SorobanResources{ Footprint: xdr.LedgerFootprint{ ReadOnly: []xdr.LedgerKey{}, diff --git a/ingest/processors/transaction_processor/transaction_test.go b/ingest/processors/transaction_processor/transaction_test.go index aa8fcc01c6..caf6b4b7ea 100644 --- a/ingest/processors/transaction_processor/transaction_test.go +++ b/ingest/processors/transaction_processor/transaction_test.go @@ -61,9 +61,6 @@ var genericBumpOperationEnvelope = xdr.TransactionV1Envelope{ Ext: xdr.TransactionExt{ V: 0, SorobanData: &xdr.SorobanTransactionData{ - Ext: xdr.ExtensionPoint{ - V: 0, - }, Resources: xdr.SorobanResources{ Footprint: xdr.LedgerFootprint{ ReadOnly: []xdr.LedgerKey{}, diff --git a/protocols/horizon/main.go b/protocols/horizon/main.go index 3fa6ef1ab1..b00c5e219c 100644 --- a/protocols/horizon/main.go +++ b/protocols/horizon/main.go @@ -175,21 +175,15 @@ type AssetStat struct { } `json:"_links"` base.Asset - PT string `json:"paging_token"` - ContractID string `json:"contract_id,omitempty"` - NumClaimableBalances int32 `json:"num_claimable_balances"` - NumLiquidityPools int32 `json:"num_liquidity_pools"` - NumContracts int32 `json:"num_contracts"` - // NumArchivedContracts is deprecated and will be removed in the v23 release - // Action needed in release: horizon-v23.0.0: remove field - NumArchivedContracts int32 `json:"num_archived_contracts"` + PT string `json:"paging_token"` + ContractID string `json:"contract_id,omitempty"` + NumClaimableBalances int32 `json:"num_claimable_balances"` + NumLiquidityPools int32 `json:"num_liquidity_pools"` + NumContracts int32 `json:"num_contracts"` Accounts AssetStatAccounts `json:"accounts"` ClaimableBalancesAmount string `json:"claimable_balances_amount"` LiquidityPoolsAmount string `json:"liquidity_pools_amount"` ContractsAmount string `json:"contracts_amount"` - // ArchivedContractsAmount is deprecated and will be removed in the v23 release - // Action needed in release: horizon-v23.0.0: remove field - ArchivedContractsAmount string `json:"archived_contracts_amount"` Balances AssetStatBalances `json:"balances"` Flags AccountFlags `json:"flags"` } diff --git a/services/horizon/docker/verify-range/Dockerfile b/services/horizon/docker/verify-range/Dockerfile index 6323870f38..7586756445 100644 --- a/services/horizon/docker/verify-range/Dockerfile +++ b/services/horizon/docker/verify-range/Dockerfile @@ -2,7 +2,6 @@ FROM ubuntu:22.04 ARG GO_VERSION ARG STELLAR_CORE_VERSION -ENV STELLAR_CORE_VERSION=${STELLAR_CORE_VERSION:-*} # to remove tzdata interactive flow ENV DEBIAN_FRONTEND=noninteractive diff --git a/services/horizon/docker/verify-range/dependencies b/services/horizon/docker/verify-range/dependencies index 9c13cf09b4..9854adf8a2 100644 --- a/services/horizon/docker/verify-range/dependencies +++ b/services/horizon/docker/verify-range/dependencies @@ -7,10 +7,11 @@ apt-get update apt-get install -y curl git libpq-dev libsqlite3-dev libsasl2-dev postgresql-client postgresql postgresql-contrib sudo vim zlib1g-dev wget gnupg2 lsb-release apt-get clean -wget -qO - https://apt.stellar.org/SDF.asc | apt-key add - -echo "deb https://apt.stellar.org $(lsb_release -cs) stable" | sudo tee -a /etc/apt/sources.list.d/SDF.list -# echo "deb https://apt.stellar.org $(lsb_release -cs) unstable" | sudo tee -a /etc/apt/sources.list.d/SDF-unstable.list +wget -qO - https://apt.stellar.org/SDF.asc | APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=true sudo apt-key add - +#echo "deb https://apt.stellar.org $(lsb_release -cs) stable" | sudo tee -a /etc/apt/sources.list.d/SDF.list +echo "deb https://apt.stellar.org $(lsb_release -cs) unstable" | sudo tee -a /etc/apt/sources.list.d/SDF-unstable.list apt-get update +echo "apt cache-policy ${apt-cache policy stellar-core}" apt-get install -y stellar-core=${STELLAR_CORE_VERSION} git clone https://github.com/stellar/go.git stellar-go diff --git a/services/horizon/internal/actions/asset_test.go b/services/horizon/internal/actions/asset_test.go index 6547a6b853..6fec5c8412 100644 --- a/services/horizon/internal/actions/asset_test.go +++ b/services/horizon/internal/actions/asset_test.go @@ -156,7 +156,6 @@ func TestAssetStats(t *testing.T) { ClaimableBalancesAmount: "0.0000010", LiquidityPoolsAmount: "0.0000020", ContractsAmount: "0.0000000", - ArchivedContractsAmount: "0.0000000", Asset: base.Asset{ Type: "credit_alphanum4", Code: usdAssetStat.AssetCode, @@ -199,7 +198,6 @@ func TestAssetStats(t *testing.T) { ClaimableBalancesAmount: "0.0000000", LiquidityPoolsAmount: "0.0000000", ContractsAmount: "0.0000000", - ArchivedContractsAmount: "0.0000000", Asset: base.Asset{ Type: "credit_alphanum4", Code: etherAssetStat.AssetCode, @@ -242,7 +240,6 @@ func TestAssetStats(t *testing.T) { ClaimableBalancesAmount: "0.0000000", LiquidityPoolsAmount: "0.0000000", ContractsAmount: "0.0000000", - ArchivedContractsAmount: "0.0000000", Asset: base.Asset{ Type: "credit_alphanum4", Code: otherUSDAssetStat.AssetCode, @@ -287,7 +284,6 @@ func TestAssetStats(t *testing.T) { ClaimableBalancesAmount: "0.0000000", LiquidityPoolsAmount: "0.0000000", ContractsAmount: "0.0000000", - ArchivedContractsAmount: "0.0000000", Asset: base.Asset{ Type: "credit_alphanum4", Code: eurAssetStat.AssetCode, @@ -461,7 +457,6 @@ func TestAssetStatsIssuerDoesNotExist(t *testing.T) { ClaimableBalancesAmount: "0.0000000", LiquidityPoolsAmount: "0.0000000", ContractsAmount: "0.0000000", - ArchivedContractsAmount: "0.0000000", Asset: base.Asset{ Type: "credit_alphanum4", Code: usdAssetStat.AssetCode, diff --git a/services/horizon/internal/db2/history/asset_stats.go b/services/horizon/internal/db2/history/asset_stats.go index 12e910d533..7fab13fb5b 100644 --- a/services/horizon/internal/db2/history/asset_stats.go +++ b/services/horizon/internal/db2/history/asset_stats.go @@ -186,11 +186,11 @@ func (q *Q) UpdateContractAssetBalanceExpirations(ctx context.Context, keys []xd return nil } -// GetContractAssetBalancesExpiringAt returns all contract asset balances which are active +// DeleteContractAssetBalancesExpiringAt deletes and returns all contract asset balances which are active // at `ledger` and expired at `ledger+1` -func (q *Q) GetContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]ContractAssetBalance, error) { - sql := sq.Select("contract_asset_balances.*").From("contract_asset_balances"). - Where(map[string]interface{}{"expiration_ledger": ledger}) +func (q *Q) DeleteContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]ContractAssetBalance, error) { + sql := sq.Delete("contract_asset_balances"). + Where(map[string]interface{}{"expiration_ledger": ledger}).Suffix("RETURNING *") var balances []ContractAssetBalance err := q.Select(ctx, &balances, sql) return balances, err diff --git a/services/horizon/internal/db2/history/asset_stats_test.go b/services/horizon/internal/db2/history/asset_stats_test.go index 520b480fd6..4651e7024d 100644 --- a/services/horizon/internal/db2/history/asset_stats_test.go +++ b/services/horizon/internal/db2/history/asset_stats_test.go @@ -125,28 +125,22 @@ func TestAssetContractStats(t *testing.T) { c1 := ContractAssetStatRow{ ContractID: []byte{1}, Stat: ContractStat{ - ActiveBalance: "100", - ActiveHolders: 2, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "100", + ActiveHolders: 2, }, } c2 := ContractAssetStatRow{ ContractID: []byte{2}, Stat: ContractStat{ - ActiveBalance: "40", - ActiveHolders: 1, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "40", + ActiveHolders: 1, }, } c3 := ContractAssetStatRow{ ContractID: []byte{3}, Stat: ContractStat{ - ActiveBalance: "900", - ActiveHolders: 12, - ArchivedBalance: "23", - ArchivedHolders: 3, + ActiveBalance: "900", + ActiveHolders: 12, }, } @@ -161,7 +155,6 @@ func TestAssetContractStats(t *testing.T) { c2.Stat.ActiveHolders = 3 c2.Stat.ActiveBalance = "20" - c3.Stat.ArchivedBalance = "900" c2.Stat.ActiveHolders = 5 numRows, err := q.UpdateContractAssetStat(tt.Ctx, c2) tt.Assert.NoError(err) @@ -568,10 +561,8 @@ func TestGetAssetStatsFiltersAndCursor(t *testing.T) { q := &Q{tt.HorizonSession()} zero := ContractStat{ - ActiveBalance: "0", - ActiveHolders: 0, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "0", + ActiveHolders: 0, } usdAssetStat := AssetAndContractStat{ ExpAssetStat: ExpAssetStat{ @@ -652,10 +643,8 @@ func TestGetAssetStatsFiltersAndCursor(t *testing.T) { }, }, Contracts: ContractStat{ - ActiveBalance: "120", - ActiveHolders: 3, - ArchivedBalance: "90", - ArchivedHolders: 1, + ActiveBalance: "120", + ActiveHolders: 3, }, } eurAssetStat.SetContractID([32]byte{}) @@ -684,10 +673,8 @@ func TestGetAssetStatsFiltersAndCursor(t *testing.T) { numChanged, err := q.InsertContractAssetStat(tt.Ctx, ContractAssetStatRow{ ContractID: []byte{1}, Stat: ContractStat{ - ActiveBalance: "400", - ActiveHolders: 30, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "400", + ActiveHolders: 30, }, }) tt.Assert.NoError(err) @@ -1141,14 +1128,18 @@ func TestUpdateContractAssetBalanceExpirations(t *testing.T) { q.InsertContractAssetBalances(context.Background(), []ContractAssetBalance{balance, otherBalance}), ) - balances, err := q.GetContractAssetBalancesExpiringAt(context.Background(), 10) + balances, err := q.DeleteContractAssetBalancesExpiringAt(context.Background(), 10) tt.Assert.NoError(err) assertContractAssetBalancesEqual(t, balances, []ContractAssetBalance{balance}) - balances, err = q.GetContractAssetBalancesExpiringAt(context.Background(), 11) + balances, err = q.DeleteContractAssetBalancesExpiringAt(context.Background(), 11) tt.Assert.NoError(err) assertContractAssetBalancesEqual(t, balances, []ContractAssetBalance{otherBalance}) + balances, err = q.GetContractAssetBalances(context.Background(), []xdr.Hash{keyHash, otherKeyHash}) + tt.Assert.NoError(err) + tt.Assert.Empty(balances) + nonExistantKeyHash := xdr.Hash{4} tt.Assert.NoError( @@ -1161,17 +1152,11 @@ func TestUpdateContractAssetBalanceExpirations(t *testing.T) { balances, err = q.GetContractAssetBalances(context.Background(), []xdr.Hash{keyHash, otherKeyHash}) tt.Assert.NoError(err) - balance.ExpirationLedger = 200 - otherBalance.ExpirationLedger = 200 - assertContractAssetBalancesEqual(t, balances, []ContractAssetBalance{balance, otherBalance}) + tt.Assert.Empty(balances) - balances, err = q.GetContractAssetBalancesExpiringAt(context.Background(), 10) + balances, err = q.DeleteContractAssetBalancesExpiringAt(context.Background(), 10) tt.Assert.NoError(err) assert.Empty(t, balances) - balances, err = q.GetContractAssetBalancesExpiringAt(context.Background(), 200) - tt.Assert.NoError(err) - assertContractAssetBalancesEqual(t, balances, []ContractAssetBalance{balance, otherBalance}) - tt.Assert.NoError(q.Rollback()) } diff --git a/services/horizon/internal/db2/history/main.go b/services/horizon/internal/db2/history/main.go index 7bb4516ff0..682119db85 100644 --- a/services/horizon/internal/db2/history/main.go +++ b/services/horizon/internal/db2/history/main.go @@ -376,10 +376,8 @@ type Asset struct { } type ContractStat struct { - ActiveBalance string `json:"balance"` - ActiveHolders int32 `json:"holders"` - ArchivedBalance string `json:"archived_balance"` - ArchivedHolders int32 `json:"archived_holders"` + ActiveBalance string `json:"balance"` + ActiveHolders int32 `json:"holders"` } func (c ContractStat) Value() (driver.Value, error) { @@ -393,7 +391,6 @@ func (c ContractStat) Value() (driver.Value, error) { func (c *ContractStat) Scan(src interface{}) error { if src == nil { c.ActiveBalance = "0" - c.ArchivedBalance = "0" return nil } @@ -411,9 +408,6 @@ func (c *ContractStat) Scan(src interface{}) error { if c.ActiveBalance == "" { c.ActiveBalance = "0" } - if c.ArchivedBalance == "" { - c.ArchivedBalance = "0" - } return nil } @@ -581,7 +575,7 @@ type QAssetStats interface { UpdateContractAssetBalanceAmounts(ctx context.Context, keys []xdr.Hash, amounts []string) error UpdateContractAssetBalanceExpirations(ctx context.Context, keys []xdr.Hash, expirationLedgers []uint32) error GetContractAssetBalances(ctx context.Context, keys []xdr.Hash) ([]ContractAssetBalance, error) - GetContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]ContractAssetBalance, error) + DeleteContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]ContractAssetBalance, error) InsertAssetStats(ctx context.Context, stats []ExpAssetStat) error InsertContractAssetStats(ctx context.Context, rows []ContractAssetStatRow) error InsertAssetStat(ctx context.Context, stat ExpAssetStat) (int64, error) diff --git a/services/horizon/internal/db2/history/mock_q_asset_stats.go b/services/horizon/internal/db2/history/mock_q_asset_stats.go index 84927b53ee..5f63609170 100644 --- a/services/horizon/internal/db2/history/mock_q_asset_stats.go +++ b/services/horizon/internal/db2/history/mock_q_asset_stats.go @@ -39,7 +39,7 @@ func (m *MockQAssetStats) GetContractAssetBalances(ctx context.Context, keys []x return a.Get(0).([]ContractAssetBalance), a.Error(1) } -func (m *MockQAssetStats) GetContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]ContractAssetBalance, error) { +func (m *MockQAssetStats) DeleteContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]ContractAssetBalance, error) { a := m.Called(ctx, ledger) return a.Get(0).([]ContractAssetBalance), a.Error(1) } diff --git a/services/horizon/internal/ingest/db_integration_test.go b/services/horizon/internal/ingest/db_integration_test.go index 0ac6e9d796..4f8db27d5e 100644 --- a/services/horizon/internal/ingest/db_integration_test.go +++ b/services/horizon/internal/ingest/db_integration_test.go @@ -107,7 +107,7 @@ func (s *DBTestSuite) SetupTest() { HistoryArchiveURLs: []string{"http://ignore.test"}, DisableStateVerification: false, CheckpointFrequency: 64, - CoreProtocolVersionFn: func(string) (uint, error) { return 21, nil }, + CoreProtocolVersionFn: func(string) (uint, error) { return 23, nil }, }) s.Assert().NoError(err) s.system = sIface.(*system) diff --git a/services/horizon/internal/ingest/main.go b/services/horizon/internal/ingest/main.go index 8711684e59..e44b96fa2a 100644 --- a/services/horizon/internal/ingest/main.go +++ b/services/horizon/internal/ingest/main.go @@ -64,7 +64,8 @@ const ( // contract data ledger entries. // - 18: Ingest contract asset balances so we can keep track of expired / restore asset // balances for asset stats. - CurrentVersion = 18 + // - 19: Archived contract asset balances are no longer stored in the horizon db. + CurrentVersion = 19 // MaxDBConnections is the size of the postgres connection pool dedicated to Horizon ingestion: // * Ledger ingestion, diff --git a/services/horizon/internal/ingest/main_test.go b/services/horizon/internal/ingest/main_test.go index a736c7b229..6fb28a5519 100644 --- a/services/horizon/internal/ingest/main_test.go +++ b/services/horizon/internal/ingest/main_test.go @@ -95,7 +95,7 @@ func TestNewSystem(t *testing.T) { DisableStateVerification: true, HistoryArchiveURLs: []string{"https://history.stellar.org/prd/core-live/core_live_001"}, CheckpointFrequency: 64, - CoreProtocolVersionFn: func(string) (uint, error) { return 21, nil }, + CoreProtocolVersionFn: func(string) (uint, error) { return 23, nil }, } sIface, err := NewSystem(config) diff --git a/services/horizon/internal/ingest/processor_runner.go b/services/horizon/internal/ingest/processor_runner.go index 3b91b45d58..6f749405b5 100644 --- a/services/horizon/internal/ingest/processor_runner.go +++ b/services/horizon/internal/ingest/processor_runner.go @@ -262,7 +262,16 @@ func (s *ProcessorRunner) runChangeProcessorOnLedger( if err != nil { return errors.Wrap(err, "Error creating ledger change reader") } - changeReader = ingest.NewCompactingChangeReader(changeReader, ingest.ChangeCompactorConfig{SuppressRemoveAfterRestoreChange: false}) + changeReader = ingest.NewCompactingChangeReader( + changeReader, + ingest.ChangeCompactorConfig{ + // The asset stats processor is the only processor which can ingest ledger entry restorations. + // The asset stats processor deletes contract data ledger entries immediately upon expiration. + // So, restores are equivalent to unconditional db insertions and removing after restoration + // is a no-op. + SuppressRemoveAfterRestoreChange: true, + }, + ) if err = streamChanges(s.ctx, changeProcessor, ledger.LedgerSequence(), changeReader); err != nil { return errors.Wrap(err, "Error streaming changes from ledger") diff --git a/services/horizon/internal/ingest/processor_runner_test.go b/services/horizon/internal/ingest/processor_runner_test.go index a4fd59a72b..b4fb264072 100644 --- a/services/horizon/internal/ingest/processor_runner_test.go +++ b/services/horizon/internal/ingest/processor_runner_test.go @@ -3,11 +3,12 @@ package ingest import ( "context" "fmt" - "github.com/stellar/go/amount" "io" "reflect" "testing" + "github.com/stellar/go/amount" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -280,7 +281,7 @@ func TestProcessorRunnerRunAllProcessorsOnLedger(t *testing.T) { Return(nil).Once() q.MockQAssetStats.On("UpdateContractAssetBalanceExpirations", ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - q.MockQAssetStats.On("GetContractAssetBalancesExpiringAt", ctx, uint32(22)). + q.MockQAssetStats.On("DeleteContractAssetBalancesExpiringAt", ctx, uint32(22)). Return([]history.ContractAssetBalance{}, nil).Once() runner := ProcessorRunner{ @@ -373,7 +374,7 @@ func TestProcessorRunnerRunTransactionsProcessorsOnLedgers(t *testing.T) { Return(nil).Once() q.MockQAssetStats.On("UpdateContractAssetBalanceExpirations", ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - q.MockQAssetStats.On("GetContractAssetBalancesExpiringAt", ctx, uint32(22)). + q.MockQAssetStats.On("DeleteContractAssetBalancesExpiringAt", ctx, uint32(22)). Return([]history.ContractAssetBalance{}, nil).Once() runner := ProcessorRunner{ diff --git a/services/horizon/internal/ingest/processors/asset_stats_processor.go b/services/horizon/internal/ingest/processors/asset_stats_processor.go index 7e76b509c2..c04953d570 100644 --- a/services/horizon/internal/ingest/processors/asset_stats_processor.go +++ b/services/horizon/internal/ingest/processors/asset_stats_processor.go @@ -59,6 +59,13 @@ func (p *AssetStatsProcessor) ProcessChange(ctx context.Context, change ingest.C return nil } + // We don't need to handle evictions because we immediately delete contract balances + // upon expiration. So by the time an archived ledger entry is evicted it will already + // be deleted from the horizon DB. + if change.Reason == ingest.LedgerEntryChangeReasonEviction { + return nil + } + var err error switch change.Type { case xdr.LedgerEntryTypeLiquidityPool: @@ -89,7 +96,34 @@ func (p *AssetStatsProcessor) ProcessChange(ctx context.Context, change ingest.C return err } +// AssetStatsProcessor requires that the ttl changes it ingests are already compacted +// because the TTL change semantics in CAP-63 (see +// https://github.com/stellar/stellar-protocol/blob/master/core/cap-0063.md#ttl-ledger-change-semantics ) +// are encapsulated in the ChangeCompactor +func (p *AssetStatsProcessor) checkTTLChangeIsCompacted(change ingest.Change) error { + var keyHash xdr.Hash + if change.Pre != nil { + keyHash = change.Pre.Data.MustTtl().KeyHash + } else { + keyHash = change.Post.Data.MustTtl().KeyHash + } + if _, ok := p.removedExpirationEntries[keyHash]; ok { + return errors.Errorf("ttl change is not compacted Pre: %v Post: %v", change.Pre, change.Post) + } + if _, ok := p.createdExpirationEntries[keyHash]; ok { + return errors.Errorf("ttl change is not compacted Pre: %v Post: %v", change.Pre, change.Post) + } + if _, ok := p.updatedExpirationEntries[keyHash]; ok { + return errors.Errorf("ttl change is not compacted Pre: %v Post: %v", change.Pre, change.Post) + } + return nil +} + func (p *AssetStatsProcessor) addExpirationChange(change ingest.Change) error { + if err := p.checkTTLChangeIsCompacted(change); err != nil { + return err + } + switch { case change.Pre == nil && change.Post != nil: // created post := change.Post.Data.MustTtl() @@ -131,6 +165,7 @@ func (p *AssetStatsProcessor) addExpirationChange(change ingest.Change) error { default: return errors.Errorf("unexpected change Pre: %v Post: %v", change.Pre, change.Post) } + return nil } @@ -213,10 +248,6 @@ func (p *AssetStatsProcessor) updateDB( return errors.Wrap(err, "Error inserting contract asset balances") } - if err := contractAssetStatSet.ingestRestoredBalances(ctx); err != nil { - return err - } - if err := p.updateContractAssetBalanceExpirations(ctx); err != nil { return err } @@ -585,17 +616,11 @@ func (p *AssetStatsProcessor) updateContractID( func (p *AssetStatsProcessor) addContractAssetStat(contractAssetStat assetContractStatValue, row *history.ContractAssetStatRow) error { row.Stat.ActiveHolders += contractAssetStat.activeHolders - row.Stat.ArchivedHolders += contractAssetStat.archivedHolders activeBalance, ok := new(big.Int).SetString(row.Stat.ActiveBalance, 10) if !ok { return errors.New("Error parsing: " + row.Stat.ActiveBalance) } row.Stat.ActiveBalance = activeBalance.Add(activeBalance, contractAssetStat.activeBalance).String() - archivedBalance, ok := new(big.Int).SetString(row.Stat.ArchivedBalance, 10) - if !ok { - return errors.New("Error parsing: " + row.Stat.ArchivedBalance) - } - row.Stat.ArchivedBalance = archivedBalance.Add(archivedBalance, contractAssetStat.archivedBalance).String() return nil } @@ -633,10 +658,8 @@ func (p *AssetStatsProcessor) updateAssetContractStats( } if row.Stat == (history.ContractStat{ - ActiveBalance: "0", - ActiveHolders: 0, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "0", + ActiveHolders: 0, }) { rowsAffected, err = p.assetStatsQ.RemoveAssetContractStat(ctx, contractID[:]) } else { diff --git a/services/horizon/internal/ingest/processors/asset_stats_processor_test.go b/services/horizon/internal/ingest/processors/asset_stats_processor_test.go index ea267f6d3e..dc52d677dd 100644 --- a/services/horizon/internal/ingest/processors/asset_stats_processor_test.go +++ b/services/horizon/internal/ingest/processors/asset_stats_processor_test.go @@ -345,7 +345,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertClaimableBalance() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -508,7 +508,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertTrustLine() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -623,7 +623,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractID() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -663,10 +663,8 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractBalance() { usdAssetContractStat := history.ContractAssetStatRow{ ContractID: usdID[:], Stat: history.ContractStat{ - ActiveBalance: "200", - ActiveHolders: 1, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "200", + ActiveHolders: 1, }, } s.mockQ.On("InsertContractAssetStat", s.ctx, mock.MatchedBy(func(row history.ContractAssetStatRow) bool { @@ -688,7 +686,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractBalance() { }).Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -715,10 +713,8 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractBalance() { usdAssetContractStat := history.ContractAssetStatRow{ ContractID: usdID[:], Stat: history.ContractStat{ - ActiveBalance: "150", - ActiveHolders: 1, - ArchivedBalance: "20", - ArchivedHolders: 2, + ActiveBalance: "150", + ActiveHolders: 1, }, } s.mockQ.On("GetContractAssetStat", s.ctx, usdID[:]). @@ -748,7 +744,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractBalance() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -785,10 +781,8 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractBalance() { usdAssetContractStat := history.ContractAssetStatRow{ ContractID: usdID[:], Stat: history.ContractStat{ - ActiveBalance: "200", - ActiveHolders: 1, - ArchivedHolders: 0, - ArchivedBalance: "0", + ActiveBalance: "200", + ActiveHolders: 1, }, } s.mockQ.On("GetContractAssetStat", s.ctx, usdID[:]). @@ -805,7 +799,51 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractBalance() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + Return([]history.ContractAssetBalance{}, nil).Once() + + s.Assert().NoError(s.processor.Commit(s.ctx)) +} + +func (s *AssetStatsProcessorTestSuiteLedger) TestEviction() { + lastModifiedLedgerSeq := xdr.Uint32(1234) + usdID, err := xdr.MustNewCreditAsset("USD", trustLineIssuer.Address()).ContractID("") + s.Assert().NoError(err) + + s.Assert().NoError(s.processor.ProcessChange(s.ctx, ingest.Change{ + Type: xdr.LedgerEntryTypeContractData, + Reason: ingest.LedgerEntryChangeReasonEviction, + Pre: &xdr.LedgerEntry{ + LastModifiedLedgerSeq: lastModifiedLedgerSeq, + Data: BalanceToContractData(usdID, [32]byte{1}, 200), + }, + })) + + keyHash := getKeyHashForBalance(s.T(), usdID, [32]byte{1}) + s.Assert().NoError(s.processor.ProcessChange(s.ctx, ingest.Change{ + Type: xdr.LedgerEntryTypeTtl, + Reason: ingest.LedgerEntryChangeReasonEviction, + Pre: &xdr.LedgerEntry{ + LastModifiedLedgerSeq: lastModifiedLedgerSeq, + Data: xdr.LedgerEntryData{ + Type: xdr.LedgerEntryTypeTtl, + Ttl: &xdr.TtlEntry{ + KeyHash: keyHash, + LiveUntilLedgerSeq: 2234, + }, + }, + }, + })) + + s.mockQ.On("RemoveContractAssetBalances", s.ctx, []xdr.Hash(nil)). + Return(nil).Once() + s.mockQ.On("UpdateContractAssetBalanceAmounts", s.ctx, []xdr.Hash{}, []string{}). + Return(nil).Once() + s.mockQ.On("InsertContractAssetBalances", s.ctx, []history.ContractAssetBalance(nil)). + Return(nil).Once() + s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). + Return(nil).Once() + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -931,10 +969,8 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractIDWithBalance() { btcAssetContractStat := history.ContractAssetStatRow{ ContractID: btcID[:], Stat: history.ContractStat{ - ActiveBalance: "20", - ActiveHolders: 1, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "20", + ActiveHolders: 1, }, } s.mockQ.On("InsertContractAssetStat", s.ctx, mock.MatchedBy(func(row history.ContractAssetStatRow) bool { @@ -974,7 +1010,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertContractIDWithBalance() { }).Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -1085,7 +1121,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestInsertClaimableBalanceAndTrustl Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -1152,7 +1188,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractID() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -1306,10 +1342,8 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractIDWithBalance() { eurAssetContractStat := history.ContractAssetStatRow{ ContractID: eurID[:], Stat: history.ContractStat{ - ActiveBalance: "10", - ActiveHolders: 2, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "10", + ActiveHolders: 2, }, } s.mockQ.On("GetContractAssetStat", s.ctx, eurID[:]). @@ -1337,7 +1371,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateContractIDWithBalance() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -1639,7 +1673,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateTrustLine() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -1858,7 +1892,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateTrustLineAuthorization() Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -1973,7 +2007,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveClaimableBalance() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -2074,7 +2108,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveTrustLine() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -2127,7 +2161,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractID() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -2227,7 +2261,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestUpdateTrustlineAndRemoveContrac Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -2281,7 +2315,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDFromZeroRow() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -2374,10 +2408,8 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDAndBalanceZeroR eurAssetContractStat := history.ContractAssetStatRow{ ContractID: eurID[:], Stat: history.ContractStat{ - ActiveBalance: "10", - ActiveHolders: 2, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "10", + ActiveHolders: 2, }, } s.mockQ.On("GetContractAssetStat", s.ctx, eurID[:]). @@ -2395,7 +2427,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDAndBalanceZeroR Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -2472,7 +2504,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestRemoveContractIDAndRow() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) @@ -2555,7 +2587,7 @@ func (s *AssetStatsProcessorTestSuiteLedger) TestProcessUpgradeChange() { Return(nil).Once() s.mockQ.On("UpdateContractAssetBalanceExpirations", s.ctx, []xdr.Hash{}, []uint32{}). Return(nil).Once() - s.mockQ.On("GetContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). + s.mockQ.On("DeleteContractAssetBalancesExpiringAt", s.ctx, uint32(1234)). Return([]history.ContractAssetBalance{}, nil).Once() s.Assert().NoError(s.processor.Commit(s.ctx)) diff --git a/services/horizon/internal/ingest/processors/contract_asset_stats.go b/services/horizon/internal/ingest/processors/contract_asset_stats.go index d39ceb6b6d..cf7f45fb75 100644 --- a/services/horizon/internal/ingest/processors/contract_asset_stats.go +++ b/services/horizon/internal/ingest/processors/contract_asset_stats.go @@ -13,28 +13,24 @@ import ( ) type assetContractStatValue struct { - contractID xdr.Hash - activeBalance *big.Int - activeHolders int32 - archivedBalance *big.Int - archivedHolders int32 + contractID xdr.Hash + activeBalance *big.Int + activeHolders int32 } func (v assetContractStatValue) ConvertToHistoryObject() history.ContractAssetStatRow { return history.ContractAssetStatRow{ ContractID: v.contractID[:], Stat: history.ContractStat{ - ActiveBalance: v.activeBalance.String(), - ActiveHolders: v.activeHolders, - ArchivedBalance: v.archivedBalance.String(), - ArchivedHolders: v.archivedHolders, + ActiveBalance: v.activeBalance.String(), + ActiveHolders: v.activeHolders, }, } } type contractAssetBalancesQ interface { GetContractAssetBalances(ctx context.Context, keys []xdr.Hash) ([]history.ContractAssetBalance, error) - GetContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]history.ContractAssetBalance, error) + DeleteContractAssetBalancesExpiringAt(ctx context.Context, ledger uint32) ([]history.ContractAssetBalance, error) } // ContractAssetStatSet represents a collection of asset stats for @@ -155,7 +151,7 @@ func getKeyHash(ledgerEntry xdr.LedgerEntry) (xdr.Hash, error) { func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, change ingest.Change) error { switch { - case change.Pre == nil && change.Post != nil: // created + case change.Pre == nil && change.Post != nil: // created or restored pContractID := change.Post.Data.MustContractData().Contract.ContractId if pContractID == nil { return nil @@ -174,6 +170,22 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c } expirationLedger, ok := s.createdExpirationEntries[keyHash] if !ok { + // when restoring a contract balance which is archived but not yet evicted, + // the restoration meta for the ttl entry will appear like an update because + // it will include the previous state + if expirationUpdate, ok := s.updatedExpirationEntries[keyHash]; ok { + expirationLedger = expirationUpdate[1] + } else { + return nil + } + if expirationLedger < s.currentLedger { + return errors.Errorf( + "contract balance has invalid expiration ledger keyhash %v expiration ledger %v", + keyHash, + expirationLedger, + ) + } + } else if expirationLedger < s.currentLedger { return nil } s.createdBalances = append(s.createdBalances, history.ContractAssetBalance{ @@ -184,13 +196,8 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c }) stat := s.getContractAssetStat(*pContractID) - if expirationLedger >= s.currentLedger { - stat.activeHolders++ - stat.activeBalance.Add(stat.activeBalance, postAmt) - } else { - stat.archivedHolders++ - stat.archivedBalance.Add(stat.archivedBalance, postAmt) - } + stat.activeHolders++ + stat.activeBalance.Add(stat.activeBalance, postAmt) s.maybeAddContractAssetStat(*pContractID, stat) case change.Pre != nil && change.Post == nil: // removed pContractID := change.Pre.Data.MustContractData().Contract.ContractId @@ -217,18 +224,13 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c } expirationLedger, ok := s.removedExpirationEntries[keyHash] - if !ok { + if !ok || expirationLedger < s.currentLedger { return nil } stat := s.getContractAssetStat(*pContractID) - if expirationLedger >= s.currentLedger { - stat.activeHolders-- - stat.activeBalance = new(big.Int).Sub(stat.activeBalance, preAmt) - } else { - stat.archivedHolders-- - stat.archivedBalance = new(big.Int).Sub(stat.archivedBalance, preAmt) - } + stat.activeHolders-- + stat.activeBalance = new(big.Int).Sub(stat.activeBalance, preAmt) s.maybeAddContractAssetStat(*pContractID, stat) case change.Pre != nil && change.Post != nil: // updated pContractID := change.Pre.Data.MustContractData().Contract.ContractId @@ -280,24 +282,19 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c preExpiration = rows[0].ExpirationLedger postExpiration = preExpiration } - if postExpiration < s.currentLedger { - return errors.Errorf( - "contract balance has invalid expiration ledger keyhash %v expiration ledger %v", - keyHash, - postExpiration, - ) + for _, expiration := range []uint32{preExpiration, postExpiration} { + if expiration < s.currentLedger { + return errors.Errorf( + "contract balance has invalid expiration ledger keyhash %v expiration ledger %v", + keyHash, + expiration, + ) + } } s.updatedBalances[keyHash] = postAmt stat := s.getContractAssetStat(*pContractID) - if preExpiration+1 >= s.currentLedger { // active balance was updated - stat.activeBalance.Add(stat.activeBalance, amtDelta) - } else { // balance was restored - stat.activeHolders++ - stat.archivedHolders-- - stat.activeBalance.Add(stat.activeBalance, postAmt) - stat.archivedBalance.Sub(stat.archivedBalance, amt) - } + stat.activeBalance.Add(stat.activeBalance, amtDelta) s.maybeAddContractAssetStat(*pContractID, stat) default: return errors.Errorf("unexpected change Pre: %v Post: %v", change.Pre, change.Post) @@ -305,53 +302,8 @@ func (s *ContractAssetStatSet) ingestContractAssetBalance(ctx context.Context, c return nil } -func (s *ContractAssetStatSet) ingestRestoredBalances(ctx context.Context) error { - var keyHashes []xdr.Hash - for keyHash, expirationUpdate := range s.updatedExpirationEntries { - prevExpirationLedger := expirationUpdate[0] - // prevExpirationLedger+1 >= s.currentLedger indicates that this contract balance is still - // active in our DB and therefore don't need to restore it. - // s.updatedBalances[keyHash] != nil indicates that this contract balance was already ingested - // in ingestContractAssetBalance() so we don't need to ingest it again here. - if prevExpirationLedger+1 >= s.currentLedger || s.updatedBalances[keyHash] != nil { - continue - } - keyHashes = append(keyHashes, keyHash) - } - if len(keyHashes) == 0 { - return nil - } - - rows, err := s.assetStatsQ.GetContractAssetBalances(ctx, keyHashes) - if err != nil { - return errors.Wrap(err, "Error fetching contract asset balances") - } - - for _, row := range rows { - var contractID xdr.Hash - copy(contractID[:], row.ContractID) - stat := s.getContractAssetStat(contractID) - amt, ok := new(big.Int).SetString(row.Amount, 10) - if !ok { - return errors.Errorf( - "contract balance %v has invalid amount: %v", - row.KeyHash, - row.Amount, - ) - } - - stat.activeHolders++ - stat.activeBalance.Add(stat.activeBalance, amt) - stat.archivedHolders-- - stat.archivedBalance.Sub(stat.archivedBalance, amt) - s.maybeAddContractAssetStat(contractID, stat) - } - - return nil -} - func (s *ContractAssetStatSet) ingestExpiredBalances(ctx context.Context) error { - rows, err := s.assetStatsQ.GetContractAssetBalancesExpiringAt(ctx, s.currentLedger-1) + rows, err := s.assetStatsQ.DeleteContractAssetBalancesExpiringAt(ctx, s.currentLedger-1) if err != nil { return errors.Wrap(err, "Error fetching contract asset balances") } @@ -379,8 +331,6 @@ func (s *ContractAssetStatSet) ingestExpiredBalances(ctx context.Context) error stat.activeHolders-- stat.activeBalance.Sub(stat.activeBalance, amt) - stat.archivedHolders++ - stat.archivedBalance.Add(stat.archivedBalance, amt) s.maybeAddContractAssetStat(contractID, stat) } @@ -388,9 +338,8 @@ func (s *ContractAssetStatSet) ingestExpiredBalances(ctx context.Context) error } func (s *ContractAssetStatSet) maybeAddContractAssetStat(contractID xdr.Hash, stat assetContractStatValue) { - if stat.archivedHolders == 0 && stat.activeHolders == 0 && - stat.activeBalance.Cmp(big.NewInt(0)) == 0 && - stat.archivedBalance.Cmp(big.NewInt(0)) == 0 { + if stat.activeHolders == 0 && + stat.activeBalance.Cmp(big.NewInt(0)) == 0 { delete(s.contractAssetStats, contractID) } else { s.contractAssetStats[contractID] = stat @@ -401,11 +350,9 @@ func (s *ContractAssetStatSet) getContractAssetStat(contractID xdr.Hash) assetCo stat, ok := s.contractAssetStats[contractID] if !ok { stat = assetContractStatValue{ - contractID: contractID, - activeBalance: big.NewInt(0), - activeHolders: 0, - archivedBalance: big.NewInt(0), - archivedHolders: 0, + contractID: contractID, + activeBalance: big.NewInt(0), + activeHolders: 0, } } return stat diff --git a/services/horizon/internal/ingest/processors/contract_asset_stats_test.go b/services/horizon/internal/ingest/processors/contract_asset_stats_test.go index ce89920ecd..dde3a73bef 100644 --- a/services/horizon/internal/ingest/processors/contract_asset_stats_test.go +++ b/services/horizon/internal/ingest/processors/contract_asset_stats_test.go @@ -3,11 +3,9 @@ package processors import ( "context" "crypto/sha256" - "math/big" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stellar/go/ingest" "github.com/stellar/go/keypair" @@ -108,7 +106,7 @@ func TestAddContractData(t *testing.T) { assert.NoError(t, err) otherEtherBalanceKeyHash := getKeyHashForBalance(t, etherID, [32]byte{1}) - set.createdExpirationEntries[otherEtherBalanceKeyHash] = 150 + set.updatedExpirationEntries[otherEtherBalanceKeyHash] = [2]uint32{100, 150} err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, Post: &xdr.LedgerEntry{ @@ -138,6 +136,16 @@ func TestAddContractData(t *testing.T) { }) assert.NoError(t, err) + invalidBTCBalanceKeyHash := getKeyHashForBalance(t, btcID, [32]byte{3}) + set.updatedExpirationEntries[invalidBTCBalanceKeyHash] = [2]uint32{100, 120} + err = set.AddContractData(context.Background(), ingest.Change{ + Type: xdr.LedgerEntryTypeContractData, + Post: &xdr.LedgerEntry{ + Data: BalanceToContractData(btcID, [32]byte{3}, 90), + }, + }) + assert.ErrorContains(t, err, "contract balance has invalid expiration ledger") + assert.Empty(t, set.updatedBalances) assert.Empty(t, set.removedBalances) assert.Len(t, set.contractToAsset, 2) @@ -150,12 +158,6 @@ func TestAddContractData(t *testing.T) { Amount: "0", ExpirationLedger: 150, }, - { - KeyHash: etherBalanceKeyHash[:], - ContractID: etherID[:], - Amount: "50", - ExpirationLedger: 100, - }, { KeyHash: otherEtherBalanceKeyHash[:], ContractID: etherID[:], @@ -167,19 +169,15 @@ func TestAddContractData(t *testing.T) { { ContractID: uniID[:], Stat: history.ContractStat{ - ActiveBalance: "0", - ArchivedBalance: "0", - ActiveHolders: 1, - ArchivedHolders: 0, + ActiveBalance: "0", + ActiveHolders: 1, }, }, { ContractID: etherID[:], Stat: history.ContractStat{ - ActiveBalance: "150", - ArchivedBalance: "50", - ActiveHolders: 1, - ArchivedHolders: 1, + ActiveBalance: "150", + ActiveHolders: 1, }, }, }) @@ -357,7 +355,7 @@ func TestUpdateContractBalance(t *testing.T) { assert.ErrorContains(t, err, "contract balance has invalid expiration ledger keyhash") keyHash = getKeyHashForBalance(t, uniID, [32]byte{4}) - set.updatedExpirationEntries[keyHash] = [2]uint32{100, 170} + set.updatedExpirationEntries[keyHash] = [2]uint32{150, 170} expectedBalances[keyHash] = "75" err = set.AddContractData(context.Background(), ingest.Change{ Type: xdr.LedgerEntryTypeContractData, @@ -379,32 +377,27 @@ func TestUpdateContractBalance(t *testing.T) { } assert.Empty(t, expectedBalances) - assert.ElementsMatch(t, set.GetContractStats(), []history.ContractAssetStatRow{ + result := set.GetContractStats() + assert.ElementsMatch(t, result, []history.ContractAssetStatRow{ { ContractID: usdcID[:], Stat: history.ContractStat{ - ActiveBalance: "120", - ActiveHolders: 0, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "120", + ActiveHolders: 0, }, }, { ContractID: etherID[:], Stat: history.ContractStat{ - ActiveBalance: "-150", - ActiveHolders: 0, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "-150", + ActiveHolders: 0, }, }, { ContractID: uniID[:], Stat: history.ContractStat{ - ActiveBalance: "75", - ActiveHolders: 1, - ArchivedBalance: "-50", - ArchivedHolders: -1, + ActiveBalance: "25", + ActiveHolders: 0, }, }, }) @@ -476,86 +469,13 @@ func TestRemoveContractData(t *testing.T) { { ContractID: usdcID[:], Stat: history.ContractStat{ - ActiveBalance: "-50", - ActiveHolders: -1, - ArchivedBalance: "-20", - ArchivedHolders: -1, + ActiveBalance: "-50", + ActiveHolders: -1, }, }, }) } -func TestIngestRestoredBalances(t *testing.T) { - usdcIssuer := keypair.MustRandom().Address() - usdcAsset := xdr.MustNewCreditAsset("USDC", usdcIssuer) - usdcID, err := usdcAsset.ContractID("passphrase") - assert.NoError(t, err) - - mockQ := &history.MockQAssetStats{} - set := NewContractAssetStatSet( - mockQ, - "passphrase", - map[xdr.Hash]uint32{}, - map[xdr.Hash]uint32{}, - map[xdr.Hash][2]uint32{}, - 150, - ) - - usdcKeyHash := getKeyHashForBalance(t, usdcID, [32]byte{}) - set.updatedBalances[usdcKeyHash] = big.NewInt(190) - set.updatedExpirationEntries[usdcKeyHash] = [2]uint32{120, 170} - - usdcKeyHash1 := getKeyHashForBalance(t, usdcID, [32]byte{1}) - set.updatedExpirationEntries[usdcKeyHash1] = [2]uint32{149, 190} - - usdcKeyHash2 := getKeyHashForBalance(t, usdcID, [32]byte{2}) - set.updatedExpirationEntries[usdcKeyHash2] = [2]uint32{100, 200} - - usdcKeyHash3 := getKeyHashForBalance(t, usdcID, [32]byte{3}) - set.updatedExpirationEntries[usdcKeyHash3] = [2]uint32{150, 210} - - usdcKeyHash4 := getKeyHashForBalance(t, usdcID, [32]byte{4}) - set.updatedExpirationEntries[usdcKeyHash4] = [2]uint32{170, 900} - - usdcKeyHash5 := getKeyHashForBalance(t, usdcID, [32]byte{5}) - set.updatedExpirationEntries[usdcKeyHash5] = [2]uint32{120, 600} - - ctx := context.Background() - - mockQ.On("GetContractAssetBalances", ctx, mock.MatchedBy(func(keys []xdr.Hash) bool { - return assert.ElementsMatch(t, []xdr.Hash{usdcKeyHash2, usdcKeyHash5}, keys) - })). - Return([]history.ContractAssetBalance{ - { - KeyHash: usdcKeyHash2[:], - ContractID: usdcID[:], - Amount: "67", - ExpirationLedger: 100, - }, - { - KeyHash: usdcKeyHash5[:], - ContractID: usdcID[:], - Amount: "200", - ExpirationLedger: 120, - }, - }, nil).Once() - - assert.NoError(t, set.ingestRestoredBalances(ctx)) - assert.ElementsMatch(t, set.GetContractStats(), []history.ContractAssetStatRow{ - { - ContractID: usdcID[:], - Stat: history.ContractStat{ - ActiveBalance: "267", - ActiveHolders: 2, - ArchivedBalance: "-267", - ArchivedHolders: -2, - }, - }, - }) - - mockQ.AssertExpectations(t) -} - func TestIngestExpiredBalances(t *testing.T) { usdcIssuer := keypair.MustRandom().Address() usdcAsset := xdr.MustNewCreditAsset("USDC", usdcIssuer) @@ -583,7 +503,7 @@ func TestIngestExpiredBalances(t *testing.T) { ethKeyHash1 := getKeyHashForBalance(t, etherID, [32]byte{1}) set.updatedExpirationEntries[ethKeyHash1] = [2]uint32{149, 180} ctx := context.Background() - mockQ.On("GetContractAssetBalancesExpiringAt", ctx, set.currentLedger-1). + mockQ.On("DeleteContractAssetBalancesExpiringAt", ctx, set.currentLedger-1). Return([]history.ContractAssetBalance{ { KeyHash: usdcKeyHash[:], @@ -616,19 +536,15 @@ func TestIngestExpiredBalances(t *testing.T) { { ContractID: usdcID[:], Stat: history.ContractStat{ - ActiveBalance: "-267", - ActiveHolders: -2, - ArchivedBalance: "267", - ArchivedHolders: 2, + ActiveBalance: "-267", + ActiveHolders: -2, }, }, { ContractID: etherID[:], Stat: history.ContractStat{ - ActiveBalance: "-8", - ActiveHolders: -1, - ArchivedBalance: "8", - ArchivedHolders: 1, + ActiveBalance: "-8", + ActiveHolders: -1, }, }, }) diff --git a/services/horizon/internal/ingest/verify.go b/services/horizon/internal/ingest/verify.go index 294acd2a51..d8f8143eb9 100644 --- a/services/horizon/internal/ingest/verify.go +++ b/services/horizon/internal/ingest/verify.go @@ -30,7 +30,7 @@ const assetStatsBatchSize = 500 // check them. // There is a test that checks it, to fix it: update the actual `verifyState` // method instead of just updating this value! -const stateVerifierExpectedIngestionVersion = 18 +const stateVerifierExpectedIngestionVersion = 19 // verifyState is called as a go routine from pipeline post hook every 64 // ledgers. It checks if the state is correct. If another go routine is already @@ -482,10 +482,8 @@ func checkAssetStats( entry = history.ContractAssetStatRow{ ContractID: contractID[:], Stat: history.ContractStat{ - ActiveBalance: "0", - ActiveHolders: 0, - ArchivedBalance: "0", - ArchivedHolders: 0, + ActiveBalance: "0", + ActiveHolders: 0, }, } } diff --git a/services/horizon/internal/integration/sac_test.go b/services/horizon/internal/integration/sac_test.go index 19f27d47ac..79295538d1 100644 --- a/services/horizon/internal/integration/sac_test.go +++ b/services/horizon/internal/integration/sac_test.go @@ -1120,9 +1120,7 @@ func assertAssetStats(itest *integration.Test, expected assetStats) { assert.Equal(itest.CurrentTest(), expected.issuer, asset.Issuer) assert.Equal(itest.CurrentTest(), expected.numAccounts, asset.Accounts.Authorized) assert.Equal(itest.CurrentTest(), expected.numContracts, asset.NumContracts) - assert.Equal(itest.CurrentTest(), expected.numArchivedContracts, asset.NumArchivedContracts) assert.Equal(itest.CurrentTest(), expected.balanceContracts.String(), parseBalance(itest, asset.ContractsAmount).String()) - assert.Equal(itest.CurrentTest(), expected.balanceArchivedContracts.String(), parseBalance(itest, asset.ArchivedContractsAmount).String()) assert.Equal(itest.CurrentTest(), strkey.MustEncode(strkey.VersionByteContract, expected.contractID[:]), asset.ContractID) } diff --git a/services/horizon/internal/resourceadapter/asset_stat.go b/services/horizon/internal/resourceadapter/asset_stat.go index 8cc2c12740..a9d6c314aa 100644 --- a/services/horizon/internal/resourceadapter/asset_stat.go +++ b/services/horizon/internal/resourceadapter/asset_stat.go @@ -38,7 +38,6 @@ func PopulateAssetStat( res.NumClaimableBalances = row.Accounts.ClaimableBalances res.NumLiquidityPools = row.Accounts.LiquidityPools res.NumContracts = row.Contracts.ActiveHolders - res.NumArchivedContracts = row.Contracts.ArchivedHolders err = populateAssetStatBalances(res, row) if err != nil { return err @@ -96,10 +95,5 @@ func populateAssetStatBalances(res *protocol.AssetStat, row history.AssetAndCont return errors.Wrapf(err, "Invalid amount in PopulateAssetStatBalances: %q", row.Contracts.ActiveBalance) } - res.ArchivedContractsAmount, err = amount.IntStringToAmount(row.Contracts.ArchivedBalance) - if err != nil { - return errors.Wrapf(err, "Invalid amount in PopulateAssetStatBalances: %q", row.Contracts.ArchivedBalance) - } - return nil } diff --git a/services/horizon/internal/resourceadapter/asset_stat_test.go b/services/horizon/internal/resourceadapter/asset_stat_test.go index cd3209414d..492c43c821 100644 --- a/services/horizon/internal/resourceadapter/asset_stat_test.go +++ b/services/horizon/internal/resourceadapter/asset_stat_test.go @@ -33,10 +33,8 @@ func TestPopulateExpAssetStat(t *testing.T) { }, }, Contracts: history.ContractStat{ - ActiveBalance: "900000000000000000", - ActiveHolders: 6, - ArchivedBalance: "700000000000000000", - ArchivedHolders: 3, + ActiveBalance: "900000000000000000", + ActiveHolders: 6, }, } issuer := history.AccountEntry{ @@ -57,14 +55,12 @@ func TestPopulateExpAssetStat(t *testing.T) { assert.Equal(t, int32(107), res.Accounts.Unauthorized) assert.Equal(t, int32(12), res.NumClaimableBalances) assert.Equal(t, int32(6), res.NumContracts) - assert.Equal(t, int32(3), res.NumArchivedContracts) assert.Equal(t, "10000000000000.0000000", res.Balances.Authorized) assert.Equal(t, "5000000000000.0000000", res.Balances.AuthorizedToMaintainLiabilities) assert.Equal(t, "250000000000.0000000", res.Balances.Unauthorized) assert.Equal(t, "120000000000.0000000", res.ClaimableBalancesAmount) assert.Equal(t, "770000000000.0000000", res.LiquidityPoolsAmount) assert.Equal(t, "90000000000.0000000", res.ContractsAmount) - assert.Equal(t, "70000000000.0000000", res.ArchivedContractsAmount) assert.Equal(t, horizon.AccountFlags{}, res.Flags) assert.Equal(t, "https://xim.com/.well-known/stellar.toml", res.Links.Toml.Href) assert.Equal(t, "", res.ContractID)