Skip to content

Commit

Permalink
[SDP-1184] Update the db setup-for-network CLI command to support C…
Browse files Browse the repository at this point in the history
…ircle (#327)

### What

Establish a different set of default assets for Circle distribution accounts.

### Why

The subset of Stellar assets supported by Circle is limited.

Address https://stellarorg.atlassian.net/browse/SDP-1184

Co-authored-by: Erica <[email protected]>
Co-authored-by: Marcelo Salloum <[email protected]>
Co-authored-by: Marcelo Salloum dos Santos <[email protected]>
  • Loading branch information
3 people authored Jun 18, 2024
1 parent 74208ee commit 4573c31
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 159 deletions.
32 changes: 22 additions & 10 deletions cmd/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
di "github.com/stellar/stellar-disbursement-platform-backend/internal/dependencyinjection"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services"
sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils"
"github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant"
)

const DBConfigOptionFlagName = "database-url"
Expand Down Expand Up @@ -80,33 +81,44 @@ func (c *DatabaseCommand) setupForNetworkCmd(globalOptions *utils.GlobalOptionsT
log.Ctx(ctx).Fatal(err.Error())
}

tenantIDToDSNMap, err := getTenantIDToDSNMapping(ctx, c.adminDBConnectionPool)
dsnByTenantID, err := getTenantIDToDSNMapping(ctx, c.adminDBConnectionPool)
if err != nil {
log.Ctx(ctx).Fatalf("getting tenants schemas: %s", err.Error())
}

m := tenant.NewManager(tenant.WithDatabase(c.adminDBConnectionPool))
tenants, err := m.GetAllTenants(ctx, nil)
if err != nil {
log.Ctx(ctx).Fatalf("getting all tenants: %v", err)
}
tenantsByID := make(map[string]tenant.Tenant, len(tenants))
for _, tnt := range tenants {
tenantsByID[tnt.ID] = tnt
}

if opts.TenantID != "" {
if dsn, ok := tenantIDToDSNMap[opts.TenantID]; ok {
tenantIDToDSNMap = map[string]string{opts.TenantID: dsn}
if dsn, ok := dsnByTenantID[opts.TenantID]; ok {
dsnByTenantID = map[string]string{opts.TenantID: dsn}
} else {
log.Ctx(ctx).Fatalf("tenant ID %s does not exist", opts.TenantID)
}
}

for tenantID, dsn := range tenantIDToDSNMap {
for tenantID, dsn := range dsnByTenantID {
networkType, err := sdpUtils.GetNetworkTypeFromNetworkPassphrase(globalOptions.NetworkPassphrase)
if err != nil {
log.Ctx(ctx).Fatalf("error getting network type: %s", err.Error())
}
tnt := tenantsByID[tenantID]

log.Ctx(ctx).Infof("running for tenant ID %s", tenantID)
tenantDBConnectionPool, err := db.OpenDBConnectionPool(dsn)
if err != nil {
log.Ctx(ctx).Fatalf("error connection to the database: %s", err.Error())
}
defer tenantDBConnectionPool.Close()

networkType, err := sdpUtils.GetNetworkTypeFromNetworkPassphrase(globalOptions.NetworkPassphrase)
if err != nil {
log.Ctx(ctx).Fatalf("error getting network type: %s", err.Error())
}

if err := services.SetupAssetsForProperNetwork(ctx, tenantDBConnectionPool, networkType, services.DefaultAssetsNetworkMap); err != nil {
if err := services.SetupAssetsForProperNetwork(ctx, tenantDBConnectionPool, networkType, tnt.DistributionAccountType.Platform()); err != nil {
log.Ctx(ctx).Fatalf("error upserting assets for proper network: %s", err.Error())
}

Expand Down
70 changes: 35 additions & 35 deletions cmd/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
require.NoError(t, outerErr)
defer tenant2SchemaConnectionPool.Close()

testnetUSDCIssuer := keypair.MustRandom().Address()
randomUSDCIssuer := keypair.MustRandom().Address()
clearTenantTables := func(t *testing.T, ctx context.Context, tenantSchemaConnectionPool db.DBConnectionPool) {
models, mErr := data.NewModels(tenantSchemaConnectionPool)
require.NoError(t, mErr)
Expand All @@ -494,14 +494,14 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
data.DeleteAllWalletFixtures(t, ctx, tenantSchemaConnectionPool)
data.DeleteAllAssetFixtures(t, ctx, tenantSchemaConnectionPool)

data.CreateAssetFixture(t, ctx, tenantSchemaConnectionPool, "USDC", testnetUSDCIssuer)
data.CreateAssetFixture(t, ctx, tenantSchemaConnectionPool, "USDC", randomUSDCIssuer)

assets, aErr := models.Assets.GetAll(ctx)
require.NoError(t, aErr)

assert.Len(t, assets, 1)
assert.Equal(t, "USDC", assets[0].Code)
assert.Equal(t, testnetUSDCIssuer, assets[0].Issuer)
assert.Equal(t, randomUSDCIssuer, assets[0].Issuer)

// Wallets
data.CreateWalletFixture(t, ctx, tenantSchemaConnectionPool, "Vibrant Assist", "https://vibrantapp.com", "api-dev.vibrantapp.com", "https://vibrantapp.com/sdp-dev")
Expand Down Expand Up @@ -547,12 +547,13 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
actualAssets, aErr := models.Assets.GetAll(ctx)
require.NoError(t, aErr)

assert.Len(t, actualAssets, 2)
assert.Equal(t, "USDC", actualAssets[0].Code)
assert.NotEqual(t, testnetUSDCIssuer, actualAssets[0].Issuer)
assert.Equal(t, assets.USDCAssetIssuerPubnet, actualAssets[0].Issuer)
assert.Equal(t, "XLM", actualAssets[1].Code)
assert.Empty(t, actualAssets[1].Issuer)
assert.Len(t, actualAssets, 3)
assert.Equal(t, assets.EURCAssetCode, actualAssets[0].Code)
assert.Equal(t, assets.EURCAssetIssuerPubnet, actualAssets[0].Issuer)
assert.Equal(t, assets.USDCAssetCode, actualAssets[1].Code)
assert.Equal(t, assets.USDCAssetIssuerPubnet, actualAssets[1].Issuer)
assert.Equal(t, assets.XLMAssetCode, actualAssets[2].Code)
assert.Empty(t, actualAssets[2].Issuer)

// Validating wallets
wallets, wErr := models.Wallets.GetAll(ctx)
Expand Down Expand Up @@ -581,10 +582,9 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
expectedLogs := []string{
fmt.Sprintf("running for tenant ID %s", tnt1.ID),
"updating/inserting assets for the 'pubnet' network",
"Code: USDC",
fmt.Sprintf("Issuer: %s", assets.USDCAssetIssuerPubnet),
"Code: XLM",
"Issuer: ",
fmt.Sprintf("* %s - %s", assets.USDCAssetCode, assets.USDCAssetIssuerPubnet),
fmt.Sprintf("* %s - %s", assets.EURCAssetCode, assets.EURCAssetIssuerPubnet),
fmt.Sprintf("* %s - %s", assets.XLMAssetCode, ""),
"updating/inserting wallets for the 'pubnet' network",
"Name: Vibrant Assist",
"Homepage: https://vibrantapp.com/vibrant-assist",
Expand All @@ -605,11 +605,13 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
actualAssets, err = models.Assets.GetAll(ctx)
require.NoError(t, err)

require.Len(t, actualAssets, 2)
require.Equal(t, assets.USDCAssetPubnet.Code, actualAssets[0].Code)
require.Equal(t, assets.USDCAssetPubnet.Issuer, actualAssets[0].Issuer)
require.Equal(t, assets.XLMAsset.Code, actualAssets[1].Code)
require.Empty(t, assets.XLMAsset.Issuer)
require.Len(t, actualAssets, 3)
assert.Equal(t, assets.EURCAssetCode, actualAssets[0].Code)
assert.Equal(t, assets.EURCAssetIssuerPubnet, actualAssets[0].Issuer)
assert.Equal(t, assets.USDCAssetCode, actualAssets[1].Code)
assert.Equal(t, assets.USDCAssetIssuerPubnet, actualAssets[1].Issuer)
assert.Equal(t, assets.XLMAssetCode, actualAssets[2].Code)
assert.Empty(t, actualAssets[2].Issuer)

// Validating wallets
wallets, err = models.Wallets.GetAll(ctx)
Expand Down Expand Up @@ -641,10 +643,9 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
expectedLogs = []string{
fmt.Sprintf("running for tenant ID %s", tnt2.ID),
"updating/inserting assets for the 'pubnet' network",
"Code: USDC",
fmt.Sprintf("Issuer: %s", assets.USDCAssetIssuerPubnet),
"Code: XLM",
"Issuer: ",
fmt.Sprintf("* %s - %s", assets.USDCAssetCode, assets.USDCAssetIssuerPubnet),
fmt.Sprintf("* %s - %s", assets.EURCAssetCode, assets.EURCAssetIssuerPubnet),
fmt.Sprintf("* %s - %s", assets.XLMAssetCode, ""),
"updating/inserting wallets for the 'pubnet' network",
"Name: Vibrant Assist",
"Homepage: https://vibrantapp.com/vibrant-assist",
Expand Down Expand Up @@ -686,8 +687,8 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
require.NoError(t, err)

assert.Len(t, currentAssets, 1)
assert.Equal(t, "USDC", currentAssets[0].Code)
assert.Equal(t, testnetUSDCIssuer, currentAssets[0].Issuer)
assert.Equal(t, assets.USDCAssetCode, currentAssets[0].Code)
assert.Equal(t, randomUSDCIssuer, currentAssets[0].Issuer)

// Validating wallets
wallets, err := models.Wallets.GetAll(ctx)
Expand All @@ -710,13 +711,13 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
actualAssets, err := models.Assets.GetAll(ctx)
require.NoError(t, err)

assert.Len(t, actualAssets, 2)
assert.Equal(t, "USDC", actualAssets[0].Code)
assert.NotEqual(t, testnetUSDCIssuer, actualAssets[0].Issuer)
assert.Equal(t, assets.USDCAssetIssuerPubnet, actualAssets[0].Issuer)
assert.Equal(t, "XLM", actualAssets[1].Code)
assert.Empty(t, actualAssets[1].Issuer)

assert.Len(t, actualAssets, 3)
assert.Equal(t, assets.EURCAssetCode, actualAssets[0].Code)
assert.Equal(t, assets.EURCAssetIssuerPubnet, actualAssets[0].Issuer)
assert.Equal(t, assets.USDCAssetCode, actualAssets[1].Code)
assert.Equal(t, assets.USDCAssetIssuerPubnet, actualAssets[1].Issuer)
assert.Equal(t, assets.XLMAssetCode, actualAssets[2].Code)
assert.Empty(t, actualAssets[2].Issuer)
// Validating wallets
wallets, err = models.Wallets.GetAll(ctx)
require.NoError(t, err)
Expand All @@ -743,10 +744,9 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
expectedLogs := []string{
fmt.Sprintf("running for tenant ID %s", tnt2.ID),
"updating/inserting assets for the 'pubnet' network",
"Code: USDC",
fmt.Sprintf("Issuer: %s", assets.USDCAssetPubnet.Issuer),
"Code: XLM",
"Issuer: ",
fmt.Sprintf("* %s - %s", assets.USDCAssetCode, assets.USDCAssetIssuerPubnet),
fmt.Sprintf("* %s - %s", assets.EURCAssetCode, assets.EURCAssetIssuerPubnet),
fmt.Sprintf("* %s - %s", assets.XLMAssetCode, ""),
"updating/inserting wallets for the 'pubnet' network",
"Name: Vibrant Assist",
"Homepage: https://vibrantapp.com/vibrant-assist",
Expand Down
2 changes: 1 addition & 1 deletion internal/serve/httphandler/stellar_toml_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (s StellarTomlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
httperror.InternalError(ctx, "Couldn't generate stellar.toml file for this instance", innerErr, nil).Render(w)
return
}
instanceAssets := services.DefaultAssetsNetworkMap[networkType]
instanceAssets := services.StellarAssetsNetworkMap[networkType]
stellarToml = s.buildGeneralInformation(ctx, r) + s.buildOrganizationDocumentation(s.InstanceName) + s.buildCurrencyInformation(instanceAssets)
} else {
// return a stellar.toml file for this tenant.
Expand Down
20 changes: 16 additions & 4 deletions internal/serve/httphandler/stellar_toml_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/stellar/stellar-disbursement-platform-backend/db"
"github.com/stellar/stellar-disbursement-platform-backend/db/dbtest"
"github.com/stellar/stellar-disbursement-platform-backend/internal/data"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/assets"
"github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/signing"
sigMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/signing/mocks"
"github.com/stellar/stellar-disbursement-platform-backend/pkg/schema"
Expand Down Expand Up @@ -427,20 +428,31 @@ func Test_StellarTomlHandler_ServeHTTP(t *testing.T) {
ORG_NAME="SDP Pubnet"
[[CURRENCIES]]
code = "USDC"
issuer = "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
code = %q
issuer = %q
is_asset_anchored = true
anchor_asset_type = "fiat"
status = "live"
desc = "USDC"
desc = %q
[[CURRENCIES]]
code = %q
issuer = %q
is_asset_anchored = true
anchor_asset_type = "fiat"
status = "live"
desc = %q
[[CURRENCIES]]
code = "native"
status = "live"
is_asset_anchored = true
anchor_asset_type = "crypto"
desc = "XLM, the native token of the Stellar Network."
`, network.PublicNetworkPassphrase, horizonPubnetURL)
`,
network.PublicNetworkPassphrase, horizonPubnetURL,
assets.EURCAssetCode, assets.EURCAssetIssuerPubnet, assets.EURCAssetCode,
assets.USDCAssetCode, assets.USDCAssetIssuerPubnet, assets.USDCAssetCode)
wantToml = strings.TrimSpace(wantToml)
wantToml = strings.ReplaceAll(wantToml, "\t", "")
assert.Equal(t, wantToml, rr.Body.String())
Expand Down
24 changes: 17 additions & 7 deletions internal/services/assets/assets_pubnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,32 @@ package assets

import "github.com/stellar/stellar-disbursement-platform-backend/internal/data"

// USDCAssetCode is the code for the USDC asset for pubnet and testnet
const USDCAssetCode = "USDC"
// USDC

// XLMAssetCode is the code for the XLM asset for pubnet and testnet
const XLMAssetCode = "XLM"
const USDCAssetCode = "USDC"

// USDCAssetIssuerPubnet is the issuer for the USDC asset for pubnet
const USDCAssetIssuerPubnet = "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"

// USDCAssetPubnet is the USDC asset for pubnet
var USDCAssetPubnet = data.Asset{
Code: USDCAssetCode,
Issuer: USDCAssetIssuerPubnet,
}

// XLMAsset is the XLM asset for pubnet
// EURC

const EURCAssetCode = "EURC"

const EURCAssetIssuerPubnet = "GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP2"

var EURCAssetPubnet = data.Asset{
Code: EURCAssetCode,
Issuer: EURCAssetIssuerPubnet,
}

// XLM

const XLMAssetCode = "XLM"

var XLMAsset = data.Asset{
Code: XLMAssetCode,
Issuer: "",
Expand Down
13 changes: 11 additions & 2 deletions internal/services/assets/assets_testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ package assets

import "github.com/stellar/stellar-disbursement-platform-backend/internal/data"

// USDCAssetIssuerTestnet is the issuer for the USDC asset for testnet
// USDC

const USDCAssetIssuerTestnet = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"

// USDCAssetTestnet is the USDC asset for testnet
var USDCAssetTestnet = data.Asset{
Code: USDCAssetCode,
Issuer: USDCAssetIssuerTestnet,
}

// EURC

const EURCAssetIssuerTestnet = "GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO"

var EURCAssetTestnet = data.Asset{
Code: EURCAssetCode,
Issuer: EURCAssetIssuerTestnet,
}
37 changes: 22 additions & 15 deletions internal/services/setup_assets_for_network_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,46 @@ import (
"github.com/stellar/stellar-disbursement-platform-backend/internal/data"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/assets"
"github.com/stellar/stellar-disbursement-platform-backend/internal/utils"
"github.com/stellar/stellar-disbursement-platform-backend/pkg/schema"
)

type AssetsNetworkMapType map[utils.NetworkType][]data.Asset

var DefaultAssetsNetworkMap = AssetsNetworkMapType{
utils.PubnetNetworkType: []data.Asset{assets.USDCAssetPubnet, assets.XLMAsset},
utils.TestnetNetworkType: []data.Asset{assets.USDCAssetTestnet, assets.XLMAsset},
var StellarAssetsNetworkMap = AssetsNetworkMapType{
utils.PubnetNetworkType: []data.Asset{assets.EURCAssetPubnet, assets.USDCAssetPubnet, assets.XLMAsset},
utils.TestnetNetworkType: []data.Asset{assets.EURCAssetTestnet, assets.USDCAssetTestnet, assets.XLMAsset},
}

var CircleAssetsNetworkMap = AssetsNetworkMapType{
utils.PubnetNetworkType: []data.Asset{assets.EURCAssetPubnet, assets.USDCAssetPubnet},
utils.TestnetNetworkType: []data.Asset{assets.EURCAssetTestnet, assets.USDCAssetTestnet},
}

type AssetsNetworkByPlatformMapType map[schema.Platform]AssetsNetworkMapType

var AssetsNetworkByPlatformMap = AssetsNetworkByPlatformMapType{
schema.StellarPlatform: StellarAssetsNetworkMap,
schema.CirclePlatform: CircleAssetsNetworkMap,
}

// SetupAssetsForProperNetwork updates and inserts assets for the given Network Passphrase (`network`). So it avoids the application having
// same asset code with multiple issuers.
func SetupAssetsForProperNetwork(ctx context.Context, dbConnectionPool db.DBConnectionPool, network utils.NetworkType, assetsNetworkMap AssetsNetworkMapType) error {
func SetupAssetsForProperNetwork(ctx context.Context, dbConnectionPool db.DBConnectionPool, network utils.NetworkType, distAccPlatform schema.Platform) error {
log.Ctx(ctx).Infof("updating/inserting assets for the '%s' network", network)

assets, ok := assetsNetworkMap[network]
assets, ok := AssetsNetworkByPlatformMap[distAccPlatform][network]
if !ok {
return fmt.Errorf("invalid network provided")
}

var codes, issuers []string

separator := strings.Repeat("-", 20)
buf := new(strings.Builder)
buf.WriteString("assets' code that will be updated or inserted:\n\n")
for _, asset := range assets {
codes = append(codes, asset.Code)
issuers = append(issuers, asset.Issuer)

buf.WriteString(fmt.Sprintf("Code: %s\n%s\n\n", asset.Code, separator))
}

log.Ctx(ctx).Info(buf.String())
log.Ctx(ctx).Infof("Asset codes to be updated/inserted: %v", codes)
err := db.RunInTransaction(ctx, dbConnectionPool, nil, func(dbTx db.DBTransaction) error {
query := `
WITH assets_to_update_or_insert AS (
Expand Down Expand Up @@ -104,12 +112,11 @@ func SetupAssetsForProperNetwork(ctx context.Context, dbConnectionPool db.DBConn
return fmt.Errorf("error getting all available assets on database: %w", err)
}

buf.Reset()
buf.WriteString(fmt.Sprintf("Registered assets for network %s:\n\n", network))
buf := new(strings.Builder)
buf.WriteString(fmt.Sprintf("Updated list of assets for network %s:\n\n", network))
for _, asset := range allAssets {
buf.WriteString(fmt.Sprintf("Code: %s\nIssuer: %s\n%s\n\n", asset.Code, asset.Issuer, separator))
buf.WriteString(fmt.Sprintf("\t * %s - %s\n", asset.Code, asset.Issuer))
}

log.Ctx(ctx).Info(buf.String())

return nil
Expand Down
Loading

0 comments on commit 4573c31

Please sign in to comment.