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_idThe public key identifier of the intended receiver (ReceiverID), encoded as the raw bytes of the compressed public key. |
bytes |
string |
body |
encrypted_payloadThe ECIES encrypted message payload. |
bytes |
string |
body |
tx_proofThe 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_idThe unique ID assigned to the stored message by the server. |
uint64 |
string |
Nested Messages
authmailboxrpc.BitcoinMerkleInclusionProof
| Field | gRPC Type | REST Type |
|---|---|---|
raw_tx_dataThe 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_dataThe 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_heightThe 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_proofThe Merkle path proving the transaction's inclusion in the block header's Merkle root. |
MerkleProof |
object |
claimed_outpointThe 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_keyThe 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_rootThe 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_hashesList 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 |
bitsThe 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 |
|---|---|---|
txidRaw 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_indexThe index of the output on the transaction. |
uint32 |
integer |