Bloch Genesis-4 developer portal
The integration reference for the Bloch Genesis-4 chain: a post-quantum proof-of-stake L1 with an extended-UTXO ledger and explicit Casper-FFG finality. Written for engineers — exchange integrators, wallet builders, node operators.
This portal documents the network at the deepest honest level. Where something does not exist yet — a transaction index, an EVM, official SDKs, a joinable sync path for brand-new nodes — this page says so plainly instead of papering over it. Every figure here was verified against source commit 72e5525 and the live network on 2026-09-07; where a feature is in source but not yet on the fleet, it is labeled next release.
- Read network parameters and the integer rule first — the latter breaks naive JSON parsers.
- Everything is JSON-RPC 2.0 over HTTP POST: endpoints, method reference.
- Crediting payments? The deposit rule is normative — this chain has no transaction index, tracking works differently than on Bitcoin or Ethereum.
- Running infrastructure? Start at Run a node — including the one current limitation you must know about before provisioning.
Network parameters
| Chain | Bloch Genesis-4 — post-quantum proof-of-stake L1, extended-UTXO (eUTXO) ledger, explicit Casper-FFG finality gadget over an LMD-GHOST fork choice |
| Predecessor | Genesis-3 (proof-of-work), closed at height 39,918; its ledger carried over into Genesis-4's opening state |
| Ticker | BLCH (native asset — there is no token contract) |
| Total supply | 100,000,000,000 BLCH — fixed hard cap, enforced per block |
| Denomination | 8 decimals; 1 BLCH = 100,000,000 sat; total supply = 1019 sat |
| Genesis time | 2026-08-13 21:31:19.962 UTC |
| Slot time | 30 s |
| Epoch | 32 slots (16 min) |
| Genesis validators | 64 |
| Signatures | Hybrid ML-DSA-65 ‖ Falcon-1024 — both components must verify; a signature blob measures ≈ 4,589 bytes |
| Hashing | SHA3-256 / SHAKE-256, domain-separated throughout |
| Custody model | Non-custodial by construction — the node never holds a spending key; all signing is client-side |
| Testnet | None exists. Integrate against mainnet with small amounts, or against your own observer node |
The integer rule: every *_sat field is a string
Every satoshi-denominated RPC field (balance_sat, value_sat, total_active_stake_sat, …) is a decimal string, never a JSON number. The total supply is 1019 sat, which exceeds the 253 exact-integer range of a JavaScript double (and of any IEEE-754 double). A parser that runs these values through JSON.parse as numbers will silently corrupt balances.
Parse every *_sat field as a big integer — BigInt in JS, int in Python, big.Int / uint64-with-care in Go. Plain counts, heights, and slots are ordinary numbers.
// JS: correct handling of *_sat fields
const res = await fetch("https://posternlabs.com/g4rpc", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "getbalance",
params: ["7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000"] })
});
const { result } = await res.json();
const balance = BigInt(result.balance_sat); // string -> BigInt. Never Number().
const utxoCount = result.utxo_count; // plain count -> ordinary number is fine
Endpoints & transport
Public proxy (recommended)
POST https://posternlabs.com/g4rpc
JSON-RPC 2.0 over HTTP POST. The proxy exposes a read-method allowlist plus sendrawtransaction. It is backed by multiple upstream nodes with read corroboration: branch-sensitive reads require a quorum of ≥ 2 upstream nodes agreeing before a response is served. CORS is answered at the proxy, so browser apps can call it directly.
Gateway 2.0.0 cache TTLs are disclosed by getcapabilities (methods[].cache_ttl_ms). Cached results retain corroboration checks; critical reads are never served from a stale fallback. Representative TTLs:
| Method | Cache TTL |
|---|---|
getchaininfo | 3 s |
getblockcount | 3 s |
getvalidatorcount | 60 s; keyed by epoch |
getblockbyslot | 6 s; finalized content up to 7 days, keyed by lineage |
getblockbyid | 7 days for content; finality classification is re-evaluated |
Direct archival nodes
POST http://139.180.166.5:8080 · POST http://139.180.173.231:8080
Two keyless observer nodes, plain HTTP, POST only. No TLS — treat the transport as untrusted and corroborate any answer across both nodes (this is exactly what the public proxy automates for you). Useful when you want raw node behavior without the proxy's cache or allowlist.
Explorer & wallet
- Explorer: blochl1.com — blocks, validators, epochs, analytics, transactions.
- Wallet: posternlabs.com/apps/wallet/ — a PWA with client-side signing; usable as a reference integration for the signing flow documented on this page.
Transport rules
- One call per connection. The node answers with
Connection: close; there is no keep-alive at the node. Pool at your HTTP client's peril — open a fresh connection per request. - Request body cap: 1 MiB.
- Both positional-array and named-object
paramsare accepted. - Batch arrays and notifications are refused with
-32600. Send one JSON-RPC 2.0 call per request with an explicit string, safe integer or nullid.
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"getchaininfo","params":[]}'
JSON-RPC reference — live surface
The methods below are the surface the public proxy and the live fleet serve today. Methods that exist in source but are not yet on the fleet are listed under Next release. All sample responses use illustrative values (heights, hashes, and balances are made up); shapes and field names are the contract.
- JSON-RPC 2.0;
paramspositional ([…]) or named ({…}). - All
*_satfields are decimal strings — see the integer rule. - Hashes and roots are 64-char lowercase hex (32 bytes).
- One call per connection; no batches; 1 MiB body cap.
getchaininfo live
getchaininfo [] → object
One-call snapshot of the node's view: head, finality checkpoints, validator counts, fee state, and transport health. Proxy cache TTL: 3 s.
Parameters
None — pass [].
Response fields
| Field | Type | Notes |
|---|---|---|
block_id | hex-64 | Head block id |
slot, height | number | Head slot / chain height |
finalized_height | number | Height of the finalized checkpoint |
epoch, slot_in_epoch, slots_per_epoch | number | slots_per_epoch is 32 |
state_root | hex-64 | State root at head |
justified, finalized, previous_justified | object | Each {epoch, root} — the FFG checkpoints |
validators | object | {total, active} |
total_active_stake_sat | string | Big integer — parse as BigInt |
base_fee_millisat_per_gas, next_base_fee_millisat_per_gas | string | Current and one-block-look-ahead base fee |
mempool, blocks_known | number | Mempool size; total blocks this node knows |
wall_slot, behind_by_slots | number | Wall-clock slot and how far the node lags it |
transport | object | {name, peers} |
Poll transport.peers as well as behind_by_slots. A node that is partitioned can keep a fresh-looking head (low behind_by_slots) while extending a private fork; peers dropping to 0 is the signal behind_by_slots will not give you.
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getchaininfo","params":[]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"block_id": "7c1e29aa40b3d8f15e6a0c92d4b7f81326c5a90e8f1d3b6407e2c9a51b8d4f60",
"slot": 71182,
"height": 34120,
"finalized_height": 34064,
"epoch": 2224,
"slot_in_epoch": 14,
"slots_per_epoch": 32,
"state_root": "e0a4c1d293b85f671c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7",
"justified": { "epoch": 2223, "root": "44f0b2c9d81e6a35970c4d2f8b1e5a603d7c92f406b8e1a5c2d94f705e8a1b36" },
"finalized": { "epoch": 2222, "root": "b2d94f705e8a1b3644f0b2c9d81e6a35970c4d2f8b1e5a603d7c92f406b8e1a5" },
"previous_justified": { "epoch": 2222, "root": "b2d94f705e8a1b3644f0b2c9d81e6a35970c4d2f8b1e5a603d7c92f406b8e1a5" },
"validators": { "total": 64, "active": 64 },
"total_active_stake_sat": "3200000000000000000",
"base_fee_millisat_per_gas": "1000",
"next_base_fee_millisat_per_gas": "1024",
"mempool": 3,
"blocks_known": 34121,
"wall_slot": 71182,
"behind_by_slots": 0,
"transport": { "name": "devnet", "peers": 8 }
}
}
getblockcount live
getblockcount [] → object
Lightweight height/finality probe — the cheap poll for crediting pipelines. Proxy cache TTL: 3 s.
Parameters
None — pass [].
Response fields
| Field | Type | Notes |
|---|---|---|
height, slot, epoch | number | Head position |
finalized_height | number | null | null before the first finalized checkpoint is known |
justified_epoch, finalized_epoch | number | FFG checkpoint epochs |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getblockcount","params":[]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"height": 34120,
"slot": 71182,
"epoch": 2224,
"finalized_height": 34064,
"justified_epoch": 2223,
"finalized_epoch": 2222
}
}
getblockbyslot / getblockbyid live
getblockbyslot [slot] → block · getblockbyid [block_id hex-64] → block
Fetch one canonical block, by slot or by id. Both return the same block object. Under proof of stake, empty slots are normal: getblockbyslot answers -32007 SLOT_EMPTY when no canonical block occupies the slot — advance to the next slot, it is not an error condition. Proxy cache TTLs: 6 s by slot (up to 7 days for finalized, lineage-pinned content), 7 days by id. Finality classification is re-evaluated rather than cached as immutable.
Parameters
| Position | Name | Type | Notes |
|---|---|---|---|
| 0 | slot / block_id | number / hex-64 | Slot number, or 64-hex block id |
Response fields (block object)
| Field | Type | Notes |
|---|---|---|
block_id | hex-64 | Consensus block id — clients recompute it over the header |
version | number | Always 2970353669 — the raw 0xB10C0005 network magic, exposed verbatim by design; do not expect a small integer |
parent | hex-64 | Parent block id |
slot, epoch, height, proposer_index | number | |
timestamp | number | Block time, unix seconds (genesis + slot × 30 s) |
state_root, body_root | hex-64 | |
randao_reveal, randao_mix | hex | Reveal is a full hybrid signature (≈ 4,589 B); mix is 32 bytes |
justified_root, finalized_root | hex-64 | FFG votes carried by this block |
attestation_root, coherence_root | hex-64 | |
finality | string | "finalized" | "justified" | … — this block's finality standing |
finalized | boolean | Convenience flag |
tx_count, attestation_count | number | Counts only — the block object does not inline transactions |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getblockbyslot","params":[71180]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"block_id": "7c1e29aa40b3d8f15e6a0c92d4b7f81326c5a90e8f1d3b6407e2c9a51b8d4f60",
"version": 2970353669,
"parent": "9d3b6407e2c9a51b8d4f607c1e29aa40b3d8f15e6a0c92d4b7f81326c5a90e8f",
"slot": 71180,
"epoch": 2224,
"height": 34118,
"proposer_index": 41,
"timestamp": 1788792079,
"state_root": "e0a4c1d293b85f671c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7",
"body_root": "970c4d2f8b1e5a603d7c92f406b8e1a5c2d94f705e8a1b3644f0b2c9d81e6a35",
"randao_reveal": "a3b1…(hybrid signature, ~4589 bytes hex, truncated here)",
"randao_mix": "a5c3e71b94d6f28c07e1a538b2f4d96e05a7c31d48f6b20f31a7c059e64b2d80",
"justified_root": "44f0b2c9d81e6a35970c4d2f8b1e5a603d7c92f406b8e1a5c2d94f705e8a1b36",
"finalized_root": "b2d94f705e8a1b3644f0b2c9d81e6a35970c4d2f8b1e5a603d7c92f406b8e1a5",
"attestation_root": "0c92d4b7f81326c5a90e8f1d3b6407e2c9a51b8d4f607c1e29aa40b3d8f15e6a",
"coherence_root": "1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7e0a4c1d293b85f67",
"finality": "justified",
"finalized": false,
"tx_count": 1,
"attestation_count": 42
}
}
Empty slot
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32007, "message": "no canonical block at slot" }
}
getvalidator live
getvalidator [index u32] → object
One validator's registry entry by index.
Parameters
| Position | Name | Type | Notes |
|---|---|---|---|
| 0 | index | u32 | Validator index; unknown index → -32001 |
Response fields
| Field | Type | Notes |
|---|---|---|
index | number | |
pubkey_hash | hex-64 | SHA3-256 of the raw public key |
pubkey_bytes | number | Length of the raw hybrid public key in bytes |
state | string | e.g. "active" |
own_stake_sat | string | |
effective_stake_sat | string | null | |
commission_bps | string | Basis points, as a string on this method |
randao_commitment | hex-64 | |
slashed | boolean | |
activation_epoch, exit_epoch, withdrawable_epoch | number | null |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getvalidator","params":[41]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"index": 41,
"pubkey_hash": "5a0e8d21c47bf93602de5a81b64c9f307e12ad85f09c36b4d871e25a40c3b96f",
"pubkey_bytes": 3749,
"state": "active",
"own_stake_sat": "50000000000000000",
"effective_stake_sat": "50000000000000000",
"commission_bps": "500",
"randao_commitment": "d871e25a40c3b96f5a0e8d21c47bf93602de5a81b64c9f307e12ad85f09c36b4",
"slashed": false,
"activation_epoch": 0,
"exit_epoch": null,
"withdrawable_epoch": null
}
}
getvalidatorcount live
getvalidatorcount [] → object
Aggregate validator statistics. Proxy cache TTL: 60 s, keyed by epoch.
Parameters
None — pass [].
Response fields
| Field | Type | Notes |
|---|---|---|
total, active | number | |
total_active_stake_sat | string |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getvalidatorcount","params":[]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": { "total": 64, "active": 64, "total_active_stake_sat": "3200000000000000000" }
}
getbalance live
getbalance [script_hash hex-64] → object
Balance and UTXO count for one script hash. This is the cheap poll for deposit detection. See Addresses & keys for how to derive a script_hash from an address.
Parameters
| Position | Name | Type | Notes |
|---|---|---|---|
| 0 | script_hash | hex-64 | 32-byte script hash (Carried or Native form) |
Response fields
| Field | Type | Notes |
|---|---|---|
script_hash | hex-64 | Echoed |
balance_sat | string | Parse as BigInt |
utxo_count | number (u64) | Reference total for UTXO enumeration — see getutxos |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getbalance","params":["7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000"]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"script_hash": "7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000",
"balance_sat": "125000000000",
"utxo_count": 3
}
}
getutxos / listunspent live
getutxos [script_hash hex-64, limit?] → object · listunspent is an alias
Unspent outputs for a script hash. limit defaults to 100 and is clamped to 1..=1000.
limit is a count, not a page: repeated calls return the same first page. You cannot enumerate past the first 1,000 outputs with this method. The working pattern:
- Use
getbalance.utxo_countas the reference total; if it exceedsreturned, you are not seeing everything. - Keep hot addresses under 1,000 outputs (consolidate).
gettxout(txid, vout)is the only exact single-output check past the first page.
Parameters
| Position | Name | Type | Notes |
|---|---|---|---|
| 0 | script_hash | hex-64 | Required |
| 1 | limit | number | Optional; default 100; clamped 1..=1000 |
Response fields
| Field | Type | Notes |
|---|---|---|
script_hash | hex-64 | Echoed |
total, returned | number | |
truncated | boolean | true when more outputs exist than were returned |
utxos[] | array | Each {txid, vout, value_sat (string), script_hash} |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getutxos","params":["7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000", 100]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"script_hash": "7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000",
"total": 3,
"returned": 3,
"truncated": false,
"utxos": [
{ "txid": "f31a7c059e64b2d80a5c3e71b94d6f28c07e1a538b2f4d96e05a7c31d48f6b20",
"vout": 0, "value_sat": "100000000000",
"script_hash": "7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000" },
{ "txid": "28c07e1a538b2f4d96e05a7c31d48f6b20f31a7c059e64b2d80a5c3e71b94d6f",
"vout": 1, "value_sat": "20000000000",
"script_hash": "7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000" },
{ "txid": "6f28c07e1a538b2f4d96e05a7c31d48f6b20f31a7c059e64b2d80a5c3e71b94d",
"vout": 0, "value_sat": "5000000000",
"script_hash": "7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000" }
]
}
}
gettxout live
gettxout [txid hex-64, vout?] → object
Exact spent/unspent status for one specific outpoint. This is the precision instrument of the whole read surface: it works regardless of how many outputs an address holds.
at_slot is the answering node's head — not the output's age
at_slot is the node's current head slot at the moment it answered, NOT the slot the output was created in. No RPC exposes an output's creation height. If you need "this output existed at or before height H", read getblockcount in the same round-trip — see the crediting rule.
Parameters
| Position | Name | Type | Notes |
|---|---|---|---|
| 0 | txid | hex-64 | Client-computed consensus txid |
| 1 | vout | number | Optional |
Response fields
| Field | Type | Notes |
|---|---|---|
txid, vout | hex-64 / number | Echoed |
unspent | boolean | |
utxo | object | null | {txid, vout, value_sat (string), script_hash} when unspent |
at_slot | number | Answering node's current head slot — see callout above |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"gettxout","params":["f31a7c059e64b2d80a5c3e71b94d6f28c07e1a538b2f4d96e05a7c31d48f6b20", 0]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"txid": "f31a7c059e64b2d80a5c3e71b94d6f28c07e1a538b2f4d96e05a7c31d48f6b20",
"vout": 0,
"unspent": true,
"utxo": {
"txid": "f31a7c059e64b2d80a5c3e71b94d6f28c07e1a538b2f4d96e05a7c31d48f6b20",
"vout": 0,
"value_sat": "100000000000",
"script_hash": "7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000"
},
"at_slot": 71182
}
}
getmempoolinfo live
getmempoolinfo [] → object
Mempool occupancy and fee look-ahead. Read next_base_fee_millisat_per_gas here or from getchaininfo immediately before building a transaction.
Parameters
None — pass [].
Response fields
| Field | Type | Notes |
|---|---|---|
size, max | number | max is 4096 |
bytes | number | Total serialized bytes queued |
next_base_fee_millisat_per_gas | string | One-block look-ahead base fee |
barred, barred_hits | number | Currently barred sources / hits against bars |
expired, evicted_low_fee | number | Lifetime drop counters (TTL expiry / fee eviction) |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getmempoolinfo","params":[]}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"size": 2,
"max": 4096,
"bytes": 9178,
"next_base_fee_millisat_per_gas": "1024",
"barred": 0,
"barred_hits": 0,
"expired": 1,
"evicted_low_fee": 0
}
}
sendrawtransaction live
sendrawtransaction [hex] → object
Gateway 2.0.0 submits to one configured observer, without caching, fan-out or automatic retry. Node success and refusal responses are preserved; transport uncertainty is reported as -32052. Broadcast limits per isolate: burst 4, sustained 2/s, at most 2 concurrent attempts.
Submit signed transaction bytes. The response is a queue acknowledgement, not inclusion — the transaction has been admitted to the mempool, nothing more. Track inclusion yourself, by the txid you computed client-side before broadcast (see Transactions & fees).
txid field in the response — deliberately
The tx_hash field is a node-local digest over the raw bytes, not the consensus txid. The consensus txid is SHA3-256("BLCH4:TXID" ‖ spend_signing_root), computable client-side before you broadcast. Everything downstream (outpoints, gettxout, reconciliation) keys on the txid you computed — never on tx_hash.
Parameters
| Position | Name | Type | Notes |
|---|---|---|---|
| 0 | hex | string | Serialized signed transaction, hex-encoded |
Response fields
| Field | Type | Notes |
|---|---|---|
accepted | boolean | true on admission |
status, kind | string | Queue status; transaction kind |
bytes | number | Size as admitted |
tx_hash | hex-64 | Node-local digest over raw bytes — not the consensus txid |
tx_hash_note | string | The node says the above itself |
confirmation | string | States that this is queue acknowledgement, not inclusion |
Example
curl -s -X POST https://posternlabs.com/g4rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"sendrawtransaction","params":{"hex":"06a1b2c3…"}}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"accepted": true,
"status": "accepted",
"kind": "transfer_v2",
"bytes": 4890,
"tx_hash": "b94d6f28c07e1a538b2f4d96e05a7c31d48f6b20f31a7c059e64b2d80a5c3e71",
"tx_hash_note": "node-local digest over raw bytes; not the consensus txid — compute the txid client-side",
"confirmation": "queue acknowledgement only; not evidence of inclusion"
}
}
Resubmitting byte-identical bytes while the transaction is pending returns Admitted::Duplicate — a success, not an error. See error codes for the retry discipline on refusals.
Methods that are routed but refuse — by design
gettransaction [txid] → error -32005 · getnewaddress [] → error -32006
gettransactionrefuses — permanently answers-32005 NO_TRANSACTION_INDEX. This chain has no txid→block index, by design. There is no server-side transaction lookup to wait for; build your tracking on client-computed txids +gettxout/getutxoscorrelation. See Transactions & fees.getnewaddressrefuses — answers-32006 NO_WALLET. The node holds no wallet and no keys; address generation is a client-side operation (Addresses & keys).
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32005, "message": "no transaction index on this chain (by design)" }
}
Next-release methods
The three methods below are in source at commit 72e5525 and arrive with the next fleet release. Today both the public proxy and the direct nodes refuse them. Documented here so you can build against the shapes; do not ship code that assumes they answer yet.
getvalidators next release
getvalidators [] → array
The whole validator registry in one call: for each validator, index, pubkey_hash, status, effective_stake_sat (string), and commission_bps. Note an inconsistency to plan for: commission_bps is a plain number on this method, while getvalidator returns it as a string.
gettxstatus next release
gettxstatus [txid hex-64] → { status }
Returns {"status": "pending" | "included" | "justified" | "finalized" | "unknown"}. This is a tracking convenience, not a settlement signal: do not use its "finalized" string as the crediting trigger — the crediting rule (finalized-height cross-reference, prudence margin, two-node agreement) remains normative.
getbuildinfo next release
getbuildinfo [] → object
Returns build_version, commit, source_digest (SHA3-256 over the source set), rustc, target, and similar build provenance. Operational use: compare source_digest across the nodes you trust — two nodes claiming the same version with different digests are not running the same code.
Error codes
Standard JSON-RPC
| Code | Meaning |
|---|---|
-32700 | Parse error |
-32600 | Invalid request — also returned for batch arrays, which are refused: one call per request |
-32601 | Method not found |
-32602 | Invalid params |
-32603 | Internal error |
Dedicated codes
| Code | Name | Meaning & handling |
|---|---|---|
-32000 | BLOCK_NOT_FOUND | No such block |
-32001 | VALIDATOR_NOT_FOUND | No such validator index |
-32002 | TX_DECODE_FAILED | Bytes did not decode as a transaction |
-32003 | MEMPOOL_FULL | Capacity, not a verdict on your transaction — retry later |
-32004 | NODE_UNAVAILABLE | Consensus thread busy — retry |
-32005 | NO_TRANSACTION_INDEX | Permanent. This chain has no txid→block index by design |
-32006 | NO_WALLET | Permanent. The node holds no wallet |
-32007 | SLOT_EMPTY | Normal under PoS — no canonical block at that slot; advance |
-32008 | TX_REFUSED | Terminal. Never resubmit those bytes; rebuild the transaction |
-32009 | TX_REFUSED_RETRYABLE | State-dependent bar that lapses; error.data = {retryable: true, until_slot: N} — bar duration ≈ 64 min (REJECTION_TTL_SLOTS = 128) |
-32052 | SUBMISSION_UNKNOWN (gateway) | Reconcile status before retrying the same bytes; never infer rejection |
-32051 | NO_QUORUM (proxy) | Upstream disagreement; withhold credit, back off and retry |
-32010 | see callout | Node source capacity; legacy proxies may reuse this code — see below |
posternlabs.com/g4rpc (gateway 2.0.0) and blochl1.com/rpc (explorer 1.2.0) use -32051 for NO_QUORUM. Withhold credit, back off and retry. The Postern gateway refuses critical reads without two agreeing archival responses and fresh fleet corroboration; it does not fall back to a single node or stale cache. getcapabilities advertises the running version and error_codes.no_quorum.
-32010remains the node’sTX_REFUSED_SOURCE_CAP: a spend-authority source already has 64 pending transactions. Conditionally retryable, not terminal. Wait for capacity, then retry if the transaction remains valid. There is no fixeduntil_slot.-32052means the gateway could not determine the outcome of a submitted transaction. Check transaction status and expected outpoints before retrying the same signed bytes. Do not build a second payment from this error.- Legacy deployments may still use
-32010for read quorum. Log endpoint, method, version and raw error when diagnosing older responses.
Proxy corroboration does not establish independent operation of its upstreams. The independent-observer deposit-crediting rule still applies. Terminal transaction-byte refusal remains -32008.
Retry discipline
-32008: rebuild, never resubmit. The bytes are terminally refused.-32009: retry afteruntil_slot, or rebuild against the current base fee — usually faster.-32003/-32004: capacity/availability — back off and retry the same bytes.- Identical duplicate bytes while pending:
Admitted::Duplicate— a success response, not an error. Safe to use as an idempotent redelivery strategy.
Transactions & fees
The txid: computed by you, before broadcast
txid = SHA3-256( "BLCH4:TXID" ‖ spend_signing_root )
The consensus transaction id is deterministic and non-malleable — it excludes witnesses, so no third party can mutate it in flight. Because it is a pure function of the spend signing root, you compute it client-side, before broadcast. Outpoints are (txid, vout). This txid is how you track everything on this chain: the node will never look a transaction up for you (no transaction index, by design), but gettxout(txid, vout) answers exactly for any outpoint you know.
Fees: exactly one valid base fee
- A transaction commits to exactly one valid base fee — the one committed by the block that includes it. There is no fee range, no tolerance.
- Read
next_base_fee_millisat_per_gas(fromgetchaininfoorgetmempoolinfo) immediately before building — it is a one-block look-ahead. - If the base fee moves before your transaction is included, the signed bytes fail
ValueNotConserved. Rebuild against the new fee; do not resubmit. - Conservation is an equality:
spent == created + fee— not a tolerance.
TransferV2 (wire tag 0x06)
Live since epoch 800. Witness-table format: ~40 bytes per input instead of full per-input keys as in V1. The practical input ceiling rises from ~61 inputs (V1) to ~1,000 — which is also the UTXO enumeration bound (getutxos limit clamp), so the two constraints align. V1 remains valid; a single-input payment may reasonably choose V1.
Mempool & block mechanics
- Transactions in a block are packed by
tip_millisat_per_gasdescending. - Per-source pending cap: 64 transactions per spend-authority source (
-32010at the node when exceeded). - Mempool TTL: 100 slots unincluded → dropped. Rebuild and resubmit after expiry (the fee has almost certainly moved by then anyway).
There is no fee/tip history endpoint and no way to ask the chain "what did transaction X pay, and when was it included?" after the fact. Correlate client-side, keyed by the txid you computed before broadcast: record (txid, inputs, outputs, fee, next_base_fee at build time) in your own database at build time, then confirm outpoint existence via gettxout/getutxos. Plan fee-reconciliation tooling around this from day one — it cannot be bolted on later by querying the chain.
Client-side signing flow
- Select inputs —
getutxosfor the first page;gettxoutfor exact checks. - Read
next_base_fee_millisat_per_gas— immediately before building. - Set tip and outputs.
- Fix the
tx_bytesbudget — Falcon signatures vary in length; budget the declared size, not the size of one sample signature. - Sign — the
spend_signing_rootfixes the txid at this moment; record it. - Broadcast —
sendrawtransaction {"hex": …}; track by your recorded txid.
Addresses & keys
Address format
bloch1q + 48 hex = 55 characters
- The 48 hex characters are: 40 hex = the 20-byte SHA3-256 prefix of the raw public key (this is not RIPEMD — there is no hash160 anywhere), followed by 8 hex = a SHA3 double-checksum.
- The network emits lowercase. The reference parser accepts mixed-case hex — validate leniently, emit lowercase.
# illustrative address anatomy
bloch1q 7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4 0a9b3c7d
prefix 20-byte SHA3-256 prefix of raw pubkey checksum
(7) (40 hex) (8 hex)
script_hash — what every balance/UTXO method keys on
The 32-byte script_hash has two valid forms:
| Form | Construction | Derivable from an address? |
|---|---|---|
| Carried (legacy-compatible) | The 20 address bytes + 12 zero bytes | Yes — from the address string alone |
| Native | Full 32-byte hash | No — requires the raw public key; not expressible as an address |
To derive the Carried script_hash from an address: strip bloch1q, take the first 40 hex characters, zero-pad to 64 hex.
# address -> Carried script_hash (shell)
addr="bloch1q7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f40a9b3c7d"
hex="${addr#bloch1q}" # drop the prefix
hex="${hex:0:40}" # 20 address bytes (drops the 8-hex checksum)
printf '%s%024d\n' "$hex" 0 # zero-pad to 64 hex
# -> 7d02c4a19e5f60b3d8a1f2c47e9b0d6a35c8e1f4000000000000000000000000
// address -> Carried script_hash (JS)
function scriptHashFromAddress(addr) {
const a = addr.toLowerCase();
if (!/^bloch1q[0-9a-f]{48}$/.test(a)) throw new Error("not a bloch address");
// NOTE: a full implementation verifies the 8-hex SHA3 double-checksum here.
return a.slice(7, 47).padEnd(64, "0");
}
Keys are hybrid ML-DSA-65 ‖ Falcon-1024; the node never sees them — generation, storage, and signing are entirely client-side (see the signing flow).
Crediting deposits — the rule
Normative procedure for anyone crediting incoming payments: exchanges, merchants, custody providers. It exists because this chain has no transaction index and no per-output creation height — detection and settlement are built from primitives.
1. Detect
- Poll
getbalance(script_hash)cheaply per deposit address. - On change, call
getutxosto see which outpoints arrived. - Use
gettxout(txid, vout)to confirm a specific expected payment — the only exact check for addresses past 1,000 outputs.
2. Cross-reference an observed height
When gettxout shows unspent: true, read height H from getblockcount in the same round-trip. You now know: the output existed at or before height H. (Remember: gettxout.at_slot is the node's head, not the output's age — this cross-reference is the substitute for a creation height the RPC does not expose.)
3. Credit
Credit the deposit when all of the following hold:
finalized_height ≥ H, plus- a prudence margin of ~30 epochs (~8 h) of continued, uninterrupted finality advance beyond that, and
- two independently operated nodes agree on the same finalized root at the same epoch.
Reverify immediately before releasing funds.
An exchange with no established observer and no verified signed checkpoint must keep deposit crediting disabled. An operator-supplied data directory, two endpoints from one operator, a proxy quorum, and a transaction-format exercise do not by themselves satisfy this rule. No verified alternative observer pair is established here.
Finality on this young network is not yet backed by live slashing economics. The ~30-epoch margin and the two-node agreement requirement are deliberate compensations. Choosing a lower margin is a legitimate business decision — but document it as an accepted risk, not as "finality means final".
Reorg handling
Above the finalized checkpoint, reorgs to any depth are normal LMD-GHOST behavior for included-but-unfinalized transactions. An included transaction is not settled; only the rule above settles.
Run a node
The source is public: github.com/tiagobeltraoacioli-sketch/bloch-sis-pow. Running your own observer node is the recommended posture for any serious integrator: it validates every block independently, serves the full RPC surface, and signs nothing (no validator.key).
A fresh node requires an authenticated signed checkpoint after the genesis trust window. The published epoch-1536 verification record reports three valid signatures with a 2-of-3 quorum. A keyless fresh-sync test passed on September 14. The checkpoint is refused for initial sync at epoch 3552 (2026-09-22 08:43:19 UTC).
Signature validity does not establish signer identity or independence. External roles in this signer set are declarations; the validator-set root is an all-zero placeholder accepted by this release. The operator confirms the ceremony took place on September 4, 2026; independent evidence of the specified Phase-A signer identities and custody arrangement is still not established by the published verification record. See the dated status and remaining assurances.
Authenticate the envelope, signer set and fingerprints through independent trusted channels. An operator-supplied data directory or two endpoints under one operator do not establish independent historical trust. Meet the crediting requirements before crediting deposits. Observer operation needs no validator key, bond or admission transaction.
Requirements
- Linux x86-64; 2+ vCPU, 8 GB RAM, 80 GB SSD.
- Full replay from genesis measured ≈ 21–22 min (~34k blocks, peak RSS ~934 MB). Replay cost is O(chain length) — there is no snapshot layer yet, so expect this to grow with the chain.
Download and verify the pinned release
Do not build an unpinned main revision for this network. The deployed release is 58da5ce209acb6e293b3b381dfd826dedb8741fd: metadata, checksums and source archive. Linux x86-64 requires glibc 2.34 or newer.
Download and read the public preparation script. It verifies pinned artifacts, stages the signed checkpoint and creates the launch script without generating a validator key or broadcasting a transaction. Separately authenticate the signer set before trusting the checkpoint.
curl --fail --show-error --location \
--output prepare-node.sh https://blochl1.com/downloads/prepare-node.sh
less prepare-node.sh
bash prepare-node.sh "$HOME/bloch-observer-20260914"
cd "$HOME/bloch-observer-20260914"
Run as a keyless observer
./run-node.sh
Do not run the identity helper or deposit steps. The generated launch script supplies the checkpoint and signer-set arguments required for fresh synchronization. Public bootnodes are 139.180.166.5:19100 and 139.180.173.231:19100. Keep RPC bound to loopback; use an authenticated proxy if remote access is required.
Wait for full replay, a head following the network and advancing finality. See sections 1–2 of the pinned installation instructions and the timestamped fresh-observer test. That test does not certify signer independence or exchange crediting readiness.
Observability
Monitor synchronization and finality through getchaininfo. The pinned release also supports an optional metrics endpoint configured with --metrics-port; keep operational endpoints private.
SDKs & tooling
The repository's sdk/typescript, sdk/python and sdk/go directories are legacy Genesis-3-era client scaffolds — machine-generated from an OpenAPI spec of the retired proof-of-work chain, explicitly labeled scaffold / pre-production / unaudited. Their method surface, constants, and disclosures describe the closed Genesis-3 chain and must not be pointed at Genesis-4. Treat them as historical reference material only.
The practical integration path today:
- Direct JSON-RPC. This portal's method reference is the contract — the surface is small enough that a thin typed client in your own codebase is an afternoon's work.
- The wallet PWA as reference implementation. posternlabs.com/apps/wallet/ signs entirely client-side; study it for the transaction-building and signing flow.
- Contributing a Genesis-4 typed client is the natural community contribution: the frozen method registry in the node source (
src/rpc/method_registry.rs, withtests/rpc_method_registry.rspinning it) is the surface to generate from.
Other tooling: the explorer at blochl1.com (blocks, validators, epochs, analytics, transactions).
Integration reference documents for exchanges and partners exist and are provided on request via Postern Labs — they are not published here.
EVM roadmap & status
No EVM-compatible execution layer is operating on Genesis-4. There is no EVM node software to download, and offering one here would be a lie.
What is true, precisely:
- The Genesis-4 L1 is an eUTXO chain. Smart-contract execution is roadmap, not product.
- Chain id 8400 (
0x20d0) is reserved, but it refers to a legacy Genesis-3-era L2 scaffold that is retired, not extended. Please do not deploy against chain id 8400 expecting a current network — nothing is listening on the other end that you should build on. - The successor plan — EVM at L1 — is an explicit draft with no code on either track yet.
If you need EVM execution for your use case, the honest advice is: not here, not yet. Watch this page and the repository for the flag-day announcement when an execution layer ships; this section will change from a warning into documentation on that day.
Security & disclosure
Coordinated disclosure
- Report via the repository's security advisory flow, or encrypted contact, with the subject tag
[bloch-security]. - Acknowledgement target: ≤ 2 days.
- In scope: Genesis-4 consensus, P2P/RPC surfaces, hybrid signature verification, privacy findings.
Audit status — stated plainly
No external third-party audit of the post-quantum stack has been contracted to date. Internal tool-assisted audits are continuous. If your risk model requires an external audit, that requirement is not yet met — factor it in.
Decentralization — stated plainly
The chain is young and run by a small operator set today; the validator-opening program is staged. Do not build assumptions of a large independent validator set into your threat model yet — the crediting rule's margins exist precisely because of this.