Skip to content

Commit dc53053

Browse files
committed
OpenAccounts intent now supports multiple mints and VMs
1 parent 1447a3d commit dc53053

File tree

10 files changed

+106
-40
lines changed

10 files changed

+106
-40
lines changed

pkg/code/async/geyser/external_deposit.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,25 @@ func initiateExternalDepositIntoVm(ctx context.Context, data code_data.Provider,
9494
return err
9595
}
9696

97-
vmDepositAccounts, err := userAuthority.GetVmDepositAccounts(vmConfig)
97+
timelockAccounts, err := userAuthority.GetTimelockAccounts(vmConfig)
9898
if err != nil {
99-
return errors.Wrap(err, "error getting vm deposit ata")
99+
return err
100+
}
101+
102+
err = ensureVirtualTimelockAccountIsInitialzed(ctx, data, timelockAccounts.Vault)
103+
if err != nil {
104+
return err
100105
}
101106

102-
memoryAccount, memoryIndex, err := getVirtualTimelockAccountLocationInMemory(ctx, vmIndexerClient, vmConfig.Vm, userAuthority)
107+
var memoryAccount *common.Account
108+
var memoryIndex uint16
109+
_, err = retry.Retry(
110+
func() error {
111+
memoryAccount, memoryIndex, err = getVirtualTimelockAccountLocationInMemory(ctx, vmIndexerClient, vmConfig.Vm, userAuthority)
112+
return err
113+
},
114+
waitForFinalizationRetryStrategies...,
115+
)
103116
if err != nil {
104117
return errors.Wrap(err, "error getting vta location in memory")
105118
}
@@ -114,15 +127,15 @@ func initiateExternalDepositIntoVm(ctx context.Context, data code_data.Provider,
114127
VmAuthority: vmConfig.Authority.PublicKey().ToBytes(),
115128
Vm: vmConfig.Vm.PublicKey().ToBytes(),
116129
VmMemory: memoryAccount.PublicKey().ToBytes(),
117-
Depositor: vmDepositAccounts.VaultOwner.PublicKey().ToBytes(),
118-
DepositPda: vmDepositAccounts.Pda.PublicKey().ToBytes(),
119-
DepositAta: vmDepositAccounts.Ata.PublicKey().ToBytes(),
130+
Depositor: timelockAccounts.VaultOwner.PublicKey().ToBytes(),
131+
DepositPda: timelockAccounts.VmDepositAccounts.Pda.PublicKey().ToBytes(),
132+
DepositAta: timelockAccounts.VmDepositAccounts.Ata.PublicKey().ToBytes(),
120133
VmOmnibus: vmConfig.Omnibus.PublicKey().ToBytes(),
121134
},
122135
&cvm.DepositInstructionArgs{
123136
AccountIndex: memoryIndex,
124137
Amount: balance,
125-
Bump: vmDepositAccounts.PdaBump,
138+
Bump: timelockAccounts.VmDepositAccounts.PdaBump,
126139
},
127140
),
128141
)

pkg/code/async/geyser/retry.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,12 @@ package async_geyser
22

33
import (
44
"context"
5-
"errors"
65
"time"
76

87
"github.com/code-payments/code-server/pkg/retry"
98
"github.com/code-payments/code-server/pkg/retry/backoff"
109
)
1110

12-
var (
13-
errSignatureNotConfirmed = errors.New("signature is not confirmed")
14-
errSignatureNotFinalized = errors.New("signature is not finalized")
15-
)
16-
1711
var waitForFinalizationRetryStrategies = []retry.Strategy{
1812
retry.NonRetriableErrors(context.Canceled),
1913
retry.Limit(30),

pkg/code/async/geyser/util.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/code-payments/code-server/pkg/cache"
99
"github.com/code-payments/code-server/pkg/code/common"
1010
code_data "github.com/code-payments/code-server/pkg/code/data"
11+
"github.com/code-payments/code-server/pkg/code/data/fulfillment"
1112
"github.com/code-payments/code-server/pkg/code/data/timelock"
1213
)
1314

