REST: ledger
Four endpoints under /ledger let a program read the ledger of the node and send a transaction to it. They
need the bearer token like every other endpoint, see REST interface. A node started without
the hidden --preview switch answers all four with 503 and This release does not include the ledger part,
as release 0.0.9 leaves the ledger off. A node started with --preview but not with --ledger, or whose ledger
has not started yet, answers them as a node without a ledger: GET /ledger says running: false and the others
answer 503. The node never signs for a client: the client
signs the transaction and sends its bytes, so a key never reaches the node.
Ledger command line is a client of these endpoints.
| Endpoint | Returns |
|---|---|
GET /ledger |
The state of the ledger: chain, tip, supply, validators and minimum fee |
GET /ledger/account/{address} |
The balance of an account or a legacy address; for what the legacy chain held, see REST: legacy chain |
GET /ledger/block/{height} |
The block at a height, with its transactions |
POST /ledger/transaction |
Hands a signed transaction to the ledger |
How values are written. An amount is a string of coins with exactly eight decimals, such as
"12.50000000", never a number, so nothing is rounded on the way. A hash is 128 lower-case hex characters. An
account is a public key of 64 lower-case hex characters. A legacy address is base58. A time is in Unix
milliseconds. A field that does not apply is left out, not set to zero. An error is {"error": "<reason>"}.
GET /ledger
GET /ledger answers 200 with these fields:
| Field | Meaning |
|---|---|
running |
The ledger has started and has neither stopped nor halted |
halted, haltReason |
Whether the ledger halted, and why; the reason is empty otherwise |
signing |
The node runs, its newest node was built to sign, and its key is one of the validators of the newest ledger |
chainId |
The id of the chain, 128 hex characters |
height, tipHash |
The height and hash of the newest block; 0 and empty before there is a ledger |
supply |
The coins that exist |
round |
The validator round of the node’s clock, never before the tip’s, which the next block it would make is in and a vote is signed for |
roundEnds |
When that round ends and the next starts, in Unix milliseconds |
validators |
The keys of the validators that sign blocks now, in the order that decides who proposes |
roundValidators |
The set that a vote of round is judged by; it differs from validators between the end of a round and the block that closes it |
minimumFee |
The lowest fee of a transfer that this node takes; left out while the fee spork is not set |
droppedInbound |
How many items the node has dropped from its queue of incoming ledger messages |
unsetSporks, laggingSporks |
The ledger sporks that are not set, and those of which a newer version is known that this node does not hold yet, by name: MINT_STORAGE, MINT_SUPPLY, LEDGER_BLACKLIST and LEDGER_FEE |
A node answers no for a spork that is unset or lagging, so anything that depends on it is not minted, claimed
or taken until it is set. Before the ledger exists the answer holds only running, halted, haltReason,
signing, height, tipHash, droppedInbound and the two lists. A halted ledger is still read from the
blocks it holds.
GET /ledger/account/{address}
GET /ledger/account/{address} takes an account in lower-case hex or a legacy address, and answers 200 also
for an address that holds nothing:
| Field | Account | Legacy address | Meaning |
|---|---|---|---|
address |
yes | yes | The address as the ledger writes it |
kind |
yes | yes | account or legacy |
balance |
yes | yes | What it holds. For a legacy address that is what mints have put on it, and nothing once it is claimed |
nonce |
yes | The number of transfers the account has made, which the next one must carry | |
mintedBySporks |
yes | yes | The sum of the mints made to the address so far, as the mints named it |
claimable |
yes | What a claim would move now: the unclaimed snapshot balance and the mints; 0 once claimed | |
claimedBy |
yes | The account that claimed it, once claimed |
{
"address": "3f2a…c9",
"kind": "account",
"balance": "250.00000000",
"nonce": 3,
"mintedBySporks": "0.00000000"
}
An address in upper-case hex, or one that is neither an account nor a valid legacy address, is answered with
- A 503 means the ledger is not running.
GET /ledger/block/{height}
GET /ledger/block/{height} answers with the block as it was committed:
| Field | Meaning |
|---|---|
height, view, time |
Where it sits, the view it was decided in, and its time |
hash, previousHash |
The hash of the block and of the one before it, 128 hex characters |
stateRoot, transactionRoot |
The roots it commits to |
proposer |
The key of the validator that proposed it |
certifiedBy, endorsements |
The validators whose precommits make its certificate, and how many |
transactions |
The transactions, each with the fields of its kind |
A transaction always has id, the hash of its encoding, and kind. The rest depends on the kind:
| Kind | Other fields |
|---|---|
mint |
recipient, amount |
vote |
voter, candidate, action (add or remove), round |
claim |
recipient, the account that receives the coins, and claimedAddress, the legacy address that was moved |
transfer |
sender, recipient, amount, fee, nonce |
The height is plain digits. A height that is not, or that does not fit a 64-bit number, is answered with 400.
A height of 0, or above the newest block, is answered with 404, and 503 means the ledger is not running.
The fees of a block are paid to the validators that signed the block before it, so they are not those in
certifiedBy, see Ledger.
POST /ledger/transaction
POST /ledger/transaction takes a body of {"transaction": "<hex>"}, where the hex is the encoding of a
claim, a transfer or a vote, see Ledger encoding. The client signs it first. A mint is
refused: mints come only from the sporks. The call waits for the ledger to say whether it takes the
transaction in, which is a check against the state, not a wait for a block. Find the transaction in a block
later by its id.
| Status | Meaning | Body |
|---|---|---|
| 202 | The ledger took it in; it waits for a block | {"accepted": true, "id": "<hex>"} |
| 409 | The ledger refused it, with its own reason, such as Transfer nonce is already used, Balance does not cover the transfer or Fee is below what this node demands |
{"accepted": false, "id": "<hex>", "reason": "<reason>"} |
| 400 | The body is not that JSON, the hex is not valid or is longer than any transaction, the transaction does not decode, or it is a mint | {"error": "<reason>"} |
| 413 | The body is above 4096 bytes | {"error": "<reason>"} |
| 429 | 16 submits already wait for an answer | {"error": "<reason>"} |
| 503 | The ledger is not running, or failed to answer | {"error": "<reason>"} |
| 504 | No answer within 10 seconds; the ledger may still take the transaction in | {"error": "<reason>"} |
A status of 202 means the transaction is waiting, not that a block holds it. A transaction that waits is sent on to the other nodes, and a validator puts it into a block. A transfer whose nonce is above the next one, by less than 16, is taken in too and waits for the transfers before it.
The limit of 16. Every submit that waits holds a thread of the server, and the server has no other limit on them, so at most 16 may wait at once and the next gets 429. A place is given back when the ledger has answered, not when the response is sent, so a client that gives up and tries again cannot queue more than the limit. A place is also given back after 60 seconds at the latest, so a ledger that never answers cannot fill all 16 for good.
In the source
application/src/main/java/org/unigrid/hedgehog/server/rest/LedgerResource.java: the four endpoints; the JSON records they answer with are inserver/rest/entity/(LedgerInfo,LedgerAccount,LedgerBlock,LedgerTransactionView,LedgerSubmissionandLedgerSubmitted).application/src/main/java/org/unigrid/hedgehog/model/bootstrap/LedgerText.java: how addresses and coin amounts are read and written, shared with the command line and the mint spork.