TAP Message
Core message processing for the Transaction Authorization Protocol (TAP) providing secure message types and validation.
Features
- TAP Message Types: Complete implementation of all TAP message types
- Generic Typed Messages: Compile-time type safety with
PlainMessage<Transfer>while maintaining backward compatibility - Derive Macro: Automatic implementation of
TapMessageandMessageContexttraits with#[derive(TapMessage)] - Message Security: Support for secure message formats with JWS (signed) and JWE (encrypted) capabilities
- Attachments Support: Full support for message attachments in Base64, JSON, and Links formats with optional JWS
- Validation: Proper validation of all message fields and formats
- CAIP Support: Validation for chain-agnostic identifiers (CAIP-2, CAIP-10, CAIP-19)
- Authorization Flows: Support for authorization, rejection, and settlement flows
- Agent Policies: TAIP-7 compliant policy implementation for defining agent requirements
- Invoice Support: TAIP-16 compliant structured invoice implementation with tax and line item support
- Payment Requests: TAIP-14 compliant payment requests with currency and asset options
- Name Hashing: TAIP-12 compliant name hashing for privacy-preserving Travel Rule compliance
- Extensibility: Easy addition of new message types
Usage
Basic Transfer Message
use Transfer;
use Participant;
use TapMessageBody;
use AssetId;
use HashMap;
use FromStr;
// Create a Transfer message body
let asset = from_str?;
let originator = Participant ;
let beneficiary = Participant ;
let transfer = Transfer ;
// Use the TapMessageBody trait for validation
transfer.validate?;
// In a full implementation, you would use an agent to send this transfer message:
// let (packed_message, _) = agent.send_message(&transfer, vec![&beneficiary.id], true).await?;
Payment Request with Invoice
use ;
use TapMessageBody;
use HashMap;
// Create a merchant and a basic invoice
let merchant = Participant ;
// Create line items for the invoice
let line_items = vec!;
// Create a basic invoice
let invoice = new;
// Create a payment request with the invoice
let mut payment_request = with_currency;
payment_request.invoice = Some;
// Validate the message
payment_request.validate?;
Message Types
Plain Message
The PlainMessage struct is the core representation of a message in TAP:
The attachments field supports all attachment formats and can be used to include additional data with messages.
Transfer
The Transfer struct represents a TAP transfer message, which is the core message type in the protocol:
Agent Policies (TAIP-7)
The TAP protocol supports defining agent policies according to TAIP-7, which allows agents to specify requirements for authorizing transactions:
use ;
use HashMap;
// Create a participant with a policy
let auth_policy = RequireAuthorization ;
let participant = Participant ;
// Create an UpdatePolicies message to dynamically update policies
let proof_policy = default; // Uses default values
let update_policies = UpdatePolicies ;
// Validate the message
update_policies.validate?;
Authorization Messages
TAP supports various authorization messages for compliance workflows:
// Authorization message
// Rejection message
// Settlement message
Error Messages
TAP provides standardized error messages:
Presentation
The Presentation struct represents a verifiable presentation message:
This structure enables compatibility with the present-proof protocol and enforces the requirement for format field and attachments validation. The format field is required for each attachment and must match one of the formats specified in the presentation.
Invoice and Payment Requests (TAIP-14, TAIP-16)
TAP supports structured invoices according to TAIP-16, which can be embedded in payment requests (TAIP-14):
use ;
use TapMessageBody;
use Participant;
use HashMap;
// Create a merchant participant
let merchant = Participant ;
// Create a simple invoice with line items
let invoice = Invoice ;
// Create a payment request with the invoice
let mut payment_request = with_currency;
// Add the invoice to the payment request
payment_request.invoice = Some;
// Alternatively, you can reference an invoice by URL
// payment_request.invoice = Some(InvoiceReference::Url("https://example.com/invoice/123".to_string()));
Generic Typed Messages
TAP-MSG now supports compile-time type safety through generic PlainMessage<T> while maintaining 100% backward compatibility:
Creating Typed Messages
use ;
use HashMap;
// Create a Transfer message body
let transfer = Transfer ;
// Create a strongly-typed message with builder pattern
let typed_msg = new_typed
.with_recipient
.with_thread_id
.with_expires_at;
// Access the typed body with compile-time safety
println!;
println!;
Converting Between Typed and Untyped
// Convert typed message to untyped for serialization/transport
let untyped_msg: = typed_msg.to_plain_message?;
// Parse untyped message to specific type
let untyped_msg: = from_str?;
let typed_msg: = untyped_msg.parse_body?;
// Alternative using parse_as method
let typed_msg: = untyped_msg.parse_as?;
Runtime Dispatch with TapMessage Enum
use TapMessage;
// Parse any TAP message for runtime dispatch
let plain_msg: = from_str?;
match plain_msg.parse_tap_message?
Backward Compatibility
// Existing code continues to work unchanged
let plain_msg: PlainMessage = from_str?;
// This is now PlainMessage<Value> due to default type parameter
// All existing methods work unchanged
println!;
println!;
See GENERIC_PLAINMESSAGE.md for complete documentation.
Message Security
The TAP protocol provides several security modes:
- Plain: No security, for testing only
- Signed: Messages are signed to ensure integrity
- AuthCrypt: Messages are both signed and encrypted for confidentiality
When using the tap-agent crate with this message library, you can specify the security mode when sending messages:
use Agent;
use SecurityMode;
use Transfer;
async
Message Attachments
TAP supports message attachments through the Attachment struct and related types:
use ;
use json;
// Create a JSON attachment
let attachment = Attachment ;
// Create a presentation with attachment
let presentation = Presentation ;
// Validate the presentation
presentation.validate?;
Message Validation
TAP messages implement the TapMessageBody trait, which provides a validate() method for checking message correctness:
Authorizable Trait
The Authorizable trait provides methods for handling authorization, rejection, and settlement flows:
Derive Macro for TAP Messages
The #[derive(TapMessage)] macro automatically implements both TapMessage and MessageContext traits based on field attributes:
Basic Usage
use TapMessage;
use ;
use PlainMessage;
use Result;
use ;
// You still need to implement TapMessageBody for message-specific logic
Supported Attributes
#[tap(participant)]- Single participant field (required or optional)#[tap(participant_list)]- Vec field#[tap(transaction_id)]- Transaction ID field for threading#[tap(optional_transaction_id)]- Optional transaction ID#[tap(thread_id)]- Thread ID field for thread-based messages
What the Macro Provides
The derive macro automatically implements:
-
TapMessage trait:
thread_id()- Returns transaction/thread IDmessage_id()- Returns appropriate message identifierget_all_participants()- Extracts all participant DIDscreate_reply()- Creates properly threaded reply messages
-
MessageContext trait:
participants()- Returns references to all Participant objectsparticipant_dids()- Returns all participant DIDstransaction_context()- Returns transaction context with ID and type
Example with Generated Methods
let transfer = CustomTransfer ;
// Automatically implemented methods:
println!; // Some("tx-123")
println!;
// ["did:example:alice", "did:example:bob", "did:example:agent"]
// MessageContext methods:
let participants = transfer.participants; // Vec of &Participant
let tx_context = transfer.transaction_context; // TransactionContext
Name Hashing (TAIP-12)
TAP supports privacy-preserving name sharing through TAIP-12 compliant hashing:
use ;
use Party;
// Hash a name directly
let hash = hash_name;
assert_eq!;
// Use with Party objects
let party = new
.with_name_hash;
// The party now includes the nameHash metadata
assert_eq!;
// Implement NameHashable for your own types
// Now you can use the trait method
let hash = hash_name;
assert_eq!;
The name hashing follows TAIP-12 normalization:
- Remove all whitespace characters
- Convert to uppercase
- Apply SHA-256 hash
- Return as lowercase hex string
This enables compliance with Travel Rule requirements while preserving privacy by sharing only hashed names that can be matched against sanctions lists without revealing personal information.
Adding New Message Types
To add a new TAP message type, you have two options:
Option 1: Using the Derive Macro (Recommended)
- Define your message struct with appropriate field attributes
- Add
#[derive(TapMessage)]to automatically implement traits - Implement the
TapMessageBodytrait for message-specific logic - Optional: Implement the
Authorizabletrait for messages that can be authorized
Option 2: Manual Implementation
To add a new TAP message type manually, follow these steps:
- Define your message struct with required fields
- Implement the
TapMessageBodytrait for your struct - Implement the
TapMessagetrait manually - Optional: Implement the
MessageContexttrait for participant extraction - Optional: Implement the
Authorizabletrait for messages that can be authorized
Here's an example of manual implementation:
use TapMessageBody;
use Result;
use ;
use HashMap;
/// Define your new message struct
/// Implement the TapMessageBody trait
Examples
See the examples directory for more detailed examples of using TAP messages.