SendMessage

Sends a single message to a receiver's mailbox. Requires a valid, unused Bitcoin P2TR transaction outpoint as proof of uniqueness, included in a block as proof of work.

Source: authmailboxrpc/mailbox.proto

gRPC

rpc SendMessage (SendMessageRequest) returns (SendMessageResponse);

REST

HTTP Method Path
POST /v1/taproot-assets/mailbox/send

Code Samples

  • gRPC

  • REST

  • Javascript

  • Python

  • grpcurl

const fs = require('fs');
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const GRPC_HOST = 'localhost:10029'
const MACAROON_PATH = 'TAPD_DIR/data/regtest/admin.macaroon'
const TLS_PATH = 'TAPD_DIR/tls.cert'

const loaderOptions = {
  keepCase: true,
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true,
};
const packageDefinition = protoLoader.loadSync('authmailboxrpc/mailbox.proto', loaderOptions);
const authmailboxrpc = grpc.loadPackageDefinition(packageDefinition).authmailboxrpc;
process.env.GRPC_SSL_CIPHER_SUITES = 'HIGH+ECDSA';
const tlsCert = fs.readFileSync(TLS_PATH);
const sslCreds = grpc.credentials.createSsl(tlsCert);
const macaroon = fs.readFileSync(MACAROON_PATH).toString('hex');
const macaroonCreds = grpc.credentials.createFromMetadataGenerator(function(args, callback) {
  let metadata = new grpc.Metadata();
  metadata.add('macaroon', macaroon);
  callback(null, metadata);
});
let creds = grpc.credentials.combineChannelCredentials(sslCreds, macaroonCreds);
let client = new authmailboxrpc.Mailbox(GRPC_HOST, creds);
let request = {
  receiver_id: <bytes>,
  encrypted_payload: <bytes>,
  tx_proof: <BitcoinMerkleInclusionProof>,
};
client.sendMessage(request, function(err, response) {
  console.log(response);
});
// Console output:
//  {
//    "message_id": <uint64>,
//  }
import codecs, grpc, os
# Generate the following 2 modules by compiling the authmailboxrpc/mailbox.proto with the grpcio-tools.
# See https://github.com/lightningnetwork/lnd/blob/master/docs/grpc/python.md for instructions.
import mailbox_pb2 as authmailboxrpc, mailbox_pb2_grpc as mailboxstub

GRPC_HOST = 'localhost:10029'
MACAROON_PATH = 'TAPD_DIR/data/regtest/admin.macaroon'
TLS_PATH = 'TAPD_DIR/tls.cert'

# create macaroon credentials
macaroon = codecs.encode(open(MACAROON_PATH, 'rb').read(), 'hex')
def metadata_callback(context, callback):
  callback([('macaroon', macaroon)], None)
auth_creds = grpc.metadata_call_credentials(metadata_callback)
# create SSL credentials
os.environ['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA'
cert = open(TLS_PATH, 'rb').read()
ssl_creds = grpc.ssl_channel_credentials(cert)
# combine macaroon and SSL credentials
combined_creds = grpc.composite_channel_credentials(ssl_creds, auth_creds)
# make the request
channel = grpc.secure_channel(GRPC_HOST, combined_creds)
stub = mailboxstub.MailboxStub(channel)
request = authmailboxrpc.SendMessageRequest(
  receiver_id=<bytes>,
  encrypted_payload=<bytes>,
  tx_proof=<BitcoinMerkleInclusionProof>,
)
response = stub.SendMessage(request)
print(response)
# {
#    "message_id": <uint64>,
# }
# grpcurl docs: https://github.com/fullstorydev/grpcurl
# Proto source: https://github.com/lightninglabs/taproot-assets
GRPC_HOST=localhost:10029
TAPD_DIR=~/.tapd
TAPD_SOURCE=path/to/taproot-assets
NETWORK=mainnet
MACAROON_PATH="$TAPD_DIR/data/$NETWORK/admin.macaroon"
TLS_PATH="$TAPD_DIR/tls.cert"

grpcurl \
    -import-path $TAPD_SOURCE/taprpc/ \
    -proto authmailboxrpc/mailbox.proto \
    -cacert $TLS_PATH \
    -H "macaroon: $(xxd -ps -u -c 1000 $MACAROON_PATH)" \
    -d '{ "receiver_id": BASE64_ENCODED_VALUE, "encrypted_payload": BASE64_ENCODED_VALUE, "tx_proof": <BitcoinMerkleInclusionProof> }' \
    $GRPC_HOST \
    authmailboxrpc.Mailbox/SendMessage
  • Javascript
  • Python
  • curl
const fs = require('fs');
const request = require('request');

const REST_HOST = 'localhost:8089'
const MACAROON_PATH = 'TAPD_DIR/data/regtest/admin.macaroon'

let requestBody = {
  receiver_id: <string>, // <bytes> (base64 encoded)
  encrypted_payload: <string>, // <bytes> (base64 encoded)
  tx_proof: <object>, // <BitcoinMerkleInclusionProof>
};
let options = {
  url: `https://${REST_HOST}/v1/taproot-assets/mailbox/send`,
  // Work-around for self-signed certificates.
  rejectUnauthorized: false,
  json: true,
  headers: {
    'Grpc-Metadata-macaroon': fs.readFileSync(MACAROON_PATH).toString('hex'),
  },
  form: JSON.stringify(requestBody),
}
request.post(options, function(error, response, body) {
  console.log(body);
});
// Console output:
//  {
//    "message_id": <string>, // <uint64>
//  }
import base64, codecs, json, requests

REST_HOST = 'localhost:8089'
MACAROON_PATH = 'TAPD_DIR/data/regtest/admin.macaroon'
TLS_PATH = 'TAPD_DIR/tls.cert'

url = f'https://{REST_HOST}/v1/taproot-assets/mailbox/send'
macaroon = codecs.encode(open(MACAROON_PATH, 'rb').read(), 'hex')
headers = {'Grpc-Metadata-macaroon': macaroon}
data = {
  'receiver_id': base64.b64encode(<bytes>),
  'encrypted_payload': base64.b64encode(<bytes>),
  'tx_proof': <BitcoinMerkleInclusionProof>,
}
r = requests.post(url, headers=headers, data=json.dumps(data), verify=TLS_PATH)
print(r.json())
# {
#    "message_id": <uint64>,
# }
REST_HOST=localhost:8089
TAPD_DIR=~/.tapd
NETWORK=mainnet
MACAROON_PATH="$TAPD_DIR/data/$NETWORK/admin.macaroon"
TLS_PATH="$TAPD_DIR/tls.cert"

curl -X POST \
    --cacert $TLS_PATH \
    -H "Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 $MACAROON_PATH)" \
    -d '{ "receiver_id": BASE64_ENCODED_VALUE, "encrypted_payload": BASE64_ENCODED_VALUE, "tx_proof": <BitcoinMerkleInclusionProof> }' \
    https://$REST_HOST/v1/taproot-assets/mailbox/send

Messages

authmailboxrpc.SendMessageRequest

Source: authmailboxrpc/mailbox.proto

Field gRPC Type REST Type REST Placement
receiver_id
The public key identifier of the intended receiver (ReceiverID), encoded as the raw bytes of the compressed public key.
bytes string body
encrypted_payload
The ECIES encrypted message payload.
bytes string body
tx_proof
The Bitcoin Merkle Inclusion Proof used as the sender's authentication. The server MUST perform full validation of this proof: 1. Verify claimed_outpoint.txid_hex matches hash(raw_tx_data). 2. Verify claimed_outpoint.index is valid for the transaction. 3. Verify merkle_proof connects the transaction hash to the raw_block_header_data's Merkle root. 4. Verify block_header validity (e.g., PoW, potentially chain context). 5. Ensure the claimed_outpoint has not been used previously (check used_proofs table).
BitcoinMerkleInclusionProof object body

authmailboxrpc.SendMessageResponse

Source: authmailboxrpc/mailbox.proto

Field gRPC Type REST Type
message_id
The unique ID assigned to the stored message by the server.
uint64 string

Nested Messages

authmailboxrpc.BitcoinMerkleInclusionProof

Field gRPC Type REST Type
raw_tx_data
The raw Bitcoin transaction bytes, in standard Bitcoin serialization format, containing the outpoint being claimed. The server will hash this to get the TXID.
bytes string
raw_block_header_data
The raw block header bytes (typically 80 bytes) of the block in which the transaction was mined. Contains the Merkle root against which the proof is verified.
bytes string
block_height
The height at which the block was mined. This is used to determine the block's validity and to ensure the transaction is not too old.
uint32 integer
merkle_proof
The Merkle path proving the transaction's inclusion in the block header's Merkle root.
MerkleProof object
claimed_outpoint
The specific output within the provided transaction being claimed as the proof "token". The output at the given index must be a P2TR output. The server must verify that the txid_hex matches the hash of the provided transaction data, and that this specific outpoint index exists in the transaction.
OutPoint object
internal_key
The Taproot internal key used to construct the P2TR output that is claimed by the outpoint above. Must be provided alongside the Taproot Merkle root to prove knowledge of the output's construction.
bytes string
merkle_root
The Taproot Merkle root, if applicable. This, alongside the internal key, is used to prove knowledge of the output's construction. If this is not provided (empty or nil), a BIP-0086 construction is assumed.
bytes string

authmailboxrpc.MerkleProof

Field gRPC Type REST Type
sibling_hashes
List of sibling hashes in the Merkle path, ordered from the transaction's sibling up towards the root. Each hash is typically 32 bytes.
bytes[] array
bits
The bitmask indicating the direction (left/right) of each sibling hash in the Merkle tree. Each bit corresponds to a sibling hash in the sibling_hashes list. 0 indicates left, 1 indicates right.
bool[] array

taprpc.OutPoint

Field gRPC Type REST Type
txid
Raw bytes representing the transaction id. Must be in internal byte order (little-endian), i.e. reversed compared to the human-readable (RPC/block explorer) hex encoding.
bytes string
output_index
The index of the output on the transaction.
uint32 integer