Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 9 additions & 32 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ Before implementing any task:
3. Log blockers/decisions under `## Notes & Decisions`
4. Mark plan as `✅ COMPLETE` when finished

### Documentation Rule
Before marking a task complete, update or create the relevant documentation under `docs/`:
- If the task changes a public API, protocol, or user-facing behaviour, update the corresponding `docs/` page (or create one if it does not exist).
- Keep docs consistent with code: function names, message fields, flow diagrams, and examples must match the implementation.
- New `docs/` pages must follow the existing style (Markdown, same heading hierarchy as neighbouring files).
- If no existing doc page covers the changed area, create `docs/<subsystem>/<topic>.md` and add a link from the nearest index or README.

## 🔍 Debugging & Advanced Testing

### Log Locations
Expand All @@ -197,38 +204,8 @@ Before implementing any task:
- `token/`: Core SDK logic
- `integration/`: Integration tests and Network Orchestrator

## 🔄 CI Workflow Overview

To ensure your commits pass CI automatically, understand what runs:

### 🔧 Pre-Merge Checks (GitHub Actions)
All PRs and pushes to `main` trigger these workflows:

1. **Checks Job** (Prerequisite):
- License verification
- Code formatting (`gofmt`, `goimports`)
- Static analysis (`govet`, `staticcheck`, `ineffassign`, `misspell`)
- *Run locally with:* `make checks`

2. **Unit Testing**:
- Race detector enabled tests
- Regression tests
- Coverage reporting to Coveralls

3. **Integration Testing** (Extensive Matrix):
- Fabtoken (cleartext tokens): t1-t5
- ZKATDLog (privacy tokens): t1-t13
- Fabric-X, Interop, NFT, DVP, Update tests
- Stress tests
- All with coverage reporting

4. **Separate Workflows**:
- **golangci-lint**: Comprehensive linting (30 min timeout)
- **Markdown links**: Validates all doc links
- **CodeQL**: Security analysis (weekly + on push/PR)

### 💡 Best Practices for CI Success
## 💡 Best Practices for CI Success
Before marking a task complete:
- **Always run** `make checks` and `make lint-auto-fix` before committing
- **Verify** `FAB_BINS` is set for integration test compatibility
- **Address** all linting and static check warnings promptly
- **Keep** dependencies updated with `make tidy`
56 changes: 38 additions & 18 deletions docs/services/ttx.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,24 @@ The lifecycle of a token transaction typically involves the following stages, co
```mermaid
sequenceDiagram
autonumber
participant Initiator

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I view this file I see the following error (see picture)

Image

participant Recipient
participant Auditor
participant Network as Network Service
participant Ledger as DLT / Ledger

box darkgreen Panurus Stack
participant Initiator
participant Recipient
participant Auditor
participant Network
participant Network as Network Service
end
participant Ledger as DLT / Ledger

Note over Initiator: 1. Request Identities (with nonce/signature attestation)
Initiator->>+Recipient: RequestRecipientIdentityView (includes Nonce)
Recipient-->>-Initiator: RecipientResponse (Identity + Audit Info + Signature)

Note over Initiator: 2. Assemble Request
Initiator->>+Initiator: Issue / Transfer / Redeem operations
Initiator->>Initiator: Issue / Transfer / Redeem operations

Note over Initiator: 3. Collect Endorsements
Initiator->>+Initiator: Sign locally
Initiator->>Initiator: Sign locally
Initiator->>+Recipient: Request Signatures (for spent tokens)
Recipient-->>-Initiator: Signature
Initiator->>+Auditor: AuditApproveView
Expand All @@ -41,13 +37,13 @@ sequenceDiagram
Network-->>-Initiator: Endorsed Envelope

Note over Initiator: 4. Distribution & Ordering
Initiator->>+Recipient: Distribute Transaction Metadata
Initiator->>Recipient: Distribute Transaction Metadata
Initiator->>+Network: Broadcast Transaction
Network->>+Ledger: Submit to Orderer
Ledger-->>-Network: Transaction Committed

Note over Initiator: 5. Finality Tracking
Initiator->>+Network: Listen for Finality
Ledger-->>-Network: Transaction Committed
Network-->>-Initiator: Notify Finality
```

Expand Down Expand Up @@ -290,7 +286,7 @@ The message-type discriminators live with the service that uses them — the ttx
| `TypeRecipientRequest` / `TypeRecipientResponse` | `recipient_req` / `recipient_resp` | `recipients.go` request flow |
| `TypeExchangeRecipientRequest` / `TypeExchangeRecipientResp` | `exchange_req` / `exchange_resp` | `recipients.go` exchange flow |
| `TypeMultisigRecipientData` / `TypePolicyRecipientData` | `multisig_data` / `policy_data` | recipient follow-ups for multisig / policy identities |
| `TypeWithdrawalRequest` | `withdrawal_req` | `withdrawal.go` |
| `TypeWithdrawalRequest` / `TypeWithdrawalChallenge` / `TypeWithdrawalResponse` | `withdrawal_req` / `withdrawal_challenge` / `withdrawal_resp` | `withdrawal.go` |
| `TypeUpgradeAgreement` / `TypeUpgradeRequest` | `upgrade_agree` / `upgrade_req` | `upgrade.go` |
| `TypeSpendRequest` / `TypeSpendResponse` | `spend_req` / `spend_resp` | `multisig/spend.go`, `boolpolicy/spend.go` |
| `TypeSignatureRequest` / `TypeSignature` | `sig_req` / `signature` | `collectendorsements.go`, `endorse.go`, `accept.go`, `auditor.go` |
Expand All @@ -317,21 +313,45 @@ All satisfy `errors.Is`. `VersionCompatibility` / `IsCompatible(local, remote)`

