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 block indexing to Indexer API #1606

Merged
merged 22 commits into from
Oct 3, 2024
Merged

Add block indexing to Indexer API #1606

merged 22 commits into from
Oct 3, 2024

Conversation

aaronbuchwald
Copy link
Collaborator

This PR adds block indexing to the indexer API.

The new API serves both the block JSON and the ExecutedBlock bytes to support both #1513 and decoding the response in the golang indexer client.

We can't unmarshal the block's JSON as is because it requires unmarshalling into the Action and Auth interfaces within the transaction. This PR includes both the JSON and binary in the response to support both cases.

@aaronbuchwald aaronbuchwald self-assigned this Sep 30, 2024
@aaronbuchwald aaronbuchwald marked this pull request as ready for review September 30, 2024 14:43
Comment on lines +56 to +59
type GetBlockResponse struct {
Block *chain.ExecutedBlock `json:"block"`
BlockBytes codec.Bytes `json:"blockBytes"`
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Alternative here is to include the requested encoding in the request

Copy link
Contributor

Choose a reason for hiding this comment

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

It might create a mess. There aren't that many methods. It's fine to duplicate unless we plan to increase the number of methods.

@aaronbuchwald aaronbuchwald changed the base branch from executed-block to main October 2, 2024 01:21
@containerman17
Copy link
Contributor

containerman17 commented Oct 2, 2024

1. GetBlock improvements

{
    "jsonrpc": "2.0",
    "result": {
        "block": {
            "blockID": "2NmFKR118wRQc88qgmgeLqjz125UWxRb7jsv9nFNj6ygfedKyL",
            "block": {
                "parent": "qsFoFWSJkFgXgtWmh8MtyVgdTtzv77qNyjJPB7Fc43UXeUUKj",
                "timestamp": 1727846356939,
                "height": 3420,
                "txs": [
                    {
                        // 1.4 Transaction ID  
                        "base": {
                            "timestamp": 1727846413000,
                            "chainId": "2gdnF9MkpFFBsmFQHSpDrnXT5N3DGKXPmBJfradUyE3kzHc4QE",
                            "maxFee": 10000000
                        },
                        "actions": [
                            {// 1.2 Action names or IDs
                                "to": "0x0000000000000000000000000000000000000000000000000000000000deadc0de",
                                "value": 123456789,
                                "memo": "THVpZ2k="
                            }
                        ],
                        "auth": {
                            "signer": [ // 1.3 Auth.* should be hex
                                70,
                                220,
                                217,
                                //... more bytes here
                            ],
                            "signature": [ // 1.3 Auth.* should be hex
                                103,
                                151,
                                155,
                                //... more bytes here
                            ]
                        }
                    }
                ],
                "stateRoot": "28TpB5nFDvBYZWfEZesFpDA8zLic4MnXYwniEFDKvpkWkiBKYD"
            },
            "results": [
                { // 1.6 Result fields should be lowercase
                    "Success": true,
                    "Error": "",// 1.5 Transaction error should be a Unicode string
                    "Outputs": [
                        "aW52YWxpZCBiYWxhbmNlOiBjb...."
                    ],
                    // 1.1 Units and unit prices should be an array or object
                    "Units": "(Bandwidth=197, Compute=7, Storage(Read)=14, Storage(Allocate)=50, Storage(Write)=26)", 
                    "Fee": 29400
                }
            ],
            // 1.1 Units and unit prices should be an array or object
            "unitPrices": "(Bandwidth=100, Compute=100, Storage(Read)=100, Storage(Allocate)=100, Storage(Write)=100)"
        },
        "blockBytes": "000001196ef5112686d9fcfc4b3a3d2f2d439e1" //shortened
    },
    "id": 2652924454900838
}

1.1 Units and unit prices format

Should be an array or object. Binary format (see fees.UnpackDimensions) would work too. If needed, I can write a parser for the current string format too.

1.2 Action names or IDs

There is action payload, but no action name or ID. The ideal solution is to provide action binary, including the first TypeID byte, as discussed.

1.3 Auth.* as hex

Currently an array of numbers. It should be in hex format (note that in Golang, []byte serializes as base64 by default).

1.4 Transaction ID

Transaction ID is missing.

1.5 Transaction error should be a Unicode string

I can convert from base64 to bytes, then into Unicode, but a regular string format would look much better in the API

1.6 Result fields should be lowercase

For consistency across APIs.

@containerman17
Copy link
Contributor

containerman17 commented Oct 2, 2024

2. GetTX improvements

{
    "jsonrpc": "2.0",
    "result": {
        // 2.1 Error field 
        "timestamp": 1727848107595,
        "success": false,
        "units": "(Bandwidth=197, Compute=7, Storage(Read)=14, Storage(Allocate)=50, Storage(Write)=26)", // 2.2 Units format
        "fee": 29400,
        "result": []
        // 2.3 Sender address
    },
    "id": 9669870267519104
}

2.1 Error field

Although this transaction failed, there is no error field in the JSON. Unicode would be better, than base64 too.

2.2 Units and unit prices format

Same as 1.1 above, it should be formatted as an array or object.

2.3 Sender address

In GetBlock, I infer the sender's address by hashing their public key from the auth section. Since there's no auth here, adding a sender address field would be the best solution.

@aaronbuchwald
Copy link
Collaborator Author

aaronbuchwald commented Oct 2, 2024

Ok here are the changes I'm planning to make as a follow on to this PR:

Tx JSON Marshalling

  • include txID in JSON
  • marshal actions and auth as bytes and encode them as hex via codec.Bytes
  • Add TransactionJSON intermediate type that supports MarshalJSON and UnmarshalJSON and stores the action/auth as bytes
  • add MarshalJSON implementation to chain.Transaction
  • add UnmarshalJSON that takes chain.Parser in as a parameter so that it can parse the actions/auth

Results

  • Add JSON annotations to lowercase the fields of chain.Result
  • Add ResultJSON intermediate representation that supports json marshalling and add MarshalJSON and UnmarshalJSON to chain.Result that uses the intermediate type (will use codec.Bytes for the error result)
  • Already added JSON marshalling for fees.Dimensions here: Add JSON marshalling for fee dimensions #1622

Side note (out of scope for this PR): we currently use an error string for the result. There's a weird edge case here where if we or a developer changes an error string that is actually part of chain processing. This should be clear, but I've found that it's easy to forget this and think we could make it more explicit. We could, for example, include errors as possible return values in the ABI.

Added here #1627

Block Requests

  • Add encoding parameter to block request types
  • Use a single block response type w/ omitempty so that it sends JSON with either the block bytes or JSON of the block

Note: golang clients can use binary representation, so that it can decode with chain.Parser argument and other languages can handle the JSON or binary if they choose

@containerman17 containerman17 mentioned this pull request Oct 3, 2024
"github.com/ava-labs/hypersdk/internal/pebble"
)

const maxBlockWindow uint64 = 1_000_000
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we limit it? If someone wants to run an indexer that stores all transactions and they have 20TB of NVMe, why should we stop them?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't maintain a blockID -> height mapping on disk to avoid using hashes as keys. This means we iterate over all the data on startup, so we set a reasonable guardrail.

If you want to store all transactions, you need to scale horizontally rather than writing to a single pebbledb instance. This won't work at scale, so I'd prefer to set a reasonable guardrail than allow users to try and scale something that wasn't meant to.

// Use a separate type that only decodes the block bytes because we cannot decode block JSON
// due to Actions/Auth interfaces included in the block's transactions.
type GetBlockClientResponse struct {
BlockBytes codec.Bytes `json:"blockBytes"`
Copy link
Contributor

Choose a reason for hiding this comment

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

What prevents a Block from being unmarshalled from JSON? It's the Auth and Action interfaces, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yup

Copy link
Contributor

Choose a reason for hiding this comment

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

Why even have this separate type? If we're only going to use the block bytes from the response since we can't unmarshal the actual block type why don't we just drop the block field from the server-side response struct entirely and get rid of this second struct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is to support clients (JS) that prefer to handle JSON than the binary representation. For golang, we actually cannot handle the JSON using the current requester because it will simply unmarshal the json into the struct and we can't unmarshal JSON directly into the block type due to the Action and Auth interfaces.

Have a note here #1606 (comment) to add an encoding parameter that we could block this PR on if you prefer.

fee uint64,
outputs [][]byte,
) error {
outputLength := consts.ByteLen // Single byte containing number of outputs
Copy link
Contributor

Choose a reason for hiding this comment

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

I would either store the entire transaction or just the result. There's no need for a custom marshaler in this case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree and think storing the entire tx + result makes sense here #1626

This is not changed in this PR though, so don't think it needs to be in scope

"github.com/ava-labs/hypersdk/fees"
)

