Skip to content

Commit 51ff086

Browse files
committed
SubmitIntent swap validation checks and state handling
1 parent 1b41b72 commit 51ff086

File tree

3 files changed

+162
-21
lines changed

3 files changed

+162
-21
lines changed

pkg/code/data/internal.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ type DatabaseData interface {
230230
// --------------------------------------------------------------------------------
231231
SaveSwap(ctx context.Context, record *swap.Record) error
232232
GetSwapById(ctx context.Context, id string) (*swap.Record, error)
233+
GetSwapByFundingId(ctx context.Context, fundingId string) (*swap.Record, error)
233234
GetAllSwapsByOwnerAndState(ctx context.Context, owner string, state swap.State) ([]*swap.Record, error)
234235
GetAllSwapsByState(ctx context.Context, state swap.State, opts ...query.Option) ([]*swap.Record, error)
235236

pkg/code/server/transaction/intent.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,13 @@ func (s *transactionServer) SubmitIntent(streamer transactionpb.Transaction_Subm
594594
return err
595595
}
596596

597+
// Save additional state related to the intent
598+
err = intentHandler.OnCommitToDB(ctx)
599+
if err != nil {
600+
log.WithError(err).Warn("failure executing intent db commit callback handler")
601+
return err
602+
}
603+
597604
// Save all actions
598605
err = s.data.PutAllActions(ctx, actionRecords...)
599606
if err != nil {
@@ -605,7 +612,7 @@ func (s *transactionServer) SubmitIntent(streamer transactionpb.Transaction_Subm
605612
for _, actionHandler := range actionHandlers {
606613
err = actionHandler.OnCommitToDB(ctx)
607614
if err != nil {
608-
log.WithError(err).Warn("failure executing action db save callback handler")
615+
log.WithError(err).Warn("failure executing action db commit callback handler")
609616
return err
610617
}
611618
}

pkg/code/server/transaction/intent_handler.go

Lines changed: 153 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/code-payments/code-server/pkg/code/data/account"
2222
"github.com/code-payments/code-server/pkg/code/data/action"
2323
"github.com/code-payments/code-server/pkg/code/data/intent"
24+
"github.com/code-payments/code-server/pkg/code/data/swap"
2425
"github.com/code-payments/code-server/pkg/code/data/timelock"
2526
currency_lib "github.com/code-payments/code-server/pkg/currency"
2627
"github.com/code-payments/code-server/pkg/solana"
@@ -62,6 +63,11 @@ type CreateIntentHandler interface {
6263

6364
// AllowCreation determines whether the new intent creation should be allowed.
6465
AllowCreation(ctx context.Context, intentRecord *intent.Record, metadata *transactionpb.Metadata, actions []*transactionpb.Action) error
66+
67+
// OnCommitToDB is a callback when the intent is being committed to the DB
68+
// within the scope of a DB transaction. Additional supporting DB records
69+
// relevant to the intent should be saved here.
70+
OnCommitToDB(ctx context.Context) error
6571
}
6672

6773
type OpenAccountsIntentHandler struct {
@@ -221,7 +227,17 @@ func (h *OpenAccountsIntentHandler) AllowCreation(ctx context.Context, intentRec
221227
// Part 4: Validate fee payments
222228
//
223229

224-
return validateFeePayments(ctx, h.data, h.conf, intentRecord, simResult)
230+
err = validateFeePayments(ctx, h.data, h.conf, intentRecord, simResult)
231+
if err != nil {
232+
return err
233+
}
234+
235+
//
236+
// Part 5: Validate reserved intent IDs
237+
//
238+
239+
_, err = validateSwapFunding(ctx, h.data, intentRecord)
240+
return err
225241
}
226242

227243
func (h *OpenAccountsIntentHandler) validateActions(
@@ -331,13 +347,18 @@ func (h *OpenAccountsIntentHandler) validateActions(
331347
return nil
332348
}
333349

350+
func (h *OpenAccountsIntentHandler) OnCommitToDB(ctx context.Context) error {
351+
return nil
352+
}
353+
334354
type SendPublicPaymentIntentHandler struct {
335355
conf *conf
336356
data code_data.Provider
337357
antispamGuard *antispam.Guard
338358
amlGuard *aml.Guard
339359

340360
cachedDestinationAccountInfoRecord *account.Record
361+
cachedSwapRecord *swap.Record
341362
}
342363

343364
func NewSendPublicPaymentIntentHandler(
@@ -574,7 +595,16 @@ func (h *SendPublicPaymentIntentHandler) AllowCreation(ctx context.Context, inte
574595
}
575596

576597
//
577-
// Part 7: Validate the individual actions
598+
// Part 7: Validate reserved intent IDs
599+
//
600+
601+
h.cachedSwapRecord, err = validateSwapFunding(ctx, h.data, intentRecord)
602+
if err != nil {
603+
return err
604+
}
605+
606+
//
607+
// Part 8: Validate the individual actions
578608
//
579609

580610
return h.validateActions(
@@ -719,27 +749,12 @@ func (h *SendPublicPaymentIntentHandler) validateActions(
719749
return NewIntentValidationErrorf("destination is not the ata for %s", destinationOwner.PublicKey().ToBase58())
720750
}
721751

722-
vmSwapPdaTimelockRecord, err := h.data.GetTimelockBySwapPda(ctx, destinationOwner.PublicKey().ToBase58())
752+
_, err = h.data.GetTimelockBySwapPda(ctx, destinationOwner.PublicKey().ToBase58())
723753
if err == nil {
724754
isVmSwapPda = true
725755
} else if err != timelock.ErrTimelockNotFound {
726756
return err
727757
}
728-
729-
if isVmSwapPda {
730-
accountInfoRecord, err := h.data.GetAccountInfoByTokenAddress(ctx, vmSwapPdaTimelockRecord.VaultAddress)
731-
if err != nil {
732-
return err
733-
}
734-
735-
if accountInfoRecord.OwnerAccount != initiatorOwnerAccount.PublicKey().ToBase58() {
736-
return NewIntentDeniedError("can only send to your own vm swap pda")
737-
}
738-
739-
if accountInfoRecord.MintAccount != intentMint.PublicKey().ToBase58() {
740-
return NewIntentValidationError("vm swap pda is for a different mint")
741-
}
742-
}
743758
}
744759

745760
// Technically we should always enforce a fee payment, but these checks are only
@@ -876,6 +891,15 @@ func (h *SendPublicPaymentIntentHandler) validateActions(
876891
return nil
877892
}
878893

894+
func (h *SendPublicPaymentIntentHandler) OnCommitToDB(ctx context.Context) error {
895+
if h.cachedSwapRecord != nil {
896+
h.cachedSwapRecord.State = swap.StateFunding
897+
return h.data.SaveSwap(ctx, h.cachedSwapRecord)
898+
}
899+
900+
return nil
901+
}
902+
879903
type ReceivePaymentsPubliclyIntentHandler struct {
880904
conf *conf
881905
data code_data.Provider
@@ -1087,7 +1111,16 @@ func (h *ReceivePaymentsPubliclyIntentHandler) AllowCreation(ctx context.Context
10871111
}
10881112

10891113
//
1090-
// Part 7: Validate the individual actions
1114+
// Part 7: Validate reserved intent IDs
1115+
//
1116+
1117+
_, err = validateSwapFunding(ctx, h.data, intentRecord)
1118+
if err != nil {
1119+
return err
1120+
}
1121+
1122+
//
1123+
// Part 8: Validate the individual actions
10911124
//
10921125

10931126
return h.validateActions(
@@ -1194,6 +1227,10 @@ func (h *ReceivePaymentsPubliclyIntentHandler) validateActions(
11941227
return validateMoneyMovementActionUserAccounts(ctx, h.data, intent.ReceivePaymentsPublicly, initiatorAccountsByVault, actions)
11951228
}
11961229

1230+
func (h *ReceivePaymentsPubliclyIntentHandler) OnCommitToDB(ctx context.Context) error {
1231+
return nil
1232+
}
1233+
11971234
type PublicDistributionIntentHandler struct {
11981235
conf *conf
11991236
data code_data.Provider
@@ -1392,7 +1429,16 @@ func (h *PublicDistributionIntentHandler) AllowCreation(ctx context.Context, int
13921429
}
13931430

13941431
//
1395-
// Part 5: Validate actions
1432+
// Part 5: Validate reserved intent IDs
1433+
//
1434+
1435+
_, err = validateSwapFunding(ctx, h.data, intentRecord)
1436+
if err != nil {
1437+
return err
1438+
}
1439+
1440+
//
1441+
// Part 6: Validate actions
13961442
//
13971443

13981444
return h.validateActions(typedMetadata, actions, simResult)
@@ -1525,6 +1571,10 @@ func (h *PublicDistributionIntentHandler) validateActions(
15251571
return nil
15261572
}
15271573

1574+
func (h *PublicDistributionIntentHandler) OnCommitToDB(ctx context.Context) error {
1575+
return nil
1576+
}
1577+
15281578
func validateAllUserAccountsManagedByCode(ctx context.Context, initiatorAccounts []*common.AccountRecords) error {
15291579
// Try to unlock *ANY* latest account, and you're done
15301580
for _, accountRecords := range initiatorAccounts {
@@ -1942,6 +1992,89 @@ func validateDistributedPool(ctx context.Context, data code_data.Provider, poolV
19421992
return nil
19431993
}
19441994

1995+
func validateSwapFunding(ctx context.Context, data code_data.Provider, intentRecord *intent.Record) (*swap.Record, error) {
1996+
swapRecord, err := data.GetSwapByFundingId(ctx, intentRecord.IntentId)
1997+
if err != nil && err != swap.ErrNotFound {
1998+
return nil, err
1999+
}
2000+
isIntentReservedForSwap := swapRecord != nil
2001+
2002+
if isIntentReservedForSwap && swapRecord.State != swap.StateCreated {
2003+
return nil, errors.Errorf("unexpected swap state: %s", swapRecord.State)
2004+
}
2005+
2006+
// Intent-specific validation for swaps
2007+
if isIntentReservedForSwap {
2008+
if intentRecord.IntentType != intent.SendPublicPayment {
2009+
return nil, NewIntentDeniedError("intent id reserved to fund swap")
2010+
}
2011+
2012+
if intentRecord.InitiatorOwnerAccount != swapRecord.Owner {
2013+
return nil, NewIntentDeniedError("not the owner of the swap")
2014+
}
2015+
2016+
if !intentRecord.SendPublicPaymentMetadata.IsWithdrawal {
2017+
return nil, NewIntentValidationError("swap funding must be an external withdrawal")
2018+
}
2019+
2020+
if len(intentRecord.SendPublicPaymentMetadata.DestinationOwnerAccount) == 0 {
2021+
return nil, NewIntentValidationError("destination owner must be a vm swap pda")
2022+
}
2023+
2024+
if intentRecord.MintAccount != swapRecord.FromMint {
2025+
return nil, NewIntentValidationErrorf("must fund swap with %s mint", swapRecord.FromMint)
2026+
}
2027+
2028+
if intentRecord.SendPublicPaymentMetadata.Quantity != swapRecord.Amount {
2029+
return nil, NewIntentValidationErrorf("must fund swap with %d quarks", swapRecord.Amount)
2030+
}
2031+
}
2032+
if !isIntentReservedForSwap && intentRecord.IntentType != intent.SendPublicPayment {
2033+
return nil, nil
2034+
}
2035+
2036+
// Account-specific validation for swaps
2037+
if len(intentRecord.SendPublicPaymentMetadata.DestinationOwnerAccount) > 0 {
2038+
destinationOwner, err := common.NewAccountFromPublicKeyString(intentRecord.SendPublicPaymentMetadata.DestinationOwnerAccount)
2039+
if err != nil {
2040+
return nil, err
2041+
}
2042+
2043+
var isVmSwapPda bool
2044+
vmSwapPdaTimelockRecord, err := data.GetTimelockBySwapPda(ctx, destinationOwner.PublicKey().ToBase58())
2045+
if err == nil {
2046+
isVmSwapPda = true
2047+
} else if err != timelock.ErrTimelockNotFound {
2048+
return nil, err
2049+
}
2050+
2051+
if isVmSwapPda {
2052+
if !isIntentReservedForSwap {
2053+
return nil, NewIntentDeniedError("intent id is not reserved for a swap")
2054+
}
2055+
2056+
accountInfoRecord, err := data.GetAccountInfoByTokenAddress(ctx, vmSwapPdaTimelockRecord.VaultAddress)
2057+
if err != nil {
2058+
return nil, err
2059+
}
2060+
2061+
if accountInfoRecord.OwnerAccount != intentRecord.InitiatorOwnerAccount {
2062+
return nil, NewIntentDeniedError("can only send to your own vm swap pda")
2063+
}
2064+
2065+
if accountInfoRecord.MintAccount != intentRecord.MintAccount {
2066+
return nil, NewIntentValidationError("vm swap pda is for a different mint")
2067+
}
2068+
}
2069+
2070+
if !isVmSwapPda && isIntentReservedForSwap {
2071+
return nil, NewIntentValidationError("destination owner must be a vm swap pda")
2072+
}
2073+
}
2074+
2075+
return swapRecord, nil
2076+
}
2077+
19452078
func validateTimelockUnlockStateDoesntExist(ctx context.Context, data code_data.Provider, openAction *transactionpb.OpenAccountAction) error {
19462079
mintAccount, err := common.GetBackwardsCompatMint(openAction.Mint)
19472080
if err != nil {

0 commit comments

Comments
 (0)