Knowledge Base Home

Public Ethereum Tether

Counter against collusion and historical rewrites by appending collectively signed state snapshots to the Ethereum Mainnet or Testnet.

The Public Ethereum Tether is an environmental utility service that allows for synchronized views of the chain, designated via a block hash, to be signed by each node and periodically (based on a specified configuration) relayed to a public Ethereum network. This subsequently creates an irrefutable state proof of the private network at that point in time.

Why the Tether?

Setting aside the technical complexities of such an operation, it’s hypothetically possible for a supermajority or entirety of the participants in a network to, for mutual benefit or malicious purposes, collectively collude in an effort to alter the blockchain or its underlying state database. Pinning aggregated signed agreements of the private chain’s state to a public network makes collusion attempts futile and provides a trustworthy attestation in the event of litigation or dispute.  The state reports are retrievable by any member of the consortium, with the embedded digital signature on each report mapping deterministically to the public key of the signing node.

Enable the Tether

The recommended happy path for provisioning the Public Tether Service is the Kaleido user interface.  An end-to-end successful activation will ultimately result in a new utility service instance within the specified environment, as well as the creation of an Ethereum account (needed to hold ether for public network gas costs), the deployment of a tether smart contract on the targeted network (responsible for storing the signed state proofs from the environment’s nodes) and an initial transaction pinning your environment’s current state (i.e. signed block hash) to the configured endpoint.


  • Navigate to the environment which you would like to anchor to a public network, and click the +ADD dropdown in the top right portion of the screen.
  • Select the Add Services option. This will open a new panel exposing the currently available Kaleido Services.
  • Click the ADD button beneath Public Ethereum Tether.
  • Supply an arbitrary name for the tether service and click ADD. Click DONE to finish the deployment.
  • The newly created tether service will appear under the Services view on your environment dashboard.

Configure the Tether

  • Expand the dropdown next to your Public Ethereum Tether service and click View Dashboard.
  • Click the Generate Account button to create a new Ethereum account for the service.
  • You will be taken to a new screen displaying the account’s private signing key and a QR code for the key.

IMPORTANT:  Take great care in the security and storage of this private key; it belongs to you.  Once ether has been deposited into the account, it can ONLY ever be withdrawn by the individual(s) in possession of the private key.  As such, you are strongly encouraged to follow best practices (hardware wallets, multiple copies, cold storage, etc.) in order to preserve the durability and integrity of the key.  Manage the key and account in the same fashion as you would for a regular cryptocurrency wallet.

  • Once you have safely stored the account’s private key, click the Submit button at the bottom of the page.  You will be taken to a new screen displaying the corresponding address for your Public Ethereum Tether account.  Behind the scenes the public key is cryptographically derived and the account address is represented as the last 20 bytes of its Keccak-256 hash.
  • Using the dropdown box, decide which public network you would like to target for tethering.  Select between the Ethereum Mainnet and Rinkeby Testnet.
  • Next, fund the Public Ethereum Tether account on the targeted network by clicking on the Add Funds button.  This will redirect you to Metamask, an intuitive interface offering secure identity management and transaction signing capabilities.  You are, however, welcome to employ alternative approaches if you wish (e.g. programmatically using web3 APIs).

ETHER CONSUMPTION:  We have performed internal testing with regards to the tether service’s ether consumption, and offer the following conservative estimates as guidance for your initial funding – Tether Smart Contract Deployment:  0.001306 ETH  |  Pinning Transaction on a per node basis:  0.0002 ETH apiece.  Each transaction contains the aggregated signed state reports from all nodes in the environment and activation of the service results in an immediate pinning transaction to the targeted public network.  As such, if you have an environment with n nodes and a Public Ethereum Tether service configured with hourly intervals, the estimated monthly ether usage would equate to – [((0.001306+(0.0002*n)) + 31((0.0002*n)*24))].  Also, be cognizant that any reactivation of the service will also result in an immediate anchor transaction (i.e. an expenditure of 0.0002*n ETH). Reactivations occur upon any restart of a node (environmental quiesce/restart and environmental upgrades) and when the service itself is manually deactivated/reactivated by a member of the consortium.

DISCLAIMER:While the aforementioned behavior has been repeatedly revealed during testing, we offer no explicit guarantees on the enumerated ETH values. You are strongly encouraged to peruse the Tether Service’s logs and ascertain the specific gasUsed values for your individual service in order reach your own funding conclusions.

  • If you’ve chosen to target the Rinkeby Testnet, ether can be acquired via the Rinkeby Faucet.  If you are targeting the Ethereum Mainnet, you are responsible for acquiring real ether or accessing a funded account.
  • After your funding transaction has been confirmed click the Refresh button to see the available balance.  These are the funds that Kaleido will make use of when sending transactions on your behalf.

  • Next, click Deploy to instantiate the tether smart contract on the targeted network.  This is the smart contract that will maintain the private Kaleido state proofs (i.e. the signed block hashes).  The deployment should take roughly 10-15 seconds.
  • Lastly, choose the frequency with which you wish to send state snapshots to the publicly targeted network.  The choices are:  1, 6, 12 and 24 hour intervals, with 6 hours enabled as the default. Click Activate to enable the tether service.
  • NOTE: Refer to the API Reference tab for details on setting custom, user-defined intervals.

  • At any point, you can elect to stop pinning transactions by clicking the Manage button and selecting Deactivate Tether Relay.

Refer to the Kaleido Relay whitepaper the full architectural specification.

Download a State Report

Access your Public Ethereum Tether dashboard and click the Download Last Report hyperlink.

The report is returned as one or more JSON objects (where each object correlates to an individual node’s signed state proof) and contains the following information:

  • Node ID hash(es)
  • Block number
  • Block hash
  • Checksum(s)
  • v(s), r(s), s(s)

The values for r and s are the normal output for an ECDSA signature, with v serving as a recovery ID for cryptographic derivation of the signature’s corresponding account address.  The nuances of elliptical curves and the associated signing algorithms are beyond the scope of this documentation, however, the following article – A (Relatively Easy to Understand) Primer on Elliptic Curve Cryptography – is a good place to start if you’re interested in learning more about the underlying constructs of ECDSA.  For those in search of a deeper understanding behind the core primitives of elliptic curve cryptography, this article by Vitalik Buterin is an excellent resource.

Assume an environment with three nodes.  For example:

A  downloaded state report for this environment would look similar to the following:

        "nodeIdHash": "0x1a0c5087e279cb9941a2661da10821a0784f1e9dcbbc44add1912ac5a2f9f9b3",
        "blockNumber": "101169",
        "blockHash": "0xea8d2cca7c58263602860b723ec9da5cc5f8a6f22197e22b90fa42a8af928433",
        "checksum": "0xd95117e40a74ed7f72e9def25ad53ac5927683a3b7c7a2efba5045e37ad4f9fc",
        "v": "28",
        "r": "0x32357a9cfb30821b1071a2d2bdd59ee5444250ff51bbc77fb57408d3c142bb8e",
        "s": "0x46de6bad76ead4ad2af7e77415b6422c1c6affaf4343ec548b846e265dbe2940"
        "nodeIdHash": "0xcc814cf744fffb78797fe0dc6666e13734741d17e1deb0e2098f29d007343426",
        "blockNumber": "101169",
        "blockHash": "0xea8d2cca7c58263602860b723ec9da5cc5f8a6f22197e22b90fa42a8af928433",
        "checksum": "0x235bffbde1db601db7a4d2145faf2019c4e7ed801b9a7d43fea8bc5b6d0d5c29",
        "v": "28",
        "r": "0x43b9a1f18a98de5feba9848197c4a7cfd8718080e4fd54a683d9f9eb4c2a4210",
        "s": "0x7cefa80e2994a4520a5f78eb0480bfd4b4b146d3d5c011bf3fc4ba2f40c9875a"
        "nodeIdHash": "0x407f869854e60ab38465a5ba26afe528793f23454b931d4d5548b1b0753cb543",
        "blockNumber": "101169",
        "blockHash": "0xea8d2cca7c58263602860b723ec9da5cc5f8a6f22197e22b90fa42a8af928433",
        "checksum": "0x63503425bc60bad29862db73696b0171b7f05f9e3f06495951e8168d151c0efd",
        "v": "27",
        "r": "0x8b7e00d0c71fee10fb9d6a7b3e26a3a4c918c6721b06e72ed4be6ac0cb8b0c3f",
        "s": "0x0c2f87ee7fe50992b3b837d5d74bd8d58aa1bec1c40c794139e431d5228ae4b7"

The report will contain a unique JSON object for each node in the environment, with the individual objects represented in the same order as the nodes on the environmental screen.  For example, node 1 maps to the first JSON object with a nodeIdHash value of 0x1a0c5087e279cb9941a2661da10821a0784f1e9dcbbc44add1912ac5a2f9f9b3.  Whereas node 2 maps to the second object with a nodeIdHash value of 0xcc814cf744fffb78797fe0dc6666e13734741d17e1deb0e2098f29d007343426 and so on, until all of the nodes in the environment are accounted for.

Upload & Verify a State Report

Navigate to the environment running your tether service.  Expand the dropdown next to the node whose report you wish to verify and click Node Details.  Scroll to the bottom of the screen and click the Upload Report Details button:

Use the Node ID Hash as an identifier and locate the corresponding JSON object for your node. Next, populate the required arguments for Block Number, Block Hash, Signature-V, Signature-R and Signature-S. With the above state report as a frame of reference, a properly configured upload panel for node 1 would look as follows:

The report is verified by first stringifying the values for Node ID, Node ID Hash, Block Number and Block Hash.  This string and the values for the three pieces of the digital signature – v, r and s – are passed as arguments to the eth.accounts.recover method in order to cryptographically derive the address of the signing account.  If the ensuing result matches the Node Signing Address, then the report is considered verified.

Programmatically Verify a State Report

The following section explains how to programmatically verify a downloaded state report using secp256k1 ECDSA cryptographic recovery maths.  Note that this is the same mechanism used behind the scenes by Kaleido to verify the node’s signing address against the supplied arguments.

Before starting, ensure that you have Node.js and its accompanying package manger installed on your machine.

node -v && npm -v

If you don’t have them, they can be downloaded here.

For the sake of consistency, we will once again leverage the JSON object for node 1 as the frame of reference for this exercise:

        "nodeIdHash": "0x1a0c5087e279cb9941a2661da10821a0784f1e9dcbbc44add1912ac5a2f9f9b3",
        "blockNumber": "101169",
        "blockHash": "0xea8d2cca7c58263602860b723ec9da5cc5f8a6f22197e22b90fa42a8af928433",
        "checksum": "0xd95117e40a74ed7f72e9def25ad53ac5927683a3b7c7a2efba5045e37ad4f9fc",
        "v": "28",
        "r": "0x32357a9cfb30821b1071a2d2bdd59ee5444250ff51bbc77fb57408d3c142bb8e",
        "s": "0x46de6bad76ead4ad2af7e77415b6422c1c6affaf4343ec548b846e265dbe2940"

First, navigate to a working directory on your local machine and initialize a node project.  For example:

cd $HOME/mylocaltests && npm init

We aren’t concerned with any dependencies or alternate metadata in the package.json, so press ENTER until the basic file is generated.

Next, install the web3 modules. Our program needs access to the Ethereum APIs in order to perform the cryptographic recovery of the account address. More on this at the conclusion.

npm i web3

Now, create a javascript file named index.js at the root of your directory.  You’ll notice that, by default, this file is specified in the package.json as the main entry point for the application.

vi index.js

Populate the program with the following template code:

let Web3 = require('web3')
let web3 = new Web3()

let payload = {
    "nodeId": "zzzgnlszeq",
    "nodeIdHash": "0x1a0c5087e279cb9941a2661da10821a0784f1e9dcbbc44add1912ac5a2f9f9b3",
    "blockNumber": 101169,
    "blockHash": "0xea8d2cca7c58263602860b723ec9da5cc5f8a6f22197e22b90fa42a8af928433"

let msg = JSON.stringify(payload)

let result = web3.eth.accounts.recover(


With the exception of nodeId, all of these values are extracted from the node’s state report JSON object.  The 10 character string for nodeId is withheld from the publicly pinned transaction because it serves as the only uniquely identifiable piece of user information. All of the other values are either indecipherable or meaningless to any user attempting to glean state report details from the publicly callable smart contract.

The nodeId string can be easily ascertained by accessing the environmental panel in the Kaleido console and viewing the Node Details screen for the targeted node. Ensure that this page also displays the same nodeIdHash value that you see in the state report. If you’d like to manually verify the hashed representation of the Node ID, you can feed it to a SHA256 algorithm in your terminal. For example:

echo -n "zzzgnlszeq" | shasum -a 256

This returns:


Which, when prepended with 0x for proper hex representation, leaves us with the same value displayed in the state report.

Go ahead and run the program with the sample values:

node .

It will return us 0xAcA169A8418d3902E01DcC91C04a7161524a02Dc, the exact string for Node Signing Address displayed in the screenshots above.

Use a Different Report

To verify your own unique report, edit all of the placeholder values in the program and make sure to:

  • specify the block number as an integer and not a string; do NOT include quotations
  • pass the four arguments to the recover method exactly as follows: eth.accounts.recover(msg, v, r, s)
  • specify v, the recovery ID, in hexadecimal representation. 27 is 0x1b and 28 is 0x1c

Under the covers the stringified JSON object defined as variable msg will be enveloped with \x19Ethereum Signed Message:\n" + message.length + message and hashed using Keccak256. The outputted value is the same as the checksum string in the state report and is ultimately what will be signed using the node’s private key. You can generate the same checksum string for your report by passing the stringfied JSON object to the eth.accounts.hashMessage method and logging the output.

Refer to the web3 API documentation for additional details around the hashsign and recover methods.

The tether APIs can be leveraged to retrieve reports and balance information. Alternatively, they can be exercised to set configuration parameters.

The sample cURL commands make use of the following exported environment variables; please set them or enumerate the appropriate values accordingly:

export APIURL=""
export HDR_AUTH="Authorization: Bearer $APIKEY"
export HDR_CT="Content-Type: application/json"

If you are targeting an environment outside of the US, make sure to modify your URL accordingly. The ap qualifier resolves to Sydney, while ko resolves to Seoul:

export APIURL=""
export APIURL=""
export APIURL=""

The tether_id is the last 10 characters of the service’s URL. For example, if the service’s URL is, then the tether_id would be zzeh2wtgm2


  • /reportCount returns the total number of environmental reports for your tether service instance.  Each node’s individual state report object is contained within a single report.

curl -X GET -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/reportCount" | jq

  • /lastReport returns the most recent aggregated collection of state report objects.

curl -X GET -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/lastReport" | jq

  • /reports/{index} returns the enumerated report, where the first report in the index defined as int 0.  In other words, if /reportCount returns 100, then the most recent report would be specified as int 99.

curl -X GET -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/reports/{index}" | jq

  • /accounts returns the service’s account address and the balance (specified in wei) for the currently targeted network.

curl -X GET -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/account" | jq

  • /balance/{network} returns the account’s balance for the specified network.  This call accepts either mainnet or rinkeby as specifications.  For example, to retrieve the balance for the account funding MainNet transactions:

curl -X GET -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/balance/mainnet" | jq


  • /targetNetwork can be used to set or reconfigure the targeted public network.  This call requires either mainnet or rinkeby as a specification in the body of the call.  This configuration must be set prior to deploying the tether smart contract and subsequently activating the service.  For example, to set or change your target network to the Ethereum MainNet:

curl -X POST -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/targetNetwork" -d '{"network":"mainnet"}'

  • /interval can be used to set or reconfigure the pinning interval.  This call requires an integer of ≥ 1 in the body of the call, with the integer enumerating the hourly interval between pinning transactions.  This configuration must be set prior to activating the service.  For example, to set the pinning interval to 48 hours (i.e. pin once every two days):

curl -X POST -H "$HDR_AUTH" -H "L/tether/{tether_id}/interval" -d '{"interval":48}'

  • /deploy is used to deploy the tether smart contract on the configured target network.  Note that if the Tether Service’s account address does not have funds on the configured target network, then the deployment will fail.  The deployment of the tether smart contract must succeed prior to activating a service instance.  Requires an empty body. For example:

curl -X POST -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/deploy" -d '{}'

  • /activate starts a new tether service or resumes a deactivated instance.  This call requires an empty body and returns a boolean of true if successful. For example:

curl -X POST -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/activate" -d '{}'

  • /deactivate stops an active tether service.  This call requires an empty body and returns a boolean of true if successful. For example:

curl -X POST -H "$HDR_AUTH" -H "$HDR_CT" "$APIURL/tether/{tether_id}/deactivate" -d '{}'

Prev Racecourse Next Ether Pool