2016-06-22 00:07:06 +02:00
# include <gtest/gtest.h>
2017-09-16 01:18:58 +02:00
# include "blockstore/implementations/integrity/IntegrityBlockStore2.h"
2017-07-21 04:32:42 +02:00
# include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
2016-06-22 00:07:06 +02:00
# include "blockstore/utils/BlockStoreUtils.h"
# include <cpp-utils/data/DataFixture.h>
2016-06-22 00:56:29 +02:00
# include <cpp-utils/tempfile/TempFile.h>
2017-09-11 15:37:06 +02:00
# include "../../testutils/gtest_printers.h"
2016-06-22 00:07:06 +02:00
using : : testing : : Test ;
using cpputils : : DataFixture ;
using cpputils : : Data ;
using cpputils : : unique_ref ;
using cpputils : : make_unique_ref ;
2016-06-22 00:56:29 +02:00
using cpputils : : TempFile ;
2017-12-03 20:01:41 +01:00
using cpputils : : serialize ;
using cpputils : : deserialize ;
2016-06-23 22:13:00 +02:00
using boost : : none ;
2016-06-26 00:28:16 +02:00
using std : : make_unique ;
using std : : unique_ptr ;
2016-06-22 00:07:06 +02:00
2017-07-21 04:32:42 +02:00
using blockstore : : inmemory : : InMemoryBlockStore2 ;
2016-06-22 00:07:06 +02:00
2017-09-16 01:18:58 +02:00
using namespace blockstore : : integrity ;
2016-06-22 00:07:06 +02:00
2017-09-16 01:18:58 +02:00
class IntegrityBlockStoreTest : public Test {
2016-06-22 00:07:06 +02:00
public :
static constexpr unsigned int BLOCKSIZE = 1024 ;
2017-09-16 01:18:58 +02:00
IntegrityBlockStoreTest ( ) :
2016-06-22 00:56:29 +02:00
stateFile ( false ) ,
2017-07-21 04:32:42 +02:00
baseBlockStore ( new InMemoryBlockStore2 ) ,
2017-09-16 17:49:33 +02:00
blockStore ( make_unique_ref < IntegrityBlockStore2 > ( std : : move ( cpputils : : nullcheck ( std : : unique_ptr < InMemoryBlockStore2 > ( baseBlockStore ) ) . value ( ) ) , stateFile . path ( ) , myClientId , false , false ) ) ,
2016-06-22 00:07:06 +02:00
data ( DataFixture : : generate ( BLOCKSIZE ) ) {
}
2016-06-26 23:35:52 +02:00
static constexpr uint32_t myClientId = 0x12345678 ;
2016-06-22 00:56:29 +02:00
TempFile stateFile ;
2017-07-21 04:32:42 +02:00
InMemoryBlockStore2 * baseBlockStore ;
2017-09-16 01:18:58 +02:00
unique_ref < IntegrityBlockStore2 > blockStore ;
2016-06-22 00:07:06 +02:00
Data data ;
2017-09-16 01:18:58 +02:00
std : : pair < InMemoryBlockStore2 * , unique_ptr < IntegrityBlockStore2 > > makeBlockStoreWithDeletionPrevention ( ) {
2017-07-21 04:32:42 +02:00
InMemoryBlockStore2 * baseBlockStore = new InMemoryBlockStore2 ;
2017-09-16 17:49:33 +02:00
auto blockStore = make_unique < IntegrityBlockStore2 > ( std : : move ( cpputils : : nullcheck ( std : : unique_ptr < InMemoryBlockStore2 > ( baseBlockStore ) ) . value ( ) ) , stateFile . path ( ) , myClientId , false , true ) ;
2016-06-26 00:28:16 +02:00
return std : : make_pair ( baseBlockStore , std : : move ( blockStore ) ) ;
}
2017-09-16 01:18:58 +02:00
std : : pair < InMemoryBlockStore2 * , unique_ptr < IntegrityBlockStore2 > > makeBlockStoreWithoutDeletionPrevention ( ) {
2017-07-21 04:32:42 +02:00
InMemoryBlockStore2 * baseBlockStore = new InMemoryBlockStore2 ;
2017-09-16 17:49:33 +02:00
auto blockStore = make_unique < IntegrityBlockStore2 > ( std : : move ( cpputils : : nullcheck ( std : : unique_ptr < InMemoryBlockStore2 > ( baseBlockStore ) ) . value ( ) ) , stateFile . path ( ) , myClientId , false , false ) ;
2016-06-26 00:28:16 +02:00
return std : : make_pair ( baseBlockStore , std : : move ( blockStore ) ) ;
}
2017-09-17 03:07:27 +02:00
blockstore : : BlockId CreateBlockReturnKey ( ) {
2016-06-22 00:07:06 +02:00
return CreateBlockReturnKey ( data ) ;
}
2017-09-17 03:07:27 +02:00
blockstore : : BlockId CreateBlockReturnKey ( const Data & initData ) {
2017-07-21 04:32:42 +02:00
return blockStore - > create ( initData . copy ( ) ) ;
2016-06-22 00:07:06 +02:00
}
2017-09-17 03:07:27 +02:00
Data loadBaseBlock ( const blockstore : : BlockId & blockId ) {
return baseBlockStore - > load ( blockId ) . value ( ) ;
2016-06-22 00:07:06 +02:00
}
2017-09-17 03:07:27 +02:00
Data loadBlock ( const blockstore : : BlockId & blockId ) {
return blockStore - > load ( blockId ) . value ( ) ;
2016-06-23 01:27:35 +02:00
}
2017-09-17 03:07:27 +02:00
void modifyBlock ( const blockstore : : BlockId & blockId ) {
auto block = blockStore - > load ( blockId ) . value ( ) ;
2018-02-18 03:54:22 +01:00
CryptoPP : : byte * first_byte = static_cast < CryptoPP : : byte * > ( block . data ( ) ) ;
2017-07-21 04:32:42 +02:00
* first_byte = * first_byte + 1 ;
2017-09-17 03:07:27 +02:00
blockStore - > store ( blockId , block ) ;
2016-06-22 00:07:06 +02:00
}
2017-09-17 03:07:27 +02:00
void rollbackBaseBlock ( const blockstore : : BlockId & blockId , const Data & data ) {
baseBlockStore - > store ( blockId , data ) ;
2016-06-22 00:07:06 +02:00
}
2017-09-17 03:07:27 +02:00
void decreaseVersionNumber ( const blockstore : : BlockId & blockId ) {
auto baseBlock = baseBlockStore - > load ( blockId ) . value ( ) ;
2017-12-03 20:01:41 +01:00
void * versionPtr = static_cast < uint8_t * > ( baseBlock . data ( ) ) + IntegrityBlockStore2 : : VERSION_HEADER_OFFSET ;
uint64_t version = deserialize < uint64_t > ( versionPtr ) ;
ASSERT ( version > 1 , " Can't decrease the lowest allowed version number " ) ;
serialize < uint64_t > ( versionPtr , version - 1 ) ;
2017-09-17 03:07:27 +02:00
baseBlockStore - > store ( blockId , baseBlock ) ;
2016-06-23 01:27:35 +02:00
}
2017-09-17 03:07:27 +02:00
void increaseVersionNumber ( const blockstore : : BlockId & blockId ) {
auto baseBlock = baseBlockStore - > load ( blockId ) . value ( ) ;
2017-12-03 20:01:41 +01:00
void * versionPtr = static_cast < uint8_t * > ( baseBlock . data ( ) ) + IntegrityBlockStore2 : : VERSION_HEADER_OFFSET ;
uint64_t version = deserialize < uint64_t > ( versionPtr ) ;
serialize < uint64_t > ( versionPtr , version + 1 ) ;
2017-09-17 03:07:27 +02:00
baseBlockStore - > store ( blockId , baseBlock ) ;
2016-06-25 23:53:29 +02:00
}
2017-09-17 03:07:27 +02:00
void changeClientId ( const blockstore : : BlockId & blockId ) {
auto baseBlock = baseBlockStore - > load ( blockId ) . value ( ) ;
2017-12-03 20:01:41 +01:00
void * clientIdPtr = static_cast < uint8_t * > ( baseBlock . data ( ) ) + IntegrityBlockStore2 : : CLIENTID_HEADER_OFFSET ;
uint64_t clientId = deserialize < uint64_t > ( clientIdPtr ) ;
serialize < uint64_t > ( clientIdPtr , clientId + 1 ) ;
2017-09-17 03:07:27 +02:00
baseBlockStore - > store ( blockId , baseBlock ) ;
2016-06-23 01:27:35 +02:00
}
2017-09-17 03:07:27 +02:00
void deleteBlock ( const blockstore : : BlockId & blockId ) {
blockStore - > remove ( blockId ) ;
2016-06-23 22:13:00 +02:00
}
2017-09-17 03:07:27 +02:00
void insertBaseBlock ( const blockstore : : BlockId & blockId , Data data ) {
2017-12-01 16:01:49 +01:00
EXPECT_TRUE ( baseBlockStore - > tryCreate ( blockId , data ) ) ;
2016-06-23 22:13:00 +02:00
}
2016-06-22 00:07:06 +02:00
private :
2017-09-16 01:18:58 +02:00
DISALLOW_COPY_AND_ASSIGN ( IntegrityBlockStoreTest ) ;
2016-06-22 00:07:06 +02:00
} ;
2017-09-16 01:18:58 +02:00
constexpr uint32_t IntegrityBlockStoreTest : : myClientId ;
2016-06-26 23:35:52 +02:00
2016-06-23 01:27:35 +02:00
// Test that a decreasing version number is not allowed
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_1 ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( ) ;
Data oldBaseBlock = loadBaseBlock ( blockId ) ;
modifyBlock ( blockId ) ;
rollbackBaseBlock ( blockId , oldBaseBlock ) ;
2016-06-26 00:15:56 +02:00
EXPECT_THROW (
2017-09-17 03:07:27 +02:00
blockStore - > load ( blockId ) ,
2016-06-26 00:15:56 +02:00
IntegrityViolationError
) ;
2016-06-22 00:07:06 +02:00
}
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_2 ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( ) ;
2016-06-23 01:27:35 +02:00
// Increase the version number
2017-09-17 03:07:27 +02:00
modifyBlock ( blockId ) ;
2016-06-23 01:27:35 +02:00
// Decrease the version number again
2017-09-17 03:07:27 +02:00
decreaseVersionNumber ( blockId ) ;
2016-06-26 00:15:56 +02:00
EXPECT_THROW (
2017-09-17 03:07:27 +02:00
blockStore - > load ( blockId ) ,
2016-06-26 00:15:56 +02:00
IntegrityViolationError
) ;
2016-06-23 01:27:35 +02:00
}
// Test that a different client doesn't need to have a higher version number (i.e. version numbers are per client).
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , RollbackPrevention_DoesAllowDecreasingVersionNumberForDifferentClient ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( ) ;
2016-06-23 01:27:35 +02:00
// Increase the version number
2017-09-17 03:07:27 +02:00
modifyBlock ( blockId ) ;
2016-06-23 01:27:35 +02:00
// Fake a modification by a different client with lower version numbers
2017-09-17 03:07:27 +02:00
changeClientId ( blockId ) ;
decreaseVersionNumber ( blockId ) ;
EXPECT_NE ( boost : : none , blockStore - > load ( blockId ) ) ;
2016-06-23 01:27:35 +02:00
}
// Test that it doesn't allow a rollback to the "newest" block of a client, when this block was superseded by a version of a different client
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , RollbackPrevention_DoesntAllowSameVersionNumberForOldClient ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( ) ;
2016-06-23 01:27:35 +02:00
// Increase the version number
2017-09-17 03:07:27 +02:00
modifyBlock ( blockId ) ;
Data oldBaseBlock = loadBaseBlock ( blockId ) ;
2016-06-23 01:27:35 +02:00
// Fake a modification by a different client with lower version numbers
2017-09-17 03:07:27 +02:00
changeClientId ( blockId ) ;
loadBlock ( blockId ) ; // make the block store know about this other client's modification
2016-06-23 01:27:35 +02:00
// Rollback to old client
2017-09-17 03:07:27 +02:00
rollbackBaseBlock ( blockId , oldBaseBlock ) ;
2016-06-26 00:15:56 +02:00
EXPECT_THROW (
2017-09-17 03:07:27 +02:00
blockStore - > load ( blockId ) ,
2016-06-26 00:15:56 +02:00
IntegrityViolationError
) ;
2016-06-23 01:27:35 +02:00
}
2016-06-23 22:13:00 +02:00
// Test that deleted blocks cannot be re-introduced
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , RollbackPrevention_DoesntAllowReintroducingDeletedBlocks ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( ) ;
Data oldBaseBlock = loadBaseBlock ( blockId ) ;
deleteBlock ( blockId ) ;
insertBaseBlock ( blockId , std : : move ( oldBaseBlock ) ) ;
2016-06-26 00:15:56 +02:00
EXPECT_THROW (
2017-09-17 03:07:27 +02:00
blockStore - > load ( blockId ) ,
2016-06-26 00:15:56 +02:00
IntegrityViolationError
) ;
2016-06-23 22:13:00 +02:00
}
2016-06-25 23:53:29 +02:00
// This can happen if a client synchronization is delayed. Another client might have won the conflict and pushed a new version for the deleted block.
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( ) ;
Data oldBaseBlock = loadBaseBlock ( blockId ) ;
deleteBlock ( blockId ) ;
insertBaseBlock ( blockId , std : : move ( oldBaseBlock ) ) ;
increaseVersionNumber ( blockId ) ;
EXPECT_NE ( boost : : none , blockStore - > load ( blockId ) ) ;
2016-06-25 23:53:29 +02:00
}
2016-06-26 00:28:16 +02:00
// Check that in a multi-client scenario, missing blocks are not integrity errors, because another client might have deleted them.
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , DeletionPrevention_AllowsDeletingBlocksWhenDeactivated ) {
2017-07-21 04:32:42 +02:00
InMemoryBlockStore2 * baseBlockStore ;
2017-09-16 01:18:58 +02:00
unique_ptr < IntegrityBlockStore2 > blockStore ;
2016-06-26 00:28:16 +02:00
std : : tie ( baseBlockStore , blockStore ) = makeBlockStoreWithoutDeletionPrevention ( ) ;
2017-09-17 03:07:27 +02:00
auto blockId = blockStore - > create ( Data ( 0 ) ) ;
baseBlockStore - > remove ( blockId ) ;
EXPECT_EQ ( boost : : none , blockStore - > load ( blockId ) ) ;
2016-06-26 00:28:16 +02:00
}
// Check that in a single-client scenario, missing blocks are integrity errors.
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , DeletionPrevention_DoesntAllowDeletingBlocksWhenActivated ) {
2017-07-21 04:32:42 +02:00
InMemoryBlockStore2 * baseBlockStore ;
2017-09-16 01:18:58 +02:00
unique_ptr < IntegrityBlockStore2 > blockStore ;
2016-06-26 00:28:16 +02:00
std : : tie ( baseBlockStore , blockStore ) = makeBlockStoreWithDeletionPrevention ( ) ;
2017-09-17 03:07:27 +02:00
auto blockId = blockStore - > create ( Data ( 0 ) ) ;
baseBlockStore - > remove ( blockId ) ;
2016-06-26 00:28:16 +02:00
EXPECT_THROW (
2017-09-17 03:07:27 +02:00
blockStore - > load ( blockId ) ,
2016-06-26 00:28:16 +02:00
IntegrityViolationError
) ;
}
2016-06-26 01:36:41 +02:00
// Check that in a multi-client scenario, missing blocks are not integrity errors, because another client might have deleted them.
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenDeactivated ) {
2017-07-21 04:32:42 +02:00
InMemoryBlockStore2 * baseBlockStore ;
2017-09-16 01:18:58 +02:00
unique_ptr < IntegrityBlockStore2 > blockStore ;
2016-06-26 01:36:41 +02:00
std : : tie ( baseBlockStore , blockStore ) = makeBlockStoreWithoutDeletionPrevention ( ) ;
2017-09-17 03:07:27 +02:00
auto blockId = blockStore - > create ( Data ( 0 ) ) ;
baseBlockStore - > remove ( blockId ) ;
2016-06-26 01:36:41 +02:00
int count = 0 ;
2017-09-17 03:07:27 +02:00
blockStore - > forEachBlock ( [ & count ] ( const blockstore : : BlockId & ) {
2016-06-26 01:36:41 +02:00
+ + count ;
} ) ;
EXPECT_EQ ( 0 , count ) ;
}
// Check that in a single-client scenario, missing blocks are integrity errors.
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , DeletionPrevention_InForEachBlock_DoesntAllowDeletingBlocksWhenActivated ) {
2017-07-21 04:32:42 +02:00
InMemoryBlockStore2 * baseBlockStore ;
2017-09-16 01:18:58 +02:00
unique_ptr < IntegrityBlockStore2 > blockStore ;
2016-06-26 01:36:41 +02:00
std : : tie ( baseBlockStore , blockStore ) = makeBlockStoreWithDeletionPrevention ( ) ;
2017-09-17 03:07:27 +02:00
auto blockId = blockStore - > create ( Data ( 0 ) ) ;
baseBlockStore - > remove ( blockId ) ;
2016-06-26 01:36:41 +02:00
EXPECT_THROW (
2017-09-17 03:07:27 +02:00
blockStore - > forEachBlock ( [ ] ( const blockstore : : BlockId & ) { } ) ,
2016-06-26 01:36:41 +02:00
IntegrityViolationError
) ;
}
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , LoadingWithDifferentBlockIdFails ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( ) ;
blockstore : : BlockId key2 = blockstore : : BlockId : : FromString ( " 1491BB4932A389EE14BC7090AC772972 " ) ;
baseBlockStore - > store ( key2 , baseBlockStore - > load ( blockId ) . value ( ) ) ;
2017-09-16 01:09:15 +02:00
EXPECT_THROW (
blockStore - > load ( key2 ) ,
IntegrityViolationError
) ;
}
2016-06-25 23:53:29 +02:00
// TODO Test more integrity cases:
// - RollbackPrevention_DoesntAllowReintroducingDeletedBlocks with different client id (i.e. trying to re-introduce the newest block of a different client)
// - RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber with different client id
2016-06-26 00:28:16 +02:00
// - Think about more...
2017-09-16 17:18:04 +02:00
// TODO Test that disabling integrity checks allows all these cases
2016-06-25 23:53:29 +02:00
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , PhysicalBlockSize_zerophysical ) {
2016-06-22 00:07:06 +02:00
EXPECT_EQ ( 0u , blockStore - > blockSizeFromPhysicalBlockSize ( 0 ) ) ;
}
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , PhysicalBlockSize_zerovirtual ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( Data ( 0 ) ) ;
auto base = baseBlockStore - > load ( blockId ) . value ( ) ;
2017-07-21 04:32:42 +02:00
EXPECT_EQ ( 0u , blockStore - > blockSizeFromPhysicalBlockSize ( base . size ( ) ) ) ;
2016-06-22 00:07:06 +02:00
}
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , PhysicalBlockSize_negativeboundaries ) {
2016-06-22 00:07:06 +02:00
// This tests that a potential if/else in blockSizeFromPhysicalBlockSize that catches negative values has the
// correct boundary set. We test the highest value that is negative and the smallest value that is positive.
2017-07-21 04:32:42 +02:00
auto physicalSizeForVirtualSizeZero = baseBlockStore - > load ( CreateBlockReturnKey ( Data ( 0 ) ) ) . value ( ) . size ( ) ;
2016-06-22 00:07:06 +02:00
if ( physicalSizeForVirtualSizeZero > 0 ) {
EXPECT_EQ ( 0u , blockStore - > blockSizeFromPhysicalBlockSize ( physicalSizeForVirtualSizeZero - 1 ) ) ;
}
EXPECT_EQ ( 0u , blockStore - > blockSizeFromPhysicalBlockSize ( physicalSizeForVirtualSizeZero ) ) ;
EXPECT_EQ ( 1u , blockStore - > blockSizeFromPhysicalBlockSize ( physicalSizeForVirtualSizeZero + 1 ) ) ;
}
2017-09-16 01:18:58 +02:00
TEST_F ( IntegrityBlockStoreTest , PhysicalBlockSize_positive ) {
2017-09-17 03:07:27 +02:00
auto blockId = CreateBlockReturnKey ( Data ( 10 * 1024 ) ) ;
auto base = baseBlockStore - > load ( blockId ) . value ( ) ;
2017-07-21 04:32:42 +02:00
EXPECT_EQ ( 10 * 1024u , blockStore - > blockSizeFromPhysicalBlockSize ( base . size ( ) ) ) ;
2016-06-22 00:07:06 +02:00
}