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
4 changes: 2 additions & 2 deletions CLAUDE.md

Large diffs are not rendered by default.

44 changes: 36 additions & 8 deletions activity/counterparty_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package activity

import (
"bytes"
"context"
"testing"
"time"
Expand Down Expand Up @@ -34,6 +35,11 @@ type counterpartyEnv struct {

// boostChatID is the group reactionBoost was paid in.
boostChatID *commonpb.ChatId

// requestChatID and requestID name the payment request requestedPayment
// paid a share of, and its group.
requestChatID *commonpb.ChatId
requestID *paymentrequestpb.PaymentRequestId
}

func newCounterpartyEnv(t *testing.T) *counterpartyEnv {
Expand Down Expand Up @@ -64,11 +70,13 @@ func newCounterpartyEnv(t *testing.T) *counterpartyEnv {
GroupPayment: &intentpb.ChatMetadata_GroupPayment{Action: intentpb.PaymentAction_SEND},
},
})
e.requestChatID = chat.MustGenerateGroupChatID()
e.requestID = &paymentrequestpb.PaymentRequestId{Value: bytes.Repeat([]byte{7}, 16)}
e.requestedPayment = chatAppMetadata(t, &intentpb.ChatMetadata{
ChatId: chat.MustGenerateGroupChatID(),
ChatId: e.requestChatID,
Type: &intentpb.ChatMetadata_RequestedPayment_{
RequestedPayment: &intentpb.ChatMetadata_RequestedPayment{
PaymentRequestId: &paymentrequestpb.PaymentRequestId{Value: make([]byte, 16)},
PaymentRequestId: e.requestID,
},
},
})
Expand Down Expand Up @@ -112,7 +120,7 @@ func chatAppMetadata(t *testing.T, chatMetadata *intentpb.ChatMetadata) []byte {
// TestHistoryCounterparties: a group payment names the other party by user
// ID from both sides, and reads Sent and Received, exactly as a DM payment
// does, and so does a requested payment, from its payer to the request's
// creator. A reaction boost paying one person names the other party the same
// creator, whose entries on both sides also name the request. A reaction boost paying one person names the other party the same
// way, and the payer's side reads Boosted. A payment carrying no chat
// metadata, like one handed off in person, names no one, and the payer's side
// reads Gave.
Expand All @@ -127,13 +135,14 @@ func TestHistoryCounterparties(t *testing.T) {
appMetadata []byte
named bool
boosted bool
requested bool
sentText string
}{
{"group_payment", e.groupPayment, true, false, "Sent"},
{"requested_payment", e.requestedPayment, true, false, "Sent"},
{"dm_payment", e.dmPayment, true, false, "Sent"},
{"reaction_boost", e.reactionBoost, true, true, "Boosted"},
{"no_chat_metadata", nil, false, false, "Gave"},
{"group_payment", e.groupPayment, true, false, false, "Sent"},
{"requested_payment", e.requestedPayment, true, false, true, "Sent"},
{"dm_payment", e.dmPayment, true, false, false, "Sent"},
{"reaction_boost", e.reactionBoost, true, true, false, "Boosted"},
{"no_chat_metadata", nil, false, false, false, "Gave"},
} {
t.Run(tc.name, func(t *testing.T) {
records := []*history.Record{
Expand All @@ -159,6 +168,13 @@ func TestHistoryCounterparties(t *testing.T) {
require.Nil(t, sent.GetDirectlySentCrypto().GetBoostedReaction())
require.Nil(t, received.GetReceivedCrypto().GetBoostedReaction())
}
if tc.requested {
e.requireRequestedPayment(t, sent.GetDirectlySentCrypto().GetRequestedPayment())
e.requireRequestedPayment(t, received.GetReceivedCrypto().GetRequestedPayment())
} else {
require.Nil(t, sent.GetDirectlySentCrypto().GetRequestedPayment())
require.Nil(t, received.GetReceivedCrypto().GetRequestedPayment())
}
if tc.named {
require.True(t, proto.Equal(e.recipient, sent.GetDirectlySentCrypto().GetUserId()))
require.True(t, proto.Equal(e.payer, received.GetReceivedCrypto().GetUserId()))
Expand Down Expand Up @@ -277,10 +293,12 @@ func TestHistoryCounterparties_GroupMultiPayment(t *testing.T) {
require.NotNil(t, notifications[0].GetDirectlySentCrypto())
require.Nil(t, notifications[0].GetDirectlySentCrypto().GetDestinationIdentifier())
require.Nil(t, notifications[0].GetDirectlySentCrypto().GetBoostedReaction())
require.Nil(t, notifications[0].GetDirectlySentCrypto().GetRequestedPayment())

require.Equal(t, "Received", notifications[1].LocalizedText)
require.True(t, proto.Equal(e.payer, notifications[1].GetReceivedCrypto().GetUserId()))
require.Nil(t, notifications[1].GetReceivedCrypto().GetBoostedReaction())
require.Nil(t, notifications[1].GetReceivedCrypto().GetRequestedPayment())
}

// requireBoostedReaction checks that a notification names the reaction
Expand All @@ -292,3 +310,13 @@ func (e *counterpartyEnv) requireBoostedReaction(t *testing.T, boosted *activity
require.EqualValues(t, 1, boosted.GetMessageId().GetValue())
require.Equal(t, "🔥", boosted.GetEmoji().GetValue())
}

// requireRequestedPayment checks that a notification names the payment
// request e.requestedPayment paid a share of, and that it is valid as the
// proto defines it.
func (e *counterpartyEnv) requireRequestedPayment(t *testing.T, requested *activitypb.RequestedPayment) {
require.NotNil(t, requested)
require.NoError(t, requested.Validate())
require.True(t, proto.Equal(e.requestChatID, requested.GetChatId()))
require.True(t, proto.Equal(e.requestID, requested.GetPaymentRequestId()))
}
31 changes: 29 additions & 2 deletions activity/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,13 @@ func toNotification(
UserId: userID,
}
}
metadata.BoostedReaction = toBoostedReaction(record.AppMetadata)
// A payment carries at most one chat metadata type, so at most one
// context applies.
if boosted := toBoostedReaction(record.AppMetadata); boosted != nil {
metadata.Context = &activitypb.DirectlySentCryptoNotificationMetadata_BoostedReaction{BoostedReaction: boosted}
} else if requested := toRequestedPayment(record.AppMetadata); requested != nil {
metadata.Context = &activitypb.DirectlySentCryptoNotificationMetadata_RequestedPayment{RequestedPayment: requested}
}
notification.AdditionalMetadata = &activitypb.Notification_DirectlySentCrypto{DirectlySentCrypto: metadata}

case history.DirectlyReceived, history.IndirectlyReceived:
Expand All @@ -138,7 +144,11 @@ func toNotification(
UserId: userID,
}
}
metadata.BoostedReaction = toBoostedReaction(record.AppMetadata)
if boosted := toBoostedReaction(record.AppMetadata); boosted != nil {
metadata.Context = &activitypb.ReceivedCryptoNotificationMetadata_BoostedReaction{BoostedReaction: boosted}
} else if requested := toRequestedPayment(record.AppMetadata); requested != nil {
metadata.Context = &activitypb.ReceivedCryptoNotificationMetadata_RequestedPayment{RequestedPayment: requested}
}
notification.AdditionalMetadata = &activitypb.Notification_ReceivedCrypto{ReceivedCrypto: metadata}

case history.IndirectlySent:
Expand Down Expand Up @@ -213,6 +223,23 @@ func toBoostedReaction(appMetadata []byte) *activitypb.BoostedReaction {
}
}

// toRequestedPayment names the payment request a payment paid a share of,
// read off the app metadata its intent carried, or nil for a payment that was
// not a requested payment. A requested payment was validated when it was
// paid, so its metadata names the request and the request's group; it is what
// the payment was for then, and is not checked against the request now, which
// may since have closed or expired.
func toRequestedPayment(appMetadata []byte) *activitypb.RequestedPayment {
requested := intent.GetRequestedPayment(appMetadata)
if requested == nil {
return nil
}
return &activitypb.RequestedPayment{
ChatId: intent.GetChatMetadata(appMetadata).GetChatId(),
PaymentRequestId: requested.GetPaymentRequestId(),
}
}

// toSwapMetadata describes the conversion behind a record, or nil for a record
// that converted nothing.
//
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/aws/smithy-go v1.27.7
github.com/buckket/go-blurhash v1.1.0
github.com/cespare/xxhash/v2 v2.3.0
github.com/code-payments/flipcash2-protobuf-api v1.31.1-0.20261027220701-28b039661c35
github.com/code-payments/flipcash2-protobuf-api v1.31.1-0.20261028161846-14f05bb0521e
github.com/code-payments/ocp-protobuf-api v1.17.1-0.20261016120110-6f70c0d8e832
github.com/code-payments/ocp-server v1.27.1-0.20261016133142-d1b9b228d45d
github.com/devsisters/go-applereceipt v0.0.0-20240805020915-fa22a0160fc2
Expand Down Expand Up @@ -105,7 +105,6 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/steebchen/prisma-client-go v0.47.0 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,8 @@ github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB0
github.com/cockroachdb/cockroach-go/v2 v2.2.0/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI=
github.com/code-payments/code-vm-indexer v1.2.0 h1:rSHpBMiT9BKgmKcXg/VIoi/h0t7jNxGx07Qz59m+6Q0=
github.com/code-payments/code-vm-indexer v1.2.0/go.mod h1:vn91YN2qNqb+gGJeZe2+l+TNxVmEEiRHXXnIn2Y40h8=
github.com/code-payments/flipcash2-protobuf-api v1.30.1-0.20261026192314-b655df1770f1 h1:rFKHMwujz7xkEhiMbdw9vmGzVxsZefz5YGHlljzbfns=
github.com/code-payments/flipcash2-protobuf-api v1.30.1-0.20261026192314-b655df1770f1/go.mod h1:s/1pOsb4FTRD+LcvRKGjfmm6ygRS/m1ep34EIW0fuDs=
github.com/code-payments/flipcash2-protobuf-api v1.31.1-0.20261027220701-28b039661c35 h1:Z5Ryi0Bhg+0jRV0VCWi9xBPOi8m232hjHkrU3G3vsAc=
github.com/code-payments/flipcash2-protobuf-api v1.31.1-0.20261027220701-28b039661c35/go.mod h1:s/1pOsb4FTRD+LcvRKGjfmm6ygRS/m1ep34EIW0fuDs=
github.com/code-payments/flipcash2-protobuf-api v1.31.1-0.20261028161846-14f05bb0521e h1:1fM6uhPIfj57ss2BGoCVf1cARx9y0spvaPw4TtnO+rw=
github.com/code-payments/flipcash2-protobuf-api v1.31.1-0.20261028161846-14f05bb0521e/go.mod h1:s/1pOsb4FTRD+LcvRKGjfmm6ygRS/m1ep34EIW0fuDs=
github.com/code-payments/ocp-protobuf-api v1.17.1-0.20261016120110-6f70c0d8e832 h1:HFY6mGBaZm8C3tJDK2PvaVQjfUS91NFDDWQSzla80hA=
github.com/code-payments/ocp-protobuf-api v1.17.1-0.20261016120110-6f70c0d8e832/go.mod h1:tw6BooY5a8l6CtSZnKOruyKII0W04n89pcM4BizrgG8=
github.com/code-payments/ocp-server v1.27.1-0.20261016133142-d1b9b228d45d h1:tliC1IE+4TYInxfv3zzd+iGLRq7QQkFaeebIZTdP/os=
Expand Down Expand Up @@ -322,8 +320,6 @@ github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0Zxu
github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY=
github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w=
github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg=
github.com/steebchen/prisma-client-go v0.47.0 h1:mKelgkcGPcIardjTP5diGq6hvnueQc/DYEyQ+6uZ0/E=
github.com/steebchen/prisma-client-go v0.47.0/go.mod h1:i1B0PEaE+BUcBUiwvd9drWpyMG/zNYMRrD5MancMf2I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
Expand Down
Loading