Permalink
Browse files

Merge pull request #6777

dcd8e27 Refer to obfuscate_key via pointer in peripheral CLevelDB classes (James O'Beirne)
1488506 Add tests for gettxoutsetinfo, CLevelDBBatch, CLevelDBIterator (James O'Beirne)
0fdf8c8 Handle obfuscation in CLevelDBIterator (James O'Beirne)
3499ce1 Encapsulate CLevelDB iterators cleanly (Pieter Wuille)
  • Loading branch information...
laanwj committed Oct 13, 2015
2 parents 16faccb + dcd8e27 commit 9caaf6ed22d052bae2b9552a7d2df529166664e8
Showing with 245 additions and 61 deletions.
  1. +8 −7 qa/pull-tester/rpc-tests.py
  2. +52 −0 qa/rpc-tests/blockchain.py
  3. +8 −1 src/leveldbwrapper.cpp
  4. +73 −9 src/leveldbwrapper.h
  5. +80 −1 src/test/leveldbwrapper_tests.cpp
  6. +24 −43 src/txdb.cpp
@@ -38,7 +38,7 @@
buildDir = BUILDDIR
os.environ["BITCOIND"] = buildDir + '/src/bitcoind' + EXEEXT
os.environ["BITCOINCLI"] = buildDir + '/src/bitcoin-cli' + EXEEXT
#Disable Windows tests by default
if EXEEXT == ".exe" and "-win" not in opts:
print "Win tests currently disabled. Use -win option to enable"
@@ -67,6 +67,7 @@
'reindex.py',
'decodescript.py',
'p2p-fullblocktest.py',
'blockchain.py',
]
testScriptsExt = [
'bipdersig-p2p.py',
@@ -98,20 +99,20 @@
rpcTestDir = buildDir + '/qa/rpc-tests/'
#Run Tests
for i in range(len(testScripts)):
if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts
if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts
or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ):
print "Running testscript " + testScripts[i] + "..."
subprocess.call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True)
print "Running testscript " + testScripts[i] + "..."
subprocess.call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True)
#exit if help is called so we print just one set of instructions
p = re.compile(" -h| --help")
if p.match(passOn):
sys.exit(0)
#Run Extended Tests
for i in range(len(testScriptsExt)):
if ('-extended' in opts or testScriptsExt[i] in opts
if ('-extended' in opts or testScriptsExt[i] in opts
or re.sub(".py$", "", testScriptsExt[i]) in opts):
print "Running 2nd level testscript " + testScriptsExt[i] + "..."
subprocess.call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True)
print "Running 2nd level testscript " + testScriptsExt[i] + "..."
subprocess.call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True)
else:
print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled"
View
@@ -0,0 +1,52 @@
#!/usr/bin/env python2
# Copyright (c) 2014 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Test RPC calls related to blockchain state.
#
import decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
initialize_chain,
assert_equal,
start_nodes,
connect_nodes_bi,
)
class BlockchainTest(BitcoinTestFramework):
"""
Test blockchain-related RPC calls:
- gettxoutsetinfo
"""
def setup_chain(self):
print("Initializing test directory " + self.options.tmpdir)
initialize_chain(self.options.tmpdir)
def setup_network(self, split=False):
self.nodes = start_nodes(2, self.options.tmpdir)
connect_nodes_bi(self.nodes, 0, 1)
self.is_network_split = False
self.sync_all()
def run_test(self):
node = self.nodes[0]
res = node.gettxoutsetinfo()
assert_equal(res[u'total_amount'], decimal.Decimal('8725.00000000'))
assert_equal(res[u'transactions'], 200)
assert_equal(res[u'height'], 200)
assert_equal(res[u'txouts'], 200)
assert_equal(res[u'bytes_serialized'], 13000),
assert_equal(len(res[u'bestblock']), 64)
assert_equal(len(res[u'hash_serialized']), 64)
if __name__ == '__main__':
BlockchainTest().main()
View
@@ -131,7 +131,7 @@ std::vector<unsigned char> CLevelDBWrapper::CreateObfuscateKey() const
bool CLevelDBWrapper::IsEmpty()
{
boost::scoped_ptr<leveldb::Iterator> it(NewIterator());
boost::scoped_ptr<CLevelDBIterator> it(NewIterator());
it->SeekToFirst();
return !(it->Valid());
}
@@ -145,3 +145,10 @@ std::string CLevelDBWrapper::GetObfuscateKeyHex() const
{
return HexStr(obfuscate_key);
}
CLevelDBIterator::~CLevelDBIterator() { delete piter; }
bool CLevelDBIterator::Valid() { return piter->Valid(); }
void CLevelDBIterator::SeekToFirst() { piter->SeekToFirst(); }
void CLevelDBIterator::SeekToLast() { piter->SeekToLast(); }
void CLevelDBIterator::Next() { piter->Next(); }
void CLevelDBIterator::Prev() { piter->Prev(); }
View
@@ -32,13 +32,13 @@ class CLevelDBBatch
private:
leveldb::WriteBatch batch;
const std::vector<unsigned char> obfuscate_key;
const std::vector<unsigned char> *obfuscate_key;
public:
/**
* @param[in] obfuscate_key If passed, XOR data with this key.
*/
CLevelDBBatch(const std::vector<unsigned char>& obfuscate_key) : obfuscate_key(obfuscate_key) { };
CLevelDBBatch(const std::vector<unsigned char> *obfuscate_key) : obfuscate_key(obfuscate_key) { };
template <typename K, typename V>
void Write(const K& key, const V& value)
@@ -51,7 +51,7 @@ class CLevelDBBatch
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.reserve(ssValue.GetSerializeSize(value));
ssValue << value;
ssValue.Xor(obfuscate_key);
ssValue.Xor(*obfuscate_key);
leveldb::Slice slValue(&ssValue[0], ssValue.size());
batch.Put(slKey, slValue);
@@ -68,7 +68,72 @@ class CLevelDBBatch
batch.Delete(slKey);
}
};
class CLevelDBIterator
{
private:
leveldb::Iterator *piter;
const std::vector<unsigned char> *obfuscate_key;
public:
/**
* @param[in] piterIn The original leveldb iterator.
* @param[in] obfuscate_key If passed, XOR data with this key.
*/
CLevelDBIterator(leveldb::Iterator *piterIn, const std::vector<unsigned char>* obfuscate_key) :
piter(piterIn), obfuscate_key(obfuscate_key) { };
~CLevelDBIterator();
bool Valid();
void SeekToFirst();
void SeekToLast();
template<typename K> void Seek(const K& key) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(ssKey.GetSerializeSize(key));
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
piter->Seek(slKey);
}
void Next();
void Prev();
template<typename K> bool GetKey(K& key) {
leveldb::Slice slKey = piter->key();
try {
CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
ssKey >> key;
} catch(std::exception &e) {
return false;
}
return true;
}
unsigned int GetKeySize() {
return piter->key().size();
}
template<typename V> bool GetValue(V& value) {
leveldb::Slice slValue = piter->value();
try {
CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
ssValue.Xor(*obfuscate_key);
ssValue >> value;
} catch(std::exception &e) {
return false;
}
return true;
}
unsigned int GetValueSize() {
return piter->value().size();
}
};
class CLevelDBWrapper
{
private:
@@ -145,7 +210,7 @@ class CLevelDBWrapper
template <typename K, typename V>
bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error)
{
CLevelDBBatch batch(obfuscate_key);
CLevelDBBatch batch(&obfuscate_key);
batch.Write(key, value);
return WriteBatch(batch, fSync);
}
@@ -172,7 +237,7 @@ class CLevelDBWrapper
template <typename K>
bool Erase(const K& key, bool fSync = false) throw(leveldb_error)
{
CLevelDBBatch batch(obfuscate_key);
CLevelDBBatch batch(&obfuscate_key);
batch.Erase(key);
return WriteBatch(batch, fSync);
}
@@ -187,14 +252,13 @@ class CLevelDBWrapper
bool Sync() throw(leveldb_error)
{
CLevelDBBatch batch(obfuscate_key);
CLevelDBBatch batch(&obfuscate_key);
return WriteBatch(batch, true);
}
// not exactly clean encapsulation, but it's easiest for now
leveldb::Iterator* NewIterator()
CLevelDBIterator *NewIterator()
{
return pdb->NewIterator(iteroptions);
return new CLevelDBIterator(pdb->NewIterator(iteroptions), &obfuscate_key);
}
/**
@@ -46,7 +46,86 @@ BOOST_AUTO_TEST_CASE(leveldbwrapper)
BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
}
}
// Test batch operations
BOOST_AUTO_TEST_CASE(leveldbwrapper_batch)
{
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
path ph = temp_directory_path() / unique_path();
CLevelDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'i';
uint256 in = GetRandHash();
char key2 = 'j';
uint256 in2 = GetRandHash();
char key3 = 'k';
uint256 in3 = GetRandHash();
uint256 res;
CLevelDBBatch batch(&dbw.GetObfuscateKey());
batch.Write(key, in);
batch.Write(key2, in2);
batch.Write(key3, in3);
// Remove key3 before it's even been written
batch.Erase(key3);
dbw.WriteBatch(batch);
BOOST_CHECK(dbw.Read(key, res));
BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
BOOST_CHECK(dbw.Read(key2, res));
BOOST_CHECK_EQUAL(res.ToString(), in2.ToString());
// key3 never should've been written
BOOST_CHECK(dbw.Read(key3, res) == false);
}
}
BOOST_AUTO_TEST_CASE(leveldbwrapper_iterator)
{
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
path ph = temp_directory_path() / unique_path();
CLevelDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
char key = 'j';
uint256 in = GetRandHash();
BOOST_CHECK(dbw.Write(key, in));
char key2 = 'k';
uint256 in2 = GetRandHash();
BOOST_CHECK(dbw.Write(key2, in2));
boost::scoped_ptr<CLevelDBIterator> it(const_cast<CLevelDBWrapper*>(&dbw)->NewIterator());
// Be sure to seek past the obfuscation key (if it exists)
it->Seek(key);
char key_res;
uint256 val_res;
it->GetKey(key_res);
it->GetValue(val_res);
BOOST_CHECK_EQUAL(key_res, key);
BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString());
it->Next();
it->GetKey(key_res);
it->GetValue(val_res);
BOOST_CHECK_EQUAL(key_res, key2);
BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString());
it->Next();
BOOST_CHECK_EQUAL(it->Valid(), false);
}
}
// Test that we do not obfuscation if there is existing data.
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
Oops, something went wrong.

0 comments on commit 9caaf6e

Please sign in to comment.