Skip to content

helium/rosetta-helium

Repository files navigation

Read the wiki!

Overview

Dockerized Rosetta API implementation mostly based off of blockchain-node:

  • Rosetta specs: https://www.rosetta-api.org/
  • This is NOT a full node, but rather works off the latest snapshot as specified in blockchain-node. As a result, there is currently no support for historical balances or reconciliation.
  • blockchain-node provides the basic blockchain that the Data API reads from
  • ./helium-constructor implements a simple Express server written in TypeScript exposing helium-js for Construction API actions (transaction construction, signing mechanisms, etc)

This project was created by @syuan100 and supported, in part, by the DeWi Grants Program.

See Also

Quick setup

Build container from source

Mainnet:

DOCKER_BUILDKIT=1 docker build . -t rosetta-helium:latest

Testnet:

DOCKER_BUILDKIT=1 docker build . -t rosetta-helium:latest --build-arg NETWORK=testnet

Note: DOCKER_BUILDKIT=1 is not necessary but including it may reduce the image size due to the nature of the conditional build.

Build container using pre-built Helium images

Mainnet:

DOCKER_BUILDKIT=1 docker build . -f Dockerfile_quick -t rosetta-helium:latest

Testnet:

DOCKER_BUILDKIT=1 docker build . -f Dockerfile_quick -t rosetta-helium:latest --build-arg NETWORK=testnet

Run container

Local data is stored in helium-data

docker run -d --rm --init --ulimit "nofile=1000000:1000000" -v "$(pwd)/helium-data:/data" -p 8080:8080 -p 44158:44158 rosetta-helium:latest

It's HIGHLY recommended that you set the internal/external NAT settings through environment variables for better performance:

NAT_INTERNAL_IP -> 172.17.0.X X depends on how many docker containers you have

NAT_INTERNAL_PORT -> 44158 Default port for peering

NAT_EXTERNAL_IP -> Your publicly accessible IP address

NAT_EXTERNAL_PORT -> 44158 Generally would want to keep the same port that you exposed in the command line

docker run -d --rm --init --ulimit "nofile=1000000:1000000" -v "$(pwd)/helium-data:/data" -p 8080:8080 -p 44158:44158 -e NAT_INTERNAL_IP={{docker_ip}} -e NAT_INTERNAL_PORT={{docker_port}} -e NAT_EXTERNAL_IP={{public_ip}} -e NAT_EXTERNAL_PORT={{public_port}} rosetta-helium:latest

Rosetta CLI check

Mainnet:

rosetta-cli check:data --configuration-file rosetta-cli-config/mainnet/config.json

rosetta-cli check:construction --configuration-file rosetta-cli-config/mainnet/config.json

Testnet:

rosetta-cli check:data --configuration-file rosetta-cli-config/testnet/config.json

rosetta-cli check:construction --configuration-file rosetta-cli-config/testnet/config.json

(Please wait a few minutes for the Helium node to initialize before running this command)

Read more on using the rosetta-cli.

Contributing

It's annoying to spin up a docker container for every change that you want to make. So for local development, it is recommended that you run each part of the implementation separately.

rosetta-helium

  1. Install golang if you haven't yet.
  2. At the root directory, run go run . to start the rosetta server at port :8080

blockchain-node

  1. Checkout blockchain-node.
  2. Run make && make release to build a release
  3. Run make start to start blockchain-node at port :4467

helium-constructor

  1. Install node. I prefer nvm.
  2. cd helium-constructor
  3. npm ci
  4. npm run build or npm run watch
  5. npm run nodemon to start the express server at port :3000

At this point you should be able to run the rosetta-cli check from above and get similiar results to the docker container. Remember, make sure to give blockchain-node a few minutes to warm up before it picks up blocks.

Implementation details

Supported currencies

  • HNT (Helium Token)
  • HST (Helium Security Token)

Unsupported currencies

  • DC (Data Credits): not implemented as they cannot be actively traded

Data API transactions

Transactions support for reading from the Data API

Transaction Implemented
payment_v1
payment_v2
reward_v1
reward_v2
security_coinbase_v1
security_exchange_v1
token_burn_v1
transfer_hotspot_v1
transfer_hotspot_v2
stake_validator_v1
unstake_validator_v1
transfer_validator_v1
create_htlc_v1
redeem_htlc_v1

Fee-only transactions (Only recording implicit_burns for HNT deductions)

Transaction Implemented
add_gateway_v1
assert_location_v1
assert_location_v2
oui_v1
update_gateway_oui_v1
routing_v1
state_channel_open_v1

Pass-through transactions (No balance changes, only metadata)

Transaction Notes
dc_coinbase_v1 DC only transaction
state_channel_close_v1 DC only transaction
gen_gateway_v1 Internal blockchain only
poc_request_v1 Internal blockchain only
poc_receipt_v1 Internal blockchain only
consensus_group_v1 Internal blockchain only
vars_v1 Internal blockchain only
price_oracle_v1 Oracle HNT value transactions

Construction API transactions

Transaction support for creation via the construction API

Transaction Implemented
payment_v2
security_exchange_v1
create_htlc_v1
redeem_htlc_v1
stake_validator_v1
unstake_validator_v1
transfer_validator_v1

Additional notes

Unstake Transaction Oddities

The unstake_validator_v1 transaction is unique in that the balance changing portion of the transaction doesn't happen until the specified cooldown has passed. At that point, there is a callback on the ledger that records the balance change. Unfortunately, there is no way for blockchain-node to surface information about this balance change when inspecting a block at a particular height. This is especially important for the rosetta-cli check:data command to pass.

To mitigate this, we decided to store what we are calling "ghost transactions" that will be triggered when Rosetta queries a particular block. That way, Rosetta can notice the balance change and properly credit the appropriate account for reconciliation purposes.

Example:

  • Unstake transaction dectected at block 5 with a cool down of 10 blocks (transaction fee is deducted here)
  • We store a "ghost transaction" in our local BadgerDB at block 15 (current block + cool down) with the appropriate credit to the appropriate account
  • When we query block 15, Rosetta will check BadgerDB for any ghost transactions and include them in the query response.