Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

services/horizon: Include contract asset balances in asset stats #4805

Merged
merged 12 commits into from
Mar 16, 2023

Conversation

tamirms
Copy link
Contributor

@tamirms tamirms commented Mar 10, 2023

PR Checklist

PR Structure

  • This PR has reasonably narrow scope (if not, break it down into smaller PRs).
  • This PR avoids mixing refactoring changes with feature changes (split into two PRs
    otherwise).
  • This PR's title starts with name of package that is most changed in the PR, ex.
    services/friendbot, or all or doc if the changes are broad or impact many
    packages.

Thoroughness

  • This PR adds tests for the most critical parts of the new functionality or fixes.
  • I've updated any docs (developer docs, .md
    files, etc... affected by this change). Take a look in the docs folder for a given service,
    like this one.

Release planning

  • I've updated the relevant CHANGELOG (here for Horizon) if
    needed with deprecations, added features, breaking changes, and DB schema changes.
  • I've decided if this PR requires a new major/minor version according to
    semver, or if it's mainly a patch change. The PR is targeted at the next
    release branch if it's not a patch change.

What

This PR a follow up to #4782 where we update asset stats ingestion to keep track of how many contracts hold an asset and the amount of an asset held by all contracts in total.

This is implemented by ingesting balance contract data ledger entries which are updated by the stellar asset contract. The balance contract data ledger entries are like trustlines but for contract holders of an asset.

Why

This PR is required to implement #4727

Known limitations

We cannot directly identify if a balance contract data ledger entry belongs to a genuine stellar asset contract. So we work around this by querying the asset stats table by contract id. If there is a row present then we know that the balance contract data ledger entry corresponds to the stellar asset contract. Otherwise, we ignore the ledger entry change in ingestion.

If the extra db load becomes a problem we could think about having a something like a bloom filter which we can use to discard ledger entries from non stellar asset contracts that mimic the stellar asset contract balance format.

@tamirms tamirms marked this pull request as ready for review March 13, 2023 08:59
@tamirms tamirms requested a review from a team March 13, 2023 09:32
// Action needed in release: horizon-v3.0.0: deprecated field
Amount string `json:"amount"`
Accounts AssetStatAccounts `json:"accounts"`
ClaimableBalancesAmount string `json:"claimable_balances_amount"`
LiquidityPoolsAmount string `json:"liquidity_pools_amount"`
ContractsAmount string `json:"contracts_amount"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the total amount of assets in all the contracts? Then I would rename it to something like AllContractsAmount or ContractsTotalAmount or ContractsAmountSum

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the amount of the given, specific asset that is stored w/in its SAC contract. Or does it go beyond the SAC, @tamirms?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@2opremio is correct that is the total amount held by all smart contracts. I agree that renaming it would be better but then it would be inconsistent with liquidity_pools_amount and claimable_balances_amount. We could rename all the fields but that would be a breaking change

// Action needed in release: horizon-v3.0.0: deprecated field
Amount string `json:"amount"`
Accounts AssetStatAccounts `json:"accounts"`
ClaimableBalancesAmount string `json:"claimable_balances_amount"`
LiquidityPoolsAmount string `json:"liquidity_pools_amount"`
ContractsAmount string `json:"contracts_amount"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the amount of the given, specific asset that is stored w/in its SAC contract. Or does it go beyond the SAC, @tamirms?

Comment on lines 235 to 239
amount := amountObj.MustI128()
// check if negative
if ((amount.Hi >> 56) & 0x80) > 0 {
return [32]byte{}, nil, false
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😳 is there a test for this? why 56?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also check that Hi or Lo fits in an int64?

Copy link
Contributor

@sreuland sreuland Mar 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondered similar, for balances,Hi is just holding sign extension bits for Lo as uint64? just the most significant bit in Hi could be checked?

services/horizon/internal/ingest/verify.go Show resolved Hide resolved
services/horizon/internal/integration/sac_test.go Outdated Show resolved Hide resolved
Copy link
Contributor

@tsachiherman tsachiherman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few comments.. but looks good.

if obj.Type != xdr.ScObjectTypeScoBytes {
return nil
}
var err error
assetIssuer, err = strkey.Encode(strkey.VersionByteAccountID, obj.MustBin())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is wrong :
if objMjustBin() is nil ,then you shoudl return nil.
however, if strkey.Encode returns an error, you should return this error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the issuer byte array is invalid then this is an indication that it's not coming from the stellar asset contract which is why I return nil. But you're right that I should check that obj.MustBin() is not nill

@tamirms
Copy link
Contributor Author

tamirms commented Mar 14, 2023

@stellar/horizon-committers PTAL I think I've addressed all the code review feedback

Copy link
Contributor

@sreuland sreuland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure my approval is enough, other reviewers had some conversations in flight still, but wanted to check off, thanks for all clarifications.

Copy link
Contributor

@Shaptic Shaptic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple comments around subtleties in i128 but otherwise I think it LGTM!

return [32]byte{}, nil, false
}
amt := new(big.Int).Lsh(big.NewInt(int64(amount.Hi)), 64)
amt.Add(amt, big.NewInt(int64(amount.Lo)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my discussion on Slack here, I think int64(amount.Lo) is not safe. You should do big.NewInt(0).SetUint64(amount.Lo), instead, because only the upper half contains info about the overall sign of amount. Otherwise this will give you a negative value if amount > MaxInt64

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a great catch. I think there is a bug here. I will fix this and update the tests to catch this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general, we should probably shove this into a library wrapper around Int128Parts as these conversions/math can easily be a huge source of bugs down the line

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made #4809 to track this for the future.

Copy link
Contributor

@Shaptic Shaptic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, awesome work 👏

@tamirms tamirms merged commit cd3a221 into stellar:soroban-xdr-next Mar 16, 2023
@tamirms tamirms deleted the asset-stat-contract-balance branch March 16, 2023 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants