Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F10907584
dbwrapper.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Subscribers
None
dbwrapper.cpp
View Options
// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
<dbwrapper.h>
#include
<fs.h>
#include
<random.h>
#include
<util/system.h>
#include
<leveldb/cache.h>
#include
<leveldb/env.h>
#include
<leveldb/filter_policy.h>
#include
<memenv.h>
#include
<algorithm>
#include
<cstdint>
#include
<memory>
class
CBitcoinLevelDBLogger
:
public
leveldb
::
Logger
{
public
:
// This code is adapted from posix_logger.h, which is why it is using
// vsprintf.
// Please do not do this in normal code
void
Logv
(
const
char
*
format
,
va_list
ap
)
override
{
if
(
!
LogAcceptCategory
(
BCLog
::
LEVELDB
))
{
return
;
}
char
buffer
[
500
];
for
(
int
iter
=
0
;
iter
<
2
;
iter
++
)
{
char
*
base
;
int
bufsize
;
if
(
iter
==
0
)
{
bufsize
=
sizeof
(
buffer
);
base
=
buffer
;
}
else
{
bufsize
=
30000
;
base
=
new
char
[
bufsize
];
}
char
*
p
=
base
;
char
*
limit
=
base
+
bufsize
;
// Print the message
if
(
p
<
limit
)
{
va_list
backup_ap
;
va_copy
(
backup_ap
,
ap
);
// Do not use vsnprintf elsewhere in bitcoin source code, see
// above.
p
+=
vsnprintf
(
p
,
limit
-
p
,
format
,
backup_ap
);
va_end
(
backup_ap
);
}
// Truncate to available space if necessary
if
(
p
>=
limit
)
{
if
(
iter
==
0
)
{
continue
;
// Try again with larger buffer
}
else
{
p
=
limit
-
1
;
}
}
// Add newline if necessary
if
(
p
==
base
||
p
[
-1
]
!=
'\n'
)
{
*
p
++
=
'\n'
;
}
assert
(
p
<=
limit
);
base
[
std
::
min
(
bufsize
-
1
,
(
int
)(
p
-
base
))]
=
'\0'
;
LogPrintfToBeContinued
(
"leveldb: %s"
,
base
);
if
(
base
!=
buffer
)
{
delete
[]
base
;
}
break
;
}
}
};
static
void
SetMaxOpenFiles
(
leveldb
::
Options
*
options
)
{
// On most platforms the default setting of max_open_files (which is 1000)
// is optimal. On Windows using a large file count is OK because the handles
// do not interfere with select() loops. On 64-bit Unix hosts this value is
// also OK, because up to that amount LevelDB will use an mmap
// implementation that does not use extra file descriptors (the fds are
// closed after being mmaped).
//
// Increasing the value beyond the default is dangerous because LevelDB will
// fall back to a non-mmap implementation when the file count is too large.
// On 32-bit Unix host we should decrease the value because the handles use
// up real fds, and we want to avoid fd exhaustion issues.
//
// See PR #12495 for further discussion.
int
default_open_files
=
options
->
max_open_files
;
#ifndef WIN32
if
(
sizeof
(
void
*
)
<
8
)
{
options
->
max_open_files
=
64
;
}
#endif
LogPrint
(
BCLog
::
LEVELDB
,
"LevelDB using max_open_files=%d (default=%d)
\n
"
,
options
->
max_open_files
,
default_open_files
);
}
static
leveldb
::
Options
GetOptions
(
size_t
nCacheSize
)
{
leveldb
::
Options
options
;
options
.
block_cache
=
leveldb
::
NewLRUCache
(
nCacheSize
/
2
);
// up to two write buffers may be held in memory simultaneously
options
.
write_buffer_size
=
nCacheSize
/
4
;
options
.
filter_policy
=
leveldb
::
NewBloomFilterPolicy
(
10
);
options
.
compression
=
leveldb
::
kNoCompression
;
options
.
info_log
=
new
CBitcoinLevelDBLogger
();
if
(
leveldb
::
kMajorVersion
>
1
||
(
leveldb
::
kMajorVersion
==
1
&&
leveldb
::
kMinorVersion
>=
16
))
{
// LevelDB versions before 1.16 consider short writes to be corruption.
// Only trigger error on corruption in later versions.
options
.
paranoid_checks
=
true
;
}
SetMaxOpenFiles
(
&
options
);
return
options
;
}
CDBWrapper
::
CDBWrapper
(
const
fs
::
path
&
path
,
size_t
nCacheSize
,
bool
fMemory
,
bool
fWipe
,
bool
obfuscate
)
:
m_name
(
fs
::
basename
(
path
))
{
penv
=
nullptr
;
readoptions
.
verify_checksums
=
true
;
iteroptions
.
verify_checksums
=
true
;
iteroptions
.
fill_cache
=
false
;
syncoptions
.
sync
=
true
;
options
=
GetOptions
(
nCacheSize
);
options
.
create_if_missing
=
true
;
if
(
fMemory
)
{
penv
=
leveldb
::
NewMemEnv
(
leveldb
::
Env
::
Default
());
options
.
env
=
penv
;
}
else
{
if
(
fWipe
)
{
LogPrintf
(
"Wiping LevelDB in %s
\n
"
,
path
.
string
());
leveldb
::
Status
result
=
leveldb
::
DestroyDB
(
path
.
string
(),
options
);
dbwrapper_private
::
HandleError
(
result
);
}
TryCreateDirectories
(
path
);
LogPrintf
(
"Opening LevelDB in %s
\n
"
,
path
.
string
());
}
leveldb
::
Status
status
=
leveldb
::
DB
::
Open
(
options
,
path
.
string
(),
&
pdb
);
dbwrapper_private
::
HandleError
(
status
);
LogPrintf
(
"Opened LevelDB successfully
\n
"
);
if
(
gArgs
.
GetBoolArg
(
"-forcecompactdb"
,
false
))
{
LogPrintf
(
"Starting database compaction of %s
\n
"
,
path
.
string
());
pdb
->
CompactRange
(
nullptr
,
nullptr
);
LogPrintf
(
"Finished database compaction of %s
\n
"
,
path
.
string
());
}
// The base-case obfuscation key, which is a noop.
obfuscate_key
=
std
::
vector
<
uint8_t
>
(
OBFUSCATE_KEY_NUM_BYTES
,
'\000'
);
bool
key_exists
=
Read
(
OBFUSCATE_KEY_KEY
,
obfuscate_key
);
if
(
!
key_exists
&&
obfuscate
&&
IsEmpty
())
{
// Initialize non-degenerate obfuscation if it won't upset existing,
// non-obfuscated data.
std
::
vector
<
uint8_t
>
new_key
=
CreateObfuscateKey
();
// Write `new_key` so we don't obfuscate the key with itself
Write
(
OBFUSCATE_KEY_KEY
,
new_key
);
obfuscate_key
=
new_key
;
LogPrintf
(
"Wrote new obfuscate key for %s: %s
\n
"
,
path
.
string
(),
HexStr
(
obfuscate_key
));
}
LogPrintf
(
"Using obfuscation key for %s: %s
\n
"
,
path
.
string
(),
HexStr
(
obfuscate_key
));
}
CDBWrapper
::~
CDBWrapper
()
{
delete
pdb
;
pdb
=
nullptr
;
delete
options
.
filter_policy
;
options
.
filter_policy
=
nullptr
;
delete
options
.
info_log
;
options
.
info_log
=
nullptr
;
delete
options
.
block_cache
;
options
.
block_cache
=
nullptr
;
delete
penv
;
options
.
env
=
nullptr
;
}
bool
CDBWrapper
::
WriteBatch
(
CDBBatch
&
batch
,
bool
fSync
)
{
const
bool
log_memory
=
LogAcceptCategory
(
BCLog
::
LEVELDB
);
double
mem_before
=
0
;
if
(
log_memory
)
{
mem_before
=
DynamicMemoryUsage
()
/
1024.0
/
1024
;
}
leveldb
::
Status
status
=
pdb
->
Write
(
fSync
?
syncoptions
:
writeoptions
,
&
batch
.
batch
);
dbwrapper_private
::
HandleError
(
status
);
if
(
log_memory
)
{
double
mem_after
=
DynamicMemoryUsage
()
/
1024.0
/
1024
;
LogPrint
(
BCLog
::
LEVELDB
,
"WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB
\n
"
,
m_name
,
mem_before
,
mem_after
);
}
return
true
;
}
size_t
CDBWrapper
::
DynamicMemoryUsage
()
const
{
std
::
string
memory
;
if
(
!
pdb
->
GetProperty
(
"leveldb.approximate-memory-usage"
,
&
memory
))
{
LogPrint
(
BCLog
::
LEVELDB
,
"Failed to get approximate-memory-usage property
\n
"
);
return
0
;
}
return
stoul
(
memory
);
}
// Prefixed with null character to avoid collisions with other keys
//
// We must use a string constructor which specifies length so that we copy past
// the null-terminator.
const
std
::
string
CDBWrapper
::
OBFUSCATE_KEY_KEY
(
"
\000
obfuscate_key"
,
14
);
const
unsigned
int
CDBWrapper
::
OBFUSCATE_KEY_NUM_BYTES
=
8
;
/**
* Returns a string (consisting of 8 random bytes) suitable for use as an
* obfuscating XOR key.
*/
std
::
vector
<
uint8_t
>
CDBWrapper
::
CreateObfuscateKey
()
const
{
uint8_t
buff
[
OBFUSCATE_KEY_NUM_BYTES
];
GetRandBytes
(
buff
,
OBFUSCATE_KEY_NUM_BYTES
);
return
std
::
vector
<
uint8_t
>
(
&
buff
[
0
],
&
buff
[
OBFUSCATE_KEY_NUM_BYTES
]);
}
bool
CDBWrapper
::
IsEmpty
()
{
std
::
unique_ptr
<
CDBIterator
>
it
(
NewIterator
());
it
->
SeekToFirst
();
return
!
(
it
->
Valid
());
}
CDBIterator
::~
CDBIterator
()
{
delete
piter
;
}
bool
CDBIterator
::
Valid
()
const
{
return
piter
->
Valid
();
}
void
CDBIterator
::
SeekToFirst
()
{
piter
->
SeekToFirst
();
}
void
CDBIterator
::
Next
()
{
piter
->
Next
();
}
namespace
dbwrapper_private
{
void
HandleError
(
const
leveldb
::
Status
&
status
)
{
if
(
status
.
ok
())
{
return
;
}
const
std
::
string
errmsg
=
"Fatal LevelDB error: "
+
status
.
ToString
();
LogPrintf
(
"%s
\n
"
,
errmsg
);
LogPrintf
(
"You can use -debug=leveldb to get more complete diagnostic "
"messages
\n
"
);
throw
dbwrapper_error
(
errmsg
);
}
const
std
::
vector
<
uint8_t
>
&
GetObfuscateKey
(
const
CDBWrapper
&
w
)
{
return
w
.
obfuscate_key
;
}
};
// namespace dbwrapper_private
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Mon, Nov 25, 08:04 (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4515137
Default Alt Text
dbwrapper.cpp (8 KB)
Attached To
rSTAGING Bitcoin ABC staging
Event Timeline
Log In to Comment