Skip to content
Merged
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
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
### Added

- GitHub Actions CI with test, lint (golangci-lint), and vulncheck jobs
- `TransferBody.TransactionValue` (TAIP-3) — optional fiat-equivalent value
(`amount`, `currency`) for Travel Rule threshold determination when an asset
is not widely traded

### Changed

- Bumped Go from 1.25.0 to 1.25.3 to fix stdlib vulnerabilities
- Bumped Go from 1.25.0 to 1.26.2 to fix stdlib vulnerabilities
- Use published go-didcomm v0.1.0 instead of local replace directive
- **BREAKING (TAIP-17):** Renamed `Escrow` message type to `Lock`. Constants,
types, constructors, files, and CLI subcommand all renamed:
`TypeEscrow` → `TypeLock`, `EscrowBody` → `LockBody`,
`NewEscrowMessage` → `NewLockMessage`, `escrow.go` → `lock.go`,
`tap message escrow` → `tap message lock`. The `EscrowAgent` role name is
preserved.
- **BREAKING (TAIP-18):** Renamed `Exchange` message type to `RFQ` (Request
for Quote). `TypeExchange` → `TypeRFQ`, `ExchangeBody` → `RFQBody`,
`NewExchangeMessage` → `NewRFQMessage`, `exchange.go` → `rfq.go`,
`tap message exchange` → `tap message rfq`.
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ Single flat `tap` package — all types and helpers in the root. One file per me
| `client.go` | `Client` wrapping `didcomm.Client`, `Receive()` returning `TAPResult` |
| `transfer.go` | `TransferBody` + `NewTransferMessage()` |
| `payment.go` | `PaymentBody` + `NewPaymentMessage()` |
| `exchange.go` | `ExchangeBody` + `NewExchangeMessage()` |
| `rfq.go` | `RFQBody` + `NewRFQMessage()` |
| `quote.go` | `QuoteBody` + `NewQuoteMessage()` |
| `escrow.go` | `EscrowBody` + `NewEscrowMessage()` |
| `lock.go` | `LockBody` + `NewLockMessage()` |
| `authorize.go` | `AuthorizeBody` + `NewAuthorizeMessage()` |
| `authorization_required.go` | `AuthorizationRequiredBody` + `NewAuthorizationRequiredMessage()` |
| `settle.go` | `SettleBody` + `NewSettleMessage()` |
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ envelope, _ := dc.PackAuthcrypt(ctx, authorizeMsg)
|------------|-------------|------|-----------------|
| `NewTransferMessage` | `TransferBody` | [TAIP-3](https://tap.rsvp/TAIPs/taip-3) | `asset`, `agents` |
| `NewPaymentMessage` | `PaymentBody` | [TAIP-14](https://tap.rsvp/TAIPs/taip-14) | `amount`, `merchant`, `agents`, `asset` or `currency` |
| `NewExchangeMessage` | `ExchangeBody` | [TAIP-18](https://tap.rsvp/TAIPs/taip-18) | `fromAssets`, `toAssets`, `requester`, `agents`, `fromAmount` or `toAmount` |
| `NewRFQMessage` | `RFQBody` | [TAIP-18](https://tap.rsvp/TAIPs/taip-18) | `fromAssets`, `toAssets`, `requester`, `agents`, `fromAmount` or `toAmount` |
| `NewQuoteMessage` | `QuoteBody` | [TAIP-18](https://tap.rsvp/TAIPs/taip-18) | `fromAsset`, `toAsset`, `fromAmount`, `toAmount`, `provider`, `agents`, `expiresAt` |
| `NewEscrowMessage` | `EscrowBody` | [TAIP-17](https://tap.rsvp/TAIPs/taip-17) | `amount`, `originator`, `beneficiary`, `expiry`, `agents`, `asset` or `currency` |
| `NewLockMessage` | `LockBody` | [TAIP-17](https://tap.rsvp/TAIPs/taip-17) | `amount`, `originator`, `beneficiary`, `expiry`, `agents`, `asset` or `currency` |

### Authorization Flow Messages

Expand Down Expand Up @@ -331,7 +331,7 @@ The `receive` command outputs JSON with the unpacked message, typed body, and en

### TAP message types

**Initiating (no `--thid`):** `transfer`, `payment`, `exchange`, `escrow`, `connect`
**Initiating (no `--thid`):** `transfer`, `payment`, `rfq`, `lock`, `connect`

**Reply (require `--thid`):** `authorize`, `authorization-required`, `settle`, `reject`, `cancel`, `revert`, `capture`, `quote`, `add-agents`, `remove-agent`, `replace-agent`, `update-agent`, `update-party`, `update-policies`, `confirm-relationship`

Expand Down
2 changes: 1 addition & 1 deletion cmd/tap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Commands:
help Print this help

TAP message types:
Initiating: transfer, payment, exchange, escrow, connect
Initiating: transfer, payment, rfq, lock, connect
Reply: authorize, authorization-required, settle, reject, cancel,
revert, capture, quote, add-agents, remove-agent,
replace-agent, update-agent, update-party, update-policies,
Expand Down
26 changes: 13 additions & 13 deletions cmd/tap/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
const messageUsage = `Usage: tap message <type> --from <did> --to <did> [--thid <id>] [--body <json>]

Initiating types (no --thid):
transfer, payment, exchange, escrow, connect
transfer, payment, rfq, lock, connect

Reply types (require --thid):
authorize, authorization-required, settle, reject, cancel, revert,
Expand Down Expand Up @@ -111,10 +111,10 @@ func runMessage(args []string) error {
return runMessageTransfer(msgArgs)
case "payment":
return runMessagePayment(msgArgs)
case "exchange":
return runMessageExchange(msgArgs)
case "escrow":
return runMessageEscrow(msgArgs)
case "rfq":
return runMessageRFQ(msgArgs)
case "lock":
return runMessageLock(msgArgs)
case "connect":
return runMessageConnect(msgArgs)

Expand Down Expand Up @@ -189,32 +189,32 @@ func runMessagePayment(args []string) error {
return writeMessage(msg)
}

func runMessageExchange(args []string) error {
f, err := parseMessageFlags("exchange", args, false)
func runMessageRFQ(args []string) error {
f, err := parseMessageFlags("rfq", args, false)
if err != nil {
return err
}
var body tap.ExchangeBody
var body tap.RFQBody
if err := json.Unmarshal(f.body, &body); err != nil {
return fmt.Errorf("parse body JSON: %w", err)
}
msg, err := tap.NewExchangeMessage(f.from, f.to, &body)
msg, err := tap.NewRFQMessage(f.from, f.to, &body)
if err != nil {
return err
}
return writeMessage(msg)
}

func runMessageEscrow(args []string) error {
f, err := parseMessageFlags("escrow", args, false)
func runMessageLock(args []string) error {
f, err := parseMessageFlags("lock", args, false)
if err != nil {
return err
}
var body tap.EscrowBody
var body tap.LockBody
if err := json.Unmarshal(f.body, &body); err != nil {
return fmt.Errorf("parse body JSON: %w", err)
}
msg, err := tap.NewEscrowMessage(f.from, f.to, &body)
msg, err := tap.NewLockMessage(f.from, f.to, &body)
if err != nil {
return err
}
Expand Down
20 changes: 10 additions & 10 deletions cmd/tap/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,49 +237,49 @@ func TestCLI_MessagePayment(t *testing.T) {
}
}

func TestCLI_MessageExchange(t *testing.T) {
func TestCLI_MessageRFQ(t *testing.T) {
bin := buildBinary(t)
body := `{"fromAssets":["eip155:1/slip44:60"],"toAssets":["eip155:1/slip44:0"],"fromAmount":"1.0","requester":{"@id":"did:key:z1"},"agents":[{"@id":"did:key:z1","role":"OriginatingVASP"}]}`

cmd := exec.Command(bin, "message", "exchange",
cmd := exec.Command(bin, "message", "rfq",
"--from", "did:key:z1",
"--to", "did:key:z2",
"--body", body,
)
out, err := cmd.Output()
if err != nil {
t.Fatalf("message exchange failed: %s", err)
t.Fatalf("message rfq failed: %s", err)
}

var msg didcomm.Message
if err := json.Unmarshal(out, &msg); err != nil {
t.Fatalf("invalid JSON: %s", err)
}
if msg.Type != tap.TypeExchange {
t.Fatalf("expected type %s, got %s", tap.TypeExchange, msg.Type)
if msg.Type != tap.TypeRFQ {
t.Fatalf("expected type %s, got %s", tap.TypeRFQ, msg.Type)
}
}

func TestCLI_MessageEscrow(t *testing.T) {
func TestCLI_MessageLock(t *testing.T) {
bin := buildBinary(t)
body := `{"asset":"eip155:1/slip44:60","amount":"5.0","originator":{"@id":"did:key:z1"},"beneficiary":{"@id":"did:key:z2"},"expiry":"2025-12-31T23:59:59Z","agents":[{"@id":"did:key:z1","role":"OriginatingVASP"}]}`

cmd := exec.Command(bin, "message", "escrow",
cmd := exec.Command(bin, "message", "lock",
"--from", "did:key:z1",
"--to", "did:key:z2",
"--body", body,
)
out, err := cmd.Output()
if err != nil {
t.Fatalf("message escrow failed: %s", err)
t.Fatalf("message lock failed: %s", err)
}

var msg didcomm.Message
if err := json.Unmarshal(out, &msg); err != nil {
t.Fatalf("invalid JSON: %s", err)
}
if msg.Type != tap.TypeEscrow {
t.Fatalf("expected type %s, got %s", tap.TypeEscrow, msg.Type)
if msg.Type != tap.TypeLock {
t.Fatalf("expected type %s, got %s", tap.TypeLock, msg.Type)
}
}

Expand Down
6 changes: 3 additions & 3 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
// Transaction messages initiate financial operations:
// - Transfer (TAIP-3) — asset transfer between parties
// - Payment (TAIP-14) — merchant payment request
// - Exchange (TAIP-18) — asset exchange request
// - RFQ (TAIP-18) — request for quote on an asset exchange
// - Quote (TAIP-18) — exchange price quote response
// - Escrow (TAIP-17) — hold funds in escrow
// - Lock (TAIP-17) — hold funds in escrow
//
// Authorization flow messages manage transaction lifecycle:
// - Authorize (TAIP-4) — approve a transaction
Expand All @@ -27,7 +27,7 @@
// - Reject (TAIP-4) — reject a transaction
// - Cancel (TAIP-4) — cancel a transaction
// - Revert (TAIP-4) — request reversal of settled transaction
// - Capture (TAIP-17) — release escrowed funds
// - Capture (TAIP-17) — release locked funds
//
// Agent management messages modify transaction participants:
// - UpdateAgent (TAIP-5) — update agent information
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/TransactionAuthorizationProtocol/tap-go

go 1.26.0
go 1.26.2

require (
github.com/Notabene-id/go-didcomm v0.2.0
Expand Down
14 changes: 7 additions & 7 deletions escrow.go → lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"github.com/google/uuid"
)

// EscrowBody represents the body of a TAP Escrow message (TAIP-17).
type EscrowBody struct {
// LockBody represents the body of a TAP Lock message (TAIP-17).
type LockBody struct {
Context string `json:"@context"`
Type string `json:"@type"`
Asset string `json:"asset,omitempty"`
Expand All @@ -22,10 +22,10 @@ type EscrowBody struct {
Agreement string `json:"agreement,omitempty"`
}

func (b *EscrowBody) TAPType() string { return TypeEscrow }
func (b *LockBody) TAPType() string { return TypeLock }

// NewEscrowMessage creates a new DIDComm message with an Escrow body.
func NewEscrowMessage(from string, to []string, body *EscrowBody) (*didcomm.Message, error) {
// NewLockMessage creates a new DIDComm message with a Lock body.
func NewLockMessage(from string, to []string, body *LockBody) (*didcomm.Message, error) {
if body.Amount == "" {
return nil, fmt.Errorf("%w: missing amount", ErrInvalidBody)
}
Expand All @@ -46,7 +46,7 @@ func NewEscrowMessage(from string, to []string, body *EscrowBody) (*didcomm.Mess
}

body.Context = TAPContext
body.Type = TypeEscrow
body.Type = TypeLock

rawBody, err := json.Marshal(body)
if err != nil {
Expand All @@ -55,7 +55,7 @@ func NewEscrowMessage(from string, to []string, body *EscrowBody) (*didcomm.Mess

return &didcomm.Message{
ID: uuid.New().String(),
Type: TypeEscrow,
Type: TypeLock,
From: from,
To: to,
Body: rawBody,
Expand Down
Loading
Loading