chore: initial commit for v0.0.1
DChain single-node blockchain + React Native messenger client. Core: - PBFT consensus with multi-sig validator admission + equivocation slashing - BadgerDB + schema migration scaffold (CurrentSchemaVersion=0) - libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1) - Native Go contracts (username_registry) alongside WASM (wazero) - WebSocket gateway with topic-based fanout + Ed25519-nonce auth - Relay mailbox with NaCl envelope encryption (X25519 + Ed25519) - Prometheus /metrics, per-IP rate limit, body-size cap Deployment: - Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus - 3-node dev compose (docker-compose.yml) with mocked internet topology - 3-validator prod compose (deploy/prod/) for federation - Auto-update from Gitea via /api/update-check + systemd timer - Build-time version injection (ldflags → node --version) - UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER) Client (client-app/): - Expo / React Native / NativeWind - E2E NaCl encryption, typing indicator, contact requests - Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch Documentation: - README.md, CHANGELOG.md, CONTEXT.md - deploy/single/README.md with 6 operator scenarios - deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design - docs/contracts/*.md per contract
This commit is contained in:
23
node/swagger/index.html
Normal file
23
node/swagger/index.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>DChain API Docs</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
||||
<script>
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: "/swagger/openapi.json",
|
||||
dom_id: "#swagger-ui",
|
||||
deepLinking: true,
|
||||
docExpansion: "list",
|
||||
defaultModelsExpandDepth: 1
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
727
node/swagger/openapi.json
Normal file
727
node/swagger/openapi.json
Normal file
@@ -0,0 +1,727 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "DChain Node API",
|
||||
"version": "0.4.0",
|
||||
"description": "API for reading blockchain state, submitting transactions, and using the relay mailbox. All token amounts are in micro-tokens (µT). 1 T = 1 000 000 µT."
|
||||
},
|
||||
"servers": [{ "url": "/" }],
|
||||
"tags": [
|
||||
{ "name": "chain", "description": "V2 chain-compatible endpoints" },
|
||||
{ "name": "explorer", "description": "Block explorer read API" },
|
||||
{ "name": "relay", "description": "Encrypted relay mailbox" }
|
||||
],
|
||||
"paths": {
|
||||
"/api/netstats": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Network statistics",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Aggregate chain stats",
|
||||
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/NetStats" } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/blocks": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Recent blocks",
|
||||
"parameters": [
|
||||
{ "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 100 } }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Array of recent block summaries",
|
||||
"content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/BlockSummary" } } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/block/{index}": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Block by index",
|
||||
"parameters": [
|
||||
{ "name": "index", "in": "path", "required": true, "schema": { "type": "integer", "format": "uint64" } }
|
||||
],
|
||||
"responses": {
|
||||
"200": { "description": "Block detail", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BlockDetail" } } } },
|
||||
"404": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/txs/recent": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Recent transactions",
|
||||
"parameters": [
|
||||
{ "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 100 } }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Recent transactions",
|
||||
"content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/TxListEntry" } } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/tx/{txid}": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Transaction by ID",
|
||||
"parameters": [
|
||||
{ "name": "txid", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": {
|
||||
"200": { "description": "Transaction detail", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TxDetail" } } } },
|
||||
"404": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/tx": {
|
||||
"post": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Submit a signed transaction",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Transaction" } } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubmitTransactionResponse" } } } },
|
||||
"400": { "$ref": "#/components/responses/Error" },
|
||||
"500": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/address/{addr}": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Address balance and transactions",
|
||||
"description": "Accepts a DC... wallet address or hex Ed25519 public key.",
|
||||
"parameters": [
|
||||
{ "name": "addr", "in": "path", "required": true, "schema": { "type": "string" }, "description": "DC... address or hex pub key" },
|
||||
{ "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50 } },
|
||||
{ "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
|
||||
],
|
||||
"responses": {
|
||||
"200": { "description": "Address detail", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AddressDetail" } } } },
|
||||
"404": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/node/{pubkey}": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Node reputation and stats",
|
||||
"parameters": [
|
||||
{ "name": "pubkey", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Hex Ed25519 pub key or DC... address" },
|
||||
{ "name": "window", "in": "query", "schema": { "type": "integer", "default": 200 }, "description": "Number of recent blocks to scan for rewards" }
|
||||
],
|
||||
"responses": {
|
||||
"200": { "description": "Node stats", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NodeStats" } } } },
|
||||
"404": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/relays": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Registered relay nodes",
|
||||
"description": "Returns all nodes that have submitted an REGISTER_RELAY transaction.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of relay nodes",
|
||||
"content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/RegisteredRelayInfo" } } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/validators": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Active validator set",
|
||||
"description": "Returns the current on-chain validator set. The set changes via ADD_VALIDATOR / REMOVE_VALIDATOR transactions.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Active validators",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"count": { "type": "integer" },
|
||||
"validators": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pub_key": { "type": "string" },
|
||||
"address": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/identity/{pubkey}": {
|
||||
"get": {
|
||||
"tags": ["explorer"],
|
||||
"summary": "Identity info (Ed25519 + X25519 keys)",
|
||||
"description": "Returns identity info for a pub key or DC address. x25519_pub is populated only if the identity has submitted a REGISTER_KEY transaction with an X25519 key.",
|
||||
"parameters": [
|
||||
{ "name": "pubkey", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Hex Ed25519 pub key or DC... address" }
|
||||
],
|
||||
"responses": {
|
||||
"200": { "description": "Identity info", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IdentityInfo" } } } },
|
||||
"404": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/relay/send": {
|
||||
"post": {
|
||||
"tags": ["relay"],
|
||||
"summary": "Send an encrypted message via the relay node",
|
||||
"description": "The relay node seals the message using its own X25519 keypair (sender = relay node) and broadcasts it on gossipsub. No on-chain fee is attached.",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["recipient_pub", "msg_b64"],
|
||||
"properties": {
|
||||
"recipient_pub": { "type": "string", "description": "Hex X25519 public key of the recipient" },
|
||||
"msg_b64": { "type": "string", "description": "Base64-encoded plaintext message" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Message sent",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string", "description": "Envelope ID" },
|
||||
"recipient_pub": { "type": "string" },
|
||||
"status": { "type": "string", "example": "sent" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "$ref": "#/components/responses/Error" },
|
||||
"503": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/relay/broadcast": {
|
||||
"post": {
|
||||
"tags": ["relay"],
|
||||
"summary": "Broadcast a pre-sealed envelope",
|
||||
"description": "Light clients that seal their own NaCl-box envelopes use this to publish without a direct libp2p connection. The node stores it in the mailbox and gossips it.",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["envelope"],
|
||||
"properties": {
|
||||
"envelope": { "$ref": "#/components/schemas/Envelope" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Broadcast accepted",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"status": { "type": "string", "example": "broadcast" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "$ref": "#/components/responses/Error" },
|
||||
"503": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/relay/inbox": {
|
||||
"get": {
|
||||
"tags": ["relay"],
|
||||
"summary": "List inbox envelopes",
|
||||
"description": "Returns envelopes stored for the given X25519 public key. Envelopes remain encrypted — the relay cannot read them.",
|
||||
"parameters": [
|
||||
{ "name": "pub", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Hex X25519 public key of the recipient" },
|
||||
{ "name": "since", "in": "query", "schema": { "type": "integer", "format": "int64" }, "description": "Unix timestamp — return only messages after this time" },
|
||||
{ "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50 } }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Inbox contents",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pub": { "type": "string" },
|
||||
"count": { "type": "integer" },
|
||||
"has_more": { "type": "boolean" },
|
||||
"items": { "type": "array", "items": { "$ref": "#/components/schemas/InboxItem" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/relay/inbox/count": {
|
||||
"get": {
|
||||
"tags": ["relay"],
|
||||
"summary": "Count inbox envelopes",
|
||||
"parameters": [
|
||||
{ "name": "pub", "in": "query", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Envelope count",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pub": { "type": "string" },
|
||||
"count": { "type": "integer" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/relay/inbox/{envID}": {
|
||||
"delete": {
|
||||
"tags": ["relay"],
|
||||
"summary": "Delete an envelope from the inbox",
|
||||
"parameters": [
|
||||
{ "name": "envID", "in": "path", "required": true, "schema": { "type": "string" } },
|
||||
{ "name": "pub", "in": "query", "required": true, "schema": { "type": "string" }, "description": "X25519 pub key of the inbox owner" }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Deleted",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"status": { "type": "string", "example": "deleted" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/relay/contacts": {
|
||||
"get": {
|
||||
"tags": ["relay"],
|
||||
"summary": "Incoming contact requests",
|
||||
"description": "Returns all CONTACT_REQUEST records for the given Ed25519 pub key, including pending, accepted, and blocked.",
|
||||
"parameters": [
|
||||
{ "name": "pub", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Hex Ed25519 pub key" }
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Contact list",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pub": { "type": "string" },
|
||||
"count": { "type": "integer" },
|
||||
"contacts": { "type": "array", "items": { "$ref": "#/components/schemas/ContactInfo" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/chain/accounts/{account_id}/transactions": {
|
||||
"get": {
|
||||
"tags": ["chain"],
|
||||
"summary": "Account transactions",
|
||||
"parameters": [
|
||||
{ "name": "account_id", "in": "path", "required": true, "schema": { "type": "string" } },
|
||||
{ "name": "limit", "in": "query", "schema": { "type": "integer", "default": 100, "minimum": 1, "maximum": 1000 } },
|
||||
{ "name": "order", "in": "query", "schema": { "type": "string", "enum": ["desc", "asc"], "default": "desc" } },
|
||||
{ "name": "after_block", "in": "query", "schema": { "type": "integer", "format": "uint64" } },
|
||||
{ "name": "before_block", "in": "query", "schema": { "type": "integer", "format": "uint64" } }
|
||||
],
|
||||
"responses": {
|
||||
"200": { "description": "Transactions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChainTransactionsResponse" } } } },
|
||||
"400": { "$ref": "#/components/responses/Error" },
|
||||
"404": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/chain/transactions/{tx_id}": {
|
||||
"get": {
|
||||
"tags": ["chain"],
|
||||
"summary": "Transaction by ID",
|
||||
"parameters": [
|
||||
{ "name": "tx_id", "in": "path", "required": true, "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": {
|
||||
"200": { "description": "Transaction detail", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChainTransactionDetailResponse" } } } },
|
||||
"404": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/chain/transactions": {
|
||||
"post": {
|
||||
"tags": ["chain"],
|
||||
"summary": "Submit transaction",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubmitTransactionRequest" } } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubmitTransactionResponse" } } } },
|
||||
"400": { "$ref": "#/components/responses/Error" },
|
||||
"500": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/chain/transactions/draft": {
|
||||
"post": {
|
||||
"tags": ["chain"],
|
||||
"summary": "Build unsigned TRANSFER draft",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/DraftTransactionRequest" } } }
|
||||
},
|
||||
"responses": {
|
||||
"200": { "description": "Draft", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DraftTransactionResponse" } } } },
|
||||
"400": { "$ref": "#/components/responses/Error" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"responses": {
|
||||
"Error": {
|
||||
"description": "Error response",
|
||||
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": { "error": { "type": "string" } },
|
||||
"required": ["error"]
|
||||
},
|
||||
"NetStats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"height": { "type": "integer" },
|
||||
"total_txs": { "type": "integer" },
|
||||
"total_transfers": { "type": "integer" },
|
||||
"total_relay_proofs": { "type": "integer" },
|
||||
"avg_block_time_ms": { "type": "number" },
|
||||
"tps": { "type": "number" },
|
||||
"total_supply_ut": { "type": "integer", "format": "uint64" }
|
||||
}
|
||||
},
|
||||
"BlockSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": { "type": "integer", "format": "uint64" },
|
||||
"hash": { "type": "string" },
|
||||
"time": { "type": "string", "format": "date-time" },
|
||||
"validator": { "type": "string", "description": "DC... address" },
|
||||
"tx_count": { "type": "integer" },
|
||||
"total_fees_ut": { "type": "integer", "format": "uint64" }
|
||||
}
|
||||
},
|
||||
"BlockDetail": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/components/schemas/BlockSummary" },
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prev_hash": { "type": "string" },
|
||||
"validator_addr": { "type": "string" },
|
||||
"transactions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"type": { "type": "string" },
|
||||
"from": { "type": "string" },
|
||||
"to": { "type": "string" },
|
||||
"amount_ut": { "type": "integer", "format": "uint64" },
|
||||
"fee_ut": { "type": "integer", "format": "uint64" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"TxListEntry": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"type": { "type": "string", "enum": ["TRANSFER","REGISTER_KEY","RELAY_PROOF","REGISTER_RELAY","CONTACT_REQUEST","ACCEPT_CONTACT","BLOCK_CONTACT","ADD_VALIDATOR","REMOVE_VALIDATOR","HEARTBEAT","BIND_WALLET","SLASH","OPEN_PAY_CHAN","CLOSE_PAY_CHAN","BLOCK_REWARD"] },
|
||||
"from": { "type": "string" },
|
||||
"from_addr": { "type": "string" },
|
||||
"to": { "type": "string" },
|
||||
"to_addr": { "type": "string" },
|
||||
"amount_ut": { "type": "integer", "format": "uint64" },
|
||||
"amount": { "type": "string", "description": "Human-readable token amount" },
|
||||
"fee_ut": { "type": "integer", "format": "uint64" },
|
||||
"fee": { "type": "string" },
|
||||
"time": { "type": "string", "format": "date-time" },
|
||||
"block_index": { "type": "integer", "format": "uint64" },
|
||||
"block_hash": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"TxDetail": {
|
||||
"allOf": [
|
||||
{ "$ref": "#/components/schemas/TxListEntry" },
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block_time": { "type": "string", "format": "date-time" },
|
||||
"payload": { "description": "Decoded payload JSON (type-specific)" },
|
||||
"payload_hex": { "type": "string" },
|
||||
"signature_hex": { "type": "string" }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"AddressDetail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": { "type": "string" },
|
||||
"pub_key": { "type": "string" },
|
||||
"balance_ut": { "type": "integer", "format": "uint64" },
|
||||
"balance": { "type": "string" },
|
||||
"tx_count": { "type": "integer" },
|
||||
"offset": { "type": "integer" },
|
||||
"limit": { "type": "integer" },
|
||||
"has_more": { "type": "boolean" },
|
||||
"next_offset": { "type": "integer" },
|
||||
"transactions": { "type": "array", "items": { "$ref": "#/components/schemas/TxListEntry" } }
|
||||
}
|
||||
},
|
||||
"NodeStats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pub_key": { "type": "string" },
|
||||
"address": { "type": "string" },
|
||||
"node_balance_ut": { "type": "integer", "format": "uint64" },
|
||||
"node_balance": { "type": "string" },
|
||||
"wallet_binding_address": { "type": "string" },
|
||||
"wallet_binding_balance_ut": { "type": "integer", "format": "uint64" },
|
||||
"reputation_score": { "type": "integer", "format": "int64" },
|
||||
"reputation_rank": { "type": "string", "enum": ["Observer","Active","Trusted","Validator"] },
|
||||
"blocks_produced": { "type": "integer", "format": "uint64" },
|
||||
"relay_proofs": { "type": "integer", "format": "uint64" },
|
||||
"slash_count": { "type": "integer", "format": "uint64" },
|
||||
"heartbeats": { "type": "integer", "format": "uint64" },
|
||||
"recent_window_blocks": { "type": "integer" },
|
||||
"recent_blocks_produced": { "type": "integer" },
|
||||
"recent_rewards_ut": { "type": "integer", "format": "uint64" },
|
||||
"recent_rewards": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"IdentityInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pub_key": { "type": "string", "description": "Hex Ed25519 public key" },
|
||||
"address": { "type": "string", "description": "DC... wallet address" },
|
||||
"x25519_pub": { "type": "string", "description": "Hex Curve25519 public key for E2E encryption; empty if not published" },
|
||||
"nickname": { "type": "string" },
|
||||
"registered": { "type": "boolean", "description": "true if REGISTER_KEY tx was committed" }
|
||||
}
|
||||
},
|
||||
"RegisteredRelayInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pub_key": { "type": "string" },
|
||||
"address": { "type": "string" },
|
||||
"relay": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"x25519_pub_key": { "type": "string" },
|
||||
"fee_per_msg_ut": { "type": "integer", "format": "uint64" },
|
||||
"multiaddr": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Envelope": {
|
||||
"type": "object",
|
||||
"description": "NaCl-box sealed message envelope. Only the holder of the recipient's X25519 private key can decrypt it.",
|
||||
"required": ["id", "recipient_pub", "sender_pub", "nonce", "ciphertext"],
|
||||
"properties": {
|
||||
"id": { "type": "string", "description": "Hex SHA-256[:16] of nonce||ciphertext" },
|
||||
"recipient_pub": { "type": "string", "description": "Hex X25519 public key of the recipient" },
|
||||
"sender_pub": { "type": "string", "description": "Hex X25519 public key of the sender" },
|
||||
"sender_ed25519_pub": { "type": "string", "description": "Sender's Ed25519 pub key for on-chain fee claims" },
|
||||
"fee_ut": { "type": "integer", "format": "uint64", "description": "Delivery fee µT (0 = free)" },
|
||||
"fee_sig": { "type": "string", "format": "byte", "description": "Ed25519 sig over FeeAuthBytes(id, fee_ut)" },
|
||||
"nonce": { "type": "string", "format": "byte", "description": "24-byte NaCl nonce (base64)" },
|
||||
"ciphertext": { "type": "string", "format": "byte", "description": "NaCl box ciphertext (base64)" },
|
||||
"sent_at": { "type": "integer", "format": "int64", "description": "Unix timestamp" }
|
||||
}
|
||||
},
|
||||
"InboxItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"sender_pub": { "type": "string" },
|
||||
"recipient_pub": { "type": "string" },
|
||||
"fee_ut": { "type": "integer", "format": "uint64" },
|
||||
"sent_at": { "type": "integer", "format": "int64" },
|
||||
"sent_at_human": { "type": "string", "format": "date-time" },
|
||||
"nonce": { "type": "string", "format": "byte" },
|
||||
"ciphertext": { "type": "string", "format": "byte" }
|
||||
}
|
||||
},
|
||||
"ContactInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"requester_pub": { "type": "string" },
|
||||
"requester_addr": { "type": "string" },
|
||||
"status": { "type": "string", "enum": ["pending", "accepted", "blocked"] },
|
||||
"intro": { "type": "string", "description": "Optional plaintext intro (≤ 280 chars)" },
|
||||
"fee_ut": { "type": "integer", "format": "uint64" },
|
||||
"tx_id": { "type": "string" },
|
||||
"created_at": { "type": "integer", "format": "int64" }
|
||||
}
|
||||
},
|
||||
"Transaction": {
|
||||
"type": "object",
|
||||
"description": "Signed blockchain transaction. Sign the canonical JSON of the object with Signature set to null, using Ed25519.",
|
||||
"required": ["type", "from"],
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"type": { "type": "string", "enum": ["TRANSFER","REGISTER_KEY","RELAY_PROOF","REGISTER_RELAY","CONTACT_REQUEST","ACCEPT_CONTACT","BLOCK_CONTACT","ADD_VALIDATOR","REMOVE_VALIDATOR","HEARTBEAT","BIND_WALLET","SLASH","OPEN_PAY_CHAN","CLOSE_PAY_CHAN"] },
|
||||
"from": { "type": "string", "description": "Hex Ed25519 pub key of the signer" },
|
||||
"to": { "type": "string", "description": "Hex Ed25519 pub key of the recipient (if applicable)" },
|
||||
"amount": { "type": "integer", "format": "uint64", "description": "µT to transfer (TRANSFER, CONTACT_REQUEST)" },
|
||||
"fee": { "type": "integer", "format": "uint64", "description": "µT fee to block validator (min 1000)" },
|
||||
"memo": { "type": "string" },
|
||||
"payload": { "type": "string", "format": "byte", "description": "Base64 JSON payload (type-specific)" },
|
||||
"signature": { "type": "string", "format": "byte", "description": "Ed25519 signature over canonical bytes" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"SubmitTransactionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tx": { "$ref": "#/components/schemas/Transaction" },
|
||||
"signed_tx": { "type": "string", "description": "Signed transaction as JSON/base64/hex string" }
|
||||
}
|
||||
},
|
||||
"DraftTransactionRequest": {
|
||||
"type": "object",
|
||||
"required": ["from", "to", "amount_ut"],
|
||||
"properties": {
|
||||
"from": { "type": "string" },
|
||||
"to": { "type": "string" },
|
||||
"amount_ut": { "type": "integer", "format": "uint64" },
|
||||
"memo": { "type": "string" },
|
||||
"fee_ut": { "type": "integer", "format": "uint64", "description": "Optional; defaults to MinFee (1000 µT)" }
|
||||
}
|
||||
},
|
||||
"DraftTransactionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tx": { "$ref": "#/components/schemas/Transaction" },
|
||||
"sign_bytes_hex": { "type": "string" },
|
||||
"sign_bytes_base64": { "type": "string" },
|
||||
"note": { "type": "string" }
|
||||
},
|
||||
"required": ["tx", "sign_bytes_hex", "sign_bytes_base64"]
|
||||
},
|
||||
"SubmitTransactionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": { "type": "string" },
|
||||
"id": { "type": "string" }
|
||||
},
|
||||
"required": ["status", "id"]
|
||||
},
|
||||
"ChainTx": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"type": { "type": "string" },
|
||||
"memo": { "type": "string" },
|
||||
"from": { "type": "string" },
|
||||
"from_addr": { "type": "string" },
|
||||
"to": { "type": "string" },
|
||||
"to_addr": { "type": "string" },
|
||||
"amount_ut": { "type": "integer", "format": "uint64" },
|
||||
"fee_ut": { "type": "integer", "format": "uint64" },
|
||||
"block_index": { "type": "integer", "format": "uint64" },
|
||||
"block_hash": { "type": "string" },
|
||||
"time": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"ChainTransactionsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"account_id": { "type": "string" },
|
||||
"account_addr": { "type": "string" },
|
||||
"count": { "type": "integer" },
|
||||
"order": { "type": "string" },
|
||||
"limit_applied": { "type": "integer" },
|
||||
"transactions": { "type": "array", "items": { "$ref": "#/components/schemas/ChainTx" } }
|
||||
}
|
||||
},
|
||||
"ChainTransactionDetailResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tx": { "$ref": "#/components/schemas/ChainTx" },
|
||||
"payload": {},
|
||||
"payload_hex": { "type": "string" },
|
||||
"signature_hex": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user