## Withdrawal Flow

The withdrawal protocol (`withdrawal.go`) lets a wallet ask an issuer to mint tokens to a freshly generated recipient identity. Single-shot: the initiator sends a `WithdrawalRequest`; the issuer registers the recipient identity and returns the session for the subsequent issuance flow.
The withdrawal protocol (`withdrawal.go`) lets a wallet ask an issuer to mint tokens to a freshly generated recipient identity. It uses a **three-message challenge-response** so that freshness is controlled by the issuer the issuer samples the nonce, which prevents an attacker from pre-computing a valid `(nonce, signature)` pair and replaying it across sessions.

```mermaid
sequenceDiagram
autonumber
participant I as Initiator (RequestWithdrawalView)
participant Iss as Issuer (ReceiveWithdrawalRequestView)

I->>I: Resolve recipient identity (caller-supplied or wallet-generated)
I->>Iss: Envelope{t:"withdrawal_req", b:WithdrawalRequest{TMSID, RecipientData, TokenType, Amount, NotAnonymous}}
Iss->>Iss: RegisterRecipientIdentity(request.RecipientData)
Iss->>Iss: endpoint.Bind(caller -> RecipientData.Identity)
Note over I,Iss: Session returned to caller for the issuance/endorsement flow
rect rgba(230, 230, 250, 0.35)
Note over I,Iss: Phase 1 - Request
I->>I: Resolve recipient identity (caller-supplied or wallet-generated)
I->>Iss: Envelope{t:"withdrawal_req", b:WithdrawalRequest{TMSID, RecipientData, TokenType, Amount, NotAnonymous}}
end

rect rgba(255, 245, 238, 0.5)
Note over Iss: Phase 2 - Challenge
Iss->>Iss: nonce = GetRandomNonce()
Iss-->>I: Envelope{t:"withdrawal_challenge", b:WithdrawalChallenge{Nonce}}
end

rect rgba(240, 255, 240, 0.45)
Note over I: Phase 3 - Response
I->>I: msg = asn1(TMSID + nil walletID + RecipientData.Identity + nonce + session id + context id)
I->>I: sig = Sign(msg)
I->>Iss: Envelope{t:"withdrawal_resp", b:WithdrawalResponse{Signature: sig}}
end

rect rgba(245, 245, 245, 0.55)
Note over Iss: Phase 4 - Verification and registration
Iss->>Iss: msg = asn1(same fields, using issuer-held nonce)
Iss->>Iss: verifier.Verify(msg, resp.Signature)
Iss->>Iss: RegisterRecipientIdentity(request.RecipientData)
Iss->>Iss: endpoint.Bind(caller -> RecipientData.Identity)
Note over I,Iss: Session returned to caller for the issuance/endorsement flow
end
```

The attestation message is the same DER-encoded (`encoding/asn1`) structure used by the recipient-identity protocols, with `walletID` fixed to `nil` — the issuer does not need to know the requester's wallet identifier.

## Token Upgrade Flow

The upgrade protocol (`upgrade.go`) exchanges old-format tokens for new-format ones over two round-trips: an agreement that establishes a fresh challenge, then a request carrying the proof.
Expand Down Expand Up @@ -409,7 +429,7 @@ sequenceDiagram
I->>R: Envelope{t:"transaction", b:TransactionPayload{Raw}}
I->>R: Envelope{t:"actions", b:Actions}
I->>R: Envelope{t:"action_transfer", b:ActionTransfer}
R->>R: Receive transaction, actions, action; assemble
R->>R: Receive transaction, actions, action assemble
R-->>I: Envelope{t:"tx_resp", b:TransactionPayload{Raw}}
```

Expand Down
14 changes: 14 additions & 0 deletions token/services/ttx/envelope_protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ func TestVersionedWithdrawalRequestRoundTrip(t *testing.T) {
roundTripTTXMessage(t, TypeWithdrawalRequest, original, &WithdrawalRequest{})
}

func TestVersionedWithdrawalChallengeRoundTrip(t *testing.T) {
original := &WithdrawalChallenge{
Nonce: []byte("withdrawal-nonce-32bytes-pad-xxxx"),
}
roundTripTTXMessage(t, TypeWithdrawalChallenge, original, &WithdrawalChallenge{})
}

func TestVersionedWithdrawalResponseRoundTrip(t *testing.T) {
original := &WithdrawalResponse{
Signature: []byte("attestation-sig"),
}
roundTripTTXMessage(t, TypeWithdrawalResponse, original, &WithdrawalResponse{})
}

func TestVersionedUpgradeAgreementRoundTrip(t *testing.T) {
original := &UpgradeTokensAgreement{
Challenge: []byte("challenge"),
Expand Down
4 changes: 3 additions & 1 deletion token/services/ttx/protocol_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const (
TypePolicyRecipientData = "policy_data"

// withdrawal.go
TypeWithdrawalRequest = "withdrawal_req"
TypeWithdrawalRequest = "withdrawal_req"
TypeWithdrawalChallenge = "withdrawal_challenge"
TypeWithdrawalResponse = "withdrawal_resp"

// upgrade.go
TypeUpgradeAgreement = "upgrade_agree"
Expand Down
Loading
Loading