Design Principles
Before reading this section, it may be helpful to familiarize yourself with Rosetta Objects.
No Gatekeepers
Rosetta is an open standard that anyone can adhere to or utilize. There are no PRs to open against an official repository and there is no required implementation language.
No Predefined Operation Types
The Rosetta interface does not restrict implementations to use a predefined set of types to describe network-specific activity. For example, PAYMENT could be a predefined type that could be required for all implementers to apply to network-specific transactions. While this sounds appealing and feasible for a simple type like PAYMENT, the task of producing such a set of types can quickly become very complex and controversial. Instead, the interface puts the burden on the client to apply whatever types they feel best apply to the standardized Operations that compose each transaction.
Signed Operation Amounts
Although there are no predefined Operation types in the interface, an explicit attempt is made to make balance reconciliation much easier: using signed Operation amounts. This means that all operations within a transaction must clearly specify the balance change that results from that operation are explicitly credit or debits to a specified account.
Single Party, Linked Operations
All Operations in the interface can affect at most 1 account but can be linked to any number of other Operations. To represent network-specific operations that are inherently multi-participant (a network-specific transfer that has a sender and recipient specified in a single op), this entails creating an explicit operation for each account involved. At a high-level, this means reducing account-based blockchains to something resembling ledger accounting (all credits have debits). While this approach may seem burdensome for simple payments, the generality of this design allows for capturing the structure of complicated on-chain interactions more closely to how they are represented natively on-chain without imposing more restrictions on the Operation model. This single operation abstraction for all accounting types also means that downstream processing does not require conditional parsing on different operation abstractions.
Operation Status, Not Transaction Status
Indication of status on the Transaction implies that all Operations within that Transaction atomically succeed or fail. While this is often true on blockchains without smart contracting, it is not always the case on blockchains that support generalized smart contracting. In Ethereum, transactions can fail anytime during execution and changes are only rolled back when explicitly indicated to do so in the smart contract itself. Requiring that implementers have atomic transaction success/failure is a pretty heavy handed restriction that could limit innovation.
Transaction Fees are Just Operations
For blockchains where there is no explicit fee payer, where there are multiple fee payers, where fees can be paid in multiple currencies, or where fee payment is made by one of many parties in a transaction, it becomes complicated to represent a fee payment as a Transaction property. Thus, all Transaction fees are represented exclusively as Operations.
Sharded Blockchain Support
Many new blockchains are utilizing a sharded design to offer increased scalability. It is critical that this interface treats these blockchains as first-class citizens, albeit in a generalized manner. The interface introduces the idea of SubNetworks to identify specific portions of the network that can be used to qualify a fetch of blocks or state.
Staking and Smart Contract Support
In blockchains with generalized smart contracting, the notion of account state can be much more nuanced than a single token balance at a single height. The interface introduces SubAccounts to identify state that is specific to a certain contract or lockup period (ex: delegated stake). Each account can have an array of balances that are uniquely identified by a SubAccount.
JSON-Based RPC Protocol
When working on this interface, we considered many interface specification formats that have gained popularity in recent years (like Protobuf + gRPC and Avro) but found most only had strong support for a few programming languages, required users to have enough programming experience to know how to use autogenerated code to encode/decode some binary format, and/or didn't work well in the browser.
The Rosetta interface is specified in the OpenAPI 3.0 format (the successor to Swagger/OpenAPI 2.0). Requests and responses can be crafted with autogenerated code using Swagger Codegen or OpenAPI Generator (with the downloadable JSON spec at the top of this site), are human-readable (easy to debug and understand), and can be used in servers and browsers. Using JSON incurs some performance penalty, however, we have found the challenge of robust integration to be a much more painful issue than mitigating JSON overhead.
Due to the complexity of requests (which can often contain a number of parameters of unspecified size), all communication with the Rosetta Interface Server utilizes POST requests. This makes the interface much closer to JSON-RPC 2.0 with the exception that requests go to specific paths instead of using methods. If you have used gRPC, it will feel very familiar.
Stateful Rosetta Servers are OK
To efficiently populate responses, it may be necessary to preprocess data in the Rosetta Server. For example, a Bitcoin Rosetta Server may keep persistent state to quickly return UTXOs. The only requirement here is that any state is stored in the /data directory specified in the node requirements.
Maintaining state in the Rosetta Server is optional and it may, in some cases, be better to store any data required to answer requests in the node itself.