Skip to main content

Tech

This document describes how DePIN Messaging is implemented in Neurai Core, based on the DePIN pool, network gateway, RPCs, and ECIES encryption stack.

System overview​

DePIN Messaging is a token-gated, end-to-end encrypted message pool with a TCP/JSON-RPC gateway. The design assumes:

  • Messages are encrypted client-side and stored as ciphertext.
  • Pool nodes validate token ownership and signatures before storing.
  • Recipients decrypt locally using their private keys.

Runtime components​

  • CDepinMsgPool: in-memory pool for encrypted messages, limits, and expiry.
  • CDepinMsgPoolServer: TCP server on the DePIN port (default 19002) with JSON-RPC support.
  • RPC layer: depin* RPCs that submit, fetch, inspect, and manage pool contents.
  • ECIES layer: hybrid encryption using secp256k1 ECDH + AES-256-GCM.
  • MCP worker (optional): background bot that polls messages and replies via an MCP AI server.

Message structure​

CDepinMessage (outer envelope)​

Fields:

  • token: asset name controlling access
  • senderAddress: Neurai address
  • timestamp: UNIX time
  • signature: ECDSA over secp256k1
  • encryptedPayload: serialized ECIES envelope

Signature hash (CHashWriter, double-SHA256 over serialized fields):

hash = SHA256(SHA256(token || senderAddress || timestamp || encryptedPayload))

CECIESEncryptedMessage (inner payload)​

Fields:

  • ephemeralPubKey: 33 bytes (compressed secp256k1)
  • encryptedPayload: [12B nonce || ciphertext || 16B GCM tag]
  • recipientKeys: map of hash160(address) -> [12B nonce || encrypted_aes_key || 16B tag]

Because recipients are encoded by hash160(address) and stored inside the payload, the pool cannot filter per-recipient. Clients decrypt locally and discard failures.

Encryption and decryption (ECIES hybrid)​

Encryption:

  1. Generate a per-message ephemeral keypair.
  2. Derive an AES-256 key from the ephemeral private key (KDF-SHA256).
  3. Encrypt plaintext once with AES-256-GCM (12B nonce, 16B tag).
  4. For each recipient:
    • ECDH with ephemeral privkey and recipient pubkey.
    • Derive wrap key (KDF-SHA256).
    • Encrypt the AES key with AES-256-GCM.
    • Store [nonce || encrypted_aes_key || tag] keyed by hash160(address).

Decryption:

  1. Locate recipient package by hash160(address).
  2. ECDH with recipient private key and message ephemeral pubkey.
  3. Derive wrap key, decrypt AES key with GCM.
  4. Decrypt message payload with AES-256-GCM.

Diagram: ECIES encryption/decryption​

Client (Sender)                                              Client (Recipient)
----------------- -------------------
ephemeral keypair
(priv_e, pub_e)
|
| KDF-SHA256(priv_e)
v
AES key (K_msg)
|
| AES-256-GCM(plaintext, K_msg, nonce_msg)
v
encryptedPayload = nonce_msg || ciphertext || tag_msg
|
| For each recipient:
| shared = ECDH(priv_e, pub_recipient)
| wrap = KDF-SHA256(shared)
| AES-256-GCM(K_msg, wrap, nonce_r)
v
recipientKeys[hash160(addr)] = nonce_r || enc_K_msg || tag_r
|
+-------------------------------> recipientKeys + encryptedPayload
(via pool)
|
| Find own hash160(addr)
v
nonce_r || enc_K_msg || tag_r
|
| shared = ECDH(priv_recipient, pub_e)
| wrap = KDF-SHA256(shared)
| AES-256-GCM-Decrypt(enc_K_msg, wrap, nonce_r, tag_r)
v
K_msg
|
| AES-256-GCM-Decrypt(ciphertext, K_msg, nonce_msg, tag_msg)
v
plaintext

Pool validation and limits​

When adding a message, the pool enforces:

  • Token must match the pool token.
  • Timestamp cannot be more than 60 seconds in the future.
  • Message must not be expired (default 168 hours).
  • Payload cannot be empty and must fit size limits.
    • Total payload limit: maxMessageSize * maxRecipients.
  • Pool size cannot exceed maxPoolSizeMB.
  • Signature must verify (unless skipped by a server-authenticated path).
  • Sender must hold the token.

Addresses must have a revealed public key in the pubkeyindex, otherwise:

  • They cannot be used as recipients.
  • Their signatures cannot be verified.

RPC surface (core)​

  • depingetmsginfo: pool status, limits, cipher, memory usage.
  • depinsubmitmsg: accepts a pre-encrypted, pre-signed CDepinMessage.
  • depinreceivemsg: returns encrypted messages as JSON (client decrypts).
  • depinsendmsg: node prepares encryption and signature on behalf of the sender.
  • depingetmsg: decrypts messages for local wallet addresses (local or remote).
  • depinclearmsg: removes expired messages or clears the pool.
  • depinpoolstats: aggregate pool stats (counts, sizes, age buckets).

depinsubmitmsg verifies the message signature and token ownership before storing.

DePIN port protocol (TCP + JSON-RPC)​

The DePIN gateway listens on depinmsgport (default 19002). It supports:

  • JSON-RPC: depinsubmitmsg, depinsendmsg, depingetmsg
  • Plain text commands (newline-terminated):
    • AUTH|token|address[|SEND] -> CHALLENGE|nonce|timeout
    • GETMESSAGES|token|addr1,addr2|authAddress|signature|challenge
    • INFO, PING

Challenge/response authentication​

  1. Client requests AUTH.
  2. Server checks:
    • token matches pool token
    • address owns the token
    • address has a revealed pubkey
  3. Server issues a short-lived nonce (30s).
  4. Client signs:
    • DEPIN-GET|token|address|challenge for receive
    • DEPIN-SEND|token|address|challenge for send
  5. Server recovers pubkey from the compact signature and verifies it matches the address.

This reduces unauthenticated sends/reads and limits some DoS vectors.

Local and remote message flows​

Local send:

  • Client encrypts and signs.
  • pDepinMsgPool->AddMessage(...).

Remote send (depinsendmsg):

  • Client performs challenge/response with the remote pool.
  • Node encrypts and signs on the sender wallet.

Remote send (secure depinsubmitmsg):

  • Client encrypts and signs.
  • Submit serialized hex message to the remote gateway.

Remote receive:

  • Client performs challenge/response.
  • Pool returns encrypted messages in bulk.
  • Client attempts ECIES decryption per address.

Diagram: End-to-end send/receive flow​

Sender Client                        Pool Node                           Recipient Client
------------- ---------- -----------------
1) Build ECIES payload
2) Build CDepinMessage
3) Sign message hash
4) depinsubmitmsg(hex) -----------> [DePIN gateway]
- validate token
- verify signature
- check ownership
- enforce size/expiry/limits
- store in CDepinMsgPool
- return success

5) depinreceivemsg(token, addr, t) -------------------------------> [Core RPC]
- read from pool (no filtering)
- return encrypted payloads
6) Attempt ECIES decrypt
7) Ignore non-matching payloads
8) Use plaintext message

Persistence and cleanup​

If -depinpoolpersist=1:

  • Pool is saved to depinpool.dat on shutdown.
  • File format includes magic bytes 0xD0D1D2D3, version, timestamp, and messages.
  • Expired messages are dropped during load, and the file is compacted if needed.

Expired messages are also cleaned periodically via -depinmsgcleanupinterval (default 300s).

Configuration flags​

Key flags (from init.cpp):

  • -depinmsg=1 enable DePIN messaging.
  • -depinmsgtoken=TOKEN required token name.
  • -depinmsgport=19002 DePIN TCP/JSON-RPC port.
  • -depinmsgmaxusers recipient limit.
  • -depinmsgsize max message size (bytes).
  • -depinmsgexpire expiry hours.
  • -depinpoolsize max pool size (MB).
  • -depinpoolpersist enable pool persistence.
  • -depinmsgcleanupinterval expiry sweep interval (seconds).

Required indexes:

  • -assetindex for token ownership and holders.
  • -pubkeyindex to verify signatures and encrypt to recipients.

Messaging activation flags​

Core messaging features are gated by a version-bits deployment named messaging_restricted. Asset transfers can automatically subscribe wallets to message channels when messaging is enabled.

MCP worker (optional AI integration)​

When -depinmcp=1, a background worker:

  • Polls the pool every -depinmcpinterval seconds.
  • Decrypts messages for the configured node address.
  • Filters commands by -depinmcpkey (default /ai).
  • Sends prompts to the MCP server and posts responses back to the pool.
  • Applies optional per-address rate limiting and deduplicates processed messages.