Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add quote hash validation inside repository #586

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions internal/adapters/dataproviders/database/mongo/pegin.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func (repo *peginMongoRepository) GetQuote(ctx context.Context, hash string) (*q
dbCtx, cancel := context.WithTimeout(ctx, dbTimeout)
defer cancel()

if err := quote.ValidateQuoteHash(hash); err != nil {
return nil, err
}

collection := repo.conn.Collection(PeginQuoteCollection)
filter := bson.D{primitive.E{Key: "hash", Value: hash}}

Expand All @@ -69,6 +73,10 @@ func (repo *peginMongoRepository) GetRetainedQuote(ctx context.Context, hash str
dbCtx, cancel := context.WithTimeout(ctx, dbTimeout)
defer cancel()

if err := quote.ValidateQuoteHash(hash); err != nil {
return nil, err
}

collection := repo.conn.Collection(RetainedPeginQuoteCollection)
filter := bson.D{primitive.E{Key: "quote_hash", Value: hash}}

Expand Down
30 changes: 22 additions & 8 deletions internal/adapters/dataproviders/database/mongo/pegin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ func TestPeginMongoRepository_GetQuote(t *testing.T) {
t.Run("Get pegin quote successfully", func(t *testing.T) {
const expectedLog = "READ interaction with db: {FedBtcAddress:3LxPz39femVBL278mTiBvgzBNMVFqXssoH LbcAddress:0xAA9cAf1e3967600578727F975F283446A3Da6612 LpRskAddress:0x4202bac9919c3412fc7c8be4e678e26279386603 BtcRefundAddress:171gGjg8NeLUonNSrFmgwkgT1jgqzXR6QX RskRefundAddress:0xaD0DE1962ab903E06C725A1b343b7E8950a0Ff82 LpBtcAddress:17kksixYkbHeLy9okV16kr4eAxVhFkRhP CallFee:100000000000000 PenaltyFee:10000000000000 ContractAddress:0xaD0DE1962ab903E06C725A1b343b7E8950a0Ff82 Data:010203 GasLimit:21000 Nonce:8373381263192041574 Value:8000000000000000 AgreementTimestamp:1727298699 TimeForDeposit:3600 LpCallTime:7200 Confirmations:2 CallOnRegister:true GasFee:1341211956000 ProductFeeAmount:1}"
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "hash", Value: test.AnyString}}).
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "hash", Value: test.AnyHash}}).
Return(mongoDb.NewSingleResultFromDocument(mongo.StoredPeginQuote{
PeginQuote: testPeginQuote,
Hash: test.AnyString,
}, nil, nil)).Once()
defer assertDbInteractionLog(t, expectedLog)()
result, err := repo.GetQuote(context.Background(), test.AnyString)
result, err := repo.GetQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Equal(t, testPeginQuote, *result)
Expand All @@ -99,7 +99,7 @@ func TestPeginMongoRepository_GetQuote(t *testing.T) {
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(mongo.StoredPeginQuote{}, assert.AnError, nil)).Once()
result, err := repo.GetQuote(context.Background(), test.AnyString)
result, err := repo.GetQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.Error(t, err)
assert.Nil(t, result)
Expand All @@ -108,11 +108,18 @@ func TestPeginMongoRepository_GetQuote(t *testing.T) {
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(mongo.StoredPeginQuote{}, mongoDb.ErrNoDocuments, nil)).Once()
result, err := repo.GetQuote(context.Background(), test.AnyString)
result, err := repo.GetQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Nil(t, result)
})
t.Run("Fail on invalid pegin quote hash", func(t *testing.T) {
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
result, err := repo.GetQuote(context.Background(), test.AnyString)
collection.AssertNotCalled(t, "FindOne")
require.Error(t, err)
assert.Nil(t, result)
})
}