@@ -43,3 +44,42 @@ func testForKnownUserAuthorityFromDepositPda(ctx context.Context, data code_data
4344
return false, nil, errors.Wrap(err, "error getting timelock record")
4445
}
4546
}
47+
48+
func ensureVirtualTimelockAccountIsInitialzed(ctx context.Context, data code_data.Provider, vault *common.Account) error {
49+
timelockRecord, err := data.GetTimelockByVault(ctx, vault.PublicKey().ToBase58())
50+
if err != nil {
51+
return err
52+
}
53+
54+
if !timelockRecord.ExistsOnBlockchain() {
55+
initializeFulfillmentRecord, err := data.GetFirstSchedulableFulfillmentByAddressAsSource(ctx, vault.PublicKey().ToBase58())
56+
if err != nil {
57+
return err
58+
}
59+
60+
if initializeFulfillmentRecord.FulfillmentType != fulfillment.InitializeLockedTimelockAccount {
61+
return errors.New("expected an initialize locked timelock account fulfillment")
62+
}
63+
64+
return markFulfillmentAsActivelyScheduled(ctx, data, initializeFulfillmentRecord)
65+
}
66+
67+
return nil
68+
}
69+
70+
func markFulfillmentAsActivelyScheduled(ctx context.Context, data code_data.Provider, fulfillmentRecord *fulfillment.Record) error {
71+
if fulfillmentRecord.Id == 0 {
72+
return nil
73+
}
74+
75+
if !fulfillmentRecord.DisableActiveScheduling {
76+
return nil
77+
}
78+
79+
if fulfillmentRecord.State != fulfillment.StateUnknown {
80+
return nil
81+
}
82+
83+
fulfillmentRecord.DisableActiveScheduling = false
84+
return data.UpdateFulfillment(ctx, fulfillmentRecord)
85+
}

pkg/code/common/account_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestInvalidAccount(t *testing.T) {
103103
}
104104

105105
func TestConvertToTimelockVault(t *testing.T) {
106-
vmConfig := newRandomVmConfig(t)
106+
vmConfig := newRandomVmConfig(t, true)
107107

108108
ownerAccount := newRandomTestAccount(t)
109109

@@ -126,7 +126,7 @@ func TestConvertToTimelockVault(t *testing.T) {
126126
}
127127

128128
func TestGetTimelockAccounts(t *testing.T) {
129-
vmConfig := newRandomVmConfig(t)
129+
vmConfig := newRandomVmConfig(t, true)
130130

131131
ownerAccount := newRandomTestAccount(t)
132132

@@ -164,7 +164,7 @@ func TestGetTimelockAccounts(t *testing.T) {
164164
}
165165

166166
func TestGetVmDepositAccounts(t *testing.T) {
167-
vmConfig := newRandomVmConfig(t)
167+
vmConfig := newRandomVmConfig(t, true)
168168

169169
ownerAccount := newRandomTestAccount(t)
170170

@@ -206,7 +206,7 @@ func TestIsAccountManagedByCode_TimelockState(t *testing.T) {
206206
ctx := context.Background()
207207
data := code_data.NewTestDataProvider()
208208

209-
vmConfig := newRandomVmConfig(t)
209+
vmConfig := newRandomVmConfig(t, true)
210210

211211
ownerAccount := newRandomTestAccount(t)
212212

@@ -257,7 +257,7 @@ func TestIsAccountManagedByCode_OtherAccounts(t *testing.T) {
257257
ctx := context.Background()
258258
data := code_data.NewTestDataProvider()
259259

260-
vmConfig := newRandomVmConfig(t)
260+
vmConfig := newRandomVmConfig(t, true)
261261

262262
ownerAccount := newRandomTestAccount(t)
263263

pkg/code/common/owner.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@ func GetOwnerMetadata(ctx context.Context, data code_data.Provider, owner *Accou
5656
}
5757

5858
if mtdt.Type == OwnerTypeUnknown {
59-
// Is the owner account a user 12 words?
60-
_, err := data.GetLatestAccountInfoByOwnerAddressAndType(ctx, owner.publicKey.ToBase58(), commonpb.AccountType_PRIMARY)
59+
// Is the owner account a user 12 words? We know by detecting the presence of a core mint primary account.
60+
accountInfoRecordsByMint, err := data.GetLatestAccountInfoByOwnerAddressAndType(ctx, owner.publicKey.ToBase58(), commonpb.AccountType_PRIMARY)
6161
if err == nil {
62-
mtdt.Type = OwnerTypeUser12Words
62+
_, ok := accountInfoRecordsByMint[CoreMintAccount.PublicKey().ToBase58()]
63+
if ok {
64+
mtdt.Type = OwnerTypeUser12Words
65+
}
6366
} else if err != account.ErrAccountInfoNotFound {
6467
return nil, err
6568
}

pkg/code/common/owner_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func TestGetOwnerMetadata_User12Words(t *testing.T) {
1818
ctx := context.Background()
1919
data := code_data.NewTestDataProvider()
2020

21-
coreVmConfig := newRandomVmConfig(t)
21+
coreVmConfig := newRandomVmConfig(t, true)
2222

2323
swapMintAccount := newRandomTestAccount(t)
2424

@@ -87,7 +87,7 @@ func TestGetOwnerMetadata_RemoteSendGiftCard(t *testing.T) {
8787
ctx := context.Background()
8888
data := code_data.NewTestDataProvider()
8989

90-
coreVmConfig := newRandomVmConfig(t)
90+
coreVmConfig := newRandomVmConfig(t, true)
9191

9292
owner := newRandomTestAccount(t)
9393

@@ -120,8 +120,8 @@ func TestGetLatestTokenAccountRecordsForOwner(t *testing.T) {
120120
ctx := context.Background()
121121
data := code_data.NewTestDataProvider()
122122

123-
coreVmConfig := newRandomVmConfig(t)
124-
jeffyVmConfig := newRandomVmConfig(t)
123+
coreVmConfig := newRandomVmConfig(t, true)
124+
jeffyVmConfig := newRandomVmConfig(t, false)
125125

126126
swapMintAccount := newRandomTestAccount(t)
127127

pkg/code/common/testutil.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@ func newRandomTestAccount(t *testing.T) *Account {
1414
}
1515

1616
// Required because we'd have a dependency loop with the testutil package
17-
func newRandomVmConfig(t *testing.T) *VmConfig {
17+
func newRandomVmConfig(t *testing.T, isCore bool) *VmConfig {
18+
if isCore {
19+
return &VmConfig{
20+
Authority: newRandomTestAccount(t),
21+
Vm: newRandomTestAccount(t),
22+
Omnibus: newRandomTestAccount(t),
23+
Mint: CoreMintAccount,
24+
}
25+
}
1826
return &VmConfig{
1927
Authority: newRandomTestAccount(t),
2028
Vm: newRandomTestAccount(t),

pkg/code/server/currency/currency.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func (s *currencyServer) GetMints(ctx context.Context, req *currencypb.GetMintsR
9393
VmMetadata: &currencypb.VmMetadata{
9494
Vm: common.CodeVmAccount.ToProto(),
9595
Authority: common.GetSubsidizer().ToProto(),
96-
LockDurationInDays: 21,
96+
LockDurationInDays: uint32(timelock_token.DefaultNumDaysLocked),
9797
},
9898
}
9999
default:

pkg/code/server/transaction/action_handler.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,10 @@ func (h *OpenAccountActionHandler) GetFulfillmentMetadata(
177177
source: h.timelockAccounts.Vault,
178178
destination: nil,
179179
fulfillmentOrderingIndex: 0,
180-
disableActiveScheduling: h.accountType != commonpb.AccountType_PRIMARY, // Non-primary accounts are created on demand after first usage
180+
disableActiveScheduling: h.accountType != commonpb.AccountType_PRIMARY || !common.IsCoreMint(h.timelockAccounts.Mint), // Non-primary and non-core mint accounts are created on demand after first usage
181181
}, nil
182182
default:
183-
return nil, errors.New("invalid virtual ixn index")
183+
return nil, errors.New("invalid transaction index")
184184
}
185185
}
186186

@@ -334,7 +334,7 @@ func (h *NoPrivacyTransferActionHandler) GetFulfillmentMetadata(
334334
fulfillmentOrderingIndex: 0,
335335
}, nil
336336
default:
337-
return nil, errors.New("invalid transaction index")
337+
return nil, errors.New("invalid virtual ixn index")
338338
}
339339
}
340340

@@ -459,7 +459,7 @@ func (h *NoPrivacyWithdrawActionHandler) GetFulfillmentMetadata(
459459
disableActiveScheduling: h.isAutoReturn,
460460
}, nil
461461
default:
462-
return nil, errors.New("invalid transaction index")
462+
return nil, errors.New("invalid virtual ixn index")
463463
}
464464
}
465465

pkg/code/server/transaction/intent_handler.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@ func (h *OpenAccountsIntentHandler) PopulateMetadata(ctx context.Context, intent
8787
if err != nil {
8888
return err
8989
}
90-
if !common.IsCoreMint(mint) {
91-
return NewIntentValidationError("only the core mint is supported")
90+
91+
if typedProtoMetadata.AccountSet != transactionpb.OpenAccountsMetadata_USER && !common.IsCoreMint(mint) {
92+
return NewIntentDeniedError("non-core mint account opening is only supported for the user account set")
9293
}
9394

9495
intentRecord.IntentType = intent.OpenAccounts
@@ -104,10 +105,14 @@ func (h *OpenAccountsIntentHandler) CreatesNewUser(ctx context.Context, metadata
104105
return false, errors.New("unexpected metadata proto message")
105106
}
106107

107-
return typedMetadata.AccountSet == transactionpb.OpenAccountsMetadata_USER, nil
108+
mint, err := common.GetBackwardsCompatMint(typedMetadata.Mint)
109+
if err != nil {
110+
return false, err
111+
}
112+
113+
return typedMetadata.AccountSet == transactionpb.OpenAccountsMetadata_USER && common.IsCoreMint(mint), nil
108114
}
109115

110-
// todo: Not all multi-mint validation checks are implemented
111116
func (h *OpenAccountsIntentHandler) IsNoop(ctx context.Context, intentRecord *intent.Record, metadata *transactionpb.Metadata, actions []*transactionpb.Action) (bool, error) {
112117
typedMetadata := metadata.GetOpenAccounts()
113118
if typedMetadata == nil {
@@ -119,9 +124,13 @@ func (h *OpenAccountsIntentHandler) IsNoop(ctx context.Context, intentRecord *in
119124
return false, NewActionValidationError(actions[0], "expected an open account action")
120125
}
121126

127+
mint, err := common.GetBackwardsCompatMint(typedMetadata.Mint)
128+
if err != nil {
129+
return false, err
130+
}
131+
122132
var authorityToCheck *common.Account
123133
var expectedAccountType commonpb.AccountType
124-
var err error
125134
switch typedMetadata.AccountSet {
126135
case transactionpb.OpenAccountsMetadata_USER:
127136
authorityToCheck, err = common.NewAccountFromPublicKeyString(intentRecord.InitiatorOwnerAccount)
@@ -145,18 +154,17 @@ func (h *OpenAccountsIntentHandler) IsNoop(ctx context.Context, intentRecord *in
145154
} else if err != nil {
146155
return false, err
147156
}
148-
coreMintAccountInfoRecord, ok := accountInfoRecordByMint[common.CoreMintAccount.PublicKey().ToBase58()]
157+
accountInfoRecord, ok := accountInfoRecordByMint[mint.PublicKey().ToBase58()]
149158
if !ok {
150159
return false, nil
151160
}
152-
return coreMintAccountInfoRecord.AccountType == expectedAccountType, nil
161+
return accountInfoRecord.AccountType == expectedAccountType, nil
153162
}
154163

155164
func (h *OpenAccountsIntentHandler) GetBalanceLocks(ctx context.Context, intentRecord *intent.Record, metadata *transactionpb.Metadata) ([]*intentBalanceLock, error) {
156165
return nil, nil
157166
}
158167

159-
// todo: Not all multi-mint validation checks are implemented
160168
func (h *OpenAccountsIntentHandler) AllowCreation(ctx context.Context, intentRecord *intent.Record, metadata *transactionpb.Metadata, actions []*transactionpb.Action) error {
161169
typedMetadata := metadata.GetOpenAccounts()
162170
if typedMetadata == nil {
@@ -1188,7 +1196,7 @@ func (h *PublicDistributionIntentHandler) PopulateMetadata(ctx context.Context,
11881196
return err
11891197
}
11901198
if !common.IsCoreMint(mint) {
1191-
return NewIntentValidationError("only the core mint is supported")
1199+
return NewIntentDeniedError("only the core mint is supported")
11921200
}
11931201

11941202
source, err := common.NewAccountFromPublicKeyBytes(typedProtoMetadata.Source.Value)

0 commit comments

Comments
 (0)