func TestBlockIndex(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This test feels like it's testing a lot of different cases which is making it complex to read/debug/add to. I feel like testing more targeted code edge-cases instead of a single god-test would make sense here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved a helper to generate a sequence of empty executed blocks to chaintest and separated out the restart portion of the test.

@aaronbuchwald aaronbuchwald merged commit 2cb5530 into main Oct 3, 2024
17 checks passed
joshua-kim added a commit that referenced this pull request Oct 10, 2024
commit fee360f
Author: Tsachi Herman <[email protected]>
Date:   Fri Oct 4 11:23:58 2024 -0400

    refactor marshalActions implementation (#1631)

commit 2cb5530
Author: aaronbuchwald <[email protected]>
Date:   Thu Oct 3 18:49:12 2024 -0400

    Add block indexing to Indexer API (#1606)

commit f1bcc59
Author: aaronbuchwald <[email protected]>
Date:   Thu Oct 3 18:31:39 2024 -0400

    Add JSON marshalling to Result (#1627)

commit 2ea784a
Author: Richard Pringle <[email protected]>
Date:   Thu Oct 3 12:54:10 2024 -0400

    Implement multisig example (#1581)

commit 6bbf236
Author: Richard Pringle <[email protected]>
Date:   Thu Oct 3 12:43:41 2024 -0400

    Update codeowners (#1629)

commit 47849d8
Author: aaronbuchwald <[email protected]>
Date:   Thu Oct 3 08:34:51 2024 -0400

    Re-mark pubsub tests as flaky (#1625)

commit d847132
Author: aaronbuchwald <[email protected]>
Date:   Wed Oct 2 14:23:24 2024 -0400

    Reduce tests.unit.sh timeout value less than CI job (#1623)

commit 404e74f
Author: aaronbuchwald <[email protected]>
Date:   Wed Oct 2 14:07:04 2024 -0400

    Add JSON marshalling for fee dimensions (#1622)

commit 7e53003
Author: Joshua Kim <[email protected]>
Date:   Wed Oct 2 12:38:34 2024 -0400

    Replace usage of `internal/network` with `p2p` (#1613)

    Signed-off-by: Joshua Kim <[email protected]>

commit 79f363e
Author: containerman17 <[email protected]>
Date:   Wed Oct 2 11:53:59 2024 +0900

    Standardize decimals (#1620)

commit e191c88
Author: Joshua Kim <[email protected]>
Date:   Tue Oct 1 20:02:30 2024 -0400

    Update to [email protected] (#1614)

    Signed-off-by: Joshua Kim <[email protected]>

commit 59c84d7
Author: aaronbuchwald <[email protected]>
Date:   Tue Oct 1 17:19:17 2024 -0400

    Add ExecutedBlock type (#1601)

commit 165a455
Author: Tsachi Herman <[email protected]>
Date:   Tue Oct 1 09:26:32 2024 -0400

    validate buffer length prior to calling Uint64 (#1588)

commit c24f0d8
Author: rodrigo <[email protected]>
Date:   Tue Oct 1 09:25:18 2024 -0400

    Fix VM-With-Contracts Unit Tests (#1602)

commit 8ab9d83
Author: aaronbuchwald <[email protected]>
Date:   Mon Sep 30 16:57:29 2024 -0400

    Minor nits on pubsub implementation (#1593)

commit 592cd7a
Author: rodrigo <[email protected]>
Date:   Mon Sep 30 12:46:35 2024 -0400

    Remove `StateKeysMaxChunks()` (#1607)

commit 3e358c1
Author: aaronbuchwald <[email protected]>
Date:   Mon Sep 30 12:46:20 2024 -0400

    Update ActionBenchmark to use single ExpectedOutput (#1608)

commit e78841e
Author: Franfran <[email protected]>
Date:   Mon Sep 30 16:42:00 2024 +0200

    Write FIFO cache unit tests (#1201)

    * write FIFO cache unit tests

    * invert expected / actual

    * add limit parameter and cache fail on 0 size

    * simplify test table and create new test for empty cache

    * simpler tests by having a limit of 2

    * fix test cases

    * typos

    * backout uselesss change

    * add a "not an LRU" test case

    * use switch in fifo unit tests

commit f1150a1
Author: aaronbuchwald <[email protected]>
Date:   Fri Sep 27 17:46:28 2024 -0400

    Update tx indexer to include tx action outputs (#1597)

commit 6a3fd63
Author: Joshua Kim <[email protected]>
Date:   Thu Sep 26 15:44:56 2024 -0400

    Remove usage of mockgen (#1591)

    Signed-off-by: Joshua Kim <[email protected]>

commit 49376bd
Author: rodrigo <[email protected]>
Date:   Thu Sep 26 14:39:59 2024 -0400

    Fix benchmarks (#1590)

commit 77a73ba
Author: Richard Pringle <[email protected]>
Date:   Tue Sep 24 12:38:27 2024 -0400

    Ignore gas on mock-function-call (#1585)

commit 1bbab7b
Author: containerman17 <[email protected]>
Date:   Tue Sep 24 23:19:18 2024 +0900

    Remove special cases for Bytes, add arrays support (#1587)

commit 3410599
Author: aaronbuchwald <[email protected]>
Date:   Mon Sep 23 12:34:55 2024 -0400

    Add spam cmd to morpheus readme (#1577)

commit dfefd27
Author: aaronbuchwald <[email protected]>
Date:   Mon Sep 23 12:34:32 2024 -0400

    Improve chain comments (#1579)

commit 6c15244
Author: Richard Pringle <[email protected]>
Date:   Fri Sep 20 16:10:21 2024 -0400

    Series of changes that improve development (#1583)

commit e0a3324
Author: Richard Pringle <[email protected]>
Date:   Fri Sep 20 13:12:58 2024 -0400

    Use repr(packed) with state-keys (#1580)

commit 75c6cfd
Author: aaronbuchwald <[email protected]>
Date:   Thu Sep 19 07:58:39 2024 -0400

    Separate proposer monitor from vm into internal package (#1574)

Signed-off-by: Joshua Kim <[email protected]>
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.

3 participants