func TestPeginMongoRepository_GetRetainedQuote(t *testing.T) {
Expand All @@ -121,10 +128,10 @@ func TestPeginMongoRepository_GetRetainedQuote(t *testing.T) {
t.Run("Get retained pegin quote successfully", func(t *testing.T) {
const expectedLog = "READ interaction with db: {QuoteHash:8d1ba2cb559a6ebe41f19131602467e1d939682d651b2a91e55b86bc664a6819 DepositAddress:2N7Vw5f59V3o3bDcaJK5oA829LFTBYZHLoG Signature:b24831aac7230910087d9818b378a31679be5e3991a7227cc160bc3add09e1645a26e9c740e3467f53953d7ec086c82bf8ef0eb03c118d0382ee6049a8f0119f1c RequiredLiquidity:100 State:CallForUserSucceeded UserBtcTxHash:619c4d69ccaa5f78aaa2284817cf070609ac40af3792916ca3d0ef82b14af75f CallForUserTxHash:0x2c73de184c80797c04a655217d121588e8d5c228d3e0cc26187cb249123aa7c3 RegisterPeginTxHash:0x3a0feaef4d803468ba5bfc1db78f4d2568de1b7cf002dec5991c469e6719db89}"
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "quote_hash", Value: test.AnyString}}).
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "quote_hash", Value: test.AnyHash}}).
Return(mongoDb.NewSingleResultFromDocument(testRetainedPeginQuote, nil, nil)).Once()
defer assertDbInteractionLog(t, expectedLog)()
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
result, err := repo.GetRetainedQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Equal(t, testRetainedPeginQuote, *result)
Expand All @@ -133,7 +140,7 @@ func TestPeginMongoRepository_GetRetainedQuote(t *testing.T) {
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(quote.RetainedPeginQuote{}, assert.AnError, nil)).Once()
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
result, err := repo.GetRetainedQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.Error(t, err)
assert.Nil(t, result)
Expand All @@ -142,11 +149,18 @@ func TestPeginMongoRepository_GetRetainedQuote(t *testing.T) {
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(quote.RetainedPeginQuote{}, mongoDb.ErrNoDocuments, nil)).Once()
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
result, err := repo.GetRetainedQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Nil(t, result)
})
t.Run("Fail on invalid pegin quote hash", func(t *testing.T) {
repo := mongo.NewPeginMongoRepository(mongo.NewConnection(client))
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
collection.AssertNotCalled(t, "FindOne")
require.Error(t, err)
assert.Nil(t, result)
})
}

func TestPeginMongoRepository_InsertRetainedQuote(t *testing.T) {
Expand Down
12 changes: 11 additions & 1 deletion internal/adapters/dataproviders/database/mongo/pegout.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"regexp"
)

