Multiple Networks

So I’ve glanced over the Rosetta spec and API schemas, and one thing that I continuously find to be a prevalent theme is the notion of “networks”. Specifically, I’m curious how Rosetta treats and interprets multiple networks from a single Rosetta compliant API? For example, the Cosmos Hub is a single live ledger, with two previous ledgers that are no longer active as they’ve been upgraded from (i.e. CH1 to CH2 to CH3 and soon to be CH4). Is it intended that a Rosetta compliant API serve data from multiple networks, both active and inactive? Is there more documentation on this that I could look at? Thanks!

Hey @aleksbez :wave:

We use this network concept throughout the Rosetta API for 2 specific reasons (which I admit are not very well documented at this point):

  1. We imagine that running Rosetta Gateways will become popular once there are a few completed implementations. With the NetworkIdentifier attached to each request, developers can use the same Client SDK for both direct usage of Rosetta implementations and for access through a gateway. Furthermore, there is no need to use a separate URL or port to access specific blockchains.
  2. In sharded blockchains, it is a common access pattern to query data scoped to a particular shard. If we did not include the ability to specify NetworkIdentifier in requests, we would need some custom bolt on to support shared blockchains. It was an explicit design goal to support these types of networks as first-class citizens.

In terms of validation or syncing, the tooling treats all data belonging to a particular NetworkIdentifier as distinctly and validates it independently.

If a blockchain does not have multiple “networks”, it is expected that a Rosetta implementation just supports queries from whatever NetworkIdentifier it returns in /network/list.

This is a GREAT question! This is very much up to you and what sort of support you think would be most useful to your community. Either serving from only the active ledger or from all ledgers would both be “compliant” with the only active ledger approach likely being easier (obviously). To over-communicate, I’m going to outline what each approach could look like.

[1 Ledger at a Time] Specify Cosmos Hub Network to Run Using an ENV

At startup, one could specify the network-id of the Cosmos Hub they wish to run (or the location of the genesis file to use). This would start a Rosetta API Server that only supports that particular network-id. In which case, the /network/list endpoint would return something like this:

{
    "network_identifiers": [
        {
            "blockchain": "Cosmos",
            "network": "cosmoshub-4"
        }
    ]
}

[Multiple Ledgers] Support all Cosmos Hub Ledgers in the Same Deployment

I’m not sure what the compatibility story is around using new node software to run old ledgers or if a node can support multiple ledgers, so this solution may be a “non-starter”. If this is possible, you would return all NetworkIdentifiers that the deployment supports.

{
    "network_identifiers": [
        {
            "blockchain": "Cosmos",
            "network": "cosmoshub-1"
        },
        {
            "blockchain": "Cosmos",
            "network": "cosmoshub-2"
        },
        {
            "blockchain": "Cosmos",
            "network": "cosmoshub-3"
        },
        {
            "blockchain": "Cosmos",
            "network": "cosmoshub-4"
        }
    ]
}

It may be much simpler to only ever run 1 ledger per deployment and provide a “gateway” that allows for querying all previous ledgers by routing to ledger-specific instances associated with a specific NetworkIdentifier (just spitballing).

When specific questions like this are asked, the community becomes the best source of documentation (I spent some extra time trying to cover most relevant info). The only docs on the website that really touch on this concept (albeit lightly) are at this link:

If you want to dive deeper, you would probably get the most benefit by digging into some of the syncing code in rosetta-sdk-go:

1 Like

What are your thoughts on building an image per network if the architecture of a particular blockchain service leans heavily towards a single network per instance?

Rather than making it a runtime selection, a build-time argument ensures reliable operation between container restarts, without the need for guards and an exception process to be modelled. The network config can be loaded well down the stack in a Dockerfile, so has insignificant build overhead, but would result in a cleaner mainnet instance runtime.

This is a GREAT question @rhysbw. I was looking through the website and found that I never elaborated on this point (will be updated to reflect the content in this response).

If your implementation will only support 1 network per instance (which is likely 95% of Rosetta implementations), we encourage the use of a unique image for each network. The reasons for this are (as you mentioned):

  • Supporting multiple networks from a single image can require writing extra scripts that are tough to debug and test (that may coordinate starting different binaries or have extra checks to ensure restarts are correct). This is made more complicated when significant network upgrades occur as Rosetta implementations may change significantly.
  • Supporting all networks in the same Dockerfile can result in larger images as the dependencies between testnet and mainnet are often different (testnet usually runs newer versions of software).

That being said, it is not advised to have vastly different Dockerfiles for mainnet and testnet. The goal for running testnet is to mirror what could happen on mainnet and if things are significantly different, it will not serve this purpose.

Hope this clarifies things @rhysbw! I think something like Dockerfile.mainnet and Dockerfile.testnet could be a good approach?

P.S. Make sure to follow these guidelines mentioned here when writing your Dockerfile:


There are also some common mistakes mentioned here:

Thanks @patrick.ogrady

I propose a single Dockerfile, that for mainnet has no changes to the build command:

docker build -t my-rosetta:1.0.0 .

… then when building an image for an alternative supported network, supplying the name via a build arg

docker build --build-arg=NETWORK=testnet -t my-rosetta:1.0.0-testnet .

This retains the nice property of a single build definition, with just enough input to produce a minimally configured testnet image. This avoids the need to pass the extra -f Dockerfile.mainnet too, and only adds verbosity to the command during a testnet build.

I think this would be a great addition to the spec, including the standard naming of this argument. It should detail that passing --build-arg=NETWORK=mainnet is not illegal to allow more structured processes to be defined.