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

Add mpl-core account indexing #178

Merged
merged 11 commits into from
Mar 27, 2024
Merged

Add mpl-core account indexing #178

merged 11 commits into from
Mar 27, 2024

Conversation

danenbm
Copy link
Contributor

@danenbm danenbm commented Mar 25, 2024

Notes

  • This indexes mpl-core Assets and Collections using account updates.
  • Two new interfaces were created: MplCoreAsset and MplCoreCollection to deal with fact that they are slightly different types and must be selectable by search_assets.
  • Added integration tests:
    • getAsset for an mpl-core Asset.
    • getAsset for an mpl-core Collection.
    • getAssetsByAuthority
    • getAssetsByGroup
    • getAssetsByOwner

There's a couple scenarios which are hard to write integ tests for. These scenarios are described below and were tested locally with the Docker container:

  • Burned assets, which in the case of mpl-core, become 1-byte accounts but are never closed.
  • Assets with mpl-core plugins that are unknown to DAS.
    • This is an anticipated case where the mpl-core contract is updated to have new plugins, but DAS has not been updated with the latest mpl-core Rust client.
    • Rather than allowing DAS to fail, the output goes into an "unknown_plugins" section and is output as base64, which would allow users to parse it if they have an updated JS client. See below for an example.
    • If the RPC provider wishes to later backfill assets that previously had uknown plugins, it can be done by selecting accounts where the unknown_plugins column is not NULL.

Related blockbuster PR

metaplex-foundation/blockbuster#41

Example output for an mpl-core getAsset with known and unknown plugins:

{
   "id" : 0,
   "jsonrpc" : "2.0",
   "result" : {
      "authorities" : [
         {
            "address" : "HBgemgDSTDJaKbkUJMabmUNrnPQcaovn9Vw4phJMiDMh",
            "scopes" : [
               "full"
            ]
         }
      ],
      "burnt" : false,
      "compression" : {
         "asset_hash" : "",
         "compressed" : false,
         "creator_hash" : "",
         "data_hash" : "",
         "eligible" : false,
         "leaf_id" : 0,
         "seq" : 0,
         "tree" : ""
      },
      "content" : {
         "$schema" : "https://schema.metaplex.com/nft1.0.json",
         "files" : [],
         "json_uri" : "https://example.com/asset",
         "links" : {},
         "metadata" : {
            "name" : "Test Asset",
            "symbol" : ""
         }
      },
      "creators" : [
         {
            "address" : "HBgemgDSTDJaKbkUJMabmUNrnPQcaovn9Vw4phJMiDMh",
            "share" : 100,
            "verified" : true
         }
      ],
      "grouping" : [
         {
            "group_key" : "collection",
            "group_value" : "5u2cpYZJYsL9gSCoLFAxTKpZTsCiyX6sJQKsRAr6epbL"
         }
      ],
      "id" : "315pzQSeaE6AQEtqVaFqR4mzSHxZSqXePVjQjWYKLWsW",
      "interface" : "MplCoreAsset",
      "mpl_core_info" : {
         "plugins_json_version" : 1
      },
      "mutable" : true,
      "ownership" : {
         "delegate" : "HBgemgDSTDJaKbkUJMabmUNrnPQcaovn9Vw4phJMiDMh",
         "delegated" : true,
         "frozen" : false,
         "owner" : "HBgemgDSTDJaKbkUJMabmUNrnPQcaovn9Vw4phJMiDMh",
         "ownership_model" : "single"
      },
      "plugins" : {
         "freeze_delegate" : {
            "authority" : {
               "address" : "HBgemgDSTDJaKbkUJMabmUNrnPQcaovn9Vw4phJMiDMh",
               "type" : "Address"
            },
            "data" : {
               "frozen" : false
            },
            "index" : 1,
            "offset" : 160
         },
         "royalties" : {
            "authority" : {
               "address" : null,
               "type" : "UpdateAuthority"
            },
            "data" : {
               "basis_points" : 5,
               "creators" : [
                  {
                     "address" : "HBgemgDSTDJaKbkUJMabmUNrnPQcaovn9Vw4phJMiDMh",
                     "percentage" : 100
                  }
               ],
               "rule_set" : "None"
            },
            "index" : 0,
            "offset" : 119
         },
         "transfer_delegate" : {
            "authority" : {
               "address" : null,
               "type" : "Owner"
            },
            "data" : {},
            "index" : 2,
            "offset" : 162
         }
      },
      "royalty" : {
         "basis_points" : 5,
         "locked" : false,
         "percent" : 0.0005,
         "primary_sale_happened" : false,
         "royalty_model" : "creators",
         "target" : null
      },
      "supply" : null,
      "unknown_plugins" : [
         {
            "authority" : {
               "address" : "HBgemgDSTDJaKbkUJMabmUNrnPQcaovn9Vw4phJMiDMh",
               "type" : "Address"
            },
            "data" : "CQA=",
            "index" : 3,
            "offset" : 163,
            "type" : 9
         }
      ]
   }
}

@danenbm danenbm marked this pull request as ready for review March 26, 2024 01:28
@@ -95,6 +96,11 @@ pub struct AssetMetadataAccountColumns {
pub royalty_amount: i32,
pub asset_data: Option<Vec<u8>>,
pub slot_updated_metadata_account: u64,
pub plugins: Option<Value>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might be cleaner to store in a different table?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I could do that, its probably better then prefixing them with mpl_core_ in the main asset table.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Tough balance between normalization and query speed. Right now though the model is complex the read times are quick because its as simple index lookup. I'd like to keep that read speed.

The interface on the asset maps the plugins to a specific protocol if another pluggable asset type is creating in the future.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am also happy keeping the new columns in the asset table. I do think it its better for the query speed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did draft it up using separate mpl_core table here: #180

As you guys have pointed out it will add the extra queries so we will skip it.

@danenbm danenbm force-pushed the danenbm/add-mpl-core branch from cdd6a48 to 2269810 Compare March 27, 2024 10:03
@danenbm danenbm merged commit 2627055 into main Mar 27, 2024
3 checks passed
@danenbm danenbm deleted the danenbm/add-mpl-core branch March 27, 2024 10:17
let update_authority = match asset.update_authority {
UpdateAuthority::Address(address) => address.to_bytes().to_vec(),
UpdateAuthority::Collection(address) => {
find_collection_authority(conn, address.to_bytes().to_vec(), RETRY_INTERVALS)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I find the retry logic might not be a good idea here. We may slow down the indexing while retrying to index the account. Isn't this going to result in a race or may be a different state if collection authority isn't indexed yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes it could slow down indexing, but honestly not sure how to deal with it other than a retry. This approach is similar to what token metadata indexing uses to look for the token.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In real life, for mpl-core the collection will have to exist before the assets are minted to the collection or if the asset is added to the collection, so this should not fail as long as concurrent process indexing got the collection.

.one(conn)
.await?;

for interval in retry_intervals {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this loop be at the beginning? Seems like there's a sleep without actually fetching the authority after waking up. This will result in hitting the max retry every time

Copy link
Contributor Author

@danenbm danenbm Mar 28, 2024

Choose a reason for hiding this comment

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

You are right I messed up this logic when copying it over from toke metadata and changing it. I think I was trying to make it more like a do-while but didn't complete it properly. Will fix now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in #181

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.

4 participants