const (
Expand Down Expand Up @@ -53,6 +54,10 @@ func (repo *pegoutMongoRepository) GetQuote(ctx context.Context, hash string) (*
dbCtx, cancel := context.WithTimeout(ctx, dbTimeout)
defer cancel()

if err := quote.ValidateQuoteHash(hash); err != nil {
return nil, err
}

collection := repo.conn.Collection(PegoutQuoteCollection)
filter := bson.D{primitive.E{Key: "hash", Value: hash}}

Expand All @@ -71,6 +76,10 @@ func (repo *pegoutMongoRepository) GetRetainedQuote(ctx context.Context, hash st
dbCtx, cancel := context.WithTimeout(ctx, dbTimeout)
defer cancel()

if err := quote.ValidateQuoteHash(hash); err != nil {
return nil, err
}

collection := repo.conn.Collection(RetainedPegoutQuoteCollection)
filter := bson.D{primitive.E{Key: "quote_hash", Value: hash}}

Expand Down Expand Up @@ -101,7 +110,8 @@ func (repo *pegoutMongoRepository) ListPegoutDepositsByAddress(ctx context.Conte
dbCtx, cancel := context.WithTimeout(ctx, dbTimeout)
defer cancel()

filter := bson.M{"from": bson.M{"$regex": address, "$options": "i"}}
sanitizedAddress := regexp.QuoteMeta(address)
filter := bson.M{"from": bson.M{"$regex": sanitizedAddress, "$options": "i"}}
sort := options.Find().SetSort(bson.M{"timestamp": -1})
cursor, err := repo.conn.Collection(DepositEventsCollection).Find(dbCtx, filter, sort)
if err != nil {
Expand Down
41 changes: 33 additions & 8 deletions internal/adapters/dataproviders/database/mongo/pegout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ func TestPegoutMongoRepository_GetQuote(t *testing.T) {
t.Run("Get pegout quote successfully", func(t *testing.T) {
const expectedLog = "READ interaction with db: {LbcAddress:0xc2A630c053D12D63d32b025082f6Ba268db18300 LpRskAddress:0x7c4890a0f1d4bbf2c669ac2d1effa185c505359b BtcRefundAddress:n2Ge4xMVQKp5Hzzf8xTBJBLppRgjRZYYyq RskRefundAddress:0x79568C2989232dcA1840087d73d403602364c0D4 LpBtcAddress:mvL2bVzGUeC9oqVyQWJ4PxQspFzKgjzAqe CallFee:100000000000000 PenaltyFee:10000000000000 Nonce:6410832321595034747 DepositAddress:n2Ge4xMVQKp5Hzzf8xTBJBLppRgjRZYYyq Value:5000000000000000 AgreementTimestamp:1721944367 DepositDateLimit:1721951567 DepositConfirmations:4 TransferConfirmations:2 TransferTime:7200 ExpireDate:1721958767 ExpireBlock:5366409 GasFee:4170000000000 ProductFeeAmount:13}"
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "hash", Value: test.AnyString}}).
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "hash", Value: test.AnyHash}}).
Return(mongoDb.NewSingleResultFromDocument(mongo.StoredPegoutQuote{
PegoutQuote: testPegoutQuote,
Hash: test.AnyString,
}, nil, nil)).Once()
defer assertDbInteractionLog(t, expectedLog)()
result, err := repo.GetQuote(context.Background(), test.AnyString)
result, err := repo.GetQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Equal(t, testPegoutQuote, *result)
Expand All @@ -111,7 +111,7 @@ func TestPegoutMongoRepository_GetQuote(t *testing.T) {
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(mongo.StoredPegoutQuote{}, assert.AnError, nil)).Once()
result, err := repo.GetQuote(context.Background(), test.AnyString)
result, err := repo.GetQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.Error(t, err)
assert.Nil(t, result)
Expand All @@ -120,11 +120,18 @@ func TestPegoutMongoRepository_GetQuote(t *testing.T) {
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(mongo.StoredPegoutQuote{}, mongoDb.ErrNoDocuments, nil)).Once()
result, err := repo.GetQuote(context.Background(), test.AnyString)
result, err := repo.GetQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Nil(t, result)
})
t.Run("Fail on invalid pegout quote hash", func(t *testing.T) {
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
result, err := repo.GetQuote(context.Background(), test.AnyString)
collection.AssertNotCalled(t, "FindOne")
require.Error(t, err)
assert.Nil(t, result)
})
}

func TestPegoutMongoRepository_GetRetainedQuote(t *testing.T) {
Expand All @@ -133,10 +140,10 @@ func TestPegoutMongoRepository_GetRetainedQuote(t *testing.T) {
t.Run("Get retained pegout quote successfully", func(t *testing.T) {
const expectedLog = "READ interaction with db: {QuoteHash:27d70ec2bc2c3154dc9a5b53b118a755441b22bc1c8ccde967ed33609970c25f DepositAddress:mkE1WWdiu5VgjfugomDk8GxV6JdEEEJR9s Signature:5c9eab91c753355f87c19d09ea88b2fd02773981e513bc2821fed5ceba0d452a0a3d21e2252cb35348ce5c6803117e3abb62837beb8f5866a375ce66587d004b1c RequiredLiquidity:55 State:WaitingForDepositConfirmations UserRskTxHash:0x6b2e1e4daf8cf00c5c3534b72cdeec3526e8a38f70c11e44888b6e4ae1ee7d38 LpBtcTxHash:6ac3779dc33ad52f3409cbb909bcd458745995496a2a3954406206f6e5d4cb0e RefundPegoutTxHash:0x8e773a2826e73f8e5792304379a7e46dff38f17089c6d344335e03537b31c2bc BridgeRefundTxHash:0x4f3f6f0664a732e4c907971e75c1e3fd8671461dcb53f566660432fc47255d8b}"
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "quote_hash", Value: test.AnyString}}).
collection.On("FindOne", mock.Anything, bson.D{primitive.E{Key: "quote_hash", Value: test.AnyHash}}).
Return(mongoDb.NewSingleResultFromDocument(testRetainedPegoutQuote, nil, nil)).Once()
defer assertDbInteractionLog(t, expectedLog)()
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
result, err := repo.GetRetainedQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Equal(t, testRetainedPegoutQuote, *result)
Expand All @@ -145,7 +152,7 @@ func TestPegoutMongoRepository_GetRetainedQuote(t *testing.T) {
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(quote.RetainedPegoutQuote{}, assert.AnError, nil)).Once()
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
result, err := repo.GetRetainedQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.Error(t, err)
assert.Nil(t, result)
Expand All @@ -154,11 +161,18 @@ func TestPegoutMongoRepository_GetRetainedQuote(t *testing.T) {
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
collection.On("FindOne", mock.Anything, mock.Anything).
Return(mongoDb.NewSingleResultFromDocument(quote.RetainedPegoutQuote{}, mongoDb.ErrNoDocuments, nil)).Once()
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
result, err := repo.GetRetainedQuote(context.Background(), test.AnyHash)
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Nil(t, result)
})
t.Run("Fail on invalid pegout quote hash", func(t *testing.T) {
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
result, err := repo.GetRetainedQuote(context.Background(), test.AnyString)
collection.AssertNotCalled(t, "FindOne")
require.Error(t, err)
assert.Nil(t, result)
})
}

