Selective disclosure

Every output on NULL is encrypted, so by default nobody but the sender and the recipient can see what a payment carried. Selective disclosure lets the owner reveal exactly one fact to someone else, off-chain and on their own initiative: what one output paid, or that they control an address. Neither touches consensus.

There are two mechanisms, each with one wallet method and one node method:

QuestionProver (wallet daemon)Verifier (node)
Did withdrawal X pay this address this amount?getpaymentdisclosureverifypaymentdisclosure
Do you control this address?answerchallengecreatechallenge

The wallet methods are served by null-wallet-rpc (examples on 127.0.0.1:18447 with WALLET_TOKEN), and the node methods by nulld (examples on 127.0.0.1:18446 with TOKEN). See the overview for transport and errors.

Payment disclosure

For one output of one transaction, the sender's wallet reveals the recipient's transmission key and the ephemeral secret used to encrypt that output. These two values decrypt that output and nothing else: not the other outputs of the transaction, and nothing else the wallet sent or received. A verifier with any node checks them against the output's ciphertext and note commitment. A disclosure cannot claim an amount, address or memo the transaction does not carry.

A typical use: an exchange proves to a customer, or to support staff, that a withdrawal paid the customer's address.

Address ownership

The checker encrypts a secret message of their choosing to the address, the way a payment output would be encrypted. Only the holder of that address's incoming viewing key can read it back. The checker keeps the message secret, hands over the challenge, and compares the answer. No signature scheme is involved. A watch-only wallet can answer, because it holds the incoming viewing key.


RPCgetpaymentdisclosure

getpaymentdisclosure

Wallet daemon. Disclosures for the outputs this wallet sent in a transaction, change included. Only transactions that spent this wallet's notes qualify, and the wallet must have recorded the spending txid, which requires full-block scanning (not --light). Zero-value padding outputs are skipped.

Parameters

  • Name
    txid
    Type
    hex
    Description

    Required. A transaction this wallet sent.

  • Name
    index
    Type
    number
    Description

    Optional. Only the output at this action index.

Result

An array, one entry per output:

  • Name
    index
    Type
    number
    Description

    Action index of the output in the transaction.

  • Name
    disclosure
    Type
    hex
    Description

    The disclosure to hand to the verifier: txid, action index, transmission key and ephemeral secret (100 bytes).

  • Name
    address
    Type
    string
    Description

    The recipient address.

  • Name
    amount
    Type
    string
    Description

    The amount paid, in units.

  • Name
    memo
    Type
    string | object | null
    Description

    The memo as text; {"hex": "…"} if not UTF-8; null if empty.

Errors

-1 if the wallet did not send this transaction, or it has no output this wallet can open at that index.

Request

RPC
getpaymentdisclosure
curl -s http://127.0.0.1:18447/ \
  -H "Authorization: Bearer $WALLET_TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"getpaymentdisclosure","params":["5a9e3c1f7b2d8e4a6c0f9b3d5e7a1c8f2b4d6e0a9c3f5b7d1e8a2c4f6b0d9e3a", 0]}'

Response

{
  "jsonrpc": "2.0",
  "result": [
    {
      "index": 0,
      "disclosure": "5a9e3c1f7b2d8e4a6c0f9b3d5e7a1c8f2b4d6e0a9c3f5b7d1e8a2c4f6b0d9e3a00000000730108852af1d39a37e931703c560014246c4f039ff6bd5c974e6a2f4a7a9a60b9a8194dd983f7f352b87bb2226a33c52499dd2d460090299ca94057b894d8cc",
      "address": "null19crg8qy05x8gj7mz6tuq0r9c9j48njnny34ycueljywcrm7uquvyjhfkzknarmngc0yegdyfvh8",
      "amount": "150000000",
      "memo": "withdrawal 5521"
    }
  ],
  "id": 1
}

RPCverifypaymentdisclosure

verifypaymentdisclosure

Node. Opens the output a disclosure names and reports what it proves. Any node can verify; it needs no keys. A disclosure that proves nothing is a normal result with valid: false, not an error.

Parameters

  • Name
    disclosure
    Type
    hex
    Description

    Required. From getpaymentdisclosure.

Result

  • Name
    valid
    Type
    boolean
    Description

    Whether the disclosure opens the output.

  • Name
    txid
    Type
    hex
    Description

    The transaction. Only when valid.

  • Name
    index
    Type
    number
    Description

    The action index. Only when valid.

  • Name
    address
    Type
    string
    Description

    The address the output paid. Only when valid.

  • Name
    amount
    Type
    string
    Description

    The amount in units. Only when valid.

  • Name
    memo
    Type
    string | null
    Description

    The memo as text; "" if empty; null if not UTF-8. Only when valid.

  • Name
    height
    Type
    number | null
    Description

    Block height; null while the transaction is in the mempool. Only when valid.

  • Name
    confirmations
    Type
    number
    Description

    0 while in the mempool. Only when valid.

  • Name
    reason
    Type
    string
    Description

    Why it proves nothing, for example transaction not found or no such action. Only when not valid.

Errors

-32602 if the hex does not decode as a disclosure.

Request

RPC
verifypaymentdisclosure
curl -s http://127.0.0.1:18446/ \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"verifypaymentdisclosure","params":["5a9e3c1f7b2d8e4a6c0f9b3d5e7a1c8f2b4d6e0a9c3f5b7d1e8a2c4f6b0d9e3a00000000730108852af1d39a37e931703c560014246c4f039ff6bd5c974e6a2f4a7a9a60b9a8194dd983f7f352b87bb2226a33c52499dd2d460090299ca94057b894d8cc"]}'

Valid

{
  "jsonrpc": "2.0",
  "result": {
    "valid": true,
    "txid": "5a9e3c1f7b2d8e4a6c0f9b3d5e7a1c8f2b4d6e0a9c3f5b7d1e8a2c4f6b0d9e3a",
    "index": 0,
    "address": "null19crg8qy05x8gj7mz6tuq0r9c9j48njnny34ycueljywcrm7uquvyjhfkzknarmngc0yegdyfvh8",
    "amount": "150000000",
    "memo": "withdrawal 5521",
    "height": 6203,
    "confirmations": 4
  },
  "id": 1
}

Not valid

{
  "jsonrpc": "2.0",
  "result": { "valid": false, "reason": "transaction not found" },
  "id": 1
}

RPCcreatechallenge

createchallenge

Node. Encrypts a message to an address and returns the challenge. Run it on your own node; it needs no keys. Choose an unpredictable message and keep it secret until the other party answers.

Parameters

  • Name
    address
    Type
    string
    Description

    Required. The address whose owner should answer.

  • Name
    message
    Type
    string
    Description

    Required. The secret message, UTF-8, at most 512 bytes.

Result

  • Name
    challenge
    Type
    hex
    Description

    The encrypted challenge to send to the address owner.

Errors

-32602 if the address does not decode for this network or the message is too long.

Request

RPC
createchallenge
curl -s http://127.0.0.1:18446/ \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"createchallenge","params":{"address":"null1c32nxrlucj0u4vepa9swy97v2p3gwxyzgxsxya6nj26jf7nxl2tqdm2xr7pyurdaez4nj4g3cw7","message":"proof 7f3a91c2"}}'

Response

{
  "jsonrpc": "2.0",
  "result": {
    "challenge": "ef64d5a7d9c66ec8f2137a815e86db7bb41208e0bc8081a915cf09d0be9b9580422a9ab5c1aff438…"
  },
  "id": 1
}

RPCanswerchallenge

answerchallenge

Wallet daemon. Decrypts a challenge made for any of this wallet's addresses and returns the message. Send the message back to the checker. Watch-only wallets can answer.

Parameters

  • Name
    challenge
    Type
    hex
    Description

    Required. From createchallenge.

Result

  • Name
    message
    Type
    string
    Description

    The decrypted message.

Errors

-2 with challenge is not for this wallet. -32602 if the hex does not decode as a challenge.

Request

RPC
answerchallenge
curl -s http://127.0.0.1:18447/ \
  -H "Authorization: Bearer $WALLET_TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"answerchallenge","params":["ef64d5a7d9c66ec8f2137a815e86db7bb41208e0bc8081a915cf09d0be9b9580422a9ab5c1aff438…"]}'

Response

{ "jsonrpc": "2.0", "result": { "message": "proof 7f3a91c2" }, "id": 1 }

What is not disclosed

Block explorers, including explorer.nullnet.sh, show blocks, txids, action counts and nullifiers only. No public service can show the balance of an address or the payments it received. Proving anything beyond one output or one address takes the owner's cooperation through the methods above. Multisig and hardware wallets are not supported.