From 901f45722daf35e0d12d7c1b7cf065a6af7ed368 Mon Sep 17 00:00:00 2001 From: Marwen Abid Date: Fri, 14 Jun 2024 23:27:44 +0100 Subject: [PATCH] [SDP-1170] Add `PATCH /organization/circle-config` (#326) --- cmd/serve.go | 1 + cmd/serve_test.go | 1 + db/db.go | 1 + db/dbtest/dbtest.go | 10 + ...06-12.0-add-circle-client-config-table.sql | 47 +++ internal/circle/client_config.go | 152 +++++++ internal/circle/client_config_test.go | 399 ++++++++++++++++++ .../httphandler/circle_config_handler.go | 97 +++++ .../httphandler/circle_config_handler_test.go | 160 +++++++ internal/serve/serve.go | 9 + internal/serve/serve_test.go | 1 + internal/testutils/db.go | 37 ++ internal/testutils/http.go | 22 + .../channel_account_db_signature_client.go | 7 +- ...hannel_account_db_signature_client_test.go | 21 +- ...ution_account_db_vault_signature_client.go | 7 +- ..._account_db_vault_signature_client_test.go | 21 +- .../engine/signing/signature_client.go | 5 +- .../engine/signing/signature_client_test.go | 9 +- .../engine/signing/signature_service.go | 5 +- .../engine/signing/signature_service_test.go | 7 +- .../engine/signing/signer_router.go | 5 +- .../engine/signing/signer_router_test.go | 17 +- .../transactionsubmission/manager_test.go | 5 +- .../services/horizon_test.go | 19 +- .../transactionsubmission/store/fixtures.go | 11 +- .../transaction_worker_test.go | 6 +- internal/transactionsubmission/utils/utils.go | 19 - internal/utils/encrypter.go | 19 + .../utils/mocks.go | 0 internal/utils/utils.go | 5 + internal/utils/utils_test.go | 30 ++ .../httphandler/tenants_handler_test.go | 3 +- .../internal/provisioning/manager_test.go | 3 +- 34 files changed, 1072 insertions(+), 89 deletions(-) create mode 100644 db/migrations/sdp-migrations/2024-06-12.0-add-circle-client-config-table.sql create mode 100644 internal/circle/client_config.go create mode 100644 internal/circle/client_config_test.go create mode 100644 internal/serve/httphandler/circle_config_handler.go create mode 100644 internal/serve/httphandler/circle_config_handler_test.go create mode 100644 internal/testutils/db.go create mode 100644 internal/testutils/http.go create mode 100644 internal/utils/encrypter.go rename internal/{transactionsubmission => }/utils/mocks.go (100%) diff --git a/cmd/serve.go b/cmd/serve.go index 573b59be9..0602c19d1 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -479,6 +479,7 @@ func (c *ServeCommand) Command(serverService ServerServiceInterface, monitorServ serveOpts.MonitorService = monitorService serveOpts.BaseURL = globalOptions.BaseURL serveOpts.NetworkPassphrase = globalOptions.NetworkPassphrase + serveOpts.DistAccEncryptionPassphrase = txSubmitterOpts.SignatureServiceOptions.DistAccEncryptionPassphrase // Inject metrics server dependencies metricsServeOpts.MonitorService = monitorService diff --git a/cmd/serve_test.go b/cmd/serve_test.go index e7ebe8c05..2ec96ca74 100644 --- a/cmd/serve_test.go +++ b/cmd/serve_test.go @@ -169,6 +169,7 @@ func Test_serve(t *testing.T) { SubmitterEngine: submitterEngine, DistributionAccountService: mDistAccService, MaxInvitationSMSResendAttempts: 3, + DistAccEncryptionPassphrase: distributionAccPrivKey, } serveOpts.AnchorPlatformAPIService, err = anchorplatform.NewAnchorPlatformAPIService(httpclient.DefaultClient(), serveOpts.AnchorPlatformBasePlatformURL, serveOpts.AnchorPlatformOutgoingJWTSecret) require.NoError(t, err) diff --git a/db/db.go b/db/db.go index c7153ee52..0bde1884e 100644 --- a/db/db.go +++ b/db/db.go @@ -9,6 +9,7 @@ import ( "github.com/jmoiron/sqlx" _ "github.com/lib/pq" "github.com/stellar/go/support/log" + "github.com/stellar/stellar-disbursement-platform-backend/internal/monitor" ) diff --git a/db/dbtest/dbtest.go b/db/dbtest/dbtest.go index 9e6f28308..261f81b4d 100644 --- a/db/dbtest/dbtest.go +++ b/db/dbtest/dbtest.go @@ -11,11 +11,15 @@ import ( ) func OpenWithoutMigrations(t *testing.T) *dbtest.DB { + t.Helper() + db := dbtest.Postgres(t) return db } func openWithMigrations(t *testing.T, configs ...migrations.MigrationRouter) *dbtest.DB { + t.Helper() + db := OpenWithoutMigrations(t) conn := db.Open() @@ -34,6 +38,8 @@ func openWithMigrations(t *testing.T, configs ...migrations.MigrationRouter) *db } func Open(t *testing.T) *dbtest.DB { + t.Helper() + return openWithMigrations(t, migrations.AdminMigrationRouter, migrations.SDPMigrationRouter, @@ -43,9 +49,13 @@ func Open(t *testing.T) *dbtest.DB { } func OpenWithAdminMigrationsOnly(t *testing.T) *dbtest.DB { + t.Helper() + return openWithMigrations(t, migrations.AdminMigrationRouter) } func OpenWithTSSMigrationsOnly(t *testing.T) *dbtest.DB { + t.Helper() + return openWithMigrations(t, migrations.TSSMigrationRouter) } diff --git a/db/migrations/sdp-migrations/2024-06-12.0-add-circle-client-config-table.sql b/db/migrations/sdp-migrations/2024-06-12.0-add-circle-client-config-table.sql new file mode 100644 index 000000000..e86fdcd67 --- /dev/null +++ b/db/migrations/sdp-migrations/2024-06-12.0-add-circle-client-config-table.sql @@ -0,0 +1,47 @@ +-- +migrate Up +CREATE TABLE circle_client_config ( + wallet_id VARCHAR(64) NOT NULL, + encrypted_api_key VARCHAR(256) NOT NULL, + encrypter_public_key VARCHAR(256) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- +migrate StatementBegin +CREATE OR REPLACE FUNCTION enforce_single_row_for_circle_client_config() + RETURNS TRIGGER AS $$ +BEGIN + IF (SELECT COUNT(*) FROM circle_client_config) != 0 THEN + RAISE EXCEPTION 'circle_client_config must contain exactly one row'; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; +-- +migrate StatementEnd + +CREATE TRIGGER enforce_single_row_for_circle_client_config_insert_trigger + BEFORE INSERT ON circle_client_config + FOR EACH ROW +EXECUTE FUNCTION enforce_single_row_for_circle_client_config(); + +CREATE TRIGGER enforce_single_row_for_circle_client_config_delete_trigger + BEFORE DELETE ON circle_client_config + FOR EACH ROW +EXECUTE FUNCTION enforce_single_row_for_circle_client_config(); + + +-- TRIGGER: updated_at +CREATE TRIGGER refresh_circle_client_config_updated_at BEFORE UPDATE ON circle_client_config FOR EACH ROW EXECUTE PROCEDURE update_at_refresh(); + + +-- +migrate Down + +DROP TRIGGER enforce_single_row_for_circle_client_config_delete_trigger ON circle_client_config; + +DROP TRIGGER enforce_single_row_for_circle_client_config_insert_trigger ON circle_client_config; + +DROP FUNCTION enforce_single_row_for_circle_client_config; + +DROP TRIGGER refresh_circle_client_config_updated_at ON circle_client_config; + +DROP TABLE circle_client_config; \ No newline at end of file diff --git a/internal/circle/client_config.go b/internal/circle/client_config.go new file mode 100644 index 000000000..5641ff158 --- /dev/null +++ b/internal/circle/client_config.go @@ -0,0 +1,152 @@ +package circle + +import ( + "context" + "database/sql" + "errors" + "fmt" + "strings" + "time" + + "github.com/stellar/stellar-disbursement-platform-backend/db" +) + +type ClientConfig struct { + EncryptedAPIKey *string `db:"encrypted_api_key"` + WalletID *string `db:"wallet_id"` + EncrypterPublicKey *string `db:"encrypter_public_key"` + UpdatedAt time.Time `db:"updated_at"` + CreatedAt time.Time `db:"created_at"` +} + +type ClientConfigModel struct { + DBConnectionPool db.DBConnectionPool +} + +func NewClientConfigModel(dbConnectionPool db.DBConnectionPool) *ClientConfigModel { + return &ClientConfigModel{DBConnectionPool: dbConnectionPool} +} + +// Upsert insert or update the client configuration for Circle into the database. +func (m *ClientConfigModel) Upsert(ctx context.Context, configUpdate ClientConfigUpdate) error { + err := db.RunInTransaction(ctx, m.DBConnectionPool, nil, func(tx db.DBTransaction) error { + existingConfig, err := m.get(ctx, tx) + if err != nil { + return fmt.Errorf("getting existing circle config: %w", err) + } + + if existingConfig == nil { + err = m.insert(ctx, tx, configUpdate) + if err != nil { + return fmt.Errorf("inserting new circle config: %w", err) + } + } else { + err = m.update(ctx, tx, configUpdate) + if err != nil { + return fmt.Errorf("updating existing circle config: %w", err) + } + } + + return nil + }) + if err != nil { + return fmt.Errorf("running transaction: %w", err) + } + + return nil +} + +// Get retrieves the circle client config from the database if it exists. +func (m *ClientConfigModel) Get(ctx context.Context) (*ClientConfig, error) { + return m.get(ctx, m.DBConnectionPool) +} + +// get retrieves the circle client config from the database if it exists. +func (m *ClientConfigModel) get(ctx context.Context, sqlExec db.SQLExecuter) (*ClientConfig, error) { + const q = `SELECT * FROM circle_client_config` + var config ClientConfig + err := sqlExec.GetContext(ctx, &config, q) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + return nil, fmt.Errorf("getting circle config: %w", err) + } + return &config, nil +} + +// insert inserts the circle client config into the database. +func (m *ClientConfigModel) insert(ctx context.Context, sqlExec db.SQLExecuter, config ClientConfigUpdate) error { + if err := config.validateForInsert(); err != nil { + return fmt.Errorf("invalid circle config for insert: %w", err) + } + const q = ` + INSERT INTO circle_client_config (encrypted_api_key, wallet_id, encrypter_public_key) + VALUES ($1, $2, $3) + ` + _, err := sqlExec.ExecContext(ctx, q, config.EncryptedAPIKey, config.WalletID, config.EncrypterPublicKey) + if err != nil { + return fmt.Errorf("inserting circle config: %w", err) + } + return nil +} + +// update updates the circle client config in the database. +func (m *ClientConfigModel) update(ctx context.Context, sqlExec db.SQLExecuter, config ClientConfigUpdate) error { + if err := config.validate(); err != nil { + return fmt.Errorf("invalid circle config for update: %w", err) + } + + query := ` + UPDATE + circle_client_config + SET + %s + ` + + args := []interface{}{} + fields := []string{} + if config.WalletID != nil { + fields = append(fields, "wallet_id = ?") + args = append(args, config.WalletID) + } + + if config.EncryptedAPIKey != nil { + fields = append(fields, "encrypted_api_key = ?", "encrypter_public_key = ?") + args = append(args, config.EncryptedAPIKey, config.EncrypterPublicKey) + } + + query = m.DBConnectionPool.Rebind(fmt.Sprintf(query, strings.Join(fields, ", "))) + + _, err := sqlExec.ExecContext(ctx, query, args...) + if err != nil { + return fmt.Errorf("error updating client config: %w", err) + } + + return nil +} + +type ClientConfigUpdate struct { + EncryptedAPIKey *string `db:"encrypted_api_key"` + WalletID *string `db:"wallet_id"` + EncrypterPublicKey *string `db:"encrypter_public_key"` +} + +func (c ClientConfigUpdate) validate() error { + if c.WalletID == nil && c.EncryptedAPIKey == nil { + return fmt.Errorf("wallet_id or encrypted_api_key must be provided") + } + + if c.EncryptedAPIKey != nil && c.EncrypterPublicKey == nil { + return fmt.Errorf("encrypter_public_key must be provided if encrypted_api_key is provided") + } + + return nil +} + +func (c ClientConfigUpdate) validateForInsert() error { + if c.WalletID == nil || c.EncryptedAPIKey == nil || c.EncrypterPublicKey == nil { + return fmt.Errorf("wallet_id, encrypted_api_key, and encrypter_public_key must be provided") + } + return nil +} diff --git a/internal/circle/client_config_test.go b/internal/circle/client_config_test.go new file mode 100644 index 000000000..03ef688a0 --- /dev/null +++ b/internal/circle/client_config_test.go @@ -0,0 +1,399 @@ +package circle + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "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/testutils" +) + +func Test_ClientConfigModel_Upsert_Update(t *testing.T) { + dbt := dbtest.Open(t) + defer dbt.Close() + + dbConnectionPool, outerErr := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, outerErr) + defer dbConnectionPool.Close() + + ctx := context.Background() + ccm := &ClientConfigModel{DBConnectionPool: dbConnectionPool} + + walletID := "the_wallet_id" + encryptedAPIKey := "the_encrypted_api_key" + encrypterPublicKey := "the_encrypter_public_key" + + updatedWalletID := "another_wallet_id" + updatedEncryptedAPIKey := "another_encrypted_api_key" + updatedEncrypterPublicKey := "another_encrypter_public_key" + + outerErr = ccm.insert(ctx, dbConnectionPool, ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: &encrypterPublicKey, + }) + require.NoError(t, outerErr) + + t.Run("update existing config", func(t *testing.T) { + // Ensure there is an existing config + cc, err := ccm.Get(ctx) + require.NoError(t, err) + + // Verify the existing config + assert.Equal(t, walletID, *cc.WalletID) + assert.Equal(t, encryptedAPIKey, *cc.EncryptedAPIKey) + assert.Equal(t, encrypterPublicKey, *cc.EncrypterPublicKey) + + err = ccm.Upsert(ctx, ClientConfigUpdate{ + WalletID: &updatedWalletID, + EncryptedAPIKey: &updatedEncryptedAPIKey, + EncrypterPublicKey: &updatedEncrypterPublicKey, + }) + assert.NoError(t, err) + + cc, err = ccm.Get(ctx) + require.NoError(t, err) + require.NotNil(t, cc) + assert.Equal(t, updatedWalletID, *cc.WalletID) + assert.Equal(t, updatedEncryptedAPIKey, *cc.EncryptedAPIKey) + assert.Equal(t, updatedEncrypterPublicKey, *cc.EncrypterPublicKey) + }) + + t.Run("return error on validation failure", func(t *testing.T) { + err := ccm.Upsert(ctx, ClientConfigUpdate{ + WalletID: nil, + EncryptedAPIKey: nil, + EncrypterPublicKey: nil, + }) + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid circle config for update: wallet_id or encrypted_api_key must be provided") + }) +} + +func Test_ClientConfigModel_Upsert_Insert(t *testing.T) { + dbt := dbtest.Open(t) + defer dbt.Close() + + dbConnectionPool, outerErr := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, outerErr) + defer dbConnectionPool.Close() + + ctx := context.Background() + ccm := &ClientConfigModel{DBConnectionPool: dbConnectionPool} + + walletID := "the_wallet_id" + encryptedAPIKey := "the_encrypted_api_key" + encrypterPublicKey := "the_encrypter_public_key" + + t.Run("return error on validation failure for no values", func(t *testing.T) { + err := ccm.Upsert(ctx, ClientConfigUpdate{ + WalletID: nil, + EncryptedAPIKey: nil, + EncrypterPublicKey: nil, + }) + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid circle config for insert: wallet_id, encrypted_api_key, and encrypter_public_key must be provided") + }) + + t.Run("return error on validation failure for partial values", func(t *testing.T) { + err := ccm.Upsert(ctx, ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: nil, + EncrypterPublicKey: nil, + }) + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid circle config for insert: wallet_id, encrypted_api_key, and encrypter_public_key must be provided") + }) + + t.Run("insert new config", func(t *testing.T) { + // Ensure there is an existing config + cc, err := ccm.Get(ctx) + assert.NoError(t, err) + assert.Nil(t, cc) + + err = ccm.Upsert(ctx, ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: &encrypterPublicKey, + }) + assert.NoError(t, err) + + cc, err = ccm.Get(ctx) + require.NoError(t, err) + require.NotNil(t, cc) + assert.Equal(t, walletID, *cc.WalletID) + assert.Equal(t, encryptedAPIKey, *cc.EncryptedAPIKey) + assert.Equal(t, encrypterPublicKey, *cc.EncrypterPublicKey) + }) +} + +func Test_ClientConfigModel_get(t *testing.T) { + dbt := dbtest.Open(t) + defer dbt.Close() + + dbConnectionPool, outerErr := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, outerErr) + defer dbConnectionPool.Close() + + ctx := context.Background() + ccm := &ClientConfigModel{DBConnectionPool: dbConnectionPool} + + walletID := "the_wallet_id" + encryptedAPIKey := "the_encrypted_api_key" + encrypterPublicKey := "the_encrypter_public_key" + + t.Run("retrieve existing config successfully", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + // Insert a record to retrieve + insertErr := ccm.insert(ctx, tx, ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: &encrypterPublicKey, + }) + require.NoError(t, insertErr) + + config, err := ccm.get(ctx, tx) + require.NoError(t, err) + require.NotNil(t, config) + assert.Equal(t, walletID, *config.WalletID) + assert.Equal(t, encryptedAPIKey, *config.EncryptedAPIKey) + assert.Equal(t, encrypterPublicKey, *config.EncrypterPublicKey) + }) + + t.Run("return nil if no config exists", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + config, err := ccm.get(ctx, tx) + require.NoError(t, err) + assert.Nil(t, config) + }) +} + +func Test_ClientConfigModel_update(t *testing.T) { + dbt := dbtest.Open(t) + defer dbt.Close() + + dbConnectionPool, outerErr := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, outerErr) + defer dbConnectionPool.Close() + + ctx := context.Background() + + ccm := &ClientConfigModel{DBConnectionPool: dbConnectionPool} + + walletID := "the_wallet_id" + encryptedAPIKey := "the_encrypted_api_key" + encrypterPublicKey := "the_encrypter_public_key" + + updatedWalletID := "another_wallet_id" + updatedEncryptedAPIKey := "another_encrypted_api_key" + updatedEncrypterPublicKey := "another_encrypter_public_key" + + // Insert a record to update + insertErr := ccm.insert(ctx, dbConnectionPool, ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: &encrypterPublicKey, + }) + require.NoError(t, insertErr) + + t.Run("return error if no fields are provided", func(t *testing.T) { + config := ClientConfigUpdate{} + err := ccm.update(ctx, dbConnectionPool, config) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid circle config for update: wallet_id or encrypted_api_key must be provided") + }) + + t.Run("update wallet_id successfully", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + config := ClientConfigUpdate{WalletID: &updatedWalletID} + + err := ccm.update(ctx, tx, config) + require.NoError(t, err) + + cc, err := ccm.get(ctx, tx) + assert.NoError(t, err) + assert.Equal(t, updatedWalletID, *cc.WalletID) + assert.Equal(t, encryptedAPIKey, *cc.EncryptedAPIKey) + assert.Equal(t, encrypterPublicKey, *cc.EncrypterPublicKey) + }) + + t.Run("updates encrypted_api_key and encrypter_public_key successfully", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + err := ccm.update(ctx, dbConnectionPool, ClientConfigUpdate{ + EncryptedAPIKey: &updatedEncryptedAPIKey, + EncrypterPublicKey: &updatedEncrypterPublicKey, + }) + require.NoError(t, err) + + cc, err := ccm.get(ctx, tx) + assert.NoError(t, err) + assert.Equal(t, walletID, *cc.WalletID) + assert.Equal(t, updatedEncryptedAPIKey, *cc.EncryptedAPIKey) + assert.Equal(t, updatedEncrypterPublicKey, *cc.EncrypterPublicKey) + }) + + t.Run("updates both wallet_id and encrypted_api_key with encrypter_public_key successfully", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + err := ccm.update(ctx, dbConnectionPool, ClientConfigUpdate{ + WalletID: &updatedWalletID, + EncryptedAPIKey: &updatedEncryptedAPIKey, + EncrypterPublicKey: &updatedEncrypterPublicKey, + }) + require.NoError(t, err) + + cc, err := ccm.get(ctx, tx) + assert.NoError(t, err) + assert.Equal(t, updatedWalletID, *cc.WalletID) + assert.Equal(t, updatedEncryptedAPIKey, *cc.EncryptedAPIKey) + assert.Equal(t, updatedEncrypterPublicKey, *cc.EncrypterPublicKey) + }) +} + +func Test_ClientConfigModel_insert(t *testing.T) { + dbt := dbtest.Open(t) + defer dbt.Close() + + dbConnectionPool, outerErr := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, outerErr) + defer dbConnectionPool.Close() + + ctx := context.Background() + ccm := &ClientConfigModel{DBConnectionPool: dbConnectionPool} + + walletID := "the_wallet_id" + encryptedAPIKey := "the_encrypted_api_key" + encrypterPublicKey := "the_encrypter_public_key" + + t.Run("insert successfully", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + config := ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: &encrypterPublicKey, + } + + err := ccm.insert(ctx, tx, config) + require.NoError(t, err) + + cc, err := ccm.get(ctx, tx) + assert.NoError(t, err) + assert.Equal(t, walletID, *cc.WalletID) + assert.Equal(t, encryptedAPIKey, *cc.EncryptedAPIKey) + assert.Equal(t, encrypterPublicKey, *cc.EncrypterPublicKey) + }) + + t.Run("insert fails with missing encrypted_api_key", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + config := ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: nil, + EncrypterPublicKey: &encrypterPublicKey, + } + + err := ccm.insert(ctx, tx, config) + assert.Error(t, err) + assert.Contains(t, + err.Error(), + "invalid circle config for insert: wallet_id, encrypted_api_key, and encrypter_public_key must be provided") + }) + + t.Run("insert fails with missing wallet_id", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + config := ClientConfigUpdate{ + WalletID: nil, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: &encrypterPublicKey, + } + + err := ccm.insert(ctx, tx, config) + assert.Error(t, err) + assert.Contains(t, + err.Error(), + "invalid circle config for insert: wallet_id, encrypted_api_key, and encrypter_public_key must be provided") + }) + + t.Run("insert fails with missing encrypter_public_key", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + config := ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: nil, + } + + err := ccm.insert(ctx, tx, config) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid circle config for insert: wallet_id, encrypted_api_key, and encrypter_public_key must be provided") + }) + + t.Run("insert fails when inserting a second record", func(t *testing.T) { + tx := testutils.BeginTxWithRollback(t, ctx, dbConnectionPool) + + config := ClientConfigUpdate{ + WalletID: &walletID, + EncryptedAPIKey: &encryptedAPIKey, + EncrypterPublicKey: &encrypterPublicKey, + } + + err := ccm.insert(ctx, tx, config) + require.NoError(t, err) + + err = ccm.insert(ctx, tx, config) + assert.EqualError(t, err, "inserting circle config: pq: circle_client_config must contain exactly one row") + }) +} + +func Test_ClientConfigUpdate_Validate(t *testing.T) { + walletID := "wallet_id" + encryptedAPIKey := "encrypted_api_key" + encrypterPublicKey := "encrypter_public_key" + + tests := []struct { + name string + input ClientConfigUpdate + wantErr error + }{ + { + name: "both wallet_id and encrypted_api_key are nil", + input: ClientConfigUpdate{}, + wantErr: errors.New("wallet_id or encrypted_api_key must be provided"), + }, + { + name: "encrypted_api_key is provided without encrypter_public_key", + input: ClientConfigUpdate{EncryptedAPIKey: &encryptedAPIKey}, + wantErr: errors.New("encrypter_public_key must be provided if encrypted_api_key is provided"), + }, + { + name: "wallet_id is provided without encrypted_api_key", + input: ClientConfigUpdate{WalletID: &walletID}, + }, + { + name: "both wallet_id and encrypted_api_key are provided with encrypter_public_key", + input: ClientConfigUpdate{WalletID: &walletID, EncryptedAPIKey: &encryptedAPIKey, EncrypterPublicKey: &encrypterPublicKey}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.input.validate() + if tt.wantErr != nil { + assert.Equal(t, tt.wantErr.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/serve/httphandler/circle_config_handler.go b/internal/serve/httphandler/circle_config_handler.go new file mode 100644 index 000000000..05909a3f9 --- /dev/null +++ b/internal/serve/httphandler/circle_config_handler.go @@ -0,0 +1,97 @@ +package httphandler + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/stellar/go/keypair" + "github.com/stellar/go/support/render/httpjson" + + "github.com/stellar/stellar-disbursement-platform-backend/internal/circle" + "github.com/stellar/stellar-disbursement-platform-backend/internal/serve/httperror" + "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/signing" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" +) + +// CircleConfigHandler implements a handler to configure the Circle API access. +type CircleConfigHandler struct { + Encrypter sdpUtils.PrivateKeyEncrypter + EncryptionPassphrase string + CircleClientConfigModel *circle.ClientConfigModel + DistributionAccountResolver signing.DistributionAccountResolver +} + +type PatchCircleConfigRequest struct { + WalletID *string `json:"wallet_id"` + APIKey *string `json:"api_key"` +} + +// validate validates the request. +func (r PatchCircleConfigRequest) validate() error { + if r.WalletID == nil && r.APIKey == nil { + return fmt.Errorf("wallet_id or api_key must be provided") + } + return nil +} + +// Patch is a handler to configure the Circle API access. +func (h CircleConfigHandler) Patch(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + distAccount, err := h.DistributionAccountResolver.DistributionAccountFromContext(ctx) + if err != nil { + httperror.InternalError(ctx, "Cannot retrieve distribution account", err, nil).Render(w) + return + } + + if !distAccount.IsCircle() { + errResponseMsg := fmt.Sprintf("This endpoint is only available for tenants using %v", schema.CirclePlatform) + httperror.BadRequest(errResponseMsg, nil, nil).Render(w) + return + } + + var patchRequest PatchCircleConfigRequest + err = json.NewDecoder(r.Body).Decode(&patchRequest) + if err != nil { + httperror.BadRequest("Request body is not valid", err, nil).Render(w) + return + } + + if err = patchRequest.validate(); err != nil { + extras := map[string]interface{}{"validation_error": err.Error()} + httperror.BadRequest("Request body is not valid", err, extras).Render(w) + return + } + + var clientConfigUpdate circle.ClientConfigUpdate + if patchRequest.APIKey != nil { + kp, kpErr := keypair.ParseFull(h.EncryptionPassphrase) + if kpErr != nil { + httperror.InternalError(ctx, "Cannot parse the encryption keypair", kpErr, nil).Render(w) + return + } + + encryptedAPIKey, encryptErr := h.Encrypter.Encrypt(*patchRequest.APIKey, kp.Seed()) + if encryptErr != nil { + httperror.InternalError(ctx, "Cannot encrypt the API key", encryptErr, nil).Render(w) + return + } + clientConfigUpdate.EncryptedAPIKey = &encryptedAPIKey + encrypterPublicKey := kp.Address() + clientConfigUpdate.EncrypterPublicKey = &encrypterPublicKey + } + + if patchRequest.WalletID != nil { + clientConfigUpdate.WalletID = patchRequest.WalletID + } + + err = h.CircleClientConfigModel.Upsert(ctx, clientConfigUpdate) + if err != nil { + httperror.InternalError(ctx, "Cannot insert the Circle configuration", err, nil).Render(w) + return + } + + httpjson.RenderStatus(w, http.StatusOK, map[string]string{"message": "Circle configuration updated"}, httpjson.JSON) +} diff --git a/internal/serve/httphandler/circle_config_handler_test.go b/internal/serve/httphandler/circle_config_handler_test.go new file mode 100644 index 000000000..b3d4a8fb6 --- /dev/null +++ b/internal/serve/httphandler/circle_config_handler_test.go @@ -0,0 +1,160 @@ +package httphandler + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/go-chi/chi/v5" + "github.com/stellar/go/keypair" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "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/circle" + "github.com/stellar/stellar-disbursement-platform-backend/internal/testutils" + sigMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/signing/mocks" + "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" +) + +func TestCircleConfigHandler_Patch(t *testing.T) { + dbt := dbtest.Open(t) + defer dbt.Close() + + dbConnectionPool, outerErr := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, outerErr) + defer dbConnectionPool.Close() + + kp := keypair.MustRandom() + encryptionPassphrase := kp.Seed() + encryptionPublicKey := kp.Address() + + ccm := circle.ClientConfigModel{DBConnectionPool: dbConnectionPool} + encrypter := utils.DefaultPrivateKeyEncrypter{} + + validPatchRequest := PatchCircleConfigRequest{ + APIKey: utils.StringPtr("new_api_key"), + WalletID: utils.StringPtr("new_wallet_id"), + } + validRequestBody, err := json.Marshal(validPatchRequest) + require.NoError(t, err) + + invalidRequestBody, err := json.Marshal(PatchCircleConfigRequest{}) + require.NoError(t, err) + + testCases := []struct { + name string + prepareMocksFn func(t *testing.T, mDistributionAccountResolver *sigMocks.MockDistributionAccountResolver) + requestBody string + statusCode int + assertions func(t *testing.T, rr *httptest.ResponseRecorder) + }{ + { + name: "returns bad request for invalid request body", + prepareMocksFn: func(t *testing.T, m *sigMocks.MockDistributionAccountResolver) { + t.Helper() + m. + On("DistributionAccountFromContext", mock.Anything). + Return(schema.TransactionAccount{Type: schema.DistributionAccountCircleDBVault}, nil). + Once() + }, + requestBody: "invalid json", + statusCode: http.StatusBadRequest, + assertions: func(t *testing.T, rr *httptest.ResponseRecorder) { + t.Helper() + + assert.JSONEq(t, `{"error": "Request body is not valid"}`, rr.Body.String()) + }, + }, + { + name: "returns bad request for invalid patch request", + prepareMocksFn: func(t *testing.T, m *sigMocks.MockDistributionAccountResolver) { + t.Helper() + m. + On("DistributionAccountFromContext", mock.Anything). + Return(schema.TransactionAccount{Type: schema.DistributionAccountCircleDBVault}, nil). + Once() + }, + requestBody: string(invalidRequestBody), + statusCode: http.StatusBadRequest, + assertions: func(t *testing.T, rr *httptest.ResponseRecorder) { + t.Helper() + + assert.JSONEq(t, `{"error":"Request body is not valid", "extras":{"validation_error":"wallet_id or api_key must be provided"}}`, rr.Body.String()) + }, + }, + { + name: "returns bad request if distribution account type is not Circle", + prepareMocksFn: func(t *testing.T, m *sigMocks.MockDistributionAccountResolver) { + t.Helper() + m. + On("DistributionAccountFromContext", mock.Anything). + Return(schema.TransactionAccount{Type: schema.DistributionAccountStellarEnv}, nil). + Once() + }, + requestBody: string(validRequestBody), + statusCode: http.StatusBadRequest, + assertions: func(t *testing.T, rr *httptest.ResponseRecorder) { + t.Helper() + + assert.JSONEq(t, `{"error": "This endpoint is only available for tenants using CIRCLE"}`, rr.Body.String()) + }, + }, + { + name: "updates Circle configuration successfully", + prepareMocksFn: func(t *testing.T, m *sigMocks.MockDistributionAccountResolver) { + t.Helper() + m. + On("DistributionAccountFromContext", mock.Anything). + Return(schema.TransactionAccount{Type: schema.DistributionAccountCircleDBVault}, nil). + Once() + }, + requestBody: string(validRequestBody), + statusCode: http.StatusOK, + assertions: func(t *testing.T, rr *httptest.ResponseRecorder) { + t.Helper() + + // Check the updated config in the database + config, err := ccm.Get(context.Background()) + require.NoError(t, err) + require.NotNil(t, config) + assert.Equal(t, "new_wallet_id", *config.WalletID) + + decryptedAPIKey, err := encrypter.Decrypt(*config.EncryptedAPIKey, encryptionPassphrase) + assert.NoError(t, err) + assert.Equal(t, "new_api_key", decryptedAPIKey) + assert.Equal(t, encryptionPublicKey, *config.EncrypterPublicKey) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mDistributionAccountResolver := sigMocks.NewMockDistributionAccountResolver(t) + handler := CircleConfigHandler{ + Encrypter: &encrypter, + EncryptionPassphrase: encryptionPassphrase, + CircleClientConfigModel: &ccm, + DistributionAccountResolver: mDistributionAccountResolver, + } + + if tc.prepareMocksFn != nil { + tc.prepareMocksFn(t, mDistributionAccountResolver) + } + + r := chi.NewRouter() + url := "/organization/circle-config" + r.Patch(url, handler.Patch) + + rr := testutils.Request(t, r, url, http.MethodPatch, strings.NewReader(tc.requestBody)) + assert.Equal(t, tc.statusCode, rr.Code) + tc.assertions(t, rr) + }) + } +} diff --git a/internal/serve/serve.go b/internal/serve/serve.go index e08195aab..abd472ba9 100644 --- a/internal/serve/serve.go +++ b/internal/serve/serve.go @@ -87,6 +87,7 @@ type ServeOptions struct { EnableScheduler bool tenantManager tenant.ManagerInterface DistributionAccountService services.DistributionAccountServiceInterface + DistAccEncryptionPassphrase string EventProducer events.Producer MaxInvitationSMSResendAttempts int SingleTenantMode bool @@ -389,6 +390,14 @@ func handleHTTP(o ServeOptions) *chi.Mux { r.With(middleware.AnyRoleMiddleware(authManager, data.GetAllRoles()...)). Get("/logo", profileHandler.GetOrganizationLogo) + + r.With(middleware.AnyRoleMiddleware(authManager, data.OwnerUserRole)). + Patch("/circle-config", httphandler.CircleConfigHandler{ + Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + EncryptionPassphrase: o.DistAccEncryptionPassphrase, + CircleClientConfigModel: circle.NewClientConfigModel(o.MtnDBConnectionPool), + DistributionAccountResolver: o.SubmitterEngine.DistributionAccountResolver, + }.Patch) }) balancesHandler := httphandler.BalancesHandler{ diff --git a/internal/serve/serve_test.go b/internal/serve/serve_test.go index dc251c56a..98b19b220 100644 --- a/internal/serve/serve_test.go +++ b/internal/serve/serve_test.go @@ -468,6 +468,7 @@ func Test_handleHTTP_authenticatedEndpoints(t *testing.T) { {http.MethodGet, "/organization"}, {http.MethodPatch, "/organization"}, {http.MethodGet, "/organization/logo"}, + {http.MethodPatch, "/organization/circle-config"}, // Balances {http.MethodGet, "/balances"}, } diff --git a/internal/testutils/db.go b/internal/testutils/db.go new file mode 100644 index 000000000..c5acfd8b3 --- /dev/null +++ b/internal/testutils/db.go @@ -0,0 +1,37 @@ +package testutils + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/stellar/stellar-disbursement-platform-backend/db" +) + +func BeginTxWithRollback(t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool) db.DBTransaction { + t.Helper() + + return beginTx(t, ctx, dbConnectionPool, true) +} + +func beginTx(t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool, autoRollback bool) db.DBTransaction { + t.Helper() + + dbTx, err := dbConnectionPool.BeginTxx(ctx, nil) + require.NoError(t, err) + + if autoRollback { + t.Cleanup(func() { + rollback(t, dbTx) + }) + } + return dbTx +} + +func rollback(t *testing.T, dbTx db.DBTransaction) { + t.Helper() + + err := dbTx.Rollback() + require.NoError(t, err) +} diff --git a/internal/testutils/http.go b/internal/testutils/http.go new file mode 100644 index 000000000..f4ef00f6a --- /dev/null +++ b/internal/testutils/http.go @@ -0,0 +1,22 @@ +package testutils + +import ( + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-chi/chi/v5" + "github.com/stretchr/testify/require" +) + +func Request(t *testing.T, r *chi.Mux, url, httpMethod string, body io.Reader) *httptest.ResponseRecorder { + t.Helper() + + req, err := http.NewRequest(httpMethod, url, body) + require.NoError(t, err) + + rr := httptest.NewRecorder() + r.ServeHTTP(rr, req) + return rr +} diff --git a/internal/transactionsubmission/engine/signing/channel_account_db_signature_client.go b/internal/transactionsubmission/engine/signing/channel_account_db_signature_client.go index 6cb1aa2c1..2545c11c0 100644 --- a/internal/transactionsubmission/engine/signing/channel_account_db_signature_client.go +++ b/internal/transactionsubmission/engine/signing/channel_account_db_signature_client.go @@ -11,7 +11,6 @@ import ( "github.com/stellar/stellar-disbursement-platform-backend/db" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" ) @@ -19,7 +18,7 @@ type ChannelAccountDBSignatureClientOptions struct { NetworkPassphrase string DBConnectionPool db.DBConnectionPool EncryptionPassphrase string - Encrypter utils.PrivateKeyEncrypter + Encrypter sdpUtils.PrivateKeyEncrypter LedgerNumberTracker preconditions.LedgerNumberTracker } @@ -47,7 +46,7 @@ type ChannelAccountDBSignatureClient struct { networkPassphrase string dbConnectionPool db.DBConnectionPool chAccModel store.ChannelAccountStore - encrypter utils.PrivateKeyEncrypter + encrypter sdpUtils.PrivateKeyEncrypter encryptionPassphrase string ledgerNumberTracker preconditions.LedgerNumberTracker } @@ -60,7 +59,7 @@ func NewChannelAccountDBSignatureClient(opts ChannelAccountDBSignatureClientOpti encrypter := opts.Encrypter if encrypter == nil { - encrypter = &utils.DefaultPrivateKeyEncrypter{} + encrypter = &sdpUtils.DefaultPrivateKeyEncrypter{} } return &ChannelAccountDBSignatureClient{ diff --git a/internal/transactionsubmission/engine/signing/channel_account_db_signature_client_test.go b/internal/transactionsubmission/engine/signing/channel_account_db_signature_client_test.go index 50bd12eae..0009f6eae 100644 --- a/internal/transactionsubmission/engine/signing/channel_account_db_signature_client_test.go +++ b/internal/transactionsubmission/engine/signing/channel_account_db_signature_client_test.go @@ -6,6 +6,8 @@ import ( "reflect" "testing" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/keypair" "github.com/stellar/go/network" "github.com/stellar/go/txnbuild" @@ -16,7 +18,6 @@ import ( "github.com/stellar/stellar-disbursement-platform-backend/db/dbtest" preconditionsMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions/mocks" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" ) func Test_ChannelAccountDBSignatureClientOptions_Validate(t *testing.T) { @@ -119,7 +120,7 @@ func Test_NewChannelAccountDBSignatureClient(t *testing.T) { EncryptionPassphrase: "SCPGNK3MRMXKNWGZ4ET3JZ6RUJIN7FMHT4ASVXDG7YPBL4WKBQNEL63F", LedgerNumberTracker: mLedgerNumberTracker, }, - wantEncrypterTypeName: reflect.TypeOf(&utils.DefaultPrivateKeyEncrypter{}).String(), + wantEncrypterTypeName: reflect.TypeOf(&sdpUtils.DefaultPrivateKeyEncrypter{}).String(), }, { name: "🎉 Successfully instantiates a new channel account DB signature client with a custom encrypter", @@ -128,9 +129,9 @@ func Test_NewChannelAccountDBSignatureClient(t *testing.T) { DBConnectionPool: dbConnectionPool, EncryptionPassphrase: "SCPGNK3MRMXKNWGZ4ET3JZ6RUJIN7FMHT4ASVXDG7YPBL4WKBQNEL63F", LedgerNumberTracker: mLedgerNumberTracker, - Encrypter: &utils.PrivateKeyEncrypterMock{}, + Encrypter: &sdpUtils.PrivateKeyEncrypterMock{}, }, - wantEncrypterTypeName: reflect.TypeOf(&utils.PrivateKeyEncrypterMock{}).String(), + wantEncrypterTypeName: reflect.TypeOf(&sdpUtils.PrivateKeyEncrypterMock{}).String(), }, } @@ -171,7 +172,7 @@ func Test_ChannelAccountDBSignatureClient_getKPsForAccounts(t *testing.T) { chAccountStore := store.NewChannelAccountModel(dbConnectionPool) // create default encrypter - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} encrypterPass := keypair.MustRandom().Seed() // create channel accounts in the DB @@ -264,7 +265,7 @@ func Test_ChannelAccountDBSignatureClient_SignStellarTransaction(t *testing.T) { // create default encrypter encrypterPass := keypair.MustRandom().Seed() - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} // create channel accounts in the DB channelAccounts := store.CreateChannelAccountFixturesEncryptedKPs(t, ctx, dbConnectionPool, encrypter, encrypterPass, 2) @@ -359,7 +360,7 @@ func Test_ChannelAccountDBSignatureClient_SignFeeBumpStellarTransaction(t *testi // create default encrypter encrypterPass := keypair.MustRandom().Seed() - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} // create channel accounts in the DB channelAccounts := store.CreateChannelAccountFixturesEncryptedKPs(t, ctx, dbConnectionPool, encrypter, encrypterPass, 2) @@ -369,7 +370,7 @@ func Test_ChannelAccountDBSignatureClient_SignFeeBumpStellarTransaction(t *testi NetworkPassphrase: network.TestNetworkPassphrase, DBConnectionPool: dbConnectionPool, EncryptionPassphrase: encrypterPass, - Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + Encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, LedgerNumberTracker: preconditionsMocks.NewMockLedgerNumberTracker(t), }) require.NoError(t, err) @@ -485,7 +486,7 @@ func Test_ChannelAccountDBSignatureClient_BatchInsert(t *testing.T) { mLedgerNumberTracker := preconditionsMocks.NewMockLedgerNumberTracker(t) mLedgerNumberTracker.On("GetLedgerNumber").Return(100, nil).Once() - defaultEncrypter := &utils.DefaultPrivateKeyEncrypter{} + defaultEncrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} encrypterPass := distributionKP.Seed() sigClient, err := NewChannelAccountDBSignatureClient(ChannelAccountDBSignatureClientOptions{ NetworkPassphrase: network.TestNetworkPassphrase, @@ -548,7 +549,7 @@ func Test_ChannelAccountDBSignatureClient_Delete(t *testing.T) { // create default encrypter encrypterPass := keypair.MustRandom().Seed() - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} // current ledger number currLedgerNumber := 0 diff --git a/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client.go b/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client.go index 6effe8e57..f4b6d8fdf 100644 --- a/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client.go +++ b/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client.go @@ -10,7 +10,6 @@ import ( "github.com/stellar/stellar-disbursement-platform-backend/db" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" ) @@ -18,7 +17,7 @@ type DistributionAccountDBVaultSignatureClientOptions struct { NetworkPassphrase string DBConnectionPool db.DBConnectionPool EncryptionPassphrase string - Encrypter utils.PrivateKeyEncrypter + Encrypter sdpUtils.PrivateKeyEncrypter } func (opts *DistributionAccountDBVaultSignatureClientOptions) Validate() error { @@ -40,7 +39,7 @@ func (opts *DistributionAccountDBVaultSignatureClientOptions) Validate() error { type DistributionAccountDBVaultSignatureClient struct { networkPassphrase string dbVault store.DBVault - encrypter utils.PrivateKeyEncrypter + encrypter sdpUtils.PrivateKeyEncrypter encryptionPassphrase string } @@ -52,7 +51,7 @@ func NewDistributionAccountDBVaultSignatureClient(opts DistributionAccountDBVaul encrypter := opts.Encrypter if encrypter == nil { - encrypter = &utils.DefaultPrivateKeyEncrypter{} + encrypter = &sdpUtils.DefaultPrivateKeyEncrypter{} } return &DistributionAccountDBVaultSignatureClient{ diff --git a/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client_test.go b/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client_test.go index 4476fa20c..78c5f2fb5 100644 --- a/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client_test.go +++ b/internal/transactionsubmission/engine/signing/distribution_account_db_vault_signature_client_test.go @@ -5,6 +5,8 @@ import ( "reflect" "testing" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/keypair" "github.com/stellar/go/network" "github.com/stellar/go/txnbuild" @@ -14,7 +16,6 @@ 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/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" ) func Test_DistributionAccountDBVaultSignatureClientOptions_Validate(t *testing.T) { @@ -104,7 +105,7 @@ func Test_NewDistributionAccountDBVaultSignatureClient(t *testing.T) { DBConnectionPool: dbConnectionPool, EncryptionPassphrase: "SCPGNK3MRMXKNWGZ4ET3JZ6RUJIN7FMHT4ASVXDG7YPBL4WKBQNEL63F", }, - wantEncrypterTypeName: reflect.TypeOf(&utils.DefaultPrivateKeyEncrypter{}).String(), + wantEncrypterTypeName: reflect.TypeOf(&sdpUtils.DefaultPrivateKeyEncrypter{}).String(), }, { name: "🎉 Successfully instantiates a new distribution account DB signature client with a custom encrypter", @@ -112,9 +113,9 @@ func Test_NewDistributionAccountDBVaultSignatureClient(t *testing.T) { NetworkPassphrase: network.TestNetworkPassphrase, DBConnectionPool: dbConnectionPool, EncryptionPassphrase: "SCPGNK3MRMXKNWGZ4ET3JZ6RUJIN7FMHT4ASVXDG7YPBL4WKBQNEL63F", - Encrypter: &utils.PrivateKeyEncrypterMock{}, + Encrypter: &sdpUtils.PrivateKeyEncrypterMock{}, }, - wantEncrypterTypeName: reflect.TypeOf(&utils.PrivateKeyEncrypterMock{}).String(), + wantEncrypterTypeName: reflect.TypeOf(&sdpUtils.PrivateKeyEncrypterMock{}).String(), }, } @@ -155,7 +156,7 @@ func Test_DistributionAccountDBVaultSignatureClient_getKPsForAccounts(t *testing dbVaultStore := store.NewDBVaultModel(dbConnectionPool) // create default encrypter - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} encrypterPass := keypair.MustRandom().Seed() // create distribution accounts in the DB @@ -248,7 +249,7 @@ func Test_DistributionAccountDBVaultSignatureClient_SignStellarTransaction(t *te // create default encrypter encrypterPass := keypair.MustRandom().Seed() - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} // create distribution accounts in the DB distributionAccounts := store.CreateDBVaultFixturesEncryptedKPs(t, ctx, dbConnectionPool, encrypter, encrypterPass, 2) @@ -343,7 +344,7 @@ func Test_DistributionAccountDBVaultSignatureClient_SignFeeBumpStellarTransactio // create default encrypter encrypterPass := keypair.MustRandom().Seed() - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} // create distribution accounts in the DB distributionAccounts := store.CreateDBVaultFixturesEncryptedKPs(t, ctx, dbConnectionPool, encrypter, encrypterPass, 2) @@ -354,7 +355,7 @@ func Test_DistributionAccountDBVaultSignatureClient_SignFeeBumpStellarTransactio NetworkPassphrase: network.TestNetworkPassphrase, DBConnectionPool: dbConnectionPool, EncryptionPassphrase: encrypterPass, - Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + Encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, }) require.NoError(t, err) @@ -475,7 +476,7 @@ func Test_DistributionAccountDBVaultSignatureClient_BatchInsert(t *testing.T) { }, } - defaultEncrypter := &utils.DefaultPrivateKeyEncrypter{} + defaultEncrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} encrypterPass := distributionKP.Seed() sigClient, err := NewDistributionAccountDBVaultSignatureClient(DistributionAccountDBVaultSignatureClientOptions{ NetworkPassphrase: network.TestNetworkPassphrase, @@ -533,7 +534,7 @@ func Test_DistributionAccountDBVaultSignatureClient_Delete(t *testing.T) { // create default encrypter encrypterPass := keypair.MustRandom().Seed() - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} // at start: count=0 allDistAccounts := allDBVaultEntries(t, ctx, dbConnectionPool) diff --git a/internal/transactionsubmission/engine/signing/signature_client.go b/internal/transactionsubmission/engine/signing/signature_client.go index 22be60cef..e9dd61c9d 100644 --- a/internal/transactionsubmission/engine/signing/signature_client.go +++ b/internal/transactionsubmission/engine/signing/signature_client.go @@ -4,11 +4,12 @@ import ( "context" "fmt" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/txnbuild" "github.com/stellar/stellar-disbursement-platform-backend/db" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" ) @@ -39,7 +40,7 @@ type SignatureClientOptions struct { // *AccountDB: DBConnectionPool db.DBConnectionPool LedgerNumberTracker preconditions.LedgerNumberTracker - Encrypter utils.PrivateKeyEncrypter // (optional) + Encrypter sdpUtils.PrivateKeyEncrypter // (optional) } func NewSignatureClient(accType schema.AccountType, opts SignatureClientOptions) (SignatureClient, error) { diff --git a/internal/transactionsubmission/engine/signing/signature_client_test.go b/internal/transactionsubmission/engine/signing/signature_client_test.go index 84ce55802..19b23fb45 100644 --- a/internal/transactionsubmission/engine/signing/signature_client_test.go +++ b/internal/transactionsubmission/engine/signing/signature_client_test.go @@ -3,6 +3,8 @@ package signing import ( "testing" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/keypair" "github.com/stellar/go/network" "github.com/stretchr/testify/assert" @@ -12,7 +14,6 @@ import ( "github.com/stellar/stellar-disbursement-platform-backend/db/dbtest" preconditionsMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions/mocks" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" ) @@ -52,7 +53,7 @@ func Test_NewSignatureClient(t *testing.T) { wantResult: &ChannelAccountDBSignatureClient{ chAccModel: store.NewChannelAccountModel(dbConnectionPool), dbConnectionPool: dbConnectionPool, - encrypter: &utils.DefaultPrivateKeyEncrypter{}, + encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, encryptionPassphrase: encryptionPassphrase, ledgerNumberTracker: mLedgerNumberTracker, networkPassphrase: network.TestNetworkPassphrase, @@ -65,11 +66,11 @@ func Test_NewSignatureClient(t *testing.T) { NetworkPassphrase: network.TestNetworkPassphrase, DBConnectionPool: dbConnectionPool, DistAccEncryptionPassphrase: encryptionPassphrase, - Encrypter: &utils.PrivateKeyEncrypterMock{}, + Encrypter: &sdpUtils.PrivateKeyEncrypterMock{}, }, wantResult: &DistributionAccountDBVaultSignatureClient{ dbVault: store.NewDBVaultModel(dbConnectionPool), - encrypter: &utils.PrivateKeyEncrypterMock{}, + encrypter: &sdpUtils.PrivateKeyEncrypterMock{}, encryptionPassphrase: encryptionPassphrase, networkPassphrase: network.TestNetworkPassphrase, }, diff --git a/internal/transactionsubmission/engine/signing/signature_service.go b/internal/transactionsubmission/engine/signing/signature_service.go index ec6fb9ee9..dde3ae384 100644 --- a/internal/transactionsubmission/engine/signing/signature_service.go +++ b/internal/transactionsubmission/engine/signing/signature_service.go @@ -3,9 +3,10 @@ package signing import ( "fmt" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/stellar-disbursement-platform-backend/db" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" ) type SignatureService struct { @@ -50,7 +51,7 @@ type SignatureServiceOptions struct { // *AccountDB: DBConnectionPool db.DBConnectionPool LedgerNumberTracker preconditions.LedgerNumberTracker - Encrypter utils.PrivateKeyEncrypter + Encrypter sdpUtils.PrivateKeyEncrypter // DistributionAccountResolver DistributionAccountResolver diff --git a/internal/transactionsubmission/engine/signing/signature_service_test.go b/internal/transactionsubmission/engine/signing/signature_service_test.go index 24587b0a9..6d77920b7 100644 --- a/internal/transactionsubmission/engine/signing/signature_service_test.go +++ b/internal/transactionsubmission/engine/signing/signature_service_test.go @@ -3,6 +3,8 @@ package signing import ( "testing" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/keypair" "github.com/stellar/go/network" "github.com/stretchr/testify/require" @@ -12,7 +14,6 @@ import ( preconditionsMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions/mocks" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/signing/mocks" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" ) @@ -107,7 +108,7 @@ func Test_NewSignatureService(t *testing.T) { encryptionPassphrase: chAccEncryptionPassphrase, ledgerNumberTracker: mLedgerNumberTracker, chAccModel: store.NewChannelAccountModel(dbConnectionPool), - encrypter: &utils.DefaultPrivateKeyEncrypter{}, + encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } wantDistAccountEnvSigner := &AccountEnvSignatureClient{ networkPassphrase: network.TestNetworkPassphrase, @@ -125,7 +126,7 @@ func Test_NewSignatureService(t *testing.T) { networkPassphrase: network.TestNetworkPassphrase, encryptionPassphrase: distAccEncryptionPassphrase, dbVault: store.NewDBVaultModel(dbConnectionPool), - encrypter: &utils.DefaultPrivateKeyEncrypter{}, + encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } wantSigRouterStrategies := map[schema.AccountType]SignatureClient{ schema.HostStellarEnv: wantHostAccountEnvSigner, diff --git a/internal/transactionsubmission/engine/signing/signer_router.go b/internal/transactionsubmission/engine/signing/signer_router.go index a0fc250df..29447dc38 100644 --- a/internal/transactionsubmission/engine/signing/signer_router.go +++ b/internal/transactionsubmission/engine/signing/signer_router.go @@ -6,12 +6,13 @@ import ( "fmt" "sort" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/txnbuild" "golang.org/x/exp/maps" "github.com/stellar/stellar-disbursement-platform-backend/db" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" ) @@ -58,7 +59,7 @@ type SignatureRouterOptions struct { // *.STELLAR.DB_VAULT: DBConnectionPool db.DBConnectionPool - Encrypter utils.PrivateKeyEncrypter // (optional) + Encrypter sdpUtils.PrivateKeyEncrypter // (optional) } func NewSignerRouter(opts SignatureRouterOptions, accountTypes ...schema.AccountType) (SignerRouter, error) { diff --git a/internal/transactionsubmission/engine/signing/signer_router_test.go b/internal/transactionsubmission/engine/signing/signer_router_test.go index 8b0782801..f897c5aec 100644 --- a/internal/transactionsubmission/engine/signing/signer_router_test.go +++ b/internal/transactionsubmission/engine/signing/signer_router_test.go @@ -6,6 +6,8 @@ import ( "fmt" "testing" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/keypair" "github.com/stellar/go/network" "github.com/stellar/go/txnbuild" @@ -17,7 +19,6 @@ import ( preconditionsMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions/mocks" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/signing/mocks" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" ) @@ -43,7 +44,7 @@ func Test_NewSignerRouter(t *testing.T) { DistAccEncryptionPassphrase: distAccEncryptionPassphrase, DistributionPrivateKey: distributionKP.Seed(), HostPrivateKey: hostKP.Seed(), - Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + Encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } // Create valid SignerClients: @@ -59,7 +60,7 @@ func Test_NewSignerRouter(t *testing.T) { encryptionPassphrase: chAccEncryptionPassphrase, ledgerNumberTracker: mLedgerNumberTracker, chAccModel: store.NewChannelAccountModel(dbConnectionPool), - encrypter: &utils.DefaultPrivateKeyEncrypter{}, + encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } wantDistAccStelarEnvSigner := &AccountEnvSignatureClient{ networkPassphrase: networkPassphrase, @@ -71,7 +72,7 @@ func Test_NewSignerRouter(t *testing.T) { networkPassphrase: networkPassphrase, encryptionPassphrase: distAccEncryptionPassphrase, dbVault: store.NewDBVaultModel(dbConnectionPool), - encrypter: &utils.DefaultPrivateKeyEncrypter{}, + encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } testCases := []struct { @@ -232,7 +233,7 @@ func Test_SignerRouterImpl_RouteSigner(t *testing.T) { DistAccEncryptionPassphrase: distAccEncryptionPassphrase, DistributionPrivateKey: distributionKP.Seed(), HostPrivateKey: hostKP.Seed(), - Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + Encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } sigRouter, err := NewSignerRouter(validOptions) @@ -642,7 +643,7 @@ func Test_SignerRouterImpl_BatchInsert(t *testing.T) { DistAccEncryptionPassphrase: distAccEncryptionPassphrase, DistributionPrivateKey: distributionKP.Seed(), HostPrivateKey: hostKP.Seed(), - Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + Encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } testCases := []struct { @@ -759,7 +760,7 @@ func Test_SignerRouterImpl_Delete(t *testing.T) { DistAccEncryptionPassphrase: distAccEncryptionPassphrase, DistributionPrivateKey: distributionKP.Seed(), HostPrivateKey: hostKP.Seed(), - Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + Encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } sigRouter, err := NewSignerRouter(validOptions) require.NoError(t, err) @@ -920,7 +921,7 @@ func Test_SignerRouterImpl_SupportedAccountTypes(t *testing.T) { DistAccEncryptionPassphrase: distAccEncryptionPassphrase, DistributionPrivateKey: distributionKP.Seed(), HostPrivateKey: hostKP.Seed(), - Encrypter: &utils.DefaultPrivateKeyEncrypter{}, + Encrypter: &sdpUtils.DefaultPrivateKeyEncrypter{}, } testCases := []struct { diff --git a/internal/transactionsubmission/manager_test.go b/internal/transactionsubmission/manager_test.go index 355ddbb57..7d7c26887 100644 --- a/internal/transactionsubmission/manager_test.go +++ b/internal/transactionsubmission/manager_test.go @@ -7,6 +7,8 @@ import ( "testing" "time" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/clients/horizonclient" "github.com/stellar/go/keypair" "github.com/stellar/go/network" @@ -29,7 +31,6 @@ import ( tssMonitor "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/monitor" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" storeMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store/mocks" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" ) @@ -374,7 +375,7 @@ func Test_Manager_ProcessTransactions(t *testing.T) { defer dbConnectionPool.Close() // Signature service - encrypter := &utils.DefaultPrivateKeyEncrypter{} + encrypter := &sdpUtils.DefaultPrivateKeyEncrypter{} chAccEncryptionPassphrase := keypair.MustRandom().Seed() distAccEncryptionPassphrase := keypair.MustRandom().Seed() distributionKP := keypair.MustRandom() diff --git a/internal/transactionsubmission/services/horizon_test.go b/internal/transactionsubmission/services/horizon_test.go index 67dd8a35a..53e15fe32 100644 --- a/internal/transactionsubmission/services/horizon_test.go +++ b/internal/transactionsubmission/services/horizon_test.go @@ -8,6 +8,8 @@ import ( "net/http" "testing" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/stellar/go/clients/horizonclient" "github.com/stellar/go/keypair" "github.com/stellar/go/network" @@ -27,7 +29,6 @@ import ( "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/internal/transactionsubmission/store" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" ) @@ -55,7 +56,7 @@ func Test_CreateChannelAccountsOnChain(t *testing.T) { testCases := []struct { name string numOfChanAccToCreate int - prepareMocksFn func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *utils.PrivateKeyEncrypterMock) + prepareMocksFn func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *sdpUtils.PrivateKeyEncrypterMock) wantErrContains string }{ { @@ -76,7 +77,7 @@ func Test_CreateChannelAccountsOnChain(t *testing.T) { { name: "returns error when HorizonClient fails getting AccountDetails", numOfChanAccToCreate: 2, - prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, _ *preconditionsMocks.MockLedgerNumberTracker, _ *utils.PrivateKeyEncrypterMock) { + prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, _ *preconditionsMocks.MockLedgerNumberTracker, _ *sdpUtils.PrivateKeyEncrypterMock) { horizonClientMock. On("AccountDetail", horizonclient.AccountRequest{AccountID: hostAccount.Address}). Return(horizon.Account{}, horizonclient.Error{ @@ -89,7 +90,7 @@ func Test_CreateChannelAccountsOnChain(t *testing.T) { { name: "returns error when fails to retrieve ledger bounds", numOfChanAccToCreate: 2, - prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, _ *utils.PrivateKeyEncrypterMock) { + prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, _ *sdpUtils.PrivateKeyEncrypterMock) { horizonClientMock. On("AccountDetail", horizonclient.AccountRequest{AccountID: hostAccount.Address}). Return(horizon.Account{ @@ -107,7 +108,7 @@ func Test_CreateChannelAccountsOnChain(t *testing.T) { { name: "returns error when fails encrypting private key", numOfChanAccToCreate: 2, - prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *utils.PrivateKeyEncrypterMock) { + prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *sdpUtils.PrivateKeyEncrypterMock) { horizonClientMock. On("AccountDetail", horizonclient.AccountRequest{AccountID: hostAccount.Address}). Return(horizon.Account{ @@ -128,7 +129,7 @@ func Test_CreateChannelAccountsOnChain(t *testing.T) { { name: "returns error when fails submitting transaction to horizon", numOfChanAccToCreate: 2, - prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *utils.PrivateKeyEncrypterMock) { + prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *sdpUtils.PrivateKeyEncrypterMock) { horizonClientMock. On("AccountDetail", horizonclient.AccountRequest{AccountID: hostAccount.Address}). Return(horizon.Account{ @@ -161,7 +162,7 @@ func Test_CreateChannelAccountsOnChain(t *testing.T) { { name: "🎉 successfully creates channel accounts on-chain (ENCRYPTED)", numOfChanAccToCreate: 3, - prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *utils.PrivateKeyEncrypterMock) { + prepareMocksFn: func(horizonClientMock *horizonclient.MockClient, mLedgerNumberTracker *preconditionsMocks.MockLedgerNumberTracker, privateKeyEncrypterMock *sdpUtils.PrivateKeyEncrypterMock) { horizonClientMock. On("AccountDetail", horizonclient.AccountRequest{AccountID: hostAccount.Address}). Return(horizon.Account{ @@ -195,7 +196,7 @@ func Test_CreateChannelAccountsOnChain(t *testing.T) { // Prepare mocks mLedgerNumberTracker := preconditionsMocks.NewMockLedgerNumberTracker(t) horizonClientMock := &horizonclient.MockClient{} - privateKeyEncrypterMock := &utils.PrivateKeyEncrypterMock{} + privateKeyEncrypterMock := &sdpUtils.PrivateKeyEncrypterMock{} mDistAccResolver := sigMocks.NewMockDistributionAccountResolver(t) mDistAccResolver. On("HostDistributionAccount"). @@ -396,7 +397,7 @@ func Test_DeleteChannelAccountOnChain(t *testing.T) { // prepare mocks mLedgerNumberTracker := preconditionsMocks.NewMockLedgerNumberTracker(t) horizonClientMock := &horizonclient.MockClient{} - privateKeyEncrypterMock := &utils.PrivateKeyEncrypterMock{} + privateKeyEncrypterMock := &sdpUtils.PrivateKeyEncrypterMock{} sigService, sigRouter, mDistAccResolver := signing.NewMockSignatureService(t) mDistAccResolver. On("HostDistributionAccount"). diff --git a/internal/transactionsubmission/store/fixtures.go b/internal/transactionsubmission/store/fixtures.go index 0a7b1ee0e..8412aad78 100644 --- a/internal/transactionsubmission/store/fixtures.go +++ b/internal/transactionsubmission/store/fixtures.go @@ -7,12 +7,13 @@ import ( "testing" "time" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + "github.com/lib/pq" "github.com/stellar/go/keypair" "github.com/stretchr/testify/require" "github.com/stellar/stellar-disbursement-platform-backend/db" - "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" ) // CreateTransactionFixtures creates count number submitter transactions @@ -122,7 +123,7 @@ func CreateChannelAccountFixturesEncrypted( t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool, - encrypter utils.PrivateKeyEncrypter, + encrypter sdpUtils.PrivateKeyEncrypter, encryptionPassphrase string, count int, ) []*ChannelAccount { @@ -152,7 +153,7 @@ func CreateChannelAccountFixturesEncryptedKPs( t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool, - encrypter utils.PrivateKeyEncrypter, + encrypter sdpUtils.PrivateKeyEncrypter, encryptionPassphrase string, count int, ) []*keypair.Full { @@ -185,7 +186,7 @@ func CreateDBVaultFixturesEncrypted( t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool, - encrypter utils.PrivateKeyEncrypter, + encrypter sdpUtils.PrivateKeyEncrypter, encryptionPassphrase string, count int, ) []*DBVaultEntry { @@ -223,7 +224,7 @@ func CreateDBVaultFixturesEncryptedKPs( t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool, - encrypter utils.PrivateKeyEncrypter, + encrypter sdpUtils.PrivateKeyEncrypter, encryptionPassphrase string, count int, ) []*keypair.Full { diff --git a/internal/transactionsubmission/transaction_worker_test.go b/internal/transactionsubmission/transaction_worker_test.go index cd9e3d7df..e5020f802 100644 --- a/internal/transactionsubmission/transaction_worker_test.go +++ b/internal/transactionsubmission/transaction_worker_test.go @@ -43,7 +43,7 @@ import ( "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store" storeMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store/mocks" "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" - sdpUtlis "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" + sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" "github.com/stellar/stellar-disbursement-platform-backend/pkg/schema" "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" ) @@ -102,7 +102,7 @@ func getTransactionWorkerInstance(t *testing.T, dbConnectionPool db.DBConnection } var ( - encrypter = &utils.DefaultPrivateKeyEncrypter{} + encrypter = &sdpUtils.DefaultPrivateKeyEncrypter{} chAccEncryptionPassphrase = keypair.MustRandom().Seed() ) @@ -1716,7 +1716,7 @@ func Test_TransactionWorker_buildAndSignTransaction(t *testing.T) { // mock horizon mockHorizon := &horizonclient.MockClient{} - if !sdpUtlis.IsEmpty(tc.getAccountResponseObj) || !sdpUtlis.IsEmpty(tc.getAccountResponseError) { + if !sdpUtils.IsEmpty(tc.getAccountResponseObj) || !sdpUtils.IsEmpty(tc.getAccountResponseError) { var hErr error if tc.getAccountResponseError != nil { hErr = tc.getAccountResponseError diff --git a/internal/transactionsubmission/utils/utils.go b/internal/transactionsubmission/utils/utils.go index 8c0baa8da..d4177139b 100644 --- a/internal/transactionsubmission/utils/utils.go +++ b/internal/transactionsubmission/utils/utils.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/stellar/stellar-disbursement-platform-backend/db" - sdpUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" ) // AcquireAdvisoryLock attempt to acquire an advisory lock on the provided lockKey, returns true if acquired, or false @@ -19,21 +18,3 @@ func AcquireAdvisoryLock(ctx context.Context, dbConnectionPool db.DBConnectionPo } return tssAdvisoryLockAcquired, nil } - -type PrivateKeyEncrypter interface { - Encrypt(message string, passphrase string) (string, error) - Decrypt(message string, passphrase string) (string, error) -} - -type DefaultPrivateKeyEncrypter struct{} - -func (e *DefaultPrivateKeyEncrypter) Encrypt(message, passphrase string) (string, error) { - return sdpUtils.Encrypt(message, passphrase) -} - -func (e *DefaultPrivateKeyEncrypter) Decrypt(message, passphrase string) (string, error) { - return sdpUtils.Decrypt(message, passphrase) -} - -// Making sure that DefaultPrivateKeyEncrypter implements PrivateKeyEncrypter -var _ PrivateKeyEncrypter = (*DefaultPrivateKeyEncrypter)(nil) diff --git a/internal/utils/encrypter.go b/internal/utils/encrypter.go new file mode 100644 index 000000000..14e8c7e01 --- /dev/null +++ b/internal/utils/encrypter.go @@ -0,0 +1,19 @@ +package utils + +type PrivateKeyEncrypter interface { + Encrypt(message string, passphrase string) (string, error) + Decrypt(message string, passphrase string) (string, error) +} + +type DefaultPrivateKeyEncrypter struct{} + +func (e *DefaultPrivateKeyEncrypter) Encrypt(message, passphrase string) (string, error) { + return Encrypt(message, passphrase) +} + +func (e *DefaultPrivateKeyEncrypter) Decrypt(message, passphrase string) (string, error) { + return Decrypt(message, passphrase) +} + +// Making sure that DefaultPrivateKeyEncrypter implements PrivateKeyEncrypter +var _ PrivateKeyEncrypter = (*DefaultPrivateKeyEncrypter)(nil) diff --git a/internal/transactionsubmission/utils/mocks.go b/internal/utils/mocks.go similarity index 100% rename from internal/transactionsubmission/utils/mocks.go rename to internal/utils/mocks.go diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 9fb3c0afe..fac914e7e 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -83,3 +83,8 @@ func GetTypeName(v interface{}) string { return fullTypeName } + +// StringPtr returns a pointer to a string +func StringPtr(s string) *string { + return &s +} diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go index 9e597ad46..61067f23a 100644 --- a/internal/utils/utils_test.go +++ b/internal/utils/utils_test.go @@ -242,3 +242,33 @@ func Test_GetTypeName(t *testing.T) { }) } } + +func TestStringPtr(t *testing.T) { + t.Run("returns a pointer to the string", func(t *testing.T) { + s := "test string" + result := StringPtr(s) + + assert.NotNil(t, result) + assert.Equal(t, s, *result) + }) + + t.Run("returns a pointer to an empty string", func(t *testing.T) { + s := "" + result := StringPtr(s) + + assert.NotNil(t, result) + assert.Equal(t, s, *result) + }) + + t.Run("changing the original string does not affect the pointer", func(t *testing.T) { + s := "initial string" + result := StringPtr(s) + + // Modify the original string + s = "modified string" + + assert.NotNil(t, result) + assert.NotEqual(t, s, *result) + assert.Equal(t, "initial string", *result) + }) +} diff --git a/stellar-multitenant/internal/httphandler/tenants_handler_test.go b/stellar-multitenant/internal/httphandler/tenants_handler_test.go index 2d7039997..9e37de9b4 100644 --- a/stellar-multitenant/internal/httphandler/tenants_handler_test.go +++ b/stellar-multitenant/internal/httphandler/tenants_handler_test.go @@ -346,15 +346,16 @@ func Test_TenantHandler_Post(t *testing.T) { "auth_user_mfa_codes", "auth_user_password_reset", "auth_users", + "circle_client_config", "countries", "disbursements", - "sdp_migrations", "messages", "organizations", "payments", "receiver_verifications", "receiver_wallets", "receivers", + "sdp_migrations", "wallets", "wallets_assets", } diff --git a/stellar-multitenant/internal/provisioning/manager_test.go b/stellar-multitenant/internal/provisioning/manager_test.go index a66a04091..53acec32a 100644 --- a/stellar-multitenant/internal/provisioning/manager_test.go +++ b/stellar-multitenant/internal/provisioning/manager_test.go @@ -430,15 +430,16 @@ func getExpectedTablesAfterMigrationsApplied() []string { "auth_user_mfa_codes", "auth_user_password_reset", "auth_users", + "circle_client_config", "countries", "disbursements", - "sdp_migrations", "messages", "organizations", "payments", "receiver_verifications", "receiver_wallets", "receivers", + "sdp_migrations", "wallets", "wallets_assets", }