diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -62,6 +62,7 @@ */ class CConnman; struct TestingSetup : public BasicTestingSetup { +public: CCoinsViewDB *pcoinsdbview; fs::path pathTemp; boost::thread_group threadGroup; diff --git a/src/test/utxocommit_tests.cpp b/src/test/utxocommit_tests.cpp --- a/src/test/utxocommit_tests.cpp +++ b/src/test/utxocommit_tests.cpp @@ -7,7 +7,9 @@ #include "coins.h" #include "test/test_bitcoin.h" +#include "testutil.h" #include "util.h" + #include #include @@ -27,7 +29,7 @@ return c; } -BOOST_FIXTURE_TEST_SUITE(utxocommit_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(utxocommit_tests, TestingSetup) BOOST_AUTO_TEST_CASE(utxo_commit_order) { @@ -123,4 +125,49 @@ BOOST_ASSERT(uint256(expectedhash) == hash); } +BOOST_AUTO_TEST_CASE(utxo_commit_addcursor) { + + // Test adding a CCoinView Cursor to a CUtxoCommit + // This simulates the initial upgrade where the commitment stored in + // LevelDB must be generated from the existing UTXO set. + + const int count = 50000; + + // We use the pcoinviewdb provided by the test fixture's TestingSetup + CCoinsViewCache cache(pcoinsdbview); + cache.SetBestBlock(InsecureRand256()); + + // We will compare the commitment generated step-by-step, and the one + // created + // from cursor + CUtxoCommit commit_step, commit_cursor; + + LogPrintf("Preparing database\n"); + + for (int n = 0; n < count; n++) { + const COutPoint op = RandomOutpoint(); + const Coin c = RandomCoin(); + + if (c.GetTxOut().scriptPubKey.IsUnspendable()) { + continue; + } + + commit_step.Add(op, c); + cache.AddCoin(op, c, false); + if ((n + 1) % 5000000 == 0) { + LogPrintf("Flushing\n"); + BOOST_ASSERT(cache.Flush()); + } + } + + BOOST_ASSERT(cache.Flush()); + LogPrintf("Starting ECMH generation from cursor\n"); + + std::unique_ptr pcursor(pcoinsdbview->Cursor()); + commit_cursor.AddCoinView(pcursor.get()); + + BOOST_CHECK(commit_step == commit_cursor); + LogPrintf("ECMH generation from cursor done\n"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/utxocommit.h b/src/utxocommit.h --- a/src/utxocommit.h +++ b/src/utxocommit.h @@ -12,6 +12,7 @@ #include class Coin; +class CCoinsViewCursor; /* A Utxo Commitment * @@ -36,12 +37,12 @@ // Constructs empty CUtxoCommit CUtxoCommit(); - // Construct by combining two other CUtxoCommits - CUtxoCommit(const CUtxoCommit &commit1, const CUtxoCommit &commit2); - // Adds a TXO from multiset void Add(const COutPoint &out, const Coin &element); + // Adds another commitment to this one + void Add(const CUtxoCommit &other); + // Removes a TXO from multiset void Remove(const COutPoint &out, const Coin &element); @@ -49,6 +50,9 @@ uint256 GetHash() const; + // Initializes from an existing UTXO set + bool AddCoinView(CCoinsViewCursor *cursor); + // Comparison friend bool operator==(const CUtxoCommit &a, const CUtxoCommit &b) { return a.GetHash() == b.GetHash(); diff --git a/src/utxocommit.cpp b/src/utxocommit.cpp --- a/src/utxocommit.cpp +++ b/src/utxocommit.cpp @@ -4,6 +4,8 @@ #include "utxocommit.h" +#include "util.h" + namespace { secp256k1_context *secp256k1_context_multiset; int secp256k1_context_refcount = 0; @@ -16,6 +18,7 @@ secp256k1_context_create(SECP256K1_CONTEXT_NONE); } secp256k1_context_refcount++; + secp256k1_multiset_init(secp256k1_context_multiset, &multiset); } @@ -26,15 +29,6 @@ } } -// Construct by combining two other CUtxoCommits -CUtxoCommit::CUtxoCommit(const CUtxoCommit &commit1, const CUtxoCommit &commit2) - : CUtxoCommit() { - secp256k1_multiset_combine(secp256k1_context_multiset, &this->multiset, - &commit1.multiset); - secp256k1_multiset_combine(secp256k1_context_multiset, &this->multiset, - &commit2.multiset); -} - // Adds a TXO from multiset void CUtxoCommit::Add(const COutPoint &out, const Coin &element) { @@ -44,6 +38,12 @@ (const uint8_t *)&txo[0], txo.size()); } +// Adds another commitment to this one +void CUtxoCommit::Add(const CUtxoCommit &other) { + secp256k1_multiset_combine(secp256k1_context_multiset, &this->multiset, + &other.multiset); +} + // Removes a TXO from multiset void CUtxoCommit::Remove(const COutPoint &out, const Coin &element) { @@ -64,3 +64,32 @@ &multiset); return uint256(hash); } + +bool CUtxoCommit::AddCoinView(CCoinsViewCursor *pcursor) { + + LogPrintf("Adding existing UTXO set to the UTXO commitment"); + + // TODO: Parallelize + int n = 0; + while (pcursor->Valid()) { + + COutPoint key; + Coin coin; + if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { + + Add(key, coin); + } else { + return error("Failed to retrieve UTXO from cursor"); + } + + if ((n % 1000000) == 0) { + uint8_t c = *key.hash.begin(); + LogPrintf("Generating UTXO commitment; progress %d\n", + uint32_t(c) * 100 / 256); + } + n++; + + pcursor->Next(); + } + return true; +}