From 7ae1afa98e116fa0ebb9e83ca8fd0c64658cf309 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Fri, 29 Oct 2021 13:56:19 -0400 Subject: [PATCH 1/9] Add database support for SELECT DISTINCT Signed-off-by: Andrew Richardson --- internal/database/sqlcommon/filter_sql.go | 3 +++ internal/database/sqlcommon/filter_sql_test.go | 14 ++++++++++++++ pkg/database/filter.go | 14 ++++++++++++++ pkg/database/filter_test.go | 8 ++++++++ 4 files changed, 39 insertions(+) diff --git a/internal/database/sqlcommon/filter_sql.go b/internal/database/sqlcommon/filter_sql.go index aed34d2e45..cf40804448 100644 --- a/internal/database/sqlcommon/filter_sql.go +++ b/internal/database/sqlcommon/filter_sql.go @@ -70,6 +70,9 @@ func (s *SQLCommon) filterSelect(ctx context.Context, tableName string, sel sq.S sel = sel.Limit(fi.Limit) } } + if fi.Distinct { + sel = sel.Distinct() + } return sel, fop, fi, err } diff --git a/internal/database/sqlcommon/filter_sql_test.go b/internal/database/sqlcommon/filter_sql_test.go index f7f20730d5..9b64254593 100644 --- a/internal/database/sqlcommon/filter_sql_test.go +++ b/internal/database/sqlcommon/filter_sql_test.go @@ -169,3 +169,17 @@ func TestSQLQueryFactoryDefaultSortBadType(t *testing.T) { s.filterSelect(context.Background(), "", sel, f, nil, []interface{}{100}) }) } + +func TestSQLQueryFactorySelectDistinct(t *testing.T) { + + s, _ := newMockProvider().init() + sel := squirrel.Select("name").From("mytable") + fb := database.MessageQueryFactory.NewFilter(context.Background()) + f := fb.And().Distinct(true) + sel, _, _, err := s.filterSelect(context.Background(), "", sel, f, nil, []interface{}{"name"}) + assert.NoError(t, err) + + sqlFilter, _, err := sel.ToSql() + assert.NoError(t, err) + assert.Equal(t, "SELECT DISTINCT name FROM mytable WHERE (1=1) ORDER BY name DESC", sqlFilter) +} diff --git a/pkg/database/filter.go b/pkg/database/filter.go index f9fb5433af..7c5e6c4f1e 100644 --- a/pkg/database/filter.go +++ b/pkg/database/filter.go @@ -46,6 +46,9 @@ type Filter interface { // Request a count to be returned on the total number that match the query Count(c bool) Filter + // Select distinct rows + Distinct(bool) Filter + // Finalize completes the filter, and for the plugin to validated output structure to convert Finalize() (*FilterInfo, error) @@ -156,6 +159,7 @@ type FilterInfo struct { Skip uint64 Limit uint64 Count bool + Distinct bool Field string Op FilterOp Values []FieldSerialization @@ -231,6 +235,9 @@ func (f *FilterInfo) String() string { if f.Count { val.WriteString(" count=true") } + if f.Distinct { + val.WriteString(" distinct=true") + } return val.String() } @@ -254,6 +261,7 @@ type filterBuilder struct { count bool forceAscending bool forceDescending bool + distinct bool } type baseFilter struct { @@ -327,6 +335,7 @@ func (f *baseFilter) Finalize() (fi *FilterInfo, err error) { Skip: f.fb.skip, Limit: f.fb.limit, Count: f.fb.count, + Distinct: f.fb.distinct, }, nil } @@ -372,6 +381,11 @@ func (f *baseFilter) Descending() Filter { return f } +func (f *baseFilter) Distinct(d bool) Filter { + f.fb.distinct = true + return f +} + type andFilter struct { baseFilter } diff --git a/pkg/database/filter_test.go b/pkg/database/filter_test.go index 2243d0038a..aa93b1c1bf 100644 --- a/pkg/database/filter_test.go +++ b/pkg/database/filter_test.go @@ -79,6 +79,14 @@ func TestBuildMessageFilter3(t *testing.T) { assert.Equal(t, "( created IN [1000000000,2000000000,3000000000] ) && ( created NI [1000000000,2000000000,3000000000] ) && ( created < 0 ) && ( created <= 0 ) && ( created >= 0 ) && ( created != 0 ) && ( sequence > 12345 ) && ( topics %= 'abc' ) && ( topics %! 'def' ) && ( topics ^= 'ghi' ) && ( topics ^! 'jkl' ) sort=-created,topics,-sequence", f.String()) } +func TestBuildMessageFilterDistinct(t *testing.T) { + fb := MessageQueryFactory.NewFilter(context.Background()) + f, err := fb.And().Distinct(true).Finalize() + + assert.NoError(t, err) + assert.Equal(t, " distinct=true", f.String()) +} + func TestBuildMessageBadInFilterField(t *testing.T) { fb := MessageQueryFactory.NewFilter(context.Background()) _, err := fb.And( From 852e74f7ad54fff06fb99cffbf2f71c05cf78e04 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 1 Nov 2021 11:51:53 -0400 Subject: [PATCH 2/9] Make account balance transfer a single database operation Rather than separate "from" and "to" operations, this should be a single operation for the database plugin (since it must happen transactionally). Signed-off-by: Andrew Richardson --- .../database/sqlcommon/tokenaccount_sql.go | 69 +++++----- .../sqlcommon/tokenaccount_sql_test.go | 122 ++++++------------ internal/events/tokens_transferred.go | 28 +--- internal/events/tokens_transferred_test.go | 74 ++--------- mocks/databasemocks/plugin.go | 28 ++-- pkg/database/plugin.go | 4 +- pkg/fftypes/bigint.go | 4 + pkg/fftypes/bigint_test.go | 7 + pkg/fftypes/tokenaccount.go | 9 -- test/e2e/tokens_test.go | 23 ++-- 10 files changed, 134 insertions(+), 234 deletions(-) diff --git a/internal/database/sqlcommon/tokenaccount_sql.go b/internal/database/sqlcommon/tokenaccount_sql.go index e7fffdebaf..985b1d2dc1 100644 --- a/internal/database/sqlcommon/tokenaccount_sql.go +++ b/internal/database/sqlcommon/tokenaccount_sql.go @@ -43,37 +43,25 @@ var ( } ) -func (s *SQLCommon) AddTokenAccountBalance(ctx context.Context, account *fftypes.TokenBalanceChange) (err error) { - ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) +func (s *SQLCommon) addTokenAccountBalance(ctx context.Context, tx *txWrapper, transfer *fftypes.TokenTransfer, key string, negate bool) error { + account, err := s.GetTokenAccount(ctx, transfer.PoolProtocolID, transfer.TokenIndex, key) if err != nil { return err } - defer s.rollbackTx(ctx, tx, autoCommit) - rows, _, err := s.queryTx(ctx, tx, - sq.Select("balance"). - From("tokenaccount"). - Where(sq.And{ - sq.Eq{"pool_protocol_id": account.PoolProtocolID}, - sq.Eq{"token_index": account.TokenIndex}, - sq.Eq{"key": account.Key}, - }), - ) - if err != nil { - return err + var balance *fftypes.BigInt + if account != nil { + balance = &account.Balance + } else { + balance = &fftypes.BigInt{} } - existing := rows.Next() - - var balance fftypes.BigInt - if existing { - if err = rows.Scan(&balance); err != nil { - return i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "tokenaccount") - } + if negate { + balance.Int().Sub(balance.Int(), transfer.Amount.Int()) + } else { + balance.Int().Add(balance.Int(), transfer.Amount.Int()) } - balance.Int().Add(balance.Int(), account.Amount.Int()) - rows.Close() - if existing { + if account != nil { if err = s.updateTx(ctx, tx, sq.Update("tokenaccount"). Set("balance", balance). @@ -92,12 +80,12 @@ func (s *SQLCommon) AddTokenAccountBalance(ctx context.Context, account *fftypes sq.Insert("tokenaccount"). Columns(tokenAccountColumns...). Values( - account.PoolProtocolID, - account.TokenIndex, - account.Connector, - account.Namespace, - account.Key, - account.Amount, + transfer.PoolProtocolID, + transfer.TokenIndex, + transfer.Connector, + transfer.Namespace, + key, + balance, fftypes.Now(), ), nil, @@ -106,6 +94,27 @@ func (s *SQLCommon) AddTokenAccountBalance(ctx context.Context, account *fftypes } } + return nil +} + +func (s *SQLCommon) UpdateTokenAccountBalances(ctx context.Context, transfer *fftypes.TokenTransfer) (err error) { + ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) + if err != nil { + return err + } + defer s.rollbackTx(ctx, tx, autoCommit) + + if transfer.From != "" { + if err := s.addTokenAccountBalance(ctx, tx, transfer, transfer.From, true); err != nil { + return err + } + } + if transfer.To != "" { + if err := s.addTokenAccountBalance(ctx, tx, transfer, transfer.To, false); err != nil { + return err + } + } + return s.commitTx(ctx, tx, autoCommit) } diff --git a/internal/database/sqlcommon/tokenaccount_sql_test.go b/internal/database/sqlcommon/tokenaccount_sql_test.go index e348237408..b6d5c01cf7 100644 --- a/internal/database/sqlcommon/tokenaccount_sql_test.go +++ b/internal/database/sqlcommon/tokenaccount_sql_test.go @@ -23,7 +23,6 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/hyperledger/firefly/mocks/databasemocks" "github.com/hyperledger/firefly/pkg/database" "github.com/hyperledger/firefly/pkg/fftypes" "github.com/stretchr/testify/assert" @@ -36,25 +35,25 @@ func TestTokenAccountE2EWithDB(t *testing.T) { ctx := context.Background() // Create a new token account - operation := &fftypes.TokenBalanceChange{ + transfer := &fftypes.TokenTransfer{ PoolProtocolID: "F1", TokenIndex: "1", Connector: "erc1155", Namespace: "ns1", - Key: "0x0", + To: "0x0", + Amount: *fftypes.NewBigInt(10), } - operation.Amount.Int().SetInt64(10) account := &fftypes.TokenAccount{ PoolProtocolID: "F1", TokenIndex: "1", Connector: "erc1155", Namespace: "ns1", Key: "0x0", + Balance: *fftypes.NewBigInt(10), } - account.Balance.Int().SetInt64(10) accountJson, _ := json.Marshal(&account) - err := s.AddTokenAccountBalance(ctx, operation) + err := s.UpdateTokenAccountBalances(ctx, transfer) assert.NoError(t, err) // Query back the token account (by pool ID and identity) @@ -82,8 +81,11 @@ func TestTokenAccountE2EWithDB(t *testing.T) { accountReadJson, _ = json.Marshal(accounts[0]) assert.Equal(t, string(accountJson), string(accountReadJson)) - // Add to the balance - err = s.AddTokenAccountBalance(ctx, operation) + // Transfer half to a different account + transfer.From = "0x0" + transfer.To = "0x1" + transfer.Amount = *fftypes.NewBigInt(5) + err = s.UpdateTokenAccountBalances(ctx, transfer) assert.NoError(t, err) // Query back the token account (by pool ID and identity) @@ -93,126 +95,84 @@ func TestTokenAccountE2EWithDB(t *testing.T) { assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) accountRead.Updated = nil accountReadJson, _ = json.Marshal(&accountRead) - account.Balance.Int().SetInt64(20) + account.Balance = *fftypes.NewBigInt(5) + accountJson, _ = json.Marshal(&account) + assert.Equal(t, string(accountJson), string(accountReadJson)) + + // Query back the other token account (by pool ID and identity) + accountRead, err = s.GetTokenAccount(ctx, "F1", "1", "0x1") + assert.NoError(t, err) + assert.NotNil(t, accountRead) + assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) + accountRead.Updated = nil + accountReadJson, _ = json.Marshal(&accountRead) + account.Key = "0x1" + account.Balance = *fftypes.NewBigInt(5) accountJson, _ = json.Marshal(&account) assert.Equal(t, string(accountJson), string(accountReadJson)) } -func TestAddTokenAccountBalanceFailBegin(t *testing.T) { +func TestUpdateTokenAccountBalancesFailBegin(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) - err := s.AddTokenAccountBalance(context.Background(), &fftypes.TokenBalanceChange{}) + err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{}) assert.Regexp(t, "FF10114", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestAddTokenAccountBalanceFailSelect(t *testing.T) { +func TestUpdateTokenAccountBalancesFailSelect(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) - err := s.AddTokenAccountBalance(context.Background(), &fftypes.TokenBalanceChange{}) + err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10115", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestAddTokenAccountSelectBadExistingValue(t *testing.T) { +func TestUpdateTokenAccountBalancesFailInsert(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() - mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{ - "balance", - }).AddRow( - "!not an integer", - )) - err := s.AddTokenAccountBalance(context.Background(), &fftypes.TokenBalanceChange{}) - assert.Regexp(t, "FF10121", err) + mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) + mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) + mock.ExpectRollback() + err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{From: "0x0"}) + assert.Regexp(t, "FF10116", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestAddTokenAccountBalanceFailInsert(t *testing.T) { +func TestUpdateTokenAccountBalancesFailInsert2(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) mock.ExpectRollback() - err := s.AddTokenAccountBalance(context.Background(), &fftypes.TokenBalanceChange{}) + err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10116", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestAddTokenAccountBalanceFailUpdate(t *testing.T) { +func TestUpdateTokenAccountBalancesFailUpdate(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() - mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("1")) + mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows(tokenAccountColumns).AddRow("F1", "1", "", "", "0x0", "0", 0)) mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) mock.ExpectRollback() - err := s.AddTokenAccountBalance(context.Background(), &fftypes.TokenBalanceChange{}) + err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10117", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestAddTokenAccountBalanceFailCommit(t *testing.T) { +func TestUpdateTokenAccountBalancesFailCommit(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() - mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) + mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit().WillReturnError(fmt.Errorf("pop")) - err := s.AddTokenAccountBalance(context.Background(), &fftypes.TokenBalanceChange{}) + err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10119", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestAddTokenAccountBalanceInsertSuccess(t *testing.T) { - s, db := newMockProvider().init() - callbacks := &databasemocks.Callbacks{} - s.SQLCommon.callbacks = callbacks - operation := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "1", - Connector: "erc1155", - Namespace: "ns1", - Key: "0x0", - } - operation.Amount.Int().SetInt64(10) - - db.ExpectBegin() - db.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) - db.ExpectExec("INSERT .*"). - WithArgs("F1", "1", "erc1155", "ns1", "0x0", sqlmock.AnyArg(), sqlmock.AnyArg()). - WillReturnResult(sqlmock.NewResult(1, 1)) - db.ExpectCommit() - err := s.AddTokenAccountBalance(context.Background(), operation) - assert.NoError(t, err) - assert.NoError(t, db.ExpectationsWereMet()) -} - -func TestAddTokenAccountBalanceUpdateSuccess(t *testing.T) { - s, db := newMockProvider().init() - callbacks := &databasemocks.Callbacks{} - s.SQLCommon.callbacks = callbacks - operation := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "1", - Key: "0x0", - } - operation.Amount.Int().SetInt64(10) - - db.ExpectBegin() - db.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"seq"}).AddRow("1")) - db.ExpectExec("UPDATE .*").WillReturnResult(sqlmock.NewResult(1, 1)) - db.ExpectCommit() - err := s.AddTokenAccountBalance(context.Background(), operation) - assert.NoError(t, err) - assert.NoError(t, db.ExpectationsWereMet()) -} - -func TestGetTokenAccountSelectFail(t *testing.T) { - s, mock := newMockProvider().init() - mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) - _, err := s.GetTokenAccount(context.Background(), "F1", "1", "0x0") - assert.Regexp(t, "FF10115", err) - assert.NoError(t, mock.ExpectationsWereMet()) -} - func TestGetTokenAccountNotFound(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) diff --git a/internal/events/tokens_transferred.go b/internal/events/tokens_transferred.go index 963d95b116..5e962c0e68 100644 --- a/internal/events/tokens_transferred.go +++ b/internal/events/tokens_transferred.go @@ -112,32 +112,10 @@ func (em *eventManager) TokensTransferred(tk tokens.Plugin, transfer *fftypes.To log.L(ctx).Errorf("Failed to record token transfer '%s': %s", transfer.ProtocolID, err) return err } - - balance := &fftypes.TokenBalanceChange{ - PoolProtocolID: transfer.PoolProtocolID, - TokenIndex: transfer.TokenIndex, - Connector: transfer.Connector, - Namespace: transfer.Namespace, - } - - if transfer.Type != fftypes.TokenTransferTypeMint { - balance.Key = transfer.From - balance.Amount.Int().Neg(transfer.Amount.Int()) - if err := em.database.AddTokenAccountBalance(ctx, balance); err != nil { - log.L(ctx).Errorf("Failed to update account '%s' for token transfer '%s': %s", balance.Key, transfer.ProtocolID, err) - return err - } - } - - if transfer.Type != fftypes.TokenTransferTypeBurn { - balance.Key = transfer.To - balance.Amount.Int().Set(transfer.Amount.Int()) - if err := em.database.AddTokenAccountBalance(ctx, balance); err != nil { - log.L(ctx).Errorf("Failed to update account '%s for token transfer '%s': %s", balance.Key, transfer.ProtocolID, err) - return err - } + if err := em.database.UpdateTokenAccountBalances(ctx, transfer); err != nil { + log.L(ctx).Errorf("Failed to update accounts %s -> %s for token transfer '%s': %s", transfer.From, transfer.To, transfer.ProtocolID, err) + return err } - log.L(ctx).Infof("Token transfer recorded id=%s author=%s", transfer.ProtocolID, transfer.Key) if transfer.MessageHash != nil { diff --git a/internal/events/tokens_transferred_test.go b/internal/events/tokens_transferred_test.go index 3ae2993305..69d87b5525 100644 --- a/internal/events/tokens_transferred_test.go +++ b/internal/events/tokens_transferred_test.go @@ -43,36 +43,18 @@ func TestTokensTransferredSucceedWithRetries(t *testing.T) { Key: "0x12345", From: "0x1", To: "0x2", + Amount: *fftypes.NewBigInt(1), } - transfer.Amount.Int().SetInt64(1) - fromBalance := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "0", - Connector: "erc1155", - Namespace: "ns1", - Key: "0x1", - } - fromBalance.Amount.Int().SetInt64(-1) - toBalance := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "0", - Connector: "erc1155", - Namespace: "ns1", - Key: "0x2", - } - toBalance.Amount.Int().SetInt64(1) pool := &fftypes.TokenPool{ Namespace: "ns1", } mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(nil, fmt.Errorf("pop")).Once() - mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(pool, nil).Times(4) + mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(pool, nil).Times(3) mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(fmt.Errorf("pop")).Once() - mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(nil).Times(3) - mdi.On("AddTokenAccountBalance", em.ctx, fromBalance).Return(fmt.Errorf("pop")).Once() - mdi.On("AddTokenAccountBalance", em.ctx, fromBalance).Return(nil).Times(2) - mdi.On("AddTokenAccountBalance", em.ctx, toBalance).Return(fmt.Errorf("pop")).Once() - mdi.On("AddTokenAccountBalance", em.ctx, toBalance).Return(nil).Once() + mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(nil).Times(2) + mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(fmt.Errorf("pop")).Once() + mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(nil).Once() mdi.On("InsertEvent", em.ctx, mock.MatchedBy(func(ev *fftypes.Event) bool { return ev.Type == fftypes.EventTypeTransferConfirmed && ev.Reference == transfer.LocalID && ev.Namespace == pool.Namespace })).Return(nil).Once() @@ -99,12 +81,12 @@ func TestTokensTransferredWithTransactionRetries(t *testing.T) { Key: "0x12345", From: "0x1", To: "0x2", + Amount: *fftypes.NewBigInt(1), TX: fftypes.TransactionRef{ ID: fftypes.NewUUID(), Type: fftypes.TransactionTypeTokenTransfer, }, } - transfer.Amount.Int().SetInt64(1) pool := &fftypes.TokenPool{ Namespace: "ns1", } @@ -151,8 +133,8 @@ func TestTokensTransferredAddBalanceIgnore(t *testing.T) { Key: "0x12345", From: "0x1", To: "0x2", + Amount: *fftypes.NewBigInt(1), } - transfer.Amount.Int().SetInt64(1) mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(nil, nil) @@ -180,24 +162,8 @@ func TestTokensTransferredWithMessageReceived(t *testing.T) { From: "0x1", To: "0x2", MessageHash: fftypes.NewRandB32(), + Amount: *fftypes.NewBigInt(1), } - transfer.Amount.Int().SetInt64(1) - fromBalance := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "0", - Connector: "erc1155", - Namespace: "ns1", - Key: "0x1", - } - fromBalance.Amount.Int().SetInt64(-1) - toBalance := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "0", - Connector: "erc1155", - Namespace: "ns1", - Key: "0x2", - } - toBalance.Amount.Int().SetInt64(1) pool := &fftypes.TokenPool{ Namespace: "ns1", } @@ -207,8 +173,7 @@ func TestTokensTransferredWithMessageReceived(t *testing.T) { mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(pool, nil).Times(2) mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(nil).Times(2) - mdi.On("AddTokenAccountBalance", em.ctx, fromBalance).Return(nil).Times(2) - mdi.On("AddTokenAccountBalance", em.ctx, toBalance).Return(nil).Times(2) + mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(nil).Times(2) mdi.On("GetMessages", em.ctx, mock.Anything).Return(nil, nil, fmt.Errorf("pop")).Once() mdi.On("GetMessages", em.ctx, mock.Anything).Return(messages, nil, nil).Once() mdi.On("InsertEvent", em.ctx, mock.MatchedBy(func(ev *fftypes.Event) bool { @@ -239,24 +204,8 @@ func TestTokensTransferredWithMessageSend(t *testing.T) { From: "0x1", To: "0x2", MessageHash: fftypes.NewRandB32(), + Amount: *fftypes.NewBigInt(1), } - transfer.Amount.Int().SetInt64(1) - fromBalance := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "0", - Connector: "erc1155", - Namespace: "ns1", - Key: "0x1", - } - fromBalance.Amount.Int().SetInt64(-1) - toBalance := &fftypes.TokenBalanceChange{ - PoolProtocolID: "F1", - TokenIndex: "0", - Connector: "erc1155", - Namespace: "ns1", - Key: "0x2", - } - toBalance.Amount.Int().SetInt64(1) pool := &fftypes.TokenPool{ Namespace: "ns1", } @@ -267,8 +216,7 @@ func TestTokensTransferredWithMessageSend(t *testing.T) { mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(pool, nil).Times(2) mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(nil).Times(2) - mdi.On("AddTokenAccountBalance", em.ctx, fromBalance).Return(nil).Times(2) - mdi.On("AddTokenAccountBalance", em.ctx, toBalance).Return(nil).Times(2) + mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(nil).Times(2) mdi.On("GetMessages", em.ctx, mock.Anything).Return(messages, nil, nil).Times(2) mdi.On("UpsertMessage", em.ctx, mock.Anything, true, false).Return(fmt.Errorf("pop")) mdi.On("UpsertMessage", em.ctx, mock.MatchedBy(func(msg *fftypes.Message) bool { diff --git a/mocks/databasemocks/plugin.go b/mocks/databasemocks/plugin.go index fd04026b07..1e8bef10bf 100644 --- a/mocks/databasemocks/plugin.go +++ b/mocks/databasemocks/plugin.go @@ -19,20 +19,6 @@ type Plugin struct { mock.Mock } -// AddTokenAccountBalance provides a mock function with given fields: ctx, account -func (_m *Plugin) AddTokenAccountBalance(ctx context.Context, account *fftypes.TokenBalanceChange) error { - ret := _m.Called(ctx, account) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *fftypes.TokenBalanceChange) error); ok { - r0 = rf(ctx, account) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // Capabilities provides a mock function with given fields: func (_m *Plugin) Capabilities() *database.Capabilities { ret := _m.Called() @@ -1858,6 +1844,20 @@ func (_m *Plugin) UpdateSubscription(ctx context.Context, ns string, name string return r0 } +// UpdateTokenAccountBalances provides a mock function with given fields: ctx, transfer +func (_m *Plugin) UpdateTokenAccountBalances(ctx context.Context, transfer *fftypes.TokenTransfer) error { + ret := _m.Called(ctx, transfer) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *fftypes.TokenTransfer) error); ok { + r0 = rf(ctx, transfer) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateTransaction provides a mock function with given fields: ctx, id, update func (_m *Plugin) UpdateTransaction(ctx context.Context, id *fftypes.UUID, update database.Update) error { ret := _m.Called(ctx, id, update) diff --git a/pkg/database/plugin.go b/pkg/database/plugin.go index f5c979592a..396b68e38f 100644 --- a/pkg/database/plugin.go +++ b/pkg/database/plugin.go @@ -364,8 +364,8 @@ type iTokenPoolCollection interface { } type iTokenAccountCollection interface { - // AddTokenAccountBalance - Add a (positive or negative) balance to the account's current balance - AddTokenAccountBalance(ctx context.Context, account *fftypes.TokenBalanceChange) error + // UpdateTokenAccountBalances - Move some token balance from one account to another + UpdateTokenAccountBalances(ctx context.Context, transfer *fftypes.TokenTransfer) error // GetTokenAccount - Get a token account by pool and account identity GetTokenAccount(ctx context.Context, protocolID, tokenIndex, identity string) (*fftypes.TokenAccount, error) diff --git a/pkg/fftypes/bigint.go b/pkg/fftypes/bigint.go index 1c0c498466..ee0da2abe3 100644 --- a/pkg/fftypes/bigint.go +++ b/pkg/fftypes/bigint.go @@ -57,6 +57,10 @@ func (i *BigInt) UnmarshalJSON(b []byte) error { } } +func NewBigInt(x int64) *BigInt { + return (*BigInt)(big.NewInt(x)) +} + func (i BigInt) Value() (driver.Value, error) { // Represent as base 16 string in database, to allow a 64 character limit res := (*big.Int)(&i).Text(16) diff --git a/pkg/fftypes/bigint_test.go b/pkg/fftypes/bigint_test.go index 7c7eed0eff..900a9eb5a7 100644 --- a/pkg/fftypes/bigint_test.go +++ b/pkg/fftypes/bigint_test.go @@ -209,3 +209,10 @@ func TestEquals(t *testing.T) { assert.True(t, i2.Equals(&i1)) } + +func TestNewBigInt(t *testing.T) { + + n := NewBigInt(10) + assert.Equal(t, int64(10), n.Int().Int64()) + +} diff --git a/pkg/fftypes/tokenaccount.go b/pkg/fftypes/tokenaccount.go index 1857fc5805..ddf07559db 100644 --- a/pkg/fftypes/tokenaccount.go +++ b/pkg/fftypes/tokenaccount.go @@ -33,12 +33,3 @@ func TokenAccountIdentifier(protocolID, tokenIndex, identity string) string { func (t *TokenAccount) Identifier() string { return TokenAccountIdentifier(t.PoolProtocolID, t.TokenIndex, t.Key) } - -type TokenBalanceChange struct { - PoolProtocolID string - TokenIndex string - Connector string - Namespace string - Key string - Amount BigInt -} diff --git a/test/e2e/tokens_test.go b/test/e2e/tokens_test.go index c345c72c3d..fcb46bdb94 100644 --- a/test/e2e/tokens_test.go +++ b/test/e2e/tokens_test.go @@ -68,8 +68,9 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { assert.Equal(suite.T(), fftypes.TokenTypeFungible, pools[0].Type) assert.NotEmpty(suite.T(), pools[0].ProtocolID) - transfer := &fftypes.TokenTransferInput{} - transfer.Amount.Int().SetInt64(1) + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{Amount: *fftypes.NewBigInt(1)}, + } MintTokens(suite.T(), suite.testState.client1, poolName, transfer, false) <-received1 @@ -94,7 +95,8 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { transfer = &fftypes.TokenTransferInput{ TokenTransfer: fftypes.TokenTransfer{ - To: suite.testState.org2.Identity, + To: suite.testState.org2.Identity, + Amount: *fftypes.NewBigInt(1), }, Message: &fftypes.MessageInOut{ InlineData: fftypes.InlineData{ @@ -104,7 +106,6 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { }, }, } - transfer.Amount.Int().SetInt64(1) TransferTokens(suite.T(), suite.testState.client1, poolName, transfer, false) <-received1 // one event for transfer @@ -134,8 +135,9 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { suite.testState.org2.Identity: 1, }) - transfer = &fftypes.TokenTransferInput{} - transfer.Amount.Int().SetInt64(1) + transfer = &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{Amount: *fftypes.NewBigInt(1)}, + } BurnTokens(suite.T(), suite.testState.client2, poolName, transfer, false) <-received2 @@ -192,8 +194,9 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { assert.Equal(suite.T(), fftypes.TokenTypeNonFungible, pools[0].Type) assert.NotEmpty(suite.T(), pools[0].ProtocolID) - transfer := &fftypes.TokenTransferInput{} - transfer.Amount.Int().SetInt64(1) + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{Amount: *fftypes.NewBigInt(1)}, + } transferOut := MintTokens(suite.T(), suite.testState.client1, poolName, transfer, true) assert.Equal(suite.T(), fftypes.TokenTransferTypeMint, transferOut.Type) assert.Equal(suite.T(), "1", transferOut.TokenIndex) @@ -217,6 +220,7 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { TokenTransfer: fftypes.TokenTransfer{ TokenIndex: "1", To: suite.testState.org2.Identity, + Amount: *fftypes.NewBigInt(1), }, Message: &fftypes.MessageInOut{ InlineData: fftypes.InlineData{ @@ -226,7 +230,6 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { }, }, } - transfer.Amount.Int().SetInt64(1) transferOut = TransferTokens(suite.T(), suite.testState.client1, poolName, transfer, true) assert.Equal(suite.T(), fftypes.TokenTransferTypeTransfer, transferOut.Type) assert.Equal(suite.T(), "1", transferOut.TokenIndex) @@ -256,9 +259,9 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { transfer = &fftypes.TokenTransferInput{ TokenTransfer: fftypes.TokenTransfer{ TokenIndex: "1", + Amount: *fftypes.NewBigInt(1), }, } - transfer.Amount.Int().SetInt64(1) transferOut = BurnTokens(suite.T(), suite.testState.client2, poolName, transfer, true) assert.Equal(suite.T(), fftypes.TokenTransferTypeBurn, transferOut.Type) assert.Equal(suite.T(), "1", transferOut.TokenIndex) From 2ccdd7be2401d789e84a895cf6670bd661395283 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 1 Nov 2021 13:22:15 -0400 Subject: [PATCH 3/9] Rename TokenAccount to TokenBalance Signed-off-by: Andrew Richardson --- ...name_tokenaccount_to_tokenbalance.down.sql | 3 + ...rename_tokenaccount_to_tokenbalance.up.sql | 3 + ...name_tokenaccount_to_tokenbalance.down.sql | 1 + ...rename_tokenaccount_to_tokenbalance.up.sql | 1 + docs/swagger/swagger.yaml | 4 +- .../route_get_token_accounts_by_pool.go | 6 +- .../route_get_token_accounts_by_pool_test.go | 4 +- ...ccounts.go => route_get_token_balances.go} | 12 ++-- ...st.go => route_get_token_balances_test.go} | 8 +-- internal/apiserver/routes.go | 2 +- internal/assets/manager.go | 15 ++-- internal/assets/manager_test.go | 22 +++--- ...okenaccount_sql.go => tokenbalance_sql.go} | 50 ++++++------- ...t_sql_test.go => tokenbalance_sql_test.go} | 70 +++++++++---------- internal/events/tokens_transferred.go | 2 +- internal/events/tokens_transferred_test.go | 8 +-- mocks/assetmocks/manager.go | 20 +++--- mocks/databasemocks/plugin.go | 24 +++---- pkg/database/plugin.go | 22 +++--- .../{tokenaccount.go => tokenbalance.go} | 8 +-- ...enaccount_test.go => tokenbalance_test.go} | 6 +- test/e2e/restclient.go | 4 +- 22 files changed, 153 insertions(+), 142 deletions(-) create mode 100644 db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.down.sql create mode 100644 db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.up.sql create mode 100644 db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.down.sql create mode 100644 db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.up.sql rename internal/apiserver/{route_get_token_accounts.go => route_get_token_balances.go} (80%) rename internal/apiserver/{route_get_token_accounts_test.go => route_get_token_balances_test.go} (87%) rename internal/database/sqlcommon/{tokenaccount_sql.go => tokenbalance_sql.go} (65%) rename internal/database/sqlcommon/{tokenaccount_sql_test.go => tokenbalance_sql_test.go} (72%) rename pkg/fftypes/{tokenaccount.go => tokenbalance.go} (84%) rename pkg/fftypes/{tokenaccount_test.go => tokenbalance_test.go} (86%) diff --git a/db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.down.sql b/db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.down.sql new file mode 100644 index 0000000000..747833dcef --- /dev/null +++ b/db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.down.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE tokenbalance RENAME TO tokenaccount; +COMMIT: diff --git a/db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.up.sql b/db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.up.sql new file mode 100644 index 0000000000..a99c49f24e --- /dev/null +++ b/db/migrations/postgres/000042_rename_tokenaccount_to_tokenbalance.up.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE tokenaccount RENAME TO tokenbalance; +COMMIT; diff --git a/db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.down.sql b/db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.down.sql new file mode 100644 index 0000000000..35d03d9a95 --- /dev/null +++ b/db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.down.sql @@ -0,0 +1 @@ +ALTER TABLE tokenbalance RENAME TO tokenaccount; diff --git a/db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.up.sql b/db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.up.sql new file mode 100644 index 0000000000..c98f91c81b --- /dev/null +++ b/db/migrations/sqlite/000042_rename_tokenaccount_to_tokenbalance.up.sql @@ -0,0 +1 @@ +ALTER TABLE tokenaccount RENAME TO tokenbalance; diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index b0a323d243..50e5e8fe7a 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -5419,10 +5419,10 @@ paths: description: Success default: description: "" - /namespaces/{ns}/tokens/accounts: + /namespaces/{ns}/tokens/balances: get: description: 'TODO: Description' - operationId: getTokenAccounts + operationId: getTokenBalances parameters: - description: 'TODO: Description' in: path diff --git a/internal/apiserver/route_get_token_accounts_by_pool.go b/internal/apiserver/route_get_token_accounts_by_pool.go index 26eaa348e2..05d6bccd36 100644 --- a/internal/apiserver/route_get_token_accounts_by_pool.go +++ b/internal/apiserver/route_get_token_accounts_by_pool.go @@ -36,12 +36,12 @@ var getTokenAccountsByPool = &oapispec.Route{ {Name: "name", Description: i18n.MsgTBD}, }, QueryParams: nil, - FilterFactory: database.TokenAccountQueryFactory, + FilterFactory: database.TokenBalanceQueryFactory, Description: i18n.MsgTBD, JSONInputValue: nil, - JSONOutputValue: func() interface{} { return []*fftypes.TokenAccount{} }, + JSONOutputValue: func() interface{} { return []*fftypes.TokenBalance{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - return filterResult(r.Or.Assets().GetTokenAccountsByPool(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Filter)) + return filterResult(r.Or.Assets().GetTokenBalancesByPool(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Filter)) }, } diff --git a/internal/apiserver/route_get_token_accounts_by_pool_test.go b/internal/apiserver/route_get_token_accounts_by_pool_test.go index faa90d45c5..88e0081b96 100644 --- a/internal/apiserver/route_get_token_accounts_by_pool_test.go +++ b/internal/apiserver/route_get_token_accounts_by_pool_test.go @@ -34,8 +34,8 @@ func TestGetTokenAccountsByPool(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mam.On("GetTokenAccountsByPool", mock.Anything, "ns1", "tok1", "pool1", mock.Anything). - Return([]*fftypes.TokenAccount{}, nil, nil) + mam.On("GetTokenBalancesByPool", mock.Anything, "ns1", "tok1", "pool1", mock.Anything). + Return([]*fftypes.TokenBalance{}, nil, nil) r.ServeHTTP(res, req) assert.Equal(t, 200, res.Result().StatusCode) diff --git a/internal/apiserver/route_get_token_accounts.go b/internal/apiserver/route_get_token_balances.go similarity index 80% rename from internal/apiserver/route_get_token_accounts.go rename to internal/apiserver/route_get_token_balances.go index 3e43229ced..2eb7a1f218 100644 --- a/internal/apiserver/route_get_token_accounts.go +++ b/internal/apiserver/route_get_token_balances.go @@ -26,20 +26,20 @@ import ( "github.com/hyperledger/firefly/pkg/fftypes" ) -var getTokenAccounts = &oapispec.Route{ - Name: "getTokenAccounts", - Path: "namespaces/{ns}/tokens/accounts", +var getTokenBalances = &oapispec.Route{ + Name: "getTokenBalances", + Path: "namespaces/{ns}/tokens/balances", Method: http.MethodGet, PathParams: []*oapispec.PathParam{ {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, }, QueryParams: nil, - FilterFactory: database.TokenAccountQueryFactory, + FilterFactory: database.TokenBalanceQueryFactory, Description: i18n.MsgTBD, JSONInputValue: nil, - JSONOutputValue: func() interface{} { return []*fftypes.TokenAccount{} }, + JSONOutputValue: func() interface{} { return []*fftypes.TokenBalance{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - return filterResult(r.Or.Assets().GetTokenAccounts(r.Ctx, r.PP["ns"], r.Filter)) + return filterResult(r.Or.Assets().GetTokenBalances(r.Ctx, r.PP["ns"], r.Filter)) }, } diff --git a/internal/apiserver/route_get_token_accounts_test.go b/internal/apiserver/route_get_token_balances_test.go similarity index 87% rename from internal/apiserver/route_get_token_accounts_test.go rename to internal/apiserver/route_get_token_balances_test.go index 6c481f25a6..d25168acc4 100644 --- a/internal/apiserver/route_get_token_accounts_test.go +++ b/internal/apiserver/route_get_token_balances_test.go @@ -26,16 +26,16 @@ import ( "github.com/stretchr/testify/mock" ) -func TestGetTokenAccounts(t *testing.T) { +func TestGetTokenBalances(t *testing.T) { o, r := newTestAPIServer() mam := &assetmocks.Manager{} o.On("Assets").Return(mam) - req := httptest.NewRequest("GET", "/api/v1/namespaces/ns1/tokens/accounts", nil) + req := httptest.NewRequest("GET", "/api/v1/namespaces/ns1/tokens/balances", nil) req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mam.On("GetTokenAccounts", mock.Anything, "ns1", mock.Anything). - Return([]*fftypes.TokenAccount{}, nil, nil) + mam.On("GetTokenBalances", mock.Anything, "ns1", mock.Anything). + Return([]*fftypes.TokenBalance{}, nil, nil) r.ServeHTTP(res, req) assert.Equal(t, 200, res.Result().StatusCode) diff --git a/internal/apiserver/routes.go b/internal/apiserver/routes.go index 1fc28b3f2e..5e3a03b130 100644 --- a/internal/apiserver/routes.go +++ b/internal/apiserver/routes.go @@ -83,7 +83,7 @@ var routes = []*oapispec.Route{ getTokenPoolsByType, getTokenPoolByNameOrID, getTokenPoolByName, - getTokenAccounts, + getTokenBalances, getTokenAccountsByPool, getTokenTransfers, getTokenTransfersByPool, diff --git a/internal/assets/manager.go b/internal/assets/manager.go index 5cd1c85639..bc43218b1b 100644 --- a/internal/assets/manager.go +++ b/internal/assets/manager.go @@ -42,11 +42,14 @@ type Manager interface { GetTokenPool(ctx context.Context, ns, connector, poolName string) (*fftypes.TokenPool, error) GetTokenPoolByNameOrID(ctx context.Context, ns string, poolNameOrID string) (*fftypes.TokenPool, error) ValidateTokenPoolTx(ctx context.Context, pool *fftypes.TokenPool, protocolTxID string) error - GetTokenAccounts(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) - GetTokenAccountsByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) + + GetTokenBalances(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) + GetTokenBalancesByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) + GetTokenTransfers(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenTransfer, *database.FilterResult, error) GetTokenTransferByID(ctx context.Context, ns, id string) (*fftypes.TokenTransfer, error) GetTokenTransfersByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenTransfer, *database.FilterResult, error) + NewTransfer(ns, connector, poolName string, transfer *fftypes.TokenTransferInput) sysmessaging.MessageSender MintTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) MintTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) @@ -112,16 +115,16 @@ func (am *assetManager) scopeNS(ns string, filter database.AndFilter) database.A return filter.Condition(filter.Builder().Eq("namespace", ns)) } -func (am *assetManager) GetTokenAccounts(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { - return am.database.GetTokenAccounts(ctx, am.scopeNS(ns, filter)) +func (am *assetManager) GetTokenBalances(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) { + return am.database.GetTokenBalances(ctx, am.scopeNS(ns, filter)) } -func (am *assetManager) GetTokenAccountsByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { +func (am *assetManager) GetTokenBalancesByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) { pool, err := am.GetTokenPool(ctx, ns, connector, poolName) if err != nil { return nil, nil, err } - return am.database.GetTokenAccounts(ctx, filter.Condition(filter.Builder().Eq("poolprotocolid", pool.ProtocolID))) + return am.database.GetTokenBalances(ctx, filter.Condition(filter.Builder().Eq("poolprotocolid", pool.ProtocolID))) } func (am *assetManager) GetTokenConnectors(ctx context.Context, ns string) ([]*fftypes.TokenConnector, error) { diff --git a/internal/assets/manager_test.go b/internal/assets/manager_test.go index 0bab15dd7d..89557c47f8 100644 --- a/internal/assets/manager_test.go +++ b/internal/assets/manager_test.go @@ -67,19 +67,19 @@ func TestStartStop(t *testing.T) { am.WaitStop() } -func TestGetTokenAccounts(t *testing.T) { +func TestGetTokenBalances(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() mdi := am.database.(*databasemocks.Plugin) - fb := database.TokenAccountQueryFactory.NewFilter(context.Background()) + fb := database.TokenBalanceQueryFactory.NewFilter(context.Background()) f := fb.And() - mdi.On("GetTokenAccounts", context.Background(), f).Return([]*fftypes.TokenAccount{}, nil, nil) - _, _, err := am.GetTokenAccounts(context.Background(), "ns1", f) + mdi.On("GetTokenBalances", context.Background(), f).Return([]*fftypes.TokenBalance{}, nil, nil) + _, _, err := am.GetTokenBalances(context.Background(), "ns1", f) assert.NoError(t, err) } -func TestGetTokenAccountsByPool(t *testing.T) { +func TestGetTokenBalancesByPool(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -87,23 +87,23 @@ func TestGetTokenAccountsByPool(t *testing.T) { ID: fftypes.NewUUID(), } mdi := am.database.(*databasemocks.Plugin) - fb := database.TokenAccountQueryFactory.NewFilter(context.Background()) + fb := database.TokenBalanceQueryFactory.NewFilter(context.Background()) f := fb.And() mdi.On("GetTokenPool", context.Background(), "ns1", "test").Return(pool, nil) - mdi.On("GetTokenAccounts", context.Background(), f).Return([]*fftypes.TokenAccount{}, nil, nil) - _, _, err := am.GetTokenAccountsByPool(context.Background(), "ns1", "magic-tokens", "test", f) + mdi.On("GetTokenBalances", context.Background(), f).Return([]*fftypes.TokenBalance{}, nil, nil) + _, _, err := am.GetTokenBalancesByPool(context.Background(), "ns1", "magic-tokens", "test", f) assert.NoError(t, err) } -func TestGetTokenAccountsByPoolBadPool(t *testing.T) { +func TestGetTokenBalancesByPoolBadPool(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() mdi := am.database.(*databasemocks.Plugin) - fb := database.TokenAccountQueryFactory.NewFilter(context.Background()) + fb := database.TokenBalanceQueryFactory.NewFilter(context.Background()) f := fb.And() mdi.On("GetTokenPool", context.Background(), "ns1", "test").Return(nil, fmt.Errorf("pop")) - _, _, err := am.GetTokenAccountsByPool(context.Background(), "ns1", "magic-tokens", "test", f) + _, _, err := am.GetTokenBalancesByPool(context.Background(), "ns1", "magic-tokens", "test", f) assert.EqualError(t, err, "pop") } diff --git a/internal/database/sqlcommon/tokenaccount_sql.go b/internal/database/sqlcommon/tokenbalance_sql.go similarity index 65% rename from internal/database/sqlcommon/tokenaccount_sql.go rename to internal/database/sqlcommon/tokenbalance_sql.go index 985b1d2dc1..46bbfc463c 100644 --- a/internal/database/sqlcommon/tokenaccount_sql.go +++ b/internal/database/sqlcommon/tokenbalance_sql.go @@ -28,7 +28,7 @@ import ( ) var ( - tokenAccountColumns = []string{ + tokenBalanceColumns = []string{ "pool_protocol_id", "token_index", "connector", @@ -37,14 +37,14 @@ var ( "balance", "updated", } - tokenAccountFilterFieldMap = map[string]string{ + tokenBalanceFilterFieldMap = map[string]string{ "poolprotocolid": "pool_protocol_id", "tokenindex": "token_index", } ) -func (s *SQLCommon) addTokenAccountBalance(ctx context.Context, tx *txWrapper, transfer *fftypes.TokenTransfer, key string, negate bool) error { - account, err := s.GetTokenAccount(ctx, transfer.PoolProtocolID, transfer.TokenIndex, key) +func (s *SQLCommon) addTokenBalance(ctx context.Context, tx *txWrapper, transfer *fftypes.TokenTransfer, key string, negate bool) error { + account, err := s.GetTokenBalance(ctx, transfer.PoolProtocolID, transfer.TokenIndex, key) if err != nil { return err } @@ -63,7 +63,7 @@ func (s *SQLCommon) addTokenAccountBalance(ctx context.Context, tx *txWrapper, t if account != nil { if err = s.updateTx(ctx, tx, - sq.Update("tokenaccount"). + sq.Update("tokenbalance"). Set("balance", balance). Set("updated", fftypes.Now()). Where(sq.And{ @@ -77,8 +77,8 @@ func (s *SQLCommon) addTokenAccountBalance(ctx context.Context, tx *txWrapper, t } } else { if _, err = s.insertTx(ctx, tx, - sq.Insert("tokenaccount"). - Columns(tokenAccountColumns...). + sq.Insert("tokenbalance"). + Columns(tokenBalanceColumns...). Values( transfer.PoolProtocolID, transfer.TokenIndex, @@ -97,7 +97,7 @@ func (s *SQLCommon) addTokenAccountBalance(ctx context.Context, tx *txWrapper, t return nil } -func (s *SQLCommon) UpdateTokenAccountBalances(ctx context.Context, transfer *fftypes.TokenTransfer) (err error) { +func (s *SQLCommon) UpdateTokenBalances(ctx context.Context, transfer *fftypes.TokenTransfer) (err error) { ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) if err != nil { return err @@ -105,12 +105,12 @@ func (s *SQLCommon) UpdateTokenAccountBalances(ctx context.Context, transfer *ff defer s.rollbackTx(ctx, tx, autoCommit) if transfer.From != "" { - if err := s.addTokenAccountBalance(ctx, tx, transfer, transfer.From, true); err != nil { + if err := s.addTokenBalance(ctx, tx, transfer, transfer.From, true); err != nil { return err } } if transfer.To != "" { - if err := s.addTokenAccountBalance(ctx, tx, transfer, transfer.To, false); err != nil { + if err := s.addTokenBalance(ctx, tx, transfer, transfer.To, false); err != nil { return err } } @@ -118,8 +118,8 @@ func (s *SQLCommon) UpdateTokenAccountBalances(ctx context.Context, transfer *ff return s.commitTx(ctx, tx, autoCommit) } -func (s *SQLCommon) tokenAccountResult(ctx context.Context, row *sql.Rows) (*fftypes.TokenAccount, error) { - account := fftypes.TokenAccount{} +func (s *SQLCommon) tokenBalanceResult(ctx context.Context, row *sql.Rows) (*fftypes.TokenBalance, error) { + account := fftypes.TokenBalance{} err := row.Scan( &account.PoolProtocolID, &account.TokenIndex, @@ -130,15 +130,15 @@ func (s *SQLCommon) tokenAccountResult(ctx context.Context, row *sql.Rows) (*fft &account.Updated, ) if err != nil { - return nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "tokenaccount") + return nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "tokenbalance") } return &account, nil } -func (s *SQLCommon) getTokenAccountPred(ctx context.Context, desc string, pred interface{}) (*fftypes.TokenAccount, error) { +func (s *SQLCommon) getTokenBalancePred(ctx context.Context, desc string, pred interface{}) (*fftypes.TokenBalance, error) { rows, _, err := s.query(ctx, - sq.Select(tokenAccountColumns...). - From("tokenaccount"). + sq.Select(tokenBalanceColumns...). + From("tokenbalance"). Where(pred), ) if err != nil { @@ -151,7 +151,7 @@ func (s *SQLCommon) getTokenAccountPred(ctx context.Context, desc string, pred i return nil, nil } - account, err := s.tokenAccountResult(ctx, rows) + account, err := s.tokenBalanceResult(ctx, rows) if err != nil { return nil, err } @@ -159,17 +159,17 @@ func (s *SQLCommon) getTokenAccountPred(ctx context.Context, desc string, pred i return account, nil } -func (s *SQLCommon) GetTokenAccount(ctx context.Context, protocolID, tokenIndex, key string) (message *fftypes.TokenAccount, err error) { - desc := fftypes.TokenAccountIdentifier(protocolID, tokenIndex, key) - return s.getTokenAccountPred(ctx, desc, sq.And{ +func (s *SQLCommon) GetTokenBalance(ctx context.Context, protocolID, tokenIndex, key string) (message *fftypes.TokenBalance, err error) { + desc := fftypes.TokenBalanceIdentifier(protocolID, tokenIndex, key) + return s.getTokenBalancePred(ctx, desc, sq.And{ sq.Eq{"pool_protocol_id": protocolID}, sq.Eq{"token_index": tokenIndex}, sq.Eq{"key": key}, }) } -func (s *SQLCommon) GetTokenAccounts(ctx context.Context, filter database.Filter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { - query, fop, fi, err := s.filterSelect(ctx, "", sq.Select(tokenAccountColumns...).From("tokenaccount"), filter, tokenAccountFilterFieldMap, []interface{}{"seq"}) +func (s *SQLCommon) GetTokenBalances(ctx context.Context, filter database.Filter) ([]*fftypes.TokenBalance, *database.FilterResult, error) { + query, fop, fi, err := s.filterSelect(ctx, "", sq.Select(tokenBalanceColumns...).From("tokenbalance"), filter, tokenBalanceFilterFieldMap, []interface{}{"seq"}) if err != nil { return nil, nil, err } @@ -180,14 +180,14 @@ func (s *SQLCommon) GetTokenAccounts(ctx context.Context, filter database.Filter } defer rows.Close() - accounts := []*fftypes.TokenAccount{} + accounts := []*fftypes.TokenBalance{} for rows.Next() { - d, err := s.tokenAccountResult(ctx, rows) + d, err := s.tokenBalanceResult(ctx, rows) if err != nil { return nil, nil, err } accounts = append(accounts, d) } - return accounts, s.queryRes(ctx, tx, "tokenaccount", fop, fi), err + return accounts, s.queryRes(ctx, tx, "tokenbalance", fop, fi), err } diff --git a/internal/database/sqlcommon/tokenaccount_sql_test.go b/internal/database/sqlcommon/tokenbalance_sql_test.go similarity index 72% rename from internal/database/sqlcommon/tokenaccount_sql_test.go rename to internal/database/sqlcommon/tokenbalance_sql_test.go index b6d5c01cf7..6266de6f62 100644 --- a/internal/database/sqlcommon/tokenaccount_sql_test.go +++ b/internal/database/sqlcommon/tokenbalance_sql_test.go @@ -28,7 +28,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestTokenAccountE2EWithDB(t *testing.T) { +func TestTokenBalanceE2EWithDB(t *testing.T) { s, cleanup := newSQLiteTestProvider(t) defer cleanup() @@ -43,7 +43,7 @@ func TestTokenAccountE2EWithDB(t *testing.T) { To: "0x0", Amount: *fftypes.NewBigInt(10), } - account := &fftypes.TokenAccount{ + account := &fftypes.TokenBalance{ PoolProtocolID: "F1", TokenIndex: "1", Connector: "erc1155", @@ -53,11 +53,11 @@ func TestTokenAccountE2EWithDB(t *testing.T) { } accountJson, _ := json.Marshal(&account) - err := s.UpdateTokenAccountBalances(ctx, transfer) + err := s.UpdateTokenBalances(ctx, transfer) assert.NoError(t, err) // Query back the token account (by pool ID and identity) - accountRead, err := s.GetTokenAccount(ctx, "F1", "1", "0x0") + accountRead, err := s.GetTokenBalance(ctx, "F1", "1", "0x0") assert.NoError(t, err) assert.NotNil(t, accountRead) assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) @@ -66,13 +66,13 @@ func TestTokenAccountE2EWithDB(t *testing.T) { assert.Equal(t, string(accountJson), string(accountReadJson)) // Query back the token account (by query filter) - fb := database.TokenAccountQueryFactory.NewFilter(ctx) + fb := database.TokenBalanceQueryFactory.NewFilter(ctx) filter := fb.And( fb.Eq("poolprotocolid", account.PoolProtocolID), fb.Eq("tokenindex", account.TokenIndex), fb.Eq("key", account.Key), ) - accounts, res, err := s.GetTokenAccounts(ctx, filter.Count(true)) + accounts, res, err := s.GetTokenBalances(ctx, filter.Count(true)) assert.NoError(t, err) assert.Equal(t, 1, len(accounts)) assert.Equal(t, int64(1), *res.TotalCount) @@ -85,11 +85,11 @@ func TestTokenAccountE2EWithDB(t *testing.T) { transfer.From = "0x0" transfer.To = "0x1" transfer.Amount = *fftypes.NewBigInt(5) - err = s.UpdateTokenAccountBalances(ctx, transfer) + err = s.UpdateTokenBalances(ctx, transfer) assert.NoError(t, err) // Query back the token account (by pool ID and identity) - accountRead, err = s.GetTokenAccount(ctx, "F1", "1", "0x0") + accountRead, err = s.GetTokenBalance(ctx, "F1", "1", "0x0") assert.NoError(t, err) assert.NotNil(t, accountRead) assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) @@ -100,7 +100,7 @@ func TestTokenAccountE2EWithDB(t *testing.T) { assert.Equal(t, string(accountJson), string(accountReadJson)) // Query back the other token account (by pool ID and identity) - accountRead, err = s.GetTokenAccount(ctx, "F1", "1", "0x1") + accountRead, err = s.GetTokenBalance(ctx, "F1", "1", "0x1") assert.NoError(t, err) assert.NotNil(t, accountRead) assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) @@ -112,105 +112,105 @@ func TestTokenAccountE2EWithDB(t *testing.T) { assert.Equal(t, string(accountJson), string(accountReadJson)) } -func TestUpdateTokenAccountBalancesFailBegin(t *testing.T) { +func TestUpdateTokenBalancesFailBegin(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) - err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{}) + err := s.UpdateTokenBalances(context.Background(), &fftypes.TokenTransfer{}) assert.Regexp(t, "FF10114", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestUpdateTokenAccountBalancesFailSelect(t *testing.T) { +func TestUpdateTokenBalancesFailSelect(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) - err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) + err := s.UpdateTokenBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10115", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestUpdateTokenAccountBalancesFailInsert(t *testing.T) { +func TestUpdateTokenBalancesFailInsert(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) mock.ExpectRollback() - err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{From: "0x0"}) + err := s.UpdateTokenBalances(context.Background(), &fftypes.TokenTransfer{From: "0x0"}) assert.Regexp(t, "FF10116", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestUpdateTokenAccountBalancesFailInsert2(t *testing.T) { +func TestUpdateTokenBalancesFailInsert2(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) mock.ExpectRollback() - err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) + err := s.UpdateTokenBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10116", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestUpdateTokenAccountBalancesFailUpdate(t *testing.T) { +func TestUpdateTokenBalancesFailUpdate(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() - mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows(tokenAccountColumns).AddRow("F1", "1", "", "", "0x0", "0", 0)) + mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows(tokenBalanceColumns).AddRow("F1", "1", "", "", "0x0", "0", 0)) mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) mock.ExpectRollback() - err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) + err := s.UpdateTokenBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10117", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestUpdateTokenAccountBalancesFailCommit(t *testing.T) { +func TestUpdateTokenBalancesFailCommit(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectBegin() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit().WillReturnError(fmt.Errorf("pop")) - err := s.UpdateTokenAccountBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) + err := s.UpdateTokenBalances(context.Background(), &fftypes.TokenTransfer{To: "0x0"}) assert.Regexp(t, "FF10119", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestGetTokenAccountNotFound(t *testing.T) { +func TestGetTokenBalanceNotFound(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) - msg, err := s.GetTokenAccount(context.Background(), "F1", "1", "0x0") + msg, err := s.GetTokenBalance(context.Background(), "F1", "1", "0x0") assert.NoError(t, err) assert.Nil(t, msg) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestGetTokenAccountScanFail(t *testing.T) { +func TestGetTokenBalanceScanFail(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) - _, err := s.GetTokenAccount(context.Background(), "F1", "1", "0x0") + _, err := s.GetTokenBalance(context.Background(), "F1", "1", "0x0") assert.Regexp(t, "FF10121", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestGetTokenAccountsQueryFail(t *testing.T) { +func TestGetTokenBalancesQueryFail(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) - f := database.TokenAccountQueryFactory.NewFilter(context.Background()).Eq("poolprotocolid", "") - _, _, err := s.GetTokenAccounts(context.Background(), f) + f := database.TokenBalanceQueryFactory.NewFilter(context.Background()).Eq("poolprotocolid", "") + _, _, err := s.GetTokenBalances(context.Background(), f) assert.Regexp(t, "FF10115", err) assert.NoError(t, mock.ExpectationsWereMet()) } -func TestGetTokenAccountsBuildQueryFail(t *testing.T) { +func TestGetTokenBalancesBuildQueryFail(t *testing.T) { s, _ := newMockProvider().init() - f := database.TokenAccountQueryFactory.NewFilter(context.Background()).Eq("poolprotocolid", map[bool]bool{true: false}) - _, _, err := s.GetTokenAccounts(context.Background(), f) + f := database.TokenBalanceQueryFactory.NewFilter(context.Background()).Eq("poolprotocolid", map[bool]bool{true: false}) + _, _, err := s.GetTokenBalances(context.Background(), f) assert.Regexp(t, "FF10149.*id", err) } -func TestGetTokenAccountsScanFail(t *testing.T) { +func TestGetTokenBalancesScanFail(t *testing.T) { s, mock := newMockProvider().init() mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"poolprotocolid"}).AddRow("only one")) - f := database.TokenAccountQueryFactory.NewFilter(context.Background()).Eq("poolprotocolid", "") - _, _, err := s.GetTokenAccounts(context.Background(), f) + f := database.TokenBalanceQueryFactory.NewFilter(context.Background()).Eq("poolprotocolid", "") + _, _, err := s.GetTokenBalances(context.Background(), f) assert.Regexp(t, "FF10121", err) assert.NoError(t, mock.ExpectationsWereMet()) } diff --git a/internal/events/tokens_transferred.go b/internal/events/tokens_transferred.go index 5e962c0e68..19440f2df7 100644 --- a/internal/events/tokens_transferred.go +++ b/internal/events/tokens_transferred.go @@ -112,7 +112,7 @@ func (em *eventManager) TokensTransferred(tk tokens.Plugin, transfer *fftypes.To log.L(ctx).Errorf("Failed to record token transfer '%s': %s", transfer.ProtocolID, err) return err } - if err := em.database.UpdateTokenAccountBalances(ctx, transfer); err != nil { + if err := em.database.UpdateTokenBalances(ctx, transfer); err != nil { log.L(ctx).Errorf("Failed to update accounts %s -> %s for token transfer '%s': %s", transfer.From, transfer.To, transfer.ProtocolID, err) return err } diff --git a/internal/events/tokens_transferred_test.go b/internal/events/tokens_transferred_test.go index 69d87b5525..47da805404 100644 --- a/internal/events/tokens_transferred_test.go +++ b/internal/events/tokens_transferred_test.go @@ -53,8 +53,8 @@ func TestTokensTransferredSucceedWithRetries(t *testing.T) { mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(pool, nil).Times(3) mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(fmt.Errorf("pop")).Once() mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(nil).Times(2) - mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(fmt.Errorf("pop")).Once() - mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(nil).Once() + mdi.On("UpdateTokenBalances", em.ctx, transfer).Return(fmt.Errorf("pop")).Once() + mdi.On("UpdateTokenBalances", em.ctx, transfer).Return(nil).Once() mdi.On("InsertEvent", em.ctx, mock.MatchedBy(func(ev *fftypes.Event) bool { return ev.Type == fftypes.EventTypeTransferConfirmed && ev.Reference == transfer.LocalID && ev.Namespace == pool.Namespace })).Return(nil).Once() @@ -173,7 +173,7 @@ func TestTokensTransferredWithMessageReceived(t *testing.T) { mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(pool, nil).Times(2) mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(nil).Times(2) - mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(nil).Times(2) + mdi.On("UpdateTokenBalances", em.ctx, transfer).Return(nil).Times(2) mdi.On("GetMessages", em.ctx, mock.Anything).Return(nil, nil, fmt.Errorf("pop")).Once() mdi.On("GetMessages", em.ctx, mock.Anything).Return(messages, nil, nil).Once() mdi.On("InsertEvent", em.ctx, mock.MatchedBy(func(ev *fftypes.Event) bool { @@ -216,7 +216,7 @@ func TestTokensTransferredWithMessageSend(t *testing.T) { mdi.On("GetTokenPoolByProtocolID", em.ctx, "F1").Return(pool, nil).Times(2) mdi.On("UpsertTokenTransfer", em.ctx, transfer).Return(nil).Times(2) - mdi.On("UpdateTokenAccountBalances", em.ctx, transfer).Return(nil).Times(2) + mdi.On("UpdateTokenBalances", em.ctx, transfer).Return(nil).Times(2) mdi.On("GetMessages", em.ctx, mock.Anything).Return(messages, nil, nil).Times(2) mdi.On("UpsertMessage", em.ctx, mock.Anything, true, false).Return(fmt.Errorf("pop")) mdi.On("UpsertMessage", em.ctx, mock.MatchedBy(func(msg *fftypes.Message) bool { diff --git a/mocks/assetmocks/manager.go b/mocks/assetmocks/manager.go index 3480a6407e..f0827145c5 100644 --- a/mocks/assetmocks/manager.go +++ b/mocks/assetmocks/manager.go @@ -112,16 +112,16 @@ func (_m *Manager) CreateTokenPoolByType(ctx context.Context, ns string, connect return r0, r1 } -// GetTokenAccounts provides a mock function with given fields: ctx, ns, filter -func (_m *Manager) GetTokenAccounts(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { +// GetTokenBalances provides a mock function with given fields: ctx, ns, filter +func (_m *Manager) GetTokenBalances(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) { ret := _m.Called(ctx, ns, filter) - var r0 []*fftypes.TokenAccount - if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*fftypes.TokenAccount); ok { + var r0 []*fftypes.TokenBalance + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*fftypes.TokenBalance); ok { r0 = rf(ctx, ns, filter) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]*fftypes.TokenAccount) + r0 = ret.Get(0).([]*fftypes.TokenBalance) } } @@ -144,16 +144,16 @@ func (_m *Manager) GetTokenAccounts(ctx context.Context, ns string, filter datab return r0, r1, r2 } -// GetTokenAccountsByPool provides a mock function with given fields: ctx, ns, connector, poolName, filter -func (_m *Manager) GetTokenAccountsByPool(ctx context.Context, ns string, connector string, poolName string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { +// GetTokenBalancesByPool provides a mock function with given fields: ctx, ns, connector, poolName, filter +func (_m *Manager) GetTokenBalancesByPool(ctx context.Context, ns string, connector string, poolName string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) { ret := _m.Called(ctx, ns, connector, poolName, filter) - var r0 []*fftypes.TokenAccount - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, database.AndFilter) []*fftypes.TokenAccount); ok { + var r0 []*fftypes.TokenBalance + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, database.AndFilter) []*fftypes.TokenBalance); ok { r0 = rf(ctx, ns, connector, poolName, filter) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]*fftypes.TokenAccount) + r0 = ret.Get(0).([]*fftypes.TokenBalance) } } diff --git a/mocks/databasemocks/plugin.go b/mocks/databasemocks/plugin.go index 1e8bef10bf..8e76cb4f71 100644 --- a/mocks/databasemocks/plugin.go +++ b/mocks/databasemocks/plugin.go @@ -1293,16 +1293,16 @@ func (_m *Plugin) GetSubscriptions(ctx context.Context, filter database.Filter) return r0, r1, r2 } -// GetTokenAccount provides a mock function with given fields: ctx, protocolID, tokenIndex, identity -func (_m *Plugin) GetTokenAccount(ctx context.Context, protocolID string, tokenIndex string, identity string) (*fftypes.TokenAccount, error) { +// GetTokenBalance provides a mock function with given fields: ctx, protocolID, tokenIndex, identity +func (_m *Plugin) GetTokenBalance(ctx context.Context, protocolID string, tokenIndex string, identity string) (*fftypes.TokenBalance, error) { ret := _m.Called(ctx, protocolID, tokenIndex, identity) - var r0 *fftypes.TokenAccount - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *fftypes.TokenAccount); ok { + var r0 *fftypes.TokenBalance + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *fftypes.TokenBalance); ok { r0 = rf(ctx, protocolID, tokenIndex, identity) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*fftypes.TokenAccount) + r0 = ret.Get(0).(*fftypes.TokenBalance) } } @@ -1316,16 +1316,16 @@ func (_m *Plugin) GetTokenAccount(ctx context.Context, protocolID string, tokenI return r0, r1 } -// GetTokenAccounts provides a mock function with given fields: ctx, filter -func (_m *Plugin) GetTokenAccounts(ctx context.Context, filter database.Filter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { +// GetTokenBalances provides a mock function with given fields: ctx, filter +func (_m *Plugin) GetTokenBalances(ctx context.Context, filter database.Filter) ([]*fftypes.TokenBalance, *database.FilterResult, error) { ret := _m.Called(ctx, filter) - var r0 []*fftypes.TokenAccount - if rf, ok := ret.Get(0).(func(context.Context, database.Filter) []*fftypes.TokenAccount); ok { + var r0 []*fftypes.TokenBalance + if rf, ok := ret.Get(0).(func(context.Context, database.Filter) []*fftypes.TokenBalance); ok { r0 = rf(ctx, filter) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]*fftypes.TokenAccount) + r0 = ret.Get(0).([]*fftypes.TokenBalance) } } @@ -1844,8 +1844,8 @@ func (_m *Plugin) UpdateSubscription(ctx context.Context, ns string, name string return r0 } -// UpdateTokenAccountBalances provides a mock function with given fields: ctx, transfer -func (_m *Plugin) UpdateTokenAccountBalances(ctx context.Context, transfer *fftypes.TokenTransfer) error { +// UpdateTokenBalances provides a mock function with given fields: ctx, transfer +func (_m *Plugin) UpdateTokenBalances(ctx context.Context, transfer *fftypes.TokenTransfer) error { ret := _m.Called(ctx, transfer) var r0 error diff --git a/pkg/database/plugin.go b/pkg/database/plugin.go index 396b68e38f..c8385c20fb 100644 --- a/pkg/database/plugin.go +++ b/pkg/database/plugin.go @@ -363,15 +363,15 @@ type iTokenPoolCollection interface { GetTokenPools(ctx context.Context, filter Filter) ([]*fftypes.TokenPool, *FilterResult, error) } -type iTokenAccountCollection interface { - // UpdateTokenAccountBalances - Move some token balance from one account to another - UpdateTokenAccountBalances(ctx context.Context, transfer *fftypes.TokenTransfer) error +type iTokenBalanceCollection interface { + // UpdateTokenBalances - Move some token balance from one account to another + UpdateTokenBalances(ctx context.Context, transfer *fftypes.TokenTransfer) error - // GetTokenAccount - Get a token account by pool and account identity - GetTokenAccount(ctx context.Context, protocolID, tokenIndex, identity string) (*fftypes.TokenAccount, error) + // GetTokenBalance - Get a token balance by pool and account identity + GetTokenBalance(ctx context.Context, protocolID, tokenIndex, identity string) (*fftypes.TokenBalance, error) - // GetTokenAccounts - Get token accounts - GetTokenAccounts(ctx context.Context, filter Filter) ([]*fftypes.TokenAccount, *FilterResult, error) + // GetTokenBalances - Get token balances + GetTokenBalances(ctx context.Context, filter Filter) ([]*fftypes.TokenBalance, *FilterResult, error) } type iTokenTransferCollection interface { @@ -439,7 +439,7 @@ type PeristenceInterface interface { iBlobCollection iConfigRecordCollection iTokenPoolCollection - iTokenAccountCollection + iTokenBalanceCollection iTokenTransferCollection } @@ -510,7 +510,7 @@ const ( CollectionNextpins OtherCollection = "nextpins" CollectionNonces OtherCollection = "nonces" CollectionOffsets OtherCollection = "offsets" - CollectionTokenAccounts OtherCollection = "tokenaccounts" + CollectionTokenBalances OtherCollection = "tokenbalances" ) // Callbacks are the methods for passing data from plugin to core @@ -766,8 +766,8 @@ var TokenPoolQueryFactory = &queryFields{ "connector": &StringField{}, } -// TokenAccountQueryFactory filter fields for token accounts -var TokenAccountQueryFactory = &queryFields{ +// TokenBalanceQueryFactory filter fields for token accounts +var TokenBalanceQueryFactory = &queryFields{ "poolprotocolid": &StringField{}, "tokenindex": &StringField{}, "connector": &StringField{}, diff --git a/pkg/fftypes/tokenaccount.go b/pkg/fftypes/tokenbalance.go similarity index 84% rename from pkg/fftypes/tokenaccount.go rename to pkg/fftypes/tokenbalance.go index ddf07559db..7c0ae28848 100644 --- a/pkg/fftypes/tokenaccount.go +++ b/pkg/fftypes/tokenbalance.go @@ -16,7 +16,7 @@ package fftypes -type TokenAccount struct { +type TokenBalance struct { PoolProtocolID string `json:"poolProtocolId,omitempty"` TokenIndex string `json:"tokenIndex,omitempty"` Connector string `json:"connector,omitempty"` @@ -26,10 +26,10 @@ type TokenAccount struct { Updated *FFTime `json:"updated,omitempty"` } -func TokenAccountIdentifier(protocolID, tokenIndex, identity string) string { +func TokenBalanceIdentifier(protocolID, tokenIndex, identity string) string { return protocolID + ":" + tokenIndex + ":" + identity } -func (t *TokenAccount) Identifier() string { - return TokenAccountIdentifier(t.PoolProtocolID, t.TokenIndex, t.Key) +func (t *TokenBalance) Identifier() string { + return TokenBalanceIdentifier(t.PoolProtocolID, t.TokenIndex, t.Key) } diff --git a/pkg/fftypes/tokenaccount_test.go b/pkg/fftypes/tokenbalance_test.go similarity index 86% rename from pkg/fftypes/tokenaccount_test.go rename to pkg/fftypes/tokenbalance_test.go index bf40fa5009..426ed31d8b 100644 --- a/pkg/fftypes/tokenaccount_test.go +++ b/pkg/fftypes/tokenbalance_test.go @@ -22,11 +22,11 @@ import ( "github.com/stretchr/testify/assert" ) -func TestTokenAccountIdentifier(t *testing.T) { - account := &TokenAccount{ +func TestTokenBalanceIdentifier(t *testing.T) { + balance := &TokenBalance{ PoolProtocolID: "123", TokenIndex: "1", Key: "0x00", } - assert.Equal(t, "123:1:0x00", account.Identifier()) + assert.Equal(t, "123:1:0x00", balance.Identifier()) } diff --git a/test/e2e/restclient.go b/test/e2e/restclient.go index 23c49fb5f2..b664669066 100644 --- a/test/e2e/restclient.go +++ b/test/e2e/restclient.go @@ -429,8 +429,8 @@ func GetTokenTransfers(t *testing.T, client *resty.Client, poolName string) (tra return transfers } -func GetTokenAccount(t *testing.T, client *resty.Client, poolName, tokenIndex, key string) (account *fftypes.TokenAccount) { - var accounts []*fftypes.TokenAccount +func GetTokenAccount(t *testing.T, client *resty.Client, poolName, tokenIndex, key string) (account *fftypes.TokenBalance) { + var accounts []*fftypes.TokenBalance path := fmt.Sprintf(urlTokenAccounts, poolName) resp, err := client.R(). SetQueryParam("tokenIndex", tokenIndex). From 869a5db347d023eb0738cd8620bf1e6acce918b7 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 1 Nov 2021 14:26:53 -0400 Subject: [PATCH 4/9] Add GET /accounts to query all unique token account addresses This isn't a full-fledged view of accounts, but it provides a way to query all unique addresses that have (or have had) a non-zero token balance. Signed-off-by: Andrew Richardson --- docs/swagger/swagger.yaml | 102 ++++++++++++++++ .../apiserver/route_get_token_accounts.go | 45 +++++++ .../route_get_token_accounts_test.go | 42 +++++++ internal/apiserver/routes.go | 1 + internal/assets/manager.go | 5 + internal/assets/manager_test.go | 12 ++ .../database/sqlcommon/tokenbalance_sql.go | 25 ++++ .../sqlcommon/tokenbalance_sql_test.go | 114 ++++++++++++------ mocks/assetmocks/manager.go | 32 +++++ mocks/databasemocks/plugin.go | 32 +++++ pkg/database/plugin.go | 3 + pkg/fftypes/tokenbalance.go | 6 + 12 files changed, 379 insertions(+), 40 deletions(-) create mode 100644 internal/apiserver/route_get_token_accounts.go create mode 100644 internal/apiserver/route_get_token_accounts_test.go diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 50e5e8fe7a..0e24853344 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -5419,6 +5419,108 @@ paths: description: Success default: description: "" + /namespaces/{ns}/tokens/accounts: + get: + description: 'TODO: Description' + operationId: getTokenAccounts + parameters: + - description: 'TODO: Description' + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: balance + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: connector + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: key + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: namespace + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: poolprotocolid + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: tokenindex + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: updated + schema: + type: string + - description: Sort field. For multi-field sort use comma separated values (or + multiple query values) with '-' prefix for descending + in: query + name: sort + schema: + type: string + - description: Ascending sort order (overrides all fields in a multi-field sort) + in: query + name: ascending + schema: + type: string + - description: Descending sort order (overrides all fields in a multi-field + sort) + in: query + name: descending + schema: + type: string + - description: 'The number of records to skip (max: 1,000). Unsuitable for bulk + operations' + in: query + name: skip + schema: + type: string + - description: 'The maximum number of records to return (max: 1,000)' + in: query + name: limit + schema: + example: "25" + type: string + - description: Return a total count as well as items (adds extra database processing) + in: query + name: count + schema: + type: string + responses: + "200": + content: + application/json: + schema: + items: + properties: + key: + type: string + type: object + type: array + description: Success + default: + description: "" /namespaces/{ns}/tokens/balances: get: description: 'TODO: Description' diff --git a/internal/apiserver/route_get_token_accounts.go b/internal/apiserver/route_get_token_accounts.go new file mode 100644 index 0000000000..dec93a3f58 --- /dev/null +++ b/internal/apiserver/route_get_token_accounts.go @@ -0,0 +1,45 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http" + + "github.com/hyperledger/firefly/internal/config" + "github.com/hyperledger/firefly/internal/i18n" + "github.com/hyperledger/firefly/internal/oapispec" + "github.com/hyperledger/firefly/pkg/database" + "github.com/hyperledger/firefly/pkg/fftypes" +) + +var getTokenAccounts = &oapispec.Route{ + Name: "getTokenAccounts", + Path: "namespaces/{ns}/tokens/accounts", + Method: http.MethodGet, + PathParams: []*oapispec.PathParam{ + {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, + }, + QueryParams: nil, + FilterFactory: database.TokenBalanceQueryFactory, + Description: i18n.MsgTBD, + JSONInputValue: nil, + JSONOutputValue: func() interface{} { return []*fftypes.TokenAccount{} }, + JSONOutputCodes: []int{http.StatusOK}, + JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + return filterResult(r.Or.Assets().GetTokenAccounts(r.Ctx, r.PP["ns"], r.Filter)) + }, +} diff --git a/internal/apiserver/route_get_token_accounts_test.go b/internal/apiserver/route_get_token_accounts_test.go new file mode 100644 index 0000000000..6c481f25a6 --- /dev/null +++ b/internal/apiserver/route_get_token_accounts_test.go @@ -0,0 +1,42 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http/httptest" + "testing" + + "github.com/hyperledger/firefly/mocks/assetmocks" + "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestGetTokenAccounts(t *testing.T) { + o, r := newTestAPIServer() + mam := &assetmocks.Manager{} + o.On("Assets").Return(mam) + req := httptest.NewRequest("GET", "/api/v1/namespaces/ns1/tokens/accounts", nil) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + mam.On("GetTokenAccounts", mock.Anything, "ns1", mock.Anything). + Return([]*fftypes.TokenAccount{}, nil, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 200, res.Result().StatusCode) +} diff --git a/internal/apiserver/routes.go b/internal/apiserver/routes.go index 5e3a03b130..80314345c9 100644 --- a/internal/apiserver/routes.go +++ b/internal/apiserver/routes.go @@ -84,6 +84,7 @@ var routes = []*oapispec.Route{ getTokenPoolByNameOrID, getTokenPoolByName, getTokenBalances, + getTokenAccounts, getTokenAccountsByPool, getTokenTransfers, getTokenTransfersByPool, diff --git a/internal/assets/manager.go b/internal/assets/manager.go index bc43218b1b..2f9d15b757 100644 --- a/internal/assets/manager.go +++ b/internal/assets/manager.go @@ -45,6 +45,7 @@ type Manager interface { GetTokenBalances(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) GetTokenBalancesByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) + GetTokenAccounts(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) GetTokenTransfers(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenTransfer, *database.FilterResult, error) GetTokenTransferByID(ctx context.Context, ns, id string) (*fftypes.TokenTransfer, error) @@ -127,6 +128,10 @@ func (am *assetManager) GetTokenBalancesByPool(ctx context.Context, ns, connecto return am.database.GetTokenBalances(ctx, filter.Condition(filter.Builder().Eq("poolprotocolid", pool.ProtocolID))) } +func (am *assetManager) GetTokenAccounts(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { + return am.database.GetTokenAccounts(ctx, am.scopeNS(ns, filter)) +} + func (am *assetManager) GetTokenConnectors(ctx context.Context, ns string) ([]*fftypes.TokenConnector, error) { if err := fftypes.ValidateFFNameField(ctx, ns, "namespace"); err != nil { return nil, err diff --git a/internal/assets/manager_test.go b/internal/assets/manager_test.go index 89557c47f8..0dc79c0d58 100644 --- a/internal/assets/manager_test.go +++ b/internal/assets/manager_test.go @@ -107,6 +107,18 @@ func TestGetTokenBalancesByPoolBadPool(t *testing.T) { assert.EqualError(t, err, "pop") } +func TestGetTokenAccounts(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdi := am.database.(*databasemocks.Plugin) + fb := database.TokenBalanceQueryFactory.NewFilter(context.Background()) + f := fb.And() + mdi.On("GetTokenAccounts", context.Background(), f).Return([]*fftypes.TokenAccount{}, nil, nil) + _, _, err := am.GetTokenAccounts(context.Background(), "ns1", f) + assert.NoError(t, err) +} + func TestGetTokenConnectors(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() diff --git a/internal/database/sqlcommon/tokenbalance_sql.go b/internal/database/sqlcommon/tokenbalance_sql.go index 46bbfc463c..2c7124863b 100644 --- a/internal/database/sqlcommon/tokenbalance_sql.go +++ b/internal/database/sqlcommon/tokenbalance_sql.go @@ -191,3 +191,28 @@ func (s *SQLCommon) GetTokenBalances(ctx context.Context, filter database.Filter return accounts, s.queryRes(ctx, tx, "tokenbalance", fop, fi), err } + +func (s *SQLCommon) GetTokenAccounts(ctx context.Context, filter database.Filter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { + query, fop, fi, err := s.filterSelect(ctx, "", sq.Select("key").From("tokenbalance"), filter.Distinct(true), tokenBalanceFilterFieldMap, []interface{}{"seq"}) + if err != nil { + return nil, nil, err + } + + rows, tx, err := s.query(ctx, query) + if err != nil { + return nil, nil, err + } + defer rows.Close() + + var accounts []*fftypes.TokenAccount + for rows.Next() { + var account fftypes.TokenAccount + err := rows.Scan(&account.Key) + if err != nil { + return nil, nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "tokenbalance") + } + accounts = append(accounts, &account) + } + + return accounts, s.queryRes(ctx, tx, "tokenbalance", fop, fi), err +} diff --git a/internal/database/sqlcommon/tokenbalance_sql_test.go b/internal/database/sqlcommon/tokenbalance_sql_test.go index 6266de6f62..6e433ec7e5 100644 --- a/internal/database/sqlcommon/tokenbalance_sql_test.go +++ b/internal/database/sqlcommon/tokenbalance_sql_test.go @@ -43,7 +43,7 @@ func TestTokenBalanceE2EWithDB(t *testing.T) { To: "0x0", Amount: *fftypes.NewBigInt(10), } - account := &fftypes.TokenBalance{ + balance := &fftypes.TokenBalance{ PoolProtocolID: "F1", TokenIndex: "1", Connector: "erc1155", @@ -51,65 +51,74 @@ func TestTokenBalanceE2EWithDB(t *testing.T) { Key: "0x0", Balance: *fftypes.NewBigInt(10), } - accountJson, _ := json.Marshal(&account) + balanceJson, _ := json.Marshal(&balance) err := s.UpdateTokenBalances(ctx, transfer) assert.NoError(t, err) - // Query back the token account (by pool ID and identity) - accountRead, err := s.GetTokenBalance(ctx, "F1", "1", "0x0") + // Query back the token balance (by pool ID and identity) + balanceRead, err := s.GetTokenBalance(ctx, "F1", "1", "0x0") assert.NoError(t, err) - assert.NotNil(t, accountRead) - assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) - accountRead.Updated = nil - accountReadJson, _ := json.Marshal(&accountRead) - assert.Equal(t, string(accountJson), string(accountReadJson)) + assert.NotNil(t, balanceRead) + assert.Greater(t, balanceRead.Updated.UnixNano(), int64(0)) + balanceRead.Updated = nil + balanceReadJson, _ := json.Marshal(&balanceRead) + assert.Equal(t, string(balanceJson), string(balanceReadJson)) - // Query back the token account (by query filter) + // Query back the token balance (by query filter) fb := database.TokenBalanceQueryFactory.NewFilter(ctx) filter := fb.And( - fb.Eq("poolprotocolid", account.PoolProtocolID), - fb.Eq("tokenindex", account.TokenIndex), - fb.Eq("key", account.Key), + fb.Eq("poolprotocolid", balance.PoolProtocolID), + fb.Eq("tokenindex", balance.TokenIndex), + fb.Eq("key", balance.Key), ) - accounts, res, err := s.GetTokenBalances(ctx, filter.Count(true)) + balances, res, err := s.GetTokenBalances(ctx, filter.Count(true)) assert.NoError(t, err) - assert.Equal(t, 1, len(accounts)) + assert.Equal(t, 1, len(balances)) assert.Equal(t, int64(1), *res.TotalCount) - assert.Greater(t, accounts[0].Updated.UnixNano(), int64(0)) - accounts[0].Updated = nil - accountReadJson, _ = json.Marshal(accounts[0]) - assert.Equal(t, string(accountJson), string(accountReadJson)) + assert.Greater(t, balances[0].Updated.UnixNano(), int64(0)) + balances[0].Updated = nil + balanceReadJson, _ = json.Marshal(balances[0]) + assert.Equal(t, string(balanceJson), string(balanceReadJson)) - // Transfer half to a different account + // Transfer half to a different address transfer.From = "0x0" transfer.To = "0x1" transfer.Amount = *fftypes.NewBigInt(5) err = s.UpdateTokenBalances(ctx, transfer) assert.NoError(t, err) - // Query back the token account (by pool ID and identity) - accountRead, err = s.GetTokenBalance(ctx, "F1", "1", "0x0") + // Query back the token balance (by pool ID and identity) + balanceRead, err = s.GetTokenBalance(ctx, "F1", "1", "0x0") assert.NoError(t, err) - assert.NotNil(t, accountRead) - assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) - accountRead.Updated = nil - accountReadJson, _ = json.Marshal(&accountRead) - account.Balance = *fftypes.NewBigInt(5) - accountJson, _ = json.Marshal(&account) - assert.Equal(t, string(accountJson), string(accountReadJson)) - - // Query back the other token account (by pool ID and identity) - accountRead, err = s.GetTokenBalance(ctx, "F1", "1", "0x1") + assert.NotNil(t, balanceRead) + assert.Greater(t, balanceRead.Updated.UnixNano(), int64(0)) + balanceRead.Updated = nil + balanceReadJson, _ = json.Marshal(&balanceRead) + balance.Balance = *fftypes.NewBigInt(5) + balanceJson, _ = json.Marshal(&balance) + assert.Equal(t, string(balanceJson), string(balanceReadJson)) + + // Query back the other token balance (by pool ID and identity) + balanceRead, err = s.GetTokenBalance(ctx, "F1", "1", "0x1") assert.NoError(t, err) - assert.NotNil(t, accountRead) - assert.Greater(t, accountRead.Updated.UnixNano(), int64(0)) - accountRead.Updated = nil - accountReadJson, _ = json.Marshal(&accountRead) - account.Key = "0x1" - account.Balance = *fftypes.NewBigInt(5) - accountJson, _ = json.Marshal(&account) - assert.Equal(t, string(accountJson), string(accountReadJson)) + assert.NotNil(t, balanceRead) + assert.Greater(t, balanceRead.Updated.UnixNano(), int64(0)) + balanceRead.Updated = nil + balanceReadJson, _ = json.Marshal(&balanceRead) + balance.Key = "0x1" + balance.Balance = *fftypes.NewBigInt(5) + balanceJson, _ = json.Marshal(&balance) + assert.Equal(t, string(balanceJson), string(balanceReadJson)) + + // Query the list of unique accounts + fb2 := database.TokenBalanceQueryFactory.NewFilter(ctx) + accounts, fr, err := s.GetTokenAccounts(ctx, fb2.And().Count(true)) + assert.NoError(t, err) + assert.Equal(t, int64(2), *fr.TotalCount) + assert.Equal(t, 2, len(accounts)) + assert.Equal(t, "0x1", accounts[0].Key) + assert.Equal(t, "0x0", accounts[1].Key) } func TestUpdateTokenBalancesFailBegin(t *testing.T) { @@ -214,3 +223,28 @@ func TestGetTokenBalancesScanFail(t *testing.T) { assert.Regexp(t, "FF10121", err) assert.NoError(t, mock.ExpectationsWereMet()) } + +func TestGetTokenAccountsQueryFail(t *testing.T) { + s, mock := newMockProvider().init() + mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) + f := database.TokenBalanceQueryFactory.NewFilter(context.Background()).And() + _, _, err := s.GetTokenAccounts(context.Background(), f) + assert.Regexp(t, "FF10115", err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetTokenAccountsBuildQueryFail(t *testing.T) { + s, _ := newMockProvider().init() + f := database.TokenBalanceQueryFactory.NewFilter(context.Background()).Eq("poolprotocolid", map[bool]bool{true: false}) + _, _, err := s.GetTokenAccounts(context.Background(), f) + assert.Regexp(t, "FF10149.*id", err) +} + +func TestGetTokenAccountsScanFail(t *testing.T) { + s, mock := newMockProvider().init() + mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"key", "bad"}).AddRow("too many", "columns")) + f := database.TokenBalanceQueryFactory.NewFilter(context.Background()).And() + _, _, err := s.GetTokenAccounts(context.Background(), f) + assert.Regexp(t, "FF10121", err) + assert.NoError(t, mock.ExpectationsWereMet()) +} diff --git a/mocks/assetmocks/manager.go b/mocks/assetmocks/manager.go index f0827145c5..5400991f53 100644 --- a/mocks/assetmocks/manager.go +++ b/mocks/assetmocks/manager.go @@ -112,6 +112,38 @@ func (_m *Manager) CreateTokenPoolByType(ctx context.Context, ns string, connect return r0, r1 } +// GetTokenAccounts provides a mock function with given fields: ctx, ns, filter +func (_m *Manager) GetTokenAccounts(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { + ret := _m.Called(ctx, ns, filter) + + var r0 []*fftypes.TokenAccount + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*fftypes.TokenAccount); ok { + r0 = rf(ctx, ns, filter) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*fftypes.TokenAccount) + } + } + + var r1 *database.FilterResult + if rf, ok := ret.Get(1).(func(context.Context, string, database.AndFilter) *database.FilterResult); ok { + r1 = rf(ctx, ns, filter) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*database.FilterResult) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, database.AndFilter) error); ok { + r2 = rf(ctx, ns, filter) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // GetTokenBalances provides a mock function with given fields: ctx, ns, filter func (_m *Manager) GetTokenBalances(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) { ret := _m.Called(ctx, ns, filter) diff --git a/mocks/databasemocks/plugin.go b/mocks/databasemocks/plugin.go index 8e76cb4f71..1e5dcca5b6 100644 --- a/mocks/databasemocks/plugin.go +++ b/mocks/databasemocks/plugin.go @@ -1293,6 +1293,38 @@ func (_m *Plugin) GetSubscriptions(ctx context.Context, filter database.Filter) return r0, r1, r2 } +// GetTokenAccounts provides a mock function with given fields: ctx, filter +func (_m *Plugin) GetTokenAccounts(ctx context.Context, filter database.Filter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { + ret := _m.Called(ctx, filter) + + var r0 []*fftypes.TokenAccount + if rf, ok := ret.Get(0).(func(context.Context, database.Filter) []*fftypes.TokenAccount); ok { + r0 = rf(ctx, filter) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*fftypes.TokenAccount) + } + } + + var r1 *database.FilterResult + if rf, ok := ret.Get(1).(func(context.Context, database.Filter) *database.FilterResult); ok { + r1 = rf(ctx, filter) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*database.FilterResult) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, database.Filter) error); ok { + r2 = rf(ctx, filter) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // GetTokenBalance provides a mock function with given fields: ctx, protocolID, tokenIndex, identity func (_m *Plugin) GetTokenBalance(ctx context.Context, protocolID string, tokenIndex string, identity string) (*fftypes.TokenBalance, error) { ret := _m.Called(ctx, protocolID, tokenIndex, identity) diff --git a/pkg/database/plugin.go b/pkg/database/plugin.go index c8385c20fb..41d2d8378f 100644 --- a/pkg/database/plugin.go +++ b/pkg/database/plugin.go @@ -372,6 +372,9 @@ type iTokenBalanceCollection interface { // GetTokenBalances - Get token balances GetTokenBalances(ctx context.Context, filter Filter) ([]*fftypes.TokenBalance, *FilterResult, error) + + // GetTokenAccounts - Get token accounts (all distinct addresses that have a balance) + GetTokenAccounts(ctx context.Context, filter Filter) ([]*fftypes.TokenAccount, *FilterResult, error) } type iTokenTransferCollection interface { diff --git a/pkg/fftypes/tokenbalance.go b/pkg/fftypes/tokenbalance.go index 7c0ae28848..3be506dfa0 100644 --- a/pkg/fftypes/tokenbalance.go +++ b/pkg/fftypes/tokenbalance.go @@ -33,3 +33,9 @@ func TokenBalanceIdentifier(protocolID, tokenIndex, identity string) string { func (t *TokenBalance) Identifier() string { return TokenBalanceIdentifier(t.PoolProtocolID, t.TokenIndex, t.Key) } + +// Currently this type is just a filtered view of TokenBalance. +// If more fields/aggregation become needed, this may need its own table in the database. +type TokenAccount struct { + Key string `json:"key,omitempty"` +} From 2a020dacd6d26a7a567048f7a2b0876bc3c33081 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 1 Nov 2021 14:42:29 -0400 Subject: [PATCH 5/9] Update E2E tokens tests to use new URLs Signed-off-by: Andrew Richardson --- test/e2e/e2e_test.go | 4 +-- test/e2e/restclient.go | 32 +++++++++++---------- test/e2e/tokens_test.go | 64 ++++++++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 44 deletions(-) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index e2c2ddd6e3..cc16b469d1 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -122,9 +122,9 @@ func validateReceivedMessages(ts *testState, client *resty.Client, msgType fftyp return msgData.Value } -func validateAccountBalances(t *testing.T, client *resty.Client, poolName, tokenIndex string, balances map[string]int64) { +func validateAccountBalances(t *testing.T, client *resty.Client, poolProtocolID, tokenIndex string, balances map[string]int64) { for key, balance := range balances { - account := GetTokenAccount(t, client, poolName, tokenIndex, key) + account := GetTokenBalance(t, client, poolProtocolID, tokenIndex, key) assert.Equal(t, "erc1155", account.Connector) assert.Equal(t, balance, account.Balance.Int().Int64()) } diff --git a/test/e2e/restclient.go b/test/e2e/restclient.go index b664669066..a4c52f0a92 100644 --- a/test/e2e/restclient.go +++ b/test/e2e/restclient.go @@ -43,12 +43,12 @@ var ( urlGetData = "/namespaces/default/data" urlGetDataBlob = "/namespaces/default/data/%s/blob" urlSubscriptions = "/namespaces/default/subscriptions" - urlTokenPools = "/namespaces/default/tokens/erc1155/pools" urlDatatypes = "/namespaces/default/datatypes" - urlTokenMint = "/namespaces/default/tokens/erc1155/pools/%s/mint" - urlTokenBurn = "/namespaces/default/tokens/erc1155/pools/%s/burn" - urlTokenTransfers = "/namespaces/default/tokens/erc1155/pools/%s/transfers" - urlTokenAccounts = "/namespaces/default/tokens/erc1155/pools/%s/accounts" + urlTokenPools = "/namespaces/default/tokens/pools" + urlTokenMint = "/namespaces/default/tokens/mint" + urlTokenBurn = "/namespaces/default/tokens/burn" + urlTokenTransfers = "/namespaces/default/tokens/transfers" + urlTokenBalances = "/namespaces/default/tokens/balances" urlGetOrganizations = "/network/organizations" ) @@ -368,9 +368,9 @@ func GetTokenPools(t *testing.T, client *resty.Client, startTime time.Time) (poo return pools } -func MintTokens(t *testing.T, client *resty.Client, poolName string, mint *fftypes.TokenTransferInput, confirm bool) *fftypes.TokenTransfer { +func MintTokens(t *testing.T, client *resty.Client, mint *fftypes.TokenTransferInput, confirm bool) *fftypes.TokenTransfer { var transferOut fftypes.TokenTransfer - path := fmt.Sprintf(urlTokenMint, poolName) + path := urlTokenMint resp, err := client.R(). SetBody(mint). SetQueryParam("confirm", strconv.FormatBool(confirm)). @@ -385,9 +385,9 @@ func MintTokens(t *testing.T, client *resty.Client, poolName string, mint *fftyp return &transferOut } -func BurnTokens(t *testing.T, client *resty.Client, poolName string, burn *fftypes.TokenTransferInput, confirm bool) *fftypes.TokenTransfer { +func BurnTokens(t *testing.T, client *resty.Client, burn *fftypes.TokenTransferInput, confirm bool) *fftypes.TokenTransfer { var transferOut fftypes.TokenTransfer - path := fmt.Sprintf(urlTokenBurn, poolName) + path := urlTokenBurn resp, err := client.R(). SetBody(burn). SetQueryParam("confirm", strconv.FormatBool(confirm)). @@ -402,9 +402,9 @@ func BurnTokens(t *testing.T, client *resty.Client, poolName string, burn *fftyp return &transferOut } -func TransferTokens(t *testing.T, client *resty.Client, poolName string, transfer *fftypes.TokenTransferInput, confirm bool) *fftypes.TokenTransfer { +func TransferTokens(t *testing.T, client *resty.Client, transfer *fftypes.TokenTransferInput, confirm bool) *fftypes.TokenTransfer { var transferOut fftypes.TokenTransfer - path := fmt.Sprintf(urlTokenTransfers, poolName) + path := urlTokenTransfers resp, err := client.R(). SetBody(transfer). SetQueryParam("confirm", strconv.FormatBool(confirm)). @@ -419,9 +419,10 @@ func TransferTokens(t *testing.T, client *resty.Client, poolName string, transfe return &transferOut } -func GetTokenTransfers(t *testing.T, client *resty.Client, poolName string) (transfers []*fftypes.TokenTransfer) { - path := fmt.Sprintf(urlTokenTransfers, poolName) +func GetTokenTransfers(t *testing.T, client *resty.Client, poolProtocolID string) (transfers []*fftypes.TokenTransfer) { + path := urlTokenTransfers resp, err := client.R(). + SetQueryParam("poolprotocolid", poolProtocolID). SetResult(&transfers). Get(path) require.NoError(t, err) @@ -429,10 +430,11 @@ func GetTokenTransfers(t *testing.T, client *resty.Client, poolName string) (tra return transfers } -func GetTokenAccount(t *testing.T, client *resty.Client, poolName, tokenIndex, key string) (account *fftypes.TokenBalance) { +func GetTokenBalance(t *testing.T, client *resty.Client, poolProtocolID, tokenIndex, key string) (account *fftypes.TokenBalance) { var accounts []*fftypes.TokenBalance - path := fmt.Sprintf(urlTokenAccounts, poolName) + path := urlTokenBalances resp, err := client.R(). + SetQueryParam("poolprotocolid", poolProtocolID). SetQueryParam("tokenIndex", tokenIndex). SetQueryParam("key", key). SetResult(&accounts). diff --git a/test/e2e/tokens_test.go b/test/e2e/tokens_test.go index fcb46bdb94..5da4d308ed 100644 --- a/test/e2e/tokens_test.go +++ b/test/e2e/tokens_test.go @@ -59,6 +59,8 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { assert.Equal(suite.T(), fftypes.TokenTypeFungible, pools[0].Type) assert.NotEmpty(suite.T(), pools[0].ProtocolID) + poolProtocolID := pools[0].ProtocolID + <-received2 pools = GetTokenPools(suite.T(), suite.testState.client1, suite.testState.startTime) assert.Equal(suite.T(), 1, len(pools)) @@ -70,26 +72,27 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { transfer := &fftypes.TokenTransferInput{ TokenTransfer: fftypes.TokenTransfer{Amount: *fftypes.NewBigInt(1)}, + Pool: poolName, } - MintTokens(suite.T(), suite.testState.client1, poolName, transfer, false) + MintTokens(suite.T(), suite.testState.client1, transfer, false) <-received1 - transfers := GetTokenTransfers(suite.T(), suite.testState.client1, poolName) + transfers := GetTokenTransfers(suite.T(), suite.testState.client1, poolProtocolID) assert.Equal(suite.T(), 1, len(transfers)) assert.Equal(suite.T(), "erc1155", transfers[0].Connector) assert.Equal(suite.T(), fftypes.TokenTransferTypeMint, transfers[0].Type) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client1, poolName, "", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client1, poolProtocolID, "", map[string]int64{ suite.testState.org1.Identity: 1, }) <-received2 - transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolName) + transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolProtocolID) assert.Equal(suite.T(), 1, len(transfers)) assert.Equal(suite.T(), "erc1155", transfers[0].Connector) assert.Equal(suite.T(), fftypes.TokenTransferTypeMint, transfers[0].Type) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client2, poolName, "", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client2, poolProtocolID, "", map[string]int64{ suite.testState.org1.Identity: 1, }) @@ -98,6 +101,7 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { To: suite.testState.org2.Identity, Amount: *fftypes.NewBigInt(1), }, + Pool: poolName, Message: &fftypes.MessageInOut{ InlineData: fftypes.InlineData{ { @@ -106,11 +110,11 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { }, }, } - TransferTokens(suite.T(), suite.testState.client1, poolName, transfer, false) + TransferTokens(suite.T(), suite.testState.client1, transfer, false) <-received1 // one event for transfer <-received1 // one event for message - transfers = GetTokenTransfers(suite.T(), suite.testState.client1, poolName) + transfers = GetTokenTransfers(suite.T(), suite.testState.client1, poolProtocolID) assert.Equal(suite.T(), 2, len(transfers)) assert.Equal(suite.T(), "erc1155", transfers[0].Connector) assert.Equal(suite.T(), fftypes.TokenTransferTypeTransfer, transfers[0].Type) @@ -118,48 +122,49 @@ func (suite *TokensTestSuite) TestE2EFungibleTokensAsync() { data := GetDataForMessage(suite.T(), suite.testState.client1, suite.testState.startTime, transfers[0].MessageHash) assert.Equal(suite.T(), 1, len(data)) assert.Equal(suite.T(), `"payment for data"`, data[0].Value.String()) - validateAccountBalances(suite.T(), suite.testState.client1, poolName, "", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client1, poolProtocolID, "", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 1, }) <-received2 // one event for transfer <-received2 // one event for message - transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolName) + transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolProtocolID) assert.Equal(suite.T(), 2, len(transfers)) assert.Equal(suite.T(), "erc1155", transfers[0].Connector) assert.Equal(suite.T(), fftypes.TokenTransferTypeTransfer, transfers[0].Type) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client2, poolName, "", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client2, poolProtocolID, "", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 1, }) transfer = &fftypes.TokenTransferInput{ TokenTransfer: fftypes.TokenTransfer{Amount: *fftypes.NewBigInt(1)}, + Pool: poolName, } - BurnTokens(suite.T(), suite.testState.client2, poolName, transfer, false) + BurnTokens(suite.T(), suite.testState.client2, transfer, false) <-received2 - transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolName) + transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolProtocolID) assert.Equal(suite.T(), 3, len(transfers)) assert.Equal(suite.T(), "erc1155", transfers[0].Connector) assert.Equal(suite.T(), fftypes.TokenTransferTypeBurn, transfers[0].Type) assert.Equal(suite.T(), "", transfers[0].TokenIndex) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client2, poolName, "", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client2, poolProtocolID, "", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 0, }) <-received1 - transfers = GetTokenTransfers(suite.T(), suite.testState.client1, poolName) + transfers = GetTokenTransfers(suite.T(), suite.testState.client1, poolProtocolID) assert.Equal(suite.T(), 3, len(transfers)) assert.Equal(suite.T(), "erc1155", transfers[0].Connector) assert.Equal(suite.T(), fftypes.TokenTransferTypeBurn, transfers[0].Type) assert.Equal(suite.T(), "", transfers[0].TokenIndex) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client1, poolName, "", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client1, poolProtocolID, "", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 0, }) @@ -185,6 +190,8 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { assert.Equal(suite.T(), fftypes.TokenTypeNonFungible, poolOut.Type) assert.NotEmpty(suite.T(), poolOut.ProtocolID) + poolProtocolID := poolOut.ProtocolID + <-received1 <-received2 pools = GetTokenPools(suite.T(), suite.testState.client1, suite.testState.startTime) @@ -196,23 +203,24 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { transfer := &fftypes.TokenTransferInput{ TokenTransfer: fftypes.TokenTransfer{Amount: *fftypes.NewBigInt(1)}, + Pool: poolName, } - transferOut := MintTokens(suite.T(), suite.testState.client1, poolName, transfer, true) + transferOut := MintTokens(suite.T(), suite.testState.client1, transfer, true) assert.Equal(suite.T(), fftypes.TokenTransferTypeMint, transferOut.Type) assert.Equal(suite.T(), "1", transferOut.TokenIndex) assert.Equal(suite.T(), int64(1), transferOut.Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client1, poolName, "1", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client1, poolProtocolID, "1", map[string]int64{ suite.testState.org1.Identity: 1, }) <-received1 <-received2 - transfers := GetTokenTransfers(suite.T(), suite.testState.client2, poolName) + transfers := GetTokenTransfers(suite.T(), suite.testState.client2, poolProtocolID) assert.Equal(suite.T(), 1, len(transfers)) assert.Equal(suite.T(), fftypes.TokenTransferTypeMint, transfers[0].Type) assert.Equal(suite.T(), "1", transfers[0].TokenIndex) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client2, poolName, "1", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client2, poolProtocolID, "1", map[string]int64{ suite.testState.org1.Identity: 1, }) @@ -222,6 +230,7 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { To: suite.testState.org2.Identity, Amount: *fftypes.NewBigInt(1), }, + Pool: poolName, Message: &fftypes.MessageInOut{ InlineData: fftypes.InlineData{ { @@ -230,14 +239,14 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { }, }, } - transferOut = TransferTokens(suite.T(), suite.testState.client1, poolName, transfer, true) + transferOut = TransferTokens(suite.T(), suite.testState.client1, transfer, true) assert.Equal(suite.T(), fftypes.TokenTransferTypeTransfer, transferOut.Type) assert.Equal(suite.T(), "1", transferOut.TokenIndex) assert.Equal(suite.T(), int64(1), transferOut.Amount.Int().Int64()) data := GetDataForMessage(suite.T(), suite.testState.client1, suite.testState.startTime, transferOut.MessageHash) assert.Equal(suite.T(), 1, len(data)) assert.Equal(suite.T(), `"ownership change"`, data[0].Value.String()) - validateAccountBalances(suite.T(), suite.testState.client1, poolName, "1", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client1, poolProtocolID, "1", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 1, }) @@ -246,12 +255,12 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { <-received1 // one event for message <-received2 // one event for transfer <-received2 // one event for message - transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolName) + transfers = GetTokenTransfers(suite.T(), suite.testState.client2, poolProtocolID) assert.Equal(suite.T(), 2, len(transfers)) assert.Equal(suite.T(), fftypes.TokenTransferTypeTransfer, transfers[0].Type) assert.Equal(suite.T(), "1", transfers[0].TokenIndex) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client2, poolName, "1", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client2, poolProtocolID, "1", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 1, }) @@ -261,24 +270,25 @@ func (suite *TokensTestSuite) TestE2ENonFungibleTokensSync() { TokenIndex: "1", Amount: *fftypes.NewBigInt(1), }, + Pool: poolName, } - transferOut = BurnTokens(suite.T(), suite.testState.client2, poolName, transfer, true) + transferOut = BurnTokens(suite.T(), suite.testState.client2, transfer, true) assert.Equal(suite.T(), fftypes.TokenTransferTypeBurn, transferOut.Type) assert.Equal(suite.T(), "1", transferOut.TokenIndex) assert.Equal(suite.T(), int64(1), transferOut.Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client2, poolName, "1", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client2, poolProtocolID, "1", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 0, }) <-received2 <-received1 - transfers = GetTokenTransfers(suite.T(), suite.testState.client1, poolName) + transfers = GetTokenTransfers(suite.T(), suite.testState.client1, poolProtocolID) assert.Equal(suite.T(), 3, len(transfers)) assert.Equal(suite.T(), fftypes.TokenTransferTypeBurn, transfers[0].Type) assert.Equal(suite.T(), "1", transfers[0].TokenIndex) assert.Equal(suite.T(), int64(1), transfers[0].Amount.Int().Int64()) - validateAccountBalances(suite.T(), suite.testState.client1, poolName, "1", map[string]int64{ + validateAccountBalances(suite.T(), suite.testState.client1, poolProtocolID, "1", map[string]int64{ suite.testState.org1.Identity: 0, suite.testState.org2.Identity: 0, }) From 46bec4836567f38001a8c6c96453145edcd2a01c Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 1 Nov 2021 14:44:36 -0400 Subject: [PATCH 6/9] Mark old token routes as deprecated Signed-off-by: Andrew Richardson --- docs/swagger/swagger.yaml | 8 ++++++++ internal/apiserver/route_get_token_accounts_by_pool.go | 1 + internal/apiserver/route_get_token_pool_by_name.go | 1 + internal/apiserver/route_get_token_pools_by_type.go | 1 + internal/apiserver/route_get_token_transfers_by_pool.go | 1 + internal/apiserver/route_post_token_burn_by_type.go | 1 + internal/apiserver/route_post_token_mint_by_type.go | 1 + internal/apiserver/route_post_token_pool_by_type.go | 1 + internal/apiserver/route_post_token_transfer_by_type.go | 1 + 9 files changed, 16 insertions(+) diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 0e24853344..f9a28cb824 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -4128,6 +4128,7 @@ paths: description: "" /namespaces/{ns}/tokens/{type}/pools: get: + deprecated: true description: 'TODO: Description' operationId: getTokenPoolsByType parameters: @@ -4281,6 +4282,7 @@ paths: default: description: "" post: + deprecated: true description: 'TODO: Description' operationId: postTokenPoolByType parameters: @@ -4404,6 +4406,7 @@ paths: description: "" /namespaces/{ns}/tokens/{type}/pools/{name}: get: + deprecated: true description: 'TODO: Description' operationId: getTokenPoolByName parameters: @@ -4473,6 +4476,7 @@ paths: description: "" /namespaces/{ns}/tokens/{type}/pools/{name}/accounts: get: + deprecated: true description: 'TODO: Description' operationId: getTokenAccountsByPool parameters: @@ -4597,6 +4601,7 @@ paths: description: "" /namespaces/{ns}/tokens/{type}/pools/{name}/burn: post: + deprecated: true description: 'TODO: Description' operationId: postTokenBurnByType parameters: @@ -4817,6 +4822,7 @@ paths: description: "" /namespaces/{ns}/tokens/{type}/pools/{name}/mint: post: + deprecated: true description: 'TODO: Description' operationId: postTokenMintByType parameters: @@ -5037,6 +5043,7 @@ paths: description: "" /namespaces/{ns}/tokens/{type}/pools/{name}/transfers: get: + deprecated: true description: 'TODO: Description' operationId: getTokenTransfersByPool parameters: @@ -5201,6 +5208,7 @@ paths: default: description: "" post: + deprecated: true description: 'TODO: Description' operationId: postTokenTransferByType parameters: diff --git a/internal/apiserver/route_get_token_accounts_by_pool.go b/internal/apiserver/route_get_token_accounts_by_pool.go index 05d6bccd36..5e7f602202 100644 --- a/internal/apiserver/route_get_token_accounts_by_pool.go +++ b/internal/apiserver/route_get_token_accounts_by_pool.go @@ -44,4 +44,5 @@ var getTokenAccountsByPool = &oapispec.Route{ JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { return filterResult(r.Or.Assets().GetTokenBalancesByPool(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Filter)) }, + Deprecated: true, } diff --git a/internal/apiserver/route_get_token_pool_by_name.go b/internal/apiserver/route_get_token_pool_by_name.go index aeef91f802..a747ecb36c 100644 --- a/internal/apiserver/route_get_token_pool_by_name.go +++ b/internal/apiserver/route_get_token_pool_by_name.go @@ -44,4 +44,5 @@ var getTokenPoolByName = &oapispec.Route{ output, err = r.Or.Assets().GetTokenPool(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"]) return output, err }, + Deprecated: true, } diff --git a/internal/apiserver/route_get_token_pools_by_type.go b/internal/apiserver/route_get_token_pools_by_type.go index 22831fcf1d..91ab4ac92a 100644 --- a/internal/apiserver/route_get_token_pools_by_type.go +++ b/internal/apiserver/route_get_token_pools_by_type.go @@ -43,4 +43,5 @@ var getTokenPoolsByType = &oapispec.Route{ JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { return filterResult(r.Or.Assets().GetTokenPoolsByType(r.Ctx, r.PP["ns"], r.PP["type"], r.Filter)) }, + Deprecated: true, } diff --git a/internal/apiserver/route_get_token_transfers_by_pool.go b/internal/apiserver/route_get_token_transfers_by_pool.go index 5eb57e2806..e589b40baa 100644 --- a/internal/apiserver/route_get_token_transfers_by_pool.go +++ b/internal/apiserver/route_get_token_transfers_by_pool.go @@ -44,4 +44,5 @@ var getTokenTransfersByPool = &oapispec.Route{ JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { return filterResult(r.Or.Assets().GetTokenTransfersByPool(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Filter)) }, + Deprecated: true, } diff --git a/internal/apiserver/route_post_token_burn_by_type.go b/internal/apiserver/route_post_token_burn_by_type.go index 36de79899a..f1d0ba9177 100644 --- a/internal/apiserver/route_post_token_burn_by_type.go +++ b/internal/apiserver/route_post_token_burn_by_type.go @@ -49,4 +49,5 @@ var postTokenBurnByType = &oapispec.Route{ r.SuccessStatus = syncRetcode(waitConfirm) return r.Or.Assets().BurnTokensByType(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) }, + Deprecated: true, } diff --git a/internal/apiserver/route_post_token_mint_by_type.go b/internal/apiserver/route_post_token_mint_by_type.go index 440366d3cc..1e87b71216 100644 --- a/internal/apiserver/route_post_token_mint_by_type.go +++ b/internal/apiserver/route_post_token_mint_by_type.go @@ -49,4 +49,5 @@ var postTokenMintByType = &oapispec.Route{ r.SuccessStatus = syncRetcode(waitConfirm) return r.Or.Assets().MintTokensByType(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) }, + Deprecated: true, } diff --git a/internal/apiserver/route_post_token_pool_by_type.go b/internal/apiserver/route_post_token_pool_by_type.go index 21cdd84a55..82d0a82c52 100644 --- a/internal/apiserver/route_post_token_pool_by_type.go +++ b/internal/apiserver/route_post_token_pool_by_type.go @@ -48,4 +48,5 @@ var postTokenPoolByType = &oapispec.Route{ r.SuccessStatus = syncRetcode(waitConfirm) return r.Or.Assets().CreateTokenPoolByType(r.Ctx, r.PP["ns"], r.PP["type"], r.Input.(*fftypes.TokenPool), waitConfirm) }, + Deprecated: true, } diff --git a/internal/apiserver/route_post_token_transfer_by_type.go b/internal/apiserver/route_post_token_transfer_by_type.go index c838f0b91f..e37439cc8b 100644 --- a/internal/apiserver/route_post_token_transfer_by_type.go +++ b/internal/apiserver/route_post_token_transfer_by_type.go @@ -49,4 +49,5 @@ var postTokenTransferByType = &oapispec.Route{ r.SuccessStatus = syncRetcode(waitConfirm) return r.Or.Assets().TransferTokensByType(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) }, + Deprecated: true, } From 7b0f03478db9c97a5354d47696e69b4b002843c2 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 1 Nov 2021 15:48:27 -0400 Subject: [PATCH 7/9] Notate deprecated methods on asset manager Signed-off-by: Andrew Richardson --- internal/assets/manager.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/assets/manager.go b/internal/assets/manager.go index 2f9d15b757..c1f2b07c64 100644 --- a/internal/assets/manager.go +++ b/internal/assets/manager.go @@ -36,33 +36,36 @@ import ( type Manager interface { CreateTokenPool(ctx context.Context, ns string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) - CreateTokenPoolByType(ctx context.Context, ns, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) GetTokenPools(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenPool, *database.FilterResult, error) - GetTokenPoolsByType(ctx context.Context, ns, connector string, filter database.AndFilter) ([]*fftypes.TokenPool, *database.FilterResult, error) GetTokenPool(ctx context.Context, ns, connector, poolName string) (*fftypes.TokenPool, error) GetTokenPoolByNameOrID(ctx context.Context, ns string, poolNameOrID string) (*fftypes.TokenPool, error) ValidateTokenPoolTx(ctx context.Context, pool *fftypes.TokenPool, protocolTxID string) error GetTokenBalances(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) - GetTokenBalancesByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) GetTokenAccounts(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenAccount, *database.FilterResult, error) GetTokenTransfers(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenTransfer, *database.FilterResult, error) GetTokenTransferByID(ctx context.Context, ns, id string) (*fftypes.TokenTransfer, error) - GetTokenTransfersByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenTransfer, *database.FilterResult, error) NewTransfer(ns, connector, poolName string, transfer *fftypes.TokenTransferInput) sysmessaging.MessageSender MintTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) - MintTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) BurnTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) - BurnTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) TransferTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) - TransferTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + GetTokenConnectors(ctx context.Context, ns string) ([]*fftypes.TokenConnector, error) // Bound token callbacks TokenPoolCreated(ti tokens.Plugin, pool *fftypes.TokenPool, protocolTxID string, additionalInfo fftypes.JSONObject) error + // Deprecated + CreateTokenPoolByType(ctx context.Context, ns, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) + GetTokenPoolsByType(ctx context.Context, ns, connector string, filter database.AndFilter) ([]*fftypes.TokenPool, *database.FilterResult, error) + GetTokenBalancesByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenBalance, *database.FilterResult, error) + GetTokenTransfersByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenTransfer, *database.FilterResult, error) + MintTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + BurnTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + TransferTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + Start() error WaitStop() } From 15b155e088ef84f652c3d9a1802f76536d45e69b Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 2 Nov 2021 09:26:38 -0400 Subject: [PATCH 8/9] Move "distinct" clause from Filter to SelectBuilder Signed-off-by: Andrew Richardson --- internal/database/sqlcommon/tokenbalance_sql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/database/sqlcommon/tokenbalance_sql.go b/internal/database/sqlcommon/tokenbalance_sql.go index 2c7124863b..457b8b642f 100644 --- a/internal/database/sqlcommon/tokenbalance_sql.go +++ b/internal/database/sqlcommon/tokenbalance_sql.go @@ -193,7 +193,7 @@ func (s *SQLCommon) GetTokenBalances(ctx context.Context, filter database.Filter } func (s *SQLCommon) GetTokenAccounts(ctx context.Context, filter database.Filter) ([]*fftypes.TokenAccount, *database.FilterResult, error) { - query, fop, fi, err := s.filterSelect(ctx, "", sq.Select("key").From("tokenbalance"), filter.Distinct(true), tokenBalanceFilterFieldMap, []interface{}{"seq"}) + query, fop, fi, err := s.filterSelect(ctx, "", sq.Select("key").Distinct().From("tokenbalance"), filter, tokenBalanceFilterFieldMap, []interface{}{"seq"}) if err != nil { return nil, nil, err } From 5bf055a80a559b227d4cb7ecb7c01f5f0e67a6e7 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 2 Nov 2021 09:28:11 -0400 Subject: [PATCH 9/9] Revert "Add database support for SELECT DISTINCT" This reverts commit 7ae1afa98e116fa0ebb9e83ca8fd0c64658cf309. Signed-off-by: Andrew Richardson --- internal/database/sqlcommon/filter_sql.go | 3 --- internal/database/sqlcommon/filter_sql_test.go | 14 -------------- pkg/database/filter.go | 14 -------------- pkg/database/filter_test.go | 8 -------- 4 files changed, 39 deletions(-) diff --git a/internal/database/sqlcommon/filter_sql.go b/internal/database/sqlcommon/filter_sql.go index cf40804448..aed34d2e45 100644 --- a/internal/database/sqlcommon/filter_sql.go +++ b/internal/database/sqlcommon/filter_sql.go @@ -70,9 +70,6 @@ func (s *SQLCommon) filterSelect(ctx context.Context, tableName string, sel sq.S sel = sel.Limit(fi.Limit) } } - if fi.Distinct { - sel = sel.Distinct() - } return sel, fop, fi, err } diff --git a/internal/database/sqlcommon/filter_sql_test.go b/internal/database/sqlcommon/filter_sql_test.go index 9b64254593..f7f20730d5 100644 --- a/internal/database/sqlcommon/filter_sql_test.go +++ b/internal/database/sqlcommon/filter_sql_test.go @@ -169,17 +169,3 @@ func TestSQLQueryFactoryDefaultSortBadType(t *testing.T) { s.filterSelect(context.Background(), "", sel, f, nil, []interface{}{100}) }) } - -func TestSQLQueryFactorySelectDistinct(t *testing.T) { - - s, _ := newMockProvider().init() - sel := squirrel.Select("name").From("mytable") - fb := database.MessageQueryFactory.NewFilter(context.Background()) - f := fb.And().Distinct(true) - sel, _, _, err := s.filterSelect(context.Background(), "", sel, f, nil, []interface{}{"name"}) - assert.NoError(t, err) - - sqlFilter, _, err := sel.ToSql() - assert.NoError(t, err) - assert.Equal(t, "SELECT DISTINCT name FROM mytable WHERE (1=1) ORDER BY name DESC", sqlFilter) -} diff --git a/pkg/database/filter.go b/pkg/database/filter.go index 7c5e6c4f1e..f9fb5433af 100644 --- a/pkg/database/filter.go +++ b/pkg/database/filter.go @@ -46,9 +46,6 @@ type Filter interface { // Request a count to be returned on the total number that match the query Count(c bool) Filter - // Select distinct rows - Distinct(bool) Filter - // Finalize completes the filter, and for the plugin to validated output structure to convert Finalize() (*FilterInfo, error) @@ -159,7 +156,6 @@ type FilterInfo struct { Skip uint64 Limit uint64 Count bool - Distinct bool Field string Op FilterOp Values []FieldSerialization @@ -235,9 +231,6 @@ func (f *FilterInfo) String() string { if f.Count { val.WriteString(" count=true") } - if f.Distinct { - val.WriteString(" distinct=true") - } return val.String() } @@ -261,7 +254,6 @@ type filterBuilder struct { count bool forceAscending bool forceDescending bool - distinct bool } type baseFilter struct { @@ -335,7 +327,6 @@ func (f *baseFilter) Finalize() (fi *FilterInfo, err error) { Skip: f.fb.skip, Limit: f.fb.limit, Count: f.fb.count, - Distinct: f.fb.distinct, }, nil } @@ -381,11 +372,6 @@ func (f *baseFilter) Descending() Filter { return f } -func (f *baseFilter) Distinct(d bool) Filter { - f.fb.distinct = true - return f -} - type andFilter struct { baseFilter } diff --git a/pkg/database/filter_test.go b/pkg/database/filter_test.go index aa93b1c1bf..2243d0038a 100644 --- a/pkg/database/filter_test.go +++ b/pkg/database/filter_test.go @@ -79,14 +79,6 @@ func TestBuildMessageFilter3(t *testing.T) { assert.Equal(t, "( created IN [1000000000,2000000000,3000000000] ) && ( created NI [1000000000,2000000000,3000000000] ) && ( created < 0 ) && ( created <= 0 ) && ( created >= 0 ) && ( created != 0 ) && ( sequence > 12345 ) && ( topics %= 'abc' ) && ( topics %! 'def' ) && ( topics ^= 'ghi' ) && ( topics ^! 'jkl' ) sort=-created,topics,-sequence", f.String()) } -func TestBuildMessageFilterDistinct(t *testing.T) { - fb := MessageQueryFactory.NewFilter(context.Background()) - f, err := fb.And().Distinct(true).Finalize() - - assert.NoError(t, err) - assert.Equal(t, " distinct=true", f.String()) -} - func TestBuildMessageBadInFilterField(t *testing.T) { fb := MessageQueryFactory.NewFilter(context.Background()) _, err := fb.And(