|
| 1 | +# Asset Custodian |
| 2 | + |
| 3 | +The `Custodian` is a core component within the `tapgarden` package responsible |
| 4 | +for managing the lifecycle of incoming Taproot Assets. It operates as an |
| 5 | +event-driven system that listens for various triggers, tracks the state of each |
| 6 | +incoming asset transfer, and ensures that assets are securely and verifiably |
| 7 | +received into the wallet. |
| 8 | + |
| 9 | +## Core Responsibilities |
| 10 | + |
| 11 | +- **Address Management**: Imports new addresses into the underlying `lnd` |
| 12 | + wallet for on-chain detection (for V0/V1 addresses) or subscribes to an |
| 13 | + authentication mailbox for message-based notifications from the sender (for |
| 14 | + V2+ addresses). |
| 15 | +- **On-Chain Event Monitoring**: Watches for new transactions that match managed |
| 16 | + Taproot output keys. |
| 17 | +- **Proof Retrieval**: Coordinates with `ProofCourier` services to fetch asset |
| 18 | + provenance proofs from senders. |
| 19 | +- **Proof Validation & Storage**: Verifies the integrity and validity of |
| 20 | + incoming proofs and stores them in the `ProofArchive`. |
| 21 | +- **State Management**: Tracks the status of each inbound asset transfer through |
| 22 | + a series of states, from detection to final completion. |
| 23 | + |
| 24 | +## Event Triggers and State Flow |
| 25 | + |
| 26 | +The Custodian's logic is driven by four primary event triggers. Each trigger |
| 27 | +initiates a series of actions that advance an asset transfer through its |
| 28 | +lifecycle. |
| 29 | + |
| 30 | +### Asset State Transitions |
| 31 | + |
| 32 | +The lifecycle of an incoming asset transfer is tracked by the `address.Status` |
| 33 | +field of an `address.Event`. The diagram below illustrates the possible state |
| 34 | +transitions. |
| 35 | + |
| 36 | +```mermaid |
| 37 | +stateDiagram-v2 |
| 38 | + direction LR |
| 39 | + [*] --> CREATED: New Address |
| 40 | + CREATED --> DETECTED: On-chain TX detected (V0/V1) |
| 41 | + CREATED --> CONFIRMED: Mailbox message received (V2) |
| 42 | + DETECTED --> CONFIRMED: On-chain TX confirmed |
| 43 | + CONFIRMED --> PROOF_RECEIVED: Proof retrieval successful |
| 44 | + PROOF_RECEIVED --> COMPLETED: All proofs for transfer validated |
| 45 | + COMPLETED --> [*] |
| 46 | +
|
| 47 | + state "Event States" as States { |
| 48 | + DETECTED: StatusTransactionDetected |
| 49 | + CONFIRMED: StatusTransactionConfirmed |
| 50 | + PROOF_RECEIVED: StatusProofReceived |
| 51 | + COMPLETED: StatusCompleted |
| 52 | + } |
| 53 | +``` |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +### Trigger 1: New Address Creation |
| 58 | + |
| 59 | +When a new Taproot Asset address is created and added to the `AddrBook`, the |
| 60 | +Custodian is notified. Its behavior depends on the address version. |
| 61 | + |
| 62 | +- **V0/V1 Addresses**: The Custodian takes the `TaprootOutputKey` from the |
| 63 | + address and imports it into the `lnd` wallet. This allows the wallet to scan |
| 64 | + the blockchain for transactions paying to this specific key. |
| 65 | +- **V2+ Addresses**: The Custodian subscribes to a `ProofCourier` of type |
| 66 | + `AuthMailbox` using the address's unique script key. It does not import any |
| 67 | + keys into the on-chain wallet, as V2 transfers are communicated as separate |
| 68 | + messages sent through the authenticated mailbox system. |
| 69 | + |
| 70 | +### Trigger 2: New Wallet Transaction (V0/V1 Addresses) |
| 71 | + |
| 72 | +When the `WalletAnchor` detects a new transaction, the Custodian inspects it. |
| 73 | + |
| 74 | +1. It checks if the transaction has a Taproot output that belongs to the |
| 75 | + internal wallet. |
| 76 | +2. If a match is found, it queries the `AddrBook` to see if this output key |
| 77 | + corresponds to a known Taproot Asset address. |
| 78 | +3. If it's a match, a new `address.Event` is created with the status |
| 79 | + `StatusTransactionDetected`. |
| 80 | +4. When the transaction receives its first confirmation, the event's status is |
| 81 | + updated to `StatusTransactionConfirmed`. This triggers the proof retrieval |
| 82 | + process. |
| 83 | + |
| 84 | +```mermaid |
| 85 | +sequenceDiagram |
| 86 | + participant User |
| 87 | + participant AddrBook |
| 88 | + participant Custodian |
| 89 | + participant WalletAnchor |
| 90 | + participant ProofCourier |
| 91 | + participant ProofArchive |
| 92 | +
|
| 93 | + User->>AddrBook: Create New Address (V0/V1) |
| 94 | + AddrBook->>Custodian: Notify(New Address) |
| 95 | + Custodian->>WalletAnchor: ImportTaprootOutput(key) |
| 96 | + note right of Custodian: Custodian now watches for on-chain TXs |
| 97 | +
|
| 98 | + WalletAnchor->>Custodian: Notify(New TX) |
| 99 | + Custodian->>AddrBook: GetOrCreateEvent(StatusTransactionDetected) |
| 100 | + WalletAnchor->>Custodian: Notify(TX Confirmed) |
| 101 | + Custodian->>AddrBook: UpdateEvent(StatusTransactionConfirmed) |
| 102 | + activate Custodian |
| 103 | + Custodian->>ProofCourier: ReceiveProof() |
| 104 | + ProofCourier-->>Custodian: Return Proof |
| 105 | + deactivate Custodian |
| 106 | + Custodian->>ProofArchive: ImportProofs() |
| 107 | + ProofArchive->>Custodian: Notify(New Proof) |
| 108 | + Custodian->>AddrBook: UpdateEvent(StatusProofReceived) |
| 109 | + Custodian->>AddrBook: CompleteEvent(StatusCompleted) |
| 110 | +``` |
| 111 | + |
| 112 | +### Trigger 3: New Mailbox Message (V2+ Addresses) |
| 113 | + |
| 114 | +For V2 addresses, the process is initiated by an incoming authenticated mailbox |
| 115 | +message. |
| 116 | + |
| 117 | +1. The Custodian receives an encrypted message from the `AuthMailbox` server |
| 118 | + it's subscribed to. |
| 119 | +2. It decrypts the message using the address's script key to reveal a |
| 120 | + `SendFragment`. This fragment contains the outpoint of the on-chain anchor |
| 121 | + transaction. |
| 122 | +3. Using the information from the fragment, the Custodian creates a new |
| 123 | + `address.Event` directly with the status `StatusTransactionConfirmed`, as the |
| 124 | + fragment is only sent after the transaction is confirmed. |
| 125 | +4. This immediately triggers the proof retrieval process. |
| 126 | + |
| 127 | +```mermaid |
| 128 | +sequenceDiagram |
| 129 | + participant User |
| 130 | + participant AddrBook |
| 131 | + participant Custodian |
| 132 | + participant MailboxServer |
| 133 | + participant ProofCourier |
| 134 | + participant ProofArchive |
| 135 | +
|
| 136 | + User->>AddrBook: Create New Address (V2) |
| 137 | + AddrBook->>Custodian: Notify(New Address) |
| 138 | + Custodian->>MailboxServer: Subscribe(script_key) |
| 139 | + note right of Custodian: Custodian now listens for mailbox messages |
| 140 | +
|
| 141 | + MailboxServer->>Custodian: Notify(New Message) |
| 142 | + activate Custodian |
| 143 | + Custodian->>Custodian: Decrypt Message (SendFragment) |
| 144 | + Custodian->>AddrBook: GetOrCreateEvent(StatusTransactionConfirmed) |
| 145 | + deactivate Custodian |
| 146 | +
|
| 147 | + activate Custodian |
| 148 | + Custodian->>ProofCourier: ReceiveProof() |
| 149 | + ProofCourier-->>Custodian: Return Proof |
| 150 | + deactivate Custodian |
| 151 | + Custodian->>ProofArchive: ImportProofs() |
| 152 | + ProofArchive->>Custodian: Notify(New Proof) |
| 153 | + Custodian->>AddrBook: UpdateEvent(StatusProofReceived) |
| 154 | + Custodian->>AddrBook: CompleteEvent(StatusCompleted) |
| 155 | +``` |
| 156 | + |
| 157 | +### Trigger 4: New Proof Received |
| 158 | + |
| 159 | +This trigger is the final step in the process, common to all address versions. |
| 160 | + |
| 161 | +1. After a transaction is confirmed (either on-chain or via mailbox), the |
| 162 | + `receiveProofs` function is called. It uses the `ProofCourier` specified in |
| 163 | + the address to fetch the asset proof. |
| 164 | +2. The fetched proof is sent to the `ProofArchive` for validation and storage. |
| 165 | +3. Importing a proof into the `ProofArchive` will make sure it is imported into |
| 166 | + both the file-based local proof archive and the database-backed proof store. |
| 167 | + Both those stores will trigger a notification (via the `ProofNotifier`) for |
| 168 | + received proofs, so the below might be invoked multiple times |
| 169 | +4. The Custodian finds the corresponding `address.Event` and updates its status |
| 170 | + to `StatusProofReceived`. |
| 171 | +5. It then calls `setReceiveCompleted`, which verifies that all required proofs |
| 172 | + for the transfer have been received. |
| 173 | +6. Finally, the event status is set to `StatusCompleted`, marking the end of the |
| 174 | + asset custody process. The asset is now considered fully received and |
| 175 | + settled. |
0 commit comments