func TestPegoutMongoRepository_InsertRetainedQuote(t *testing.T) {
Expand Down Expand Up @@ -360,6 +374,17 @@ func TestPegoutMongoRepository_ListPegoutDepositsByAddress(t *testing.T) {
require.Error(t, err)
assert.Empty(t, result)
})
t.Run("Should sanitize address properly", func(t *testing.T) {
repo := mongo.NewPegoutMongoRepository(mongo.NewConnection(client))
collection.On("Find", mock.Anything,
bson.M{"from": bson.M{"$regex": "0x1234567890abcdef1234567890abcdef12345678\\(a\\+\\)\\+", "$options": "i"}},
options.Find().SetSort(bson.M{"timestamp": -1}),
).Return(mongoDb.NewCursorFromDocuments([]any{testPegoutDeposit}, nil, nil)).Once()
result, err := repo.ListPegoutDepositsByAddress(context.Background(), "0x1234567890abcdef1234567890abcdef12345678(a+)+")
collection.AssertExpectations(t)
require.NoError(t, err)
assert.Equal(t, []quote.PegoutDeposit{testPegoutDeposit}[0], result[0])
})
}

func TestPegoutMongoRepository_UpsertPegoutDeposit(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ func TestUniqueSessionStore_New(t *testing.T) {
store := cookies.NewUniqueSessionStore(cookieName, k1, k2)
t.Run("should return an error if the session name is different", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
session, err := store.New(req, test.AnyHash)
session, err := store.New(req, test.AnyString)
assertDummySession(t, session)
require.ErrorContains(t, err, "UniqueSessionStore is expecting cookie session name and received any hash")
require.ErrorContains(t, err, "UniqueSessionStore is expecting cookie session name and received any value")
})
t.Run("should return an new session the first time", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func TestPeginDepositAddressWatcher_Start_BlockchainCheck(t *testing.T) {
expiredQuote := quote.PeginQuote{Nonce: 6, AgreementTimestamp: 1}
t.Run("should handle error when expiring quotes", func(t *testing.T) {
resetMocks()
checkFunction := test.AssertLogContains(t, "Error updating expired quote (any hash)")
checkFunction := test.AssertLogContains(t, "Error updating expired quote (d8f5d705f146230553a8aec9a290a19bf4311187fa0489d41207d7215b0b65cb)")
peginRepository.EXPECT().UpdateRetainedQuote(mock.Anything, mock.Anything).Return(assert.AnError).Once()
btcRpc.On("GetHeight").Return(big.NewInt(9), nil).Once()
btcWallet.On("ImportAddress", test.AnyAddress).Return(nil).Once()
Expand Down
2 changes: 1 addition & 1 deletion test/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const (
AnyRskAddress = "0x79568c2989232dCa1840087D73d403602364c0D4"
AnyBtcAddress = "mvL2bVzGUeC9oqVyQWJ4PxQspFzKgjzAqe"
AnyString = "any value"
AnyHash = "any hash"
AnyHash = "d8f5d705f146230553a8aec9a290a19bf4311187fa0489d41207d7215b0b65cb"
AnyUrl = "url.com"
keyPath = "../../docker-compose/localstack/local-key.json"
KeyPassword = "test"
Expand Down
Loading