Skip to content

The common API #23

Closed
Closed
@pepyakin

Description

@pepyakin

There are several rollup stacks out there. E.g. #19 #8 #9 and sovereign-sdk.

Sov is Rust. Others in Go. Probably there may be yet others in completely different languages (although, not very likely). We don't want to reimplement all the intricacies in every adapter. Therefore, it may be worthwhile to push most of the heavy lifting into what we called previous an API.

There are several approaches to build such an API:

  • One of them is to try to come up with a common denominator API that could be used to implement every adapter we would encounter.
  • Another way, is to just provide API specifically for each adapter, i.e. ad-hoc API, Like, e.g. /sovereign_v1/finalized_at/:height and /rollkit_v2/get_blob/:id.

While the common denominator approach seems cleaner/more elegant/more aesthetically pleasing, I think the ad-hoc API approach is the way to go, at least for the beginning.

I think getting the common denominator API is tricky. Every new adapter may change the whole thing completely and we may still need to add the ad-hoc things for specific adapters.

I am pretty sure we will have some breaking changes initially. With the ad-hoc APIs we don't have to spend too much time to foresee all the possible use-cases, we just implement the current API in the form of RPC and that's it. There still may be a new adapter that challenges some foundational assumptions, but that would affect the internal API/the implementations which should be easier to change compared to the common denominator RPC which a public API. Such a change may affect the adapters who were perfectly happy with the previous version of the API (most of them probably).

The elegance of the common denominator API also comes at a cost: while ad-hoc API will return all the required information for the adapter call in one go, the common denominator API may require several calls and further logic on the adapter side.

OTOH, the ad-hoc APIs allow making the adapters dumb. All the heavy lifting is performed in the implementation of API. Making the adapters dumb is desirable because it makes the whole system more amenable for testing. Arguably, dumb adapters don't require as much testing besides some integration tests and we can cover the common Rust logic more extensively than we could ever do in adapters.

The ad-hoc API has a downside, that probably the adapters will have to be in-tree. I think this is fine. We can provide the adapters for select rollup stacks. For example I wonder if #9 #8 #19 cover a big chunk of rollups already. Realistically, we cannot expect that stack devs will line up to implement integrations with sugondat, so we will have to kinda do it ourselves at least initially.

Also, picking the ad-hoc API way doesn't mean we should stick forever to it. Maybe, if there's enough demand, we could add a common denominator API later on, when we gain enough experience and things may stabilize.

With all that said, I will present my current vision of the common denominator API below. The reason is, it should be obvious how to structure the ad-hoc APIs, however, as I mentioned, the common denominator API is less obvious. Also, perhaps, this API may inform under the hood abstractions for the ad-hoc API approach.

header(height) → AbridgedHeader

/// Get the blob by it's ID. 
get_blob(id) → Option<Blob>

/// Returns the inidices of blob extrinsics for the given namespace.
get_blobs(height, Namespace) → [ExtrinsicIndex]

/// Same as the above, but also returns a proof that the returned extrinsic index vector:
/// (a) doesn't omit any extrinsic index (exhaustive)
/// (b) every extrinsic index represents a blob of the specified namespace.
get_blobs_with_proof(height, Namespace) → [ExtrinsicIndex], Proof

validate(id, proof) → bool


submit(Blob, Namespace) → id

Notes:

  • height — a finalized block number. In every API so far it was u64 and I am ok with it. In JSON we may consider using String to encode it.
  • id is an identifier of an included blob, e.g. height and extrinsic index or height and extrinsic hash.
    • The is a subtle difference between picking index or hash for the ID in that hash can be obtained before submitting but index can be only obtained after the blob landed. Arguably, it doesn't matter much because height has to be obtained after landing anyway.
    • Substrate default RPC doesn't provide extrinsic-by-hash API and it's not trivial to add since that would require integrating the corresponding index. Hence height is required.
  • AbridgedHeader consists of:
    • hash
    • parent
    • nmt_root
    • timestamp
    • ...
  • Blob = Vec
  • Namespace, TBD by Determine Namespace ID size #12

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions