github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/stellar/stellarsvc/frontend_test.go (about) 1 package stellarsvc 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 "sort" 8 "testing" 9 "time" 10 11 "github.com/davecgh/go-spew/spew" 12 "github.com/keybase/client/go/engine" 13 "github.com/keybase/client/go/kbtest" 14 "github.com/keybase/client/go/libkb" 15 "github.com/keybase/client/go/protocol/chat1" 16 "github.com/keybase/client/go/protocol/keybase1" 17 "github.com/keybase/client/go/protocol/stellar1" 18 "github.com/keybase/client/go/stellar" 19 "github.com/keybase/client/go/stellar/remote" 20 "github.com/keybase/stellarnet" 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func acceptDisclaimer(tc *TestContext) { 26 // NOTE: this also creates a wallet 27 err := tc.Srv.AcceptDisclaimerLocal(context.Background(), 0) 28 require.NoError(tc.T, err) 29 } 30 31 func TestGetWalletAccountsLocal(t *testing.T) { 32 tcs, cleanup := setupNTests(t, 1) 33 defer cleanup() 34 35 acceptDisclaimer(tcs[0]) 36 37 accountID := tcs[0].Backend.AddAccount(tcs[0].Fu.GetUID()) 38 39 err := tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 40 SecretKey: tcs[0].Backend.SecretKey(accountID), 41 MakePrimary: true, 42 Name: "qq", 43 }) 44 require.NoError(t, err) 45 46 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 47 48 accts, err := tcs[0].Srv.GetWalletAccountsLocal(context.Background(), 0) 49 require.NoError(t, err) 50 51 require.Len(t, accts, 2) 52 require.Equal(t, accountID, accts[0].AccountID, accountID) 53 require.True(t, accts[0].IsDefault) 54 require.Equal(t, "qq", accts[0].Name) 55 require.Equal(t, stellar1.AccountMode_USER, accts[0].AccountMode) 56 require.Equal(t, false, accts[0].AccountModeEditable) 57 require.Equal(t, false, accts[0].DeviceReadOnly) 58 require.True(t, accts[0].CanAddTrustline) 59 require.Equal(t, "10,000.00 XLM", accts[0].BalanceDescription) 60 currencyLocal := accts[0].CurrencyLocal 61 require.Equal(t, stellar1.OutsideCurrencyCode("USD"), currencyLocal.Code) 62 require.Equal(t, "US Dollar", currencyLocal.Name) 63 require.Equal(t, "USD ($)", currencyLocal.Description) 64 require.Equal(t, "$", currencyLocal.Symbol) 65 require.NotEmpty(t, accts[0].Seqno) 66 67 require.False(t, accts[1].IsDefault) 68 require.Equal(t, firstAccountName(t, tcs[0]), accts[1].Name) 69 require.Equal(t, stellar1.AccountMode_USER, accts[1].AccountMode) 70 require.Equal(t, false, accts[1].AccountModeEditable) 71 require.Equal(t, false, accts[1].DeviceReadOnly) 72 require.Equal(t, "0 XLM", accts[1].BalanceDescription) 73 require.False(t, accts[1].CanAddTrustline) 74 currencyLocal = accts[1].CurrencyLocal 75 require.Equal(t, stellar1.OutsideCurrencyCode("USD"), currencyLocal.Code) 76 require.NotEmpty(t, accts[1].Seqno) 77 78 // test the singular version as well 79 argDetails := stellar1.GetWalletAccountLocalArg{AccountID: accountID} 80 details, err := tcs[0].Srv.GetWalletAccountLocal(context.Background(), argDetails) 81 require.NoError(t, err) 82 require.Equal(t, "qq", details.Name) 83 require.Equal(t, stellar1.AccountMode_USER, details.AccountMode) 84 require.Equal(t, false, accts[1].AccountModeEditable) 85 require.Equal(t, false, accts[1].DeviceReadOnly) 86 require.True(t, details.IsDefault) 87 require.Equal(t, "10,000.00 XLM", details.BalanceDescription) 88 require.NotEmpty(t, details.Seqno) 89 currencyLocal = details.CurrencyLocal 90 require.Equal(t, stellar1.OutsideCurrencyCode("USD"), currencyLocal.Code) 91 require.True(t, details.IsFunded) 92 require.True(t, details.CanSubmitTx) 93 require.True(t, details.CanAddTrustline) 94 95 argDetails.AccountID = accts[1].AccountID 96 details, err = tcs[0].Srv.GetWalletAccountLocal(context.Background(), argDetails) 97 require.NoError(t, err) 98 require.Equal(t, firstAccountName(t, tcs[0]), details.Name) 99 require.False(t, details.IsDefault) 100 require.Equal(t, "0 XLM", details.BalanceDescription) 101 require.NotEmpty(t, details.Seqno) 102 currencyLocal = details.CurrencyLocal 103 require.Equal(t, stellar1.OutsideCurrencyCode("USD"), currencyLocal.Code) 104 require.False(t, details.IsFunded) 105 require.False(t, details.CanSubmitTx) 106 require.False(t, details.CanAddTrustline) 107 108 // Add another account that we will add 1 XLM, enough to be funded but not 109 // enough to make any txs out of it. 110 anotherAccountID := tcs[0].Backend.AddAccountEmpty(t, tcs[0].Fu.GetUID()) 111 err = tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 112 SecretKey: tcs[0].Backend.SecretKey(anotherAccountID), 113 MakePrimary: false, 114 Name: "funded but 0 avail.", 115 }) 116 require.NoError(t, err) 117 118 tcs[0].Backend.Gift(anotherAccountID, "1") 119 120 argDetails.AccountID = anotherAccountID 121 details, err = tcs[0].Srv.GetWalletAccountLocal(context.Background(), argDetails) 122 require.NoError(t, err) 123 require.Equal(t, "1 XLM", details.BalanceDescription) 124 require.True(t, details.IsFunded) 125 require.False(t, details.CanSubmitTx) 126 require.Equal(t, stellar1.OutsideCurrencyCode("USD"), details.CurrencyLocal.Code) 127 require.False(t, details.CanAddTrustline) 128 129 // Add another account that we will add 10 XLM, enough to be funded and can create trustlines 130 yetAnotherAccountID := tcs[0].Backend.AddAccountEmpty(t, tcs[0].Fu.GetUID()) 131 err = tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 132 SecretKey: tcs[0].Backend.SecretKey(yetAnotherAccountID), 133 MakePrimary: false, 134 Name: "another funded but 0 avail.", 135 }) 136 require.NoError(t, err) 137 138 tcs[0].Backend.Gift(yetAnotherAccountID, "10") 139 argDetails.AccountID = yetAnotherAccountID 140 details, err = tcs[0].Srv.GetWalletAccountLocal(context.Background(), argDetails) 141 require.NoError(t, err) 142 require.Equal(t, "10 XLM", details.BalanceDescription) 143 require.True(t, details.IsFunded) 144 require.True(t, details.CanSubmitTx) 145 require.Equal(t, stellar1.OutsideCurrencyCode("USD"), details.CurrencyLocal.Code) 146 require.True(t, details.CanAddTrustline) 147 } 148 149 func TestGetAccountAssetsLocalWithBalance(t *testing.T) { 150 tcs, cleanup := setupNTests(t, 1) 151 defer cleanup() 152 153 acceptDisclaimer(tcs[0]) 154 155 accountID := tcs[0].Backend.AddAccount(tcs[0].Fu.GetUID()) 156 157 err := tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 158 SecretKey: tcs[0].Backend.SecretKey(accountID), 159 MakePrimary: true, 160 Name: "qq", 161 }) 162 require.NoError(t, err) 163 164 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 165 166 assets, err := tcs[0].Srv.GetAccountAssetsLocal(context.Background(), stellar1.GetAccountAssetsLocalArg{AccountID: accountID}) 167 require.NoError(t, err) 168 169 require.Len(t, assets, 1) 170 require.Equal(t, "Lumens", assets[0].Name) 171 require.Equal(t, "XLM", assets[0].AssetCode) 172 require.Equal(t, "Stellar network", assets[0].IssuerName) 173 require.Equal(t, "", assets[0].IssuerAccountID) 174 require.Equal(t, "10,000.00", assets[0].BalanceTotal) 175 require.Equal(t, "9,998.9999900", assets[0].BalanceAvailableToSend) 176 require.Equal(t, "USD", assets[0].WorthCurrency) 177 require.Equal(t, "$3,183.28 USD", assets[0].Worth) 178 require.Equal(t, "$3,182.96 USD", assets[0].AvailableToSendWorth) 179 } 180 181 func TestGetAccountAssetsLocalWithCHFBalance(t *testing.T) { 182 tcs, cleanup := setupNTests(t, 1) 183 defer cleanup() 184 185 acceptDisclaimer(tcs[0]) 186 187 accountID := tcs[0].Backend.AddAccount(tcs[0].Fu.GetUID()) 188 189 err := tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 190 SecretKey: tcs[0].Backend.SecretKey(accountID), 191 MakePrimary: true, 192 Name: "qq", 193 }) 194 require.NoError(t, err) 195 196 curr, err := tcs[0].Srv.ChangeDisplayCurrencyLocal(context.Background(), stellar1.ChangeDisplayCurrencyLocalArg{ 197 AccountID: accountID, 198 Currency: stellar1.OutsideCurrencyCode("CHF"), 199 }) 200 require.NoError(t, err) 201 require.Equal(t, stellar1.OutsideCurrencyCode("CHF"), curr.Code) 202 203 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 204 205 assets, err := tcs[0].Srv.GetAccountAssetsLocal(context.Background(), stellar1.GetAccountAssetsLocalArg{AccountID: accountID}) 206 require.NoError(t, err) 207 208 require.Len(t, assets, 1) 209 require.Equal(t, "Lumens", assets[0].Name) 210 require.Equal(t, "XLM", assets[0].AssetCode) 211 require.Equal(t, "Stellar network", assets[0].IssuerName) 212 require.Equal(t, "", assets[0].IssuerAccountID) 213 require.Equal(t, "10,000.00", assets[0].BalanceTotal) 214 require.Equal(t, "9,998.9999900", assets[0].BalanceAvailableToSend) 215 require.Equal(t, "CHF", assets[0].WorthCurrency) 216 require.Equal(t, "3,183.28 CHF", assets[0].Worth) 217 require.Equal(t, "3,182.96 CHF", assets[0].AvailableToSendWorth) 218 219 // changing currency also updates DisplayCurrency in GetWalletAccountLocal 220 argDetails := stellar1.GetWalletAccountLocalArg{AccountID: accountID} 221 details, err := tcs[0].Srv.GetWalletAccountLocal(context.Background(), argDetails) 222 require.NoError(t, err) 223 currencyLocal := details.CurrencyLocal 224 require.Equal(t, stellar1.OutsideCurrencyCode("CHF"), currencyLocal.Code) 225 require.Equal(t, "Swiss Franc", currencyLocal.Name) 226 require.Equal(t, "CHF (CHF)", currencyLocal.Description) 227 require.Equal(t, "CHF", currencyLocal.Symbol) 228 } 229 230 func TestGetAccountAssetsLocalEmptyBalance(t *testing.T) { 231 tcs, cleanup := setupNTests(t, 1) 232 defer cleanup() 233 234 acceptDisclaimer(tcs[0]) 235 236 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 237 238 accounts, err := tcs[0].Srv.GetWalletAccountsLocal(context.Background(), 0) 239 require.NoError(t, err) 240 accountID := accounts[0].AccountID 241 242 assets, err := tcs[0].Srv.GetAccountAssetsLocal(context.Background(), stellar1.GetAccountAssetsLocalArg{AccountID: accountID}) 243 require.NoError(t, err) 244 245 require.Len(t, assets, 1) 246 require.Equal(t, "Lumens", assets[0].Name) 247 require.Equal(t, "XLM", assets[0].AssetCode) 248 require.Equal(t, "Stellar network", assets[0].IssuerName) 249 require.Equal(t, "", assets[0].IssuerAccountID) 250 require.Equal(t, "0", assets[0].BalanceTotal) 251 require.Equal(t, "0", assets[0].BalanceAvailableToSend) 252 require.Equal(t, "USD", assets[0].WorthCurrency) 253 require.Equal(t, "$0.00 USD", assets[0].Worth) 254 require.Equal(t, "$0.00 USD", assets[0].AvailableToSendWorth) 255 } 256 257 func TestGetDisplayCurrenciesLocal(t *testing.T) { 258 tcs, cleanup := setupNTests(t, 1) 259 defer cleanup() 260 261 currencies, err := tcs[0].Srv.GetDisplayCurrenciesLocal(context.Background(), 0) 262 require.NoError(t, err) 263 264 require.Len(t, currencies, 32) 265 // USD should go first. 266 require.Equal(t, "USD ($)", currencies[0].Description) 267 require.Equal(t, stellar1.OutsideCurrencyCode("USD"), currencies[0].Code) 268 require.Equal(t, "$", currencies[0].Symbol) 269 // Rest is in alphabetical order. 270 require.Equal(t, "AUD ($)", currencies[1].Description) 271 require.Equal(t, stellar1.OutsideCurrencyCode("AUD"), currencies[1].Code) 272 require.Equal(t, "$", currencies[1].Symbol) 273 } 274 275 func TestValidateAccountID(t *testing.T) { 276 tcs, cleanup := setupNTests(t, 1) 277 defer cleanup() 278 279 units := []struct { 280 s string 281 ok bool 282 }{ 283 {"GCTXTGK45J3PAPOPK7XLZVZVJYAFTSTWRDWOCRPN5ICQWHQLTBLNN4AQ", true}, 284 {"gctxtgk45j3papopk7xlzvzvjyaftstwrdwocrpn5icqwhqltblnn4aq", true}, 285 {"GCTXTGK45J3PAPOPK7XLZVZVJYAFTSTWRDWOCRPN5ICQWHQLTBLNN4A", false}, 286 {"GCTXTGK45J3PAPOPK7XLZVZVJYAFTSTWRDWOCRPN5ICQWHQLTBLNN4AQQ", false}, 287 {"GCTXTGK45J3PAPOPK7XLZVZVJYAFTSTWRDWOCRPN5ICQWHQLTBLNN4AR", false}, 288 {"", false}, 289 {"a", false}, 290 } 291 for _, u := range units { 292 t.Logf("unit: %v", u) 293 err := tcs[0].Srv.ValidateAccountIDLocal(context.Background(), stellar1.ValidateAccountIDLocalArg{AccountID: stellar1.AccountID(u.s)}) 294 if u.ok { 295 require.NoError(t, err) 296 } else { 297 require.Error(t, err) 298 } 299 } 300 } 301 302 func TestValidateSecretKey(t *testing.T) { 303 tcs, cleanup := setupNTests(t, 1) 304 defer cleanup() 305 306 units := []struct { 307 s string 308 ok bool 309 }{ 310 {"SDXUQS3V6JVO7IN6ZGYEGAUMHJBZK7O7644XIRSCSQ5PFONFK3LO2SCY", true}, 311 {"sdxuqs3v6jvo7in6zgyegaumhjbzk7o7644xirscsq5pfonfk3lo2scy", true}, 312 {"SDXUQS3V6JVO7IN6ZGYEGAUMHJBZK7O7644XIRSCSQ5PFONFK3LO2SC", false}, 313 {"SDXUQS3V6JVO7IN6ZGYEGAUMHJBZK7O7644XIRSCSQ5PFONFK3LO2SCYY", false}, 314 {"SDXUQS3V6JVO7IN6ZGYEGAUMHJBZK7O7644XIRSCSQ5PFONFK3LO2SCZ", false}, 315 {"", false}, 316 {"a", false}, 317 } 318 for _, u := range units { 319 t.Logf("unit: %v", u) 320 err := tcs[0].Srv.ValidateSecretKeyLocal(context.Background(), stellar1.ValidateSecretKeyLocalArg{SecretKey: stellar1.SecretKey(u.s)}) 321 if u.ok { 322 require.NoError(t, err) 323 } else { 324 require.Error(t, err) 325 } 326 } 327 } 328 329 func TestChangeWalletName(t *testing.T) { 330 tcs, cleanup := setupNTests(t, 1) 331 defer cleanup() 332 333 acceptDisclaimer(tcs[0]) 334 335 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 336 337 accs, err := tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 338 require.NoError(t, err) 339 require.Len(t, accs, 1) 340 require.Equal(t, accs[0].Name, firstAccountName(t, tcs[0])) 341 342 chk := func(name string, expected string) { 343 err = tcs[0].Srv.ValidateAccountNameLocal(context.Background(), stellar1.ValidateAccountNameLocalArg{Name: name}) 344 if expected == "" { 345 require.NoError(t, err) 346 } else { 347 require.Error(t, err) 348 require.Equal(t, expected, err.Error()) 349 } 350 } 351 352 chk("", "name required") 353 chk("office lunch money", "") 354 chk("savings", "") 355 res, err := tcs[0].Srv.ChangeWalletAccountNameLocal(context.Background(), stellar1.ChangeWalletAccountNameLocalArg{ 356 AccountID: accs[0].AccountID, 357 NewName: "office lunch money", 358 }) 359 require.NoError(t, err) 360 require.Equal(t, "office lunch money", res.Name) 361 chk("office lunch money", "you already have an account with that name") 362 chk("career debter", "") 363 364 // check to make sure that the stored entry in wallet state also changed. 365 name, err := tcs[0].Srv.walletState.AccountName(accs[0].AccountID) 366 require.NoError(t, err) 367 require.Equal(t, "office lunch money", name) 368 369 accs, err = tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 370 require.NoError(t, err) 371 require.Len(t, accs, 1) 372 require.Equal(t, accs[0].Name, "office lunch money") 373 374 // Try invalid argument 375 invalidAccID, _ := randomStellarKeypair() 376 _, err = tcs[0].Srv.ChangeWalletAccountNameLocal(context.Background(), stellar1.ChangeWalletAccountNameLocalArg{ 377 AccountID: invalidAccID, 378 NewName: "savings", 379 }) 380 require.Error(t, err) 381 chk("savings", "") 382 383 chk("an account used for savi", "") 384 chk("an account used for savin", "account name can be 24 characters at the longest but was 25") 385 } 386 387 func TestSetAccountAsDefault(t *testing.T) { 388 tcs, cleanup := setupNTests(t, 2) 389 defer cleanup() 390 391 acceptDisclaimer(tcs[0]) 392 393 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 394 395 accs, err := tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 396 require.NoError(t, err) 397 require.Len(t, accs, 1) 398 399 require.True(t, accs[0].IsPrimary) 400 401 // Should work for accounts that are already primary and not post 402 // a bundle. 403 res, err := tcs[0].Srv.SetWalletAccountAsDefaultLocal(context.Background(), stellar1.SetWalletAccountAsDefaultLocalArg{ 404 AccountID: accs[0].AccountID, 405 }) 406 require.NoError(t, err) 407 require.Equal(t, accs[0].AccountID, res[0].AccountID) 408 409 mctx := libkb.NewMetaContextBackground(tcs[0].G) 410 bundle, err := remote.FetchSecretlessBundle(mctx) 411 require.NoError(t, err) 412 require.EqualValues(t, 1, bundle.Revision) 413 414 // Test invalid arguments 415 invalidAccID, _ := randomStellarKeypair() 416 _, err = tcs[0].Srv.SetWalletAccountAsDefaultLocal(context.Background(), stellar1.SetWalletAccountAsDefaultLocalArg{ 417 AccountID: invalidAccID, 418 }) 419 require.Error(t, err) 420 421 _, err = tcs[0].Srv.SetWalletAccountAsDefaultLocal(context.Background(), stellar1.SetWalletAccountAsDefaultLocalArg{ 422 AccountID: stellar1.AccountID(""), 423 }) 424 require.Error(t, err) 425 426 additionalAccs := []stellar1.AccountID{ 427 tcs[0].Backend.AddAccountEmpty(t, tcs[0].Fu.GetUID()), 428 tcs[0].Backend.AddAccountEmpty(t, tcs[0].Fu.GetUID()), 429 } 430 431 for _, v := range additionalAccs { 432 err = tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 433 SecretKey: tcs[0].Backend.SecretKey(v), 434 MakePrimary: false, 435 Name: "qq", 436 }) 437 require.NoError(t, err) 438 } 439 440 for i := len(additionalAccs) - 1; i >= 0; i-- { 441 v := additionalAccs[i] 442 arg := stellar1.SetWalletAccountAsDefaultLocalArg{ 443 AccountID: v, 444 } 445 _, err := tcs[0].Srv.SetWalletAccountAsDefaultLocal(context.Background(), arg) 446 require.NoError(t, err) 447 448 accs, err := tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 449 require.NoError(t, err) 450 require.Len(t, accs, 3) 451 for _, acc := range accs { 452 require.Equal(t, acc.IsPrimary, acc.AccountID == v) 453 } 454 } 455 456 // Expecting additionalAccs[0] to be default account. Lookup 457 // public Stellar address as another user. 458 u0, err := tcs[1].G.LoadUserByUID(tcs[0].Fu.User.GetUID()) 459 require.NoError(t, err) 460 u0addr := u0.StellarAccountID() 461 require.NotNil(t, u0addr) 462 require.Equal(t, additionalAccs[0], *u0addr) 463 464 isPrimary, err := tcs[0].Srv.walletState.IsPrimary(additionalAccs[0]) 465 require.NoError(t, err) 466 require.True(t, isPrimary) 467 } 468 469 func testCreateOrLinkNewAccount(t *testing.T, create bool) { 470 tcs, cleanup := setupNTests(t, 1) 471 defer cleanup() 472 473 acceptDisclaimer(tcs[0]) 474 475 // link a new account 476 var accID stellar1.AccountID 477 var err error 478 accName := "my other account" 479 if create { 480 // create new account 481 arg := stellar1.CreateWalletAccountLocalArg{ 482 Name: accName, 483 } 484 accID, err = tcs[0].Srv.CreateWalletAccountLocal(context.Background(), arg) 485 require.NoError(t, err) 486 } else { 487 a1, s1 := randomStellarKeypair() 488 arg := stellar1.LinkNewWalletAccountLocalArg{ 489 SecretKey: s1, 490 Name: accName, 491 } 492 accID, err = tcs[0].Srv.LinkNewWalletAccountLocal(context.Background(), arg) 493 require.NoError(t, err) 494 require.Equal(t, a1, accID) 495 } 496 497 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 498 499 accts, err := tcs[0].Srv.GetWalletAccountsLocal(context.Background(), 0) 500 require.NoError(t, err) 501 502 require.Len(t, accts, 2) 503 require.True(t, accts[0].IsDefault) 504 require.Equal(t, firstAccountName(t, tcs[0]), accts[0].Name) 505 require.Equal(t, "0 XLM", accts[0].BalanceDescription) 506 require.False(t, accts[1].IsDefault) 507 require.Equal(t, accID, accts[1].AccountID) 508 require.Equal(t, accName, accts[1].Name) 509 require.Equal(t, "0 XLM", accts[1].BalanceDescription) 510 } 511 512 func TestLinkNewWalletAccountLocal(t *testing.T) { 513 testCreateOrLinkNewAccount(t, false /* create */) 514 } 515 516 func TestCreateNewWalletAccountLocal(t *testing.T) { 517 testCreateOrLinkNewAccount(t, true /* create */) 518 } 519 520 func TestDeleteWallet(t *testing.T) { 521 tcs, cleanup := setupNTests(t, 1) 522 defer cleanup() 523 524 acceptDisclaimer(tcs[0]) 525 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 526 accID := getPrimaryAccountID(tcs[0]) 527 528 // Cannot delete the only account (also primary). 529 err := tcs[0].Srv.DeleteWalletAccountLocal(context.Background(), stellar1.DeleteWalletAccountLocalArg{ 530 AccountID: accID, 531 UserAcknowledged: "yes", 532 }) 533 require.Error(t, err) 534 535 // Cannot delete account that doesnt exist. 536 invalidAccID, _ := randomStellarKeypair() 537 err = tcs[0].Srv.DeleteWalletAccountLocal(context.Background(), stellar1.DeleteWalletAccountLocalArg{ 538 AccountID: invalidAccID, 539 UserAcknowledged: "yes", 540 }) 541 require.Error(t, err) 542 543 // Add new account, make it primary, now first account should be 544 // deletable. 545 accID2 := tcs[0].Backend.AddAccountEmpty(t, tcs[0].Fu.GetUID()) 546 err = tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 547 SecretKey: tcs[0].Backend.SecretKey(accID2), 548 MakePrimary: true, 549 Name: "qq", 550 }) 551 require.NoError(t, err) 552 553 // First try without `UserAcknowledged`. 554 err = tcs[0].Srv.DeleteWalletAccountLocal(context.Background(), stellar1.DeleteWalletAccountLocalArg{ 555 AccountID: accID, 556 }) 557 require.Error(t, err) 558 559 err = tcs[0].Srv.DeleteWalletAccountLocal(context.Background(), stellar1.DeleteWalletAccountLocalArg{ 560 AccountID: accID, 561 UserAcknowledged: "yes", 562 }) 563 require.NoError(t, err) 564 565 accs, err := tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 566 require.NoError(t, err) 567 require.Len(t, accs, 1) 568 require.Equal(t, accs[0].AccountID, accID2) 569 require.True(t, accs[0].IsPrimary) 570 } 571 572 func TestChangeDisplayCurrency(t *testing.T) { 573 tcs, cleanup := setupNTests(t, 2) 574 defer cleanup() 575 576 acceptDisclaimer(tcs[0]) 577 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 578 accID := getPrimaryAccountID(tcs[0]) 579 580 // Try invalid currency first. 581 _, err := tcs[0].Srv.ChangeDisplayCurrencyLocal(context.Background(), stellar1.ChangeDisplayCurrencyLocalArg{ 582 AccountID: accID, 583 Currency: stellar1.OutsideCurrencyCode("ZZZ"), 584 }) 585 require.Error(t, err) 586 587 // Try empty account id. 588 _, err = tcs[0].Srv.ChangeDisplayCurrencyLocal(context.Background(), stellar1.ChangeDisplayCurrencyLocalArg{ 589 AccountID: stellar1.AccountID(""), 590 Currency: stellar1.OutsideCurrencyCode("USD"), 591 }) 592 require.Error(t, err) 593 594 // Try non-existant account id. 595 invalidAccID, _ := randomStellarKeypair() 596 _, err = tcs[0].Srv.ChangeDisplayCurrencyLocal(context.Background(), stellar1.ChangeDisplayCurrencyLocalArg{ 597 AccountID: invalidAccID, 598 Currency: stellar1.OutsideCurrencyCode("USD"), 599 }) 600 require.Error(t, err) 601 602 // Make wallet as other user, and try to change the currency as 603 // first user. 604 acceptDisclaimer(tcs[1]) 605 tcs[1].Backend.ImportAccountsForUser(tcs[1]) 606 accID2 := getPrimaryAccountID(tcs[1]) 607 _, err = tcs[0].Srv.ChangeDisplayCurrencyLocal(context.Background(), stellar1.ChangeDisplayCurrencyLocalArg{ 608 AccountID: accID2, 609 Currency: stellar1.OutsideCurrencyCode("EUR"), 610 }) 611 require.Error(t, err) 612 613 // Finally, a happy path. 614 res, err := tcs[0].Srv.ChangeDisplayCurrencyLocal(context.Background(), stellar1.ChangeDisplayCurrencyLocalArg{ 615 AccountID: accID, 616 Currency: stellar1.OutsideCurrencyCode("EUR"), 617 }) 618 require.NoError(t, err) 619 require.Equal(t, stellar1.OutsideCurrencyCode("EUR"), res.Code) 620 621 // Check both CLI and Frontend RPCs. 622 accs, err := tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 623 require.NoError(t, err) 624 require.Len(t, accs, 1) 625 require.NotNil(t, accs[0].ExchangeRate) 626 require.EqualValues(t, "EUR", accs[0].ExchangeRate.Currency) 627 628 balances, err := tcs[0].Srv.GetAccountAssetsLocal(context.Background(), stellar1.GetAccountAssetsLocalArg{ 629 AccountID: accID, 630 }) 631 require.NoError(t, err) 632 require.Len(t, balances, 1) 633 require.EqualValues(t, "EUR", balances[0].WorthCurrency) 634 } 635 636 func TestAcceptDisclaimer(t *testing.T) { 637 tcs, cleanup := setupNTests(t, 1) 638 defer cleanup() 639 640 accepted, err := tcs[0].Srv.HasAcceptedDisclaimerLocal(context.Background(), 0) 641 require.NoError(t, err) 642 require.Equal(t, false, accepted) 643 644 t.Logf("can't create wallet before disclaimer") 645 mctx := tcs[0].MetaContext() 646 _, err = stellar.CreateWallet(mctx) 647 require.Error(t, err) 648 require.True(t, libkb.IsAppStatusCode(err, keybase1.StatusCode_SCStellarNeedDisclaimer)) 649 650 accepted, err = tcs[0].Srv.HasAcceptedDisclaimerLocal(context.Background(), 0) 651 require.NoError(t, err) 652 require.Equal(t, false, accepted) 653 654 err = tcs[0].Srv.AcceptDisclaimerLocal(context.Background(), 0) 655 require.NoError(t, err) 656 657 accepted, err = tcs[0].Srv.HasAcceptedDisclaimerLocal(context.Background(), 0) 658 require.NoError(t, err) 659 require.Equal(t, true, accepted) 660 } 661 662 func TestPublicKeyExporting(t *testing.T) { 663 tcs, cleanup := setupNTests(t, 1) 664 defer cleanup() 665 666 acceptDisclaimer(tcs[0]) 667 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 668 accID := getPrimaryAccountID(tcs[0]) 669 670 // Try empty argument. 671 _, err := tcs[0].Srv.GetWalletAccountPublicKeyLocal(context.Background(), stellar1.GetWalletAccountPublicKeyLocalArg{ 672 AccountID: stellar1.AccountID(""), 673 }) 674 require.Error(t, err) 675 676 // Anything should work - even accounts that don't exist or are 677 // not ours. 678 randomAccID, _ := randomStellarKeypair() 679 pubKey, err := tcs[0].Srv.GetWalletAccountPublicKeyLocal(context.Background(), stellar1.GetWalletAccountPublicKeyLocalArg{ 680 AccountID: randomAccID, 681 }) 682 require.NoError(t, err) 683 require.EqualValues(t, randomAccID, pubKey) 684 685 // Try account of our own. 686 pubKey, err = tcs[0].Srv.GetWalletAccountPublicKeyLocal(context.Background(), stellar1.GetWalletAccountPublicKeyLocalArg{ 687 AccountID: accID, 688 }) 689 require.NoError(t, err) 690 require.EqualValues(t, accID, pubKey) 691 } 692 693 func TestPrivateKeyExporting(t *testing.T) { 694 tcs, cleanup := setupNTests(t, 1) 695 defer cleanup() 696 697 acceptDisclaimer(tcs[0]) 698 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 699 accID := getPrimaryAccountID(tcs[0]) 700 701 // Try empty argument. 702 _, err := tcs[0].Srv.GetWalletAccountSecretKeyLocal(context.Background(), stellar1.GetWalletAccountSecretKeyLocalArg{ 703 AccountID: stellar1.AccountID(""), 704 }) 705 require.Error(t, err) 706 707 // Try random account ID. 708 randomAccID, _ := randomStellarKeypair() 709 _, err = tcs[0].Srv.GetWalletAccountSecretKeyLocal(context.Background(), stellar1.GetWalletAccountSecretKeyLocalArg{ 710 AccountID: randomAccID, 711 }) 712 require.Error(t, err) 713 714 // Happy path. 715 privKey, err := tcs[0].Srv.GetWalletAccountSecretKeyLocal(context.Background(), stellar1.GetWalletAccountSecretKeyLocalArg{ 716 AccountID: accID, 717 }) 718 require.NoError(t, err) 719 require.EqualValues(t, tcs[0].Backend.SecretKey(accID), privKey) 720 } 721 722 func TestGetPaymentsLocal(t *testing.T) { 723 tcs, cleanup := setupNTests(t, 2) 724 defer cleanup() 725 726 acceptDisclaimer(tcs[0]) 727 acceptDisclaimer(tcs[1]) 728 729 srvSender := tcs[0].Srv 730 rm := tcs[0].Backend 731 accountIDSender := rm.AddAccount(tcs[0].Fu.GetUID()) 732 accountIDRecip := rm.AddAccount(tcs[1].Fu.GetUID()) 733 accountIDRecip2 := rm.AddAccount(tcs[1].Fu.GetUID()) 734 735 srvRecip := tcs[1].Srv 736 737 err := srvSender.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 738 SecretKey: rm.SecretKey(accountIDSender), 739 MakePrimary: true, 740 Name: "qq", 741 }) 742 require.NoError(t, err) 743 744 _, err = srvSender.ChangeWalletAccountNameLocal(context.Background(), stellar1.ChangeWalletAccountNameLocalArg{ 745 AccountID: accountIDSender, 746 NewName: "office lunch money", 747 }) 748 require.NoError(t, err) 749 750 err = srvRecip.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 751 SecretKey: rm.SecretKey(accountIDRecip), 752 MakePrimary: true, 753 Name: "uu", 754 }) 755 require.NoError(t, err) 756 757 err = srvRecip.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 758 SecretKey: rm.SecretKey(accountIDRecip2), 759 MakePrimary: false, 760 Name: "vv", 761 }) 762 require.NoError(t, err) 763 764 // Try some payments that should fail locally 765 { 766 _, err := srvSender.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 767 BypassBid: true, 768 From: accountIDRecip, // From the wrong account 769 To: tcs[1].Fu.Username, 770 ToIsAccountID: false, 771 Amount: "1011.123", 772 Asset: stellar1.AssetNative(), 773 WorthAmount: "321.87", 774 WorthCurrency: &usd, 775 SecretNote: "here you go", 776 PublicMemo: "public note", 777 }) 778 require.Error(t, err) 779 require.Contains(t, err.Error(), "Sender account not found") 780 781 _, err = srvSender.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 782 BypassBid: true, 783 From: accountIDSender, 784 To: tcs[1].Fu.Username, 785 ToIsAccountID: true, // fail to parse account ID 786 Amount: "1011.123", 787 Asset: stellar1.AssetNative(), 788 WorthAmount: "321.87", 789 WorthCurrency: &usd, 790 SecretNote: "here you go", 791 PublicMemo: "public note", 792 }) 793 require.Error(t, err) 794 require.Equal(t, "recipient: Invalid Stellar address.", err.Error()) 795 } 796 797 // set up notification listeners 798 listenerSender := newChatListener() 799 listenerRecip := newChatListener() 800 tcs[0].G.NotifyRouter.AddListener(listenerSender) 801 tcs[1].G.NotifyRouter.AddListener(listenerRecip) 802 803 sendRes, err := srvSender.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 804 BypassBid: true, 805 From: accountIDSender, 806 To: tcs[1].Fu.Username, 807 ToIsAccountID: false, 808 Amount: "1011.123", 809 Asset: stellar1.AssetNative(), 810 WorthAmount: "321.87", 811 WorthCurrency: &usd, 812 SecretNote: "here you go", 813 PublicMemo: "public note", 814 }) 815 require.NoError(t, err) 816 require.Len(t, sendRes.KbTxID, 32) 817 require.False(t, sendRes.Pending) 818 819 // simulate exchange rate changing 820 tcs[0].Backend.UseAlternateExchangeRate() 821 822 checkPayment := func(p stellar1.PaymentLocal, sender bool) { 823 require.NotEmpty(t, p.Id) 824 require.NotZero(t, p.Time) 825 require.Equal(t, stellar1.PaymentStatus_COMPLETED, p.StatusSimplified) 826 require.Equal(t, "completed", p.StatusDescription) 827 require.Empty(t, p.StatusDetail) 828 if sender { 829 require.Equal(t, "1,011.1230000 XLM", p.AmountDescription, "Amount") 830 require.Equal(t, stellar1.BalanceDelta_DECREASE, p.Delta) 831 } else { 832 require.Equal(t, "1,011.1230000 XLM", p.AmountDescription, "Amount") 833 require.Equal(t, stellar1.BalanceDelta_INCREASE, p.Delta) 834 } 835 require.Equal(t, "$321.87 USD", p.Worth, "Worth") 836 require.Equal(t, "", p.WorthAtSendTime, "WorthAtSendTIme") 837 838 require.Equal(t, stellar1.ParticipantType_KEYBASE, p.FromType) 839 require.Equal(t, accountIDSender, p.FromAccountID) 840 var fromAccountName string 841 if sender { 842 fromAccountName = "office lunch money" 843 } 844 require.Equal(t, fromAccountName, p.FromAccountName, "FromAccountName") 845 require.Equal(t, tcs[0].Fu.Username, p.FromUsername) 846 require.Equal(t, stellar1.ParticipantType_KEYBASE, p.ToType) 847 require.Equal(t, accountIDRecip, *p.ToAccountID) 848 var toAccountName string 849 if !sender { 850 toAccountName = "uu" 851 } 852 require.Equal(t, toAccountName, p.ToAccountName, "ToAccountName") 853 require.Equal(t, tcs[1].Fu.Username, p.ToUsername) 854 require.Equal(t, "", p.ToAssertion) 855 856 require.Equal(t, "here you go", p.Note) 857 require.Empty(t, p.NoteErr) 858 } 859 senderPaymentsPage, err := srvSender.GetPaymentsLocal(context.Background(), stellar1.GetPaymentsLocalArg{AccountID: accountIDSender}) 860 require.NoError(t, err) 861 senderPayments := senderPaymentsPage.Payments 862 require.Len(t, senderPayments, 1) 863 t.Logf("senderPayments: %v", spew.Sdump(senderPayments)) 864 if senderPayments[0].Err != nil { 865 t.Logf("senderPayments error: %+v", *senderPayments[0].Err) 866 } 867 require.NotNil(t, senderPayments[0].Payment) 868 checkPayment(*senderPayments[0].Payment, true) 869 870 recipPaymentsPage, err := srvRecip.GetPaymentsLocal(context.Background(), stellar1.GetPaymentsLocalArg{AccountID: accountIDRecip}) 871 require.NoError(t, err) 872 recipPayments := recipPaymentsPage.Payments 873 require.Len(t, recipPayments, 1) 874 require.NotNil(t, recipPayments[0].Payment) 875 checkPayment(*recipPayments[0].Payment, false) 876 877 // pretend that the chat message was unboxed and call the payment loader to load the info: 878 loader := stellar.DefaultLoader(tcs[0].G) 879 convID := chat1.ConversationID("abcd") 880 msgID := chat1.MessageID(987) 881 loader.LoadPayment(context.Background(), convID, msgID, tcs[0].Fu.Username, senderPayments[0].Payment.Id) 882 883 // for the recipient too 884 recipLoader := stellar.NewLoader(tcs[1].G) 885 recipLoader.LoadPayment(context.Background(), convID, msgID, tcs[0].Fu.Username, senderPayments[0].Payment.Id) 886 887 // check the sender chat notification 888 select { 889 case info := <-listenerSender.paymentInfos: 890 t.Logf("info from listener: %+v", info) 891 require.NotNil(t, info) 892 require.Equal(t, info.Uid, tcs[0].Fu.User.GetUID()) 893 require.Equal(t, info.MsgID, msgID) 894 require.True(t, info.ConvID.Eq(convID)) 895 require.Equal(t, info.Info.AmountDescription, "1,011.1230000 XLM") 896 require.Equal(t, stellar1.BalanceDelta_DECREASE, info.Info.Delta) 897 require.Equal(t, "$321.87 USD", info.Info.Worth) 898 require.Equal(t, info.Info.Note, "here you go") 899 require.Equal(t, info.Info.Status, stellar1.PaymentStatus_COMPLETED) 900 require.Equal(t, info.Info.StatusDescription, "completed") 901 case <-time.After(20 * time.Second): 902 t.Fatal("timed out waiting for chat payment info notification to sender") 903 } 904 905 // check the recipient chat notification 906 select { 907 case info := <-listenerRecip.paymentInfos: 908 t.Logf("info from listener: %+v", info) 909 require.NotNil(t, info) 910 require.Equal(t, info.Uid, tcs[1].Fu.User.GetUID()) 911 require.Equal(t, info.MsgID, msgID) 912 require.True(t, info.ConvID.Eq(convID)) 913 require.Equal(t, info.Info.AmountDescription, "1,011.1230000 XLM") 914 require.Equal(t, info.Info.Delta, stellar1.BalanceDelta_INCREASE) 915 require.Equal(t, info.Info.Worth, "$321.87 USD") 916 require.Equal(t, info.Info.Note, "here you go") 917 require.Equal(t, info.Info.Status, stellar1.PaymentStatus_COMPLETED) 918 require.Equal(t, info.Info.StatusDescription, "completed") 919 case <-time.After(20 * time.Second): 920 t.Fatal("timed out waiting for chat payment info notification to sender") 921 } 922 923 // check the details 924 checkPaymentDetails := func(pd stellar1.PaymentDetailsLocal, sender bool) { 925 p := pd.Summary 926 require.NotEmpty(t, p.Id) 927 require.NotZero(t, p.Time) 928 require.Equal(t, stellar1.PaymentStatus_COMPLETED, p.StatusSimplified) 929 require.Equal(t, "completed", p.StatusDescription) 930 require.Empty(t, p.StatusDetail) 931 if sender { 932 require.Equal(t, "1,011.1230000 XLM", p.AmountDescription, "Amount") 933 require.Equal(t, stellar1.BalanceDelta_DECREASE, p.Delta) 934 } else { 935 require.Equal(t, "1,011.1230000 XLM", p.AmountDescription, "Amount") 936 require.Equal(t, stellar1.BalanceDelta_INCREASE, p.Delta) 937 } 938 require.Equal(t, "$321.87 USD", p.Worth, "Worth") 939 require.Equal(t, "", p.WorthAtSendTime, "WorthAtSendTime") 940 941 require.Equal(t, stellar1.ParticipantType_KEYBASE, p.FromType) 942 require.Equal(t, accountIDSender, p.FromAccountID) 943 var fromAccountName string 944 if sender { 945 fromAccountName = "office lunch money" 946 } 947 require.Equal(t, fromAccountName, p.FromAccountName) 948 require.Equal(t, tcs[0].Fu.Username, p.FromUsername) 949 require.Equal(t, stellar1.ParticipantType_KEYBASE, p.ToType) 950 require.Equal(t, accountIDRecip, *p.ToAccountID) 951 var toAccountName string 952 if !sender { 953 toAccountName = "uu" 954 } 955 require.Equal(t, toAccountName, p.ToAccountName) 956 require.Equal(t, tcs[1].Fu.Username, p.ToUsername) 957 require.Equal(t, "", p.ToAssertion) 958 959 require.Equal(t, "here you go", p.Note) 960 require.Empty(t, p.NoteErr) 961 require.Equal(t, "public note", pd.Details.PublicNote) 962 require.Equal(t, "text", pd.Details.PublicNoteType) 963 t.Logf("details: %+v", p) 964 require.Equal(t, fmt.Sprintf("https://stellar.expert/explorer/public/tx/%s", p.TxID), pd.Details.ExternalTxURL) 965 } 966 details, err := srvSender.GetPaymentDetailsLocal(context.Background(), stellar1.GetPaymentDetailsLocalArg{ 967 Id: senderPayments[0].Payment.Id, 968 AccountID: accountIDSender, 969 }) 970 require.NoError(t, err) 971 checkPaymentDetails(details, true) 972 973 details, err = srvRecip.GetPaymentDetailsLocal(context.Background(), stellar1.GetPaymentDetailsLocalArg{ 974 Id: recipPayments[0].Payment.Id, 975 AccountID: accountIDRecip, 976 }) 977 require.NoError(t, err) 978 checkPaymentDetails(details, false) 979 980 // use default exchange rate again since about to send new payments. 981 tcs[0].Backend.UseDefaultExchangeRate() 982 983 // Send again with FromSeqno set. 984 // Does not test whether it has any effect. 985 _, err = srvSender.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 986 BypassBid: true, 987 From: accountIDSender, 988 To: tcs[1].Fu.Username, 989 ToIsAccountID: false, 990 Amount: "1011.123", 991 Asset: stellar1.AssetNative(), 992 WorthAmount: "321.87", 993 WorthCurrency: &usd, 994 SecretNote: "here you go", 995 PublicMemo: "public note", 996 }) 997 require.NoError(t, err) 998 999 // send to stellar account ID to check target in PaymentLocal 1000 sendRes, err = srvSender.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 1001 BypassBid: true, 1002 From: accountIDSender, 1003 // Use a secondary account so that LookupRecipient can't resolve it to the user 1004 To: accountIDRecip2.String(), 1005 ToIsAccountID: true, 1006 Amount: "101.456", 1007 Asset: stellar1.AssetNative(), 1008 WorthAmount: "321.87", 1009 WorthCurrency: &usd, 1010 SecretNote: "here you go", 1011 PublicMemo: "public note", 1012 }) 1013 require.NoError(t, err) 1014 require.Len(t, sendRes.KbTxID, 32) 1015 require.False(t, sendRes.Pending) 1016 senderPaymentsPage, err = srvSender.GetPaymentsLocal(context.Background(), stellar1.GetPaymentsLocalArg{AccountID: accountIDSender}) 1017 require.NoError(t, err) 1018 senderPayments = senderPaymentsPage.Payments 1019 require.Len(t, senderPayments, 3) 1020 t.Logf("senderPayments: %+v", senderPayments) 1021 if senderPayments[0].Err != nil { 1022 t.Logf("senderPayments error: %+v", *senderPayments[0].Err) 1023 } 1024 p := senderPayments[0].Payment 1025 require.NotNil(t, p) 1026 require.Equal(t, stellar1.ParticipantType_KEYBASE, p.FromType) 1027 require.Equal(t, accountIDSender, p.FromAccountID) 1028 require.Equal(t, "office lunch money", p.FromAccountName) 1029 require.Equal(t, tcs[0].Fu.Username, p.FromUsername) 1030 require.Equal(t, stellar1.ParticipantType_STELLAR, p.ToType) 1031 require.Equal(t, accountIDRecip2, *p.ToAccountID) 1032 require.Equal(t, "", p.ToAccountName) 1033 require.Equal(t, "", p.ToUsername) 1034 require.Equal(t, "", p.ToAssertion) 1035 1036 recipPaymentsPage, err = srvRecip.GetPaymentsLocal(context.Background(), stellar1.GetPaymentsLocalArg{AccountID: accountIDRecip2}) 1037 require.NoError(t, err) 1038 recipPayments = recipPaymentsPage.Payments 1039 require.Len(t, recipPayments, 1) 1040 p = recipPayments[0].Payment 1041 t.Logf("recipPayments[0]: %+v", p) 1042 require.NotNil(t, p) 1043 require.Equal(t, stellar1.ParticipantType_KEYBASE, p.FromType) 1044 require.Equal(t, accountIDSender, p.FromAccountID) 1045 require.Equal(t, tcs[0].Fu.Username, p.FromUsername) 1046 require.Equal(t, "", p.FromAccountName) 1047 require.Equal(t, stellar1.ParticipantType_STELLAR, p.ToType) 1048 require.Equal(t, accountIDRecip2, *p.ToAccountID) 1049 require.Equal(t, "vv", p.ToAccountName) 1050 require.Equal(t, "", p.ToUsername) 1051 require.Equal(t, "", p.ToAssertion) 1052 require.NotEmpty(t, p.NoteErr) // can't send encrypted note to stellar address 1053 } 1054 1055 func TestSendToSelf(t *testing.T) { 1056 tcs, cleanup := setupNTests(t, 1) 1057 defer cleanup() 1058 1059 acceptDisclaimer(tcs[0]) 1060 rm := tcs[0].Backend 1061 accountID1 := rm.AddAccount(tcs[0].Fu.GetUID()) 1062 accountID2 := rm.AddAccount(tcs[0].Fu.GetUID()) 1063 1064 err := tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 1065 SecretKey: rm.SecretKey(accountID1), 1066 MakePrimary: true, 1067 Name: "qq", 1068 }) 1069 require.NoError(t, err) 1070 1071 _, err = tcs[0].Srv.ChangeWalletAccountNameLocal(context.Background(), stellar1.ChangeWalletAccountNameLocalArg{ 1072 AccountID: accountID1, 1073 NewName: "office lunch money", 1074 }) 1075 require.NoError(t, err) 1076 1077 err = tcs[0].Srv.ImportSecretKeyLocal(context.Background(), stellar1.ImportSecretKeyLocalArg{ 1078 SecretKey: rm.SecretKey(accountID2), 1079 Name: "uu", 1080 }) 1081 require.NoError(t, err) 1082 1083 _, err = tcs[0].Srv.ChangeWalletAccountNameLocal(context.Background(), stellar1.ChangeWalletAccountNameLocalArg{ 1084 AccountID: accountID2, 1085 NewName: "savings", 1086 }) 1087 require.NoError(t, err) 1088 1089 t.Logf("Send to the same account") 1090 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 1091 BypassBid: true, 1092 From: accountID1, 1093 To: accountID1.String(), 1094 ToIsAccountID: true, 1095 Amount: "100", 1096 Asset: stellar1.AssetNative(), 1097 }) 1098 require.NoError(t, err) 1099 1100 t.Logf("Send to another of the same user's account") 1101 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 1102 BypassBid: true, 1103 From: accountID1, 1104 To: accountID2.String(), 1105 ToIsAccountID: true, 1106 Amount: "200", 1107 Asset: stellar1.AssetNative(), 1108 }) 1109 require.NoError(t, err) 1110 1111 t.Logf("Send from another of the same user's account") 1112 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 1113 BypassBid: true, 1114 From: accountID2, 1115 To: accountID1.String(), 1116 ToIsAccountID: true, 1117 Amount: "300", 1118 Asset: stellar1.AssetNative(), 1119 }) 1120 require.NoError(t, err) 1121 1122 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), accountID1, "test") 1123 require.NoError(t, err) 1124 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), accountID2, "test") 1125 require.NoError(t, err) 1126 1127 page, err := tcs[0].Srv.GetPaymentsLocal(context.Background(), stellar1.GetPaymentsLocalArg{AccountID: accountID1}) 1128 require.NoError(t, err) 1129 t.Logf("%v", spew.Sdump(page)) 1130 require.Len(t, page.Payments, 3) 1131 1132 p := page.Payments[2].Payment 1133 require.Equal(t, "100 XLM", p.AmountDescription) 1134 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, p.FromType) 1135 require.Equal(t, accountID1, p.FromAccountID) 1136 require.Equal(t, "office lunch money", p.FromAccountName) 1137 require.Equal(t, tcs[0].Fu.Username, p.FromUsername) 1138 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, p.ToType) 1139 require.Equal(t, accountID1, *p.ToAccountID) 1140 require.Equal(t, "office lunch money", p.ToAccountName) 1141 require.Equal(t, tcs[0].Fu.Username, p.ToUsername) 1142 require.Equal(t, "", p.ToAssertion) 1143 require.Equal(t, "$123.23 USD", p.WorthAtSendTime) 1144 1145 p = page.Payments[1].Payment 1146 require.Equal(t, "200 XLM", p.AmountDescription) 1147 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, p.FromType) 1148 require.Equal(t, accountID1, p.FromAccountID) 1149 require.Equal(t, "office lunch money", p.FromAccountName) 1150 require.Equal(t, tcs[0].Fu.Username, p.FromUsername) 1151 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, p.ToType) 1152 require.Equal(t, accountID2, *p.ToAccountID) 1153 require.Equal(t, "savings", p.ToAccountName) 1154 require.Equal(t, tcs[0].Fu.Username, p.ToUsername) 1155 require.Equal(t, "", p.ToAssertion) 1156 require.Equal(t, "$123.23 USD", p.WorthAtSendTime) 1157 1158 p = page.Payments[0].Payment 1159 require.Equal(t, "300 XLM", p.AmountDescription) 1160 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, p.FromType) 1161 require.Equal(t, accountID2, p.FromAccountID) 1162 require.Equal(t, "savings", p.FromAccountName) 1163 require.Equal(t, tcs[0].Fu.Username, p.FromUsername) 1164 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, p.ToType) 1165 require.Equal(t, accountID1, *p.ToAccountID) 1166 require.Equal(t, "office lunch money", p.ToAccountName) 1167 require.Equal(t, tcs[0].Fu.Username, p.ToUsername) 1168 require.Equal(t, "", p.ToAssertion) 1169 require.Equal(t, "$123.23 USD", p.WorthAtSendTime) 1170 1171 pd1, err := tcs[0].Srv.GetPaymentDetailsLocal(context.Background(), stellar1.GetPaymentDetailsLocalArg{ 1172 Id: page.Payments[2].Payment.Id, 1173 AccountID: accountID1, 1174 }) 1175 pd := pd1.Summary 1176 require.NoError(t, err) 1177 require.Equal(t, "100 XLM", pd.AmountDescription) 1178 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, pd.FromType) 1179 require.Equal(t, accountID1, pd.FromAccountID) 1180 require.Equal(t, "office lunch money", pd.FromAccountName) 1181 require.Equal(t, tcs[0].Fu.Username, pd.FromUsername) 1182 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, pd.ToType) 1183 require.Equal(t, accountID1, *pd.ToAccountID) 1184 require.Equal(t, "office lunch money", pd.ToAccountName) 1185 require.Equal(t, tcs[0].Fu.Username, pd.ToUsername) 1186 require.Equal(t, "", pd.ToAssertion) 1187 require.Equal(t, "$123.23 USD", p.WorthAtSendTime) 1188 1189 pd1, err = tcs[0].Srv.GetPaymentDetailsLocal(context.Background(), stellar1.GetPaymentDetailsLocalArg{ 1190 Id: page.Payments[1].Payment.Id, 1191 AccountID: accountID1, 1192 }) 1193 pd = pd1.Summary 1194 require.NoError(t, err) 1195 require.Equal(t, "200 XLM", pd.AmountDescription) 1196 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, pd.FromType) 1197 require.Equal(t, accountID1, pd.FromAccountID) 1198 require.Equal(t, "office lunch money", pd.FromAccountName) 1199 require.Equal(t, tcs[0].Fu.Username, pd.FromUsername) 1200 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, pd.ToType) 1201 require.Equal(t, accountID2, *pd.ToAccountID) 1202 require.Equal(t, "savings", pd.ToAccountName) 1203 require.Equal(t, tcs[0].Fu.Username, pd.ToUsername) 1204 require.Equal(t, "", pd.ToAssertion) 1205 require.Equal(t, "$123.23 USD", p.WorthAtSendTime) 1206 1207 pd1, err = tcs[0].Srv.GetPaymentDetailsLocal(context.Background(), stellar1.GetPaymentDetailsLocalArg{ 1208 Id: page.Payments[0].Payment.Id, 1209 AccountID: accountID2, 1210 }) 1211 pd = pd1.Summary 1212 require.NoError(t, err) 1213 require.Equal(t, "300 XLM", pd.AmountDescription) 1214 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, pd.FromType) 1215 require.Equal(t, accountID2, pd.FromAccountID) 1216 require.Equal(t, "savings", pd.FromAccountName) 1217 require.Equal(t, tcs[0].Fu.Username, pd.FromUsername) 1218 require.Equal(t, stellar1.ParticipantType_OWNACCOUNT, pd.ToType) 1219 require.Equal(t, accountID1, *pd.ToAccountID) 1220 require.Equal(t, "office lunch money", pd.ToAccountName) 1221 require.Equal(t, tcs[0].Fu.Username, pd.ToUsername) 1222 require.Equal(t, "", pd.ToAssertion) 1223 require.Equal(t, "$123.23 USD", p.WorthAtSendTime) 1224 } 1225 1226 func TestPaymentDetailsEmptyAccId(t *testing.T) { 1227 tcs, cleanup := setupNTests(t, 2) 1228 defer cleanup() 1229 1230 acceptDisclaimer(tcs[0]) 1231 acceptDisclaimer(tcs[1]) 1232 backend := tcs[0].Backend 1233 backend.ImportAccountsForUser(tcs[0]) 1234 backend.ImportAccountsForUser(tcs[1]) 1235 1236 accID := getPrimaryAccountID(tcs[0]) 1237 backend.accounts[accID].AddBalance("1000") 1238 1239 const secretNote string = "pleasure doing business 🤔" 1240 1241 _, err := tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 1242 BypassBid: true, 1243 From: accID, 1244 To: tcs[1].Fu.Username, 1245 ToIsAccountID: false, 1246 Amount: "505.612", 1247 Asset: stellar1.AssetNative(), 1248 WorthAmount: "160.93", 1249 WorthCurrency: &usd, 1250 SecretNote: secretNote, 1251 PublicMemo: "", 1252 }) 1253 require.NoError(t, err) 1254 1255 senderMsgs := kbtest.MockSentMessages(tcs[0].G, tcs[0].T) 1256 require.Len(t, senderMsgs, 1) 1257 require.Equal(t, senderMsgs[0].MsgType, chat1.MessageType_SENDPAYMENT) 1258 1259 // Imagine this is the receiver reading chat. 1260 paymentID := senderMsgs[0].Body.Sendpayment().PaymentID 1261 1262 detailsRes, err := tcs[0].Srv.GetGenericPaymentDetailsLocal(context.Background(), stellar1.GetGenericPaymentDetailsLocalArg{ 1263 // Chat/Loader does not know account IDs, just payment IDs. 1264 // It derives delta and formatting (whether it's a debit or 1265 // credit) by checking chat message sender and receiver. 1266 Id: paymentID, 1267 }) 1268 require.NoError(t, err) 1269 require.Equal(t, stellar1.BalanceDelta_NONE, detailsRes.Summary.Delta) 1270 require.Equal(t, "505.6120000 XLM", detailsRes.Summary.AmountDescription) 1271 require.Equal(t, "$160.93 USD", detailsRes.Summary.Worth) 1272 require.Equal(t, "", detailsRes.Summary.WorthAtSendTime) 1273 require.Equal(t, secretNote, detailsRes.Summary.Note) 1274 require.Equal(t, "", detailsRes.Summary.NoteErr) 1275 } 1276 1277 func TestBuildRequestLocal(t *testing.T) { 1278 tcs, cleanup := setupNTests(t, 2) 1279 defer cleanup() 1280 1281 acceptDisclaimer(tcs[0]) 1282 worthInfo := "$1.00 = 3.1414139 XLM\nSource: coinmarketcap.com" 1283 1284 bres, err := tcs[0].Srv.BuildRequestLocal(context.Background(), stellar1.BuildRequestLocalArg{ 1285 To: tcs[1].Fu.Username, 1286 }) 1287 require.NoError(t, err) 1288 t.Logf(spew.Sdump(bres)) 1289 require.Equal(t, false, bres.ReadyToRequest) 1290 require.Equal(t, "", bres.ToErrMsg) 1291 require.Equal(t, "", bres.AmountErrMsg) 1292 require.Equal(t, "", bres.SecretNoteErrMsg) 1293 require.Equal(t, "$0.00 USD", bres.WorthDescription) 1294 require.Equal(t, worthInfo, bres.WorthInfo) 1295 require.True(t, bres.SendingIntentionXLM) 1296 require.Equal(t, "", bres.DisplayAmountXLM) 1297 require.Equal(t, "", bres.DisplayAmountFiat) 1298 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1299 1300 bres, err = tcs[0].Srv.BuildRequestLocal(context.Background(), stellar1.BuildRequestLocalArg{ 1301 To: tcs[1].Fu.Username, 1302 Amount: "-1", 1303 }) 1304 require.NoError(t, err) 1305 t.Logf(spew.Sdump(bres)) 1306 require.Equal(t, false, bres.ReadyToRequest) 1307 require.Equal(t, "", bres.ToErrMsg) 1308 require.Equal(t, "Invalid amount.", bres.AmountErrMsg) 1309 require.Equal(t, "", bres.SecretNoteErrMsg) 1310 require.Equal(t, "", bres.WorthDescription) 1311 require.Equal(t, "", bres.WorthInfo) 1312 require.True(t, bres.SendingIntentionXLM) 1313 require.Equal(t, "", bres.DisplayAmountXLM) 1314 require.Equal(t, "", bres.DisplayAmountFiat) 1315 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1316 1317 bres, err = tcs[0].Srv.BuildRequestLocal(context.Background(), stellar1.BuildRequestLocalArg{ 1318 To: tcs[1].Fu.Username, 1319 Amount: "15", 1320 }) 1321 require.NoError(t, err) 1322 t.Logf(spew.Sdump(bres)) 1323 require.Equal(t, true, bres.ReadyToRequest) 1324 require.Equal(t, "", bres.ToErrMsg) 1325 require.Equal(t, "", bres.AmountErrMsg) 1326 require.Equal(t, "", bres.SecretNoteErrMsg) 1327 require.Equal(t, "$4.77 USD", bres.WorthDescription) 1328 require.Equal(t, worthInfo, bres.WorthInfo) 1329 require.True(t, bres.SendingIntentionXLM) 1330 require.Equal(t, "15 XLM", bres.DisplayAmountXLM) 1331 require.Equal(t, "$4.77 USD", bres.DisplayAmountFiat) 1332 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1333 1334 t.Logf("requesting in amount composed in USD") 1335 bres, err = tcs[0].Srv.BuildRequestLocal(context.Background(), stellar1.BuildRequestLocalArg{ 1336 To: tcs[1].Fu.Username, 1337 Amount: "8.50", 1338 Currency: &usd, 1339 }) 1340 require.NoError(t, err) 1341 t.Logf(spew.Sdump(bres)) 1342 require.Equal(t, true, bres.ReadyToRequest) 1343 require.Equal(t, "", bres.ToErrMsg) 1344 require.Equal(t, "", bres.AmountErrMsg) 1345 require.Equal(t, "", bres.SecretNoteErrMsg) 1346 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1347 require.Equal(t, worthInfo, bres.WorthInfo) 1348 require.False(t, bres.SendingIntentionXLM) 1349 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1350 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1351 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1352 } 1353 1354 func TestBuildPaymentLocal(t *testing.T) { 1355 tcs, cleanup := setupNTests(t, 2) 1356 defer cleanup() 1357 1358 acceptDisclaimer(tcs[0]) 1359 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 1360 require.NoError(t, err) 1361 1362 senderSecondaryAccountID, err := tcs[0].Srv.CreateWalletAccountLocal(context.Background(), stellar1.CreateWalletAccountLocalArg{ 1363 Name: "second", 1364 }) 1365 require.NoError(t, err) 1366 1367 worthInfo := "$1.00 = 3.1414139 XLM\nSource: coinmarketcap.com" 1368 1369 for _, toIsAccountID := range []bool{false, true} { 1370 t.Logf("toIsAccountID: %v", toIsAccountID) 1371 bres, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1372 From: senderAccountID, 1373 ToIsAccountID: toIsAccountID, 1374 }) 1375 require.NoError(t, err) 1376 t.Logf(spew.Sdump(bres)) 1377 require.Equal(t, false, bres.ReadyToReview) 1378 require.Equal(t, "", bres.ToErrMsg) 1379 require.Equal(t, "", bres.AmountErrMsg) 1380 require.Equal(t, "", bres.SecretNoteErrMsg) 1381 require.Equal(t, "", bres.PublicMemoErrMsg) 1382 require.Equal(t, "$0.00 USD", bres.WorthDescription) 1383 require.Equal(t, "USD", bres.WorthCurrency) 1384 require.Equal(t, worthInfo, bres.WorthInfo) 1385 require.True(t, bres.SendingIntentionXLM) 1386 require.Equal(t, "", bres.DisplayAmountXLM) 1387 require.Equal(t, "", bres.DisplayAmountFiat) 1388 requireBannerSet(t, bres.DeepCopy().Banners, nil) 1389 } 1390 1391 acceptDisclaimer(tcs[1]) 1392 1393 bres, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1394 From: senderAccountID, 1395 To: tcs[1].Fu.Username, 1396 }) 1397 require.NoError(t, err) 1398 t.Logf(spew.Sdump(bres)) 1399 require.Equal(t, false, bres.ReadyToReview) 1400 require.Equal(t, "", bres.ToErrMsg) 1401 require.Equal(t, "", bres.AmountErrMsg) 1402 require.Equal(t, "", bres.SecretNoteErrMsg) 1403 require.Equal(t, "", bres.PublicMemoErrMsg) 1404 require.Equal(t, "$0.00 USD", bres.WorthDescription) 1405 require.Equal(t, worthInfo, bres.WorthInfo) 1406 require.True(t, bres.SendingIntentionXLM) 1407 require.Equal(t, "", bres.DisplayAmountXLM) 1408 require.Equal(t, "", bres.DisplayAmountFiat) 1409 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1410 Level: "info", 1411 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1412 }}) 1413 1414 recipientAccountID := getPrimaryAccountID(tcs[1]) 1415 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1416 From: senderAccountID, 1417 To: recipientAccountID.String(), 1418 ToIsAccountID: true, 1419 }) 1420 require.NoError(t, err) 1421 t.Logf(spew.Sdump(bres)) 1422 require.Equal(t, false, bres.ReadyToReview) 1423 require.Equal(t, "", bres.ToErrMsg) 1424 require.Equal(t, "", bres.AmountErrMsg) 1425 require.Equal(t, "", bres.SecretNoteErrMsg) 1426 require.Equal(t, "", bres.PublicMemoErrMsg) 1427 require.Equal(t, "$0.00 USD", bres.WorthDescription) 1428 require.True(t, bres.SendingIntentionXLM) 1429 require.Equal(t, "", bres.DisplayAmountXLM) 1430 require.Equal(t, "", bres.DisplayAmountFiat) 1431 require.Equal(t, worthInfo, bres.WorthInfo) 1432 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1433 Level: "info", 1434 Message: "Because it's their first transaction, you must send at least 1 XLM.", 1435 }}) 1436 1437 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1438 From: senderAccountID, 1439 To: tcs[1].Fu.Username, 1440 Amount: "-1", 1441 }) 1442 require.NoError(t, err) 1443 t.Logf(spew.Sdump(bres)) 1444 require.Equal(t, false, bres.ReadyToReview) 1445 require.Equal(t, "", bres.ToErrMsg) 1446 require.Equal(t, "Invalid amount.", bres.AmountErrMsg) 1447 require.Equal(t, "", bres.SecretNoteErrMsg) 1448 require.Equal(t, "", bres.PublicMemoErrMsg) 1449 require.Equal(t, "", bres.WorthDescription) 1450 require.Equal(t, "", bres.WorthInfo) 1451 require.True(t, bres.SendingIntentionXLM) 1452 require.Equal(t, "", bres.DisplayAmountXLM) 1453 require.Equal(t, "", bres.DisplayAmountFiat) 1454 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1455 Level: "info", 1456 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1457 }}) 1458 1459 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1460 From: senderAccountID, 1461 To: tcs[1].Fu.Username, 1462 Amount: "30", 1463 }) 1464 require.NoError(t, err) 1465 t.Logf(spew.Sdump(bres)) 1466 require.Equal(t, false, bres.ReadyToReview) 1467 require.Equal(t, "", bres.ToErrMsg) 1468 require.Equal(t, "You have *0 XLM* available to send.", bres.AmountErrMsg) 1469 require.Equal(t, "", bres.SecretNoteErrMsg) 1470 require.Equal(t, "", bres.PublicMemoErrMsg) 1471 require.Equal(t, "$9.55 USD", bres.WorthDescription) 1472 require.Equal(t, worthInfo, bres.WorthInfo) 1473 require.True(t, bres.SendingIntentionXLM) 1474 require.Equal(t, "30 XLM", bres.DisplayAmountXLM) 1475 require.Equal(t, "$9.55 USD", bres.DisplayAmountFiat) 1476 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1477 Level: "info", 1478 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1479 }}) 1480 1481 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1482 From: senderAccountID, 1483 To: tcs[1].Fu.Username, 1484 Amount: "30", 1485 Currency: &usd, 1486 }) 1487 require.NoError(t, err) 1488 t.Logf(spew.Sdump(bres)) 1489 require.Equal(t, false, bres.ReadyToReview) 1490 require.Equal(t, "", bres.ToErrMsg) 1491 require.Equal(t, "You have *$0.00 USD* worth of Lumens available to send.", bres.AmountErrMsg) 1492 require.Equal(t, "", bres.SecretNoteErrMsg) 1493 require.Equal(t, "", bres.PublicMemoErrMsg) 1494 require.Equal(t, "94.2424166 XLM", bres.WorthDescription) 1495 require.Equal(t, worthInfo, bres.WorthInfo) 1496 require.False(t, bres.SendingIntentionXLM) 1497 require.Equal(t, "94.2424166 XLM", bres.DisplayAmountXLM) 1498 require.Equal(t, "$30.00 USD", bres.DisplayAmountFiat) 1499 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1500 Level: "info", 1501 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1502 }}) 1503 1504 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 1505 tcs[0].Backend.Gift(senderAccountID, "20") 1506 tcs[0].Backend.Gift(senderSecondaryAccountID, "30") 1507 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 1508 require.NoError(t, err) 1509 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderSecondaryAccountID, "test") 1510 require.NoError(t, err) 1511 1512 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1513 From: senderAccountID, 1514 To: tcs[1].Fu.Username, 1515 Amount: "30", 1516 }) 1517 require.NoError(t, err) 1518 t.Logf(spew.Sdump(bres)) 1519 require.Equal(t, false, bres.ReadyToReview) 1520 require.Equal(t, "", bres.ToErrMsg) 1521 require.Equal(t, "You only have *18.9999900 XLM* available to send.", bres.AmountErrMsg) 1522 require.Equal(t, "", bres.SecretNoteErrMsg) 1523 require.Equal(t, "", bres.PublicMemoErrMsg) 1524 require.Equal(t, "$9.55 USD", bres.WorthDescription) 1525 require.Equal(t, worthInfo, bres.WorthInfo) 1526 require.True(t, bres.SendingIntentionXLM) 1527 require.Equal(t, "30 XLM", bres.DisplayAmountXLM) 1528 require.Equal(t, "$9.55 USD", bres.DisplayAmountFiat) 1529 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1530 Level: "info", 1531 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1532 }}) 1533 1534 // The user's available-to-send is $6.0482288 which rounds up to $6.05. 1535 // Avoid the situation where you type $6.05 and it responds "your ATS is $6.05" (CORE-9338, related PICNIC-464) 1536 // Which while true in some way true, is not helpful for the user. 1537 // A user should always be able to send the string that is presented as their ATS. 1538 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1539 From: senderAccountID, 1540 To: tcs[1].Fu.Username, 1541 Amount: "6.05", 1542 Currency: &usd, 1543 }) 1544 require.NoError(t, err) 1545 t.Logf(spew.Sdump(bres)) 1546 require.Equal(t, false, bres.ReadyToReview) 1547 require.Equal(t, "", bres.ToErrMsg) 1548 require.Equal(t, "You only have *$6.04 USD* worth of Lumens available to send.", bres.AmountErrMsg) 1549 require.Equal(t, "", bres.SecretNoteErrMsg) 1550 require.Equal(t, "", bres.PublicMemoErrMsg) 1551 require.Equal(t, "19.0055540 XLM", bres.WorthDescription) 1552 require.Equal(t, worthInfo, bres.WorthInfo) 1553 require.False(t, bres.SendingIntentionXLM) 1554 require.Equal(t, "19.0055540 XLM", bres.DisplayAmountXLM) 1555 require.Equal(t, "$6.05 USD", bres.DisplayAmountFiat) 1556 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1557 Level: "info", 1558 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1559 }}) 1560 1561 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1562 From: senderAccountID, 1563 To: recipientAccountID.String(), 1564 ToIsAccountID: true, 1565 Amount: "0.01", 1566 }) 1567 require.NoError(t, err) 1568 t.Logf(spew.Sdump(bres)) 1569 require.Equal(t, false, bres.ReadyToReview) 1570 require.Equal(t, "", bres.ToErrMsg) 1571 require.Equal(t, "You must send at least *1 XLM*", bres.AmountErrMsg) 1572 require.Equal(t, "", bres.SecretNoteErrMsg) 1573 require.Equal(t, "", bres.PublicMemoErrMsg) 1574 require.Equal(t, "$0.00 USD", bres.WorthDescription) 1575 require.Equal(t, worthInfo, bres.WorthInfo) 1576 require.True(t, bres.SendingIntentionXLM) 1577 require.Equal(t, "0.0100000 XLM", bres.DisplayAmountXLM) 1578 require.Equal(t, "$0.00 USD", bres.DisplayAmountFiat) 1579 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1580 Level: "info", 1581 Message: "Because it's their first transaction, you must send at least 1 XLM.", 1582 }}) 1583 1584 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1585 From: senderSecondaryAccountID, 1586 To: "t_alice", 1587 Amount: "15", 1588 }) 1589 require.NoError(t, err) 1590 t.Logf(spew.Sdump(bres)) 1591 require.Equal(t, false, bres.ReadyToReview) 1592 require.Equal(t, "", bres.ToErrMsg) 1593 require.Equal(t, "", bres.AmountErrMsg) 1594 require.Equal(t, "", bres.SecretNoteErrMsg) 1595 require.Equal(t, "", bres.PublicMemoErrMsg) 1596 require.Equal(t, "$4.77 USD", bres.WorthDescription) 1597 require.Equal(t, worthInfo, bres.WorthInfo) 1598 require.True(t, bres.SendingIntentionXLM) 1599 require.Equal(t, "15 XLM", bres.DisplayAmountXLM) 1600 require.Equal(t, "$4.77 USD", bres.DisplayAmountFiat) 1601 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1602 Level: "error", 1603 Message: "Because t_alice hasn’t set up their wallet yet, you can only send to them from your default account.", 1604 }}) 1605 1606 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1607 From: senderAccountID, 1608 To: tcs[1].Fu.Username, 1609 Amount: "15", 1610 }) 1611 require.NoError(t, err) 1612 t.Logf(spew.Sdump(bres)) 1613 require.Equal(t, true, bres.ReadyToReview) 1614 require.Equal(t, "", bres.ToErrMsg) 1615 require.Equal(t, "", bres.AmountErrMsg) 1616 require.Equal(t, "", bres.SecretNoteErrMsg) 1617 require.Equal(t, "", bres.PublicMemoErrMsg) 1618 require.Equal(t, "$4.77 USD", bres.WorthDescription) 1619 require.Equal(t, worthInfo, bres.WorthInfo) 1620 require.True(t, bres.SendingIntentionXLM) 1621 require.Equal(t, "15 XLM", bres.DisplayAmountXLM) 1622 require.Equal(t, "$4.77 USD", bres.DisplayAmountFiat) 1623 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1624 Level: "info", 1625 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1626 }}) 1627 // and from non-primary account has an additional privacy banner 1628 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1629 From: senderSecondaryAccountID, 1630 To: tcs[1].Fu.Username, 1631 Amount: "15", 1632 }) 1633 require.NoError(t, err) 1634 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1635 Level: "info", 1636 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 1 XLM.", tcs[1].Fu.Username), 1637 }, { 1638 Level: "info", 1639 Message: "Your Keybase username will not be linked to this transaction.", 1640 }}) 1641 1642 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 1643 BypassBid: true, 1644 From: senderAccountID, 1645 To: tcs[1].Fu.Username, 1646 Amount: "15", 1647 Asset: stellar1.AssetNative(), 1648 }) 1649 require.NoError(t, err) 1650 1651 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1652 From: senderAccountID, 1653 To: tcs[1].Fu.Username, 1654 Amount: "15", 1655 PublicMemo: "🥔🥔🥔🥔🥔🥔🥔🥔", 1656 }) 1657 require.NoError(t, err) 1658 t.Logf(spew.Sdump(bres)) 1659 require.Equal(t, false, bres.ReadyToReview) 1660 require.Equal(t, "", bres.ToErrMsg) 1661 require.Equal(t, "You only have *3.9999800 XLM* available to send.", bres.AmountErrMsg) 1662 require.Equal(t, "", bres.SecretNoteErrMsg) 1663 require.Equal(t, "Memo is too long.", bres.PublicMemoErrMsg) // too many potatoes 1664 require.Equal(t, "$4.77 USD", bres.WorthDescription) 1665 require.Equal(t, worthInfo, bres.WorthInfo) 1666 require.True(t, bres.SendingIntentionXLM) 1667 require.Equal(t, "15 XLM", bres.DisplayAmountXLM) 1668 require.Equal(t, "$4.77 USD", bres.DisplayAmountFiat) 1669 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) // recipient is funded so banner's gone 1670 // and from non-primary account has a privacy banner 1671 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1672 From: senderSecondaryAccountID, 1673 To: tcs[1].Fu.Username, 1674 Amount: "15", 1675 PublicMemo: "🥔🥔🥔🥔🥔🥔🥔🥔", 1676 }) 1677 require.NoError(t, err) 1678 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1679 Level: "info", 1680 Message: "Your Keybase username will not be linked to this transaction.", 1681 }}) 1682 1683 // Send an amount so close to available to send that the fee would push it it over the edge. 1684 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1685 From: senderAccountID, 1686 To: tcs[1].Fu.Username, 1687 Amount: "3.99999900", 1688 }) 1689 require.NoError(t, err) 1690 t.Logf(spew.Sdump(bres)) 1691 require.Equal(t, false, bres.ReadyToReview) 1692 require.Equal(t, "", bres.ToErrMsg) 1693 require.Equal(t, "You only have *3.9999800 XLM* available to send.", bres.AmountErrMsg) 1694 require.Equal(t, "", bres.SecretNoteErrMsg) 1695 require.Equal(t, "", bres.PublicMemoErrMsg) 1696 require.Equal(t, "$1.27 USD", bres.WorthDescription) 1697 require.Equal(t, worthInfo, bres.WorthInfo) 1698 require.True(t, bres.SendingIntentionXLM) 1699 require.Equal(t, "3.9999990 XLM", bres.DisplayAmountXLM) 1700 require.Equal(t, "$1.27 USD", bres.DisplayAmountFiat) 1701 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1702 1703 tcs[0].Backend.Gift(senderAccountID, "30") 1704 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 1705 require.NoError(t, err) 1706 1707 t.Logf("sending in amount composed in USD") 1708 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1709 From: senderAccountID, 1710 To: tcs[1].Fu.Username, 1711 Amount: "8.50", 1712 Currency: &usd, 1713 }) 1714 require.NoError(t, err) 1715 t.Logf(spew.Sdump(bres)) 1716 require.Equal(t, true, bres.ReadyToReview) 1717 require.Equal(t, senderAccountID, bres.From) 1718 require.Equal(t, "", bres.ToErrMsg) 1719 require.Equal(t, "", bres.AmountErrMsg) 1720 require.Equal(t, "", bres.SecretNoteErrMsg) 1721 require.Equal(t, "", bres.PublicMemoErrMsg) 1722 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1723 require.Equal(t, worthInfo, bres.WorthInfo) 1724 require.Equal(t, "26.7020180", bres.WorthAmount) 1725 require.False(t, bres.SendingIntentionXLM) 1726 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1727 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1728 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1729 1730 t.Logf("using `fromDefaultAccount`") 1731 for _, x := range []string{"blank", "match", "wrong"} { 1732 var from stellar1.AccountID 1733 var fromRes stellar1.AccountID 1734 shouldFail := false 1735 switch x { 1736 case "blank": 1737 fromRes = senderAccountID 1738 case "match": 1739 from = senderAccountID 1740 fromRes = senderAccountID 1741 shouldFail = true 1742 case "wrong": 1743 otherAccountID, _ := randomStellarKeypair() 1744 from = otherAccountID 1745 shouldFail = true 1746 default: 1747 panic("bad case") 1748 } 1749 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1750 From: from, 1751 FromPrimaryAccount: true, 1752 To: tcs[1].Fu.Username, 1753 Amount: "8.50", 1754 Currency: &usd, 1755 }) 1756 if shouldFail { 1757 require.Error(t, err) 1758 require.Equal(t, "invalid build payment parameters", err.Error()) 1759 } else { 1760 require.NoError(t, err) 1761 t.Logf(spew.Sdump(bres)) 1762 require.Equal(t, true, bres.ReadyToReview) 1763 require.Equal(t, fromRes, bres.From, x) 1764 require.Equal(t, "", bres.ToErrMsg) 1765 require.Equal(t, "", bres.AmountErrMsg) 1766 require.Equal(t, "", bres.SecretNoteErrMsg) 1767 require.Equal(t, "", bres.PublicMemoErrMsg) 1768 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1769 require.Equal(t, worthInfo, bres.WorthInfo) 1770 require.False(t, bres.SendingIntentionXLM) 1771 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1772 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1773 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1774 } 1775 } 1776 1777 t.Logf("sending to account ID") 1778 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1779 From: senderAccountID, 1780 To: "GBJCIIIWEP2ZIKSNY3AP5GJ5OHNSN6Y4W5K4IVIY4VSQF5QLVE27GADK", 1781 ToIsAccountID: true, 1782 Amount: "8.50", 1783 Currency: &usd, 1784 }) 1785 require.NoError(t, err) 1786 t.Logf(spew.Sdump(bres)) 1787 require.Equal(t, true, bres.ReadyToReview) 1788 require.Equal(t, "", bres.ToErrMsg) 1789 require.Equal(t, "", bres.AmountErrMsg) 1790 require.Equal(t, "", bres.SecretNoteErrMsg) 1791 require.Equal(t, "", bres.PublicMemoErrMsg) 1792 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1793 require.Equal(t, worthInfo, bres.WorthInfo) 1794 require.False(t, bres.SendingIntentionXLM) 1795 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1796 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1797 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1798 Level: "info", 1799 Message: "Because it's their first transaction, you must send at least 1 XLM.", 1800 }}) 1801 // and from non-primary account has an additional privacy banner 1802 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1803 From: senderSecondaryAccountID, 1804 To: "GBJCIIIWEP2ZIKSNY3AP5GJ5OHNSN6Y4W5K4IVIY4VSQF5QLVE27GADK", 1805 ToIsAccountID: true, 1806 Amount: "8.50", 1807 Currency: &usd, 1808 }) 1809 require.NoError(t, err) 1810 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1811 Level: "info", 1812 Message: "Because it's their first transaction, you must send at least 1 XLM.", 1813 }, { 1814 Level: "info", 1815 Message: "Your Keybase username will not be linked to this transaction.", 1816 }}) 1817 1818 t.Logf("sending to account ID that is someone's primary") 1819 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1820 From: senderAccountID, 1821 To: senderAccountID.String(), 1822 ToIsAccountID: true, 1823 Amount: "8.50", 1824 Currency: &usd, 1825 }) 1826 require.NoError(t, err) 1827 t.Logf(spew.Sdump(bres)) 1828 require.Equal(t, true, bres.ReadyToReview) 1829 require.Equal(t, "", bres.ToErrMsg) 1830 require.Equal(t, "", bres.AmountErrMsg) 1831 require.Equal(t, "", bres.SecretNoteErrMsg) 1832 require.Equal(t, "", bres.PublicMemoErrMsg) 1833 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1834 require.Equal(t, worthInfo, bres.WorthInfo) 1835 require.False(t, bres.SendingIntentionXLM) 1836 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1837 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1838 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1839 1840 upak, _, err := tcs[0].G.GetUPAKLoader().LoadV2( 1841 libkb.NewLoadUserArgWithMetaContext(tcs[0].MetaContext()).WithPublicKeyOptional(). 1842 WithUID(tcs[1].Fu.User.GetUID()).WithForcePoll(true)) 1843 require.NoError(t, err) 1844 require.NotNil(t, upak.Current.StellarAccountID) 1845 1846 t.Logf("sending to account ID that is someone's primary") 1847 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1848 From: senderAccountID, 1849 To: *upak.Current.StellarAccountID, 1850 ToIsAccountID: true, 1851 Amount: "8.50", 1852 Currency: &usd, 1853 }) 1854 require.NoError(t, err) 1855 t.Logf(spew.Sdump(bres)) 1856 require.Equal(t, true, bres.ReadyToReview) 1857 require.Equal(t, "", bres.ToErrMsg) 1858 require.Equal(t, "", bres.AmountErrMsg) 1859 require.Equal(t, "", bres.SecretNoteErrMsg) 1860 require.Equal(t, "", bres.PublicMemoErrMsg) 1861 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1862 require.Equal(t, worthInfo, bres.WorthInfo) 1863 require.False(t, bres.SendingIntentionXLM) 1864 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1865 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1866 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1867 } 1868 1869 // Regression test for a case where, under the given balance and exchange parameters, 1870 // an attempt to send $2.32 would respond that you only had $2.32 available to send. 1871 // Which didn't make sense. (was PICNIC-464, related CORE-9338) 1872 func TestBuildPaymentLocalATSRounding(t *testing.T) { 1873 tcs, cleanup := setupNTests(t, 1) 1874 defer cleanup() 1875 1876 acceptDisclaimer(tcs[0]) 1877 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 1878 require.NoError(t, err) 1879 1880 tcs[0].Backend.SetExchangeRate("0.0622381942426") 1881 worthInfo := "$1.00 = 16.0673042 XLM\nSource: coinmarketcap.com" 1882 1883 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 1884 tcs[0].Backend.Gift(senderAccountID, "38.2713786") 1885 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 1886 require.NoError(t, err) 1887 1888 bres, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1889 From: senderAccountID, 1890 To: "t_alice", 1891 Amount: "2.32", 1892 Currency: &usd, 1893 }) 1894 require.NoError(t, err) 1895 t.Logf(spew.Sdump(bres)) 1896 require.Equal(t, false, bres.ReadyToReview) 1897 require.Equal(t, "", bres.ToErrMsg) 1898 // Before the fix, AmountErrMsg had $2.32 1899 require.Equal(t, "You only have *$2.31 USD* worth of Lumens available to send.", bres.AmountErrMsg) 1900 require.Equal(t, "", bres.SecretNoteErrMsg) 1901 require.Equal(t, "", bres.PublicMemoErrMsg) 1902 require.Equal(t, "37.2761458 XLM", bres.WorthDescription) 1903 require.Equal(t, worthInfo, bres.WorthInfo) 1904 require.False(t, bres.SendingIntentionXLM) 1905 require.Equal(t, "37.2761458 XLM", bres.DisplayAmountXLM) 1906 require.Equal(t, "$2.32 USD", bres.DisplayAmountFiat) 1907 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1908 Level: "info", 1909 Message: fmt.Sprintf("Because it's %v's first transaction, you must send at least 2.01 XLM.", "t_alice"), 1910 }}) 1911 } 1912 1913 func TestBuildPaymentLocalAdvancedBanner(t *testing.T) { 1914 tcs, cleanup := setupNTests(t, 4) 1915 defer cleanup() 1916 1917 acceptDisclaimer(tcs[0]) 1918 acceptDisclaimer(tcs[1]) 1919 acceptDisclaimer(tcs[2]) 1920 acceptDisclaimer(tcs[3]) 1921 fakeAcct := tcs[0].Backend.ImportAccountsForUser(tcs[0])[0] 1922 fakeAcct2 := tcs[1].Backend.ImportAccountsForUser(tcs[1])[0] 1923 fakeAcct3 := tcs[2].Backend.ImportAccountsForUser(tcs[2])[0] 1924 fakeAcct4 := tcs[3].Backend.ImportAccountsForUser(tcs[3])[0] 1925 tcs[0].Backend.Gift(fakeAcct.accountID, "100") 1926 tcs[0].Backend.Gift(fakeAcct2.accountID, "100") 1927 tcs[0].Backend.Gift(fakeAcct3.accountID, "100") 1928 tcs[0].Backend.Gift(fakeAcct4.accountID, "100") 1929 1930 err := tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), fakeAcct.accountID, "test") 1931 require.NoError(t, err) 1932 err = tcs[1].Srv.walletState.Refresh(tcs[1].MetaContext(), fakeAcct2.accountID, "test") 1933 require.NoError(t, err) 1934 err = tcs[2].Srv.walletState.Refresh(tcs[2].MetaContext(), fakeAcct3.accountID, "test") 1935 require.NoError(t, err) 1936 err = tcs[3].Srv.walletState.Refresh(tcs[3].MetaContext(), fakeAcct4.accountID, "test") 1937 require.NoError(t, err) 1938 1939 t.Logf("sending from one account to another that only have native assets") 1940 bres, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1941 From: fakeAcct.accountID, 1942 To: fakeAcct2.accountID.String(), 1943 ToIsAccountID: true, 1944 Amount: "8.50", 1945 Currency: &usd, 1946 }) 1947 require.NoError(t, err) 1948 t.Logf(spew.Sdump(bres)) 1949 require.Equal(t, true, bres.ReadyToReview) 1950 require.Equal(t, "", bres.ToErrMsg) 1951 require.Equal(t, "", bres.AmountErrMsg) 1952 require.Equal(t, "", bres.SecretNoteErrMsg) 1953 require.Equal(t, "", bres.PublicMemoErrMsg) 1954 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1955 require.False(t, bres.SendingIntentionXLM) 1956 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1957 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1958 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{}) 1959 1960 t.Logf("sending from an account with non-native assets to an account with only native assets") 1961 astro := tcs[0].Backend.CreateFakeAsset("AstroDollars") 1962 fakeAcct.AdjustAssetBalance(0, astro) 1963 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), fakeAcct.accountID, "test") 1964 require.NoError(t, err) 1965 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1966 From: fakeAcct.accountID, 1967 To: fakeAcct3.accountID.String(), 1968 ToIsAccountID: true, 1969 Amount: "8.50", 1970 Currency: &usd, 1971 }) 1972 require.NoError(t, err) 1973 t.Logf(spew.Sdump(bres)) 1974 require.Equal(t, true, bres.ReadyToReview) 1975 require.Equal(t, "", bres.ToErrMsg) 1976 require.Equal(t, "", bres.AmountErrMsg) 1977 require.Equal(t, "", bres.SecretNoteErrMsg) 1978 require.Equal(t, "", bres.PublicMemoErrMsg) 1979 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 1980 require.False(t, bres.SendingIntentionXLM) 1981 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 1982 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 1983 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 1984 Level: "info", 1985 OfferAdvancedSendForm: stellar1.AdvancedBanner_SENDER_BANNER, 1986 }}) 1987 1988 t.Logf("sending from an account with only native assets to an account with non-native assets") 1989 bres, err = tcs[2].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 1990 From: fakeAcct3.accountID, 1991 To: fakeAcct.accountID.String(), 1992 ToIsAccountID: true, 1993 Amount: "8.50", 1994 Currency: &usd, 1995 }) 1996 require.NoError(t, err) 1997 t.Logf(spew.Sdump(bres)) 1998 require.Equal(t, true, bres.ReadyToReview) 1999 require.Equal(t, "", bres.ToErrMsg) 2000 require.Equal(t, "", bres.AmountErrMsg) 2001 require.Equal(t, "", bres.SecretNoteErrMsg) 2002 require.Equal(t, "", bres.PublicMemoErrMsg) 2003 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 2004 require.False(t, bres.SendingIntentionXLM) 2005 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 2006 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 2007 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 2008 Level: "info", 2009 OfferAdvancedSendForm: stellar1.AdvancedBanner_RECEIVER_BANNER, 2010 }}) 2011 2012 t.Logf("sending from an account with non-native assets to an account with the same non-native asset") 2013 fakeAcct4.AdjustAssetBalance(0, astro) 2014 err = tcs[3].Srv.walletState.Refresh(tcs[3].MetaContext(), fakeAcct4.accountID, "test") 2015 require.NoError(t, err) 2016 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 2017 From: fakeAcct.accountID, 2018 To: fakeAcct4.accountID.String(), 2019 ToIsAccountID: true, 2020 Amount: "8.50", 2021 Currency: &usd, 2022 }) 2023 require.NoError(t, err) 2024 t.Logf(spew.Sdump(bres)) 2025 require.Equal(t, true, bres.ReadyToReview) 2026 require.Equal(t, "", bres.ToErrMsg) 2027 require.Equal(t, "", bres.AmountErrMsg) 2028 require.Equal(t, "", bres.SecretNoteErrMsg) 2029 require.Equal(t, "", bres.PublicMemoErrMsg) 2030 require.Equal(t, "26.7020180 XLM", bres.WorthDescription) 2031 require.False(t, bres.SendingIntentionXLM) 2032 require.Equal(t, "26.7020180 XLM", bres.DisplayAmountXLM) 2033 require.Equal(t, "$8.50 USD", bres.DisplayAmountFiat) 2034 requireBannerSet(t, bres.DeepCopy().Banners, []stellar1.SendBannerLocal{{ 2035 Level: "info", 2036 OfferAdvancedSendForm: stellar1.AdvancedBanner_RECEIVER_BANNER, 2037 }}) 2038 } 2039 2040 // Simple happy path case. 2041 func TestBuildPaymentLocalBidHappy(t *testing.T) { 2042 testBuildPaymentLocalBidHappy(t, false) 2043 } 2044 2045 func TestBuildPaymentLocalBidHappyBypassReview(t *testing.T) { 2046 testBuildPaymentLocalBidHappy(t, true) 2047 } 2048 2049 func testBuildPaymentLocalBidHappy(t *testing.T, bypassReview bool) { 2050 t.Logf("BypassReview:%v", bypassReview) 2051 2052 tcs, cleanup := setupNTests(t, 2) 2053 defer cleanup() 2054 2055 acceptDisclaimer(tcs[0]) 2056 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 2057 require.NoError(t, err) 2058 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2059 tcs[0].Backend.Gift(senderAccountID, "100") 2060 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 2061 require.NoError(t, err) 2062 2063 bid1, err := tcs[0].Srv.StartBuildPaymentLocal(context.Background(), 0) 2064 require.NoError(t, err) 2065 2066 bres, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 2067 Bid: bid1, 2068 From: senderAccountID, 2069 To: tcs[1].Fu.Username, 2070 Amount: "11", 2071 }) 2072 require.NoError(t, err) 2073 t.Logf(spew.Sdump(bres)) 2074 require.Equal(t, true, bres.ReadyToReview) 2075 2076 t.Logf("Change the amount") 2077 bres, err = tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 2078 Bid: bid1, 2079 From: senderAccountID, 2080 To: tcs[1].Fu.Username, 2081 Amount: "15", 2082 }) 2083 require.NoError(t, err) 2084 t.Logf(spew.Sdump(bres)) 2085 require.Equal(t, true, bres.ReadyToReview) 2086 2087 if !bypassReview { 2088 reviewPaymentExpectQuickSuccess(t, tcs[0], stellar1.ReviewPaymentLocalArg{ 2089 Bid: bid1, 2090 }) 2091 } 2092 2093 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 2094 Bid: bid1, 2095 BypassReview: bypassReview, 2096 From: senderAccountID, 2097 To: tcs[1].Fu.Username, 2098 Amount: "15", 2099 Asset: stellar1.AssetNative(), 2100 }) 2101 require.NoError(t, err) 2102 } 2103 2104 func reviewPaymentExpectQuickSuccess(t testing.TB, tc *TestContext, arg stellar1.ReviewPaymentLocalArg) { 2105 start := time.Now() 2106 timeout := time.Second 2107 if libkb.UseCITime(tc.G) { 2108 timeout = 15 * time.Second 2109 } 2110 timeoutCh := time.After(timeout) 2111 mockUI := tc.Srv.uiSource.(*testUISource).stellarUI.(*mockStellarUI) 2112 original := mockUI.PaymentReviewedHandler 2113 defer func() { 2114 mockUI.PaymentReviewedHandler = original 2115 }() 2116 reviewSuccessCh := make(chan struct{}, 1) 2117 reviewExitedCh := make(chan struct{}, 1) 2118 mockUI.PaymentReviewedHandler = func(ctx context.Context, notification stellar1.PaymentReviewedArg) error { 2119 assert.Equal(t, arg.Bid, notification.Msg.Bid) 2120 // Allow the not-following warning banner. 2121 if len(notification.Msg.Banners) == 1 { 2122 assert.True(t, regexp.MustCompile( 2123 `^You are not following .*\. Are you sure this is the right person\?$`, 2124 ).MatchString(notification.Msg.Banners[0].Message)) 2125 expect := []stellar1.SendBannerLocal{{ 2126 Level: "warning", 2127 Message: notification.Msg.Banners[0].Message, 2128 }} 2129 assert.Equal(t, expect, notification.Msg.Banners) 2130 } else { 2131 assert.Len(t, notification.Msg.Banners, 0) 2132 } 2133 switch notification.Msg.NextButton { 2134 case "spinning": 2135 case "enabled": 2136 select { 2137 case reviewSuccessCh <- struct{}{}: 2138 default: 2139 } 2140 default: 2141 assert.Failf(t, "unexpected button status", "%v", notification.Msg.NextButton) 2142 } 2143 return nil 2144 } 2145 go func() { 2146 err := tc.Srv.ReviewPaymentLocal(context.Background(), arg) 2147 assert.NoError(t, err) // Use 'assert' since 'require' can only be used from the main goroutine. 2148 reviewExitedCh <- struct{}{} 2149 }() 2150 select { 2151 case <-timeoutCh: 2152 assert.Fail(t, "timed out") 2153 case <-reviewSuccessCh: 2154 } 2155 select { 2156 case <-timeoutCh: 2157 assert.Fail(t, "timed out") 2158 case <-reviewExitedCh: 2159 } 2160 t.Logf("review ran for %v", time.Since(start)) 2161 check(t) 2162 } 2163 2164 func reviewPaymentExpectContractFailure(t testing.TB, tc *TestContext, arg stellar1.ReviewPaymentLocalArg, msg string) { 2165 start := time.Now() 2166 timeout := time.Second 2167 if libkb.UseCITime(tc.G) { 2168 timeout = 15 * time.Second 2169 } 2170 timeoutCh := time.After(timeout) 2171 mockUI := tc.Srv.uiSource.(*testUISource).stellarUI.(*mockStellarUI) 2172 original := mockUI.PaymentReviewedHandler 2173 defer func() { 2174 mockUI.PaymentReviewedHandler = original 2175 }() 2176 mockUI.PaymentReviewedHandler = func(ctx context.Context, notification stellar1.PaymentReviewedArg) error { 2177 assert.Equal(t, arg.Bid, notification.Msg.Bid) 2178 switch notification.Msg.NextButton { 2179 case "spinning": 2180 case "disabled": 2181 default: 2182 assert.Failf(t, "unexpected button status", "%v", notification.Msg.NextButton) 2183 } 2184 return nil 2185 } 2186 reviewFinishCh := make(chan error, 1) 2187 go func() { 2188 err := tc.Srv.ReviewPaymentLocal(context.Background(), arg) 2189 reviewFinishCh <- err 2190 }() 2191 select { 2192 case <-timeoutCh: 2193 assert.Fail(t, "timed out") 2194 case err := <-reviewFinishCh: 2195 require.Error(t, err) 2196 require.Equal(t, msg, err.Error()) 2197 } 2198 t.Logf("review ran for %v", time.Since(start)) 2199 check(t) 2200 } 2201 2202 // Start a review. Expect that the review will send a broken tracking banner and then hang. 2203 // `expectSuccess` can be called later after fixing the tracking and will assert that the review soon enables the button. 2204 func reviewPaymentExpectBrokenTracking(t testing.TB, tc *TestContext, arg stellar1.ReviewPaymentLocalArg) (expectSuccess func()) { 2205 start := time.Now() 2206 timeout := time.Second 2207 if libkb.UseCITime(tc.G) { 2208 timeout = 15 * time.Second 2209 } 2210 timeoutCh := time.After(timeout) 2211 mockUI := tc.Srv.uiSource.(*testUISource).stellarUI.(*mockStellarUI) 2212 original := mockUI.PaymentReviewedHandler 2213 reviewDisabledCh := make(chan struct{}, 1) 2214 // Install a handler that's geared for track failures. 2215 mockUI.PaymentReviewedHandler = func(ctx context.Context, notification stellar1.PaymentReviewedArg) error { 2216 assert.Equal(t, arg.Bid, notification.Msg.Bid) 2217 switch notification.Msg.NextButton { 2218 case "spinning": 2219 case "disabled": 2220 select { 2221 case reviewDisabledCh <- struct{}{}: 2222 default: 2223 assert.Fail(t, "review disabled channel full") 2224 } 2225 default: 2226 assert.Failf(t, "unexpected button status", "%v", notification.Msg.NextButton) 2227 } 2228 return nil 2229 } 2230 reviewFinishCh := make(chan error, 1) 2231 go func() { 2232 err := tc.Srv.ReviewPaymentLocal(context.Background(), arg) 2233 reviewFinishCh <- err 2234 }() 2235 select { 2236 case <-timeoutCh: 2237 assert.Fail(t, "timed out") 2238 case err := <-reviewFinishCh: 2239 require.FailNowf(t, "review unexpectedly finished", "%v", err) 2240 case <-reviewDisabledCh: 2241 // great 2242 } 2243 t.Logf("review ran for %v", time.Since(start)) 2244 check(t) 2245 2246 // Install a new handler that's geared for success. 2247 reviewEnabledCh := make(chan struct{}, 1) 2248 mockUI.PaymentReviewedHandler = func(ctx context.Context, notification stellar1.PaymentReviewedArg) error { 2249 assert.Equal(t, arg.Bid, notification.Msg.Bid) 2250 switch notification.Msg.NextButton { 2251 case "enabled": 2252 select { 2253 case reviewEnabledCh <- struct{}{}: 2254 default: 2255 assert.Fail(t, "review enabled channel full") 2256 } 2257 default: 2258 assert.Failf(t, "unexpected button status", "%v", notification.Msg.NextButton) 2259 } 2260 return nil 2261 } 2262 return func() { 2263 defer func() { 2264 mockUI.PaymentReviewedHandler = original 2265 }() 2266 timeoutCh := time.After(timeout) 2267 select { 2268 case <-timeoutCh: 2269 require.FailNow(t, "timed out") 2270 case <-reviewEnabledCh: 2271 // great 2272 } 2273 check(t) 2274 } 2275 } 2276 2277 // Review a payment. 2278 // - At first the review fails on a tracking failure. 2279 // - The user reaffirms their tracking of the recipient. 2280 // - As a result the review succeeds. 2281 func TestReviewPaymentLocal(t *testing.T) { 2282 tcs, cleanup := setupNTests(t, 2) 2283 defer cleanup() 2284 2285 acceptDisclaimer(tcs[0]) 2286 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 2287 require.NoError(t, err) 2288 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2289 tcs[0].Backend.Gift(senderAccountID, "100") 2290 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 2291 require.NoError(t, err) 2292 2293 t.Logf("u1 proves rooter") 2294 _, sigID := proveRooter(tcs[1]) 2295 2296 t.Logf("u0 tracks u1") 2297 _, err = kbtest.RunTrack(tcs[0].TestContext, tcs[0].Fu, tcs[1].Fu.Username) 2298 require.NoError(t, err) 2299 2300 t.Logf("u1 removes their proof") 2301 eng := engine.NewRevokeSigsEngine(tcs[1].G, []string{sigID.String()}) 2302 err = engine.RunEngine2(tcs[1].MetaContext().WithUIs(libkb.UIs{ 2303 LogUI: tcs[1].G.UI.GetLogUI(), 2304 SecretUI: &libkb.TestSecretUI{Passphrase: "dummy-passphrase"}, 2305 }), eng) 2306 require.NoError(t, err) 2307 2308 t.Logf("u0 starts a payment") 2309 bid1, err := tcs[0].Srv.StartBuildPaymentLocal(context.Background(), 0) 2310 require.NoError(t, err) 2311 amount := "11.0" 2312 buildRes, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 2313 Bid: bid1, 2314 From: senderAccountID, 2315 To: tcs[1].Fu.Username, 2316 Amount: amount, 2317 }) 2318 require.NoError(t, err) 2319 require.Equal(t, true, buildRes.ReadyToReview) 2320 2321 t.Logf("u0 starts review of a payment, which gets stuck on u1's broken proof") 2322 expectSuccess := reviewPaymentExpectBrokenTracking(t, tcs[0], stellar1.ReviewPaymentLocalArg{Bid: bid1}) 2323 2324 t.Logf("u0 affirms tracking of u1, causing the review to complete") 2325 _, err = kbtest.RunTrack(tcs[0].TestContext, tcs[0].Fu, tcs[1].Fu.Username) 2326 require.NoError(t, err) 2327 expectSuccess() 2328 2329 t.Logf("u0 completes the send") 2330 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 2331 Bid: bid1, 2332 From: senderAccountID, 2333 To: tcs[1].Fu.Username, 2334 Amount: amount, 2335 Asset: stellar1.AssetNative(), 2336 }) 2337 require.NoError(t, err) 2338 } 2339 2340 // Review a payment where recipient is a keybase.io federation address. 2341 // - At first the review fails on a tracking failure. 2342 // - The user reaffirms their tracking of the recipient. 2343 // - As a result the review succeeds. 2344 func TestKeybaseFederationReviewPaymentLocal(t *testing.T) { 2345 tcs, cleanup := setupNTests(t, 2) 2346 defer cleanup() 2347 2348 acceptDisclaimer(tcs[0]) 2349 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 2350 require.NoError(t, err) 2351 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2352 tcs[0].Backend.Gift(senderAccountID, "100") 2353 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 2354 require.NoError(t, err) 2355 2356 t.Logf("u1 proves rooter") 2357 _, sigID := proveRooter(tcs[1]) 2358 2359 t.Logf("u0 tracks u1") 2360 _, err = kbtest.RunTrack(tcs[0].TestContext, tcs[0].Fu, tcs[1].Fu.Username) 2361 require.NoError(t, err) 2362 2363 t.Logf("u1 removes their proof") 2364 eng := engine.NewRevokeSigsEngine(tcs[1].G, []string{sigID.String()}) 2365 err = engine.RunEngine2(tcs[1].MetaContext().WithUIs(libkb.UIs{ 2366 LogUI: tcs[1].G.UI.GetLogUI(), 2367 SecretUI: &libkb.TestSecretUI{Passphrase: "dummy-passphrase"}, 2368 }), eng) 2369 require.NoError(t, err) 2370 2371 t.Logf("u0 starts a payment") 2372 bid1, err := tcs[0].Srv.StartBuildPaymentLocal(context.Background(), 0) 2373 require.NoError(t, err) 2374 amount := "11.0" 2375 buildRes, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 2376 Bid: bid1, 2377 From: senderAccountID, 2378 To: tcs[1].Fu.Username + "*keybase.io", 2379 Amount: amount, 2380 }) 2381 require.NoError(t, err) 2382 require.Equal(t, true, buildRes.ReadyToReview) 2383 2384 t.Logf("u0 starts review of a payment, which gets stuck on u1's broken proof") 2385 expectSuccess := reviewPaymentExpectBrokenTracking(t, tcs[0], stellar1.ReviewPaymentLocalArg{Bid: bid1}) 2386 2387 t.Logf("u0 affirms tracking of u1, causing the review to complete") 2388 _, err = kbtest.RunTrack(tcs[0].TestContext, tcs[0].Fu, tcs[1].Fu.Username) 2389 require.NoError(t, err) 2390 expectSuccess() 2391 2392 t.Logf("u0 completes the send") 2393 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 2394 Bid: bid1, 2395 From: senderAccountID, 2396 To: tcs[1].Fu.Username + "*keybase.io", 2397 Amount: amount, 2398 Asset: stellar1.AssetNative(), 2399 }) 2400 require.NoError(t, err) 2401 } 2402 2403 // Review a payment where recipient is an SBS twitter user. 2404 func TestReviewPaymentLocalSBS(t *testing.T) { 2405 tcs, cleanup := setupNTests(t, 2) 2406 defer cleanup() 2407 2408 acceptDisclaimer(tcs[0]) 2409 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 2410 require.NoError(t, err) 2411 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2412 tcs[0].Backend.Gift(senderAccountID, "100") 2413 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 2414 require.NoError(t, err) 2415 2416 t.Logf("u0 starts a payment") 2417 bid1, err := tcs[0].Srv.StartBuildPaymentLocal(context.Background(), 0) 2418 require.NoError(t, err) 2419 amount := "11.0" 2420 buildRes, err := tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 2421 Bid: bid1, 2422 From: senderAccountID, 2423 To: "torproject@twitter", 2424 Amount: amount, 2425 }) 2426 require.NoError(t, err) 2427 require.Equal(t, true, buildRes.ReadyToReview) 2428 2429 t.Logf("u0 starts a review of the payment") 2430 reviewPaymentExpectQuickSuccess(t, tcs[0], stellar1.ReviewPaymentLocalArg{Bid: bid1}) 2431 2432 t.Logf("u0 completes the send") 2433 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 2434 Bid: bid1, 2435 From: senderAccountID, 2436 To: "torproject@twitter", 2437 Amount: amount, 2438 Asset: stellar1.AssetNative(), 2439 }) 2440 require.NoError(t, err) 2441 } 2442 2443 // Cases where Send is blocked because the build gamut wasn't run. 2444 func TestBuildPaymentLocalBidBlocked(t *testing.T) { 2445 tcs, cleanup := setupNTests(t, 2) 2446 defer cleanup() 2447 2448 acceptDisclaimer(tcs[0]) 2449 acceptDisclaimer(tcs[1]) 2450 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 2451 require.NoError(t, err) 2452 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2453 tcs[0].Backend.Gift(senderAccountID, "100") 2454 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), senderAccountID, "test") 2455 require.NoError(t, err) 2456 fakeAcct := tcs[1].Backend.ImportAccountsForUser(tcs[1])[0] 2457 err = tcs[1].Srv.walletState.Refresh(tcs[1].MetaContext(), fakeAcct.accountID, "test") 2458 require.NoError(t, err) 2459 2460 send := func(bid stellar1.BuildPaymentID, amount string) (errorString string) { 2461 _, err = tcs[0].Srv.SendPaymentLocal(context.Background(), stellar1.SendPaymentLocalArg{ 2462 Bid: bid, 2463 From: senderAccountID, 2464 To: tcs[1].Fu.Username, 2465 Amount: amount, 2466 Asset: stellar1.AssetNative(), 2467 }) 2468 if err != nil { 2469 errorString = err.Error() 2470 require.NotEqual(t, "", errorString, "empty error string") 2471 return errorString 2472 } 2473 return "" 2474 } 2475 2476 build := func(bid stellar1.BuildPaymentID, amount string) (res stellar1.BuildPaymentResLocal, err error) { 2477 return tcs[0].Srv.BuildPaymentLocal(context.Background(), stellar1.BuildPaymentLocalArg{ 2478 Bid: bid, 2479 From: senderAccountID, 2480 To: tcs[1].Fu.Username, 2481 Amount: amount, 2482 }) 2483 } 2484 2485 units := []struct { 2486 Key string 2487 Description string 2488 }{ 2489 { 2490 Key: "forgotBuild", 2491 Description: "Can't send without a successful build. Also can't review without a build.", 2492 }, { 2493 Key: "forgotReview", 2494 Description: "Can't send before review", 2495 }, { 2496 Key: "forgotBoth", 2497 Description: "Can't send before precheck and review", 2498 }, { 2499 Key: "wrongAmount", 2500 Description: "Can't send with wrong amount", 2501 }, { 2502 Key: "afterStoppedByFailedSend", 2503 Description: "Can't send after stopped (by failed send)", 2504 }, { 2505 Key: "afterStoppedBySend", 2506 Description: "Can't send after stopped (by successful send)", 2507 }, { 2508 Key: "afterStoppedByStop", 2509 Description: "Can't send after stopped (by stop call)", 2510 }, { 2511 Key: "afterStopppedByStopThenBuild", 2512 Description: "Can't send after stopped (by stop call) even after build", 2513 }, { 2514 Key: "build-review-build-send", 2515 Description: "Can't send without a review after the _latest_ send", 2516 }, 2517 } 2518 2519 for _, unit := range units { 2520 t.Logf("unit %v: %v", unit.Key, unit.Description) 2521 bid1, err := tcs[0].Srv.StartBuildPaymentLocal(context.Background(), 0) 2522 require.NoError(t, err) 2523 2524 reviewExpectContractFailure := func(msg string) { 2525 reviewPaymentExpectContractFailure(t, tcs[0], stellar1.ReviewPaymentLocalArg{Bid: bid1}, msg) 2526 } 2527 reviewExpectQuickSuccess := func() { 2528 reviewPaymentExpectQuickSuccess(t, tcs[0], stellar1.ReviewPaymentLocalArg{Bid: bid1}) 2529 } 2530 2531 switch unit.Key { 2532 case "forgotBuild": 2533 reviewExpectContractFailure("this payment is not ready to review") 2534 2535 errString := send(bid1, "11") 2536 require.Equal(t, "this payment is not ready to send", errString) 2537 2538 case "forgotReview": 2539 bres, err := build(bid1, "12") 2540 require.NoError(t, err) 2541 require.Equal(t, true, bres.ReadyToReview) 2542 2543 errString := send(bid1, "11") 2544 require.Equal(t, "this payment has not been reviewed", errString) 2545 2546 case "forgotBoth": 2547 errString := send(bid1, "12") 2548 require.Equal(t, "this payment is not ready to send", errString) 2549 2550 case "wrongAmount": 2551 bres, err := build(bid1, "12") 2552 require.NoError(t, err) 2553 require.Equal(t, true, bres.ReadyToReview) 2554 2555 reviewExpectQuickSuccess() 2556 2557 errString := send(bid1, "15") 2558 require.Equal(t, "mismatched amount: 15 != 12", errString) 2559 2560 case "afterStoppedByFailedSend": 2561 bres, err := build(bid1, "12") 2562 require.NoError(t, err) 2563 require.Equal(t, true, bres.ReadyToReview) 2564 2565 reviewExpectQuickSuccess() 2566 2567 errString := send(bid1, "15") 2568 require.Equal(t, "mismatched amount: 15 != 12", errString) 2569 2570 errString = send(bid1, "15") 2571 require.Equal(t, "This payment might have already been sent. Check your recent payments before trying again.", errString) 2572 2573 case "afterStoppedBySend": 2574 bres, err := build(bid1, "11") 2575 require.NoError(t, err) 2576 require.Equal(t, true, bres.ReadyToReview) 2577 2578 reviewExpectQuickSuccess() 2579 2580 errString := send(bid1, "11") 2581 require.Equal(t, "", errString) 2582 2583 errString = send(bid1, "11") 2584 require.Equal(t, "This payment might have already been sent. Check your recent payments before trying again.", errString) 2585 2586 case "afterStoppedByStop": 2587 errString := send(bid1, "11") 2588 require.Equal(t, "this payment is not ready to send", errString) 2589 2590 err = tcs[0].Srv.StopBuildPaymentLocal(context.Background(), stellar1.StopBuildPaymentLocalArg{Bid: bid1}) 2591 require.NoError(t, err) 2592 2593 errString = send(bid1, "11") 2594 require.Equal(t, "This payment might have already been sent. Check your recent payments before trying again.", errString) 2595 2596 case "afterStopppedByStopThenBuild": 2597 errString := send(bid1, "11") 2598 require.Equal(t, "this payment is not ready to send", errString) 2599 2600 err = tcs[0].Srv.StopBuildPaymentLocal(context.Background(), stellar1.StopBuildPaymentLocalArg{Bid: bid1}) 2601 require.NoError(t, err) 2602 2603 _, err := build(bid1, "11") 2604 _ = err // Calling build on a stopped payment is do-no-harm undefined behavior. 2605 2606 reviewExpectContractFailure("This payment might have already been sent. Check your recent payments before trying again.") // Calling review on a stopped payment is do-no-harm not gonna happen. 2607 2608 errString = send(bid1, "11") 2609 require.Equal(t, "This payment might have already been sent. Check your recent payments before trying again.", errString) 2610 2611 case "build-review-build-send": 2612 bres, err := build(bid1, "12") 2613 require.NoError(t, err) 2614 require.Equal(t, true, bres.ReadyToReview) 2615 2616 reviewExpectQuickSuccess() 2617 2618 bres, err = build(bid1, "15") 2619 require.NoError(t, err) 2620 require.Equal(t, true, bres.ReadyToReview) 2621 2622 errString := send(bid1, "15") 2623 require.Equal(t, "this payment has not been reviewed", errString) 2624 2625 default: 2626 t.Fatalf("unknown case %v", unit.Key) 2627 } 2628 } 2629 } 2630 2631 // modifies `expected` 2632 func requireBannerSet(t testing.TB, got []stellar1.SendBannerLocal, expected []stellar1.SendBannerLocal) { 2633 if len(got) != len(expected) { 2634 t.Logf("%s", spew.Sdump(got)) 2635 require.Len(t, got, len(expected)) 2636 } 2637 sort.Slice(got, func(i, j int) bool { 2638 return got[i].Message < got[j].Message 2639 }) 2640 sort.Slice(expected, func(i, j int) bool { 2641 return expected[i].Message < expected[j].Message 2642 }) 2643 for i := range expected { 2644 require.Equal(t, expected[i], got[i]) 2645 } 2646 } 2647 2648 var usd = stellar1.OutsideCurrencyCode("USD") 2649 2650 func TestGetSendAssetChoices(t *testing.T) { 2651 tcs, cleanup := setupNTests(t, 2) 2652 defer cleanup() 2653 2654 acceptDisclaimer(tcs[0]) 2655 acceptDisclaimer(tcs[1]) 2656 fakeAccts := tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2657 fakeAccts2 := tcs[1].Backend.ImportAccountsForUser(tcs[1]) 2658 2659 // Empty account (not even on the network), expecting to see 0 2660 // other assets here. 2661 choices, err := tcs[0].Srv.GetSendAssetChoicesLocal(context.Background(), stellar1.GetSendAssetChoicesLocalArg{ 2662 From: fakeAccts[0].accountID, 2663 }) 2664 require.NoError(t, err) 2665 require.Len(t, choices, 0) 2666 2667 // Same with `To` argument. 2668 choices, err = tcs[0].Srv.GetSendAssetChoicesLocal(context.Background(), stellar1.GetSendAssetChoicesLocalArg{ 2669 From: fakeAccts[0].accountID, 2670 To: tcs[1].Fu.Username, 2671 }) 2672 require.NoError(t, err) 2673 require.Len(t, choices, 0) 2674 2675 // Test assets 2676 keys := tcs[0].Backend.CreateFakeAsset("KEYS") 2677 astro := tcs[0].Backend.CreateFakeAsset("AstroDollars") 2678 2679 // Adjust balance with 0 adds empty balance of given asset (mock 2680 // "open a trustline"). 2681 fakeAccts[0].AdjustAssetBalance(0, keys) 2682 fakeAccts[0].AdjustAssetBalance(0, astro) 2683 2684 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), fakeAccts[0].accountID, "test") 2685 require.NoError(t, err) 2686 2687 // New asset choices should be visible 2688 choices, err = tcs[0].Srv.GetSendAssetChoicesLocal(context.Background(), stellar1.GetSendAssetChoicesLocalArg{ 2689 From: fakeAccts[0].accountID, 2690 }) 2691 require.NoError(t, err) 2692 require.Len(t, choices, 2) 2693 require.Equal(t, keys, choices[0].Asset) 2694 require.Equal(t, astro, choices[1].Asset) 2695 for _, v := range choices { 2696 require.Equal(t, v.Asset.Code, v.Left) 2697 require.Equal(t, v.Asset.Issuer, v.Right) 2698 require.True(t, v.Enabled) 2699 } 2700 2701 // We should see the same choices, but all disabled because the 2702 // recipient does not accept them. 2703 choices2, err := tcs[0].Srv.GetSendAssetChoicesLocal(context.Background(), stellar1.GetSendAssetChoicesLocalArg{ 2704 From: fakeAccts[0].accountID, 2705 To: tcs[1].Fu.Username, 2706 }) 2707 require.NoError(t, err) 2708 require.Len(t, choices2, len(choices)) 2709 for i, v := range choices2 { 2710 require.Equal(t, choices[i].Asset, v.Asset) 2711 require.Equal(t, v.Asset.Code, v.Left) 2712 require.Equal(t, v.Asset.Issuer, v.Right) 2713 require.False(t, v.Enabled) 2714 require.Contains(t, v.Subtext, tcs[1].Fu.Username) 2715 require.Contains(t, v.Subtext, "does not accept") 2716 require.Contains(t, v.Subtext, v.Asset.Code) 2717 } 2718 2719 // Open AstroDollars for tcs[1] 2720 fakeAccts2[0].AdjustAssetBalance(0, astro) 2721 2722 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), fakeAccts[0].accountID, "test") 2723 require.NoError(t, err) 2724 2725 choices2, err = tcs[0].Srv.GetSendAssetChoicesLocal(context.Background(), stellar1.GetSendAssetChoicesLocalArg{ 2726 From: fakeAccts[0].accountID, 2727 To: fakeAccts2[0].accountID.String(), // this time use account ID as `To` argument 2728 }) 2729 require.NoError(t, err) 2730 require.Len(t, choices2, len(choices)) 2731 2732 require.Equal(t, keys, choices2[0].Asset) 2733 require.False(t, choices2[0].Enabled) 2734 require.Equal(t, choices2[0].Subtext, fmt.Sprintf("Recipient does not accept %v", choices2[0].Asset.Code)) 2735 2736 require.Equal(t, astro, choices2[1].Asset) 2737 require.True(t, choices2[1].Enabled) 2738 2739 // Try with arg.To AccountID not in the system. 2740 externalAcc := tcs[0].Backend.AddAccount(tcs[0].Fu.GetUID()) 2741 choices3, err := tcs[0].Srv.GetSendAssetChoicesLocal(context.Background(), stellar1.GetSendAssetChoicesLocalArg{ 2742 From: fakeAccts[0].accountID, 2743 To: externalAcc.String(), 2744 }) 2745 require.NoError(t, err) 2746 require.Len(t, choices3, len(choices)) 2747 for _, v := range choices3 { 2748 require.False(t, v.Enabled) 2749 require.Contains(t, v.Subtext, "Recipient does not accept") 2750 } 2751 } 2752 2753 func TestMakeRequestLocalBasics(t *testing.T) { 2754 tcs, cleanup := setupNTests(t, 2) 2755 defer cleanup() 2756 acceptDisclaimer(tcs[0]) 2757 2758 xlm := stellar1.AssetNative() 2759 _, err := tcs[0].Srv.MakeRequestLocal(context.Background(), stellar1.MakeRequestLocalArg{ 2760 Recipient: tcs[1].Fu.Username, 2761 Asset: &xlm, 2762 }) 2763 require.Error(t, err) 2764 2765 _, err = tcs[0].Srv.MakeRequestLocal(context.Background(), stellar1.MakeRequestLocalArg{ 2766 Recipient: tcs[1].Fu.Username, 2767 Asset: &xlm, 2768 Amount: "0", 2769 }) 2770 require.Error(t, err) 2771 2772 _, err = tcs[0].Srv.MakeRequestLocal(context.Background(), stellar1.MakeRequestLocalArg{ 2773 Recipient: tcs[1].Fu.Username, 2774 Asset: &xlm, 2775 Amount: "-1.2345", 2776 }) 2777 require.Error(t, err) 2778 2779 reqID, err := tcs[0].Srv.MakeRequestLocal(context.Background(), stellar1.MakeRequestLocalArg{ 2780 Recipient: tcs[1].Fu.Username, 2781 Asset: &xlm, 2782 Amount: "1.2345", 2783 }) 2784 require.NoError(t, err) 2785 require.NotEmpty(t, reqID) 2786 } 2787 2788 func TestMakeRequestLocalNotifications(t *testing.T) { 2789 tcs, cleanup := setupNTests(t, 2) 2790 defer cleanup() 2791 acceptDisclaimer(tcs[0]) 2792 acceptDisclaimer(tcs[1]) 2793 2794 // set up notification listeners 2795 listenerSender := newChatListener() 2796 listenerRecip := newChatListener() 2797 tcs[0].G.NotifyRouter.AddListener(listenerSender) 2798 tcs[1].G.NotifyRouter.AddListener(listenerRecip) 2799 2800 xlm := stellar1.AssetNative() 2801 reqID, err := tcs[0].Srv.MakeRequestLocal(context.Background(), stellar1.MakeRequestLocalArg{ 2802 Recipient: tcs[1].Fu.Username, 2803 Asset: &xlm, 2804 Amount: "1.2345", 2805 }) 2806 require.NoError(t, err) 2807 require.NotEmpty(t, reqID) 2808 2809 // pretend that the chat message was unboxed and call the request loader to load the info: 2810 loaderSender := stellar.DefaultLoader(tcs[0].G) 2811 convID := chat1.ConversationID("efef") 2812 msgID := chat1.MessageID(654) 2813 loaderSender.LoadRequest(context.Background(), convID, msgID, tcs[0].Fu.Username, reqID) 2814 2815 loaderRecip := stellar.NewLoader(tcs[1].G) 2816 loaderRecip.LoadRequest(context.Background(), convID, msgID, tcs[0].Fu.Username, reqID) 2817 2818 // check the sender chat notification 2819 select { 2820 case info := <-listenerSender.requestInfos: 2821 require.NotNil(t, info) 2822 require.Equal(t, tcs[0].Fu.User.GetUID(), info.Uid) 2823 require.Equal(t, convID, info.ConvID) 2824 require.Equal(t, msgID, info.MsgID) 2825 require.Equal(t, "1.2345", info.Info.Amount) 2826 require.Equal(t, "1.2345 XLM", info.Info.AmountDescription) 2827 require.NotNil(t, info.Info.Asset) 2828 require.Equal(t, "native", info.Info.Asset.Type) 2829 require.Nil(t, info.Info.Currency) 2830 require.Equal(t, stellar1.RequestStatus_OK, info.Info.Status) 2831 case <-time.After(20 * time.Second): 2832 t.Fatal("timed out waiting for chat request info notification to sender") 2833 } 2834 2835 // check the recipient chat notification 2836 select { 2837 case info := <-listenerRecip.requestInfos: 2838 require.NotNil(t, info) 2839 require.Equal(t, tcs[1].Fu.User.GetUID(), info.Uid) 2840 require.Equal(t, convID, info.ConvID) 2841 require.Equal(t, msgID, info.MsgID) 2842 require.Equal(t, "1.2345", info.Info.Amount) 2843 require.Equal(t, "1.2345 XLM", info.Info.AmountDescription) 2844 require.NotNil(t, info.Info.Asset) 2845 require.Equal(t, "native", info.Info.Asset.Type) 2846 require.Nil(t, info.Info.Currency) 2847 require.Equal(t, stellar1.RequestStatus_OK, info.Info.Status) 2848 case <-time.After(20 * time.Second): 2849 t.Fatal("timed out waiting for chat request info notification to sender") 2850 } 2851 2852 // load it again, should not get another notification 2853 loaderRecip.LoadRequest(context.Background(), convID, msgID, tcs[0].Fu.Username, reqID) 2854 select { 2855 case info := <-listenerRecip.requestInfos: 2856 t.Fatalf("received request notification on second load: %+v", info) 2857 case <-time.After(100 * time.Millisecond): 2858 } 2859 2860 } 2861 2862 func TestSetMobileOnly(t *testing.T) { 2863 tcs, cleanup := setupTestsWithSettings(t, []usetting{usettingMobile}) 2864 defer cleanup() 2865 2866 makeActiveDeviceOlder(t, tcs[0].G) 2867 setupWithNewBundle(t, tcs[0]) 2868 2869 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2870 accountID := getPrimaryAccountID(tcs[0]) 2871 walletAcctLocalArg := stellar1.GetWalletAccountLocalArg{AccountID: accountID} 2872 2873 // assert not mobile only yet 2874 mobileOnly, err := tcs[0].Srv.IsAccountMobileOnlyLocal(context.Background(), stellar1.IsAccountMobileOnlyLocalArg{AccountID: accountID}) 2875 require.NoError(t, err) 2876 require.False(t, mobileOnly) 2877 accs, err := tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 2878 require.NoError(t, err) 2879 require.Len(t, accs, 1) 2880 require.Equal(t, accs[0].AccountMode, stellar1.AccountMode_USER) 2881 details, err := tcs[0].Srv.GetWalletAccountLocal(context.Background(), walletAcctLocalArg) 2882 require.NoError(t, err) 2883 require.Equal(t, stellar1.AccountMode_USER, details.AccountMode) 2884 require.Equal(t, true, details.AccountModeEditable) 2885 require.Equal(t, false, details.DeviceReadOnly) 2886 2887 err = tcs[0].Srv.SetAccountMobileOnlyLocal(context.Background(), stellar1.SetAccountMobileOnlyLocalArg{AccountID: accountID}) 2888 require.NoError(t, err) 2889 2890 // yes mobile only 2891 mobileOnly, err = tcs[0].Srv.IsAccountMobileOnlyLocal(context.Background(), stellar1.IsAccountMobileOnlyLocalArg{AccountID: accountID}) 2892 require.NoError(t, err) 2893 require.True(t, mobileOnly) 2894 accs, err = tcs[0].Srv.WalletGetAccountsCLILocal(context.Background()) 2895 require.NoError(t, err) 2896 require.Len(t, accs, 1) 2897 require.Equal(t, accs[0].AccountMode, stellar1.AccountMode_MOBILE) 2898 details, err = tcs[0].Srv.GetWalletAccountLocal(context.Background(), walletAcctLocalArg) 2899 require.NoError(t, err) 2900 require.Equal(t, stellar1.AccountMode_MOBILE, details.AccountMode) 2901 require.Equal(t, true, details.AccountModeEditable) 2902 require.Equal(t, false, details.DeviceReadOnly) 2903 2904 mode, err := tcs[0].Srv.walletState.AccountMode(accountID) 2905 require.NoError(t, err) 2906 require.Equal(t, stellar1.AccountMode_MOBILE, mode) 2907 2908 // service_test verifies that `SetAccountMobileOnlyLocal` behaves correctly under the covers 2909 2910 // Provision new mobile to check AccountModeEditable and DeviceReadOnly 2911 tc2, cleanup2 := provisionNewDeviceForTest(t, tcs[0], keybase1.DeviceTypeV2_MOBILE) 2912 defer cleanup2() 2913 details, err = tc2.Srv.GetWalletAccountLocal(context.Background(), walletAcctLocalArg) 2914 require.NoError(t, err) 2915 require.Equal(t, stellar1.AccountMode_MOBILE, details.AccountMode) 2916 require.Equal(t, false, details.AccountModeEditable) 2917 require.Equal(t, true, details.DeviceReadOnly) 2918 2919 // Provision new desktop device. 2920 tc3, cleanup3 := provisionNewDeviceForTest(t, tcs[0], keybase1.DeviceTypeV2_DESKTOP) 2921 defer cleanup3() 2922 details, err = tc3.Srv.GetWalletAccountLocal(context.Background(), walletAcctLocalArg) 2923 require.NoError(t, err) 2924 require.Equal(t, stellar1.AccountMode_MOBILE, details.AccountMode) 2925 require.Equal(t, false, details.AccountModeEditable) 2926 require.Equal(t, true, details.DeviceReadOnly) 2927 } 2928 2929 const lumenautAccID = stellar1.AccountID("GCCD6AJOYZCUAQLX32ZJF2MKFFAUJ53PVCFQI3RHWKL3V47QYE2BNAUT") 2930 2931 func TestSetInflation(t *testing.T) { 2932 tcs, cleanup := setupNTests(t, 1) 2933 defer cleanup() 2934 2935 // Test to see if RPCs are reaching remote (mocks). 2936 acceptDisclaimer(tcs[0]) 2937 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 2938 require.NoError(t, err) 2939 2940 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 2941 tcs[0].Backend.Gift(senderAccountID, "20") 2942 2943 getInflation := func() stellar1.InflationDestinationResultLocal { 2944 res, err := tcs[0].Srv.GetInflationDestinationLocal(context.Background(), stellar1.GetInflationDestinationLocalArg{ 2945 AccountID: senderAccountID, 2946 }) 2947 require.NoError(t, err) 2948 return res 2949 } 2950 res := getInflation() 2951 require.Nil(t, res.Destination) 2952 require.Nil(t, res.KnownDestination) 2953 require.False(t, res.Self) 2954 2955 pub, _ := randomStellarKeypair() 2956 err = tcs[0].Srv.SetInflationDestinationLocal(context.Background(), stellar1.SetInflationDestinationLocalArg{ 2957 AccountID: senderAccountID, 2958 Destination: pub, 2959 }) 2960 require.NoError(t, err) 2961 2962 res = getInflation() 2963 require.NotNil(t, res.Destination) 2964 require.Equal(t, pub, *res.Destination) 2965 require.Nil(t, res.KnownDestination) 2966 require.False(t, res.Self) 2967 2968 err = tcs[0].Srv.SetInflationDestinationLocal(context.Background(), stellar1.SetInflationDestinationLocalArg{ 2969 AccountID: senderAccountID, 2970 Destination: senderAccountID, 2971 }) 2972 require.NoError(t, err) 2973 2974 res = getInflation() 2975 require.NotNil(t, res.Destination) 2976 require.Equal(t, senderAccountID, *res.Destination) 2977 require.Nil(t, res.KnownDestination) 2978 require.True(t, res.Self) 2979 2980 err = tcs[0].Srv.SetInflationDestinationLocal(context.Background(), stellar1.SetInflationDestinationLocalArg{ 2981 AccountID: senderAccountID, 2982 Destination: lumenautAccID, 2983 }) 2984 require.NoError(t, err) 2985 2986 res = getInflation() 2987 require.NotNil(t, res.Destination) 2988 require.Equal(t, lumenautAccID, *res.Destination) 2989 require.NotNil(t, res.KnownDestination) 2990 require.Equal(t, lumenautAccID, res.KnownDestination.AccountID) 2991 require.Equal(t, "https://pool.lumenaut.net/", res.KnownDestination.Url) 2992 require.False(t, res.Self) 2993 } 2994 2995 func TestGetInflationDestinations(t *testing.T) { 2996 tcs, cleanup := setupNTests(t, 1) 2997 defer cleanup() 2998 2999 acceptDisclaimer(tcs[0]) 3000 tcs[0].Backend.ImportAccountsForUser(tcs[0]) 3001 3002 // This hits server, check if result is not empty and if lumenaut pool is 3003 // there (this should not change). 3004 res, err := tcs[0].Srv.GetPredefinedInflationDestinationsLocal(context.Background(), 0) 3005 require.NoError(t, err) 3006 require.NotEmpty(t, res) 3007 var found bool 3008 for _, dest := range res { 3009 if dest.Tag == "lumenaut" { 3010 require.False(t, found, "expecting to find only one lumenaut") 3011 found = true 3012 require.Equal(t, lumenautAccID, dest.AccountID) 3013 require.Equal(t, "Lumenaut", dest.Name) 3014 require.True(t, dest.Recommended) 3015 require.Equal(t, "https://pool.lumenaut.net/", dest.Url) 3016 } 3017 } 3018 require.True(t, found, "expecting to find lumenaut in the list") 3019 } 3020 3021 func TestManageTrustlines(t *testing.T) { 3022 tcs, cleanup := setupNTests(t, 1) 3023 defer cleanup() 3024 3025 otherAccountID, _ := randomStellarKeypair() 3026 trustlines, err := tcs[0].Srv.GetTrustlinesLocal(context.Background(), stellar1.GetTrustlinesLocalArg{ 3027 AccountID: otherAccountID, 3028 }) 3029 require.NoError(t, err) 3030 require.Len(t, trustlines, 0) 3031 3032 acceptDisclaimer(tcs[0]) 3033 accounts := tcs[0].Backend.ImportAccountsForUser(tcs[0]) 3034 3035 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 3036 require.NoError(t, err) 3037 tcs[0].Backend.Gift(senderAccountID, "20") 3038 3039 keys := tcs[0].Backend.CreateFakeAsset("KEYS") 3040 trustlineArg := stellar1.Trustline{ 3041 AssetCode: stellar1.AssetCode(keys.Code), 3042 Issuer: stellar1.AccountID(keys.Issuer), 3043 } 3044 3045 // Add trustline to the account. 3046 err = tcs[0].Srv.AddTrustlineLocal(context.Background(), stellar1.AddTrustlineLocalArg{ 3047 AccountID: senderAccountID, 3048 Trustline: trustlineArg, 3049 Limit: "", 3050 }) 3051 require.NoError(t, err) 3052 3053 // Check if it shows up in GetAccountAssetsLocal 3054 balances, err := tcs[0].Srv.GetAccountAssetsLocal(context.Background(), stellar1.GetAccountAssetsLocalArg{ 3055 AccountID: senderAccountID, 3056 }) 3057 require.NoError(t, err) 3058 require.Len(t, balances, 2) 3059 3060 require.Equal(t, keys.Code, balances[1].AssetCode) 3061 require.Equal(t, keys.Code, balances[1].Name) 3062 require.Equal(t, keys.Issuer, balances[1].IssuerAccountID) 3063 require.Equal(t, "0", balances[1].BalanceTotal) 3064 require.Equal(t, "0", balances[1].BalanceAvailableToSend) 3065 3066 // Check if shows up it GetTrustlinesLocal 3067 balances2, err := tcs[0].Srv.GetTrustlinesLocal(context.Background(), stellar1.GetTrustlinesLocalArg{ 3068 AccountID: senderAccountID, 3069 }) 3070 require.NoError(t, err) 3071 require.Len(t, balances2, 1) 3072 require.Equal(t, keys, balances2[0].Asset) 3073 require.Equal(t, "0.0000000", balances2[0].Amount) 3074 require.Equal(t, "922337203685.4775807", balances2[0].Limit) // max limit 3075 3076 // Check if shows up it GetTrustlinesForRecipientLocal 3077 rtlines, err := tcs[0].Srv.GetTrustlinesForRecipientLocal(context.Background(), stellar1.GetTrustlinesForRecipientLocalArg{ 3078 Recipient: tcs[0].Fu.Username, 3079 }) 3080 require.NoError(t, err) 3081 require.Len(t, rtlines.Trustlines, 1) 3082 require.Equal(t, keys, rtlines.Trustlines[0].Asset) 3083 require.Equal(t, "0.0000000", rtlines.Trustlines[0].Amount) 3084 require.Equal(t, "922337203685.4775807", rtlines.Trustlines[0].Limit) // max limit 3085 require.Equal(t, rtlines.RecipientType, stellar1.ParticipantType_KEYBASE) 3086 3087 // Change limit. 3088 err = tcs[0].Srv.ChangeTrustlineLimitLocal(context.Background(), stellar1.ChangeTrustlineLimitLocalArg{ 3089 AccountID: senderAccountID, 3090 Trustline: trustlineArg, 3091 Limit: "100", 3092 }) 3093 require.NoError(t, err) 3094 // Check if our mock account has changed: 3095 require.Equal(t, "100.0000000", accounts[0].otherBalances[0].Limit) 3096 3097 // Check if new limit shows up in BalancesLocal. Limit is not currently 3098 // returned via GetAccountAssetsLocal. 3099 balances2, err = tcs[0].Srv.BalancesLocal(context.Background(), senderAccountID) 3100 require.NoError(t, err) 3101 require.Len(t, balances2, 2) 3102 require.True(t, balances2[0].Asset.IsNativeXLM()) 3103 require.Equal(t, "20.0000000", balances2[0].Amount) 3104 require.Equal(t, "", balances2[0].Limit) 3105 require.Equal(t, keys, balances2[1].Asset) 3106 require.Equal(t, "0.0000000", balances2[1].Amount) 3107 require.Equal(t, "100.0000000", balances2[1].Limit) 3108 3109 // Delete trustline. 3110 err = tcs[0].Srv.DeleteTrustlineLocal(context.Background(), stellar1.DeleteTrustlineLocalArg{ 3111 AccountID: senderAccountID, 3112 Trustline: trustlineArg, 3113 }) 3114 require.NoError(t, err) 3115 3116 // See if it's gone from GetAccountAssetsLocal. 3117 balances, err = tcs[0].Srv.GetAccountAssetsLocal(context.Background(), stellar1.GetAccountAssetsLocalArg{ 3118 AccountID: senderAccountID, 3119 }) 3120 require.NoError(t, err) 3121 require.Len(t, balances, 1) 3122 require.Equal(t, "Lumens", balances[0].Name) 3123 require.Equal(t, "Stellar network", balances[0].IssuerName) 3124 require.Equal(t, "", balances[0].IssuerAccountID) 3125 } 3126 3127 func TestManageTrustlinesErrors(t *testing.T) { 3128 tcs, cleanup := setupNTests(t, 1) 3129 defer cleanup() 3130 3131 acceptDisclaimer(tcs[0]) 3132 accounts := tcs[0].Backend.ImportAccountsForUser(tcs[0]) 3133 3134 senderAccountID, err := stellar.GetOwnPrimaryAccountID(tcs[0].MetaContext()) 3135 require.NoError(t, err) 3136 tcs[0].Backend.Gift(senderAccountID, "20") 3137 3138 keys := tcs[0].Backend.CreateFakeAsset("KEYS") 3139 trustlineArg := stellar1.Trustline{ 3140 AssetCode: stellar1.AssetCode(keys.Code), 3141 Issuer: stellar1.AccountID(keys.Issuer), 3142 } 3143 3144 // Removing a trustline that's not currently in the account should fail. 3145 err = tcs[0].Srv.DeleteTrustlineLocal(context.Background(), stellar1.DeleteTrustlineLocalArg{ 3146 AccountID: senderAccountID, 3147 Trustline: trustlineArg, 3148 }) 3149 require.Error(t, err) 3150 3151 // Cannot change limist of a trustline that wasn't added. 3152 err = tcs[0].Srv.ChangeTrustlineLimitLocal(context.Background(), stellar1.ChangeTrustlineLimitLocalArg{ 3153 AccountID: senderAccountID, 3154 Trustline: trustlineArg, 3155 Limit: "10", 3156 }) 3157 require.Error(t, err) 3158 3159 // Finally, add the trustline. 3160 err = tcs[0].Srv.AddTrustlineLocal(context.Background(), stellar1.AddTrustlineLocalArg{ 3161 Trustline: trustlineArg, 3162 Limit: "", 3163 }) 3164 require.NoError(t, err) 3165 3166 b, err := stellarnet.ParseStellarAmount("20") 3167 require.NoError(t, err) 3168 accounts[0].AdjustAssetBalance(b, keys) 3169 err = tcs[0].Srv.walletState.Refresh(tcs[0].MetaContext(), accounts[0].accountID, "test adjust balance") 3170 require.NoError(t, err) 3171 3172 // Cannot change limit to below current balance. 3173 err = tcs[0].Srv.ChangeTrustlineLimitLocal(context.Background(), stellar1.ChangeTrustlineLimitLocalArg{ 3174 AccountID: senderAccountID, 3175 Trustline: trustlineArg, 3176 Limit: "10", 3177 }) 3178 require.Error(t, err) 3179 3180 // Cannot remove trustline with balance. 3181 err = tcs[0].Srv.DeleteTrustlineLocal(context.Background(), stellar1.DeleteTrustlineLocalArg{ 3182 AccountID: senderAccountID, 3183 Trustline: trustlineArg, 3184 }) 3185 require.Error(t, err) 3186 } 3187 3188 type chatListener struct { 3189 libkb.NoopNotifyListener 3190 3191 paymentInfos chan chat1.ChatPaymentInfoArg 3192 requestInfos chan chat1.ChatRequestInfoArg 3193 } 3194 3195 func newChatListener() *chatListener { 3196 x := &chatListener{ 3197 paymentInfos: make(chan chat1.ChatPaymentInfoArg, 1), 3198 requestInfos: make(chan chat1.ChatRequestInfoArg, 1), 3199 } 3200 return x 3201 } 3202 3203 func (c *chatListener) ChatPaymentInfo(uid keybase1.UID, convID chat1.ConversationID, msgID chat1.MessageID, info chat1.UIPaymentInfo) { 3204 c.paymentInfos <- chat1.ChatPaymentInfoArg{Uid: uid, ConvID: convID, MsgID: msgID, Info: info} 3205 } 3206 3207 func (c *chatListener) ChatRequestInfo(uid keybase1.UID, convID chat1.ConversationID, msgID chat1.MessageID, info chat1.UIRequestInfo) { 3208 c.requestInfos <- chat1.ChatRequestInfoArg{Uid: uid, ConvID: convID, MsgID: msgID, Info: info} 3209 } 3210 3211 func firstAccountName(t testing.TB, tc *TestContext) string { 3212 loggedInUsername := tc.G.ActiveDevice.Username(libkb.NewMetaContextForTest(tc.TestContext)) 3213 require.True(t, loggedInUsername.IsValid()) 3214 return fmt.Sprintf("%v's account", loggedInUsername) 3215 } 3216 3217 func check(t testing.TB) { 3218 if t.Failed() { 3219 // The test failed. Possibly in anothe goroutine. Look earlier in the logs for the real failure. 3220 require.FailNow(t, "test already failed") 3221 } 3222 } 3223 3224 func TestGetStaticConfigLocal(t *testing.T) { 3225 tcs, cleanup := setupNTests(t, 1) 3226 defer cleanup() 3227 3228 staticConfig, err := tcs[0].Srv.GetStaticConfigLocal(context.Background()) 3229 3230 require.NoError(t, err) 3231 require.Equal(t, staticConfig.PaymentNoteMaxLength, 500) 3232 require.Equal(t, staticConfig.RequestNoteMaxLength, 240) 3233 require.Equal(t, staticConfig.PublicMemoMaxLength, 28) 3234 }