Skip to content

Commit

Permalink
feat: add quote hash validation inside repository
Browse files Browse the repository at this point in the history
  • Loading branch information
Luisfc68 committed Dec 5, 2024
1 parent 04d1a25 commit 9829126
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 21 deletions.
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

0 comments on commit 9829126

Please sign in to comment.