Document payment verification and trezor safekeeping
This commit is contained in:
119
04 - Flows/Trezor Safekeeping Flow.md
Normal file
119
04 - Flows/Trezor Safekeeping Flow.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Trezor Safekeeping Flow
|
||||
|
||||
This flow adds hardware-backed custody controls without replacing the current payment model. The backend never stores private keys. Trezor support starts as a single hardware signer and is designed to upgrade to multisig later.
|
||||
|
||||
Default mode: optional. Existing release/refund flows do not require Trezor proof unless `TREZOR_SAFEKEEPING_REQUIRED=true`.
|
||||
|
||||
## Goals
|
||||
|
||||
- Generate a fresh receive address per user/payment from a registered Trezor xpub.
|
||||
- Require a Trezor-produced signature before release/refund confirmation when safekeeping enforcement is enabled.
|
||||
- Keep SHKeeper and Request Network optional provider paths intact.
|
||||
- Preserve the existing `Payment` model and orchestration surface.
|
||||
|
||||
## Registration
|
||||
|
||||
1. User connects a Trezor in the frontend and exports an Ethereum account xpub, for example `m/44'/60'/0'`.
|
||||
2. Backend builds a registration challenge:
|
||||
- `GET /api/trezor/registration-message?xpub=...®istrationAddress=...`
|
||||
3. The registration address must be the first derived address from the xpub:
|
||||
- `m/44'/60'/0'/0/0`
|
||||
4. User signs the challenge with that Trezor address.
|
||||
5. Frontend submits:
|
||||
- `POST /api/trezor/register`
|
||||
- `xpub`
|
||||
- `registrationAddress`
|
||||
- `proofMessage`
|
||||
- `proofSignature`
|
||||
- optional `basePath`, `deviceLabel`
|
||||
6. Backend verifies:
|
||||
- xpub is public, not private.
|
||||
- registration address matches xpub-derived index `0`.
|
||||
- signature recovers the registration address.
|
||||
7. Backend stores only:
|
||||
- `userId`
|
||||
- xpub fingerprint
|
||||
- xpub
|
||||
- base derivation path
|
||||
- registration address
|
||||
- next address index
|
||||
- issued address records
|
||||
|
||||
## Address Generation
|
||||
|
||||
To issue the next payment address:
|
||||
|
||||
```http
|
||||
POST /api/trezor/addresses/next
|
||||
{
|
||||
"purpose": "deposit",
|
||||
"paymentId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
The backend derives non-hardened receive addresses from the registered xpub:
|
||||
|
||||
```text
|
||||
m/44'/60'/0'/0/{index}
|
||||
```
|
||||
|
||||
If a `paymentId` already has an address, the endpoint returns the same address instead of incrementing the index.
|
||||
|
||||
## Transaction Approval
|
||||
|
||||
Before a release/refund confirmation, the admin asks the backend for the exact operation message:
|
||||
|
||||
```http
|
||||
POST /api/trezor/operation-message
|
||||
{
|
||||
"operation": "release",
|
||||
"paymentId": "...",
|
||||
"transactionHash": "0x...",
|
||||
"amount": 100,
|
||||
"currency": "USDT",
|
||||
"provider": "request.network"
|
||||
}
|
||||
```
|
||||
|
||||
The Trezor signs that message. Release/refund confirmation then includes:
|
||||
|
||||
```json
|
||||
{
|
||||
"txHash": "0x...",
|
||||
"trezor": {
|
||||
"message": "Amanat escrow Trezor transaction approval\n...",
|
||||
"signature": "0x..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When `TREZOR_SAFEKEEPING_REQUIRED=true`, `confirmReleaseRefundInstruction` verifies the signature before calling the payment adapter confirmation path.
|
||||
|
||||
## Enforcement Flag
|
||||
|
||||
```env
|
||||
TREZOR_SAFEKEEPING_REQUIRED=false
|
||||
```
|
||||
|
||||
Default is permissive so existing SHKeeper and Request Network flows continue to work. Set it to `true` only after registering the operating admin's Trezor account and testing the signing path. Any value other than the literal string `true` is treated as disabled.
|
||||
|
||||
## Safety Rules
|
||||
|
||||
- Never store Trezor seed words, private keys, or xprv/tprv values.
|
||||
- Reject private extended keys at registration.
|
||||
- Verify every signature locally before accepting it.
|
||||
- Use exact transaction-intent messages; do not accept free-form signatures.
|
||||
- Treat generated deposit addresses as public routing metadata, not as proof of payment.
|
||||
- Keep ledger availability checks enabled for release/refund accounting.
|
||||
|
||||
## Upgrade Path To Multisig
|
||||
|
||||
The current design stores a single `trezor-eoa` signer. Later, replace the signer policy with:
|
||||
|
||||
- `addressType: safe-multisig`
|
||||
- a Safe address per tenant/admin group
|
||||
- threshold policy, such as `2-of-3`
|
||||
- Trezor owners as Safe signers
|
||||
- release/refund flow creates a Safe transaction and records collected signatures before execution
|
||||
|
||||
The payment orchestration API should stay the same: build instruction, collect hardware-backed approval, confirm release/refund, append ledger entry.
|
||||
Reference in New Issue
Block a user